openvibers/openvibe.chat Commits

Gate Vibe task delegation behind owner switch (#462)

OP OpenVibers committed 3 days ago · 021a796d89 · 5 files, +13 −2

* Gate Vibe task delegation behind owner switch

* Merge remote-tracking branch 'origin/main' into feat/c3-vibe-actor

# Conflicts:
#	server/config.js

Changes

ddiff --git a/server/config.js b/server/config.js
iindex f596612..a73b31f 100644
---- a/server/config.js
++++ b/server/config.js
@@@ -236,6 +236,8 @@ module.exports = {
// Vibe, OpenVibe's assistant (server/rooms/vibe.js): on unless CHAT_VIBE=0 (it also needs AI members on); a person
// gets this many answers a UTC day across all their Vibe chats.
vibe: process.env.CHAT_VIBE !== '0',
++ // C3's explicit "do it for me" task modes (Actor-backed Research, Agent and Code); site owner opt-in.
++ vibeDoIt: bool(process.env.CHAT_VIBE_DO_IT_ENABLED, false),
// C3 web search in Vibe's composer (the existing Search Fabric tool and cited results); owner enables it explicitly.
vibeWebSearch: bool(process.env.CHAT_VIBE_WEB_SEARCH_ENABLED, false),
vibeRepliesPerDay: Math.max(1, int(process.env.CHAT_VIBE_REPLIES_PER_DAY, 100)),
ddiff --git a/server/rooms/vibe.js b/server/rooms/vibe.js
iindex 018607b..5a14447 100644
---- a/server/rooms/vibe.js
++++ b/server/rooms/vibe.js
@@@ -139,6 +139,7 @@ const ON_IT = {
};
async function work(room, user, text, mode) {
++ if (!config.ai.vibeDoIt) fail(503, 'vibe.work_disabled', 'Do it for me is not enabled on this Chat server');
const kind = MODES[String(mode || '')];
if (!kind) fail(422, 'vibe.mode', 'Pick Chat, Research, Agent or Code');
if (!isVibeRoom(room) || !user || Number(room.owner_id) !== Number(user.id)) fail(404, 'rooms.not_found', 'No such chat');
ddiff --git a/server/web/vibe-pages.js b/server/web/vibe-pages.js
iindex 09f138d..c60e800 100644
---- a/server/web/vibe-pages.js
++++ b/server/web/vibe-pages.js
@@@ -206,7 +206,8 @@ function composer({ slug, signedIn, enabled, prefill = '', engine = null, models
? `<button class="ovt-btn vb-attach" type="button" data-vb-attach aria-label="${label}" title="${label}">${ICON.attach}</button>
<dialog class="ovk-sheet vb-upload" data-vb-upload-dialog aria-labelledby="vb-upload-h"><div class="ovk-sheet-head" data-ov-sheet-head><h2 id="vb-upload-h">${label}</h2><button type="button" class="ovk-icon-btn" data-vb-upload-close aria-label="Close">${thread.icon('close')}</button></div><div class="ovk-sheet-body"><div data-vb-upload data-accept=""></div></div></dialog>`
: '';
-- const modes = `<fieldset class="vb-modes" aria-label="What Vibe does with it">${MODES.map(([value, label, hint, placeholder], i) => `<label title="${esc(hint)}"><input type="radio" name="mode" value="${value}" data-placeholder="${esc(placeholder)}"${i ? '' : ' checked'}><span>${label}</span></label>`).join('')}</fieldset>`;
++ const offeredModes = require('../config').ai.vibeDoIt ? MODES : MODES.slice(0, 1);
++ const modes = `<fieldset class="vb-modes" aria-label="What Vibe does with it">${offeredModes.map(([value, label, hint, placeholder], i) => `<label title="${esc(hint)}"><input type="radio" name="mode" value="${value}" data-placeholder="${esc(placeholder)}"${i ? '' : ' checked'}><span>${label}</span></label>`).join('')}</fieldset>`;
const fine = !enabled ? 'Vibe is resting right now. Try again later.' : engine && engine.own ? `Vibe thinks with ${esc(engine.you)} (<a href="/settings#oc-ai-key">change</a>). It can get things wrong: check what matters.` : 'Vibe can get things wrong. Check what matters.';
let html = thread.composer({
action, method: signedIn ? 'post' : 'get', name: 'message', id: 'vb-composer', attrs: signedIn ? {} : { 'data-signin': '1' },
ddiff --git a/test/vibe-canvas.test.js b/test/vibe-canvas.test.js
iindex b4c89e2..ad55b9d 100644
---- a/test/vibe-canvas.test.js
++++ b/test/vibe-canvas.test.js
@@@ -17,6 +17,7 @@ const pageJson = (html) => JSON.parse((/window\.__OV_PAGE = (\{.*?\});\n/.exec(h
t('boot: a Vibe chat whose newest task is Actor\'s', async () => {
h = await boot();
++ require('../server/config').ai.vibeDoIt = true; // This fixture explicitly enables Actor-backed tasks.
workspace = require('../server/rooms/workspace');
workspace._setTokens({ getToken: async () => 'chat-actor-token' });
ann = h.addUser('mira', { subject: ids.newId('user') });
ddiff --git a/test/vibe-modes.test.js b/test/vibe-modes.test.js
iindex af91354..793f1ce 100644
---- a/test/vibe-modes.test.js
++++ b/test/vibe-modes.test.js
@@@ -12,7 +12,7 @@ const { boot, suite } = require('./helpers');
const t = suite('vibe modes');
const SITE = 'https://openvibe.chat';
--let h, ann, bob, workspace;
++let h, ann, bob, workspace, config;
const turns = [];
const actorCalls = [];
let actorTask = { id: 'tsk_r1', state: 'queued' };
@@@ -22,6 +22,8 @@ const realFetch = globalThis.fetch;
t('boot', async () => {
h = await boot({ env: { CHAT_WEB_URL: SITE } });
++ config = require('../server/config');
++ config.ai.vibeDoIt = false;
workspace = require('../server/rooms/workspace');
workspace._setTokens({ getToken: async () => 'chat-actor-token' });
require('../server/ai/client')._setClient({
@@@ -40,6 +42,9 @@ t('boot', async () => {
});
t('Research in a new chat: a task on Actor as the person, Vibe says it is on it, no instant answer', async () => {
++ const disabled = await vibeApi('POST', '', ann, { message: 'Research this first', mode: 'research' });
++ assert.strictEqual(disabled.status, 503, 'the owner switch is off by default');
++ config.ai.vibeDoIt = true;
const r = await vibeApi('POST', '', ann, { message: 'Compare the three cheapest cloud browsers for agents', mode: 'research' });
assert.strictEqual(r.status, 201, r.text);
const slug = r.body.room.slug;
@@@ -151,6 +156,7 @@ t('refusals: an unknown mode, someone else\'s chat, no sign-in', async () => {
});
t('the composer shows the four modes, Chat chosen, each saying what it does', async () => {
++ config.ai.vibeDoIt = true;
const page = await h.http('GET', '/vibe', { headers: { cookie: `ov_token=${ann.token}` } });
assert.match(page.text, /<fieldset class="vb-modes" aria-label="What Vibe does with it">/);
assert.match(page.text, /value="chat" data-placeholder="Message Vibe" checked><span>Chat<\/span>/);