kagent-ts 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +68 -21
- package/README.md +27 -371
- package/dist/compression/progressive-compressor.d.ts +66 -0
- package/dist/compression/progressive-compressor.d.ts.map +1 -0
- package/dist/compression/progressive-compressor.js +367 -0
- package/dist/compression/progressive-compressor.js.map +1 -0
- package/dist/compression/types.d.ts +1 -5
- package/dist/compression/types.d.ts.map +1 -1
- package/dist/context/context-manager.d.ts +34 -15
- package/dist/context/context-manager.d.ts.map +1 -1
- package/dist/context/context-manager.js +78 -28
- package/dist/context/context-manager.js.map +1 -1
- package/dist/context/types.d.ts +20 -4
- package/dist/context/types.d.ts.map +1 -1
- package/dist/core/agent.d.ts +354 -25
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +646 -64
- package/dist/core/agent.js.map +1 -1
- package/dist/core/fusion-agent.d.ts +207 -0
- package/dist/core/fusion-agent.d.ts.map +1 -0
- package/dist/core/fusion-agent.js +769 -0
- package/dist/core/fusion-agent.js.map +1 -0
- package/dist/core/hooks.d.ts +19 -7
- package/dist/core/hooks.d.ts.map +1 -1
- package/dist/core/plan-solve-agent.d.ts +1 -15
- package/dist/core/plan-solve-agent.d.ts.map +1 -1
- package/dist/core/plan-solve-agent.js +142 -117
- package/dist/core/plan-solve-agent.js.map +1 -1
- package/dist/core/react-agent.d.ts +0 -13
- package/dist/core/react-agent.d.ts.map +1 -1
- package/dist/core/react-agent.js +127 -102
- package/dist/core/react-agent.js.map +1 -1
- package/dist/core/response-schema.d.ts +65 -0
- package/dist/core/response-schema.d.ts.map +1 -1
- package/dist/core/response-schema.js +174 -1
- package/dist/core/response-schema.js.map +1 -1
- package/dist/core/system-prompts.d.ts +27 -0
- package/dist/core/system-prompts.d.ts.map +1 -0
- package/dist/core/system-prompts.js +112 -0
- package/dist/core/system-prompts.js.map +1 -0
- package/dist/eval/benchmark.d.ts +81 -0
- package/dist/eval/benchmark.d.ts.map +1 -0
- package/dist/eval/benchmark.js +292 -0
- package/dist/eval/benchmark.js.map +1 -0
- package/dist/eval/eval-runner.d.ts +79 -0
- package/dist/eval/eval-runner.d.ts.map +1 -0
- package/dist/eval/eval-runner.js +252 -0
- package/dist/eval/eval-runner.js.map +1 -0
- package/dist/eval/index.d.ts +7 -0
- package/dist/eval/index.d.ts.map +1 -0
- package/dist/eval/index.js +13 -0
- package/dist/eval/index.js.map +1 -0
- package/dist/eval/tool-call-evaluator.d.ts +72 -0
- package/dist/eval/tool-call-evaluator.d.ts.map +1 -0
- package/dist/eval/tool-call-evaluator.js +265 -0
- package/dist/eval/tool-call-evaluator.js.map +1 -0
- package/dist/eval/types.d.ts +219 -0
- package/dist/eval/types.d.ts.map +1 -0
- package/dist/eval/types.js +3 -0
- package/dist/eval/types.js.map +1 -0
- package/dist/index.d.ts +58 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +116 -8
- package/dist/index.js.map +1 -1
- package/dist/llm/anthropic-provider.d.ts +141 -0
- package/dist/llm/anthropic-provider.d.ts.map +1 -0
- package/dist/llm/anthropic-provider.js +486 -0
- package/dist/llm/anthropic-provider.js.map +1 -0
- package/dist/llm/errors.d.ts +26 -0
- package/dist/llm/errors.d.ts.map +1 -0
- package/dist/llm/errors.js +19 -0
- package/dist/llm/errors.js.map +1 -0
- package/dist/llm/factory.d.ts +73 -0
- package/dist/llm/factory.d.ts.map +1 -0
- package/dist/llm/factory.js +77 -0
- package/dist/llm/factory.js.map +1 -0
- package/dist/llm/fallback-provider.d.ts +47 -0
- package/dist/llm/fallback-provider.d.ts.map +1 -0
- package/dist/llm/fallback-provider.js +91 -0
- package/dist/llm/fallback-provider.js.map +1 -0
- package/dist/llm/interface.d.ts +54 -11
- package/dist/llm/interface.d.ts.map +1 -1
- package/dist/llm/interface.js +34 -0
- package/dist/llm/interface.js.map +1 -1
- package/dist/llm/model-router.d.ts +126 -0
- package/dist/llm/model-router.d.ts.map +1 -0
- package/dist/llm/model-router.js +178 -0
- package/dist/llm/model-router.js.map +1 -0
- package/dist/llm/openai-provider.d.ts +8 -32
- package/dist/llm/openai-provider.d.ts.map +1 -1
- package/dist/llm/openai-provider.js +27 -60
- package/dist/llm/openai-provider.js.map +1 -1
- package/dist/llm/rate-limiter.d.ts +41 -0
- package/dist/llm/rate-limiter.d.ts.map +1 -0
- package/dist/llm/rate-limiter.js +93 -0
- package/dist/llm/rate-limiter.js.map +1 -0
- package/dist/llm/retry.d.ts +26 -0
- package/dist/llm/retry.d.ts.map +1 -0
- package/dist/llm/retry.js +44 -0
- package/dist/llm/retry.js.map +1 -0
- package/dist/llm/token-budget.d.ts +97 -0
- package/dist/llm/token-budget.d.ts.map +1 -0
- package/dist/llm/token-budget.js +115 -0
- package/dist/llm/token-budget.js.map +1 -0
- package/dist/logging/index.d.ts +2 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/index.js +7 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/logging/logger.d.ts +38 -0
- package/dist/logging/logger.d.ts.map +1 -0
- package/dist/logging/logger.js +34 -0
- package/dist/logging/logger.js.map +1 -0
- package/dist/mcp/mcp-client-manager.d.ts +10 -2
- package/dist/mcp/mcp-client-manager.d.ts.map +1 -1
- package/dist/mcp/mcp-client-manager.js +20 -9
- package/dist/mcp/mcp-client-manager.js.map +1 -1
- package/dist/memory/index.d.ts +3 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +6 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/memory-manager.d.ts +119 -0
- package/dist/memory/memory-manager.d.ts.map +1 -0
- package/dist/memory/memory-manager.js +334 -0
- package/dist/memory/memory-manager.js.map +1 -0
- package/dist/messages/types.d.ts +2 -0
- package/dist/messages/types.d.ts.map +1 -1
- package/dist/orchestrator/index.d.ts +5 -0
- package/dist/orchestrator/index.d.ts.map +1 -0
- package/dist/orchestrator/index.js +13 -0
- package/dist/orchestrator/index.js.map +1 -0
- package/dist/orchestrator/json-extractor.d.ts +18 -0
- package/dist/orchestrator/json-extractor.d.ts.map +1 -0
- package/dist/orchestrator/json-extractor.js +111 -0
- package/dist/orchestrator/json-extractor.js.map +1 -0
- package/dist/orchestrator/orchestrator-agent.d.ts +152 -0
- package/dist/orchestrator/orchestrator-agent.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator-agent.js +675 -0
- package/dist/orchestrator/orchestrator-agent.js.map +1 -0
- package/dist/orchestrator/orchestrator-response.d.ts +40 -0
- package/dist/orchestrator/orchestrator-response.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator-response.js +275 -0
- package/dist/orchestrator/orchestrator-response.js.map +1 -0
- package/dist/orchestrator/orchestrator-types.d.ts +116 -0
- package/dist/orchestrator/orchestrator-types.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator-types.js +3 -0
- package/dist/orchestrator/orchestrator-types.js.map +1 -0
- package/dist/preferences/preference-manager.d.ts +8 -3
- package/dist/preferences/preference-manager.d.ts.map +1 -1
- package/dist/preferences/preference-manager.js +17 -4
- package/dist/preferences/preference-manager.js.map +1 -1
- package/dist/rag/chroma-store.d.ts +52 -0
- package/dist/rag/chroma-store.d.ts.map +1 -0
- package/dist/rag/chroma-store.js +110 -0
- package/dist/rag/chroma-store.js.map +1 -0
- package/dist/rag/document-loader.d.ts +21 -0
- package/dist/rag/document-loader.d.ts.map +1 -0
- package/dist/rag/document-loader.js +129 -0
- package/dist/rag/document-loader.js.map +1 -0
- package/dist/rag/embedding-provider.d.ts +36 -0
- package/dist/rag/embedding-provider.d.ts.map +1 -0
- package/dist/rag/embedding-provider.js +74 -0
- package/dist/rag/embedding-provider.js.map +1 -0
- package/dist/rag/index.d.ts +17 -0
- package/dist/rag/index.d.ts.map +1 -0
- package/dist/rag/index.js +27 -0
- package/dist/rag/index.js.map +1 -0
- package/dist/rag/keyword-index.d.ts +53 -0
- package/dist/rag/keyword-index.d.ts.map +1 -0
- package/dist/rag/keyword-index.js +161 -0
- package/dist/rag/keyword-index.js.map +1 -0
- package/dist/rag/llm-reranker.d.ts +36 -0
- package/dist/rag/llm-reranker.d.ts.map +1 -0
- package/dist/rag/llm-reranker.js +95 -0
- package/dist/rag/llm-reranker.js.map +1 -0
- package/dist/rag/rag-manager.d.ts +54 -0
- package/dist/rag/rag-manager.d.ts.map +1 -0
- package/dist/rag/rag-manager.js +179 -0
- package/dist/rag/rag-manager.js.map +1 -0
- package/dist/rag/rag-types.d.ts +143 -0
- package/dist/rag/rag-types.d.ts.map +1 -0
- package/dist/rag/rag-types.js +9 -0
- package/dist/rag/rag-types.js.map +1 -0
- package/dist/rag/rrf.d.ts +47 -0
- package/dist/rag/rrf.d.ts.map +1 -0
- package/dist/rag/rrf.js +70 -0
- package/dist/rag/rrf.js.map +1 -0
- package/dist/rag/search-knowledge.d.ts +24 -0
- package/dist/rag/search-knowledge.d.ts.map +1 -0
- package/dist/rag/search-knowledge.js +86 -0
- package/dist/rag/search-knowledge.js.map +1 -0
- package/dist/rag/text-splitter.d.ts +25 -0
- package/dist/rag/text-splitter.d.ts.map +1 -0
- package/dist/rag/text-splitter.js +136 -0
- package/dist/rag/text-splitter.js.map +1 -0
- package/dist/rag/vector-store.d.ts +34 -0
- package/dist/rag/vector-store.d.ts.map +1 -0
- package/dist/rag/vector-store.js +73 -0
- package/dist/rag/vector-store.js.map +1 -0
- package/dist/reflection/error-notebook.d.ts +125 -0
- package/dist/reflection/error-notebook.d.ts.map +1 -0
- package/dist/reflection/error-notebook.js +368 -0
- package/dist/reflection/error-notebook.js.map +1 -0
- package/dist/reflection/index.d.ts +8 -0
- package/dist/reflection/index.d.ts.map +1 -0
- package/dist/reflection/index.js +12 -0
- package/dist/reflection/index.js.map +1 -0
- package/dist/reflection/memory-reflector.d.ts +97 -0
- package/dist/reflection/memory-reflector.d.ts.map +1 -0
- package/dist/reflection/memory-reflector.js +215 -0
- package/dist/reflection/memory-reflector.js.map +1 -0
- package/dist/reflection/reflection-agent.d.ts +105 -0
- package/dist/reflection/reflection-agent.d.ts.map +1 -0
- package/dist/reflection/reflection-agent.js +234 -0
- package/dist/reflection/reflection-agent.js.map +1 -0
- package/dist/reflection/reflection-hook.d.ts +50 -0
- package/dist/reflection/reflection-hook.d.ts.map +1 -0
- package/dist/reflection/reflection-hook.js +108 -0
- package/dist/reflection/reflection-hook.js.map +1 -0
- package/dist/rules/project-rules.d.ts +47 -0
- package/dist/rules/project-rules.d.ts.map +1 -0
- package/dist/rules/project-rules.js +166 -0
- package/dist/rules/project-rules.js.map +1 -0
- package/dist/security/boundaries.d.ts +81 -0
- package/dist/security/boundaries.d.ts.map +1 -0
- package/dist/security/boundaries.js +158 -0
- package/dist/security/boundaries.js.map +1 -0
- package/dist/security/index.d.ts +2 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +11 -0
- package/dist/security/index.js.map +1 -0
- package/dist/session/session-types.d.ts +25 -4
- package/dist/session/session-types.d.ts.map +1 -1
- package/dist/skills/file-skill-loader.d.ts +4 -6
- package/dist/skills/file-skill-loader.d.ts.map +1 -1
- package/dist/skills/file-skill-loader.js +8 -19
- package/dist/skills/file-skill-loader.js.map +1 -1
- package/dist/skills/index.d.ts +1 -1
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +1 -2
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/skill-manager.d.ts +18 -8
- package/dist/skills/skill-manager.d.ts.map +1 -1
- package/dist/skills/skill-manager.js +58 -36
- package/dist/skills/skill-manager.js.map +1 -1
- package/dist/skills/types.d.ts +3 -8
- package/dist/skills/types.d.ts.map +1 -1
- package/dist/subagent/index.d.ts +4 -0
- package/dist/subagent/index.d.ts.map +1 -0
- package/dist/subagent/index.js +8 -0
- package/dist/subagent/index.js.map +1 -0
- package/dist/subagent/subagent-loader.d.ts +53 -0
- package/dist/subagent/subagent-loader.d.ts.map +1 -0
- package/dist/subagent/subagent-loader.js +155 -0
- package/dist/subagent/subagent-loader.js.map +1 -0
- package/dist/subagent/subagent-manager.d.ts +161 -0
- package/dist/subagent/subagent-manager.d.ts.map +1 -0
- package/dist/subagent/subagent-manager.js +468 -0
- package/dist/subagent/subagent-manager.js.map +1 -0
- package/dist/subagent/subagent-types.d.ts +77 -0
- package/dist/subagent/subagent-types.d.ts.map +1 -0
- package/dist/subagent/subagent-types.js +3 -0
- package/dist/subagent/subagent-types.js.map +1 -0
- package/dist/tools/builtin/bash.d.ts +3 -0
- package/dist/tools/builtin/bash.d.ts.map +1 -0
- package/dist/tools/builtin/bash.js +87 -0
- package/dist/tools/builtin/bash.js.map +1 -0
- package/dist/tools/builtin/edit-file.d.ts.map +1 -1
- package/dist/tools/builtin/edit-file.js +1 -0
- package/dist/tools/builtin/edit-file.js.map +1 -1
- package/dist/tools/builtin/index.d.ts +14 -0
- package/dist/tools/builtin/index.d.ts.map +1 -1
- package/dist/tools/builtin/index.js +45 -1
- package/dist/tools/builtin/index.js.map +1 -1
- package/dist/tools/builtin/list-errors.d.ts +7 -0
- package/dist/tools/builtin/list-errors.d.ts.map +1 -0
- package/dist/tools/builtin/list-errors.js +64 -0
- package/dist/tools/builtin/list-errors.js.map +1 -0
- package/dist/tools/builtin/list-subagents.d.ts +7 -0
- package/dist/tools/builtin/list-subagents.d.ts.map +1 -0
- package/dist/tools/builtin/list-subagents.js +21 -0
- package/dist/tools/builtin/list-subagents.js.map +1 -0
- package/dist/tools/builtin/recall.d.ts +11 -0
- package/dist/tools/builtin/recall.d.ts.map +1 -0
- package/dist/tools/builtin/recall.js +60 -0
- package/dist/tools/builtin/recall.js.map +1 -0
- package/dist/tools/builtin/remember.d.ts +12 -0
- package/dist/tools/builtin/remember.d.ts.map +1 -0
- package/dist/tools/builtin/remember.js +72 -0
- package/dist/tools/builtin/remember.js.map +1 -0
- package/dist/tools/builtin/skill.d.ts +14 -0
- package/dist/tools/builtin/skill.d.ts.map +1 -0
- package/dist/tools/builtin/skill.js +71 -0
- package/dist/tools/builtin/skill.js.map +1 -0
- package/dist/tools/builtin/spawn-subagent.d.ts +7 -0
- package/dist/tools/builtin/spawn-subagent.d.ts.map +1 -0
- package/dist/tools/builtin/spawn-subagent.js +43 -0
- package/dist/tools/builtin/spawn-subagent.js.map +1 -0
- package/dist/tools/builtin/web-fetch.d.ts +3 -0
- package/dist/tools/builtin/web-fetch.d.ts.map +1 -0
- package/dist/tools/builtin/web-fetch.js +101 -0
- package/dist/tools/builtin/web-fetch.js.map +1 -0
- package/dist/tools/builtin/write-file.d.ts.map +1 -1
- package/dist/tools/builtin/write-file.js +1 -0
- package/dist/tools/builtin/write-file.js.map +1 -1
- package/dist/tools/circuit-breaker.d.ts +19 -10
- package/dist/tools/circuit-breaker.d.ts.map +1 -1
- package/dist/tools/circuit-breaker.js +22 -11
- package/dist/tools/circuit-breaker.js.map +1 -1
- package/dist/tools/error-tracker.d.ts +28 -44
- package/dist/tools/error-tracker.d.ts.map +1 -1
- package/dist/tools/error-tracker.js +39 -156
- package/dist/tools/error-tracker.js.map +1 -1
- package/dist/tools/tool-filter.d.ts +70 -0
- package/dist/tools/tool-filter.d.ts.map +1 -0
- package/dist/tools/tool-filter.js +92 -0
- package/dist/tools/tool-filter.js.map +1 -0
- package/dist/tools/tool-output-truncator.d.ts +36 -0
- package/dist/tools/tool-output-truncator.d.ts.map +1 -0
- package/dist/tools/tool-output-truncator.js +117 -0
- package/dist/tools/tool-output-truncator.js.map +1 -0
- package/dist/tools/tool-registry.d.ts +25 -9
- package/dist/tools/tool-registry.d.ts.map +1 -1
- package/dist/tools/tool-registry.js +77 -28
- package/dist/tools/tool-registry.js.map +1 -1
- package/dist/tools/tool-validator.d.ts +13 -0
- package/dist/tools/tool-validator.d.ts.map +1 -0
- package/dist/tools/tool-validator.js +116 -0
- package/dist/tools/tool-validator.js.map +1 -0
- package/dist/tools/types.d.ts +86 -3
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js +51 -2
- package/dist/tools/types.js.map +1 -1
- package/dist/trace/trace-logger.d.ts +30 -4
- package/dist/trace/trace-logger.d.ts.map +1 -1
- package/dist/trace/trace-logger.js +82 -6
- package/dist/trace/trace-logger.js.map +1 -1
- package/package.json +13 -4
- package/dist/compression/sliding-window.d.ts +0 -21
- package/dist/compression/sliding-window.d.ts.map +0 -1
- package/dist/compression/sliding-window.js +0 -44
- package/dist/compression/sliding-window.js.map +0 -1
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OrchestratorAgent = void 0;
|
|
4
|
+
const agent_1 = require("../core/agent");
|
|
5
|
+
const message_1 = require("../messages/message");
|
|
6
|
+
const types_1 = require("../messages/types");
|
|
7
|
+
const errors_1 = require("../llm/errors");
|
|
8
|
+
const boundaries_1 = require("../security/boundaries");
|
|
9
|
+
const orchestrator_response_1 = require("./orchestrator-response");
|
|
10
|
+
const json_extractor_1 = require("./json-extractor");
|
|
11
|
+
// ─── System Prompt ────────────────────────────────────────────────────────
|
|
12
|
+
const DEFAULT_ORCHESTRATOR_SYSTEM_PROMPT = `You are a helpful AI assistant powered by the Orchestrator paradigm.
|
|
13
|
+
You do NOT execute tasks yourself. Instead, you decompose complex requests into
|
|
14
|
+
sub-tasks, delegate them to specialised sub-agents, synthesise their results,
|
|
15
|
+
and adapt your plan when gaps are found.
|
|
16
|
+
|
|
17
|
+
You have access to tools for discovering and spawning sub-agents.`;
|
|
18
|
+
// ─── OrchestratorAgent ────────────────────────────────────────────────────
|
|
19
|
+
/**
|
|
20
|
+
* Orchestrator Agent that decomposes user requests into a DAG of sub-tasks,
|
|
21
|
+
* dispatches them to sub-agents, synthesises results, and adapts the plan
|
|
22
|
+
* based on what is learned.
|
|
23
|
+
*
|
|
24
|
+
* ## Execution flow:
|
|
25
|
+
* ```
|
|
26
|
+
* User Input
|
|
27
|
+
* ↓
|
|
28
|
+
* [1. Decompose] LLM analyses request → TaskGraph (DAG of sub-agent tasks)
|
|
29
|
+
* ↓
|
|
30
|
+
* ┌─ Loop (up to maxRounds rounds) ─────────────────────────────┐
|
|
31
|
+
* │ │
|
|
32
|
+
* │ [2. Dispatch] Topological execution of ready nodes │
|
|
33
|
+
* │ - Nodes with no pending deps are spawned in parallel │
|
|
34
|
+
* │ - Parent waits for all ready nodes to complete │
|
|
35
|
+
* │ - Results are injected into context │
|
|
36
|
+
* │ │
|
|
37
|
+
* │ [3. Synthesize] LLM reviews all results → isComplete? │
|
|
38
|
+
* │ ├─ YES → return finalAnswer │
|
|
39
|
+
* │ └─ NO → produce gaps list │
|
|
40
|
+
* │ │
|
|
41
|
+
* │ [4. Adapt] LLM turns gaps into new TaskNodes │
|
|
42
|
+
* │ - New nodes appended to task graph │
|
|
43
|
+
* │ - Loop back to Dispatch │
|
|
44
|
+
* │ │
|
|
45
|
+
* └──────────────────────────────────────────────────────────────┘
|
|
46
|
+
* ↓
|
|
47
|
+
* Final Answer
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* ## Session persistence
|
|
51
|
+
* When `enableCheckpointing` is set, the agent saves the full task graph
|
|
52
|
+
* state so orchestration can resume after interruption.
|
|
53
|
+
*/
|
|
54
|
+
class OrchestratorAgent extends agent_1.Agent {
|
|
55
|
+
// ── Configuration ───────────────────────────────────────────────────
|
|
56
|
+
maxRounds;
|
|
57
|
+
maxParallelNodes;
|
|
58
|
+
maxTotalNodes;
|
|
59
|
+
// ── Runtime state ───────────────────────────────────────────────────
|
|
60
|
+
/** The current task graph. */
|
|
61
|
+
taskGraph = { nodes: [] };
|
|
62
|
+
/** How many dispatch rounds have been completed. */
|
|
63
|
+
completedRounds = 0;
|
|
64
|
+
/** Internal flag: when true, run() skips state reset (used by resume()). */
|
|
65
|
+
_skipStateReset = false;
|
|
66
|
+
constructor(config) {
|
|
67
|
+
const mergedConfig = {
|
|
68
|
+
...config,
|
|
69
|
+
systemPrompt: config.systemPrompt ?? DEFAULT_ORCHESTRATOR_SYSTEM_PROMPT,
|
|
70
|
+
};
|
|
71
|
+
super(mergedConfig);
|
|
72
|
+
this.maxRounds = config.maxRounds ?? 3;
|
|
73
|
+
this.maxParallelNodes = config.maxParallelNodes ?? 5;
|
|
74
|
+
this.maxTotalNodes = config.maxTotalNodes ?? 20;
|
|
75
|
+
this.rebuildSystemPrompt();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Return the SubAgentManager, throwing a clear error if sub-agents were
|
|
79
|
+
* not configured. The Orchestrator cannot function without sub-agents.
|
|
80
|
+
*/
|
|
81
|
+
getSubAgentManager() {
|
|
82
|
+
if (!this.subAgentManager) {
|
|
83
|
+
throw new Error("OrchestratorAgent requires sub-agents to be configured. " +
|
|
84
|
+
"Set `subAgentsDir` in OrchestratorAgentConfig with at least one AGENT.md definition.");
|
|
85
|
+
}
|
|
86
|
+
if (!this.subAgentManager.hasDefinitions()) {
|
|
87
|
+
throw new Error("OrchestratorAgent requires at least one sub-agent definition. " +
|
|
88
|
+
`No definitions found in "${this.subAgentsDir}". Add AGENT.md files to register sub-agents.`);
|
|
89
|
+
}
|
|
90
|
+
return this.subAgentManager;
|
|
91
|
+
}
|
|
92
|
+
// ─── Main Entry Point ───────────────────────────────────────────────
|
|
93
|
+
async run(input) {
|
|
94
|
+
const skipStateReset = this._skipStateReset;
|
|
95
|
+
this._skipStateReset = false;
|
|
96
|
+
// ── Pre-flight ────────────────────────────────────────────────────
|
|
97
|
+
const sizeError = this.validateInputSize(input);
|
|
98
|
+
if (sizeError)
|
|
99
|
+
return sizeError;
|
|
100
|
+
this._abortController = new AbortController();
|
|
101
|
+
await this.init();
|
|
102
|
+
await this.reloadDynamicResources();
|
|
103
|
+
this.recoverOrphanedSubAgentResults();
|
|
104
|
+
// Validate that sub-agents are available — the Orchestrator cannot function without them.
|
|
105
|
+
try {
|
|
106
|
+
this.getSubAgentManager();
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
110
|
+
this.logger.error("Orchestrator", message);
|
|
111
|
+
return message;
|
|
112
|
+
}
|
|
113
|
+
const userMessage = message_1.Message.user(input);
|
|
114
|
+
this.contextManager.addMessage(userMessage.toDict());
|
|
115
|
+
if (!skipStateReset) {
|
|
116
|
+
this.taskGraph = { nodes: [] };
|
|
117
|
+
this.completedRounds = 0;
|
|
118
|
+
}
|
|
119
|
+
if (this.checkpointingEnabled) {
|
|
120
|
+
this.saveCheckpoint("active");
|
|
121
|
+
}
|
|
122
|
+
// ── Phase 1: Decompose ─────────────────────────────────────────────
|
|
123
|
+
// On resume (skipStateReset + graph already loaded), skip decomposition.
|
|
124
|
+
if (!skipStateReset || this.taskGraph.nodes.length === 0) {
|
|
125
|
+
const plan = await this.decompose(input);
|
|
126
|
+
this.taskGraph = plan.taskGraph;
|
|
127
|
+
if (this.taskGraph.nodes.length === 0) {
|
|
128
|
+
const fallback = "I was unable to decompose this task into sub-agent actions. " +
|
|
129
|
+
"Please try rephrasing your request with more specific goals.";
|
|
130
|
+
for (const h of this.hooks)
|
|
131
|
+
h.onFinish?.(fallback);
|
|
132
|
+
return fallback;
|
|
133
|
+
}
|
|
134
|
+
this.logger.info("Orchestrator", `Decomposed into ${this.taskGraph.nodes.length} node(s).`);
|
|
135
|
+
for (const n of this.taskGraph.nodes) {
|
|
136
|
+
const depStr = n.dependsOn.length > 0
|
|
137
|
+
? ` (deps: ${n.dependsOn.join(", ")})`
|
|
138
|
+
: "";
|
|
139
|
+
this.logger.info("Orchestrator", ` [${n.id}] → ${n.subAgentName}${depStr}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
this.logger.info("Orchestrator", `Resuming with ${this.taskGraph.nodes.length} node(s) from session.`);
|
|
144
|
+
}
|
|
145
|
+
if (this.checkpointingEnabled) {
|
|
146
|
+
this.saveCheckpoint("active");
|
|
147
|
+
}
|
|
148
|
+
// ── Phase 2-4: Orchestration Loop ──────────────────────────────────
|
|
149
|
+
for (let round = 0; round < this.maxRounds; round++) {
|
|
150
|
+
if (this.isCancelled) {
|
|
151
|
+
this.saveCheckpoint("cancelled");
|
|
152
|
+
const sid = this.sessionManager?.getSessionId() ?? "unknown";
|
|
153
|
+
const cancelMsg = `Execution cancelled by user. Session "${sid}" preserved — ` +
|
|
154
|
+
`resume with agent.resume("${sid}", "<your prompt>").`;
|
|
155
|
+
for (const h of this.hooks)
|
|
156
|
+
h.onFinish?.(cancelMsg);
|
|
157
|
+
return cancelMsg;
|
|
158
|
+
}
|
|
159
|
+
// Dispatch: execute all ready nodes (topological wave)
|
|
160
|
+
await this.dispatchReadyNodes();
|
|
161
|
+
// Synthesize: review all completed results
|
|
162
|
+
const synthesis = await this.synthesize(input);
|
|
163
|
+
this.completedRounds++;
|
|
164
|
+
if (this.checkpointingEnabled) {
|
|
165
|
+
this.saveCheckpoint("active");
|
|
166
|
+
}
|
|
167
|
+
// Check if complete
|
|
168
|
+
if (synthesis.isComplete && synthesis.finalAnswer) {
|
|
169
|
+
this.logger.info("Orchestrator", "Task complete — returning final answer.");
|
|
170
|
+
if (this.checkpointingEnabled) {
|
|
171
|
+
this.saveCheckpoint("completed");
|
|
172
|
+
}
|
|
173
|
+
for (const h of this.hooks)
|
|
174
|
+
h.onFinish?.(synthesis.finalAnswer);
|
|
175
|
+
return synthesis.finalAnswer;
|
|
176
|
+
}
|
|
177
|
+
// Check if this was the last round
|
|
178
|
+
if (round === this.maxRounds - 1) {
|
|
179
|
+
this.logger.info("Orchestrator", `Max rounds (${this.maxRounds}) reached — forcing synthesis.`);
|
|
180
|
+
const forced = await this.forceSynthesize(input);
|
|
181
|
+
if (this.checkpointingEnabled) {
|
|
182
|
+
this.saveCheckpoint("completed");
|
|
183
|
+
}
|
|
184
|
+
for (const h of this.hooks)
|
|
185
|
+
h.onFinish?.(forced);
|
|
186
|
+
return forced;
|
|
187
|
+
}
|
|
188
|
+
// Check total node limit
|
|
189
|
+
if (this.taskGraph.nodes.length >= this.maxTotalNodes) {
|
|
190
|
+
this.logger.info("Orchestrator", `Max total nodes (${this.maxTotalNodes}) reached — forcing synthesis.`);
|
|
191
|
+
const forced = await this.forceSynthesize(input);
|
|
192
|
+
if (this.checkpointingEnabled) {
|
|
193
|
+
this.saveCheckpoint("completed");
|
|
194
|
+
}
|
|
195
|
+
for (const h of this.hooks)
|
|
196
|
+
h.onFinish?.(forced);
|
|
197
|
+
return forced;
|
|
198
|
+
}
|
|
199
|
+
// Adapt: generate new nodes for gaps
|
|
200
|
+
const gaps = synthesis.gaps ?? [];
|
|
201
|
+
if (gaps.length === 0) {
|
|
202
|
+
// No gaps but not complete — force synthesis
|
|
203
|
+
this.logger.info("Orchestrator", "Synthesis incomplete but no gaps listed — forcing synthesis.");
|
|
204
|
+
const forced = await this.forceSynthesize(input);
|
|
205
|
+
if (this.checkpointingEnabled) {
|
|
206
|
+
this.saveCheckpoint("completed");
|
|
207
|
+
}
|
|
208
|
+
for (const h of this.hooks)
|
|
209
|
+
h.onFinish?.(forced);
|
|
210
|
+
return forced;
|
|
211
|
+
}
|
|
212
|
+
const adaptResult = await this.adapt(gaps);
|
|
213
|
+
if (adaptResult.stuck || adaptResult.newNodes.length === 0) {
|
|
214
|
+
this.logger.info("Orchestrator", "Adapt phase stuck — forcing synthesis.");
|
|
215
|
+
const forced = await this.forceSynthesize(input);
|
|
216
|
+
if (this.checkpointingEnabled) {
|
|
217
|
+
this.saveCheckpoint("completed");
|
|
218
|
+
}
|
|
219
|
+
for (const h of this.hooks)
|
|
220
|
+
h.onFinish?.(forced);
|
|
221
|
+
return forced;
|
|
222
|
+
}
|
|
223
|
+
// Append new nodes to the graph
|
|
224
|
+
this.taskGraph.nodes.push(...adaptResult.newNodes);
|
|
225
|
+
this.logger.info("Orchestrator", `Round ${this.completedRounds + 1}: ${adaptResult.newNodes.length} new node(s) added.`);
|
|
226
|
+
if (this.checkpointingEnabled) {
|
|
227
|
+
this.saveCheckpoint("active");
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// Should not reach here — caught by last-round check above
|
|
231
|
+
const forced = await this.forceSynthesize(input);
|
|
232
|
+
for (const h of this.hooks)
|
|
233
|
+
h.onFinish?.(forced);
|
|
234
|
+
return forced;
|
|
235
|
+
}
|
|
236
|
+
// ─── Phase 1: Decompose ──────────────────────────────────────────────
|
|
237
|
+
/**
|
|
238
|
+
* Ask the LLM to decompose the user's request into a TaskGraph.
|
|
239
|
+
*
|
|
240
|
+
* Sends a dedicated prompt with the list of available sub-agents so the
|
|
241
|
+
* LLM knows what it can delegate to.
|
|
242
|
+
*/
|
|
243
|
+
async decompose(input) {
|
|
244
|
+
const availableSubAgents = this.getSubAgentManager().buildSubAgentList();
|
|
245
|
+
const messages = [
|
|
246
|
+
{ role: types_1.Role.System, content: (0, orchestrator_response_1.buildDecomposePrompt)(availableSubAgents) },
|
|
247
|
+
{ role: types_1.Role.User, content: input },
|
|
248
|
+
];
|
|
249
|
+
for (const h of this.hooks)
|
|
250
|
+
h.onLLMStart?.(messages, []);
|
|
251
|
+
let response;
|
|
252
|
+
try {
|
|
253
|
+
response = await this.llm.chat(messages, [], this._abortController?.signal);
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
if (this.isCancelled) {
|
|
257
|
+
this.saveCheckpoint("cancelled");
|
|
258
|
+
const cancelMsg = `Execution cancelled by user. Session "${this.sessionManager?.getSessionId() ?? "unknown"}" preserved.`;
|
|
259
|
+
for (const h of this.hooks)
|
|
260
|
+
h.onFinish?.(cancelMsg);
|
|
261
|
+
return { thought: "Cancelled.", taskGraph: { nodes: [] } };
|
|
262
|
+
}
|
|
263
|
+
if (err instanceof errors_1.LLMNetworkError) {
|
|
264
|
+
for (const h of this.hooks)
|
|
265
|
+
h.onLLMError?.(err);
|
|
266
|
+
const recovered = await this.handleNetworkError(err, 0, "continue creating a decomposition plan");
|
|
267
|
+
// If recovery returned a string, wrap it
|
|
268
|
+
if (typeof recovered === "string") {
|
|
269
|
+
return { thought: recovered, taskGraph: { nodes: [] } };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
throw err;
|
|
273
|
+
}
|
|
274
|
+
for (const h of this.hooks)
|
|
275
|
+
h.onLLMEnd?.(response);
|
|
276
|
+
if (response.usage) {
|
|
277
|
+
this.tokenBudget?.recordUsage(response.usage.prompt_tokens, response.usage.completion_tokens);
|
|
278
|
+
}
|
|
279
|
+
const parsed = (0, orchestrator_response_1.parseDecomposeResponse)(response.content);
|
|
280
|
+
// Validate nodes: ensure every dependsOn references a real node ID
|
|
281
|
+
const nodeIds = new Set(parsed.taskGraph.nodes.map((n) => n.id));
|
|
282
|
+
for (const node of parsed.taskGraph.nodes) {
|
|
283
|
+
for (const dep of node.dependsOn) {
|
|
284
|
+
if (!nodeIds.has(dep)) {
|
|
285
|
+
this.logger.warn("Orchestrator", `Node "${node.id}" depends on unknown node "${dep}" — removing dependency.`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Filter out unknown dependencies
|
|
289
|
+
node.dependsOn = node.dependsOn.filter((d) => nodeIds.has(d));
|
|
290
|
+
}
|
|
291
|
+
if (parsed.thought) {
|
|
292
|
+
this.logger.info("Orchestrator", `Decompose: ${parsed.thought}`);
|
|
293
|
+
for (const h of this.hooks)
|
|
294
|
+
h.onThought?.(parsed.thought);
|
|
295
|
+
}
|
|
296
|
+
return parsed;
|
|
297
|
+
}
|
|
298
|
+
// ─── Phase 2: Dispatch ────────────────────────────────────────────────
|
|
299
|
+
/**
|
|
300
|
+
* Execute all ready nodes in the task graph using topological wave-front
|
|
301
|
+
* dispatch. Nodes with no pending dependencies run in parallel (up to
|
|
302
|
+
* `maxParallelNodes`), then their dependants become ready.
|
|
303
|
+
*
|
|
304
|
+
* This method blocks until all currently ready (and transitively ready)
|
|
305
|
+
* nodes have completed.
|
|
306
|
+
*/
|
|
307
|
+
async dispatchReadyNodes() {
|
|
308
|
+
// Keep dispatching waves until no more nodes can make progress
|
|
309
|
+
let progress = true;
|
|
310
|
+
while (progress) {
|
|
311
|
+
progress = false;
|
|
312
|
+
// Find nodes that are ready to run
|
|
313
|
+
const readyNodes = this.getReadyNodes();
|
|
314
|
+
if (readyNodes.length === 0)
|
|
315
|
+
break;
|
|
316
|
+
progress = true;
|
|
317
|
+
// Spawn in parallel, respecting maxParallelNodes
|
|
318
|
+
this.logger.info("Orchestrator", `Dispatching ${readyNodes.length} ready node(s).`);
|
|
319
|
+
for (const node of readyNodes) {
|
|
320
|
+
node.status = "running";
|
|
321
|
+
node.startedAt = Date.now();
|
|
322
|
+
// Resolve template variables in the input
|
|
323
|
+
const resolvedInput = this.resolveInputTemplate(node);
|
|
324
|
+
try {
|
|
325
|
+
const runId = this.getSubAgentManager().spawn(node.subAgentName, resolvedInput);
|
|
326
|
+
this.logger.info("Orchestrator", ` Spawned [${node.id}] → ${node.subAgentName} (${runId})`);
|
|
327
|
+
}
|
|
328
|
+
catch (err) {
|
|
329
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
330
|
+
this.logger.warn("Orchestrator", ` Failed to spawn [${node.id}]: ${message}`);
|
|
331
|
+
node.status = "failed";
|
|
332
|
+
node.result = {
|
|
333
|
+
subAgentId: `error_${node.id}`,
|
|
334
|
+
name: node.subAgentName,
|
|
335
|
+
success: false,
|
|
336
|
+
output: `Failed to spawn: ${message}`,
|
|
337
|
+
durationMs: 0,
|
|
338
|
+
};
|
|
339
|
+
node.durationMs = 0;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// Poll until all dispatched nodes in this wave complete
|
|
343
|
+
await this.pollUntilNodesComplete(readyNodes);
|
|
344
|
+
// Inject completed results into context
|
|
345
|
+
for (const node of readyNodes) {
|
|
346
|
+
if (node.result) {
|
|
347
|
+
const source = `subagent:${node.subAgentName}:${node.id}`;
|
|
348
|
+
const msg = new message_1.Message(types_1.Role.User, (0, boundaries_1.wrapAndScan)(source, node.result.output), { name: source });
|
|
349
|
+
this.contextManager.addMessage(msg.toDict());
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Return nodes whose dependencies are all completed and that haven't
|
|
356
|
+
* been dispatched yet, limited by maxParallelNodes.
|
|
357
|
+
*/
|
|
358
|
+
getReadyNodes() {
|
|
359
|
+
const completedIds = new Set(this.taskGraph.nodes
|
|
360
|
+
.filter((n) => n.status === "completed")
|
|
361
|
+
.map((n) => n.id));
|
|
362
|
+
const ready = [];
|
|
363
|
+
for (const node of this.taskGraph.nodes) {
|
|
364
|
+
if (node.status !== "pending")
|
|
365
|
+
continue;
|
|
366
|
+
const allDepsSatisfied = node.dependsOn.every((depId) => {
|
|
367
|
+
// A "failed" node still satisfies the dependency (we don't want to
|
|
368
|
+
// deadlock), but the downstream node will see the error in its input.
|
|
369
|
+
const dep = this.taskGraph.nodes.find((n) => n.id === depId);
|
|
370
|
+
return dep && (dep.status === "completed" || dep.status === "failed");
|
|
371
|
+
});
|
|
372
|
+
if (allDepsSatisfied) {
|
|
373
|
+
ready.push(node);
|
|
374
|
+
if (ready.length >= this.maxParallelNodes)
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return ready;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Busy-wait until all given nodes have resolved (completed or failed).
|
|
382
|
+
*/
|
|
383
|
+
async pollUntilNodesComplete(nodes) {
|
|
384
|
+
const targetIds = new Set(nodes.map((n) => n.id));
|
|
385
|
+
while (true) {
|
|
386
|
+
// Poll the sub-agent manager for any completed results
|
|
387
|
+
const results = await this.getSubAgentManager().pollCompleted();
|
|
388
|
+
// Match results to our task nodes
|
|
389
|
+
for (const result of results) {
|
|
390
|
+
// Find the node by matching the subAgentId pattern
|
|
391
|
+
for (const node of nodes) {
|
|
392
|
+
if (node.status === "running" && !node.result) {
|
|
393
|
+
// Match by sub-agent name + result timing
|
|
394
|
+
if (result.name === node.subAgentName) {
|
|
395
|
+
node.result = result;
|
|
396
|
+
node.status = result.success ? "completed" : "failed";
|
|
397
|
+
node.durationMs = result.durationMs;
|
|
398
|
+
this.logger.info("Orchestrator", ` [${node.id}] ${node.status} (${node.durationMs}ms)`);
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// Check if all target nodes are done
|
|
405
|
+
const allDone = nodes.every((n) => n.status === "completed" || n.status === "failed");
|
|
406
|
+
if (allDone)
|
|
407
|
+
break;
|
|
408
|
+
// Small delay before next poll
|
|
409
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Resolve `{{node_id.output}}` template references in a node's input.
|
|
414
|
+
*/
|
|
415
|
+
resolveInputTemplate(node) {
|
|
416
|
+
return node.input.replace(/\{\{(\w+)\.output\}\}/g, (_match, refId) => {
|
|
417
|
+
const refNode = this.taskGraph.nodes.find((n) => n.id === refId);
|
|
418
|
+
if (refNode?.result) {
|
|
419
|
+
return refNode.result.output;
|
|
420
|
+
}
|
|
421
|
+
if (refNode?.status === "failed") {
|
|
422
|
+
return `[Node "${refId}" failed: ${refNode.result?.output ?? "unknown error"}]`;
|
|
423
|
+
}
|
|
424
|
+
return `[Reference to unavailable node: ${refId}]`;
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
// ─── Phase 3: Synthesize ──────────────────────────────────────────────
|
|
428
|
+
/**
|
|
429
|
+
* Ask the LLM to review all completed node results and determine whether
|
|
430
|
+
* the information is sufficient to answer the user.
|
|
431
|
+
*/
|
|
432
|
+
async synthesize(userInput) {
|
|
433
|
+
const completedResults = this.formatCompletedResults();
|
|
434
|
+
if (!completedResults) {
|
|
435
|
+
return {
|
|
436
|
+
thought: "No nodes have completed.",
|
|
437
|
+
isComplete: false,
|
|
438
|
+
gaps: ["No sub-agent results available — retry decomposition."],
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
const prompt = (0, orchestrator_response_1.buildSynthesizePrompt)(userInput, completedResults);
|
|
442
|
+
const messages = [
|
|
443
|
+
{ role: types_1.Role.System, content: prompt },
|
|
444
|
+
];
|
|
445
|
+
for (const h of this.hooks)
|
|
446
|
+
h.onLLMStart?.(messages, []);
|
|
447
|
+
let response;
|
|
448
|
+
try {
|
|
449
|
+
response = await this.llm.chat(messages, [], this._abortController?.signal);
|
|
450
|
+
}
|
|
451
|
+
catch (err) {
|
|
452
|
+
if (this.isCancelled) {
|
|
453
|
+
return { thought: "Cancelled.", isComplete: false, gaps: [] };
|
|
454
|
+
}
|
|
455
|
+
if (err instanceof errors_1.LLMNetworkError) {
|
|
456
|
+
for (const h of this.hooks)
|
|
457
|
+
h.onLLMError?.(err);
|
|
458
|
+
return {
|
|
459
|
+
thought: `Network error during synthesis: ${err.message}`,
|
|
460
|
+
isComplete: false,
|
|
461
|
+
gaps: ["Synthesis failed due to network error — retry."],
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
throw err;
|
|
465
|
+
}
|
|
466
|
+
for (const h of this.hooks)
|
|
467
|
+
h.onLLMEnd?.(response);
|
|
468
|
+
if (response.usage) {
|
|
469
|
+
this.tokenBudget?.recordUsage(response.usage.prompt_tokens, response.usage.completion_tokens);
|
|
470
|
+
}
|
|
471
|
+
const parsed = (0, orchestrator_response_1.parseSynthesizeResponse)(response.content);
|
|
472
|
+
if (parsed.thought) {
|
|
473
|
+
this.logger.info("Orchestrator", `Synthesize: ${parsed.thought}`);
|
|
474
|
+
}
|
|
475
|
+
return parsed;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Build a formatted string of all completed node results for the
|
|
479
|
+
* synthesis LLM prompt.
|
|
480
|
+
*/
|
|
481
|
+
formatCompletedResults() {
|
|
482
|
+
const completed = this.taskGraph.nodes.filter((n) => n.status === "completed" || n.status === "failed");
|
|
483
|
+
if (completed.length === 0)
|
|
484
|
+
return "";
|
|
485
|
+
const parts = [];
|
|
486
|
+
for (const node of completed) {
|
|
487
|
+
const header = node.status === "completed"
|
|
488
|
+
? `=== [${node.id}] ${node.description} (SUCCESS) ===`
|
|
489
|
+
: `=== [${node.id}] ${node.description} (FAILED) ===`;
|
|
490
|
+
const body = node.result?.output ?? "(no output)";
|
|
491
|
+
parts.push(`${header}\n${body}\n`);
|
|
492
|
+
}
|
|
493
|
+
return parts.join("\n\n");
|
|
494
|
+
}
|
|
495
|
+
// ─── Phase 4: Adapt ──────────────────────────────────────────────────
|
|
496
|
+
/**
|
|
497
|
+
* Ask the LLM to generate new task nodes to fill the gaps identified
|
|
498
|
+
* during synthesis.
|
|
499
|
+
*/
|
|
500
|
+
async adapt(gaps) {
|
|
501
|
+
const availableSubAgents = this.getSubAgentManager().buildSubAgentList();
|
|
502
|
+
const prompt = (0, orchestrator_response_1.buildAdaptPrompt)(gaps, availableSubAgents);
|
|
503
|
+
const messages = [
|
|
504
|
+
{ role: types_1.Role.System, content: prompt },
|
|
505
|
+
];
|
|
506
|
+
for (const h of this.hooks)
|
|
507
|
+
h.onLLMStart?.(messages, []);
|
|
508
|
+
let response;
|
|
509
|
+
try {
|
|
510
|
+
response = await this.llm.chat(messages, [], this._abortController?.signal);
|
|
511
|
+
}
|
|
512
|
+
catch (err) {
|
|
513
|
+
if (this.isCancelled) {
|
|
514
|
+
return { thought: "Cancelled.", newNodes: [], stuck: true };
|
|
515
|
+
}
|
|
516
|
+
if (err instanceof errors_1.LLMNetworkError) {
|
|
517
|
+
for (const h of this.hooks)
|
|
518
|
+
h.onLLMError?.(err);
|
|
519
|
+
return {
|
|
520
|
+
thought: `Network error during adapt: ${err.message}`,
|
|
521
|
+
newNodes: [],
|
|
522
|
+
stuck: true,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
throw err;
|
|
526
|
+
}
|
|
527
|
+
for (const h of this.hooks)
|
|
528
|
+
h.onLLMEnd?.(response);
|
|
529
|
+
if (response.usage) {
|
|
530
|
+
this.tokenBudget?.recordUsage(response.usage.prompt_tokens, response.usage.completion_tokens);
|
|
531
|
+
}
|
|
532
|
+
const parsed = (0, orchestrator_response_1.parseAdaptResponse)(response.content);
|
|
533
|
+
// Validate new nodes
|
|
534
|
+
const existingIds = new Set(this.taskGraph.nodes.map((n) => n.id));
|
|
535
|
+
for (const node of parsed.newNodes) {
|
|
536
|
+
// Ensure unique IDs
|
|
537
|
+
if (existingIds.has(node.id)) {
|
|
538
|
+
node.id = `${node.id}_r${this.completedRounds}`;
|
|
539
|
+
}
|
|
540
|
+
existingIds.add(node.id);
|
|
541
|
+
// Filter unknown dependencies
|
|
542
|
+
node.dependsOn = node.dependsOn.filter((d) => existingIds.has(d));
|
|
543
|
+
}
|
|
544
|
+
if (parsed.thought) {
|
|
545
|
+
this.logger.info("Orchestrator", `Adapt: ${parsed.thought}`);
|
|
546
|
+
for (const h of this.hooks)
|
|
547
|
+
h.onThought?.(parsed.thought);
|
|
548
|
+
}
|
|
549
|
+
return parsed;
|
|
550
|
+
}
|
|
551
|
+
// ─── Force Synthesis ─────────────────────────────────────────────────
|
|
552
|
+
/**
|
|
553
|
+
* Force a final synthesis when rounds are exhausted or the orchestrator
|
|
554
|
+
* is stuck. Asks the LLM to produce the best answer it can with whatever
|
|
555
|
+
* results are available.
|
|
556
|
+
*/
|
|
557
|
+
async forceSynthesize(userInput) {
|
|
558
|
+
const completedResults = this.formatCompletedResults();
|
|
559
|
+
if (!completedResults) {
|
|
560
|
+
return "I was unable to complete the task — no sub-agent results were produced.";
|
|
561
|
+
}
|
|
562
|
+
const prompt = `You are a synthesiser producing a FINAL answer. The orchestrator has
|
|
563
|
+
stopped (either max rounds reached or no more useful work can be devised).
|
|
564
|
+
Using the sub-agent results below, provide the BEST answer you can to the
|
|
565
|
+
user's original request. Be honest about any limitations or incomplete information.
|
|
566
|
+
|
|
567
|
+
=== User's Original Request ===
|
|
568
|
+
${userInput}
|
|
569
|
+
|
|
570
|
+
=== Sub-Agent Results ===
|
|
571
|
+
${completedResults}
|
|
572
|
+
|
|
573
|
+
Respond with ONLY a JSON object:
|
|
574
|
+
{
|
|
575
|
+
"thought": "<your analysis of what was accomplished and what is missing>",
|
|
576
|
+
"answer": "<the best answer you can provide>"
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
Rules:
|
|
580
|
+
- Include "thought" covering both what was learned AND what remains uncertain.
|
|
581
|
+
- The "answer" should be thorough but honest about gaps.
|
|
582
|
+
- The JSON must be valid — no trailing commas, no comments.`;
|
|
583
|
+
const messages = [
|
|
584
|
+
{ role: types_1.Role.System, content: prompt },
|
|
585
|
+
];
|
|
586
|
+
for (const h of this.hooks)
|
|
587
|
+
h.onLLMStart?.(messages, []);
|
|
588
|
+
let response;
|
|
589
|
+
try {
|
|
590
|
+
response = await this.llm.chat(messages, [], this._abortController?.signal);
|
|
591
|
+
}
|
|
592
|
+
catch (err) {
|
|
593
|
+
if (err instanceof errors_1.LLMNetworkError) {
|
|
594
|
+
for (const h of this.hooks)
|
|
595
|
+
h.onLLMError?.(err);
|
|
596
|
+
return `Network error during final synthesis: ${err.message}. ` +
|
|
597
|
+
`Partial results are preserved in the session.`;
|
|
598
|
+
}
|
|
599
|
+
throw err;
|
|
600
|
+
}
|
|
601
|
+
for (const h of this.hooks)
|
|
602
|
+
h.onLLMEnd?.(response);
|
|
603
|
+
if (response.usage) {
|
|
604
|
+
this.tokenBudget?.recordUsage(response.usage.prompt_tokens, response.usage.completion_tokens);
|
|
605
|
+
}
|
|
606
|
+
// Parse with the standard ReAct response format (thought + answer)
|
|
607
|
+
const json = (0, json_extractor_1.extractJSON)(response.content);
|
|
608
|
+
if (json) {
|
|
609
|
+
try {
|
|
610
|
+
const parsed = JSON.parse(json);
|
|
611
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
612
|
+
const thought = String(parsed.thought ?? "");
|
|
613
|
+
const answer = String(parsed.answer ?? response.content);
|
|
614
|
+
this.logger.info("Orchestrator", `Force synthesize: ${thought}`);
|
|
615
|
+
return answer;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
catch {
|
|
619
|
+
// Fall through
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return response.content;
|
|
623
|
+
}
|
|
624
|
+
// ─── Session Persistence ────────────────────────────────────────────
|
|
625
|
+
/**
|
|
626
|
+
* Agent type identifier for session metadata.
|
|
627
|
+
*/
|
|
628
|
+
getAgentType() {
|
|
629
|
+
return "orchestrator";
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Include orchestrator state in session checkpoints.
|
|
633
|
+
*/
|
|
634
|
+
buildBaseSessionState(status) {
|
|
635
|
+
const base = super.buildBaseSessionState(status);
|
|
636
|
+
return {
|
|
637
|
+
...base,
|
|
638
|
+
planState: undefined,
|
|
639
|
+
fusionState: undefined,
|
|
640
|
+
orchestratorState: {
|
|
641
|
+
taskGraph: this.taskGraph,
|
|
642
|
+
completedRounds: this.completedRounds,
|
|
643
|
+
},
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Restore orchestrator state from a saved session.
|
|
648
|
+
*/
|
|
649
|
+
loadAndRestoreSession(sessionId) {
|
|
650
|
+
const state = super.loadAndRestoreSession(sessionId);
|
|
651
|
+
if (state.orchestratorState) {
|
|
652
|
+
const os = state.orchestratorState;
|
|
653
|
+
this.taskGraph = os.taskGraph;
|
|
654
|
+
this.completedRounds = os.completedRounds;
|
|
655
|
+
}
|
|
656
|
+
return state;
|
|
657
|
+
}
|
|
658
|
+
// ─── Resume ─────────────────────────────────────────────────────────
|
|
659
|
+
/**
|
|
660
|
+
* Resume a previously interrupted orchestration session.
|
|
661
|
+
*
|
|
662
|
+
* Restores messages, system prompt, and the full task graph so the
|
|
663
|
+
* orchestrator can continue from where it left off.
|
|
664
|
+
*
|
|
665
|
+
* @param sessionId The session ID to resume.
|
|
666
|
+
* @param input New user input to continue the conversation.
|
|
667
|
+
*/
|
|
668
|
+
async resume(sessionId, input) {
|
|
669
|
+
this.loadAndRestoreSession(sessionId);
|
|
670
|
+
this._skipStateReset = true;
|
|
671
|
+
return this.run(input);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
exports.OrchestratorAgent = OrchestratorAgent;
|
|
675
|
+
//# sourceMappingURL=orchestrator-agent.js.map
|