@amoree-code/ocean 0.0.0-stage → 1.2.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 (300) 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/package.json +11 -0
  112. package/node_modules/@ocean/context/dist/context/context-compression.js +66 -0
  113. package/node_modules/@ocean/context/dist/context/context-cost.js +587 -0
  114. package/node_modules/@ocean/context/dist/context/context-ladder.js +363 -0
  115. package/node_modules/@ocean/context/dist/context/context-packet.js +213 -0
  116. package/node_modules/@ocean/context/dist/context/context-references.js +255 -0
  117. package/node_modules/@ocean/context/dist/context/intent-router.js +259 -0
  118. package/node_modules/@ocean/context/dist/context/resource-injection.js +36 -0
  119. package/node_modules/@ocean/context/package.json +11 -0
  120. package/node_modules/@ocean/core/dist/domain/brain/brain-record.js +22 -0
  121. package/node_modules/@ocean/core/dist/domain/brain/record-scope.js +22 -0
  122. package/node_modules/@ocean/core/dist/domain/capabilities/browser-contract.js +64 -0
  123. package/node_modules/@ocean/core/dist/domain/capabilities/capability-contract.js +16 -0
  124. package/node_modules/@ocean/core/dist/domain/capabilities/capability-manifest.js +72 -0
  125. package/node_modules/@ocean/core/dist/domain/context/context-validator.js +5 -0
  126. package/node_modules/@ocean/core/dist/domain/context/context.js +34 -0
  127. package/node_modules/@ocean/core/dist/domain/context/project-id.js +30 -0
  128. package/node_modules/@ocean/core/dist/domain/evidence/evidence.js +16 -0
  129. package/node_modules/@ocean/core/dist/domain/mcp/mcp-contract.js +36 -0
  130. package/node_modules/@ocean/core/dist/domain/mcp/memory-contract.js +41 -0
  131. package/node_modules/@ocean/core/dist/domain/ports/brain-index-port.js +7 -0
  132. package/node_modules/@ocean/core/dist/domain/ports/browser-port.js +8 -0
  133. package/node_modules/@ocean/core/dist/domain/ports/embedder-port.js +2 -0
  134. package/node_modules/@ocean/core/dist/domain/ports/platform-ports.js +2 -0
  135. package/node_modules/@ocean/core/dist/domain/ports/runtime-ports.js +2 -0
  136. package/node_modules/@ocean/core/dist/domain/ports/session-store-port.js +2 -0
  137. package/node_modules/@ocean/core/dist/domain/process/process-events.js +5 -0
  138. package/node_modules/@ocean/core/dist/domain/profiles/profile-distribution.js +19 -0
  139. package/node_modules/@ocean/core/dist/domain/profiles/profile-policy.js +34 -0
  140. package/node_modules/@ocean/core/dist/domain/profiles/profile-selection.js +73 -0
  141. package/node_modules/@ocean/core/dist/domain/profiles/profile-upgrade.js +123 -0
  142. package/node_modules/@ocean/core/dist/domain/profiles/profile-validator.js +5 -0
  143. package/node_modules/@ocean/core/dist/domain/profiles/profile.js +218 -0
  144. package/node_modules/@ocean/core/dist/domain/profiles/runtime-config.js +30 -0
  145. package/node_modules/@ocean/core/dist/domain/providers/provider-capability.js +28 -0
  146. package/node_modules/@ocean/core/dist/domain/providers/provider.js +2 -0
  147. package/node_modules/@ocean/core/dist/domain/redaction/redaction.js +86 -0
  148. package/node_modules/@ocean/core/dist/domain/runs/agent-tools.js +92 -0
  149. package/node_modules/@ocean/core/dist/domain/runs/execution-plan.js +71 -0
  150. package/node_modules/@ocean/core/dist/domain/runs/run-contract.js +38 -0
  151. package/node_modules/@ocean/core/dist/domain/sessions/entry-contract.js +31 -0
  152. package/node_modules/@ocean/core/dist/domain/sessions/handoff.js +78 -0
  153. package/node_modules/@ocean/core/dist/domain/sessions/session.js +58 -0
  154. package/node_modules/@ocean/core/dist/domain/skills/default-skills.js +29 -0
  155. package/node_modules/@ocean/core/dist/domain/skills/skill-validator.js +5 -0
  156. package/node_modules/@ocean/core/dist/domain/skills/skill.js +11 -0
  157. package/node_modules/@ocean/core/dist/domain/tasks/task-id.js +85 -0
  158. package/node_modules/@ocean/core/dist/shared/fs-utils.js +212 -0
  159. package/node_modules/@ocean/core/dist/shared/local-date.js +16 -0
  160. package/node_modules/@ocean/core/dist/shared/paths.js +324 -0
  161. package/node_modules/@ocean/core/dist/shared/project-resolution.js +1065 -0
  162. package/node_modules/@ocean/core/dist/shared/task-reference.js +133 -0
  163. package/node_modules/@ocean/core/package.json +14 -0
  164. package/node_modules/@ocean/loop/dist/handoff/handoff-service.js +133 -0
  165. package/node_modules/@ocean/loop/dist/hooks/lifecycle-hooks.js +15 -0
  166. package/node_modules/@ocean/loop/dist/hooks/session-end-hook.js +183 -0
  167. package/node_modules/@ocean/loop/dist/hooks/session-start-hook.js +237 -0
  168. package/node_modules/@ocean/loop/dist/infrastructure/persistence/session-store.js +690 -0
  169. package/node_modules/@ocean/loop/dist/infrastructure/persistence/session-validator.js +5 -0
  170. package/node_modules/@ocean/loop/dist/loops/iteration-graph.js +147 -0
  171. package/node_modules/@ocean/loop/dist/loops/task-loop-record.js +2 -0
  172. package/node_modules/@ocean/loop/dist/loops/task-loop.js +281 -0
  173. package/node_modules/@ocean/loop/dist/operations/memory-knowledge-operations.js +263 -0
  174. package/node_modules/@ocean/loop/dist/operations/operation-contract.js +227 -0
  175. package/node_modules/@ocean/loop/dist/operations/operation-scope.js +28 -0
  176. package/node_modules/@ocean/loop/dist/operations/project-operations.js +93 -0
  177. package/node_modules/@ocean/loop/dist/operations/record-io.js +65 -0
  178. package/node_modules/@ocean/loop/dist/operations/record-operations.js +45 -0
  179. package/node_modules/@ocean/loop/dist/operations/task-linker.js +40 -0
  180. package/node_modules/@ocean/loop/dist/operations/task-operations.js +279 -0
  181. package/node_modules/@ocean/loop/dist/operations/write-guard.js +157 -0
  182. package/node_modules/@ocean/loop/dist/runs/prompt-assembly.js +112 -0
  183. package/node_modules/@ocean/loop/dist/runs/run-agent.js +425 -0
  184. package/node_modules/@ocean/loop/dist/runs/run-authorization.js +24 -0
  185. package/node_modules/@ocean/loop/dist/runs/tool-loop.js +173 -0
  186. package/node_modules/@ocean/loop/dist/sessions/session-closeout.js +150 -0
  187. package/node_modules/@ocean/loop/dist/sessions/session-pointer.js +133 -0
  188. package/node_modules/@ocean/loop/dist/sessions/session-projects.js +26 -0
  189. package/node_modules/@ocean/loop/dist/sessions/session-retention.js +132 -0
  190. package/node_modules/@ocean/loop/dist/tasks/archive-tasks.js +210 -0
  191. package/node_modules/@ocean/loop/dist/tasks/checkpoint-task.js +31 -0
  192. package/node_modules/@ocean/loop/dist/tasks/claim-task.js +126 -0
  193. package/node_modules/@ocean/loop/dist/tasks/list-tasks.js +113 -0
  194. package/node_modules/@ocean/loop/dist/tasks/new-task.js +42 -0
  195. package/node_modules/@ocean/loop/dist/tasks/renumber-tasks.js +134 -0
  196. package/node_modules/@ocean/loop/dist/tasks/verification-readiness.js +78 -0
  197. package/node_modules/@ocean/loop/dist/tasks/verify-task.js +234 -0
  198. package/node_modules/@ocean/loop/package.json +11 -0
  199. package/node_modules/@ocean/market/dist/adapters/mcp-adapter.js +64 -0
  200. package/node_modules/@ocean/market/dist/adapters/owned-directory.js +89 -0
  201. package/node_modules/@ocean/market/dist/adapters/plugin-adapter.js +39 -0
  202. package/node_modules/@ocean/market/dist/adapters/skill-adapter.js +32 -0
  203. package/node_modules/@ocean/market/dist/capability/adapter.js +2 -0
  204. package/node_modules/@ocean/market/dist/capability/catalog.js +63 -0
  205. package/node_modules/@ocean/market/dist/capability/content-digest.js +40 -0
  206. package/node_modules/@ocean/market/dist/capability/registry.js +81 -0
  207. package/node_modules/@ocean/market/dist/install/default-marketplace.js +47 -0
  208. package/node_modules/@ocean/market/dist/install/lock.js +34 -0
  209. package/node_modules/@ocean/market/dist/install/marketplace-service.js +165 -0
  210. package/node_modules/@ocean/market/dist/mcp/browser-bridge.js +263 -0
  211. package/node_modules/@ocean/market/dist/mcp/mcp-connection.js +67 -0
  212. package/node_modules/@ocean/market/dist/mcp/mcp-gateway.js +317 -0
  213. package/node_modules/@ocean/market/dist/mcp/mcp-launch.js +102 -0
  214. package/node_modules/@ocean/market/dist/mcp/mcp-runner.js +32 -0
  215. package/node_modules/@ocean/market/dist/security/description-scan.js +45 -0
  216. package/node_modules/@ocean/market/dist/security/permissions.js +57 -0
  217. package/node_modules/@ocean/market/dist/skills/core-skill-sync.js +113 -0
  218. package/node_modules/@ocean/market/dist/skills/signal-detection.js +84 -0
  219. package/node_modules/@ocean/market/dist/skills/skill-hub.js +220 -0
  220. package/node_modules/@ocean/market/package.json +11 -0
  221. package/node_modules/@ocean/services/dist/browser/browser-service.js +124 -0
  222. package/node_modules/@ocean/services/dist/browser/browser-session.js +287 -0
  223. package/node_modules/@ocean/services/dist/browser/browser-task-runner.js +70 -0
  224. package/node_modules/@ocean/services/dist/doctor/workspace-doctor.js +547 -0
  225. package/node_modules/@ocean/services/dist/infrastructure/mcp/call-ledger.js +114 -0
  226. package/node_modules/@ocean/services/dist/infrastructure/mcp/mcp-client.js +162 -0
  227. package/node_modules/@ocean/services/dist/infrastructure/mcp/obsidian-server.js +251 -0
  228. package/node_modules/@ocean/services/dist/infrastructure/mcp/ocean-server.js +880 -0
  229. package/node_modules/@ocean/services/dist/infrastructure/mcp/server-version.js +20 -0
  230. package/node_modules/@ocean/services/dist/infrastructure/mcp/tool-registry.js +29 -0
  231. package/node_modules/@ocean/services/dist/obsidian/conflict-log.js +68 -0
  232. package/node_modules/@ocean/services/dist/obsidian/inbox-promotion.js +80 -0
  233. package/node_modules/@ocean/services/dist/obsidian/vault-discovery.js +113 -0
  234. package/node_modules/@ocean/services/dist/obsidian/vault-ingestion.js +77 -0
  235. package/node_modules/@ocean/services/dist/obsidian/vault-sync.js +73 -0
  236. package/node_modules/@ocean/services/dist/obsidian/vault-writer.js +75 -0
  237. package/node_modules/@ocean/services/package.json +11 -0
  238. package/ocean-marketplace.json +415 -0
  239. package/package.json +67 -4
  240. package/skills/core/business-logic/SKILL.md +22 -0
  241. package/skills/core/catch-up/SKILL.md +17 -0
  242. package/skills/core/core-thinking/SKILL.md +11 -0
  243. package/skills/core/design-thinking/SKILL.md +19 -0
  244. package/skills/core/graft/SKILL.md +156 -0
  245. package/skills/core/loop/SKILL.md +20 -0
  246. package/skills/core/session-handoff/SKILL.md +15 -0
  247. package/skills/core/use-browser/SKILL.md +69 -0
  248. package/skills/core/verification/SKILL.md +11 -0
  249. package/skills/index.json +56 -0
  250. package/templates/README.md +4 -0
  251. package/templates/profiles/default.json +51 -0
  252. package/templates/profiles/developer.json +51 -0
  253. package/templates/profiles/reviewer.json +51 -0
  254. package/templates/profiles/strategist.json +51 -0
  255. package/templates/workspace/00-inbox/INBOX.md +7 -0
  256. package/templates/workspace/00-inbox/candidates/.gitkeep +0 -0
  257. package/templates/workspace/00-inbox/native/.gitkeep +0 -0
  258. package/templates/workspace/01-daily/.gitkeep +0 -0
  259. package/templates/workspace/02-personal/CONFLICTS.md +7 -0
  260. package/templates/workspace/02-personal/MEMORY.md +15 -0
  261. package/templates/workspace/02-personal/goals/.gitkeep +0 -0
  262. package/templates/workspace/02-personal/identity/.gitkeep +0 -0
  263. package/templates/workspace/02-personal/preferences/.gitkeep +0 -0
  264. package/templates/workspace/03-professional/.gitkeep +0 -0
  265. package/templates/workspace/04-projects/PROJECTS.md +5 -0
  266. package/templates/workspace/04-projects/_template/docs/.gitkeep +0 -0
  267. package/templates/workspace/04-projects/_template/memory/.gitkeep +0 -0
  268. package/templates/workspace/04-projects/_template/tasks/.gitkeep +0 -0
  269. package/templates/workspace/05-knowledge/KNOWLEDGE.md +8 -0
  270. package/templates/workspace/05-knowledge/decisions/.gitkeep +0 -0
  271. package/templates/workspace/05-knowledge/lessons/.gitkeep +0 -0
  272. package/templates/workspace/06-templates/TEMPLATES.md +25 -0
  273. package/templates/workspace/06-templates/decision.md +8 -0
  274. package/templates/workspace/06-templates/intelligence.md +32 -0
  275. package/templates/workspace/06-templates/project-claude.md +48 -0
  276. package/templates/workspace/06-templates/session.md +38 -0
  277. package/templates/workspace/06-templates/task.md +42 -0
  278. package/templates/workspace/MANIFEST.json +51 -0
  279. package/templates/workspace/bridge/config/CONFIG.md +21 -0
  280. package/templates/workspace/bridge/config/runtime.json +4 -0
  281. package/templates/workspace/charter/core.md +104 -0
  282. package/templates/workspace/charter/policies/README.md +31 -0
  283. package/templates/workspace/charter/policies/claude-code-context.md +90 -0
  284. package/templates/workspace/charter/policies/concise-output.md +29 -0
  285. package/templates/workspace/charter/policies/context.md +91 -0
  286. package/templates/workspace/charter/policies/contracts/task.md +189 -0
  287. package/templates/workspace/charter/policies/git.md +60 -0
  288. package/templates/workspace/charter/policies/graph.md +47 -0
  289. package/templates/workspace/charter/policies/knowledge.md +33 -0
  290. package/templates/workspace/charter/policies/lifecycle.md +119 -0
  291. package/templates/workspace/charter/policies/memory.md +67 -0
  292. package/templates/workspace/charter/policies/minimal-change.md +43 -0
  293. package/templates/workspace/charter/policies/models.md +57 -0
  294. package/templates/workspace/charter/policies/privacy-terms.txt +3 -0
  295. package/templates/workspace/charter/policies/response.md +39 -0
  296. package/templates/workspace/charter/policies/skill-observer.md +44 -0
  297. package/templates/workspace/charter/policies/stack.md +23 -0
  298. package/templates/workspace/charter/policies/task.md +139 -0
  299. package/templates/workspace/charter/policies/verification.md +29 -0
  300. package/templates/workspace/sessions/.gitkeep +0 -0
