@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.
Files changed (288) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +20 -0
  3. package/dist/apps/runtime/create-opengoat-runtime.d.ts +14 -0
  4. package/dist/apps/runtime/create-opengoat-runtime.js +23 -0
  5. package/dist/apps/runtime/create-opengoat-runtime.js.map +1 -0
  6. package/dist/core/acp/application/acp-agent.d.ts +60 -0
  7. package/dist/core/acp/application/acp-agent.js +424 -0
  8. package/dist/core/acp/application/acp-agent.js.map +1 -0
  9. package/dist/core/acp/application/session-store.d.ts +13 -0
  10. package/dist/core/acp/application/session-store.js +40 -0
  11. package/dist/core/acp/application/session-store.js.map +1 -0
  12. package/dist/core/acp/domain/meta.d.ts +7 -0
  13. package/dist/core/acp/domain/meta.js +31 -0
  14. package/dist/core/acp/domain/meta.js.map +1 -0
  15. package/dist/core/acp/domain/session.d.ts +23 -0
  16. package/dist/core/acp/domain/session.js +2 -0
  17. package/dist/core/acp/domain/session.js.map +1 -0
  18. package/dist/core/acp/index.d.ts +5 -0
  19. package/dist/core/acp/index.js +4 -0
  20. package/dist/core/acp/index.js.map +1 -0
  21. package/dist/core/agents/application/agent-manifest.service.d.ts +20 -0
  22. package/dist/core/agents/application/agent-manifest.service.js +89 -0
  23. package/dist/core/agents/application/agent-manifest.service.js.map +1 -0
  24. package/dist/core/agents/application/agent.service.d.ts +25 -0
  25. package/dist/core/agents/application/agent.service.js +191 -0
  26. package/dist/core/agents/application/agent.service.js.map +1 -0
  27. package/dist/core/agents/application/workspace-context.service.d.ts +28 -0
  28. package/dist/core/agents/application/workspace-context.service.js +157 -0
  29. package/dist/core/agents/application/workspace-context.service.js.map +1 -0
  30. package/dist/core/agents/domain/agent-manifest.d.ts +37 -0
  31. package/dist/core/agents/domain/agent-manifest.js +228 -0
  32. package/dist/core/agents/domain/agent-manifest.js.map +1 -0
  33. package/dist/core/agents/domain/workspace-context.d.ts +13 -0
  34. package/dist/core/agents/domain/workspace-context.js +14 -0
  35. package/dist/core/agents/domain/workspace-context.js.map +1 -0
  36. package/dist/core/agents/index.d.ts +6 -0
  37. package/dist/core/agents/index.js +6 -0
  38. package/dist/core/agents/index.js.map +1 -0
  39. package/dist/core/bootstrap/application/bootstrap.service.d.ts +24 -0
  40. package/dist/core/bootstrap/application/bootstrap.service.js +108 -0
  41. package/dist/core/bootstrap/application/bootstrap.service.js.map +1 -0
  42. package/dist/core/bootstrap/index.d.ts +1 -0
  43. package/dist/core/bootstrap/index.js +2 -0
  44. package/dist/core/bootstrap/index.js.map +1 -0
  45. package/dist/core/domain/agent-id.d.ts +3 -0
  46. package/dist/core/domain/agent-id.js +12 -0
  47. package/dist/core/domain/agent-id.js.map +1 -0
  48. package/dist/core/domain/agent.d.ts +39 -0
  49. package/dist/core/domain/agent.js +2 -0
  50. package/dist/core/domain/agent.js.map +1 -0
  51. package/dist/core/domain/opengoat-paths.d.ts +29 -0
  52. package/dist/core/domain/opengoat-paths.js +2 -0
  53. package/dist/core/domain/opengoat-paths.js.map +1 -0
  54. package/dist/core/gateway/domain/protocol.d.ts +113 -0
  55. package/dist/core/gateway/domain/protocol.js +394 -0
  56. package/dist/core/gateway/domain/protocol.js.map +1 -0
  57. package/dist/core/gateway/index.d.ts +2 -0
  58. package/dist/core/gateway/index.js +2 -0
  59. package/dist/core/gateway/index.js.map +1 -0
  60. package/dist/core/llm/application/vercel-ai-text-runtime.d.ts +26 -0
  61. package/dist/core/llm/application/vercel-ai-text-runtime.js +223 -0
  62. package/dist/core/llm/application/vercel-ai-text-runtime.js.map +1 -0
  63. package/dist/core/llm/domain/text-runtime.d.ts +22 -0
  64. package/dist/core/llm/domain/text-runtime.js +2 -0
  65. package/dist/core/llm/domain/text-runtime.js.map +1 -0
  66. package/dist/core/llm/index.d.ts +2 -0
  67. package/dist/core/llm/index.js +2 -0
  68. package/dist/core/llm/index.js.map +1 -0
  69. package/dist/core/logging/application/structured-logger.d.ts +29 -0
  70. package/dist/core/logging/application/structured-logger.js +86 -0
  71. package/dist/core/logging/application/structured-logger.js.map +1 -0
  72. package/dist/core/logging/domain/logger.d.ts +16 -0
  73. package/dist/core/logging/domain/logger.js +18 -0
  74. package/dist/core/logging/domain/logger.js.map +1 -0
  75. package/dist/core/logging/index.d.ts +3 -0
  76. package/dist/core/logging/index.js +3 -0
  77. package/dist/core/logging/index.js.map +1 -0
  78. package/dist/core/opengoat/application/opengoat.service.d.ts +84 -0
  79. package/dist/core/opengoat/application/opengoat.service.js +308 -0
  80. package/dist/core/opengoat/application/opengoat.service.js.map +1 -0
  81. package/dist/core/opengoat/index.d.ts +1 -0
  82. package/dist/core/opengoat/index.js +2 -0
  83. package/dist/core/opengoat/index.js.map +1 -0
  84. package/dist/core/orchestration/application/orchestration-planner.service.d.ts +28 -0
  85. package/dist/core/orchestration/application/orchestration-planner.service.js +279 -0
  86. package/dist/core/orchestration/application/orchestration-planner.service.js.map +1 -0
  87. package/dist/core/orchestration/application/orchestration.service.d.ts +52 -0
  88. package/dist/core/orchestration/application/orchestration.service.js +1044 -0
  89. package/dist/core/orchestration/application/orchestration.service.js.map +1 -0
  90. package/dist/core/orchestration/application/routing.service.d.ts +11 -0
  91. package/dist/core/orchestration/application/routing.service.js +108 -0
  92. package/dist/core/orchestration/application/routing.service.js.map +1 -0
  93. package/dist/core/orchestration/domain/loop.d.ts +119 -0
  94. package/dist/core/orchestration/domain/loop.js +2 -0
  95. package/dist/core/orchestration/domain/loop.js.map +1 -0
  96. package/dist/core/orchestration/domain/routing.d.ts +58 -0
  97. package/dist/core/orchestration/domain/routing.js +2 -0
  98. package/dist/core/orchestration/domain/routing.js.map +1 -0
  99. package/dist/core/orchestration/domain/run-events.d.ts +21 -0
  100. package/dist/core/orchestration/domain/run-events.js +2 -0
  101. package/dist/core/orchestration/domain/run-events.js.map +1 -0
  102. package/dist/core/orchestration/index.d.ts +6 -0
  103. package/dist/core/orchestration/index.js +4 -0
  104. package/dist/core/orchestration/index.js.map +1 -0
  105. package/dist/core/plugins/application/plugin.service.d.ts +32 -0
  106. package/dist/core/plugins/application/plugin.service.js +236 -0
  107. package/dist/core/plugins/application/plugin.service.js.map +1 -0
  108. package/dist/core/plugins/domain/openclaw-compat.d.ts +60 -0
  109. package/dist/core/plugins/domain/openclaw-compat.js +9 -0
  110. package/dist/core/plugins/domain/openclaw-compat.js.map +1 -0
  111. package/dist/core/plugins/index.d.ts +3 -0
  112. package/dist/core/plugins/index.js +3 -0
  113. package/dist/core/plugins/index.js.map +1 -0
  114. package/dist/core/ports/command-runner.port.d.ts +14 -0
  115. package/dist/core/ports/command-runner.port.js +2 -0
  116. package/dist/core/ports/command-runner.port.js.map +1 -0
  117. package/dist/core/ports/file-system.port.d.ts +9 -0
  118. package/dist/core/ports/file-system.port.js +2 -0
  119. package/dist/core/ports/file-system.port.js.map +1 -0
  120. package/dist/core/ports/path.port.d.ts +3 -0
  121. package/dist/core/ports/path.port.js +2 -0
  122. package/dist/core/ports/path.port.js.map +1 -0
  123. package/dist/core/ports/paths-provider.port.d.ts +4 -0
  124. package/dist/core/ports/paths-provider.port.js +2 -0
  125. package/dist/core/ports/paths-provider.port.js.map +1 -0
  126. package/dist/core/providers/application/provider.service.d.ts +80 -0
  127. package/dist/core/providers/application/provider.service.js +391 -0
  128. package/dist/core/providers/application/provider.service.js.map +1 -0
  129. package/dist/core/providers/base-provider.d.ts +20 -0
  130. package/dist/core/providers/base-provider.js +44 -0
  131. package/dist/core/providers/base-provider.js.map +1 -0
  132. package/dist/core/providers/cli-provider.d.ts +26 -0
  133. package/dist/core/providers/cli-provider.js +151 -0
  134. package/dist/core/providers/cli-provider.js.map +1 -0
  135. package/dist/core/providers/command-executor.d.ts +12 -0
  136. package/dist/core/providers/command-executor.js +76 -0
  137. package/dist/core/providers/command-executor.js.map +1 -0
  138. package/dist/core/providers/errors.d.ts +30 -0
  139. package/dist/core/providers/errors.js +52 -0
  140. package/dist/core/providers/errors.js.map +1 -0
  141. package/dist/core/providers/index.d.ts +18 -0
  142. package/dist/core/providers/index.js +27 -0
  143. package/dist/core/providers/index.js.map +1 -0
  144. package/dist/core/providers/loader.d.ts +2 -0
  145. package/dist/core/providers/loader.js +99 -0
  146. package/dist/core/providers/loader.js.map +1 -0
  147. package/dist/core/providers/onboarding.d.ts +13 -0
  148. package/dist/core/providers/onboarding.js +149 -0
  149. package/dist/core/providers/onboarding.js.map +1 -0
  150. package/dist/core/providers/provider-module.d.ts +19 -0
  151. package/dist/core/providers/provider-module.js +2 -0
  152. package/dist/core/providers/provider-module.js.map +1 -0
  153. package/dist/core/providers/provider-session.d.ts +3 -0
  154. package/dist/core/providers/provider-session.js +44 -0
  155. package/dist/core/providers/provider-session.js.map +1 -0
  156. package/dist/core/providers/providers/claude/index.d.ts +4 -0
  157. package/dist/core/providers/providers/claude/index.js +19 -0
  158. package/dist/core/providers/providers/claude/index.js.map +1 -0
  159. package/dist/core/providers/providers/claude/provider.d.ts +8 -0
  160. package/dist/core/providers/providers/claude/provider.js +106 -0
  161. package/dist/core/providers/providers/claude/provider.js.map +1 -0
  162. package/dist/core/providers/providers/codex/index.d.ts +4 -0
  163. package/dist/core/providers/providers/codex/index.js +19 -0
  164. package/dist/core/providers/providers/codex/index.js.map +1 -0
  165. package/dist/core/providers/providers/codex/provider.d.ts +7 -0
  166. package/dist/core/providers/providers/codex/provider.js +31 -0
  167. package/dist/core/providers/providers/codex/provider.js.map +1 -0
  168. package/dist/core/providers/providers/cursor/index.d.ts +4 -0
  169. package/dist/core/providers/providers/cursor/index.js +19 -0
  170. package/dist/core/providers/providers/cursor/index.js.map +1 -0
  171. package/dist/core/providers/providers/cursor/provider.d.ts +11 -0
  172. package/dist/core/providers/providers/cursor/provider.js +90 -0
  173. package/dist/core/providers/providers/cursor/provider.js.map +1 -0
  174. package/dist/core/providers/providers/extended-http/catalog.d.ts +40 -0
  175. package/dist/core/providers/providers/extended-http/catalog.js +728 -0
  176. package/dist/core/providers/providers/extended-http/catalog.js.map +1 -0
  177. package/dist/core/providers/providers/extended-http/index.d.ts +5 -0
  178. package/dist/core/providers/providers/extended-http/index.js +13 -0
  179. package/dist/core/providers/providers/extended-http/index.js.map +1 -0
  180. package/dist/core/providers/providers/extended-http/provider.d.ts +31 -0
  181. package/dist/core/providers/providers/extended-http/provider.js +580 -0
  182. package/dist/core/providers/providers/extended-http/provider.js.map +1 -0
  183. package/dist/core/providers/providers/gemini/index.d.ts +4 -0
  184. package/dist/core/providers/providers/gemini/index.js +37 -0
  185. package/dist/core/providers/providers/gemini/index.js.map +1 -0
  186. package/dist/core/providers/providers/gemini/provider.d.ts +7 -0
  187. package/dist/core/providers/providers/gemini/provider.js +59 -0
  188. package/dist/core/providers/providers/gemini/provider.js.map +1 -0
  189. package/dist/core/providers/providers/grok/index.d.ts +4 -0
  190. package/dist/core/providers/providers/grok/index.js +41 -0
  191. package/dist/core/providers/providers/grok/index.js.map +1 -0
  192. package/dist/core/providers/providers/grok/provider.d.ts +13 -0
  193. package/dist/core/providers/providers/grok/provider.js +95 -0
  194. package/dist/core/providers/providers/grok/provider.js.map +1 -0
  195. package/dist/core/providers/providers/openai/index.d.ts +4 -0
  196. package/dist/core/providers/providers/openai/index.js +45 -0
  197. package/dist/core/providers/providers/openai/index.js.map +1 -0
  198. package/dist/core/providers/providers/openai/provider.d.ts +14 -0
  199. package/dist/core/providers/providers/openai/provider.js +203 -0
  200. package/dist/core/providers/providers/openai/provider.js.map +1 -0
  201. package/dist/core/providers/providers/openclaw/index.d.ts +4 -0
  202. package/dist/core/providers/providers/openclaw/index.js +31 -0
  203. package/dist/core/providers/providers/openclaw/index.js.map +1 -0
  204. package/dist/core/providers/providers/openclaw/provider.d.ts +11 -0
  205. package/dist/core/providers/providers/openclaw/provider.js +108 -0
  206. package/dist/core/providers/providers/openclaw/provider.js.map +1 -0
  207. package/dist/core/providers/providers/opencode/index.d.ts +4 -0
  208. package/dist/core/providers/providers/opencode/index.js +23 -0
  209. package/dist/core/providers/providers/opencode/index.js.map +1 -0
  210. package/dist/core/providers/providers/opencode/provider.d.ts +11 -0
  211. package/dist/core/providers/providers/opencode/provider.js +215 -0
  212. package/dist/core/providers/providers/opencode/provider.js.map +1 -0
  213. package/dist/core/providers/providers/openrouter/index.d.ts +4 -0
  214. package/dist/core/providers/providers/openrouter/index.js +37 -0
  215. package/dist/core/providers/providers/openrouter/index.js.map +1 -0
  216. package/dist/core/providers/providers/openrouter/provider.d.ts +13 -0
  217. package/dist/core/providers/providers/openrouter/provider.js +80 -0
  218. package/dist/core/providers/providers/openrouter/provider.js.map +1 -0
  219. package/dist/core/providers/registry.d.ts +13 -0
  220. package/dist/core/providers/registry.js +36 -0
  221. package/dist/core/providers/registry.js.map +1 -0
  222. package/dist/core/providers/types.d.ts +101 -0
  223. package/dist/core/providers/types.js +2 -0
  224. package/dist/core/providers/types.js.map +1 -0
  225. package/dist/core/scenarios/application/scenario-runner.service.d.ts +23 -0
  226. package/dist/core/scenarios/application/scenario-runner.service.js +172 -0
  227. package/dist/core/scenarios/application/scenario-runner.service.js.map +1 -0
  228. package/dist/core/scenarios/domain/scenario.d.ts +43 -0
  229. package/dist/core/scenarios/domain/scenario.js +2 -0
  230. package/dist/core/scenarios/domain/scenario.js.map +1 -0
  231. package/dist/core/scenarios/index.d.ts +2 -0
  232. package/dist/core/scenarios/index.js +2 -0
  233. package/dist/core/scenarios/index.js.map +1 -0
  234. package/dist/core/sessions/application/session.service.d.ts +74 -0
  235. package/dist/core/sessions/application/session.service.js +895 -0
  236. package/dist/core/sessions/application/session.service.js.map +1 -0
  237. package/dist/core/sessions/domain/session.d.ts +80 -0
  238. package/dist/core/sessions/domain/session.js +24 -0
  239. package/dist/core/sessions/domain/session.js.map +1 -0
  240. package/dist/core/sessions/domain/transcript.d.ts +28 -0
  241. package/dist/core/sessions/domain/transcript.js +10 -0
  242. package/dist/core/sessions/domain/transcript.js.map +1 -0
  243. package/dist/core/sessions/errors.d.ts +12 -0
  244. package/dist/core/sessions/errors.js +22 -0
  245. package/dist/core/sessions/errors.js.map +1 -0
  246. package/dist/core/sessions/index.d.ts +6 -0
  247. package/dist/core/sessions/index.js +4 -0
  248. package/dist/core/sessions/index.js.map +1 -0
  249. package/dist/core/skills/application/skill.service.d.ts +24 -0
  250. package/dist/core/skills/application/skill.service.js +375 -0
  251. package/dist/core/skills/application/skill.service.js.map +1 -0
  252. package/dist/core/skills/domain/skill.d.ts +62 -0
  253. package/dist/core/skills/domain/skill.js +46 -0
  254. package/dist/core/skills/domain/skill.js.map +1 -0
  255. package/dist/core/skills/index.d.ts +3 -0
  256. package/dist/core/skills/index.js +3 -0
  257. package/dist/core/skills/index.js.map +1 -0
  258. package/dist/core/templates/default-templates.d.ts +19 -0
  259. package/dist/core/templates/default-templates.js +259 -0
  260. package/dist/core/templates/default-templates.js.map +1 -0
  261. package/dist/index.d.ts +27 -0
  262. package/dist/index.js +28 -0
  263. package/dist/index.js.map +1 -0
  264. package/dist/platform/node/acp-server.d.ts +12 -0
  265. package/dist/platform/node/acp-server.js +18 -0
  266. package/dist/platform/node/acp-server.js.map +1 -0
  267. package/dist/platform/node/dotenv.d.ts +6 -0
  268. package/dist/platform/node/dotenv.js +67 -0
  269. package/dist/platform/node/dotenv.js.map +1 -0
  270. package/dist/platform/node/node-command-runner.d.ts +4 -0
  271. package/dist/platform/node/node-command-runner.js +29 -0
  272. package/dist/platform/node/node-command-runner.js.map +1 -0
  273. package/dist/platform/node/node-file-system.d.ts +10 -0
  274. package/dist/platform/node/node-file-system.js +47 -0
  275. package/dist/platform/node/node-file-system.js.map +1 -0
  276. package/dist/platform/node/node-logger.d.ts +9 -0
  277. package/dist/platform/node/node-logger.js +124 -0
  278. package/dist/platform/node/node-logger.js.map +1 -0
  279. package/dist/platform/node/node-path.port.d.ts +9 -0
  280. package/dist/platform/node/node-path.port.js +41 -0
  281. package/dist/platform/node/node-path.port.js.map +1 -0
  282. package/dist/platform/node/opengoat-gateway-client.d.ts +19 -0
  283. package/dist/platform/node/opengoat-gateway-client.js +194 -0
  284. package/dist/platform/node/opengoat-gateway-client.js.map +1 -0
  285. package/dist/platform/node/opengoat-gateway-server.d.ts +53 -0
  286. package/dist/platform/node/opengoat-gateway-server.js +906 -0
  287. package/dist/platform/node/opengoat-gateway-server.js.map +1 -0
  288. 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