@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,210 @@
1
+ import { mkdir, open, readFile, rename, unlink, writeFile, } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { oceanPath, SYSTEM_DIR } from "@ocean/core/shared/paths.js";
4
+ import { runAgent } from "@ocean/loop/runs/run-agent.js";
5
+ const file = () => oceanPath(SYSTEM_DIR, "schedules.json");
6
+ const running = new Set();
7
+ export async function listSchedules() {
8
+ try {
9
+ return JSON.parse(await readFile(file(), "utf8"));
10
+ }
11
+ catch (error) {
12
+ if (error.code === "ENOENT")
13
+ return [];
14
+ throw error;
15
+ }
16
+ }
17
+ export async function saveSchedule(schedule) {
18
+ if (!Number.isFinite(schedule.intervalMs) || schedule.intervalMs < 1_000)
19
+ throw new Error("Schedule interval must be at least 1000ms");
20
+ await mutateSchedules((schedules) => [
21
+ ...schedules.filter((item) => item.id !== schedule.id),
22
+ schedule,
23
+ ]);
24
+ }
25
+ export async function setScheduleEnabled(id, enabled) {
26
+ let updated;
27
+ await mutateSchedules((schedules) => schedules.map((schedule) => {
28
+ if (schedule.id !== id)
29
+ return schedule;
30
+ updated = { ...schedule, enabled };
31
+ return updated;
32
+ }));
33
+ if (!updated)
34
+ throw new Error(`Schedule not found: ${id}`);
35
+ return updated;
36
+ }
37
+ export async function runSchedule(id, cwd, runtime) {
38
+ const schedule = (await listSchedules()).find((item) => item.id === id);
39
+ if (!schedule)
40
+ throw new Error(`Schedule not found: ${id}`);
41
+ await runAgent({ profileName: schedule.profile, prompt: schedule.prompt, cwd }, runtime);
42
+ }
43
+ export async function runDueSchedules(cwd, runtime) {
44
+ const now = Date.now();
45
+ const schedules = await listSchedules();
46
+ const ran = [];
47
+ for (const schedule of schedules) {
48
+ if (!schedule.enabled ||
49
+ Date.parse(schedule.nextRunAt) > now ||
50
+ (schedule.retryAt && Date.parse(schedule.retryAt) > now) ||
51
+ running.has(schedule.id))
52
+ continue;
53
+ const lease = await acquireLease(schedule.id);
54
+ if (!lease)
55
+ continue;
56
+ running.add(schedule.id);
57
+ try {
58
+ const session = await runAgent({ profileName: schedule.profile, prompt: schedule.prompt, cwd }, runtime);
59
+ if (session.status !== "completed")
60
+ throw new Error(`Scheduled run failed: ${session.sessionId}`);
61
+ schedule.nextRunAt = new Date(now + schedule.intervalMs).toISOString();
62
+ ran.push(schedule.id);
63
+ }
64
+ finally {
65
+ running.delete(schedule.id);
66
+ await lease.close();
67
+ await unlink(leaseFile(schedule.id)).catch(() => undefined);
68
+ }
69
+ }
70
+ for (const id of ran) {
71
+ const completed = schedules.find((schedule) => schedule.id === id);
72
+ if (completed)
73
+ await mutateSchedules((current) => current.map((item) => item.id === id ? { ...item, nextRunAt: completed.nextRunAt } : item));
74
+ }
75
+ return ran;
76
+ }
77
+ export async function runSchedulerWorkerOnce(cwd, runtime) {
78
+ const schedules = await listSchedules();
79
+ const ran = [];
80
+ for (const schedule of schedules) {
81
+ if (!schedule.enabled || Date.parse(schedule.nextRunAt) > Date.now())
82
+ continue;
83
+ const lease = leaseFile(schedule.id);
84
+ const handle = await acquireLease(schedule.id);
85
+ if (!handle)
86
+ continue;
87
+ try {
88
+ try {
89
+ const session = await runAgent({ profileName: schedule.profile, prompt: schedule.prompt, cwd }, runtime);
90
+ if (session.status !== "completed")
91
+ throw new Error(`Scheduled run failed: ${session.sessionId}`);
92
+ schedule.nextRunAt = new Date(Date.now() + schedule.intervalMs).toISOString();
93
+ schedule.attempts = 0;
94
+ delete schedule.retryAt;
95
+ ran.push(schedule.id);
96
+ }
97
+ catch {
98
+ schedule.attempts = (schedule.attempts ?? 0) + 1;
99
+ const delay = Math.min(schedule.intervalMs * 2 ** Math.min(schedule.attempts, 5), 3_600_000);
100
+ schedule.retryAt = new Date(Date.now() + delay).toISOString();
101
+ schedule.nextRunAt = schedule.retryAt;
102
+ }
103
+ }
104
+ finally {
105
+ await handle.close();
106
+ await unlink(lease).catch(() => undefined);
107
+ }
108
+ }
109
+ for (const changed of schedules) {
110
+ if (!ran.includes(changed.id) &&
111
+ changed.attempts === undefined &&
112
+ changed.retryAt === undefined)
113
+ continue;
114
+ await mutateSchedules((current) => current.map((item) => {
115
+ if (item.id !== changed.id)
116
+ return item;
117
+ const updated = {
118
+ ...item,
119
+ nextRunAt: changed.nextRunAt,
120
+ attempts: changed.attempts,
121
+ };
122
+ if (changed.retryAt)
123
+ updated.retryAt = changed.retryAt;
124
+ else
125
+ delete updated.retryAt;
126
+ return updated;
127
+ }));
128
+ }
129
+ return ran;
130
+ }
131
+ export async function runSchedulerWorker(cwd, runtime, options = {}) {
132
+ const pollMs = Math.max(1_000, options.pollMs ?? 30_000);
133
+ while (!options.signal?.aborted) {
134
+ await runSchedulerWorkerOnce(cwd, runtime);
135
+ await new Promise((resolve) => {
136
+ const timer = setTimeout(resolve, pollMs);
137
+ options.signal?.addEventListener("abort", () => {
138
+ clearTimeout(timer);
139
+ resolve();
140
+ }, { once: true });
141
+ });
142
+ }
143
+ }
144
+ function leaseFile(id) {
145
+ return oceanPath(SYSTEM_DIR, "schedules", `${id}.lease`);
146
+ }
147
+ async function acquireLease(id) {
148
+ const lease = leaseFile(id);
149
+ await mkdir(path.dirname(lease), { recursive: true });
150
+ try {
151
+ const handle = await open(lease, "wx");
152
+ await handle.writeFile(`${process.pid}\n`);
153
+ return handle;
154
+ }
155
+ catch (error) {
156
+ if (error.code === "EEXIST") {
157
+ const leaseContents = (await readFile(lease, "utf8").catch(() => "")).trim();
158
+ if (!leaseContents)
159
+ return null;
160
+ const owner = Number.parseInt(leaseContents, 10);
161
+ if (Number.isInteger(owner) && owner > 0 && processIsAlive(owner))
162
+ return null;
163
+ await unlink(lease).catch(() => undefined);
164
+ try {
165
+ const handle = await open(lease, "wx");
166
+ await handle.writeFile(`${process.pid}\n`);
167
+ return handle;
168
+ }
169
+ catch (retryError) {
170
+ if (retryError.code === "EEXIST")
171
+ return null;
172
+ throw retryError;
173
+ }
174
+ }
175
+ throw error;
176
+ }
177
+ }
178
+ function processIsAlive(pid) {
179
+ try {
180
+ process.kill(pid, 0);
181
+ return true;
182
+ }
183
+ catch (error) {
184
+ return error.code === "EPERM";
185
+ }
186
+ }
187
+ async function mutateSchedules(update) {
188
+ let lease = null;
189
+ for (let attempt = 0; attempt < 100 && !lease; attempt += 1) {
190
+ lease = await acquireLease("__registry__");
191
+ if (!lease)
192
+ await new Promise((resolve) => setTimeout(resolve, 10));
193
+ }
194
+ if (!lease)
195
+ throw new Error("Schedule registry remained busy for 1 second");
196
+ try {
197
+ const schedules = update(await listSchedules());
198
+ await mkdir(path.dirname(file()), { recursive: true });
199
+ const temporary = `${file()}.tmp-${process.pid}-${Date.now()}`;
200
+ await writeFile(temporary, `${JSON.stringify(schedules, null, 2)}\n`, {
201
+ mode: 0o600,
202
+ });
203
+ await rename(temporary, file());
204
+ }
205
+ finally {
206
+ await lease.close();
207
+ await unlink(leaseFile("__registry__")).catch(() => undefined);
208
+ }
209
+ }
210
+ //# sourceMappingURL=local-scheduler.js.map
@@ -0,0 +1,64 @@
1
+ // Composition root. This is the one place (besides entry points) allowed to
2
+ // import infrastructure concretes: it wires them to the domain ports that the
3
+ // application layer depends on. Dependencies point inward everywhere else —
4
+ // application → domain ports, never application → infrastructure.
5
+ import { brainIndexPort } from "@ocean/brain/infrastructure/brain/brain-index.js";
6
+ import { selectActiveLessons } from "@ocean/brain/memory/lesson-activation.js";
7
+ import { openSessionStore, SessionStore, } from "@ocean/loop/infrastructure/persistence/session-store.js";
8
+ import { loadProfile } from "../infrastructure/filesystem/profile-loader.js";
9
+ import { loadSkillIndex } from "../infrastructure/filesystem/skill-loader.js";
10
+ import { appendRuntimeLog } from "../infrastructure/observability/runtime-logger.js";
11
+ import { executeAgentTool } from "../infrastructure/process/agent-tool-executor.js";
12
+ import { runHeadless } from "../infrastructure/process/cli-process.js";
13
+ import { runInteractive } from "../infrastructure/process/interactive-process.js";
14
+ import { resolveClientHome } from "../infrastructure/providers/client-home.js";
15
+ import { chatTransport, runCompletion, } from "../infrastructure/providers/openai-compatible.js";
16
+ import { resolveOriginalExecutable } from "../infrastructure/providers/provider-registry.js";
17
+ import { assertProviderSupportsReadOnly, runProvider, } from "../infrastructure/providers/providers.js";
18
+ import { desktopWrapperPath, registerProvider, removeProvider, syncProviderWrappers, wrapperDoctor, } from "../infrastructure/wrappers/wrapper-manager.js";
19
+ /** The concrete session-store factory, as the SessionStoreFactory port. */
20
+ export const defaultSessionStoreFactory = openSessionStore;
21
+ /** Opens a session-store file at an arbitrary path, as the SessionStoreOpener port. */
22
+ export const defaultSessionStoreOpener = (file) => new SessionStore(file);
23
+ /** The concrete brain-index driver (T-228), as the BrainIndexPort. */
24
+ export const defaultBrainIndexPort = brainIndexPort;
25
+ /** The concrete dependency set for `runAgent`/`resumeAgent`. */
26
+ export const defaultAgentRuntime = {
27
+ openStore: openSessionStore,
28
+ executeProvider: runProvider,
29
+ executeCompletion: runCompletion,
30
+ chat: chatTransport,
31
+ executeAgentTool,
32
+ loadProfile,
33
+ loadSkillIndex,
34
+ loadActiveLessons: (prompt, maxBytes) => selectActiveLessons(prompt, { maxBytes, indexPort: defaultBrainIndexPort }),
35
+ resolveClientHome,
36
+ appendRuntimeLog,
37
+ assertProviderSupportsReadOnly,
38
+ };
39
+ /**
40
+ * Build an agent runtime with the real implementations, overriding any subset —
41
+ * used by tests to inject a fake provider executor while keeping the rest real.
42
+ */
43
+ export function createAgentRuntime(overrides = {}) {
44
+ return { ...defaultAgentRuntime, ...overrides };
45
+ }
46
+ /** Shell wrapper management, as the WrapperManagerPort. */
47
+ export const defaultWrapperManager = {
48
+ syncProviderWrappers,
49
+ registerProvider,
50
+ removeProvider,
51
+ wrapperDoctor,
52
+ desktopWrapperPath,
53
+ };
54
+ /** The provider registry, as the ProviderRegistryPort. */
55
+ export const defaultProviderRegistry = {
56
+ resolveOriginalExecutable,
57
+ };
58
+ /** Dependencies for the auth flows. */
59
+ export const defaultAuthDeps = {
60
+ registry: defaultProviderRegistry,
61
+ runHeadless,
62
+ runInteractive,
63
+ };
64
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1,9 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ // Publishes the `ocean` package root for @ocean/core's engineRoot() (see
4
+ // core/src/shared/paths.ts). This module sits one directory below that root, whether it
5
+ // runs compiled from dist/ or directly from src/ under tsx. main.ts imports it first, and
6
+ // it must not import core itself, so core's module-level paths already see the root.
7
+ globalThis[Symbol.for("ocean.engineRoot")] =
8
+ path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
9
+ //# sourceMappingURL=engine-root.js.map
@@ -0,0 +1,46 @@
1
+ import { readFile, stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { assertProfileName, } from "@ocean/core/domain/profiles/profile.js";
4
+ import { validateProfile } from "@ocean/core/domain/profiles/profile-validator.js";
5
+ import { oceanPath, resolveWithin, SYSTEM_DIR, } from "@ocean/core/shared/paths.js";
6
+ function missingAsNull(error) {
7
+ if (error.code === "ENOENT")
8
+ return null;
9
+ throw error;
10
+ }
11
+ // The parser's message quotes the text around the error, which could be part of a secret
12
+ // pasted into the file, so only the file is named.
13
+ function parseProfile(source, file) {
14
+ try {
15
+ return JSON.parse(source);
16
+ }
17
+ catch {
18
+ throw new Error(`Invalid profile JSON in ${file}`);
19
+ }
20
+ }
21
+ // The name inside the file must be the name it was loaded by: the name keys sessions and the
22
+ // profile's memory namespace, so a mismatch would read or write another profile's facts.
23
+ function requireName(profile, name, file) {
24
+ if (profile.name !== name)
25
+ throw new Error(`Profile ${file} declares name "${profile.name}", expected "${name}"`);
26
+ return profile;
27
+ }
28
+ export async function loadProfile(name) {
29
+ assertProfileName(name);
30
+ const root = oceanPath(SYSTEM_DIR, "profiles");
31
+ const file = resolveWithin(root, `${name}.json`);
32
+ // Only a missing canonical file falls through to the legacy directory form; a file that
33
+ // exists but cannot be read, parsed or validated is an error, never a silent fallback.
34
+ const source = await readFile(file, "utf8").catch(missingAsNull);
35
+ if (source !== null)
36
+ return requireName(validateProfile(parseProfile(source, file)), name, file);
37
+ const directory = resolveWithin(root, name);
38
+ if ((await stat(directory).catch(missingAsNull))?.isDirectory()) {
39
+ const legacy = path.join(directory, "profile.json");
40
+ const input = parseProfile(await readFile(legacy, "utf8"), legacy);
41
+ const instructions = await readFile(path.join(directory, "instructions.md"), "utf8").catch(() => "");
42
+ return requireName(validateProfile({ ...input, instructions }), name, legacy);
43
+ }
44
+ throw new Error(`Profile not found: ${name}`);
45
+ }
46
+ //# sourceMappingURL=profile-loader.js.map
@@ -0,0 +1,29 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { validateRuntimeConfig, } from "@ocean/core/domain/profiles/runtime-config.js";
3
+ import { oceanPath, SYSTEM_DIR } from "@ocean/core/shared/paths.js";
4
+ export function runtimeConfigFile() {
5
+ return oceanPath(SYSTEM_DIR, "config", "runtime.json");
6
+ }
7
+ // null only when the file does not exist; a file that exists but is not valid is an error.
8
+ export async function loadRuntimeConfig() {
9
+ const file = runtimeConfigFile();
10
+ let source;
11
+ try {
12
+ source = await readFile(file, "utf8");
13
+ }
14
+ catch (error) {
15
+ if (error.code === "ENOENT")
16
+ return null;
17
+ throw error;
18
+ }
19
+ let input;
20
+ try {
21
+ input = JSON.parse(source);
22
+ }
23
+ catch {
24
+ // The parser's message quotes the file around the error; it is not repeated.
25
+ throw new Error(`Invalid runtime config JSON in ${file}`);
26
+ }
27
+ return validateRuntimeConfig(input);
28
+ }
29
+ //# sourceMappingURL=runtime-config-loader.js.map
@@ -0,0 +1,84 @@
1
+ import { readFile, stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { PROJECT_CATEGORY_FOLDERS } from "@ocean/core/domain/context/project-id.js";
4
+ import { skillMetadataSchema, } from "@ocean/core/domain/skills/skill.js";
5
+ import { validateSkill } from "@ocean/core/domain/skills/skill-validator.js";
6
+ import { enginePath, oceanPath, oceanRoot, PROJECTS_DIR, SYSTEM_DIR, } from "@ocean/core/shared/paths.js";
7
+ const catalogPath = enginePath("skills", "index.json");
8
+ export async function listSkills() {
9
+ return readCatalog(catalogPath);
10
+ }
11
+ export async function loadSkill(name) {
12
+ const { metadata, file } = await resolveSkillFile(name, [
13
+ enginePath("skills"),
14
+ ]);
15
+ return validateSkill({
16
+ ...metadata,
17
+ instructions: await readFile(file, "utf8"),
18
+ });
19
+ }
20
+ // Resolves profile skills to catalog metadata plus their SKILL.md path, in order and
21
+ // deduplicated, without reading any body. The description comes from index.json.
22
+ export async function loadSkillIndex(names, cwd = oceanRoot()) {
23
+ const roots = skillRoots(cwd);
24
+ const entries = [];
25
+ const seen = new Set();
26
+ for (const name of names) {
27
+ if (seen.has(name))
28
+ continue;
29
+ seen.add(name);
30
+ const { metadata, file } = await resolveSkillFile(name, roots);
31
+ entries.push({ ...metadata, path: file });
32
+ }
33
+ return entries;
34
+ }
35
+ function skillRoots(cwd) {
36
+ return [
37
+ enginePath("skills"),
38
+ oceanPath(SYSTEM_DIR, "integrations", "claude-code", "skills"),
39
+ projectSkillRoot(cwd),
40
+ ].filter(Boolean);
41
+ }
42
+ // The first root whose index.json lists the skill and whose SKILL.md exists wins.
43
+ async function resolveSkillFile(name, roots) {
44
+ for (const root of roots) {
45
+ const catalog = path.join(root, "index.json");
46
+ try {
47
+ const metadata = (await readCatalog(catalog)).find((skill) => skill.name === name);
48
+ if (!metadata)
49
+ continue;
50
+ const file = path.join(root, metadata.category, name, "SKILL.md");
51
+ await stat(file);
52
+ return { metadata, file };
53
+ }
54
+ catch (error) {
55
+ if (error.code === "ENOENT")
56
+ continue;
57
+ throw error;
58
+ }
59
+ }
60
+ throw new Error(`Skill not found: ${name}`);
61
+ }
62
+ async function readCatalog(file) {
63
+ try {
64
+ return skillMetadataSchema
65
+ .array()
66
+ .parse(JSON.parse(await readFile(file, "utf8")));
67
+ }
68
+ catch (error) {
69
+ if (error.code === "ENOENT")
70
+ return [];
71
+ throw error;
72
+ }
73
+ }
74
+ function projectSkillRoot(cwd) {
75
+ const projectsRoot = path.resolve(oceanPath(PROJECTS_DIR));
76
+ const relative = path.relative(projectsRoot, path.resolve(cwd));
77
+ if (!relative || relative.startsWith("..") || path.isAbsolute(relative))
78
+ return null;
79
+ const parts = relative.split(path.sep);
80
+ const grouped = PROJECT_CATEGORY_FOLDERS.includes(parts[0] ?? "");
81
+ const projectParts = grouped ? parts.slice(0, 2) : parts.slice(0, 1);
82
+ return path.join(projectsRoot, ...projectParts, "skills");
83
+ }
84
+ //# sourceMappingURL=skill-loader.js.map
@@ -0,0 +1,37 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { workspaceReadContract } from "@ocean/core/domain/capabilities/capability-contract.js";
5
+ export async function readWorkspaceFile(root, relativePath, allowedPaths, maxBytes = 32_000, timeoutMs = 5_000) {
6
+ const absolute = path.resolve(root, relativePath);
7
+ const allowed = allowedPaths.some((allowedPath) => {
8
+ const boundary = path.resolve(root, allowedPath);
9
+ return (absolute === boundary || absolute.startsWith(`${boundary}${path.sep}`));
10
+ });
11
+ if (!allowed)
12
+ throw new Error(`Capability denied outside allowed paths: ${relativePath}`);
13
+ if (timeoutMs <= 0)
14
+ throw new Error(`Capability timed out: ${relativePath}`);
15
+ const controller = new AbortController();
16
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
17
+ let content;
18
+ try {
19
+ content = (await readFile(absolute, { encoding: "utf8", signal: controller.signal })).slice(0, maxBytes);
20
+ }
21
+ catch (error) {
22
+ if (controller.signal.aborted)
23
+ throw new Error(`Capability timed out: ${relativePath}`);
24
+ throw error;
25
+ }
26
+ finally {
27
+ clearTimeout(timeout);
28
+ }
29
+ return {
30
+ contract: workspaceReadContract,
31
+ path: relativePath,
32
+ bytes: Buffer.byteLength(content),
33
+ sha256: createHash("sha256").update(content).digest("hex"),
34
+ content,
35
+ };
36
+ }
37
+ //# sourceMappingURL=workspace-capability.js.map
@@ -0,0 +1,16 @@
1
+ import { appendFile, mkdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { redactRuntimeText, redactSecrets, } from "@ocean/core/domain/redaction/redaction.js";
4
+ import { oceanPath, SYSTEM_DIR } from "@ocean/core/shared/paths.js";
5
+ // Redaction is a pure domain transform; re-exported here so existing infrastructure
6
+ // callers keep importing it from the logger without reaching into domain directly.
7
+ export { redactRuntimeText, redactSecrets };
8
+ export async function appendRuntimeLog(log) {
9
+ const safe = {
10
+ ...log,
11
+ payload: log.payload === undefined ? undefined : redactRuntimeText(log.payload),
12
+ };
13
+ await mkdir(oceanPath(SYSTEM_DIR, "runtime", "logs"), { recursive: true });
14
+ await appendFile(path.join(oceanPath(SYSTEM_DIR, "runtime", "logs"), "runtime.jsonl"), `${JSON.stringify(safe)}\n`, "utf8");
15
+ }
16
+ //# sourceMappingURL=runtime-logger.js.map
@@ -0,0 +1,99 @@
1
+ import { mkdir, readdir, readFile, stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { containsKnownSecret } from "@ocean/core/domain/redaction/redaction.js";
4
+ import { atomicWrite } from "@ocean/core/shared/fs-utils.js";
5
+ import { realDeepestExisting, resolveWithin, } from "@ocean/core/shared/paths.js";
6
+ // Executes one already-validated file tool call for a tool-loop run (T-245 Phase 6).
7
+ // Everything it touches is bounded to the plan's allowed paths under its cwd, judged and
8
+ // operated on by real path, and never a credential-bearing or repository-internal file:
9
+ // what a tool reads is sent to a third-party API.
10
+ export const MAX_READ_BYTES = 64 * 1024;
11
+ const MAX_LIST_ENTRIES = 500;
12
+ // A policy refusal, as opposed to a tool that ran and failed. The loop records it as
13
+ // `tool_denied` by this name.
14
+ export class ToolDenied extends Error {
15
+ name = "ToolDenied";
16
+ }
17
+ // Case-insensitive: the default macOS volume is, so ".Git" is ".git".
18
+ function isSensitive(relative) {
19
+ return relative
20
+ .split(/[\\/]/)
21
+ .some((segment) => /^\.git$/i.test(segment) ||
22
+ /^\.env(\..*)?$/i.test(segment) ||
23
+ /^\.envrc$/i.test(segment) ||
24
+ /^id_[a-z0-9]+(\.pub)?$/i.test(segment) ||
25
+ /\.(pem|key|p12|pfx|keystore|jks)$/i.test(segment) ||
26
+ /^\.(npmrc|netrc|pypirc|htpasswd|pgpass|git-credentials)$/i.test(segment) ||
27
+ /^(credentials|secrets?)(\.[a-z0-9]+)*$/i.test(segment) ||
28
+ /\.tfstate(\.backup)?$/i.test(segment) ||
29
+ /^\.(aws|ssh|gnupg|docker|kube)$/i.test(segment));
30
+ }
31
+ // The real location of a requested path inside one of the plan's allowed paths. Both the
32
+ // requested name and the real location must pass the sensitivity check, so a symlink inside
33
+ // an allowed path cannot lead to .env or .git; resolveWithin rejects lexical escapes and
34
+ // symlinks that leave the root. Callers operate on the returned real path.
35
+ function resolveTarget(plan, requested) {
36
+ if (path.isAbsolute(requested))
37
+ throw new ToolDenied("path must be relative to the working directory");
38
+ if (isSensitive(path.normalize(requested)))
39
+ throw new ToolDenied("path is a credential or repository-internal file");
40
+ const target = path.resolve(plan.cwd, requested);
41
+ for (const allowed of plan.allowedPaths) {
42
+ let root;
43
+ try {
44
+ root = resolveWithin(plan.cwd, allowed);
45
+ }
46
+ catch {
47
+ continue;
48
+ }
49
+ const relative = path.relative(root, target);
50
+ if (relative.startsWith("..") || path.isAbsolute(relative))
51
+ continue;
52
+ try {
53
+ resolveWithin(root, relative);
54
+ }
55
+ catch {
56
+ throw new ToolDenied("path escapes the allowed paths");
57
+ }
58
+ const real = realDeepestExisting(target);
59
+ if (isSensitive(path.relative(realDeepestExisting(root), real)))
60
+ throw new ToolDenied("path leads to a credential or repository-internal file");
61
+ return real;
62
+ }
63
+ throw new ToolDenied("path is outside the allowed paths");
64
+ }
65
+ export async function executeAgentTool(plan, name, args) {
66
+ if (name === "read_file") {
67
+ const target = resolveTarget(plan, args.path);
68
+ const info = await stat(target);
69
+ if (!info.isFile())
70
+ throw new ToolDenied("not a file");
71
+ if (info.size > MAX_READ_BYTES)
72
+ throw new ToolDenied(`file exceeds ${MAX_READ_BYTES} bytes`);
73
+ const content = await readFile(target, "utf8");
74
+ // Refused, not redacted: a redacted file edited and written back would be corrupted.
75
+ if (containsKnownSecret(content))
76
+ throw new ToolDenied("file appears to contain a credential");
77
+ return content;
78
+ }
79
+ if (name === "list_directory") {
80
+ const target = resolveTarget(plan, args.path);
81
+ const entries = (await readdir(target, { withFileTypes: true }))
82
+ .filter((entry) => !isSensitive(entry.name))
83
+ .map((entry) => (entry.isDirectory() ? `${entry.name}/` : entry.name))
84
+ .sort();
85
+ const shown = entries.slice(0, MAX_LIST_ENTRIES);
86
+ return [
87
+ ...shown,
88
+ ...(entries.length > shown.length
89
+ ? [`[${entries.length - shown.length} more entries not shown]`]
90
+ : []),
91
+ ].join("\n");
92
+ }
93
+ const { path: requested, content } = args;
94
+ const target = resolveTarget(plan, requested);
95
+ await mkdir(path.dirname(target), { recursive: true });
96
+ await atomicWrite(target, content);
97
+ return `wrote ${Buffer.byteLength(content)} bytes to ${requested}`;
98
+ }
99
+ //# sourceMappingURL=agent-tool-executor.js.map
@@ -0,0 +1,49 @@
1
+ import { spawn } from "node:child_process";
2
+ import { ownsGroup, superviseProcess } from "./process-supervisor.js";
3
+ // Applied whenever a caller doesn't pass an explicit maxOutputBytes, so a headless run
4
+ // without a budget contract can never buffer unbounded output (disk/memory exhaustion,
5
+ // unbounded secret/log capture surface).
6
+ export const DEFAULT_MAX_OUTPUT_BYTES = 64 * 1024 * 1024;
7
+ export async function runHeadless(request) {
8
+ const events = [];
9
+ const supervision = superviseProcess({
10
+ timeoutMs: request.timeoutMs ?? 60_000,
11
+ maxOutputBytes: request.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES,
12
+ onLine: (line) => {
13
+ let event;
14
+ try {
15
+ event = { type: "json", data: JSON.parse(line) };
16
+ }
17
+ catch {
18
+ event = { type: "text", data: line };
19
+ }
20
+ events.push(event);
21
+ request.onEvent?.(event);
22
+ },
23
+ });
24
+ try {
25
+ const child = spawn(request.command, request.args, {
26
+ cwd: request.cwd,
27
+ env: { ...process.env, ...request.env },
28
+ stdio: ["ignore", "pipe", "pipe"],
29
+ detached: ownsGroup,
30
+ windowsHide: true,
31
+ });
32
+ supervision.attach(child);
33
+ if (child.pid)
34
+ request.onSpawn?.(child.pid);
35
+ await Promise.race([supervision.closed, supervision.released]);
36
+ if (supervision.spawnError && !supervision.timedOut)
37
+ throw supervision.spawnError;
38
+ supervision.flush();
39
+ return {
40
+ exitCode: supervision.exitCode(child.exitCode),
41
+ events,
42
+ stderr: supervision.stderr + supervision.stopNote,
43
+ };
44
+ }
45
+ finally {
46
+ supervision.finish();
47
+ }
48
+ }
49
+ //# sourceMappingURL=cli-process.js.map