@@ -0,0 +1,129 @@
1
+ import { execFile as execFileCallback } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import { redactRuntimeText } from "@ocean/core/domain/redaction/redaction.js";
4
+ import { oceanEnv, oceanEnvPair } from "@ocean/core/shared/paths.js";
5
+ const execFile = promisify(execFileCallback);
6
+ const SYSTEM_PROMPT = 'You are a terse session summarizer. Read the evidence and reply with ONLY a JSON object, no prose, no markdown fences: {"workLog":"one line, what happened","decision":"one line or omit","problem":"one line or omit","next":"one line or omit"}. Omit a key entirely if the evidence gives nothing for it. Never invent facts not present in the evidence.';
7
+ function safeText(value, max = 400) {
8
+ return redactRuntimeText(value).replace(/\s+/g, " ").trim().slice(0, max);
9
+ }
10
+ /** Cheap, cost-conscious skip: no point paying for a model call on a trivial session. */
11
+ export function isTrivialSession(events, changedFiles) {
12
+ if (changedFiles.length > 0)
13
+ return false;
14
+ return !events.some((event) => (event.type === "provider_output" || event.type === "text") &&
15
+ event.data.trim().length > 40);
16
+ }
17
+ function buildPrompt(input) {
18
+ const objective = input.events.find((event) => event.type === "user_input" || event.type === "resume_requested");
19
+ const outputs = input.events
20
+ .filter((event) => event.type === "provider_output" ||
21
+ event.type === "text" ||
22
+ event.type === "json")
23
+ .map((event) => safeText(event.data, 300))
24
+ .filter(Boolean)
25
+ .slice(0, 6);
26
+ const errors = input.events
27
+ .filter((event) => event.type === "error" || event.type === "provider_blocked")
28
+ .map((event) => safeText(event.data, 300))
29
+ .slice(0, 3);
30
+ const lines = [
31
+ `Objective: ${safeText(objective?.data ?? input.session.title, 200)}`,
32
+ `Changed files: ${input.changedFiles.length ? input.changedFiles.slice(0, 20).join("; ") : "none"}`,
33
+ outputs.length
34
+ ? `Conversation excerpts:\n${outputs.map((line) => `- ${line}`).join("\n")}`
35
+ : "Conversation excerpts: none",
36
+ errors.length
37
+ ? `Errors/blocks:\n${errors.map((line) => `- ${line}`).join("\n")}`
38
+ : "",
39
+ input.nextAction
40
+ ? `Stated next action: ${safeText(input.nextAction, 200)}`
41
+ : "",
42
+ ];
43
+ return lines.filter(Boolean).join("\n");
44
+ }
45
+ export function parseResult(stdout) {
46
+ // --output-format json prints one JSON object, but non-interactive terminal
47
+ // control sequences (e.g. a trailing cursor-show code) can follow it on
48
+ // stdout even when not attached to a TTY — trim to the outermost braces
49
+ // before parsing rather than parsing the whole stream verbatim.
50
+ const start = stdout.indexOf("{");
51
+ const end = stdout.lastIndexOf("}");
52
+ if (start < 0 || end < start)
53
+ return null;
54
+ let envelope;
55
+ try {
56
+ envelope = JSON.parse(stdout.slice(start, end + 1));
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ if (typeof envelope.result !== "string")
62
+ return null;
63
+ const text = envelope.result
64
+ .trim()
65
+ .replace(/^```(?:json)?\s*/i, "")
66
+ .replace(/```\s*$/, "")
67
+ .trim();
68
+ let parsed;
69
+ try {
70
+ parsed = JSON.parse(text);
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ if (!parsed || typeof parsed !== "object")
76
+ return null;
77
+ const value = parsed;
78
+ const narrative = {};
79
+ for (const key of ["workLog", "decision", "problem", "next"]) {
80
+ if (typeof value[key] === "string" && value[key].trim())
81
+ narrative[key] = safeText(value[key], 300);
82
+ }
83
+ return Object.keys(narrative).length ? narrative : null;
84
+ }
85
+ /**
86
+ * Best-effort, cost-conscious human narrative via a cheap headless model call.
87
+ * Returns null on any failure (auth, timeout, parse error) so the caller falls
88
+ * back to the deterministic heuristic in daily-narrative.ts. Never throws.
89
+ *
90
+ * Guarded against recursion: the spawned call carries OCEAN_NARRATIVE_CALL=1,
91
+ * and session-closeout.ts skips calling this again when that flag is already
92
+ * set on the current process (i.e. this IS a narrative-generation session).
93
+ */
94
+ export async function generateModelNarrative(input) {
95
+ // Opt-in only: unset (tests, CI, a fresh install) means no model call — no
96
+ // network, no auth requirement, no cost, fully deterministic by default.
97
+ if (oceanEnv("MODEL_NARRATIVE") !== "1")
98
+ return null;
99
+ if (oceanEnv("NARRATIVE_CALL") === "1")
100
+ return null;
101
+ if (isTrivialSession(input.events, input.changedFiles))
102
+ return null;
103
+ const prompt = buildPrompt(input);
104
+ try {
105
+ const result = await execFile("claude", [
106
+ "-p",
107
+ prompt,
108
+ "--model",
109
+ "haiku",
110
+ "--output-format",
111
+ "json",
112
+ "--system-prompt",
113
+ SYSTEM_PROMPT,
114
+ "--allowedTools",
115
+ "",
116
+ "--disallowedTools",
117
+ "Bash,Edit,Write,Read,Glob,Grep,NotebookEdit,WebFetch,WebSearch",
118
+ ], {
119
+ timeout: 30_000,
120
+ maxBuffer: 65_536,
121
+ env: { ...process.env, ...oceanEnvPair("NARRATIVE_CALL", "1") },
122
+ });
123
+ return parseResult(result.stdout);
124
+ }
125
+ catch {
126
+ return null;
127
+ }
128
+ }
129
+ //# sourceMappingURL=model-narrative.js.map
@@ -0,0 +1,103 @@
1
+ import { mkdir, readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { assertProfileName } from "@ocean/core/domain/profiles/profile.js";
4
+ import { atomicWrite, truncateUtf8, withFileLock, } from "@ocean/core/shared/fs-utils.js";
5
+ import { oceanPath, resolveWithin, SYSTEM_DIR, } from "@ocean/core/shared/paths.js";
6
+ const maxFacts = 100;
7
+ const maxFactBytes = 2_000;
8
+ // The profile name is validated and the path is bounds-checked (including symlinks), so one
9
+ // profile's namespace can never resolve to another file inside or outside the memory root.
10
+ export function profileFactsFile(profile) {
11
+ return resolveWithin(oceanPath(SYSTEM_DIR, "memory", "profiles"), `${assertProfileName(profile)}.json`);
12
+ }
13
+ export async function readProfileFacts(profile) {
14
+ try {
15
+ const facts = JSON.parse(await readFile(profileFactsFile(profile), "utf8"));
16
+ return Array.isArray(facts) ? facts.slice(0, maxFacts) : [];
17
+ }
18
+ catch (error) {
19
+ if (error.code === "ENOENT")
20
+ return [];
21
+ throw error;
22
+ }
23
+ }
24
+ // Read-modify-write under the file lock, replaced atomically: concurrent writers to the same
25
+ // profile each see the other's facts, and a crash never leaves a truncated file. `change`
26
+ // returns null to skip the write.
27
+ async function updateProfileFacts(profile, change) {
28
+ const file = profileFactsFile(profile);
29
+ await mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
30
+ await withFileLock(file, async () => {
31
+ const next = change(await readProfileFacts(profile));
32
+ if (next)
33
+ await atomicWrite(file, `${JSON.stringify(next.slice(0, maxFacts), null, 2)}\n`, 0o600);
34
+ });
35
+ }
36
+ export async function writeProfileFact(profile, key, value) {
37
+ if (!/^[a-zA-Z0-9._-]+$/.test(key))
38
+ throw new Error("Fact key must contain only letters, numbers, dot, underscore, or hyphen");
39
+ if (!value.trim() || Buffer.byteLength(value) > maxFactBytes)
40
+ throw new Error("Fact value is empty or exceeds 2000 bytes");
41
+ const fact = {
42
+ key,
43
+ value: value.trim(),
44
+ updatedAt: new Date().toISOString(),
45
+ };
46
+ await updateProfileFacts(profile, (facts) => [
47
+ fact,
48
+ ...facts.filter((item) => item.key !== key),
49
+ ]);
50
+ return fact;
51
+ }
52
+ export async function deleteProfileFact(profile, key) {
53
+ let deleted = false;
54
+ await updateProfileFacts(profile, (facts) => {
55
+ const remaining = facts.filter((fact) => fact.key !== key);
56
+ deleted = remaining.length !== facts.length;
57
+ return deleted ? remaining : null;
58
+ });
59
+ return deleted;
60
+ }
61
+ export const PROFILE_FACTS_DIGEST_MAX_BYTES = 2_048;
62
+ // A bounded digest of the profile's durable facts for a headless prompt: newest first (by
63
+ // updatedAt, since hand-edited files may not be ordered), whole `- key: value` lines while
64
+ // they fit, and always a pointer to the full set. The whole section stays <= maxBytes; if
65
+ // not even the newest fact fits, it is byte-truncated so at least one fact is visible.
66
+ // `partial` says whether it left anything out (a dropped or byte-truncated fact), in which
67
+ // case the provider must be able to follow the pointer to the store file.
68
+ export function buildProfileFactsDigest(profile, facts, maxBytes = PROFILE_FACTS_DIGEST_MAX_BYTES) {
69
+ if (!facts.length)
70
+ return { text: "", partial: false };
71
+ const time = (fact) => {
72
+ const value = Date.parse(fact.updatedAt);
73
+ return Number.isNaN(value) ? Number.NEGATIVE_INFINITY : value;
74
+ };
75
+ const ordered = [...facts].sort((a, b) => time(b) - time(a));
76
+ const heading = "## Durable profile facts";
77
+ const pointer = (shown) => `(${shown} of ${facts.length} facts, newest first; full set: ocean memory facts ${profile} or ${profileFactsFile(profile)})`;
78
+ // Reserve the pointer's worst case (shown === total) so its size never grows past the budget.
79
+ const reserved = Buffer.byteLength(pointer(facts.length)) + 1;
80
+ const lines = [];
81
+ let used = Buffer.byteLength(heading);
82
+ let whole = 0;
83
+ for (const fact of ordered) {
84
+ const line = `- ${fact.key}: ${String(fact.value).replace(/\s+/g, " ").trim()}`;
85
+ const cost = Buffer.byteLength(line) + 1;
86
+ if (used + cost + reserved <= maxBytes) {
87
+ lines.push(line);
88
+ used += cost;
89
+ whole += 1;
90
+ continue;
91
+ }
92
+ if (!lines.length) {
93
+ const room = maxBytes - used - reserved - 1 - Buffer.byteLength("...");
94
+ lines.push(`${truncateUtf8(line, Math.max(0, room))}...`);
95
+ }
96
+ break;
97
+ }
98
+ return {
99
+ text: [heading, ...lines, pointer(lines.length)].join("\n"),
100
+ partial: whole < facts.length,
101
+ };
102
+ }
103
+ //# sourceMappingURL=profile-facts.js.map
@@ -0,0 +1,61 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { redactRuntimeText } from "@ocean/core/domain/redaction/redaction.js";
4
+ import { KNOWLEDGE_DIR, oceanPath } from "@ocean/core/shared/paths.js";
5
+ import { syncMemoryIndexes } from "./index-sync.js";
6
+ const kinds = new Set([
7
+ "architecture",
8
+ "decisions",
9
+ "discoveries",
10
+ "failures",
11
+ "research",
12
+ "results",
13
+ "solutions",
14
+ "references",
15
+ ]);
16
+ export async function promoteSessionToKnowledge(sessionId, openStore, target = "knowledge/results", approved = false) {
17
+ if (!approved)
18
+ throw new Error("Session promotion requires explicit approval: use --approve or approved: true");
19
+ const match = /^knowledge\/(\w+)$/.exec(target);
20
+ if (!match || !kinds.has(match[1]))
21
+ throw new Error("Target must be knowledge/<architecture|decisions|discoveries|failures|research|results|solutions|references>");
22
+ const store = await openStore();
23
+ try {
24
+ const session = store.get(sessionId);
25
+ if (!session)
26
+ throw new Error(`Session not found: ${sessionId}`);
27
+ if (session.status !== "completed")
28
+ throw new Error(`Only completed sessions can be promoted: ${session.status}`);
29
+ const events = store.listEvents(sessionId);
30
+ const evidence = events
31
+ .filter((event) => event.type === "evidence")
32
+ .map((event) => event.data)
33
+ .some((data) => /"result"\s*:\s*"proven"/.test(data));
34
+ if (!evidence)
35
+ throw new Error("Session lacks independently recorded successful evidence");
36
+ const input = events.find((event) => event.type === "user_input")?.data ?? "";
37
+ const outputs = events
38
+ .filter((event) => event.type === "provider_output" ||
39
+ event.type === "text" ||
40
+ event.type === "json")
41
+ .map((event) => redactRuntimeText(event.data).trim())
42
+ .filter(Boolean);
43
+ if (!outputs.length)
44
+ throw new Error("Session has no provider output to promote");
45
+ const slug = `${session.provider}-${sessionId.slice(0, 8)}`.replace(/[^a-zA-Z0-9-]+/g, "-");
46
+ const file = path.join(oceanPath(KNOWLEDGE_DIR, match[1], `${slug}.md`));
47
+ await mkdir(path.dirname(file), { recursive: true });
48
+ await writeFile(file, `---\nname: ${slug}\ndescription: Human-approved result from an Ocean session\nmetadata:\n type: session-result\n domain: ${match[1]}\n status: current\n verification: human-approved\n source-session: ${sessionId}\n provider: ${session.provider}\n---\n\n# ${session.provider} review\n\n## Request\n\n${redactRuntimeText(input)}\n\n## Result\n\n${outputs.join("\n\n")}\n`);
49
+ store.appendEvent(sessionId, "knowledge_promoted", JSON.stringify({
50
+ file: path.relative(oceanPath(KNOWLEDGE_DIR), file),
51
+ target,
52
+ approved: true,
53
+ }));
54
+ await syncMemoryIndexes(true);
55
+ return { applied: true, sessionId, file: path.relative(oceanPath(), file) };
56
+ }
57
+ finally {
58
+ store.close();
59
+ }
60
+ }
61
+ //# sourceMappingURL=session-promotion.js.map
@@ -0,0 +1,113 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { redactRuntimeText } from "@ocean/core/domain/redaction/redaction.js";
5
+ import { safeJsonParse } from "@ocean/core/shared/fs-utils.js";
6
+ import { localDate, localMoment } from "@ocean/core/shared/local-date.js";
7
+ import { oceanPath, oceanRoot, SYSTEM_DIR } from "@ocean/core/shared/paths.js";
8
+ const maxSummaryBytes = 12_000;
9
+ function safeText(value, max = 800) {
10
+ return redactRuntimeText(value).replace(/\s+/g, " ").trim().slice(0, max);
11
+ }
12
+ function eventText(events, types, maxItems = 4) {
13
+ return events
14
+ .filter((event) => types.includes(event.type))
15
+ .map((event) => safeText(event.data))
16
+ .filter(Boolean)
17
+ .slice(0, maxItems);
18
+ }
19
+ function lastEvent(events, type) {
20
+ return [...events].reverse().find((event) => event.type === type);
21
+ }
22
+ function renderSessionSummary(input) {
23
+ const { session, events } = input;
24
+ const request = eventText(events, ["user_input", "resume_requested"], 1)[0] ??
25
+ session.title;
26
+ const outputs = eventText(events, ["provider_output", "text", "json"], 4);
27
+ const errors = eventText(events, ["error", "provider_blocked"], 4);
28
+ const evidence = events
29
+ .filter((event) => event.type === "evidence")
30
+ .map((event) => safeJsonParse(event.data))
31
+ .filter((value) => value !== null);
32
+ const proven = evidence
33
+ .filter((value) => value.result === "proven")
34
+ .map((value) => String(value.criterion ?? "verified result"));
35
+ const notProven = evidence
36
+ .filter((value) => value.result !== "proven")
37
+ .map((value) => String(value.criterion ?? value.result ?? "not proven"));
38
+ const processExit = lastEvent(events, "process_exit");
39
+ const exit = input.exitCode ??
40
+ (processExit ? safeJsonParse(processExit.data)?.exitCode : undefined);
41
+ const status = session.status === "completed" && exit === 0 ? "completed" : session.status;
42
+ const nextAction = input.nextAction ??
43
+ session.nextAction ??
44
+ "Review the summary and verify the next action.";
45
+ const lines = [
46
+ "# Session Summary",
47
+ "",
48
+ `- Session: ${session.sessionId}`,
49
+ `- Provider: ${session.provider}`,
50
+ `- Task: ${session.taskId ?? "none"}`,
51
+ `- Profile: ${session.profile}`,
52
+ `- Status: ${status}`,
53
+ `- Working directory: ${safeText(session.workingDirectory, 240)}`,
54
+ "",
55
+ "## Objective",
56
+ "",
57
+ request || "No user objective was captured.",
58
+ "",
59
+ "## What happened",
60
+ "",
61
+ ...(outputs.length
62
+ ? outputs.map((value) => `- ${value}`)
63
+ : ["- No provider output was captured."]),
64
+ "",
65
+ "## Files changed",
66
+ "",
67
+ ...(input.changedFiles?.length
68
+ ? input.changedFiles.map((value) => `- ${safeText(value, 240)}`)
69
+ : ["- No changed-file evidence was recorded."]),
70
+ "",
71
+ "## Verification",
72
+ "",
73
+ ...(proven.length
74
+ ? proven.map((value) => `- PROVEN: ${safeText(value)}`)
75
+ : ["- No independently proven verification recorded."]),
76
+ ...(notProven.length
77
+ ? notProven.map((value) => `- NOT PROVEN: ${safeText(value)}`)
78
+ : []),
79
+ "",
80
+ "## Problems and limitations",
81
+ "",
82
+ ...(errors.length
83
+ ? errors.map((value) => `- ${value}`)
84
+ : ["- None recorded."]),
85
+ "",
86
+ "## Next action",
87
+ "",
88
+ safeText(nextAction, 800),
89
+ "",
90
+ "## Retrieval",
91
+ "",
92
+ `- Detailed bounded events: ocean session events ${session.sessionId}`,
93
+ `- Context bytes: ${session.contextBytes}`,
94
+ ];
95
+ let content = `${lines.join("\n")}\n`;
96
+ if (Buffer.byteLength(content) > maxSummaryBytes)
97
+ content = `${content.slice(0, maxSummaryBytes - 120)}\n\n- Summary truncated; detailed bounded events remain available by session id.\n`;
98
+ return content;
99
+ }
100
+ export async function writeSessionSummary(input) {
101
+ const date = localDate(localMoment(input.session.createdAt));
102
+ const directory = oceanPath(SYSTEM_DIR, "sessions", "summaries");
103
+ const file = path.join(directory, `${date}-${input.session.sessionId}.md`);
104
+ await mkdir(directory, { recursive: true });
105
+ const content = renderSessionSummary(input);
106
+ await writeFile(file, content, "utf8");
107
+ return {
108
+ summaryPath: path.relative(oceanRoot(), file).split(path.sep).join("/"),
109
+ summaryHash: createHash("sha256").update(content).digest("hex"),
110
+ summaryBytes: Buffer.byteLength(content),
111
+ };
112
+ }
113
+ //# sourceMappingURL=session-summary.js.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@ocean/brain",
3
+ "version": "1.2.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "exports": {
7
+ "./*.js": {
8
+ "import": "./dist/*.js"
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,66 @@
1
+ import { createHash } from "node:crypto";
2
+ const REQUIRED = /changed files?|verification|security|approval|error|failed|next action/i;
3
+ function boundedByBytes(value, budget) {
4
+ return Buffer.from(value).subarray(0, budget).toString("utf8");
5
+ }
6
+ function unique(lines) {
7
+ return [...new Set(lines.map((line) => line.trim()).filter(Boolean))];
8
+ }
9
+ export function compressContext(input) {
10
+ const originalBytes = Buffer.byteLength(input.content);
11
+ const sourceHash = createHash("sha256").update(input.content).digest("hex");
12
+ if (input.budget <= 0)
13
+ throw new Error("Compression budget must be positive");
14
+ if (originalBytes <= input.budget) {
15
+ return {
16
+ content: input.content,
17
+ sourceId: input.sourceId,
18
+ sourceHash,
19
+ originalBytes,
20
+ compressedBytes: originalBytes,
21
+ method: "none",
22
+ budget: input.budget,
23
+ omittedSections: [],
24
+ recoveryRef: input.sourceId,
25
+ safeToUse: true,
26
+ };
27
+ }
28
+ const lines = input.content.split(/\r?\n/);
29
+ const required = unique(lines.filter((line) => REQUIRED.test(line)));
30
+ const selected = unique([lines[0] ?? "", ...required, lines.at(-1) ?? ""]);
31
+ const content = selected.join("\n");
32
+ const compressedBytes = Buffer.byteLength(content);
33
+ const omittedSections = lines
34
+ .filter((line) => line.trim() && !selected.includes(line.trim()))
35
+ .slice(0, 20)
36
+ .map((line) => line.trim().slice(0, 120));
37
+ if (compressedBytes <= input.budget &&
38
+ required.every((line) => content.includes(line.trim()))) {
39
+ return {
40
+ content,
41
+ sourceId: input.sourceId,
42
+ sourceHash,
43
+ originalBytes,
44
+ compressedBytes,
45
+ method: "ocean-bounded-v1",
46
+ budget: input.budget,
47
+ omittedSections,
48
+ recoveryRef: input.sourceId,
49
+ safeToUse: true,
50
+ };
51
+ }
52
+ const fallback = boundedByBytes(input.content, input.budget);
53
+ return {
54
+ content: fallback,
55
+ sourceId: input.sourceId,
56
+ sourceHash,
57
+ originalBytes,
58
+ compressedBytes: Buffer.byteLength(fallback),
59
+ method: "fallback-original-bounded",
60
+ budget: input.budget,
61
+ omittedSections,
62
+ recoveryRef: input.sourceId,
63
+ safeToUse: false,
64
+ };
65
+ }
66
+ //# sourceMappingURL=context-compression.js.map