@amoree-code/ocean 0.0.0-stage → 1.1.0

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 (304) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +284 -2
  3. package/dist/application/auth/auth-orchestrator.js +77 -0
  4. package/dist/application/clients/clients.js +646 -0
  5. package/dist/application/clients/editors.js +398 -0
  6. package/dist/application/clients/hooks.js +335 -0
  7. package/dist/application/clients/native.js +410 -0
  8. package/dist/application/clients/opencode-plugin.js +158 -0
  9. package/dist/application/clients/registry.js +188 -0
  10. package/dist/application/gateway/webhook-gateway.js +137 -0
  11. package/dist/application/install/provider-installer.js +184 -0
  12. package/dist/application/integrations/charter-copies.js +76 -0
  13. package/dist/application/integrations/claude-vscode.js +106 -0
  14. package/dist/application/integrations/engine-entry-repair.js +105 -0
  15. package/dist/application/integrations/skill-filter.js +254 -0
  16. package/dist/application/layout/layout-apply.js +443 -0
  17. package/dist/application/layout/layout-plan.js +236 -0
  18. package/dist/application/layout/workspace-template.js +34 -0
  19. package/dist/application/onboarding/state.js +23 -0
  20. package/dist/application/onboarding/steps.js +249 -0
  21. package/dist/application/profiles/profile-distribution.js +53 -0
  22. package/dist/application/profiles/profile-suggestion.js +39 -0
  23. package/dist/application/profiles/profile-upgrade-service.js +208 -0
  24. package/dist/application/pull/history-index.js +476 -0
  25. package/dist/application/pull/project-match.js +132 -0
  26. package/dist/application/pull/pull.js +202 -0
  27. package/dist/application/pull/server-capture.js +307 -0
  28. package/dist/application/scheduler/local-scheduler.js +210 -0
  29. package/dist/composition/runtime.js +64 -0
  30. package/dist/engine-root.js +9 -0
  31. package/dist/infrastructure/filesystem/profile-loader.js +46 -0
  32. package/dist/infrastructure/filesystem/runtime-config-loader.js +29 -0
  33. package/dist/infrastructure/filesystem/skill-loader.js +84 -0
  34. package/dist/infrastructure/filesystem/workspace-capability.js +37 -0
  35. package/dist/infrastructure/observability/runtime-logger.js +16 -0
  36. package/dist/infrastructure/process/agent-tool-executor.js +99 -0
  37. package/dist/infrastructure/process/cli-process.js +49 -0
  38. package/dist/infrastructure/process/interactive-process.js +145 -0
  39. package/dist/infrastructure/process/process-supervisor.js +194 -0
  40. package/dist/infrastructure/process/service.js +252 -0
  41. package/dist/infrastructure/providers/browser-provider.js +2 -0
  42. package/dist/infrastructure/providers/claude-sdk.js +137 -0
  43. package/dist/infrastructure/providers/client-home.js +14 -0
  44. package/dist/infrastructure/providers/fake-browser-provider.js +146 -0
  45. package/dist/infrastructure/providers/ollama-embedder.js +74 -0
  46. package/dist/infrastructure/providers/openai-compatible.js +268 -0
  47. package/dist/infrastructure/providers/playwright-browser-provider.js +380 -0
  48. package/dist/infrastructure/providers/provider-capabilities.js +50 -0
  49. package/dist/infrastructure/providers/provider-invocation.js +184 -0
  50. package/dist/infrastructure/providers/provider-registry.js +77 -0
  51. package/dist/infrastructure/providers/providers.js +134 -0
  52. package/dist/infrastructure/wrappers/wrapper-manager.js +374 -0
  53. package/dist/interfaces/cli/auth-command.js +54 -0
  54. package/dist/interfaces/cli/browser-bridge-command.js +93 -0
  55. package/dist/interfaces/cli/browser-command.js +119 -0
  56. package/dist/interfaces/cli/capture-command.js +125 -0
  57. package/dist/interfaces/cli/client-test-command.js +133 -0
  58. package/dist/interfaces/cli/clients-command.js +129 -0
  59. package/dist/interfaces/cli/command-registry.js +1211 -0
  60. package/dist/interfaces/cli/context-command.js +172 -0
  61. package/dist/interfaces/cli/daily-command.js +131 -0
  62. package/dist/interfaces/cli/governance-command.js +55 -0
  63. package/dist/interfaces/cli/handoff-command.js +35 -0
  64. package/dist/interfaces/cli/help-command.js +207 -0
  65. package/dist/interfaces/cli/idea-command.js +47 -0
  66. package/dist/interfaces/cli/intercept-command.js +317 -0
  67. package/dist/interfaces/cli/layout-command.js +27 -0
  68. package/dist/interfaces/cli/market-command.js +52 -0
  69. package/dist/interfaces/cli/mcp-gateway-command.js +30 -0
  70. package/dist/interfaces/cli/memory-command.js +317 -0
  71. package/dist/interfaces/cli/migrate-command.js +26 -0
  72. package/dist/interfaces/cli/observe-command.js +78 -0
  73. package/dist/interfaces/cli/onboard-command.js +123 -0
  74. package/dist/interfaces/cli/operate-command.js +59 -0
  75. package/dist/interfaces/cli/profiles-command.js +70 -0
  76. package/dist/interfaces/cli/pull-command.js +54 -0
  77. package/dist/interfaces/cli/setup-command.js +143 -0
  78. package/dist/interfaces/cli/tasks-command.js +249 -0
  79. package/dist/main.js +49 -0
  80. package/dist/node-warnings.js +17 -0
  81. package/dist/version.js +8 -0
  82. package/market/mcp/chrome-devtools/ocean.capability.json +28 -0
  83. package/market/mcp/gateway/ocean.capability.json +21 -0
  84. package/market/mcp/obsidian/ocean.capability.json +21 -0
  85. package/market/mcp/ocean/ocean.capability.json +21 -0
  86. package/market/mcp/playwright/ocean.capability.json +23 -0
  87. package/node_modules/@ocean/brain/dist/brain/brain-eval.js +29 -0
  88. package/node_modules/@ocean/brain/dist/brain/brain-markdown.js +226 -0
  89. package/node_modules/@ocean/brain/dist/brain/brain-reindex.js +247 -0
  90. package/node_modules/@ocean/brain/dist/brain/brain-service.js +320 -0
  91. package/node_modules/@ocean/brain/dist/capture/inbox-sync.js +22 -0
  92. package/node_modules/@ocean/brain/dist/evidence/verification-report.js +13 -0
  93. package/node_modules/@ocean/brain/dist/infrastructure/brain/brain-index.js +285 -0
  94. package/node_modules/@ocean/brain/dist/memory/brain-dump.js +145 -0
  95. package/node_modules/@ocean/brain/dist/memory/daily-narrative.js +142 -0
  96. package/node_modules/@ocean/brain/dist/memory/index-sync.js +124 -0
  97. package/node_modules/@ocean/brain/dist/memory/lesson-activation.js +93 -0
  98. package/node_modules/@ocean/brain/dist/memory/lesson-learning.js +188 -0
  99. package/node_modules/@ocean/brain/dist/memory/lessons.js +321 -0
  100. package/node_modules/@ocean/brain/dist/memory/memory-audit.js +100 -0
  101. package/node_modules/@ocean/brain/dist/memory/memory-digest.js +49 -0
  102. package/node_modules/@ocean/brain/dist/memory/memory-harvest.js +345 -0
  103. package/node_modules/@ocean/brain/dist/memory/memory-paths.js +174 -0
  104. package/node_modules/@ocean/brain/dist/memory/memory-review.js +278 -0
  105. package/node_modules/@ocean/brain/dist/memory/memory-trash.js +152 -0
  106. package/node_modules/@ocean/brain/dist/memory/memory-write.js +216 -0
  107. package/node_modules/@ocean/brain/dist/memory/model-narrative.js +129 -0
  108. package/node_modules/@ocean/brain/dist/memory/profile-facts.js +103 -0
  109. package/node_modules/@ocean/brain/dist/memory/session-promotion.js +61 -0
  110. package/node_modules/@ocean/brain/dist/memory/session-summary.js +113 -0
  111. package/node_modules/@ocean/brain/dist/memory/signal-detection.js +66 -0
  112. package/node_modules/@ocean/brain/package.json +11 -0
  113. package/node_modules/@ocean/context/dist/context/context-compression.js +66 -0
  114. package/node_modules/@ocean/context/dist/context/context-cost.js +587 -0
  115. package/node_modules/@ocean/context/dist/context/context-ladder.js +363 -0
  116. package/node_modules/@ocean/context/dist/context/context-packet.js +213 -0
  117. package/node_modules/@ocean/context/dist/context/context-references.js +255 -0
  118. package/node_modules/@ocean/context/dist/context/intent-router.js +259 -0
  119. package/node_modules/@ocean/context/dist/context/resource-injection.js +36 -0
  120. package/node_modules/@ocean/context/package.json +11 -0
  121. package/node_modules/@ocean/core/dist/domain/brain/brain-record.js +22 -0
  122. package/node_modules/@ocean/core/dist/domain/brain/record-scope.js +22 -0
  123. package/node_modules/@ocean/core/dist/domain/capabilities/browser-contract.js +64 -0
  124. package/node_modules/@ocean/core/dist/domain/capabilities/capability-contract.js +16 -0
  125. package/node_modules/@ocean/core/dist/domain/capabilities/capability-manifest.js +72 -0
  126. package/node_modules/@ocean/core/dist/domain/context/context-validator.js +5 -0
  127. package/node_modules/@ocean/core/dist/domain/context/context.js +34 -0
  128. package/node_modules/@ocean/core/dist/domain/context/project-id.js +30 -0
  129. package/node_modules/@ocean/core/dist/domain/evidence/evidence.js +16 -0
  130. package/node_modules/@ocean/core/dist/domain/mcp/mcp-contract.js +36 -0
  131. package/node_modules/@ocean/core/dist/domain/mcp/memory-contract.js +41 -0
  132. package/node_modules/@ocean/core/dist/domain/ports/brain-index-port.js +7 -0
  133. package/node_modules/@ocean/core/dist/domain/ports/browser-port.js +8 -0
  134. package/node_modules/@ocean/core/dist/domain/ports/embedder-port.js +2 -0
  135. package/node_modules/@ocean/core/dist/domain/ports/platform-ports.js +2 -0
  136. package/node_modules/@ocean/core/dist/domain/ports/runtime-ports.js +2 -0
  137. package/node_modules/@ocean/core/dist/domain/ports/session-store-port.js +2 -0
  138. package/node_modules/@ocean/core/dist/domain/process/process-events.js +5 -0
  139. package/node_modules/@ocean/core/dist/domain/profiles/profile-distribution.js +19 -0
  140. package/node_modules/@ocean/core/dist/domain/profiles/profile-policy.js +34 -0
  141. package/node_modules/@ocean/core/dist/domain/profiles/profile-selection.js +73 -0
  142. package/node_modules/@ocean/core/dist/domain/profiles/profile-upgrade.js +123 -0
  143. package/node_modules/@ocean/core/dist/domain/profiles/profile-validator.js +5 -0
  144. package/node_modules/@ocean/core/dist/domain/profiles/profile.js +218 -0
  145. package/node_modules/@ocean/core/dist/domain/profiles/runtime-config.js +30 -0
  146. package/node_modules/@ocean/core/dist/domain/providers/provider-capability.js +28 -0
  147. package/node_modules/@ocean/core/dist/domain/providers/provider.js +2 -0
  148. package/node_modules/@ocean/core/dist/domain/redaction/redaction.js +86 -0
  149. package/node_modules/@ocean/core/dist/domain/runs/agent-tools.js +92 -0
  150. package/node_modules/@ocean/core/dist/domain/runs/execution-plan.js +71 -0
  151. package/node_modules/@ocean/core/dist/domain/runs/run-contract.js +38 -0
  152. package/node_modules/@ocean/core/dist/domain/sessions/entry-contract.js +31 -0
  153. package/node_modules/@ocean/core/dist/domain/sessions/handoff.js +78 -0
  154. package/node_modules/@ocean/core/dist/domain/sessions/session.js +58 -0
  155. package/node_modules/@ocean/core/dist/domain/skills/default-skills.js +29 -0
  156. package/node_modules/@ocean/core/dist/domain/skills/skill-validator.js +5 -0
  157. package/node_modules/@ocean/core/dist/domain/skills/skill.js +11 -0
  158. package/node_modules/@ocean/core/dist/domain/tasks/task-id.js +85 -0
  159. package/node_modules/@ocean/core/dist/shared/fs-utils.js +212 -0
  160. package/node_modules/@ocean/core/dist/shared/local-date.js +16 -0
  161. package/node_modules/@ocean/core/dist/shared/paths.js +324 -0
  162. package/node_modules/@ocean/core/dist/shared/project-resolution.js +1065 -0
  163. package/node_modules/@ocean/core/dist/shared/task-reference.js +133 -0
  164. package/node_modules/@ocean/core/package.json +14 -0
  165. package/node_modules/@ocean/loop/dist/handoff/handoff-service.js +133 -0
  166. package/node_modules/@ocean/loop/dist/hooks/lifecycle-hooks.js +15 -0
  167. package/node_modules/@ocean/loop/dist/hooks/session-end-hook.js +183 -0
  168. package/node_modules/@ocean/loop/dist/hooks/session-start-hook.js +237 -0
  169. package/node_modules/@ocean/loop/dist/infrastructure/persistence/session-store.js +690 -0
  170. package/node_modules/@ocean/loop/dist/infrastructure/persistence/session-validator.js +5 -0
  171. package/node_modules/@ocean/loop/dist/loops/iteration-graph.js +147 -0
  172. package/node_modules/@ocean/loop/dist/loops/task-loop-record.js +2 -0
  173. package/node_modules/@ocean/loop/dist/loops/task-loop.js +281 -0
  174. package/node_modules/@ocean/loop/dist/operations/memory-knowledge-operations.js +263 -0
  175. package/node_modules/@ocean/loop/dist/operations/operation-contract.js +227 -0
  176. package/node_modules/@ocean/loop/dist/operations/operation-scope.js +28 -0
  177. package/node_modules/@ocean/loop/dist/operations/project-operations.js +93 -0
  178. package/node_modules/@ocean/loop/dist/operations/record-io.js +65 -0
  179. package/node_modules/@ocean/loop/dist/operations/record-operations.js +45 -0
  180. package/node_modules/@ocean/loop/dist/operations/task-linker.js +40 -0
  181. package/node_modules/@ocean/loop/dist/operations/task-operations.js +279 -0
  182. package/node_modules/@ocean/loop/dist/operations/write-guard.js +157 -0
  183. package/node_modules/@ocean/loop/dist/runs/prompt-assembly.js +112 -0
  184. package/node_modules/@ocean/loop/dist/runs/run-agent.js +425 -0
  185. package/node_modules/@ocean/loop/dist/runs/run-authorization.js +24 -0
  186. package/node_modules/@ocean/loop/dist/runs/tool-loop.js +173 -0
  187. package/node_modules/@ocean/loop/dist/sessions/session-closeout.js +150 -0
  188. package/node_modules/@ocean/loop/dist/sessions/session-pointer.js +133 -0
  189. package/node_modules/@ocean/loop/dist/sessions/session-projects.js +26 -0
  190. package/node_modules/@ocean/loop/dist/sessions/session-retention.js +132 -0
  191. package/node_modules/@ocean/loop/dist/tasks/archive-tasks.js +210 -0
  192. package/node_modules/@ocean/loop/dist/tasks/checkpoint-task.js +31 -0
  193. package/node_modules/@ocean/loop/dist/tasks/claim-task.js +126 -0
  194. package/node_modules/@ocean/loop/dist/tasks/list-tasks.js +113 -0
  195. package/node_modules/@ocean/loop/dist/tasks/new-task.js +42 -0
  196. package/node_modules/@ocean/loop/dist/tasks/renumber-tasks.js +134 -0
  197. package/node_modules/@ocean/loop/dist/tasks/verification-readiness.js +78 -0
  198. package/node_modules/@ocean/loop/dist/tasks/verify-task.js +234 -0
  199. package/node_modules/@ocean/loop/package.json +11 -0
  200. package/node_modules/@ocean/market/dist/adapters/mcp-adapter.js +64 -0
  201. package/node_modules/@ocean/market/dist/adapters/owned-directory.js +89 -0
  202. package/node_modules/@ocean/market/dist/adapters/plugin-adapter.js +39 -0
  203. package/node_modules/@ocean/market/dist/adapters/skill-adapter.js +32 -0
  204. package/node_modules/@ocean/market/dist/capability/adapter.js +2 -0
  205. package/node_modules/@ocean/market/dist/capability/catalog.js +63 -0
  206. package/node_modules/@ocean/market/dist/capability/content-digest.js +40 -0
  207. package/node_modules/@ocean/market/dist/capability/registry.js +81 -0
  208. package/node_modules/@ocean/market/dist/install/default-marketplace.js +47 -0
  209. package/node_modules/@ocean/market/dist/install/lock.js +34 -0
  210. package/node_modules/@ocean/market/dist/install/marketplace-service.js +165 -0
  211. package/node_modules/@ocean/market/dist/mcp/browser-bridge.js +263 -0
  212. package/node_modules/@ocean/market/dist/mcp/mcp-connection.js +67 -0
  213. package/node_modules/@ocean/market/dist/mcp/mcp-gateway.js +317 -0
  214. package/node_modules/@ocean/market/dist/mcp/mcp-launch.js +102 -0
  215. package/node_modules/@ocean/market/dist/mcp/mcp-runner.js +32 -0
  216. package/node_modules/@ocean/market/dist/security/description-scan.js +45 -0
  217. package/node_modules/@ocean/market/dist/security/permissions.js +57 -0
  218. package/node_modules/@ocean/market/dist/security/sandbox.js +74 -0
  219. package/node_modules/@ocean/market/dist/skills/core-skill-sync.js +113 -0
  220. package/node_modules/@ocean/market/dist/skills/signal-detection.js +84 -0
  221. package/node_modules/@ocean/market/dist/skills/skill-curation.js +123 -0
  222. package/node_modules/@ocean/market/dist/skills/skill-hub.js +220 -0
  223. package/node_modules/@ocean/market/dist/skills/task-observer.js +165 -0
  224. package/node_modules/@ocean/market/package.json +11 -0
  225. package/node_modules/@ocean/services/dist/browser/browser-service.js +124 -0
  226. package/node_modules/@ocean/services/dist/browser/browser-session.js +287 -0
  227. package/node_modules/@ocean/services/dist/browser/browser-task-runner.js +70 -0
  228. package/node_modules/@ocean/services/dist/doctor/workspace-doctor.js +547 -0
  229. package/node_modules/@ocean/services/dist/infrastructure/mcp/call-ledger.js +114 -0
  230. package/node_modules/@ocean/services/dist/infrastructure/mcp/mcp-client.js +162 -0
  231. package/node_modules/@ocean/services/dist/infrastructure/mcp/obsidian-server.js +251 -0
  232. package/node_modules/@ocean/services/dist/infrastructure/mcp/ocean-server.js +880 -0
  233. package/node_modules/@ocean/services/dist/infrastructure/mcp/server-version.js +20 -0
  234. package/node_modules/@ocean/services/dist/infrastructure/mcp/tool-registry.js +29 -0
  235. package/node_modules/@ocean/services/dist/obsidian/conflict-log.js +68 -0
  236. package/node_modules/@ocean/services/dist/obsidian/inbox-promotion.js +80 -0
  237. package/node_modules/@ocean/services/dist/obsidian/vault-discovery.js +113 -0
  238. package/node_modules/@ocean/services/dist/obsidian/vault-ingestion.js +77 -0
  239. package/node_modules/@ocean/services/dist/obsidian/vault-sync.js +73 -0
  240. package/node_modules/@ocean/services/dist/obsidian/vault-writer.js +75 -0
  241. package/node_modules/@ocean/services/package.json +11 -0
  242. package/ocean-marketplace.json +415 -0
  243. package/package.json +67 -4
  244. package/skills/core/business-logic/SKILL.md +22 -0
  245. package/skills/core/catch-up/SKILL.md +17 -0
  246. package/skills/core/core-thinking/SKILL.md +11 -0
  247. package/skills/core/design-thinking/SKILL.md +19 -0
  248. package/skills/core/graft/SKILL.md +156 -0
  249. package/skills/core/loop/SKILL.md +20 -0
  250. package/skills/core/session-handoff/SKILL.md +15 -0
  251. package/skills/core/use-browser/SKILL.md +69 -0
  252. package/skills/core/verification/SKILL.md +11 -0
  253. package/skills/index.json +56 -0
  254. package/templates/README.md +4 -0
  255. package/templates/profiles/default.json +51 -0
  256. package/templates/profiles/developer.json +51 -0
  257. package/templates/profiles/reviewer.json +51 -0
  258. package/templates/profiles/strategist.json +51 -0
  259. package/templates/workspace/00-inbox/INBOX.md +7 -0
  260. package/templates/workspace/00-inbox/candidates/.gitkeep +0 -0
  261. package/templates/workspace/00-inbox/native/.gitkeep +0 -0
  262. package/templates/workspace/01-daily/.gitkeep +0 -0
  263. package/templates/workspace/02-personal/CONFLICTS.md +7 -0
  264. package/templates/workspace/02-personal/MEMORY.md +15 -0
  265. package/templates/workspace/02-personal/goals/.gitkeep +0 -0
  266. package/templates/workspace/02-personal/identity/.gitkeep +0 -0
  267. package/templates/workspace/02-personal/preferences/.gitkeep +0 -0
  268. package/templates/workspace/03-professional/.gitkeep +0 -0
  269. package/templates/workspace/04-projects/PROJECTS.md +5 -0
  270. package/templates/workspace/04-projects/_template/docs/.gitkeep +0 -0
  271. package/templates/workspace/04-projects/_template/memory/.gitkeep +0 -0
  272. package/templates/workspace/04-projects/_template/tasks/.gitkeep +0 -0
  273. package/templates/workspace/05-knowledge/KNOWLEDGE.md +8 -0
  274. package/templates/workspace/05-knowledge/decisions/.gitkeep +0 -0
  275. package/templates/workspace/05-knowledge/lessons/.gitkeep +0 -0
  276. package/templates/workspace/06-templates/TEMPLATES.md +25 -0
  277. package/templates/workspace/06-templates/decision.md +8 -0
  278. package/templates/workspace/06-templates/intelligence.md +32 -0
  279. package/templates/workspace/06-templates/project-claude.md +48 -0
  280. package/templates/workspace/06-templates/session.md +38 -0
  281. package/templates/workspace/06-templates/task.md +42 -0
  282. package/templates/workspace/MANIFEST.json +51 -0
  283. package/templates/workspace/bridge/config/CONFIG.md +21 -0
  284. package/templates/workspace/bridge/config/runtime.json +4 -0
  285. package/templates/workspace/charter/core.md +104 -0
  286. package/templates/workspace/charter/policies/README.md +31 -0
  287. package/templates/workspace/charter/policies/claude-code-context.md +90 -0
  288. package/templates/workspace/charter/policies/concise-output.md +29 -0
  289. package/templates/workspace/charter/policies/context.md +91 -0
  290. package/templates/workspace/charter/policies/contracts/task.md +189 -0
  291. package/templates/workspace/charter/policies/git.md +60 -0
  292. package/templates/workspace/charter/policies/graph.md +47 -0
  293. package/templates/workspace/charter/policies/knowledge.md +33 -0
  294. package/templates/workspace/charter/policies/lifecycle.md +119 -0
  295. package/templates/workspace/charter/policies/memory.md +67 -0
  296. package/templates/workspace/charter/policies/minimal-change.md +43 -0
  297. package/templates/workspace/charter/policies/models.md +57 -0
  298. package/templates/workspace/charter/policies/privacy-terms.txt +3 -0
  299. package/templates/workspace/charter/policies/response.md +39 -0
  300. package/templates/workspace/charter/policies/skill-observer.md +44 -0
  301. package/templates/workspace/charter/policies/stack.md +23 -0
  302. package/templates/workspace/charter/policies/task.md +139 -0
  303. package/templates/workspace/charter/policies/verification.md +29 -0
  304. package/templates/workspace/sessions/.gitkeep +0 -0
