From d0a6c805a142b6a637913a6af876a08e72bcb1f7 Mon Sep 17 00:00:00 2001 From: Agando Date: Fri, 23 Oct 2026 19:36:52 +0200 Subject: feat(newos4): hermes command, auto-poll, chat_hermes tool, token reset (v4.6.73) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase C as repo edits (browser edits in _mods/ do not survive a reload). - `hermes `: sends through the chat endpoint and always prints an outcome — '📨 Sending…' then either '✅ sent id=… epoch=…' or a coded error with a hint (429 retry later, 503 host busy, 401 run hermes-reset). `feedback` stays as the alias. - NOT named `chat`: ChatStudioModule already registers `chat` and dispatch walks modules in order, so a second `chat` was silently unreachable. Found by test, not by reading. - `hermes-poll` / `feedback-inbox`, `hermes-reset` (token rotation). - Poller: 30 s, paused while the tab is hidden, polls on visibilitychange, first poll adopts the cursor and prints nothing, cursor persisted in webx11_chat_state. - `chat_hermes` tool: send + read from inside the agent loop. - Contract/help updated for the rename. Acceptance: 'hermes acceptance test' in the browser, reply written host-side with scripts/chat.py, appeared in the terminal by itself 30 s later with no command typed (cursor 1792776973800). --- modules/core/module-aiassist.js | 49 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'modules/core/module-aiassist.js') diff --git a/modules/core/module-aiassist.js b/modules/core/module-aiassist.js index f5895f3..ce650b9 100644 --- a/modules/core/module-aiassist.js +++ b/modules/core/module-aiassist.js @@ -430,6 +430,20 @@ buildSystemPrompt() { required: ['task'] } } + }, { + type: 'function', + function: { + name: 'chat_hermes', + description: 'Send a message to Hermes, the platform operator agent, and read replies. Use when a task needs a decision only the operator can make: policy, a deploy approval, scope. Replies are asynchronous — call with action "read" to check for an answer.', + parameters: { + type: 'object', + properties: { + action: { type: 'string', enum: ['send', 'read'], description: 'send a message, or read replies' }, + text: { type: 'string', description: 'message body, required for send' } + }, + required: ['action'] + } + } }, { type: 'function', function: { @@ -2676,6 +2690,41 @@ buildSystemPrompt() { } return JSON.stringify({ agents: results, count: results.length }); } + case 'chat_hermes': { + // v4.6.71: one channel, one store. Contract: _docs/chat-contract.md + const ds = this.terminal.moduleManager.modules['DebugSupervisorModule']; + if (!ds || typeof ds._chatPost !== 'function') { + return JSON.stringify({ error: 'chat bridge unavailable' }); + } + try { + if ((a.action || 'read') === 'send') { + const text = String(a.text || '').trim(); + if (!text) return JSON.stringify({ error: 'text is required for send' }); + const j = await ds._chatPost(text, 'agent'); + return JSON.stringify({ + sent: true, id: j.id, epoch: j.epoch, + note: 'asynchronous — call chat_hermes with action read for the answer' + }); + } + const st = ds._chatThread(); + const resp = await fetch( + '/spaces/newos4/api/chat/poll.php' + (st.cursor ? '?since=' + st.cursor : ''), + { headers: { 'X-Chat-Token': st.token } }); + const j = await resp.json().catch(() => null); + if (!resp.ok || !j || !j.success) { + return JSON.stringify({ error: (j && j.error) || ('HTTP ' + resp.status) }); + } + if (j.cursor) ds._chatSaveState({ token: st.token, cursor: j.cursor, thread: j.thread || '' }); + return JSON.stringify({ + awaiting: j.awaiting, + messages: (j.messages || []).slice(-10).map(m => ({ + author: m.author, ts: m.ts, text: m.text + })) + }); + } catch (e) { + return JSON.stringify({ error: e.message }); + } + } case 'delegate_task': { const task = a.task || ''; if (!task) return JSON.stringify({ error: 'No task provided' }); -- cgit v1.2.3