aboutsummaryrefslogtreecommitdiff
path: root/modules/core/module-orchestrator.js
diff options
context:
space:
mode:
Diffstat (limited to 'modules/core/module-orchestrator.js')
-rw-r--r--modules/core/module-orchestrator.js35
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') {