aboutsummaryrefslogtreecommitdiff
path: root/modules/core/module-aiassist.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-10-23 19:36:52 +0200
committerAgando <roduccomm@gmail.com>2026-10-23 19:36:52 +0200
commitd0a6c805a142b6a637913a6af876a08e72bcb1f7 (patch)
tree05d9965d3c7086a707133bb5182a44e53c613102 /modules/core/module-aiassist.js
parent313f13955ad9c72895b0914ac828c61b3e469715 (diff)
feat(newos4): hermes command, auto-poll, chat_hermes tool, token reset (v4.6.73)
Phase C as repo edits (browser edits in _mods/ do not survive a reload). - `hermes <msg>`: 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).
Diffstat (limited to 'modules/core/module-aiassist.js')
-rw-r--r--modules/core/module-aiassist.js49
1 files changed, 49 insertions, 0 deletions
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
@@ -433,6 +433,20 @@ buildSystemPrompt() {
}, {
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: {
name: 'spawn_agent',
description: '(DESTRUCTIVE) Spawn a new worker agent with a role and capabilities',
parameters: {
@@ -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' });