@opengoat/core 2026.2.9
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 +21 -0
- package/README.md +20 -0
- package/dist/apps/runtime/create-opengoat-runtime.d.ts +14 -0
- package/dist/apps/runtime/create-opengoat-runtime.js +23 -0
- package/dist/apps/runtime/create-opengoat-runtime.js.map +1 -0
- package/dist/core/acp/application/acp-agent.d.ts +60 -0
- package/dist/core/acp/application/acp-agent.js +424 -0
- package/dist/core/acp/application/acp-agent.js.map +1 -0
- package/dist/core/acp/application/session-store.d.ts +13 -0
- package/dist/core/acp/application/session-store.js +40 -0
- package/dist/core/acp/application/session-store.js.map +1 -0
- package/dist/core/acp/domain/meta.d.ts +7 -0
- package/dist/core/acp/domain/meta.js +31 -0
- package/dist/core/acp/domain/meta.js.map +1 -0
- package/dist/core/acp/domain/session.d.ts +23 -0
- package/dist/core/acp/domain/session.js +2 -0
- package/dist/core/acp/domain/session.js.map +1 -0
- package/dist/core/acp/index.d.ts +5 -0
- package/dist/core/acp/index.js +4 -0
- package/dist/core/acp/index.js.map +1 -0
- package/dist/core/agents/application/agent-manifest.service.d.ts +20 -0
- package/dist/core/agents/application/agent-manifest.service.js +89 -0
- package/dist/core/agents/application/agent-manifest.service.js.map +1 -0
- package/dist/core/agents/application/agent.service.d.ts +25 -0
- package/dist/core/agents/application/agent.service.js +191 -0
- package/dist/core/agents/application/agent.service.js.map +1 -0
- package/dist/core/agents/application/workspace-context.service.d.ts +28 -0
- package/dist/core/agents/application/workspace-context.service.js +157 -0
- package/dist/core/agents/application/workspace-context.service.js.map +1 -0
- package/dist/core/agents/domain/agent-manifest.d.ts +37 -0
- package/dist/core/agents/domain/agent-manifest.js +228 -0
- package/dist/core/agents/domain/agent-manifest.js.map +1 -0
- package/dist/core/agents/domain/workspace-context.d.ts +13 -0
- package/dist/core/agents/domain/workspace-context.js +14 -0
- package/dist/core/agents/domain/workspace-context.js.map +1 -0
- package/dist/core/agents/index.d.ts +6 -0
- package/dist/core/agents/index.js +6 -0
- package/dist/core/agents/index.js.map +1 -0
- package/dist/core/bootstrap/application/bootstrap.service.d.ts +24 -0
- package/dist/core/bootstrap/application/bootstrap.service.js +108 -0
- package/dist/core/bootstrap/application/bootstrap.service.js.map +1 -0
- package/dist/core/bootstrap/index.d.ts +1 -0
- package/dist/core/bootstrap/index.js +2 -0
- package/dist/core/bootstrap/index.js.map +1 -0
- package/dist/core/domain/agent-id.d.ts +3 -0
- package/dist/core/domain/agent-id.js +12 -0
- package/dist/core/domain/agent-id.js.map +1 -0
- package/dist/core/domain/agent.d.ts +39 -0
- package/dist/core/domain/agent.js +2 -0
- package/dist/core/domain/agent.js.map +1 -0
- package/dist/core/domain/opengoat-paths.d.ts +29 -0
- package/dist/core/domain/opengoat-paths.js +2 -0
- package/dist/core/domain/opengoat-paths.js.map +1 -0
- package/dist/core/gateway/domain/protocol.d.ts +113 -0
- package/dist/core/gateway/domain/protocol.js +394 -0
- package/dist/core/gateway/domain/protocol.js.map +1 -0
- package/dist/core/gateway/index.d.ts +2 -0
- package/dist/core/gateway/index.js +2 -0
- package/dist/core/gateway/index.js.map +1 -0
- package/dist/core/llm/application/vercel-ai-text-runtime.d.ts +26 -0
- package/dist/core/llm/application/vercel-ai-text-runtime.js +223 -0
- package/dist/core/llm/application/vercel-ai-text-runtime.js.map +1 -0
- package/dist/core/llm/domain/text-runtime.d.ts +22 -0
- package/dist/core/llm/domain/text-runtime.js +2 -0
- package/dist/core/llm/domain/text-runtime.js.map +1 -0
- package/dist/core/llm/index.d.ts +2 -0
- package/dist/core/llm/index.js +2 -0
- package/dist/core/llm/index.js.map +1 -0
- package/dist/core/logging/application/structured-logger.d.ts +29 -0
- package/dist/core/logging/application/structured-logger.js +86 -0
- package/dist/core/logging/application/structured-logger.js.map +1 -0
- package/dist/core/logging/domain/logger.d.ts +16 -0
- package/dist/core/logging/domain/logger.js +18 -0
- package/dist/core/logging/domain/logger.js.map +1 -0
- package/dist/core/logging/index.d.ts +3 -0
- package/dist/core/logging/index.js +3 -0
- package/dist/core/logging/index.js.map +1 -0
- package/dist/core/opengoat/application/opengoat.service.d.ts +84 -0
- package/dist/core/opengoat/application/opengoat.service.js +308 -0
- package/dist/core/opengoat/application/opengoat.service.js.map +1 -0
- package/dist/core/opengoat/index.d.ts +1 -0
- package/dist/core/opengoat/index.js +2 -0
- package/dist/core/opengoat/index.js.map +1 -0
- package/dist/core/orchestration/application/orchestration-planner.service.d.ts +28 -0
- package/dist/core/orchestration/application/orchestration-planner.service.js +279 -0
- package/dist/core/orchestration/application/orchestration-planner.service.js.map +1 -0
- package/dist/core/orchestration/application/orchestration.service.d.ts +52 -0
- package/dist/core/orchestration/application/orchestration.service.js +1044 -0
- package/dist/core/orchestration/application/orchestration.service.js.map +1 -0
- package/dist/core/orchestration/application/routing.service.d.ts +11 -0
- package/dist/core/orchestration/application/routing.service.js +108 -0
- package/dist/core/orchestration/application/routing.service.js.map +1 -0
- package/dist/core/orchestration/domain/loop.d.ts +119 -0
- package/dist/core/orchestration/domain/loop.js +2 -0
- package/dist/core/orchestration/domain/loop.js.map +1 -0
- package/dist/core/orchestration/domain/routing.d.ts +58 -0
- package/dist/core/orchestration/domain/routing.js +2 -0
- package/dist/core/orchestration/domain/routing.js.map +1 -0
- package/dist/core/orchestration/domain/run-events.d.ts +21 -0
- package/dist/core/orchestration/domain/run-events.js +2 -0
- package/dist/core/orchestration/domain/run-events.js.map +1 -0
- package/dist/core/orchestration/index.d.ts +6 -0
- package/dist/core/orchestration/index.js +4 -0
- package/dist/core/orchestration/index.js.map +1 -0
- package/dist/core/plugins/application/plugin.service.d.ts +32 -0
- package/dist/core/plugins/application/plugin.service.js +236 -0
- package/dist/core/plugins/application/plugin.service.js.map +1 -0
- package/dist/core/plugins/domain/openclaw-compat.d.ts +60 -0
- package/dist/core/plugins/domain/openclaw-compat.js +9 -0
- package/dist/core/plugins/domain/openclaw-compat.js.map +1 -0
- package/dist/core/plugins/index.d.ts +3 -0
- package/dist/core/plugins/index.js +3 -0
- package/dist/core/plugins/index.js.map +1 -0
- package/dist/core/ports/command-runner.port.d.ts +14 -0
- package/dist/core/ports/command-runner.port.js +2 -0
- package/dist/core/ports/command-runner.port.js.map +1 -0
- package/dist/core/ports/file-system.port.d.ts +9 -0
- package/dist/core/ports/file-system.port.js +2 -0
- package/dist/core/ports/file-system.port.js.map +1 -0
- package/dist/core/ports/path.port.d.ts +3 -0
- package/dist/core/ports/path.port.js +2 -0
- package/dist/core/ports/path.port.js.map +1 -0
- package/dist/core/ports/paths-provider.port.d.ts +4 -0
- package/dist/core/ports/paths-provider.port.js +2 -0
- package/dist/core/ports/paths-provider.port.js.map +1 -0
- package/dist/core/providers/application/provider.service.d.ts +80 -0
- package/dist/core/providers/application/provider.service.js +391 -0
- package/dist/core/providers/application/provider.service.js.map +1 -0
- package/dist/core/providers/base-provider.d.ts +20 -0
- package/dist/core/providers/base-provider.js +44 -0
- package/dist/core/providers/base-provider.js.map +1 -0
- package/dist/core/providers/cli-provider.d.ts +26 -0
- package/dist/core/providers/cli-provider.js +151 -0
- package/dist/core/providers/cli-provider.js.map +1 -0
- package/dist/core/providers/command-executor.d.ts +12 -0
- package/dist/core/providers/command-executor.js +76 -0
- package/dist/core/providers/command-executor.js.map +1 -0
- package/dist/core/providers/errors.d.ts +30 -0
- package/dist/core/providers/errors.js +52 -0
- package/dist/core/providers/errors.js.map +1 -0
- package/dist/core/providers/index.d.ts +18 -0
- package/dist/core/providers/index.js +27 -0
- package/dist/core/providers/index.js.map +1 -0
- package/dist/core/providers/loader.d.ts +2 -0
- package/dist/core/providers/loader.js +99 -0
- package/dist/core/providers/loader.js.map +1 -0
- package/dist/core/providers/onboarding.d.ts +13 -0
- package/dist/core/providers/onboarding.js +149 -0
- package/dist/core/providers/onboarding.js.map +1 -0
- package/dist/core/providers/provider-module.d.ts +19 -0
- package/dist/core/providers/provider-module.js +2 -0
- package/dist/core/providers/provider-module.js.map +1 -0
- package/dist/core/providers/provider-session.d.ts +3 -0
- package/dist/core/providers/provider-session.js +44 -0
- package/dist/core/providers/provider-session.js.map +1 -0
- package/dist/core/providers/providers/claude/index.d.ts +4 -0
- package/dist/core/providers/providers/claude/index.js +19 -0
- package/dist/core/providers/providers/claude/index.js.map +1 -0
- package/dist/core/providers/providers/claude/provider.d.ts +8 -0
- package/dist/core/providers/providers/claude/provider.js +106 -0
- package/dist/core/providers/providers/claude/provider.js.map +1 -0
- package/dist/core/providers/providers/codex/index.d.ts +4 -0
- package/dist/core/providers/providers/codex/index.js +19 -0
- package/dist/core/providers/providers/codex/index.js.map +1 -0
- package/dist/core/providers/providers/codex/provider.d.ts +7 -0
- package/dist/core/providers/providers/codex/provider.js +31 -0
- package/dist/core/providers/providers/codex/provider.js.map +1 -0
- package/dist/core/providers/providers/cursor/index.d.ts +4 -0
- package/dist/core/providers/providers/cursor/index.js +19 -0
- package/dist/core/providers/providers/cursor/index.js.map +1 -0
- package/dist/core/providers/providers/cursor/provider.d.ts +11 -0
- package/dist/core/providers/providers/cursor/provider.js +90 -0
- package/dist/core/providers/providers/cursor/provider.js.map +1 -0
- package/dist/core/providers/providers/extended-http/catalog.d.ts +40 -0
- package/dist/core/providers/providers/extended-http/catalog.js +728 -0
- package/dist/core/providers/providers/extended-http/catalog.js.map +1 -0
- package/dist/core/providers/providers/extended-http/index.d.ts +5 -0
- package/dist/core/providers/providers/extended-http/index.js +13 -0
- package/dist/core/providers/providers/extended-http/index.js.map +1 -0
- package/dist/core/providers/providers/extended-http/provider.d.ts +31 -0
- package/dist/core/providers/providers/extended-http/provider.js +580 -0
- package/dist/core/providers/providers/extended-http/provider.js.map +1 -0
- package/dist/core/providers/providers/gemini/index.d.ts +4 -0
- package/dist/core/providers/providers/gemini/index.js +37 -0
- package/dist/core/providers/providers/gemini/index.js.map +1 -0
- package/dist/core/providers/providers/gemini/provider.d.ts +7 -0
- package/dist/core/providers/providers/gemini/provider.js +59 -0
- package/dist/core/providers/providers/gemini/provider.js.map +1 -0
- package/dist/core/providers/providers/grok/index.d.ts +4 -0
- package/dist/core/providers/providers/grok/index.js +41 -0
- package/dist/core/providers/providers/grok/index.js.map +1 -0
- package/dist/core/providers/providers/grok/provider.d.ts +13 -0
- package/dist/core/providers/providers/grok/provider.js +95 -0
- package/dist/core/providers/providers/grok/provider.js.map +1 -0
- package/dist/core/providers/providers/openai/index.d.ts +4 -0
- package/dist/core/providers/providers/openai/index.js +45 -0
- package/dist/core/providers/providers/openai/index.js.map +1 -0
- package/dist/core/providers/providers/openai/provider.d.ts +14 -0
- package/dist/core/providers/providers/openai/provider.js +203 -0
- package/dist/core/providers/providers/openai/provider.js.map +1 -0
- package/dist/core/providers/providers/openclaw/index.d.ts +4 -0
- package/dist/core/providers/providers/openclaw/index.js +31 -0
- package/dist/core/providers/providers/openclaw/index.js.map +1 -0
- package/dist/core/providers/providers/openclaw/provider.d.ts +11 -0
- package/dist/core/providers/providers/openclaw/provider.js +108 -0
- package/dist/core/providers/providers/openclaw/provider.js.map +1 -0
- package/dist/core/providers/providers/opencode/index.d.ts +4 -0
- package/dist/core/providers/providers/opencode/index.js +23 -0
- package/dist/core/providers/providers/opencode/index.js.map +1 -0
- package/dist/core/providers/providers/opencode/provider.d.ts +11 -0
- package/dist/core/providers/providers/opencode/provider.js +215 -0
- package/dist/core/providers/providers/opencode/provider.js.map +1 -0
- package/dist/core/providers/providers/openrouter/index.d.ts +4 -0
- package/dist/core/providers/providers/openrouter/index.js +37 -0
- package/dist/core/providers/providers/openrouter/index.js.map +1 -0
- package/dist/core/providers/providers/openrouter/provider.d.ts +13 -0
- package/dist/core/providers/providers/openrouter/provider.js +80 -0
- package/dist/core/providers/providers/openrouter/provider.js.map +1 -0
- package/dist/core/providers/registry.d.ts +13 -0
- package/dist/core/providers/registry.js +36 -0
- package/dist/core/providers/registry.js.map +1 -0
- package/dist/core/providers/types.d.ts +101 -0
- package/dist/core/providers/types.js +2 -0
- package/dist/core/providers/types.js.map +1 -0
- package/dist/core/scenarios/application/scenario-runner.service.d.ts +23 -0
- package/dist/core/scenarios/application/scenario-runner.service.js +172 -0
- package/dist/core/scenarios/application/scenario-runner.service.js.map +1 -0
- package/dist/core/scenarios/domain/scenario.d.ts +43 -0
- package/dist/core/scenarios/domain/scenario.js +2 -0
- package/dist/core/scenarios/domain/scenario.js.map +1 -0
- package/dist/core/scenarios/index.d.ts +2 -0
- package/dist/core/scenarios/index.js +2 -0
- package/dist/core/scenarios/index.js.map +1 -0
- package/dist/core/sessions/application/session.service.d.ts +74 -0
- package/dist/core/sessions/application/session.service.js +895 -0
- package/dist/core/sessions/application/session.service.js.map +1 -0
- package/dist/core/sessions/domain/session.d.ts +80 -0
- package/dist/core/sessions/domain/session.js +24 -0
- package/dist/core/sessions/domain/session.js.map +1 -0
- package/dist/core/sessions/domain/transcript.d.ts +28 -0
- package/dist/core/sessions/domain/transcript.js +10 -0
- package/dist/core/sessions/domain/transcript.js.map +1 -0
- package/dist/core/sessions/errors.d.ts +12 -0
- package/dist/core/sessions/errors.js +22 -0
- package/dist/core/sessions/errors.js.map +1 -0
- package/dist/core/sessions/index.d.ts +6 -0
- package/dist/core/sessions/index.js +4 -0
- package/dist/core/sessions/index.js.map +1 -0
- package/dist/core/skills/application/skill.service.d.ts +24 -0
- package/dist/core/skills/application/skill.service.js +375 -0
- package/dist/core/skills/application/skill.service.js.map +1 -0
- package/dist/core/skills/domain/skill.d.ts +62 -0
- package/dist/core/skills/domain/skill.js +46 -0
- package/dist/core/skills/domain/skill.js.map +1 -0
- package/dist/core/skills/index.d.ts +3 -0
- package/dist/core/skills/index.js +3 -0
- package/dist/core/skills/index.js.map +1 -0
- package/dist/core/templates/default-templates.d.ts +19 -0
- package/dist/core/templates/default-templates.js +259 -0
- package/dist/core/templates/default-templates.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/platform/node/acp-server.d.ts +12 -0
- package/dist/platform/node/acp-server.js +18 -0
- package/dist/platform/node/acp-server.js.map +1 -0
- package/dist/platform/node/dotenv.d.ts +6 -0
- package/dist/platform/node/dotenv.js +67 -0
- package/dist/platform/node/dotenv.js.map +1 -0
- package/dist/platform/node/node-command-runner.d.ts +4 -0
- package/dist/platform/node/node-command-runner.js +29 -0
- package/dist/platform/node/node-command-runner.js.map +1 -0
- package/dist/platform/node/node-file-system.d.ts +10 -0
- package/dist/platform/node/node-file-system.js +47 -0
- package/dist/platform/node/node-file-system.js.map +1 -0
- package/dist/platform/node/node-logger.d.ts +9 -0
- package/dist/platform/node/node-logger.js +124 -0
- package/dist/platform/node/node-logger.js.map +1 -0
- package/dist/platform/node/node-path.port.d.ts +9 -0
- package/dist/platform/node/node-path.port.js +41 -0
- package/dist/platform/node/node-path.port.js.map +1 -0
- package/dist/platform/node/opengoat-gateway-client.d.ts +19 -0
- package/dist/platform/node/opengoat-gateway-client.js +194 -0
- package/dist/platform/node/opengoat-gateway-client.js.map +1 -0
- package/dist/platform/node/opengoat-gateway-server.d.ts +53 -0
- package/dist/platform/node/opengoat-gateway-server.js +906 -0
- package/dist/platform/node/opengoat-gateway-server.js.map +1 -0
- package/package.json +37 -0
|
@@ -0,0 +1,1044 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { isDiscoverableByOrchestrator } from "../../agents/index.js";
|
|
4
|
+
import { DEFAULT_AGENT_ID, normalizeAgentId } from "../../domain/agent-id.js";
|
|
5
|
+
import { createNoopLogger } from "../../logging/index.js";
|
|
6
|
+
import { OrchestrationPlannerService } from "./orchestration-planner.service.js";
|
|
7
|
+
import { RoutingService } from "./routing.service.js";
|
|
8
|
+
const MAX_ORCHESTRATION_STEPS = 12;
|
|
9
|
+
const MAX_DELEGATION_STEPS = 8;
|
|
10
|
+
const SHARED_NOTES_MAX_CHARS = 12_000;
|
|
11
|
+
const RECENT_EVENTS_WINDOW = 10;
|
|
12
|
+
export class OrchestrationService {
|
|
13
|
+
providerService;
|
|
14
|
+
skillService;
|
|
15
|
+
agentManifestService;
|
|
16
|
+
sessionService;
|
|
17
|
+
commandRunner;
|
|
18
|
+
routingService;
|
|
19
|
+
plannerService;
|
|
20
|
+
fileSystem;
|
|
21
|
+
pathPort;
|
|
22
|
+
nowIso;
|
|
23
|
+
logger;
|
|
24
|
+
constructor(deps) {
|
|
25
|
+
this.providerService = deps.providerService;
|
|
26
|
+
this.skillService = deps.skillService;
|
|
27
|
+
this.agentManifestService = deps.agentManifestService;
|
|
28
|
+
this.sessionService = deps.sessionService;
|
|
29
|
+
this.commandRunner = deps.commandRunner;
|
|
30
|
+
this.routingService = deps.routingService ?? new RoutingService();
|
|
31
|
+
this.plannerService = deps.plannerService ?? new OrchestrationPlannerService();
|
|
32
|
+
this.fileSystem = deps.fileSystem;
|
|
33
|
+
this.pathPort = deps.pathPort;
|
|
34
|
+
this.nowIso = deps.nowIso;
|
|
35
|
+
this.logger = (deps.logger ?? createNoopLogger()).child({ scope: "orchestration-service" });
|
|
36
|
+
}
|
|
37
|
+
async routeMessage(paths, entryAgentId, message) {
|
|
38
|
+
const manifests = await this.agentManifestService.listManifests(paths);
|
|
39
|
+
const resolvedEntryAgentId = resolveEntryAgentId(entryAgentId, manifests);
|
|
40
|
+
return this.routingService.decide({
|
|
41
|
+
entryAgentId: resolvedEntryAgentId,
|
|
42
|
+
message,
|
|
43
|
+
manifests
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async runAgent(paths, entryAgentId, options) {
|
|
47
|
+
const runId = generateRunId();
|
|
48
|
+
const runLogger = this.logger.child({ runId });
|
|
49
|
+
const startedAt = this.nowIso();
|
|
50
|
+
const manifests = await this.agentManifestService.listManifests(paths);
|
|
51
|
+
const resolvedEntryAgentId = resolveEntryAgentId(entryAgentId, manifests);
|
|
52
|
+
runLogger.info("Starting agent run.", {
|
|
53
|
+
entryAgentId,
|
|
54
|
+
resolvedEntryAgentId
|
|
55
|
+
});
|
|
56
|
+
emitRunStatusEvent(options, {
|
|
57
|
+
stage: "run_started",
|
|
58
|
+
runId,
|
|
59
|
+
timestamp: this.nowIso(),
|
|
60
|
+
agentId: resolvedEntryAgentId
|
|
61
|
+
});
|
|
62
|
+
if (resolvedEntryAgentId !== DEFAULT_AGENT_ID) {
|
|
63
|
+
runLogger.info("Running direct non-orchestrator invocation.", {
|
|
64
|
+
targetAgentId: resolvedEntryAgentId
|
|
65
|
+
});
|
|
66
|
+
const sessionAgentId = options.directAgentSession ? resolvedEntryAgentId : DEFAULT_AGENT_ID;
|
|
67
|
+
const direct = await this.invokeAgentWithSession(paths, resolvedEntryAgentId, {
|
|
68
|
+
...options,
|
|
69
|
+
message: options.message
|
|
70
|
+
}, { sessionAgentId, runId });
|
|
71
|
+
const completedAt = this.nowIso();
|
|
72
|
+
const routing = {
|
|
73
|
+
entryAgentId: resolvedEntryAgentId,
|
|
74
|
+
targetAgentId: resolvedEntryAgentId,
|
|
75
|
+
confidence: 1,
|
|
76
|
+
reason: "Direct invocation of a non-orchestrator agent.",
|
|
77
|
+
rewrittenMessage: options.message,
|
|
78
|
+
candidates: []
|
|
79
|
+
};
|
|
80
|
+
const trace = await this.buildAndWriteTrace({
|
|
81
|
+
paths,
|
|
82
|
+
runId,
|
|
83
|
+
startedAt,
|
|
84
|
+
completedAt,
|
|
85
|
+
entryAgentId: resolvedEntryAgentId,
|
|
86
|
+
userMessage: options.message,
|
|
87
|
+
routing,
|
|
88
|
+
execution: direct.execution,
|
|
89
|
+
durationMs: 0,
|
|
90
|
+
session: direct.session,
|
|
91
|
+
orchestration: {
|
|
92
|
+
mode: "single-agent",
|
|
93
|
+
finalMessage: direct.execution.stdout,
|
|
94
|
+
steps: [],
|
|
95
|
+
sessionGraph: {
|
|
96
|
+
nodes: [
|
|
97
|
+
{
|
|
98
|
+
agentId: resolvedEntryAgentId,
|
|
99
|
+
providerId: direct.execution.providerId,
|
|
100
|
+
sessionKey: direct.session?.sessionKey,
|
|
101
|
+
sessionId: direct.session?.sessionId,
|
|
102
|
+
providerSessionId: direct.execution.providerSessionId
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
edges: []
|
|
106
|
+
},
|
|
107
|
+
taskThreads: []
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
emitRunStatusEvent(options, {
|
|
111
|
+
stage: "run_completed",
|
|
112
|
+
runId,
|
|
113
|
+
timestamp: this.nowIso(),
|
|
114
|
+
agentId: resolvedEntryAgentId
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
...direct.execution,
|
|
118
|
+
entryAgentId: resolvedEntryAgentId,
|
|
119
|
+
routing,
|
|
120
|
+
tracePath: trace.tracePath,
|
|
121
|
+
session: direct.session,
|
|
122
|
+
orchestration: trace.trace.orchestration
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const startTime = Date.now();
|
|
126
|
+
const loopResult = await this.runAiOrchestrationLoop(paths, runId, manifests, options, runLogger);
|
|
127
|
+
const durationMs = Date.now() - startTime;
|
|
128
|
+
const completedAt = this.nowIso();
|
|
129
|
+
const routing = {
|
|
130
|
+
entryAgentId: DEFAULT_AGENT_ID,
|
|
131
|
+
targetAgentId: DEFAULT_AGENT_ID,
|
|
132
|
+
confidence: 0.9,
|
|
133
|
+
reason: "AI orchestration loop executed by orchestrator.",
|
|
134
|
+
rewrittenMessage: options.message,
|
|
135
|
+
candidates: []
|
|
136
|
+
};
|
|
137
|
+
const trace = await this.buildAndWriteTrace({
|
|
138
|
+
paths,
|
|
139
|
+
runId,
|
|
140
|
+
startedAt,
|
|
141
|
+
completedAt,
|
|
142
|
+
entryAgentId: DEFAULT_AGENT_ID,
|
|
143
|
+
userMessage: options.message,
|
|
144
|
+
routing,
|
|
145
|
+
execution: loopResult.execution,
|
|
146
|
+
durationMs,
|
|
147
|
+
orchestration: {
|
|
148
|
+
mode: "ai-loop",
|
|
149
|
+
finalMessage: loopResult.finalMessage,
|
|
150
|
+
steps: loopResult.steps,
|
|
151
|
+
sessionGraph: loopResult.sessionGraph,
|
|
152
|
+
taskThreads: loopResult.taskThreads
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
emitRunStatusEvent(options, {
|
|
156
|
+
stage: "run_completed",
|
|
157
|
+
runId,
|
|
158
|
+
timestamp: this.nowIso(),
|
|
159
|
+
agentId: DEFAULT_AGENT_ID
|
|
160
|
+
});
|
|
161
|
+
return {
|
|
162
|
+
...loopResult.execution,
|
|
163
|
+
entryAgentId: DEFAULT_AGENT_ID,
|
|
164
|
+
routing,
|
|
165
|
+
tracePath: trace.tracePath,
|
|
166
|
+
orchestration: trace.trace.orchestration
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
async runAiOrchestrationLoop(paths, runId, manifests, options, runLogger) {
|
|
170
|
+
const steps = [];
|
|
171
|
+
const sessionGraph = {
|
|
172
|
+
nodes: [],
|
|
173
|
+
edges: []
|
|
174
|
+
};
|
|
175
|
+
const taskThreads = new Map();
|
|
176
|
+
const sharedNotes = [];
|
|
177
|
+
const recentEvents = [];
|
|
178
|
+
let orchestratorSkillsSnapshot = await this.skillService.buildSkillsPrompt(paths, DEFAULT_AGENT_ID);
|
|
179
|
+
let orchestratorSkillsSummary = orchestratorSkillsSnapshot.skills.map((skill) => ({
|
|
180
|
+
id: skill.id,
|
|
181
|
+
name: skill.name,
|
|
182
|
+
description: skill.description,
|
|
183
|
+
source: skill.source
|
|
184
|
+
}));
|
|
185
|
+
let delegationCount = 0;
|
|
186
|
+
let finalMessage = "";
|
|
187
|
+
let lastExecution = await this.buildSyntheticExecution(paths, DEFAULT_AGENT_ID);
|
|
188
|
+
runLogger.info("Starting orchestration loop.", {
|
|
189
|
+
maxSteps: MAX_ORCHESTRATION_STEPS,
|
|
190
|
+
maxDelegations: MAX_DELEGATION_STEPS,
|
|
191
|
+
loadedSkills: orchestratorSkillsSummary.length
|
|
192
|
+
});
|
|
193
|
+
for (let step = 1; step <= MAX_ORCHESTRATION_STEPS; step += 1) {
|
|
194
|
+
const stepLogger = runLogger.child({ step });
|
|
195
|
+
const plannerPrompt = this.plannerService.buildPlannerPrompt({
|
|
196
|
+
userMessage: options.message,
|
|
197
|
+
step,
|
|
198
|
+
maxSteps: MAX_ORCHESTRATION_STEPS,
|
|
199
|
+
sharedNotes: clampText(sharedNotes.join("\n\n"), SHARED_NOTES_MAX_CHARS),
|
|
200
|
+
recentEvents,
|
|
201
|
+
agents: manifests,
|
|
202
|
+
taskThreads: summarizeTaskThreads(taskThreads),
|
|
203
|
+
skills: orchestratorSkillsSummary
|
|
204
|
+
});
|
|
205
|
+
stepLogger.debug("Planner prompt payload.", {
|
|
206
|
+
prompt: plannerPrompt
|
|
207
|
+
});
|
|
208
|
+
emitRunStatusEvent(options, {
|
|
209
|
+
stage: "planner_started",
|
|
210
|
+
runId,
|
|
211
|
+
timestamp: this.nowIso(),
|
|
212
|
+
step,
|
|
213
|
+
agentId: DEFAULT_AGENT_ID
|
|
214
|
+
});
|
|
215
|
+
const plannerCall = await this.invokeAgentWithSession(paths, DEFAULT_AGENT_ID, {
|
|
216
|
+
...options,
|
|
217
|
+
message: plannerPrompt,
|
|
218
|
+
skillsPromptOverride: orchestratorSkillsSnapshot.prompt,
|
|
219
|
+
sessionRef: options.sessionRef,
|
|
220
|
+
forceNewSession: step === 1 ? options.forceNewSession : false
|
|
221
|
+
}, { silent: true, runId, step });
|
|
222
|
+
const plannerRawOutput = plannerCall.execution.stdout.trim() || plannerCall.execution.stderr.trim();
|
|
223
|
+
stepLogger.debug("Planner raw output payload.", {
|
|
224
|
+
output: plannerRawOutput
|
|
225
|
+
});
|
|
226
|
+
if (plannerCall.execution.code !== 0) {
|
|
227
|
+
const providerFailureMessage = renderPlannerProviderFailureMessage(plannerCall.execution.providerId, plannerCall.execution.code, plannerCall.execution.stderr || plannerCall.execution.stdout);
|
|
228
|
+
const plannerDecision = {
|
|
229
|
+
rationale: "Planner provider invocation failed.",
|
|
230
|
+
action: {
|
|
231
|
+
type: "respond_user",
|
|
232
|
+
mode: "direct",
|
|
233
|
+
reason: "planner_provider_failure",
|
|
234
|
+
message: providerFailureMessage
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
emitRunStatusEvent(options, {
|
|
238
|
+
stage: "planner_decision",
|
|
239
|
+
runId,
|
|
240
|
+
timestamp: this.nowIso(),
|
|
241
|
+
step,
|
|
242
|
+
agentId: DEFAULT_AGENT_ID,
|
|
243
|
+
actionType: "respond_user",
|
|
244
|
+
mode: "direct"
|
|
245
|
+
});
|
|
246
|
+
stepLogger.warn("Planner provider invocation failed.", {
|
|
247
|
+
providerId: plannerCall.execution.providerId,
|
|
248
|
+
code: plannerCall.execution.code,
|
|
249
|
+
stderr: plannerCall.execution.stderr,
|
|
250
|
+
stdout: plannerCall.execution.stdout
|
|
251
|
+
});
|
|
252
|
+
addSessionNode(sessionGraph, DEFAULT_AGENT_ID, plannerCall.session, plannerCall.execution);
|
|
253
|
+
const stepLog = {
|
|
254
|
+
step,
|
|
255
|
+
timestamp: this.nowIso(),
|
|
256
|
+
plannerRawOutput,
|
|
257
|
+
plannerDecision,
|
|
258
|
+
note: `Planner provider ${plannerCall.execution.providerId} failed with code ${plannerCall.execution.code}.`
|
|
259
|
+
};
|
|
260
|
+
steps.push(stepLog);
|
|
261
|
+
finalMessage = providerFailureMessage;
|
|
262
|
+
lastExecution = {
|
|
263
|
+
...plannerCall.execution,
|
|
264
|
+
stdout: ensureTrailingNewline(providerFailureMessage)
|
|
265
|
+
};
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
const plannerDecision = this.plannerService.parseDecision(plannerRawOutput, "I could not complete orchestration due to planner output parsing issues.");
|
|
269
|
+
emitRunStatusEvent(options, {
|
|
270
|
+
stage: "planner_decision",
|
|
271
|
+
runId,
|
|
272
|
+
timestamp: this.nowIso(),
|
|
273
|
+
step,
|
|
274
|
+
agentId: DEFAULT_AGENT_ID,
|
|
275
|
+
actionType: plannerDecision.action.type,
|
|
276
|
+
targetAgentId: plannerDecision.action.type === "delegate_to_agent"
|
|
277
|
+
? normalizeAgentId(plannerDecision.action.targetAgentId)
|
|
278
|
+
: undefined,
|
|
279
|
+
mode: plannerDecision.action.mode
|
|
280
|
+
});
|
|
281
|
+
stepLogger.info("Planner decision parsed.", {
|
|
282
|
+
actionType: plannerDecision.action.type,
|
|
283
|
+
actionMode: plannerDecision.action.mode ?? "direct",
|
|
284
|
+
rationale: plannerDecision.rationale
|
|
285
|
+
});
|
|
286
|
+
addSessionNode(sessionGraph, DEFAULT_AGENT_ID, plannerCall.session, plannerCall.execution);
|
|
287
|
+
const stepLog = {
|
|
288
|
+
step,
|
|
289
|
+
timestamp: this.nowIso(),
|
|
290
|
+
plannerRawOutput,
|
|
291
|
+
plannerDecision
|
|
292
|
+
};
|
|
293
|
+
const actionResult = await this.executeAction({
|
|
294
|
+
paths,
|
|
295
|
+
runId,
|
|
296
|
+
step,
|
|
297
|
+
action: plannerDecision.action,
|
|
298
|
+
manifests,
|
|
299
|
+
options,
|
|
300
|
+
sessionGraph,
|
|
301
|
+
stepLog,
|
|
302
|
+
sharedNotes,
|
|
303
|
+
recentEvents,
|
|
304
|
+
taskThreads,
|
|
305
|
+
logger: stepLogger
|
|
306
|
+
});
|
|
307
|
+
steps.push(stepLog);
|
|
308
|
+
if (actionResult.finalMessage !== undefined) {
|
|
309
|
+
finalMessage = actionResult.finalMessage;
|
|
310
|
+
if (actionResult.execution) {
|
|
311
|
+
lastExecution = actionResult.execution;
|
|
312
|
+
}
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
if (actionResult.execution) {
|
|
316
|
+
lastExecution = actionResult.execution;
|
|
317
|
+
}
|
|
318
|
+
if (plannerDecision.action.type === "delegate_to_agent") {
|
|
319
|
+
delegationCount += 1;
|
|
320
|
+
}
|
|
321
|
+
if (plannerDecision.action.type === "install_skill" &&
|
|
322
|
+
(normalizeAgentId(plannerDecision.action.targetAgentId ?? DEFAULT_AGENT_ID) || DEFAULT_AGENT_ID) ===
|
|
323
|
+
DEFAULT_AGENT_ID) {
|
|
324
|
+
orchestratorSkillsSnapshot = await this.skillService.buildSkillsPrompt(paths, DEFAULT_AGENT_ID);
|
|
325
|
+
orchestratorSkillsSummary = orchestratorSkillsSnapshot.skills.map((skill) => ({
|
|
326
|
+
id: skill.id,
|
|
327
|
+
name: skill.name,
|
|
328
|
+
description: skill.description,
|
|
329
|
+
source: skill.source
|
|
330
|
+
}));
|
|
331
|
+
}
|
|
332
|
+
if (delegationCount >= MAX_DELEGATION_STEPS) {
|
|
333
|
+
stepLogger.warn("Delegation safety limit reached.");
|
|
334
|
+
finalMessage = "Stopped orchestration after reaching delegation safety limit.";
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (!finalMessage) {
|
|
339
|
+
finalMessage =
|
|
340
|
+
sharedNotes.length > 0
|
|
341
|
+
? `Orchestration reached step limit.\n\nCurrent synthesis:\n${clampText(sharedNotes.join("\n\n"), 2000)}`
|
|
342
|
+
: "Orchestration stopped at safety step limit without a final response.";
|
|
343
|
+
}
|
|
344
|
+
if (!lastExecution.stdout.trim()) {
|
|
345
|
+
lastExecution = {
|
|
346
|
+
...lastExecution,
|
|
347
|
+
code: 0,
|
|
348
|
+
stdout: ensureTrailingNewline(finalMessage),
|
|
349
|
+
stderr: lastExecution.stderr
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
finalMessage,
|
|
354
|
+
execution: lastExecution,
|
|
355
|
+
steps,
|
|
356
|
+
sessionGraph,
|
|
357
|
+
taskThreads: summarizeTaskThreads(taskThreads)
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
async executeAction(params) {
|
|
361
|
+
const action = params.action;
|
|
362
|
+
if (action.type === "finish" || action.type === "respond_user") {
|
|
363
|
+
const message = action.message.trim() || "Completed.";
|
|
364
|
+
params.logger.info("Completing orchestration with direct response.", {
|
|
365
|
+
actionType: action.type
|
|
366
|
+
});
|
|
367
|
+
params.stepLog.note = action.reason;
|
|
368
|
+
this.addRecentEvent(params.recentEvents, `Step ${params.step}: ${action.type}`);
|
|
369
|
+
return {
|
|
370
|
+
finalMessage: message,
|
|
371
|
+
execution: {
|
|
372
|
+
...(await this.buildSyntheticExecution(params.paths, DEFAULT_AGENT_ID)),
|
|
373
|
+
code: 0,
|
|
374
|
+
stdout: ensureTrailingNewline(message)
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
if (action.type === "read_workspace_file") {
|
|
379
|
+
const resolvedPath = this.resolveWorkspacePath({
|
|
380
|
+
paths: params.paths,
|
|
381
|
+
requestedPath: action.path,
|
|
382
|
+
workingPathHint: params.options.cwd
|
|
383
|
+
});
|
|
384
|
+
params.logger.info("Reading workspace file for orchestration context.", {
|
|
385
|
+
path: resolvedPath
|
|
386
|
+
});
|
|
387
|
+
const exists = await this.fileSystem.exists(resolvedPath);
|
|
388
|
+
const content = exists ? await this.fileSystem.readFile(resolvedPath) : `[MISSING] ${resolvedPath}`;
|
|
389
|
+
params.sharedNotes.push(clampText(`Read ${action.path}:\n${content}`, 2500));
|
|
390
|
+
params.stepLog.artifactIO = { readPath: resolvedPath };
|
|
391
|
+
this.addRecentEvent(params.recentEvents, `Read file ${action.path}`);
|
|
392
|
+
return {};
|
|
393
|
+
}
|
|
394
|
+
if (action.type === "write_workspace_file") {
|
|
395
|
+
const resolvedPath = this.resolveWorkspacePath({
|
|
396
|
+
paths: params.paths,
|
|
397
|
+
requestedPath: action.path,
|
|
398
|
+
workingPathHint: params.options.cwd
|
|
399
|
+
});
|
|
400
|
+
params.logger.info("Writing workspace file for orchestration context.", {
|
|
401
|
+
path: resolvedPath
|
|
402
|
+
});
|
|
403
|
+
await this.fileSystem.ensureDir(path.dirname(resolvedPath));
|
|
404
|
+
await this.fileSystem.writeFile(resolvedPath, ensureTrailingNewline(action.content));
|
|
405
|
+
params.stepLog.artifactIO = { writePath: resolvedPath };
|
|
406
|
+
this.addRecentEvent(params.recentEvents, `Wrote file ${action.path}`);
|
|
407
|
+
return {};
|
|
408
|
+
}
|
|
409
|
+
if (action.type === "install_skill") {
|
|
410
|
+
const targetAgentId = normalizeAgentId(action.targetAgentId ?? DEFAULT_AGENT_ID) || DEFAULT_AGENT_ID;
|
|
411
|
+
params.logger.info("Installing skill requested by orchestrator action.", {
|
|
412
|
+
targetAgentId,
|
|
413
|
+
skillName: action.skillName
|
|
414
|
+
});
|
|
415
|
+
const result = await this.skillService.installSkill(params.paths, {
|
|
416
|
+
scope: "agent",
|
|
417
|
+
agentId: targetAgentId,
|
|
418
|
+
skillName: action.skillName,
|
|
419
|
+
sourcePath: action.sourcePath,
|
|
420
|
+
description: action.description,
|
|
421
|
+
content: action.content
|
|
422
|
+
});
|
|
423
|
+
const installedFor = result.agentId || targetAgentId;
|
|
424
|
+
params.stepLog.note = `Installed skill ${result.skillId} for ${installedFor} (${result.source}).`;
|
|
425
|
+
params.sharedNotes.push(`Skill installed: ${result.skillId} for ${installedFor} from ${result.source} at ${result.installedPath}`);
|
|
426
|
+
this.addRecentEvent(params.recentEvents, `Installed skill ${result.skillId} for ${installedFor} (${result.source})`);
|
|
427
|
+
return {};
|
|
428
|
+
}
|
|
429
|
+
const targetAgentId = normalizeAgentId(action.targetAgentId);
|
|
430
|
+
const targetManifest = params.manifests.find((manifest) => manifest.agentId === targetAgentId);
|
|
431
|
+
if (!targetManifest || !isDiscoverableByOrchestrator(targetManifest)) {
|
|
432
|
+
const note = `Invalid delegation target "${action.targetAgentId}" (missing, non-receivable, or non-discoverable).`;
|
|
433
|
+
params.logger.warn("Invalid delegation target.", {
|
|
434
|
+
requestedTargetAgentId: action.targetAgentId
|
|
435
|
+
});
|
|
436
|
+
params.sharedNotes.push(note);
|
|
437
|
+
params.stepLog.note = note;
|
|
438
|
+
this.addRecentEvent(params.recentEvents, note);
|
|
439
|
+
return {};
|
|
440
|
+
}
|
|
441
|
+
const taskKey = resolveTaskKey(action.taskKey, targetAgentId, params.step);
|
|
442
|
+
const requestedSessionPolicy = action.sessionPolicy ?? "auto";
|
|
443
|
+
const existingThread = params.taskThreads.get(taskKey);
|
|
444
|
+
const canReuseThread = Boolean(existingThread && existingThread.agentId === targetAgentId);
|
|
445
|
+
const effectiveSessionPolicy = requestedSessionPolicy === "reuse" ? (canReuseThread ? "reuse" : "new") : requestedSessionPolicy;
|
|
446
|
+
const forceNewTaskSession = effectiveSessionPolicy === "new" || !canReuseThread;
|
|
447
|
+
if (requestedSessionPolicy === "reuse" && !canReuseThread) {
|
|
448
|
+
const note = `Requested reuse for task "${taskKey}" but no matching thread exists; creating a new session.`;
|
|
449
|
+
params.sharedNotes.push(note);
|
|
450
|
+
this.addRecentEvent(params.recentEvents, note);
|
|
451
|
+
params.logger.warn("Requested task session reuse but no matching thread was found.", {
|
|
452
|
+
taskKey,
|
|
453
|
+
targetAgentId
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
const targetRuntime = await this.providerService.getAgentRuntimeProfile(params.paths, targetAgentId);
|
|
457
|
+
const mode = action.mode ?? "hybrid";
|
|
458
|
+
params.logger.info("Delegating task to agent.", {
|
|
459
|
+
fromAgentId: DEFAULT_AGENT_ID,
|
|
460
|
+
toAgentId: targetAgentId,
|
|
461
|
+
targetWorkspaceAccess: targetRuntime.workspaceAccess,
|
|
462
|
+
mode,
|
|
463
|
+
reason: action.reason,
|
|
464
|
+
taskKey,
|
|
465
|
+
sessionPolicy: effectiveSessionPolicy
|
|
466
|
+
});
|
|
467
|
+
emitRunStatusEvent(params.options, {
|
|
468
|
+
stage: "delegation_started",
|
|
469
|
+
runId: params.runId,
|
|
470
|
+
timestamp: this.nowIso(),
|
|
471
|
+
step: params.step,
|
|
472
|
+
agentId: DEFAULT_AGENT_ID,
|
|
473
|
+
targetAgentId,
|
|
474
|
+
providerId: targetRuntime.providerId,
|
|
475
|
+
mode,
|
|
476
|
+
detail: action.reason
|
|
477
|
+
});
|
|
478
|
+
const handoffBaseDir = this.pathPort.join(params.paths.sessionsDir, params.runId);
|
|
479
|
+
let outboundPath;
|
|
480
|
+
if (mode === "artifacts" || mode === "hybrid") {
|
|
481
|
+
await this.fileSystem.ensureDir(handoffBaseDir);
|
|
482
|
+
outboundPath = this.pathPort.join(handoffBaseDir, `step-${String(params.step).padStart(2, "0")}-to-${targetAgentId}.md`);
|
|
483
|
+
const handoffDocument = renderHandoffDocument({
|
|
484
|
+
step: params.step,
|
|
485
|
+
userMessage: params.options.message,
|
|
486
|
+
delegateMessage: action.message,
|
|
487
|
+
expectedOutput: action.expectedOutput,
|
|
488
|
+
sharedNotes: params.sharedNotes
|
|
489
|
+
});
|
|
490
|
+
await this.fileSystem.writeFile(outboundPath, ensureTrailingNewline(handoffDocument));
|
|
491
|
+
params.stepLog.artifactIO = {
|
|
492
|
+
...params.stepLog.artifactIO,
|
|
493
|
+
writePath: outboundPath
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
const delegateMessage = renderDelegateMessage({
|
|
497
|
+
step: params.step,
|
|
498
|
+
userMessage: params.options.message,
|
|
499
|
+
delegateMessage: action.message,
|
|
500
|
+
expectedOutput: action.expectedOutput,
|
|
501
|
+
mode,
|
|
502
|
+
outboundPath,
|
|
503
|
+
exposeArtifactPath: targetRuntime.workspaceAccess === "internal",
|
|
504
|
+
sharedNotes: params.sharedNotes
|
|
505
|
+
});
|
|
506
|
+
params.logger.debug("Delegation message payload.", {
|
|
507
|
+
fromAgentId: DEFAULT_AGENT_ID,
|
|
508
|
+
toAgentId: targetAgentId,
|
|
509
|
+
taskKey,
|
|
510
|
+
request: delegateMessage,
|
|
511
|
+
mode,
|
|
512
|
+
outboundPath
|
|
513
|
+
});
|
|
514
|
+
const providerSessionId = effectiveSessionPolicy !== "new" && existingThread?.agentId === targetAgentId
|
|
515
|
+
? existingThread.providerSessionId
|
|
516
|
+
: undefined;
|
|
517
|
+
const delegateCall = await this.invokeAgentWithSession(params.paths, targetAgentId, {
|
|
518
|
+
message: delegateMessage,
|
|
519
|
+
env: params.options.env,
|
|
520
|
+
cwd: params.options.cwd,
|
|
521
|
+
sessionRef: `agent:${targetAgentId}:task:${taskKey}`,
|
|
522
|
+
forceNewSession: forceNewTaskSession,
|
|
523
|
+
providerSessionId,
|
|
524
|
+
forceNewProviderSession: forceNewTaskSession
|
|
525
|
+
}, { silent: true, runId: params.runId, step: params.step });
|
|
526
|
+
const responseText = delegateCall.execution.stdout.trim() ||
|
|
527
|
+
(delegateCall.execution.stderr.trim() ? `[stderr] ${delegateCall.execution.stderr.trim()}` : "");
|
|
528
|
+
params.logger.debug("Delegation response payload.", {
|
|
529
|
+
fromAgentId: DEFAULT_AGENT_ID,
|
|
530
|
+
toAgentId: targetAgentId,
|
|
531
|
+
code: delegateCall.execution.code,
|
|
532
|
+
providerId: delegateCall.execution.providerId,
|
|
533
|
+
response: responseText
|
|
534
|
+
});
|
|
535
|
+
let inboundPath;
|
|
536
|
+
if (mode === "artifacts" || mode === "hybrid") {
|
|
537
|
+
await this.fileSystem.ensureDir(handoffBaseDir);
|
|
538
|
+
inboundPath = this.pathPort.join(handoffBaseDir, `step-${String(params.step).padStart(2, "0")}-from-${targetAgentId}.md`);
|
|
539
|
+
await this.fileSystem.writeFile(inboundPath, ensureTrailingNewline(responseText || "(empty response)"));
|
|
540
|
+
params.stepLog.artifactIO = {
|
|
541
|
+
...params.stepLog.artifactIO,
|
|
542
|
+
readPath: inboundPath
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
params.stepLog.agentCall = {
|
|
546
|
+
targetAgentId,
|
|
547
|
+
taskKey,
|
|
548
|
+
sessionPolicy: effectiveSessionPolicy,
|
|
549
|
+
request: delegateMessage,
|
|
550
|
+
response: responseText,
|
|
551
|
+
code: delegateCall.execution.code,
|
|
552
|
+
providerId: delegateCall.execution.providerId,
|
|
553
|
+
sessionKey: delegateCall.session?.sessionKey,
|
|
554
|
+
sessionId: delegateCall.session?.sessionId,
|
|
555
|
+
providerSessionId: delegateCall.execution.providerSessionId,
|
|
556
|
+
workingTreeEffect: delegateCall.workingTreeEffect
|
|
557
|
+
};
|
|
558
|
+
addSessionNode(params.sessionGraph, targetAgentId, delegateCall.session, delegateCall.execution);
|
|
559
|
+
upsertTaskThread(params.taskThreads, {
|
|
560
|
+
taskKey,
|
|
561
|
+
agentId: targetAgentId,
|
|
562
|
+
createdStep: params.step,
|
|
563
|
+
updatedStep: params.step,
|
|
564
|
+
providerId: delegateCall.execution.providerId,
|
|
565
|
+
providerSessionId: delegateCall.execution.providerSessionId,
|
|
566
|
+
sessionKey: delegateCall.session?.sessionKey,
|
|
567
|
+
sessionId: delegateCall.session?.sessionId,
|
|
568
|
+
lastResponse: summarizeText(responseText || "(no response)")
|
|
569
|
+
});
|
|
570
|
+
params.sessionGraph.edges.push({
|
|
571
|
+
fromAgentId: DEFAULT_AGENT_ID,
|
|
572
|
+
toAgentId: targetAgentId,
|
|
573
|
+
reason: action.reason || params.stepLog.plannerDecision.rationale
|
|
574
|
+
});
|
|
575
|
+
const delegationFailure = summarizeDelegationFailure({
|
|
576
|
+
targetAgentId,
|
|
577
|
+
providerId: delegateCall.execution.providerId,
|
|
578
|
+
code: delegateCall.execution.code,
|
|
579
|
+
responseText,
|
|
580
|
+
workingTreeEffect: delegateCall.workingTreeEffect
|
|
581
|
+
});
|
|
582
|
+
if (delegationFailure) {
|
|
583
|
+
params.stepLog.note = delegationFailure.message;
|
|
584
|
+
this.addRecentEvent(params.recentEvents, delegationFailure.message);
|
|
585
|
+
params.sharedNotes.push(clampText(delegationFailure.message, 1200));
|
|
586
|
+
params.logger.warn("Delegation failed; stopping orchestration loop.", {
|
|
587
|
+
targetAgentId,
|
|
588
|
+
providerId: delegateCall.execution.providerId,
|
|
589
|
+
code: delegateCall.execution.code,
|
|
590
|
+
failureReason: delegationFailure.reason
|
|
591
|
+
});
|
|
592
|
+
return {
|
|
593
|
+
finalMessage: delegationFailure.message,
|
|
594
|
+
execution: {
|
|
595
|
+
...delegateCall.execution,
|
|
596
|
+
code: delegateCall.execution.code !== 0 ? delegateCall.execution.code : 1,
|
|
597
|
+
stdout: ensureTrailingNewline(delegationFailure.message)
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
const note = `Delegated to ${targetAgentId} [task:${taskKey}]: ${summarizeText(responseText || "(no response)")}`;
|
|
602
|
+
params.sharedNotes.push(clampText(note, 2000));
|
|
603
|
+
this.addRecentEvent(params.recentEvents, note);
|
|
604
|
+
if (delegateCall.workingTreeEffect && delegateCall.workingTreeEffect.enabled) {
|
|
605
|
+
const effectNote = `Working tree effect (${targetAgentId}): ${delegateCall.workingTreeEffect.summary}`;
|
|
606
|
+
params.sharedNotes.push(clampText(effectNote, 1200));
|
|
607
|
+
this.addRecentEvent(params.recentEvents, effectNote);
|
|
608
|
+
}
|
|
609
|
+
return {
|
|
610
|
+
execution: delegateCall.execution
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
addRecentEvent(events, value) {
|
|
614
|
+
events.push(summarizeText(value));
|
|
615
|
+
while (events.length > RECENT_EVENTS_WINDOW) {
|
|
616
|
+
events.shift();
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
resolveWorkspacePath(input) {
|
|
620
|
+
const { requestedPath, workingPathHint } = input;
|
|
621
|
+
const normalized = requestedPath.replace(/\\/g, "/").trim();
|
|
622
|
+
const runWorkingPath = resolveInvocationWorkingPath(workingPathHint);
|
|
623
|
+
const unsafeSegments = normalized.split("/").filter((segment) => segment === "..");
|
|
624
|
+
if (unsafeSegments.length > 0) {
|
|
625
|
+
return this.pathPort.join(runWorkingPath, ".opengoat", "coordination", "unsafe-path-blocked.md");
|
|
626
|
+
}
|
|
627
|
+
const relative = normalized.replace(/^\/+/, "");
|
|
628
|
+
return this.pathPort.join(runWorkingPath, relative || ".opengoat/coordination/context.md");
|
|
629
|
+
}
|
|
630
|
+
async invokeAgentWithSession(paths, agentId, options, behavior = {}) {
|
|
631
|
+
const sessionAgentId = normalizeAgentId(behavior.sessionAgentId ?? agentId) || DEFAULT_AGENT_ID;
|
|
632
|
+
this.logger.debug("Preparing agent invocation with session context.", {
|
|
633
|
+
agentId,
|
|
634
|
+
sessionAgentId,
|
|
635
|
+
message: options.message,
|
|
636
|
+
sessionRef: options.sessionRef,
|
|
637
|
+
forceNewSession: options.forceNewSession,
|
|
638
|
+
disableSession: options.disableSession,
|
|
639
|
+
providerSessionId: options.providerSessionId,
|
|
640
|
+
forceNewProviderSession: options.forceNewProviderSession
|
|
641
|
+
});
|
|
642
|
+
const preparedSession = await this.sessionService.prepareRunSession(paths, sessionAgentId, {
|
|
643
|
+
sessionRef: options.sessionRef,
|
|
644
|
+
forceNew: options.forceNewSession,
|
|
645
|
+
disableSession: options.disableSession,
|
|
646
|
+
workingPath: options.cwd,
|
|
647
|
+
userMessage: options.message
|
|
648
|
+
});
|
|
649
|
+
const invokeOptions = sanitizeProviderInvokeOptions({
|
|
650
|
+
...options,
|
|
651
|
+
sessionContext: preparedSession.enabled ? preparedSession.contextPrompt : undefined
|
|
652
|
+
});
|
|
653
|
+
if (behavior.silent) {
|
|
654
|
+
delete invokeOptions.onStdout;
|
|
655
|
+
delete invokeOptions.onStderr;
|
|
656
|
+
}
|
|
657
|
+
const workingPath = preparedSession.enabled ? preparedSession.info.workingPath : resolveInvocationWorkingPath(options.cwd);
|
|
658
|
+
const beforeSnapshot = await this.captureWorkingTreeSnapshot(workingPath);
|
|
659
|
+
const execution = await this.providerService.invokeAgent(paths, agentId, invokeOptions, {
|
|
660
|
+
runId: behavior.runId,
|
|
661
|
+
step: behavior.step,
|
|
662
|
+
hooks: {
|
|
663
|
+
onInvocationStarted: (event) => {
|
|
664
|
+
emitRunStatusEvent(options, {
|
|
665
|
+
stage: "provider_invocation_started",
|
|
666
|
+
runId: event.runId ?? behavior.runId ?? "unknown-run",
|
|
667
|
+
timestamp: event.timestamp,
|
|
668
|
+
step: event.step ?? behavior.step,
|
|
669
|
+
agentId: event.agentId,
|
|
670
|
+
providerId: event.providerId
|
|
671
|
+
});
|
|
672
|
+
},
|
|
673
|
+
onInvocationCompleted: (event) => {
|
|
674
|
+
emitRunStatusEvent(options, {
|
|
675
|
+
stage: "provider_invocation_completed",
|
|
676
|
+
runId: event.runId ?? behavior.runId ?? "unknown-run",
|
|
677
|
+
timestamp: event.timestamp,
|
|
678
|
+
step: event.step ?? behavior.step,
|
|
679
|
+
agentId: event.agentId,
|
|
680
|
+
providerId: event.providerId,
|
|
681
|
+
code: event.code
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
const afterSnapshot = await this.captureWorkingTreeSnapshot(workingPath);
|
|
687
|
+
const workingTreeEffect = summarizeWorkingTreeEffect(beforeSnapshot, afterSnapshot);
|
|
688
|
+
this.logger.debug("Agent invocation execution returned.", {
|
|
689
|
+
agentId,
|
|
690
|
+
sessionAgentId,
|
|
691
|
+
providerId: execution.providerId,
|
|
692
|
+
code: execution.code,
|
|
693
|
+
stdout: execution.stdout,
|
|
694
|
+
stderr: execution.stderr,
|
|
695
|
+
providerSessionId: execution.providerSessionId,
|
|
696
|
+
workingTreeEffect: workingTreeEffect?.summary
|
|
697
|
+
});
|
|
698
|
+
if (!preparedSession.enabled) {
|
|
699
|
+
return {
|
|
700
|
+
execution,
|
|
701
|
+
workingTreeEffect,
|
|
702
|
+
session: undefined
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
const assistantContent = execution.stdout.trim() ||
|
|
706
|
+
(execution.stderr.trim()
|
|
707
|
+
? `[Provider error code ${execution.code}] ${execution.stderr.trim()}`
|
|
708
|
+
: `[Provider exited with code ${execution.code}]`);
|
|
709
|
+
const postRunCompaction = await this.sessionService.recordAssistantReply(paths, preparedSession.info, assistantContent);
|
|
710
|
+
return {
|
|
711
|
+
execution,
|
|
712
|
+
workingTreeEffect,
|
|
713
|
+
session: {
|
|
714
|
+
...preparedSession.info,
|
|
715
|
+
preRunCompactionApplied: preparedSession.compactionApplied,
|
|
716
|
+
postRunCompaction
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
async buildAndWriteTrace(params) {
|
|
721
|
+
const trace = {
|
|
722
|
+
schemaVersion: 2,
|
|
723
|
+
runId: params.runId,
|
|
724
|
+
startedAt: params.startedAt,
|
|
725
|
+
completedAt: params.completedAt,
|
|
726
|
+
entryAgentId: params.entryAgentId,
|
|
727
|
+
userMessage: params.userMessage,
|
|
728
|
+
routing: params.routing,
|
|
729
|
+
session: params.session
|
|
730
|
+
? {
|
|
731
|
+
...params.session,
|
|
732
|
+
postRunCompactionApplied: params.session.postRunCompaction?.applied ?? false,
|
|
733
|
+
postRunCompactionSummary: params.session.postRunCompaction?.summary
|
|
734
|
+
}
|
|
735
|
+
: undefined,
|
|
736
|
+
execution: {
|
|
737
|
+
agentId: params.execution.agentId,
|
|
738
|
+
providerId: params.execution.providerId,
|
|
739
|
+
code: params.execution.code,
|
|
740
|
+
stdout: params.execution.stdout,
|
|
741
|
+
stderr: params.execution.stderr,
|
|
742
|
+
durationMs: params.durationMs
|
|
743
|
+
},
|
|
744
|
+
orchestration: params.orchestration
|
|
745
|
+
};
|
|
746
|
+
const tracePath = await this.writeTrace(params.paths, trace);
|
|
747
|
+
return { tracePath, trace };
|
|
748
|
+
}
|
|
749
|
+
async captureWorkingTreeSnapshot(workingPath) {
|
|
750
|
+
if (!this.commandRunner) {
|
|
751
|
+
return undefined;
|
|
752
|
+
}
|
|
753
|
+
if (!(await this.fileSystem.exists(workingPath))) {
|
|
754
|
+
return undefined;
|
|
755
|
+
}
|
|
756
|
+
try {
|
|
757
|
+
const result = await this.commandRunner.run({
|
|
758
|
+
command: "git",
|
|
759
|
+
args: ["status", "--porcelain=v1", "--untracked-files=all"],
|
|
760
|
+
cwd: workingPath
|
|
761
|
+
});
|
|
762
|
+
if (result.code !== 0) {
|
|
763
|
+
return undefined;
|
|
764
|
+
}
|
|
765
|
+
const entries = parsePorcelainEntries(result.stdout);
|
|
766
|
+
return {
|
|
767
|
+
enabled: true,
|
|
768
|
+
workingPath,
|
|
769
|
+
entries
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
catch {
|
|
773
|
+
return undefined;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
async buildSyntheticExecution(paths, agentId) {
|
|
777
|
+
const binding = await this.providerService.getAgentProvider(paths, agentId);
|
|
778
|
+
return {
|
|
779
|
+
...binding,
|
|
780
|
+
code: 0,
|
|
781
|
+
stdout: "",
|
|
782
|
+
stderr: ""
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
async writeTrace(paths, trace) {
|
|
786
|
+
await this.fileSystem.ensureDir(paths.runsDir);
|
|
787
|
+
const tracePath = this.pathPort.join(paths.runsDir, `${trace.runId}.json`);
|
|
788
|
+
await this.fileSystem.writeFile(tracePath, `${JSON.stringify(trace, null, 2)}\n`);
|
|
789
|
+
return tracePath;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
function generateRunId() {
|
|
793
|
+
return randomUUID().toLowerCase();
|
|
794
|
+
}
|
|
795
|
+
function resolveInvocationWorkingPath(cwd) {
|
|
796
|
+
const normalized = cwd?.trim();
|
|
797
|
+
if (normalized) {
|
|
798
|
+
return path.resolve(normalized);
|
|
799
|
+
}
|
|
800
|
+
return process.cwd();
|
|
801
|
+
}
|
|
802
|
+
function parsePorcelainEntries(stdout) {
|
|
803
|
+
const lines = stdout
|
|
804
|
+
.split("\n")
|
|
805
|
+
.map((line) => line.trimEnd())
|
|
806
|
+
.filter(Boolean);
|
|
807
|
+
return lines.map((line) => {
|
|
808
|
+
const status = line.slice(0, 2);
|
|
809
|
+
const rawPath = line.length > 3 ? line.slice(3).trim() : "";
|
|
810
|
+
const finalPath = rawPath.includes(" -> ") ? rawPath.split(" -> ").at(-1)?.trim() || rawPath : rawPath;
|
|
811
|
+
return {
|
|
812
|
+
raw: `${status} ${finalPath}`.trim(),
|
|
813
|
+
path: finalPath
|
|
814
|
+
};
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
function summarizeWorkingTreeEffect(beforeSnapshot, afterSnapshot) {
|
|
818
|
+
if (!beforeSnapshot || !afterSnapshot || !beforeSnapshot.enabled || !afterSnapshot.enabled) {
|
|
819
|
+
return undefined;
|
|
820
|
+
}
|
|
821
|
+
const beforeMap = new Map(beforeSnapshot.entries.map((entry) => [entry.path, entry.raw]));
|
|
822
|
+
const afterMap = new Map(afterSnapshot.entries.map((entry) => [entry.path, entry.raw]));
|
|
823
|
+
const touched = new Set();
|
|
824
|
+
for (const [path, raw] of afterMap.entries()) {
|
|
825
|
+
if (beforeMap.get(path) !== raw) {
|
|
826
|
+
touched.add(path);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
for (const [path, raw] of beforeMap.entries()) {
|
|
830
|
+
if (afterMap.get(path) !== raw) {
|
|
831
|
+
touched.add(path);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
const touchedPaths = [...touched].sort((left, right) => left.localeCompare(right));
|
|
835
|
+
const summary = touchedPaths.length === 0
|
|
836
|
+
? "No tracked changes detected."
|
|
837
|
+
: `Touched ${touchedPaths.length} path(s): ${touchedPaths.slice(0, 8).join(", ")}${touchedPaths.length > 8 ? ", ..." : ""}`;
|
|
838
|
+
return {
|
|
839
|
+
enabled: true,
|
|
840
|
+
workingPath: afterSnapshot.workingPath,
|
|
841
|
+
beforeEntries: beforeSnapshot.entries.length,
|
|
842
|
+
afterEntries: afterSnapshot.entries.length,
|
|
843
|
+
touchedPaths,
|
|
844
|
+
summary
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
function resolveEntryAgentId(entryAgentId, manifests) {
|
|
848
|
+
const normalizedEntryAgentId = normalizeAgentId(entryAgentId) || DEFAULT_AGENT_ID;
|
|
849
|
+
if (manifests.some((manifest) => manifest.agentId === normalizedEntryAgentId)) {
|
|
850
|
+
return normalizedEntryAgentId;
|
|
851
|
+
}
|
|
852
|
+
if (manifests.some((manifest) => manifest.agentId === DEFAULT_AGENT_ID)) {
|
|
853
|
+
return DEFAULT_AGENT_ID;
|
|
854
|
+
}
|
|
855
|
+
return manifests[0]?.agentId || normalizedEntryAgentId;
|
|
856
|
+
}
|
|
857
|
+
function emitRunStatusEvent(options, event) {
|
|
858
|
+
options.hooks?.onEvent?.(event);
|
|
859
|
+
}
|
|
860
|
+
function sanitizeProviderInvokeOptions(options) {
|
|
861
|
+
const sanitized = { ...options };
|
|
862
|
+
delete sanitized.sessionRef;
|
|
863
|
+
delete sanitized.forceNewSession;
|
|
864
|
+
delete sanitized.disableSession;
|
|
865
|
+
delete sanitized.directAgentSession;
|
|
866
|
+
delete sanitized.hooks;
|
|
867
|
+
return sanitized;
|
|
868
|
+
}
|
|
869
|
+
function renderDelegateMessage(params) {
|
|
870
|
+
const lines = [
|
|
871
|
+
`Delegation step: ${params.step}`,
|
|
872
|
+
"",
|
|
873
|
+
"Original user request:",
|
|
874
|
+
params.userMessage,
|
|
875
|
+
"",
|
|
876
|
+
"Delegation instruction:",
|
|
877
|
+
params.delegateMessage,
|
|
878
|
+
""
|
|
879
|
+
];
|
|
880
|
+
if (params.expectedOutput?.trim()) {
|
|
881
|
+
lines.push("Expected output:", params.expectedOutput.trim(), "");
|
|
882
|
+
}
|
|
883
|
+
if (params.sharedNotes.length > 0) {
|
|
884
|
+
lines.push("Shared notes from previous steps:", clampText(params.sharedNotes.join("\n\n"), 4000), "");
|
|
885
|
+
}
|
|
886
|
+
if ((params.mode === "artifacts" || params.mode === "hybrid") && params.outboundPath && params.exposeArtifactPath) {
|
|
887
|
+
lines.push(`Coordination file: ${params.outboundPath}`, "You may use this markdown artifact for durable handoff context.", "");
|
|
888
|
+
}
|
|
889
|
+
else if (params.mode === "artifacts" || params.mode === "hybrid") {
|
|
890
|
+
lines.push("Coordination artifacts are managed internally by the orchestrator.", "");
|
|
891
|
+
}
|
|
892
|
+
lines.push("Return a concise result for the orchestrator.");
|
|
893
|
+
return lines.join("\n");
|
|
894
|
+
}
|
|
895
|
+
function renderHandoffDocument(params) {
|
|
896
|
+
return [
|
|
897
|
+
`# Delegation Step ${params.step}`,
|
|
898
|
+
"",
|
|
899
|
+
"## User Request",
|
|
900
|
+
params.userMessage,
|
|
901
|
+
"",
|
|
902
|
+
"## Delegation Instruction",
|
|
903
|
+
params.delegateMessage,
|
|
904
|
+
"",
|
|
905
|
+
"## Expected Output",
|
|
906
|
+
params.expectedOutput?.trim() || "(not specified)",
|
|
907
|
+
"",
|
|
908
|
+
"## Prior Notes",
|
|
909
|
+
params.sharedNotes.length > 0 ? clampText(params.sharedNotes.join("\n\n"), 4000) : "(none)"
|
|
910
|
+
].join("\n");
|
|
911
|
+
}
|
|
912
|
+
function addSessionNode(graph, agentId, session, execution) {
|
|
913
|
+
if (!session) {
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
const exists = graph.nodes.some((node) => node.agentId === agentId &&
|
|
917
|
+
node.providerId === execution?.providerId &&
|
|
918
|
+
node.sessionKey === session.sessionKey &&
|
|
919
|
+
node.sessionId === session.sessionId &&
|
|
920
|
+
node.providerSessionId === execution?.providerSessionId);
|
|
921
|
+
if (exists) {
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
graph.nodes.push({
|
|
925
|
+
agentId,
|
|
926
|
+
providerId: execution?.providerId,
|
|
927
|
+
sessionKey: session.sessionKey,
|
|
928
|
+
sessionId: session.sessionId,
|
|
929
|
+
providerSessionId: execution?.providerSessionId
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
function resolveTaskKey(actionTaskKey, targetAgentId, step) {
|
|
933
|
+
const explicit = actionTaskKey?.trim().toLowerCase();
|
|
934
|
+
if (explicit) {
|
|
935
|
+
return explicit;
|
|
936
|
+
}
|
|
937
|
+
return `${targetAgentId}-step-${String(step).padStart(2, "0")}`;
|
|
938
|
+
}
|
|
939
|
+
function upsertTaskThread(threads, next) {
|
|
940
|
+
const existing = threads.get(next.taskKey);
|
|
941
|
+
if (!existing) {
|
|
942
|
+
threads.set(next.taskKey, next);
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
threads.set(next.taskKey, {
|
|
946
|
+
...existing,
|
|
947
|
+
...next,
|
|
948
|
+
createdStep: existing.createdStep,
|
|
949
|
+
updatedStep: next.updatedStep
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
function summarizeTaskThreads(threads) {
|
|
953
|
+
return [...threads.values()]
|
|
954
|
+
.sort((left, right) => left.createdStep - right.createdStep)
|
|
955
|
+
.map((thread) => ({
|
|
956
|
+
taskKey: thread.taskKey,
|
|
957
|
+
agentId: thread.agentId,
|
|
958
|
+
providerId: thread.providerId,
|
|
959
|
+
providerSessionId: thread.providerSessionId,
|
|
960
|
+
sessionKey: thread.sessionKey,
|
|
961
|
+
sessionId: thread.sessionId,
|
|
962
|
+
createdStep: thread.createdStep,
|
|
963
|
+
updatedStep: thread.updatedStep,
|
|
964
|
+
lastResponse: thread.lastResponse
|
|
965
|
+
}));
|
|
966
|
+
}
|
|
967
|
+
function summarizeText(value) {
|
|
968
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
969
|
+
if (normalized.length <= 180) {
|
|
970
|
+
return normalized;
|
|
971
|
+
}
|
|
972
|
+
return `${normalized.slice(0, 177)}...`;
|
|
973
|
+
}
|
|
974
|
+
function ensureTrailingNewline(value) {
|
|
975
|
+
return value.endsWith("\n") ? value : `${value}\n`;
|
|
976
|
+
}
|
|
977
|
+
function renderPlannerProviderFailureMessage(providerId, code, detailsRaw) {
|
|
978
|
+
const details = detailsRaw.trim();
|
|
979
|
+
const summary = details
|
|
980
|
+
? `\n\nProvider error details:\n${clampText(details, 1200)}`
|
|
981
|
+
: "";
|
|
982
|
+
return [
|
|
983
|
+
`The orchestrator provider (${providerId}) failed while planning (exit code ${code}).`,
|
|
984
|
+
"Open provider setup in the desktop app and verify credentials/model configuration.",
|
|
985
|
+
summary
|
|
986
|
+
]
|
|
987
|
+
.join("\n")
|
|
988
|
+
.trim();
|
|
989
|
+
}
|
|
990
|
+
function summarizeDelegationFailure(params) {
|
|
991
|
+
if (params.code !== 0) {
|
|
992
|
+
const detail = summarizeText(params.responseText || "(no provider details)");
|
|
993
|
+
return {
|
|
994
|
+
reason: "provider_error",
|
|
995
|
+
message: [
|
|
996
|
+
`The delegated agent "${params.targetAgentId}" failed via provider "${params.providerId}" (exit code ${params.code}).`,
|
|
997
|
+
`Details: ${detail}`
|
|
998
|
+
].join("\n")
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
const blockReason = extractBlockedNoProgressReason(params.responseText, params.workingTreeEffect);
|
|
1002
|
+
if (!blockReason) {
|
|
1003
|
+
return undefined;
|
|
1004
|
+
}
|
|
1005
|
+
return {
|
|
1006
|
+
reason: "blocked_no_progress",
|
|
1007
|
+
message: [
|
|
1008
|
+
`The delegated agent "${params.targetAgentId}" could not continue this request.`,
|
|
1009
|
+
blockReason
|
|
1010
|
+
].join("\n")
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
function extractBlockedNoProgressReason(responseText, workingTreeEffect) {
|
|
1014
|
+
const normalized = responseText.toLowerCase();
|
|
1015
|
+
if (!normalized.trim()) {
|
|
1016
|
+
return undefined;
|
|
1017
|
+
}
|
|
1018
|
+
const toolUnavailableSignals = [
|
|
1019
|
+
/write_file/,
|
|
1020
|
+
/run_shell_command/,
|
|
1021
|
+
/tool(?:s)? (?:is|are)?\s*(?:not found|unavailable|missing|not listed|not available)/,
|
|
1022
|
+
/cannot create .* file/,
|
|
1023
|
+
/i(?:'| a)?m stuck/,
|
|
1024
|
+
/unable to proceed/
|
|
1025
|
+
];
|
|
1026
|
+
const hasToolSignal = toolUnavailableSignals.some((pattern) => pattern.test(normalized));
|
|
1027
|
+
if (!hasToolSignal) {
|
|
1028
|
+
return undefined;
|
|
1029
|
+
}
|
|
1030
|
+
const noTrackedChanges = !workingTreeEffect?.enabled || workingTreeEffect.touchedPaths.length === 0;
|
|
1031
|
+
if (!noTrackedChanges) {
|
|
1032
|
+
return undefined;
|
|
1033
|
+
}
|
|
1034
|
+
return "It reported missing runtime tools/permissions and made no tracked workspace changes. Check provider capabilities or switch to a compatible agent provider for file-editing tasks.";
|
|
1035
|
+
}
|
|
1036
|
+
function clampText(value, maxChars) {
|
|
1037
|
+
if (value.length <= maxChars) {
|
|
1038
|
+
return value;
|
|
1039
|
+
}
|
|
1040
|
+
const head = value.slice(0, Math.floor(maxChars * 0.7));
|
|
1041
|
+
const tail = value.slice(-(maxChars - head.length - 20));
|
|
1042
|
+
return `${head}\n...[truncated]...\n${tail}`;
|
|
1043
|
+
}
|
|
1044
|
+
//# sourceMappingURL=orchestration.service.js.map
|