diff options
Diffstat (limited to 'modules/core/module-orchestrator.js')
| -rw-r--r-- | modules/core/module-orchestrator.js | 35 |
1 files changed, 24 insertions, 11 deletions
diff --git a/modules/core/module-orchestrator.js b/modules/core/module-orchestrator.js index 9f23ceb..6b8ee18 100644 --- a/modules/core/module-orchestrator.js +++ b/modules/core/module-orchestrator.js @@ -1116,13 +1116,21 @@ class OrchestratorModule { if (r.status === 'fulfilled') { // Find the agent that handled this sub-task const agent = Object.values(this._agents).find(a => a.currentTask === sub.goal || (a.lastTask === sub.goal)); - if (agent) { - const outbox = this.terminal.fs.fsReadJSON(`/agents/${agent.id}/outbox.json`); - const snippet = outbox?.result ? outbox.result.substring(0, 200) : 'completed (no outbox)'; - summaries.push({ task: sub.goal, agent: agent.id, status: 'ok', result: snippet }); - } else { - summaries.push({ task: sub.goal, status: 'ok', result: 'completed (agent recycled)' }); + if (!agent) { + // "ok / completed (agent recycled)" told the caller a task succeeded + // when we could not even find its agent. + summaries.push({ task: sub.goal, status: 'failed', error: 'agent not found — result unverifiable' }); + continue; } + const outbox = this.terminal.fs.fsReadJSON(`/agents/${agent.id}/outbox.json`); + const body = outbox && typeof outbox.result === 'string' ? outbox.result : ''; + const snippet = body.substring(0, 200); + summaries.push({ + task: sub.goal, agent: agent.id, + status: body ? 'ok' : 'failed', + result: body || null, + error: body ? undefined : (outbox && outbox.error) || 'no result in outbox' + }); } else { summaries.push({ task: sub.goal, status: 'failed', error: r.reason?.message || 'unknown error' }); } @@ -1228,26 +1236,31 @@ class OrchestratorModule { const _agentTag = agent.id + '/' + (agent.role || 'worker'); const result = await aiModule.chat([`[DELEGATED] ${task}`], { agentTag: _agentTag }); try { aiModule._sessions.delete(_agentTag); } catch (_e) {} - agent.status = 'completed'; + // chat() resolves {content, toolTurns}. Anything that is not a non-empty answer + // is a failure — the old code substituted the literal "completed", so a worker + // that returned nothing was indistinguishable from one that finished. + const _answer = typeof result === 'string' ? result : ((result && result.content) || ''); + agent.status = _answer ? 'completed' : 'failed'; // Write result to outbox this.terminal.fs.writeFile(`agents/${agent.id}/outbox.json`, JSON.stringify({ - result: typeof result === 'string' ? result : (result?.content || 'completed'), - status: 'completed', + result: _answer || null, + status: _answer ? 'completed' : 'failed', + error: _answer ? null : 'agent produced no answer', timestamp: new Date().toISOString() })); // Record in memory graph const memgraph = this.terminal.moduleManager.modules['MemoryGraphModule']; if (memgraph) { - memgraph.recordTask(task, typeof result === 'string' ? result : (result?.content || ''), agent.id); + memgraph.recordTask(task, _answer, agent.id); memgraph.recordFileAccess(`/agents/${agent.id}/outbox.json`); } this.terminal.print(`✅ ${agent.id} completed task`, 'success'); // Return result for callers that await delegateTask - outboxResult = typeof result === 'string' ? result : (result?.content || 'completed'); + outboxResult = _answer; // '' when the worker returned nothing; callers see that // Check restart policy — if always, set back to idle for next task if (agent.restartPolicy === 'always') { |