@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,547 @@
1
+ import { execFile as execFileCallback } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { access, chmod, mkdir, readdir, readFile, stat, writeFile, } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { promisify } from "node:util";
6
+ import { doctorMemoryIndexes, syncMemoryIndexes, } from "@ocean/brain/memory/index-sync.js";
7
+ import { BRAIN_RECORD_DIRS, CHARTER_DIR, enginePath, oceanEnv, oceanPath, POLICIES_DIR, PROJECTS_DIR, projectDir, repoPath, SYSTEM_DIR, workspaceTasksRoot, } from "@ocean/core/shared/paths.js";
8
+ import { ensureProjectLayout, listProjectManifests, PROJECT_ROOT_ENTRIES, scanProjectManifests, UNCATEGORIZED, } from "@ocean/core/shared/project-resolution.js";
9
+ const execFile = promisify(execFileCallback);
10
+ const roots = [...BRAIN_RECORD_DIRS, PROJECTS_DIR, SYSTEM_DIR];
11
+ async function exists(file) {
12
+ try {
13
+ await access(file);
14
+ return true;
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
20
+ async function markdownFiles(directory) {
21
+ if (!(await exists(directory)))
22
+ return [];
23
+ const files = [];
24
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
25
+ if (entry.name.startsWith(".") ||
26
+ entry.name === "archive" ||
27
+ entry.name === "node_modules")
28
+ continue;
29
+ const file = path.join(directory, entry.name);
30
+ if (entry.isDirectory())
31
+ files.push(...(await markdownFiles(file)));
32
+ else if (entry.name.endsWith(".md"))
33
+ files.push(file);
34
+ }
35
+ return files;
36
+ }
37
+ async function activeDirectories(directory) {
38
+ if (!(await exists(directory)))
39
+ return [];
40
+ const directories = [directory];
41
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
42
+ if (entry.name.startsWith(".") || entry.name === "archive")
43
+ continue;
44
+ if (entry.isDirectory())
45
+ directories.push(...(await activeDirectories(path.join(directory, entry.name))));
46
+ }
47
+ return directories;
48
+ }
49
+ function links(content) {
50
+ return [...content.matchAll(/\]\(([^)#]+)(?:#[^)]+)?\)/g)]
51
+ .map((match) => match[1]?.trim() ?? "")
52
+ .filter((link) => link && !/^(?:https?:|mailto:|data:)/i.test(link));
53
+ }
54
+ async function checkStructure() {
55
+ const findings = [];
56
+ for (const root of roots) {
57
+ const directory = oceanPath(root);
58
+ findings.push((await exists(directory))
59
+ ? {
60
+ code: "ROOT_PRESENT",
61
+ severity: "OK",
62
+ path: root,
63
+ message: `${root} root exists`,
64
+ fixable: false,
65
+ }
66
+ : {
67
+ code: "ROOT_MISSING",
68
+ severity: "FAIL",
69
+ path: root,
70
+ message: `${root} root is missing`,
71
+ fixable: false,
72
+ });
73
+ }
74
+ return findings;
75
+ }
76
+ // Session summaries are machine-written records that quote links from past chats, and
77
+ // bridge/skills holds installed third-party skill docs whose links are relative to their upstream
78
+ // repo. Neither is authored here, so their links are not ours to fix.
79
+ const UNCHECKED_LINK_DIRS = ["sessions", "skills"];
80
+ async function checkLinks() {
81
+ const findings = [];
82
+ const unchecked = UNCHECKED_LINK_DIRS.map((name) => path.join(oceanPath(SYSTEM_DIR), name) + path.sep);
83
+ for (const root of roots) {
84
+ for (const file of await markdownFiles(oceanPath(root))) {
85
+ if (unchecked.some((prefix) => file.startsWith(prefix)))
86
+ continue;
87
+ const content = await readFile(file, "utf8");
88
+ for (const link of links(content)) {
89
+ const target = path.resolve(path.dirname(file), link);
90
+ if (!(await exists(target)))
91
+ findings.push({
92
+ code: "BROKEN_LINK",
93
+ severity: "WARN",
94
+ path: path.relative(oceanPath(), file),
95
+ message: `broken relative link: ${link}`,
96
+ fixable: false,
97
+ });
98
+ }
99
+ }
100
+ }
101
+ return findings;
102
+ }
103
+ async function checkMemory() {
104
+ try {
105
+ const result = await doctorMemoryIndexes();
106
+ if (result.ok)
107
+ return [
108
+ {
109
+ code: "INDEXES_SYNCED",
110
+ severity: "OK",
111
+ message: "memory and knowledge indexes are synchronized",
112
+ fixable: false,
113
+ },
114
+ ];
115
+ return [
116
+ {
117
+ code: "INDEX_DRIFT",
118
+ severity: "WARN",
119
+ message: `index drift: ${[...result.missing, ...result.broken].join(", ")}`,
120
+ fixable: true,
121
+ },
122
+ ];
123
+ }
124
+ catch (error) {
125
+ if (error.code === "ENOENT") {
126
+ return [
127
+ {
128
+ code: "INDEXES_UNCHECKED",
129
+ severity: "WARN",
130
+ message: "memory and knowledge indexes are not initialized",
131
+ fixable: true,
132
+ },
133
+ ];
134
+ }
135
+ throw error;
136
+ }
137
+ }
138
+ async function checkWrappers(wrapperManager) {
139
+ const errors = await wrapperManager.wrapperDoctor();
140
+ return errors.length
141
+ ? errors.map((message) => ({
142
+ code: "WRAPPER_DRIFT",
143
+ severity: "WARN",
144
+ message,
145
+ fixable: /wrapper is (stale|missing)/i.test(message),
146
+ }))
147
+ : [
148
+ {
149
+ code: "WRAPPERS_READY",
150
+ severity: "OK",
151
+ message: "Ocean wrappers are synchronized",
152
+ fixable: false,
153
+ },
154
+ ];
155
+ }
156
+ async function checkDependencies() {
157
+ if (oceanEnv("SKIP_DEPENDENCY_AUDIT") === "1") {
158
+ return [
159
+ {
160
+ code: "DEPENDENCY_AUDIT_SKIPPED",
161
+ severity: "WARN",
162
+ message: "dependency audit was not run because the caller skipped it",
163
+ fixable: false,
164
+ },
165
+ ];
166
+ }
167
+ if (!(await exists(enginePath("pnpm-lock.yaml"))))
168
+ return [
169
+ {
170
+ code: "DEPENDENCY_AUDIT_SKIPPED",
171
+ severity: "WARN",
172
+ message: "dependency audit was not run because metadata is not included in the installed package",
173
+ fixable: false,
174
+ },
175
+ ];
176
+ try {
177
+ await execFile("pnpm", ["audit", "--audit-level", "high", "--json"], {
178
+ cwd: enginePath(),
179
+ timeout: 20_000,
180
+ });
181
+ return [
182
+ {
183
+ code: "DEPENDENCIES_CLEAN",
184
+ severity: "OK",
185
+ message: "no high-severity dependency advisories reported",
186
+ fixable: false,
187
+ },
188
+ ];
189
+ }
190
+ catch {
191
+ return [
192
+ {
193
+ code: "DEPENDENCY_AUDIT_FAILED",
194
+ severity: "WARN",
195
+ message: "dependency audit failed or reported high-severity advisories; run pnpm audit manually",
196
+ fixable: false,
197
+ },
198
+ ];
199
+ }
200
+ }
201
+ async function checkWorkspaceContracts() {
202
+ const checks = [
203
+ ["PRIVACY_BOUNDARY", "node", ["scripts/scan-privacy.mjs", "."], repoPath],
204
+ ["PACKAGE_BOUNDARY", "node", ["scripts/validate-package.mjs"], repoPath],
205
+ [
206
+ "TASK_RECORDS",
207
+ "node",
208
+ ["scripts/validate-tasks.mjs", workspaceTasksRoot()],
209
+ enginePath,
210
+ ],
211
+ ];
212
+ const findings = [];
213
+ for (const [code, command, args, basePath] of checks) {
214
+ const script = args[0];
215
+ if (!(await exists(basePath(script)))) {
216
+ findings.push({
217
+ code,
218
+ severity: "OK",
219
+ message: `${code.toLowerCase().replaceAll("_", " ")} not included in the installed package`,
220
+ fixable: false,
221
+ });
222
+ continue;
223
+ }
224
+ try {
225
+ await execFile(command, [...args], {
226
+ cwd: basePath(),
227
+ timeout: 20_000,
228
+ });
229
+ findings.push({
230
+ code,
231
+ severity: "OK",
232
+ message: `${code.toLowerCase().replaceAll("_", " ")} passed`,
233
+ fixable: false,
234
+ });
235
+ }
236
+ catch {
237
+ findings.push({
238
+ code,
239
+ severity: "WARN",
240
+ message: `${code.toLowerCase().replaceAll("_", " ")} failed; inspect its dedicated check`,
241
+ fixable: false,
242
+ });
243
+ }
244
+ }
245
+ return findings;
246
+ }
247
+ async function checkPermissions() {
248
+ if (process.platform === "win32")
249
+ return [
250
+ {
251
+ code: "PERMISSIONS_SKIPPED",
252
+ severity: "OK",
253
+ message: "owner-only directory mode is not applicable on Windows",
254
+ fixable: false,
255
+ },
256
+ ];
257
+ const unsafe = [];
258
+ for (const directory of await activeDirectories(oceanPath(SYSTEM_DIR))) {
259
+ const mode = (await stat(directory)).mode & 0o777;
260
+ if ((mode & 0o077) !== 0)
261
+ unsafe.push(path.relative(oceanPath(), directory));
262
+ }
263
+ return unsafe.length
264
+ ? [
265
+ {
266
+ code: "PRIVATE_PERMISSIONS",
267
+ severity: "WARN",
268
+ message: `system directories are not owner-only: ${unsafe.join(", ")}`,
269
+ fixable: true,
270
+ },
271
+ ]
272
+ : [
273
+ {
274
+ code: "PRIVATE_PERMISSIONS",
275
+ severity: "OK",
276
+ message: "active system directories are owner-only",
277
+ fixable: false,
278
+ },
279
+ ];
280
+ }
281
+ // A project folder keeps reference material in docs/ and lessons in memory/; anything else at
282
+ // its root is a file a session put in the wrong place. Moving it is a judgment call, so repair
283
+ // only adds the missing standard folders.
284
+ async function checkProjectLayout() {
285
+ const findings = [];
286
+ for (const manifest of await listProjectManifests()) {
287
+ const relative = projectDir(manifest.id);
288
+ const entries = await readdir(oceanPath(relative)).catch(() => []);
289
+ const missing = ["tasks", "memory", "docs", "MEMORY.md"].filter((entry) => !entries.includes(entry));
290
+ const stray = entries.filter((entry) => !entry.startsWith(".") && !PROJECT_ROOT_ENTRIES.includes(entry));
291
+ if (missing.length)
292
+ findings.push({
293
+ code: "PROJECT_LAYOUT_MISSING",
294
+ severity: "WARN",
295
+ path: relative,
296
+ message: `project '${manifest.id}' is missing ${missing.join(", ")}`,
297
+ fixable: true,
298
+ });
299
+ if (stray.length)
300
+ findings.push({
301
+ code: "PROJECT_LAYOUT_STRAY",
302
+ severity: "WARN",
303
+ path: relative,
304
+ message: `project '${manifest.id}' has ${stray.join(", ")} at its root; reference material belongs in docs/, lessons in memory/`,
305
+ fixable: false,
306
+ });
307
+ }
308
+ return findings.length
309
+ ? findings
310
+ : [
311
+ {
312
+ code: "PROJECT_LAYOUT_OK",
313
+ severity: "OK",
314
+ message: "every project folder follows the standard layout",
315
+ fixable: false,
316
+ },
317
+ ];
318
+ }
319
+ // A Claude project registered by a conversation (T-307) has no repo path: the owner should see
320
+ // it once, then keep it (set `autoRegistered` to false in its project.json), rename or remove it.
321
+ async function checkClaudeProjects() {
322
+ const registered = (await listProjectManifests()).filter((manifest) => manifest.autoRegistered && manifest.paths.length === 0);
323
+ return registered.length
324
+ ? [
325
+ {
326
+ code: "PROJECT_AUTO_REGISTERED",
327
+ severity: "WARN",
328
+ message: `registered from Claude projects: ${registered.map((manifest) => manifest.id).join(", ")} — keep (set autoRegistered to false in project.json), rename or remove`,
329
+ fixable: false,
330
+ },
331
+ ]
332
+ : [];
333
+ }
334
+ // A project lives in its category folder (T-311): one still flat under 04-projects or waiting
335
+ // in uncategorized/ is reported until `ocean project move` files it, and one id in two category
336
+ // folders is an error, since only the first is read.
337
+ async function checkProjectCategories() {
338
+ const { manifests, errors } = await scanProjectManifests();
339
+ const findings = errors
340
+ .filter(({ error }) => error.includes("already used by another"))
341
+ .map(({ file, error }) => ({
342
+ code: "PROJECT_DUPLICATE_ID",
343
+ severity: "FAIL",
344
+ path: path.relative(oceanPath(), file).split(path.sep).join("/"),
345
+ message: `${error}; merge or rename one of them`,
346
+ fixable: false,
347
+ }));
348
+ for (const { id } of manifests) {
349
+ const dir = projectDir(id);
350
+ const [first, second] = path.posix.relative(PROJECTS_DIR, dir).split("/");
351
+ const where = second === undefined ? "flat" : first === UNCATEGORIZED ? "new" : null;
352
+ if (where)
353
+ findings.push({
354
+ code: where === "flat" ? "PROJECT_FLAT" : "PROJECT_UNCATEGORIZED",
355
+ severity: "WARN",
356
+ path: dir,
357
+ message: `project '${id}' ${where === "flat" ? "sits flat in" : "has no category yet in"} ${dir}; file it with \`ocean project move ${id} <category>\``,
358
+ fixable: false,
359
+ });
360
+ }
361
+ return findings;
362
+ }
363
+ async function checkDuplicates() {
364
+ const seen = new Map();
365
+ const duplicates = [];
366
+ for (const root of [...BRAIN_RECORD_DIRS, PROJECTS_DIR]) {
367
+ for (const file of await markdownFiles(oceanPath(root))) {
368
+ const hash = createHash("sha256")
369
+ .update(await readFile(file))
370
+ .digest("hex");
371
+ const relative = path.relative(oceanPath(), file);
372
+ const previous = seen.get(hash);
373
+ if (previous)
374
+ duplicates.push(`${previous} = ${relative}`);
375
+ else
376
+ seen.set(hash, relative);
377
+ }
378
+ }
379
+ return duplicates.length
380
+ ? [
381
+ {
382
+ code: "DUPLICATE_RECORDS",
383
+ severity: "WARN",
384
+ message: `identical active markdown files: ${duplicates.join("; ")}`,
385
+ fixable: false,
386
+ },
387
+ ]
388
+ : [
389
+ {
390
+ code: "DUPLICATES_NONE",
391
+ severity: "OK",
392
+ message: "no identical active personal/project markdown files",
393
+ fixable: false,
394
+ },
395
+ ];
396
+ }
397
+ async function checkProfileAuthority() {
398
+ const root = oceanPath(SYSTEM_DIR, "profiles");
399
+ if (!(await exists(root)))
400
+ return [
401
+ {
402
+ code: "PROFILES_UNCHECKED",
403
+ severity: "WARN",
404
+ message: "profiles root is missing",
405
+ fixable: false,
406
+ },
407
+ ];
408
+ const legacy = [];
409
+ for (const entry of await readdir(root, { withFileTypes: true })) {
410
+ if (entry.isDirectory() &&
411
+ (await exists(path.join(root, entry.name, "profile.json"))))
412
+ legacy.push(entry.name);
413
+ }
414
+ return legacy.length
415
+ ? [
416
+ {
417
+ code: "PROFILE_AUTHORITY_DRIFT",
418
+ severity: "WARN",
419
+ message: `legacy profile directories remain active: ${legacy.join(", ")}`,
420
+ fixable: false,
421
+ },
422
+ ]
423
+ : [
424
+ {
425
+ code: "PROFILE_AUTHORITY_CANONICAL",
426
+ severity: "OK",
427
+ message: "profiles use canonical root-level JSON authority",
428
+ fixable: false,
429
+ },
430
+ ];
431
+ }
432
+ async function checkVersion() {
433
+ const packageFile = enginePath("package.json");
434
+ if (!(await exists(packageFile)))
435
+ return [
436
+ {
437
+ code: "VERSION_UNCHECKED",
438
+ severity: "WARN",
439
+ message: "package version file is missing",
440
+ fixable: false,
441
+ },
442
+ ];
443
+ const pkg = JSON.parse(await readFile(packageFile, "utf8"));
444
+ return [
445
+ {
446
+ code: "PACKAGE_VERSION",
447
+ severity: "OK",
448
+ message: `installed package version ${pkg.version ?? "unknown"}`,
449
+ fixable: false,
450
+ },
451
+ ];
452
+ }
453
+ async function checkGovernance() {
454
+ const core = oceanPath(CHARTER_DIR, "core.md");
455
+ const policies = oceanPath(POLICIES_DIR);
456
+ if (!(await exists(core)) || !(await exists(policies)))
457
+ return [
458
+ {
459
+ code: "GOVERNANCE_MISSING",
460
+ severity: "WARN",
461
+ message: "canonical governance rules or policies are missing",
462
+ fixable: false,
463
+ },
464
+ ];
465
+ const source = await readFile(core, "utf8");
466
+ const available = new Set((await readdir(policies))
467
+ .filter((name) => name.endsWith(".md"))
468
+ .map((name) => name.slice(0, -3)));
469
+ const referenced = [...source.matchAll(/ocean policy ([a-z-]+)/g)]
470
+ .map((match) => match[1])
471
+ .filter((name) => name !== "list");
472
+ const missing = [
473
+ ...new Set(referenced.filter((name) => !available.has(name))),
474
+ ];
475
+ return missing.length
476
+ ? [
477
+ {
478
+ code: "GOVERNANCE_DRIFT",
479
+ severity: "WARN",
480
+ message: `governance references missing policies: ${missing.join(", ")}`,
481
+ fixable: false,
482
+ },
483
+ ]
484
+ : [
485
+ {
486
+ code: "GOVERNANCE_ALIGNED",
487
+ severity: "OK",
488
+ message: "governance policy references resolve",
489
+ fixable: false,
490
+ },
491
+ ];
492
+ }
493
+ async function scanWorkspace(wrapperManager) {
494
+ return [
495
+ ...(await checkStructure()),
496
+ ...(await checkLinks()),
497
+ ...(await checkMemory()),
498
+ ...(await checkVersion()),
499
+ ...(await checkGovernance()),
500
+ ...(await checkWrappers(wrapperManager)),
501
+ ...(await checkDependencies()),
502
+ ...(await checkWorkspaceContracts()),
503
+ ...(await checkPermissions()),
504
+ ...(await checkDuplicates()),
505
+ ...(await checkProjectLayout()),
506
+ ...(await checkClaudeProjects()),
507
+ ...(await checkProjectCategories()),
508
+ ...(await checkProfileAuthority()),
509
+ ];
510
+ }
511
+ export function hasFailures(findings) {
512
+ return findings.some((finding) => finding.severity === "FAIL");
513
+ }
514
+ export async function repairWorkspace(wrapperManager) {
515
+ const before = await scanWorkspace(wrapperManager);
516
+ const changes = [];
517
+ if (before.some((finding) => finding.code === "INDEX_DRIFT")) {
518
+ await syncMemoryIndexes(true);
519
+ changes.push("synchronized memory and knowledge indexes");
520
+ }
521
+ if (before.some((finding) => finding.code === "PRIVATE_PERMISSIONS" && finding.fixable)) {
522
+ for (const directory of await activeDirectories(oceanPath(SYSTEM_DIR)))
523
+ await chmod(directory, 0o700);
524
+ changes.push("restricted active system directories to owner-only");
525
+ }
526
+ if (before.some((finding) => finding.code === "PROJECT_LAYOUT_MISSING")) {
527
+ for (const manifest of await listProjectManifests()) {
528
+ const created = await ensureProjectLayout(manifest.id);
529
+ if (created.length)
530
+ changes.push(`project '${manifest.id}': created ${created.join(", ")}`);
531
+ }
532
+ }
533
+ await wrapperManager.syncProviderWrappers();
534
+ changes.push("synchronized provider wrappers");
535
+ const after = await scanWorkspace(wrapperManager);
536
+ const reportDirectory = oceanPath(SYSTEM_DIR, "runtime", "reports");
537
+ await mkdir(reportDirectory, { recursive: true });
538
+ await writeFile(path.join(reportDirectory, "repair-report.json"), `${JSON.stringify({ changes, before, after }, null, 2)}\n`);
539
+ return { changes, findings: after };
540
+ }
541
+ export async function workspaceReport(wrapperManager) {
542
+ return {
543
+ findings: await scanWorkspace(wrapperManager),
544
+ generatedAt: new Date().toISOString(),
545
+ };
546
+ }
547
+ //# sourceMappingURL=workspace-doctor.js.map
@@ -0,0 +1,114 @@
1
+ import { appendFile, mkdir, readFile, stat, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { withFileLock } from "@ocean/core/shared/fs-utils.js";
4
+ import { oceanPath, SYSTEM_DIR } from "@ocean/core/shared/paths.js";
5
+ export const CALL_LEDGER_RETENTION_DAYS = 90;
6
+ // Past this size the oldest half goes, whatever its age.
7
+ const MAX_LEDGER_BYTES = 2_000_000;
8
+ const DAY_MS = 86_400_000;
9
+ const NAME = /^[\w .@/-]{1,64}$/;
10
+ export function callLedgerFile() {
11
+ return oceanPath(SYSTEM_DIR, "state", "mcp", "calls.jsonl");
12
+ }
13
+ // Built field by field so nothing else a caller holds can reach the file.
14
+ export function ledgerLine(client, tool, ok, now = new Date()) {
15
+ const call = {
16
+ ts: now.toISOString(),
17
+ client: NAME.test(client) ? client : "unknown",
18
+ tool: NAME.test(tool) ? tool : "unknown",
19
+ ok: ok === true,
20
+ };
21
+ return `${JSON.stringify(call)}\n`;
22
+ }
23
+ export async function readCallLedger() {
24
+ let text;
25
+ try {
26
+ text = await readFile(callLedgerFile(), "utf8");
27
+ }
28
+ catch (error) {
29
+ if (error.code === "ENOENT")
30
+ return [];
31
+ throw error;
32
+ }
33
+ const calls = [];
34
+ for (const line of text.split("\n")) {
35
+ if (!line)
36
+ continue;
37
+ try {
38
+ calls.push(JSON.parse(line));
39
+ }
40
+ catch {
41
+ // A torn line from a crash is skipped, not fatal.
42
+ }
43
+ }
44
+ return calls;
45
+ }
46
+ // Per client and tool: how often, how often it succeeded, and when last.
47
+ export function summarizeCallLedger(calls) {
48
+ const rows = new Map();
49
+ for (const call of calls) {
50
+ const key = `${call.client}\u0000${call.tool}`;
51
+ const row = rows.get(key) ?? {
52
+ client: call.client,
53
+ tool: call.tool,
54
+ calls: 0,
55
+ ok: 0,
56
+ last: call.ts,
57
+ };
58
+ row.calls += 1;
59
+ if (call.ok)
60
+ row.ok += 1;
61
+ if (call.ts > row.last)
62
+ row.last = call.ts;
63
+ rows.set(key, row);
64
+ }
65
+ return [...rows.values()].sort((a, b) => a.client.localeCompare(b.client) || a.tool.localeCompare(b.tool));
66
+ }
67
+ // Drops lines older than the retention, then the oldest half while still over the size cap.
68
+ export function pruneLedgerText(text, now = new Date()) {
69
+ const cutoff = now.getTime() - CALL_LEDGER_RETENTION_DAYS * DAY_MS;
70
+ let lines = text.split("\n").filter((line) => {
71
+ if (!line)
72
+ return false;
73
+ try {
74
+ const ts = Date.parse(JSON.parse(line).ts);
75
+ return Number.isFinite(ts) && ts >= cutoff;
76
+ }
77
+ catch {
78
+ return false;
79
+ }
80
+ });
81
+ while (lines.length > 1 &&
82
+ Buffer.byteLength(lines.join("\n")) > MAX_LEDGER_BYTES)
83
+ lines = lines.slice(Math.floor(lines.length / 2));
84
+ return lines.length ? `${lines.join("\n")}\n` : "";
85
+ }
86
+ let prunedThisProcess = false;
87
+ // Never throws: a ledger problem must not fail the tool call it records.
88
+ export async function recordMcpCall(client, tool, ok, now = new Date()) {
89
+ const file = callLedgerFile();
90
+ try {
91
+ await mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
92
+ await withFileLock(file, async () => {
93
+ await appendFile(file, ledgerLine(client, tool, ok, now), {
94
+ mode: 0o600,
95
+ });
96
+ const size = (await stat(file)).size;
97
+ if (prunedThisProcess && size <= MAX_LEDGER_BYTES)
98
+ return;
99
+ prunedThisProcess = true;
100
+ const text = await readFile(file, "utf8");
101
+ const kept = pruneLedgerText(text, now);
102
+ if (kept !== text)
103
+ await writeFile(file, kept, { mode: 0o600 });
104
+ });
105
+ }
106
+ catch {
107
+ // Best effort only.
108
+ }
109
+ }
110
+ // Tests run many workspaces in one process.
111
+ export function resetCallLedgerForTests() {
112
+ prunedThisProcess = false;
113
+ }
114
+ //# sourceMappingURL=call-ledger.js.map