@@ -0,0 +1,425 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { buildProfileFactsDigest, profileFactsFile, readProfileFacts, } from "@ocean/brain/memory/profile-facts.js";
3
+ import { buildContextReferences, referenceReadDirectories, } from "@ocean/context/context/context-references.js";
4
+ import { validateContextManifest } from "@ocean/core/domain/context/context-validator.js";
5
+ import { isApiClient, profileIdentity, selectProfileClient, } from "@ocean/core/domain/profiles/profile.js";
6
+ import { executionPolicy } from "@ocean/core/domain/profiles/profile-policy.js";
7
+ import { redactRuntimeText } from "@ocean/core/domain/redaction/redaction.js";
8
+ import { buildExecutionPlan, executionPlanIdentity, usesToolLoop, } from "@ocean/core/domain/runs/execution-plan.js";
9
+ import { validateSessionEntryContract } from "@ocean/core/domain/sessions/entry-contract.js";
10
+ import { getHandoff } from "../handoff/handoff-service.js";
11
+ import { emitHook } from "../hooks/lifecycle-hooks.js";
12
+ import { finalizeSession } from "../sessions/session-closeout.js";
13
+ import { sessionProjectId } from "../sessions/session-projects.js";
14
+ import { assemblePrompt, formatActiveLessons, formatProfileContract, formatSkillIndex, PROMOTED_SKILL_MAX_BYTES, readDirectoriesOutside, } from "./prompt-assembly.js";
15
+ import { authorizeRun } from "./run-authorization.js";
16
+ import { runToolLoop } from "./tool-loop.js";
17
+ export async function runAgent(request, deps) {
18
+ const execute = deps.executeProvider;
19
+ const profile = selectProfileClient(await deps.loadProfile(request.profileName), request.client);
20
+ const handoff = request.handoffId
21
+ ? await getHandoff(request.handoffId, deps.openStore)
22
+ : null;
23
+ const effectiveTaskId = request.taskId ??
24
+ (typeof handoff?.taskId === "string" ? handoff.taskId : null);
25
+ const clientHome = deps.resolveClientHome(profile);
26
+ executionPolicy(profile, request.cwd);
27
+ // A completion client returns text and touches nothing, so it is read-only by construction.
28
+ if (!isApiClient(profile.provider))
29
+ deps.assertProviderSupportsReadOnly(profile.provider);
30
+ if (profile.writePolicy !== "none" &&
31
+ !request.runContract?.approval.approved) {
32
+ throw new Error("Writable profile runs require an approved run contract");
33
+ }
34
+ if (profile.governance?.approvalRequired && !request.runContract) {
35
+ throw new Error("Profile requires an approved run contract");
36
+ }
37
+ // Ocean executes a tool loop's tools itself, so it can enforce writes there; a writable
38
+ // tool-loop run still needs an approved run contract. CLI clients stay read-only.
39
+ const toolLoop = usesToolLoop(profile);
40
+ const plan = buildExecutionPlan(profile, {
41
+ cwd: request.cwd,
42
+ ...runtimeCapabilities,
43
+ writeAllowed: toolLoop && request.runContract?.approval.approved === true,
44
+ });
45
+ const sessionStore = await deps.openStore();
46
+ const sessionId = request.sessionId ?? randomUUID();
47
+ const session = sessionStore.create({
48
+ sessionId,
49
+ title: request.title ??
50
+ (typeof handoff?.title === "string"
51
+ ? handoff.title
52
+ : request.prompt.slice(0, 120)),
53
+ taskId: effectiveTaskId,
54
+ handoffId: request.handoffId ?? null,
55
+ provider: profile.provider,
56
+ providerSessionId: null,
57
+ parentSessionId: request.parentSessionId ?? null,
58
+ profile: profile.name,
59
+ profileIdentity: profileIdentity(profile),
60
+ workingDirectory: request.cwd,
61
+ resumeData: null,
62
+ projectId: await sessionProjectId(request.cwd),
63
+ });
64
+ if (request.runContract) {
65
+ if (request.runContract.sessionId !== sessionId) {
66
+ sessionStore.close();
67
+ throw new Error("Run contract session does not match the session being created");
68
+ }
69
+ try {
70
+ authorizeRun(sessionStore, request.runContract);
71
+ }
72
+ catch (error) {
73
+ sessionStore.close();
74
+ throw error;
75
+ }
76
+ }
77
+ try {
78
+ sessionStore.appendEvent(sessionId, "session_entry_contract", JSON.stringify(validateSessionEntryContract({
79
+ entryPoint: "ocean-run",
80
+ controlLevel: "full-head",
81
+ inputCapture: "semantic",
82
+ contextTransport: "profile-context-and-provider-adapter",
83
+ policyEnforcement: "profile-and-run-contract",
84
+ promotion: "explicit-review",
85
+ resume: profile.provider === "claude"
86
+ ? "provider-session-id"
87
+ : "unsupported",
88
+ })));
89
+ appendExecutionPlan(sessionStore, sessionId, plan);
90
+ const context = await buildContextReferences({
91
+ profile,
92
+ prompt: request.prompt,
93
+ cwd: request.cwd,
94
+ });
95
+ const factsDigest = plan.memoryNamespace
96
+ ? buildProfileFactsDigest(plan.memoryNamespace, await readProfileFacts(plan.memoryNamespace))
97
+ : { text: "", partial: false };
98
+ const skillIndex = await deps.loadSkillIndex(profile.skills, request.cwd);
99
+ const autoSkills = await deps.loadActiveLessons(request.prompt, PROMOTED_SKILL_MAX_BYTES);
100
+ for (const skill of autoSkills) {
101
+ sessionStore.appendEvent(sessionId, "skill_auto_activated", JSON.stringify({
102
+ id: skill.id,
103
+ name: skill.title,
104
+ sourceSessionId: skill.sourceSessionId ?? null,
105
+ }));
106
+ }
107
+ sessionStore.appendEvent(sessionId, "user_input", redactRuntimeText(request.prompt));
108
+ if (request.actor)
109
+ sessionStore.appendEvent(sessionId, "actor_bound", request.actor);
110
+ sessionStore.appendEvent(sessionId, "context_manifest", JSON.stringify(context.manifest));
111
+ sessionStore.updateStatus(sessionId, "running");
112
+ await emitHook("session.start", {
113
+ sessionId,
114
+ profile: profile.name,
115
+ provider: profile.provider,
116
+ });
117
+ const handoffContent = typeof handoff?.compactContext === "string"
118
+ ? `## Ocean handoff\n${handoff.compactContext}`
119
+ : "";
120
+ // A completion client runs off this machine and cannot open a file, so it gets no file
121
+ // pointers (skill index, context references) and no profile facts: local paths would leak
122
+ // the directory layout and facts are private memory that stays local.
123
+ const local = plan.transport === "cli";
124
+ const { prompt, bytes, breakdown } = assemblePrompt({
125
+ request: request.prompt,
126
+ profile: formatProfileContract(profile, {
127
+ taskId: effectiveTaskId,
128
+ handoffId: request.handoffId ?? null,
129
+ }),
130
+ instructions: profile.instructions,
131
+ skills: local
132
+ ? [formatSkillIndex(skillIndex), formatActiveLessons(autoSkills)]
133
+ .filter(Boolean)
134
+ .join("\n\n")
135
+ : "",
136
+ facts: local ? factsDigest.text : "",
137
+ handoff: handoffContent,
138
+ context: local ? context.content : "",
139
+ });
140
+ const contextHash = createHash("sha256").update(prompt).digest("hex");
141
+ sessionStore.updateContext(sessionId, contextHash, bytes);
142
+ sessionStore.appendEvent(sessionId, "context_cost", JSON.stringify({
143
+ bytes,
144
+ sources: context.manifest.files,
145
+ handoffId: request.handoffId ?? null,
146
+ selectedSkills: local ? skillIndex.map((skill) => skill.name) : [],
147
+ selectedLessons: local ? autoSkills.map((lesson) => lesson.id) : [],
148
+ sections: breakdown,
149
+ }));
150
+ const onEvent = (event) => {
151
+ captureProviderSessionId(sessionStore, sessionId, event);
152
+ sessionStore.appendEvent(sessionId, event.type, boundedEventData(event));
153
+ };
154
+ const result = plan.toolLoop
155
+ ? await runToolLoop({
156
+ plan,
157
+ prompt,
158
+ chat: deps.chat,
159
+ executeTool: deps.executeAgentTool,
160
+ toolLimits: request.runContract && {
161
+ allowed: request.runContract.allowedTools,
162
+ denied: request.runContract.deniedTools,
163
+ },
164
+ timeoutMs: request.runContract?.budget.timeoutMs,
165
+ maxOutputBytes: request.runContract?.budget.maxOutputBytes,
166
+ maxAttempts: request.runContract?.budget.maxAttempts,
167
+ onEvent,
168
+ })
169
+ : plan.transport === "openai-compatible-api"
170
+ ? await deps.executeCompletion({
171
+ provider: plan.provider,
172
+ model: plan.model,
173
+ prompt,
174
+ ...(plan.temperature === undefined
175
+ ? {}
176
+ : { temperature: plan.temperature }),
177
+ timeoutMs: request.runContract?.budget.timeoutMs,
178
+ maxOutputBytes: request.runContract?.budget.maxOutputBytes,
179
+ maxAttempts: request.runContract?.budget.maxAttempts,
180
+ onEvent,
181
+ })
182
+ : await execute({
183
+ provider: profile.provider,
184
+ prompt,
185
+ cwd: request.cwd,
186
+ clientHome,
187
+ timeoutMs: request.runContract?.budget.timeoutMs,
188
+ maxOutputBytes: request.runContract?.budget.maxOutputBytes,
189
+ readOnly: plan.readOnly,
190
+ model: plan.model,
191
+ env: plan.childEnv,
192
+ readDirectories: headlessReadDirectories({
193
+ cwd: request.cwd,
194
+ profileName: profile.name,
195
+ skillIndex,
196
+ factsPartial: factsDigest.partial,
197
+ contextDirectories: context.readDirectories,
198
+ }),
199
+ onSpawn: (pid) => sessionStore.setProviderPid(sessionId, pid),
200
+ onEvent,
201
+ });
202
+ sessionStore.clearProviderPid(sessionId);
203
+ sessionStore.updateStatus(sessionId, result.exitCode === 0 ? "completed" : "failed");
204
+ sessionStore.appendEvent(sessionId, "process_exit", JSON.stringify({
205
+ exitCode: result.exitCode,
206
+ stderr: redactRuntimeText(result.stderr),
207
+ }));
208
+ sessionStore.appendEvent(sessionId, "evidence", JSON.stringify({
209
+ evidenceId: randomUUID(),
210
+ sessionId,
211
+ type: "provider_exit",
212
+ source: "headless-process",
213
+ observedAt: new Date().toISOString(),
214
+ result: result.exitCode === 0 ? "proven" : "not_proven",
215
+ criterion: "provider process exits successfully",
216
+ payload: JSON.stringify({ exitCode: result.exitCode }),
217
+ }));
218
+ sessionStore.scanCaptureItems(sessionId);
219
+ await deps.appendRuntimeLog({
220
+ timestamp: new Date().toISOString(),
221
+ event: result.exitCode === 124 ? "provider_timeout" : "run_finished",
222
+ correlationId: sessionId,
223
+ sessionId,
224
+ provider: profile.provider,
225
+ status: result.exitCode === 0 ? "completed" : "failed",
226
+ payload: JSON.stringify({ exitCode: result.exitCode }),
227
+ });
228
+ await finalizeSession(sessionStore, sessionId, {
229
+ exitCode: result.exitCode,
230
+ });
231
+ await emitHook("session.end", {
232
+ sessionId,
233
+ status: result.exitCode === 0 ? "completed" : "failed",
234
+ exitCode: result.exitCode,
235
+ });
236
+ return sessionStore.get(sessionId) ?? session;
237
+ }
238
+ catch (error) {
239
+ sessionStore.clearProviderPid(sessionId);
240
+ sessionStore.updateStatus(sessionId, "failed");
241
+ sessionStore.scanCaptureItems(sessionId);
242
+ sessionStore.appendEvent(sessionId, "error", redactRuntimeText(error instanceof Error ? error.message : String(error)));
243
+ await finalizeSession(sessionStore, sessionId, { exitCode: 1 });
244
+ await emitHook("run.error", {
245
+ sessionId,
246
+ error: error instanceof Error ? error.message : String(error),
247
+ });
248
+ await deps.appendRuntimeLog({
249
+ timestamp: new Date().toISOString(),
250
+ event: "run_failed",
251
+ correlationId: sessionId,
252
+ sessionId,
253
+ provider: profile.provider,
254
+ status: "failed",
255
+ });
256
+ throw error;
257
+ }
258
+ finally {
259
+ sessionStore.close();
260
+ }
261
+ }
262
+ export async function resumeAgent(sessionId, prompt, deps, runContract) {
263
+ const execute = deps.executeProvider;
264
+ const sessionStore = await deps.openStore();
265
+ try {
266
+ const existing = sessionStore.get(sessionId);
267
+ if (!existing)
268
+ throw new Error(`Session not found: ${sessionId}`);
269
+ if (existing.provider !== "claude" || !existing.providerSessionId) {
270
+ throw new Error(`Provider does not support resume yet: ${existing.provider}`);
271
+ }
272
+ const profile = selectProfileClient(await deps.loadProfile(existing.profile), existing.provider);
273
+ executionPolicy(profile, existing.workingDirectory);
274
+ deps.assertProviderSupportsReadOnly(profile.provider);
275
+ if (profile.writePolicy !== "none")
276
+ throw new Error("Writable profile resumes are not supported");
277
+ if (profileIdentity(profile) !== existing.profileIdentity)
278
+ throw new Error("Profile changed since the session was created; start a new reviewed run");
279
+ if (profile.governance?.approvalRequired && !runContract)
280
+ throw new Error("Profile requires an approved run contract to resume");
281
+ if (runContract) {
282
+ if (runContract.sessionId !== sessionId)
283
+ throw new Error("Run contract session does not match the session being resumed");
284
+ authorizeRun(sessionStore, runContract);
285
+ }
286
+ const plan = buildExecutionPlan(profile, {
287
+ cwd: existing.workingDirectory,
288
+ ...runtimeCapabilities,
289
+ });
290
+ sessionStore.updateStatus(sessionId, "running");
291
+ appendExecutionPlan(sessionStore, sessionId, plan);
292
+ sessionStore.appendEvent(sessionId, "resume_requested", redactRuntimeText(prompt));
293
+ sessionStore.appendEvent(sessionId, "user_input", redactRuntimeText(prompt));
294
+ // The first turn's prompt points at skill folders, context references and possibly the
295
+ // facts store; --add-dir is per invocation, so the same grant is rebuilt for the resume.
296
+ // profileIdentity is unchanged (checked above), so the skill set is the same. The grant
297
+ // only adds read access: a skill or facts lookup that fails now narrows it, never the turn.
298
+ const cwd = existing.workingDirectory;
299
+ let skillIndex = [];
300
+ try {
301
+ skillIndex = await deps.loadSkillIndex(profile.skills, cwd);
302
+ }
303
+ catch {
304
+ skillIndex = [];
305
+ }
306
+ let factsPartial = false;
307
+ try {
308
+ factsPartial = plan.memoryNamespace
309
+ ? buildProfileFactsDigest(plan.memoryNamespace, await readProfileFacts(plan.memoryNamespace)).partial
310
+ : false;
311
+ }
312
+ catch {
313
+ factsPartial = false;
314
+ }
315
+ const readDirectories = headlessReadDirectories({
316
+ cwd,
317
+ profileName: profile.name,
318
+ skillIndex,
319
+ factsPartial,
320
+ contextDirectories: await referenceReadDirectories(recordedReferences(sessionStore.listEvents(sessionId)), profile.allowedPaths, cwd),
321
+ });
322
+ const result = await execute({
323
+ provider: "claude",
324
+ prompt,
325
+ cwd,
326
+ resumeId: existing.providerSessionId,
327
+ clientHome: deps.resolveClientHome(profile),
328
+ timeoutMs: runContract?.budget.timeoutMs,
329
+ maxOutputBytes: runContract?.budget.maxOutputBytes,
330
+ readOnly: plan.readOnly,
331
+ model: plan.model,
332
+ env: plan.childEnv,
333
+ readDirectories,
334
+ onSpawn: (pid) => sessionStore.setProviderPid(sessionId, pid),
335
+ onEvent: (event) => {
336
+ captureProviderSessionId(sessionStore, sessionId, event);
337
+ sessionStore.appendEvent(sessionId, event.type, boundedEventData(event));
338
+ },
339
+ });
340
+ sessionStore.clearProviderPid(sessionId);
341
+ sessionStore.updateStatus(sessionId, result.exitCode === 0 ? "completed" : "failed");
342
+ sessionStore.appendEvent(sessionId, "process_exit", JSON.stringify({
343
+ exitCode: result.exitCode,
344
+ stderr: redactRuntimeText(result.stderr),
345
+ }));
346
+ sessionStore.scanCaptureItems(sessionId);
347
+ await finalizeSession(sessionStore, sessionId, {
348
+ exitCode: result.exitCode,
349
+ });
350
+ return sessionStore.get(sessionId) ?? existing;
351
+ }
352
+ catch (error) {
353
+ sessionStore.clearProviderPid(sessionId);
354
+ sessionStore.updateStatus(sessionId, "failed");
355
+ sessionStore.scanCaptureItems(sessionId);
356
+ sessionStore.appendEvent(sessionId, "error", redactRuntimeText(error instanceof Error ? error.message : String(error)));
357
+ await finalizeSession(sessionStore, sessionId, { exitCode: 1 });
358
+ throw error;
359
+ }
360
+ finally {
361
+ sessionStore.close();
362
+ }
363
+ }
364
+ // What this runtime executes today. CLI clients and completion APIs both execute. Writes
365
+ // stay off except in a tool loop, where Ocean executes (and bounds) every write itself.
366
+ const runtimeCapabilities = {
367
+ supportedTransports: ["cli", "openai-compatible-api"],
368
+ writeAllowed: false,
369
+ };
370
+ // The plan is recorded with its identity before any context is assembled or process starts,
371
+ // so a session always shows the exact client, model, memory namespace and write mode it ran with.
372
+ function appendExecutionPlan(store, sessionId, plan) {
373
+ store.appendEvent(sessionId, "execution_plan", JSON.stringify({ identity: executionPlanIdentity(plan), plan }));
374
+ }
375
+ // Read access for everything the lean prompt points at outside cwd: skill folders, the
376
+ // facts store when the digest left facts out, and the allowedPaths-bounded directories of
377
+ // context references. Deduped in that order.
378
+ function headlessReadDirectories(options) {
379
+ return [
380
+ ...new Set([
381
+ ...readDirectoriesOutside(options.cwd, [
382
+ ...options.skillIndex.map((skill) => skill.path),
383
+ ...(options.factsPartial
384
+ ? [profileFactsFile(options.profileName)]
385
+ : []),
386
+ ]),
387
+ ...options.contextDirectories,
388
+ ]),
389
+ ];
390
+ }
391
+ // The references the session's first turn recorded (its last context_manifest event).
392
+ // Packet references depend on the original prompt, so they are read back, not recomputed.
393
+ function recordedReferences(events) {
394
+ const manifest = [...events]
395
+ .reverse()
396
+ .find((event) => event.type === "context_manifest");
397
+ if (!manifest)
398
+ return [];
399
+ try {
400
+ return validateContextManifest(JSON.parse(manifest.data)).references;
401
+ }
402
+ catch {
403
+ return [];
404
+ }
405
+ }
406
+ function boundedEventData(event) {
407
+ const serialized = typeof event.data === "string" ? event.data : JSON.stringify(event.data);
408
+ return redactRuntimeText(serialized);
409
+ }
410
+ function captureProviderSessionId(store, sessionId, event) {
411
+ if (event.type === "json" &&
412
+ typeof event.data === "object" &&
413
+ event.data !== null &&
414
+ "session_id" in event.data) {
415
+ const providerSessionId = event.data
416
+ .session_id;
417
+ if (typeof providerSessionId === "string" &&
418
+ isValidProviderSessionId(providerSessionId))
419
+ store.updateProviderSessionId(sessionId, providerSessionId);
420
+ }
421
+ }
422
+ export function isValidProviderSessionId(value) {
423
+ return value.length <= 256 && /^[A-Za-z0-9._:-]+$/.test(value);
424
+ }
425
+ //# sourceMappingURL=run-agent.js.map
@@ -0,0 +1,24 @@
1
+ import { assertRunCanStart, } from "@ocean/core/domain/runs/run-contract.js";
2
+ export function authorizeRun(store, contract, attempt = 1) {
3
+ try {
4
+ const session = store.get(contract.sessionId);
5
+ if (!session)
6
+ throw new Error(`Run contract session not found: ${contract.sessionId}`);
7
+ if (session.profile !== contract.profile)
8
+ throw new Error("Run contract profile does not match the session");
9
+ if (session.workingDirectory !== contract.workingDirectory)
10
+ throw new Error("Run contract working directory does not match the session");
11
+ assertRunCanStart(contract, attempt);
12
+ store.appendEvent(contract.sessionId, "run_approval", JSON.stringify({ runId: contract.runId, approved: true, attempt }));
13
+ }
14
+ catch (error) {
15
+ store.appendEvent(contract.sessionId, "run_refusal", JSON.stringify({
16
+ runId: contract.runId,
17
+ approved: false,
18
+ reason: error instanceof Error ? error.message : String(error),
19
+ attempt,
20
+ }));
21
+ throw error;
22
+ }
23
+ }
24
+ //# sourceMappingURL=run-authorization.js.map
@@ -0,0 +1,173 @@
1
+ import { redactRuntimeText } from "@ocean/core/domain/redaction/redaction.js";
2
+ import { decideToolCall, toolDefinitions, } from "@ocean/core/domain/runs/agent-tools.js";
3
+ // A completion client working through Ocean's tools, bounded on every axis (T-245 Phase 6):
4
+ // iterations, tool calls, one wall-clock deadline, total tool output, repeated calls, and
5
+ // cancellation. Every tool call is decided against the execution plan before it runs.
6
+ export const MAX_ITERATIONS = 20;
7
+ export const MAX_TOOL_CALLS = 50;
8
+ export const MAX_REPEATS = 3;
9
+ const DEFAULT_TIMEOUT_MS = 10 * 60_000;
10
+ const DEFAULT_MAX_OUTPUT_BYTES = 1024 * 1024;
11
+ class LoopStop extends Error {
12
+ exitCode;
13
+ constructor(message, exitCode) {
14
+ super(message);
15
+ this.exitCode = exitCode;
16
+ }
17
+ }
18
+ export async function runToolLoop(request) {
19
+ const { plan } = request;
20
+ const events = [];
21
+ const emit = (data) => {
22
+ const event = { type: "json", data };
23
+ events.push(event);
24
+ request.onEvent?.(event);
25
+ };
26
+ const controller = new AbortController();
27
+ const timer = setTimeout(() => controller.abort(new LoopStop("Process timed out.", 124)), request.timeoutMs ?? DEFAULT_TIMEOUT_MS);
28
+ const cancel = () => controller.abort(new LoopStop("Run cancelled.", 130));
29
+ if (request.signal?.aborted)
30
+ cancel();
31
+ request.signal?.addEventListener("abort", cancel, { once: true });
32
+ const outputBudget = request.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
33
+ const tools = toolDefinitions(plan, request.toolLimits);
34
+ const messages = [{ role: "user", content: request.prompt }];
35
+ const seen = new Map();
36
+ let toolCalls = 0;
37
+ let outputBytes = 0;
38
+ const checkAborted = () => {
39
+ if (controller.signal.aborted)
40
+ throw controller.signal.reason;
41
+ };
42
+ try {
43
+ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration += 1) {
44
+ checkAborted();
45
+ const reply = await request.chat({
46
+ provider: plan.provider,
47
+ model: plan.model,
48
+ ...(plan.temperature === undefined
49
+ ? {}
50
+ : { temperature: plan.temperature }),
51
+ messages,
52
+ tools,
53
+ maxOutputBytes: outputBudget,
54
+ maxAttempts: request.maxAttempts,
55
+ signal: controller.signal,
56
+ onEvent: request.onEvent,
57
+ });
58
+ messages.push(reply.message);
59
+ // The model's own replies count against the run's output budget, like tool output.
60
+ outputBytes += Buffer.byteLength(JSON.stringify(reply.message));
61
+ if (outputBytes > outputBudget)
62
+ throw new LoopStop("Output budget exceeded.", 125);
63
+ const calls = reply.message.tool_calls ?? [];
64
+ if (!calls.length) {
65
+ emit({
66
+ type: "completion",
67
+ provider: plan.provider,
68
+ model: reply.model,
69
+ id: reply.id,
70
+ usage: reply.usage,
71
+ iterations: iteration,
72
+ toolCalls,
73
+ text: reply.message.content ?? "",
74
+ });
75
+ return { exitCode: 0, events, stderr: "" };
76
+ }
77
+ for (const call of calls) {
78
+ checkAborted();
79
+ toolCalls += 1;
80
+ if (toolCalls > MAX_TOOL_CALLS)
81
+ throw new LoopStop(`Tool call budget exhausted (${MAX_TOOL_CALLS}).`, 1);
82
+ const key = `${call.function.name}\0${call.function.arguments}`;
83
+ const repeats = (seen.get(key) ?? 0) + 1;
84
+ seen.set(key, repeats);
85
+ if (repeats >= MAX_REPEATS)
86
+ throw new LoopStop(`Repeated tool call stopped the run: ${call.function.name} x${repeats}.`, 1);
87
+ const decision = decideToolCall(plan, {
88
+ id: call.id,
89
+ name: call.function.name,
90
+ arguments: call.function.arguments,
91
+ }, request.toolLimits);
92
+ let content;
93
+ if (!decision.ok) {
94
+ emit({
95
+ type: "tool_denied",
96
+ id: call.id,
97
+ name: call.function.name,
98
+ reason: decision.reason,
99
+ });
100
+ content = `denied: ${decision.reason}`;
101
+ }
102
+ else {
103
+ // Only the path and size of a write are recorded: its content is the file's.
104
+ const { content: written, ...rest } = decision.args;
105
+ emit({
106
+ type: "tool_call",
107
+ id: call.id,
108
+ name: decision.name,
109
+ arguments: redactRuntimeText(JSON.stringify(rest)).slice(0, 2_000),
110
+ ...(written === undefined
111
+ ? {}
112
+ : { bytes: Buffer.byteLength(written) }),
113
+ });
114
+ try {
115
+ content = await request.executeTool(plan, decision.name, decision.args);
116
+ // A write changes what a repeated read or listing would return.
117
+ if (decision.name === "write_file")
118
+ seen.clear();
119
+ emit({
120
+ type: "tool_result",
121
+ id: call.id,
122
+ ok: true,
123
+ bytes: Buffer.byteLength(content),
124
+ });
125
+ }
126
+ catch (error) {
127
+ checkAborted();
128
+ const message = redactRuntimeText(error.message);
129
+ // A refusal at execution (path outside the allowed paths, a credential file) is a
130
+ // denial; any other failure is reported to the model, which may recover.
131
+ if (error.name === "ToolDenied") {
132
+ emit({
133
+ type: "tool_denied",
134
+ id: call.id,
135
+ name: decision.name,
136
+ reason: message,
137
+ });
138
+ content = `denied: ${message}`;
139
+ }
140
+ else {
141
+ emit({
142
+ type: "tool_result",
143
+ id: call.id,
144
+ ok: false,
145
+ error: message,
146
+ });
147
+ content = `error: ${message}`;
148
+ }
149
+ }
150
+ }
151
+ outputBytes += Buffer.byteLength(content);
152
+ if (outputBytes > outputBudget)
153
+ throw new LoopStop("Output budget exceeded.", 125);
154
+ messages.push({ role: "tool", tool_call_id: call.id, content });
155
+ }
156
+ }
157
+ throw new LoopStop(`Iteration budget exhausted (${MAX_ITERATIONS}).`, 1);
158
+ }
159
+ catch (error) {
160
+ const stop = error instanceof LoopStop
161
+ ? error
162
+ : controller.signal.aborted &&
163
+ controller.signal.reason instanceof LoopStop
164
+ ? controller.signal.reason
165
+ : new LoopStop(error.message, error.exitCode ?? 1);
166
+ return { exitCode: stop.exitCode, events, stderr: `${stop.message}\n` };
167
+ }
168
+ finally {
169
+ clearTimeout(timer);
170
+ request.signal?.removeEventListener("abort", cancel);
171
+ }
172
+ }
173
+ //# sourceMappingURL=tool-loop.js.map