@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,363 @@
1
+ import { readdir, stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { BrainIndexStaleError, brainSearch, } from "@ocean/brain/brain/brain-service.js";
4
+ import { normalizeScope, scopeVisible, } from "@ocean/core/domain/brain/record-scope.js";
5
+ import { parseTaskId } from "@ocean/core/domain/tasks/task-id.js";
6
+ import { readFrontmatterFile } from "@ocean/core/shared/fs-utils.js";
7
+ import { KNOWLEDGE_DIR, oceanRoot, PERSONAL_DIR, projectDir, resolveStorePath, resolveWithin, storeRelativeToRoot, WORKSPACE_PROJECT_ID, } from "@ocean/core/shared/paths.js";
8
+ import { projectRules, readProjectManifest, } from "@ocean/core/shared/project-resolution.js";
9
+ import { resolveTaskReference } from "@ocean/core/shared/task-reference.js";
10
+ // Hard ceilings: a caller cannot request an arbitrarily large budget and call it "bounded".
11
+ // These are generous enough for a single compact read, never for a repository scan.
12
+ const BUDGET_CEILING = {
13
+ maxFiles: 20,
14
+ maxBytes: 200_000,
15
+ maxChars: 50_000,
16
+ maxOperationCost: 10,
17
+ };
18
+ function isBoundedPositiveInteger(value, ceiling) {
19
+ return (typeof value === "number" &&
20
+ Number.isInteger(value) &&
21
+ Number.isFinite(value) &&
22
+ value > 0 &&
23
+ value <= ceiling);
24
+ }
25
+ // Rejects missing, non-numeric, negative, zero, NaN, Infinity, non-integer, and
26
+ // oversized-beyond-ceiling budgets. A budget that passes this check is never itself the
27
+ // reason a read becomes unbounded.
28
+ export function validateBudget(budget) {
29
+ if (!budget || typeof budget !== "object")
30
+ return { valid: false, reason: "budget is missing or not an object" };
31
+ const candidate = budget;
32
+ const fields = [
33
+ "maxFiles",
34
+ "maxBytes",
35
+ "maxChars",
36
+ "maxOperationCost",
37
+ ];
38
+ for (const field of fields) {
39
+ const value = candidate[field];
40
+ if (value === undefined)
41
+ return { valid: false, reason: `budget.${field} is missing` };
42
+ if (!isBoundedPositiveInteger(value, BUDGET_CEILING[field])) {
43
+ return {
44
+ valid: false,
45
+ reason: `budget.${field} must be a finite positive integer no greater than ${BUDGET_CEILING[field]} (got ${String(value)})`,
46
+ };
47
+ }
48
+ }
49
+ return { valid: true };
50
+ }
51
+ const READ_RUNG_COST = {
52
+ identity: 0,
53
+ "project-metadata": 1,
54
+ "ranked-references": 3,
55
+ "exact-record": 1,
56
+ };
57
+ // Fail-closed: any low confidence, any ambiguity reason, or the "unknown" intent stays at
58
+ // "identity" — no project metadata, no reference ranking, no record fetch is attempted.
59
+ export function resolveLadderRung(classification) {
60
+ if (classification.intent === "unknown" ||
61
+ classification.confidence === "low" ||
62
+ classification.ambiguityReason) {
63
+ return {
64
+ rung: "identity",
65
+ reason: "low confidence, ambiguous, or unknown intent — failing closed at identity, no read attempted",
66
+ };
67
+ }
68
+ if (classification.intent === "project-detect") {
69
+ return {
70
+ rung: "project-metadata",
71
+ reason: "project-detect intent resolves to project metadata only",
72
+ };
73
+ }
74
+ if (classification.intent === "task-lookup" && classification.identifier) {
75
+ return {
76
+ rung: "exact-record",
77
+ reason: "task-lookup with an explicit, validated identifier resolves to an exact record",
78
+ };
79
+ }
80
+ if (classification.intent === "memory-lookup" ||
81
+ classification.intent === "knowledge-lookup" ||
82
+ classification.intent === "decision-lookup") {
83
+ return {
84
+ rung: "ranked-references",
85
+ reason: `${classification.intent} has no explicit identifier — resolves to ranked references, not a full record`,
86
+ };
87
+ }
88
+ return {
89
+ rung: "identity",
90
+ reason: `intent '${classification.intent}' is a write/execution/create request, not a read — the context ladder does not escalate past identity for it`,
91
+ };
92
+ }
93
+ function budgetRejection(rung, reason, violation = "invalid-budget") {
94
+ return {
95
+ rung,
96
+ allowed: false,
97
+ reason,
98
+ violation,
99
+ files: [],
100
+ bytes: 0,
101
+ truncated: false,
102
+ };
103
+ }
104
+ function withinCharBudget(result, budget, rung) {
105
+ const serializedChars = JSON.stringify(result).length;
106
+ if (serializedChars > budget.maxChars) {
107
+ return budgetRejection(rung, `context result is ${serializedChars} chars, budget allows ${budget.maxChars}`, "max-chars-exceeded");
108
+ }
109
+ return { ...result, allowed: true, violation: null };
110
+ }
111
+ // Only ever resolves a single, already-validated task id to its task.md path, and only
112
+ // ever stats it (size), never reads its content — the actual compact read is a later slice.
113
+ async function planExactTaskRecord(identifier, budget, root = oceanRoot(), projectId = WORKSPACE_PROJECT_ID,
114
+ // Only a bound project's rules are shown: the workspace fallback above finds tasks, not rules.
115
+ rulesProject = null) {
116
+ const rung = "exact-record";
117
+ const parsed = parseTaskId(identifier);
118
+ if (!parsed) {
119
+ return budgetRejection(rung, `'${identifier}' is not a valid task identifier shape (expected <PREFIX>-<digits>) — refusing to guess a path`, null);
120
+ }
121
+ const cost = READ_RUNG_COST[rung];
122
+ if (cost > budget.maxOperationCost) {
123
+ return budgetRejection(rung, `exact-record operation cost ${cost} exceeds budget.maxOperationCost ${budget.maxOperationCost}`, "max-operation-cost-exceeded");
124
+ }
125
+ if (1 > budget.maxFiles) {
126
+ return budgetRejection(rung, `a single task record requires 1 file, budget.maxFiles is ${budget.maxFiles}`, "max-files-exceeded");
127
+ }
128
+ const normalized = `${parsed.prefix}${identifier.slice(identifier.indexOf("-"))}`;
129
+ // Stays inside this project: the ladder never reads another project's task.
130
+ let resolvedPath;
131
+ try {
132
+ resolvedPath = (await resolveTaskReference(identifier, {
133
+ projectId,
134
+ acrossProjects: false,
135
+ root,
136
+ })).file;
137
+ }
138
+ catch {
139
+ return budgetRejection(rung, `task ${normalized} was not found under the Ocean task root`, null);
140
+ }
141
+ let size;
142
+ try {
143
+ size = (await stat(resolvedPath)).size;
144
+ }
145
+ catch {
146
+ return budgetRejection(rung, `task ${normalized} was not found under the Ocean task root`, null);
147
+ }
148
+ if (size > budget.maxBytes) {
149
+ return budgetRejection(rung, `task ${normalized} is ${size} bytes, budget.maxBytes allows ${budget.maxBytes} — refusing to silently truncate`, "max-bytes-exceeded");
150
+ }
151
+ const relative = path.relative(root, resolvedPath).split(path.sep).join("/");
152
+ // Fail closed: a manifest that does not parse rejects the read rather than dropping rules.
153
+ let rules = [];
154
+ try {
155
+ const manifest = rulesProject
156
+ ? await readProjectManifest(rulesProject, root)
157
+ : null;
158
+ rules = manifest ? projectRules(manifest) : [];
159
+ }
160
+ catch (error) {
161
+ return budgetRejection(rung, `project ${rulesProject} rules are unreadable: ${error instanceof Error ? error.message : String(error)}`, null);
162
+ }
163
+ return withinCharBudget({
164
+ rung,
165
+ files: [relative],
166
+ ...(rulesProject && rules.length
167
+ ? { rules: { project: rulesProject, rules } }
168
+ : {}),
169
+ bytes: size,
170
+ truncated: false,
171
+ reason: `task ${normalized} is within budget (${size}/${budget.maxBytes} bytes)`,
172
+ }, budget, rung);
173
+ }
174
+ function planProjectMetadata(budget) {
175
+ const rung = "project-metadata";
176
+ const cost = READ_RUNG_COST[rung];
177
+ if (cost > budget.maxOperationCost) {
178
+ return budgetRejection(rung, `project-metadata operation cost ${cost} exceeds budget.maxOperationCost ${budget.maxOperationCost}`, "max-operation-cost-exceeded");
179
+ }
180
+ return withinCharBudget({
181
+ rung,
182
+ files: [],
183
+ bytes: 0,
184
+ truncated: false,
185
+ reason: "project metadata resolution is deferred to the existing project resolver; no file read performed by the ladder itself",
186
+ }, budget, rung);
187
+ }
188
+ // When a query is supplied and the brain index has been built, ranking is delegated to it
189
+ // (T-228) instead of returning the fixed authoritative-index files below. Each candidate
190
+ // path is still stat'd here (never silently truncated) before it's allowed further down the
191
+ // ladder. A query with no index built falls back to the authoritative files, with a reason
192
+ // that says so rather than silently behaving as if no query had been given.
193
+ async function planRankedReferencesViaIndex(query, budget, root, indexPort, projectId, leading, notes) {
194
+ const rung = "ranked-references";
195
+ let result;
196
+ try {
197
+ result = await brainSearch({
198
+ query,
199
+ root,
200
+ limit: budget.maxFiles,
201
+ scope: { project: projectId },
202
+ indexPort,
203
+ });
204
+ }
205
+ catch (error) {
206
+ if (error instanceof BrainIndexStaleError)
207
+ notes.push(error.message);
208
+ return null;
209
+ }
210
+ const files = [];
211
+ let bytes = 0;
212
+ // MEMORY.md files are not in the index; they lead, then the ranked hits (deduped).
213
+ const entries = [
214
+ ...leading.map((relative) => ({
215
+ relative,
216
+ absolute: resolveWithin(root, relative),
217
+ })),
218
+ ...result.results.map((hit) => ({
219
+ relative: storeRelativeToRoot(hit.path, root),
220
+ absolute: resolveStorePath(root, hit.path),
221
+ })),
222
+ ];
223
+ for (const entry of entries) {
224
+ if (files.length >= budget.maxFiles)
225
+ break;
226
+ if (files.includes(entry.relative))
227
+ continue;
228
+ try {
229
+ const size = (await stat(entry.absolute)).size;
230
+ if (bytes + size > budget.maxBytes)
231
+ break;
232
+ files.push(entry.relative);
233
+ bytes += size;
234
+ }
235
+ catch {
236
+ // A hit whose file vanished since the index was built is skipped, not fatal.
237
+ }
238
+ }
239
+ return withinCharBudget({
240
+ rung,
241
+ files,
242
+ bytes,
243
+ truncated: false,
244
+ reason: files.length
245
+ ? `ranked by brain index (${result.mode})`
246
+ : `brain index returned no results for query '${query}'`,
247
+ }, budget, rung);
248
+ }
249
+ // 02-personal/MEMORY.md, then the bound project's MEMORY.md when it exists. An unbound
250
+ // session (null) gets the personal file only.
251
+ async function memoryFiles(root, projectId) {
252
+ const files = [path.posix.join(PERSONAL_DIR, "MEMORY.md")];
253
+ if (projectId) {
254
+ const relative = path.posix.join(projectDir(projectId, root), "MEMORY.md");
255
+ try {
256
+ if ((await stat(resolveWithin(root, relative))).isFile())
257
+ files.push(relative);
258
+ }
259
+ catch {
260
+ // No project MEMORY.md is normal.
261
+ }
262
+ }
263
+ return files;
264
+ }
265
+ async function planRankedReferences(intent, budget, root, projectId, query, indexPort) {
266
+ const rung = "ranked-references";
267
+ const cost = READ_RUNG_COST[rung];
268
+ if (cost > budget.maxOperationCost) {
269
+ return budgetRejection(rung, `ranked-references operation cost ${cost} exceeds budget.maxOperationCost ${budget.maxOperationCost}`, "max-operation-cost-exceeded");
270
+ }
271
+ const memoryLookup = intent === "memory-lookup";
272
+ const indexNotes = [];
273
+ if (query && indexPort) {
274
+ const viaIndex = await planRankedReferencesViaIndex(query, budget, root, indexPort, projectId, memoryLookup ? await memoryFiles(root, projectId) : [], indexNotes);
275
+ if (viaIndex)
276
+ return viaIndex;
277
+ // No index built: fall through to the fixed authoritative files below, but say why.
278
+ }
279
+ const candidates = [];
280
+ if (memoryLookup)
281
+ candidates.push(...(await memoryFiles(root, projectId)));
282
+ if (intent === "work-style-lookup")
283
+ candidates.push(path.posix.join(PERSONAL_DIR, "MEMORY.md"));
284
+ if (intent === "knowledge-lookup")
285
+ candidates.push(path.posix.join(KNOWLEDGE_DIR, "KNOWLEDGE.md"));
286
+ if (intent === "decision-lookup") {
287
+ const directory = path.join(root, KNOWLEDGE_DIR, "decisions");
288
+ try {
289
+ for (const entry of (await readdir(directory, { withFileTypes: true }))
290
+ .filter((item) => item.isFile() && item.name.endsWith(".md"))
291
+ .sort((a, b) => a.name.localeCompare(b.name))) {
292
+ const relative = path.posix.join(KNOWLEDGE_DIR, "decisions", entry.name);
293
+ // Scope is read from the file itself: no index here to say whose record it is.
294
+ const meta = await readFrontmatterFile(path.join(directory, entry.name), 4096);
295
+ if (meta &&
296
+ !scopeVisible(normalizeScope(meta.fields.scope).scope, projectId))
297
+ continue;
298
+ candidates.push(relative);
299
+ }
300
+ }
301
+ catch (error) {
302
+ if (error.code !== "ENOENT")
303
+ throw error;
304
+ }
305
+ }
306
+ const files = [];
307
+ let bytes = 0;
308
+ for (const relative of candidates) {
309
+ if (files.length >= budget.maxFiles)
310
+ break;
311
+ try {
312
+ const size = (await stat(resolveWithin(root, relative))).size;
313
+ if (bytes + size > budget.maxBytes)
314
+ break;
315
+ files.push(relative);
316
+ bytes += size;
317
+ }
318
+ catch (error) {
319
+ if (error.code !== "ENOENT")
320
+ throw error;
321
+ }
322
+ }
323
+ const queryNote = !query
324
+ ? ""
325
+ : indexNotes.length
326
+ ? ` (${indexNotes[0]} — falling back to authoritative files)`
327
+ : " (brain index not built — falling back to authoritative files)";
328
+ return withinCharBudget({
329
+ rung,
330
+ files,
331
+ bytes,
332
+ truncated: false,
333
+ reason: files.length
334
+ ? `selected ${files.length} authoritative '${intent}' reference(s) within budget${queryNote}`
335
+ : `no authoritative '${intent}' references were available within budget${queryNote}`,
336
+ }, budget, rung);
337
+ }
338
+ // Top-level entry point reused across CLI/shim/hook/MCP callers: resolves the ladder rung
339
+ // from a slice-4 classification, validates the budget before touching anything, and stops
340
+ // immediately at the first violation with a clear, non-silent reason.
341
+ export async function planContextRead(classification, budget, root = oceanRoot(), projectId = WORKSPACE_PROJECT_ID, options) {
342
+ const { rung, reason } = resolveLadderRung(classification);
343
+ const validation = validateBudget(budget);
344
+ if (!validation.valid)
345
+ return budgetRejection(rung, validation.reason, "invalid-budget");
346
+ const bounded = budget;
347
+ if (rung === "identity")
348
+ return {
349
+ rung,
350
+ allowed: true,
351
+ reason,
352
+ violation: null,
353
+ files: [],
354
+ bytes: 0,
355
+ truncated: false,
356
+ };
357
+ if (rung === "exact-record")
358
+ return planExactTaskRecord(classification.identifier ?? "", bounded, root, projectId ?? WORKSPACE_PROJECT_ID, projectId);
359
+ if (rung === "project-metadata")
360
+ return planProjectMetadata(bounded);
361
+ return planRankedReferences(classification.intent, bounded, root, projectId, options?.query, options?.indexPort);
362
+ }
363
+ //# sourceMappingURL=context-ladder.js.map
@@ -0,0 +1,213 @@
1
+ import { stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { oceanRoot, resolveWithin } from "@ocean/core/shared/paths.js";
4
+ import { resolveProject, } from "@ocean/core/shared/project-resolution.js";
5
+ import { planContextRead, resolveLadderRung, validateBudget, } from "./context-ladder.js";
6
+ // A file older than this is "stale", not "current" — a fixed, deterministic threshold, not
7
+ // a guess. No file read at all (nothing selected) is reported as "unknown", never "current".
8
+ const STALE_AFTER_MS = 30 * 24 * 60 * 60 * 1000;
9
+ // Hard ceiling on the packet's own serialized size, independent of the caller-supplied
10
+ // budget — a pathological input can never make the packet itself unbounded.
11
+ const PACKET_CHAR_BOUND = 8_000;
12
+ function sanitizeBudget(budget) {
13
+ const source = budget && typeof budget === "object"
14
+ ? budget
15
+ : {};
16
+ const pick = (key) => {
17
+ const value = source[key];
18
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
19
+ };
20
+ return {
21
+ maxFiles: pick("maxFiles"),
22
+ maxBytes: pick("maxBytes"),
23
+ maxChars: pick("maxChars"),
24
+ maxOperationCost: pick("maxOperationCost"),
25
+ };
26
+ }
27
+ function activeProjectSummary(resolution) {
28
+ if (resolution.status === "bound")
29
+ return {
30
+ status: "bound",
31
+ projectId: resolution.projectId,
32
+ confidence: resolution.confidence,
33
+ };
34
+ if (resolution.status === "ambiguous")
35
+ return {
36
+ status: "ambiguous",
37
+ projectId: null,
38
+ confidence: resolution.confidence,
39
+ };
40
+ return {
41
+ status: "unbound",
42
+ projectId: null,
43
+ confidence: resolution.confidence,
44
+ };
45
+ }
46
+ function recordTypeForIntent(intent) {
47
+ switch (intent) {
48
+ case "task-lookup":
49
+ return "task";
50
+ case "memory-lookup":
51
+ return "memory";
52
+ case "knowledge-lookup":
53
+ return "knowledge";
54
+ case "work-style-lookup":
55
+ return "work-style";
56
+ case "decision-lookup":
57
+ return "decision";
58
+ case "project-detect":
59
+ case "project-create":
60
+ return "project";
61
+ case "execute":
62
+ return "execution";
63
+ default:
64
+ return "unknown";
65
+ }
66
+ }
67
+ async function freshnessOf(relativeSourcePath, root = oceanRoot()) {
68
+ try {
69
+ const resolved = resolveWithin(root, relativeSourcePath);
70
+ const info = await stat(resolved);
71
+ return Date.now() - info.mtimeMs <= STALE_AFTER_MS ? "current" : "stale";
72
+ }
73
+ catch {
74
+ return "unknown";
75
+ }
76
+ }
77
+ // A path shape allow-list: relative, no traversal, no shell metacharacters, no null bytes.
78
+ // Existence of a file is never treated as proof a reference is valid — that check happens
79
+ // upstream (context-ladder) before a candidate ever reaches here; this function only ever
80
+ // trusts a candidate's *shape*.
81
+ const SAFE_RELATIVE_PATH = /^[A-Za-z0-9][A-Za-z0-9._\-/]*$/;
82
+ const UNSAFE_SEQUENCE = /[;&|`$()<>]/;
83
+ // Normalizes, validates, and deduplicates candidate references, in stable insertion order.
84
+ // Exercised directly by tests with synthetic multi-item input: the live pipeline in this
85
+ // slice only ever produces zero or one real candidate (no search yet), but this function's
86
+ // contract must already hold for slice 7, when it will receive more than one.
87
+ export function buildSelectedReferences(candidates) {
88
+ const references = [];
89
+ const violations = [];
90
+ const seen = new Set();
91
+ for (const candidate of candidates) {
92
+ const identifier = candidate?.identifier;
93
+ const sourcePath = candidate?.sourcePath;
94
+ if (!identifier || !sourcePath) {
95
+ violations.push("reference rejected: missing identifier or sourcePath");
96
+ continue;
97
+ }
98
+ if (identifier.includes("\0") || sourcePath.includes("\0")) {
99
+ violations.push(`reference rejected: null byte in identifier or path`);
100
+ continue;
101
+ }
102
+ if (path.isAbsolute(sourcePath)) {
103
+ violations.push(`reference rejected: absolute path not allowed (${sourcePath})`);
104
+ continue;
105
+ }
106
+ if (sourcePath.split(/[\\/]/).includes("..")) {
107
+ violations.push(`reference rejected: path traversal segment (${sourcePath})`);
108
+ continue;
109
+ }
110
+ if (UNSAFE_SEQUENCE.test(sourcePath) || UNSAFE_SEQUENCE.test(identifier)) {
111
+ violations.push(`reference rejected: unsafe shell syntax in identifier or path`);
112
+ continue;
113
+ }
114
+ if (!SAFE_RELATIVE_PATH.test(sourcePath)) {
115
+ violations.push(`reference rejected: sourcePath does not match the allowed shape (${sourcePath})`);
116
+ continue;
117
+ }
118
+ const key = `${identifier}::${sourcePath}`;
119
+ if (seen.has(key)) {
120
+ violations.push(`duplicate reference skipped: ${key}`);
121
+ continue;
122
+ }
123
+ seen.add(key);
124
+ references.push({ ...candidate });
125
+ }
126
+ return { references, violations };
127
+ }
128
+ function aggregateFreshness(references) {
129
+ if (references.length === 0)
130
+ return "unknown";
131
+ if (references.some((reference) => reference.freshness === "stale"))
132
+ return "stale";
133
+ if (references.some((reference) => reference.freshness === "unknown"))
134
+ return "unknown";
135
+ return "current";
136
+ }
137
+ function distinctRecordTypes(references) {
138
+ const types = [];
139
+ for (const reference of references)
140
+ if (!types.includes(reference.recordType))
141
+ types.push(reference.recordType);
142
+ return types;
143
+ }
144
+ // Top-level entry point: reuses slice 4's classification and slice 5's ladder verbatim.
145
+ // Budget is validated before any filesystem access at all — an invalid budget skips project
146
+ // resolution and the read plan entirely, so nothing is ever read on an invalid budget.
147
+ export async function buildContextPacket(classification, budget, cwd = process.cwd(), options) {
148
+ const budgetSummary = sanitizeBudget(budget);
149
+ const violations = [];
150
+ const budgetCheck = validateBudget(budget);
151
+ if (!budgetCheck.valid)
152
+ violations.push(`invalid-budget: ${budgetCheck.reason}`);
153
+ const { reason: rungReason } = resolveLadderRung(classification);
154
+ const resolution = budgetCheck.valid ? await resolveProject(cwd) : null;
155
+ const activeProject = resolution
156
+ ? activeProjectSummary(resolution)
157
+ : {
158
+ status: "unbound",
159
+ projectId: null,
160
+ confidence: "none",
161
+ };
162
+ const readPlan = budgetCheck.valid
163
+ ? await planContextRead(classification, budget, oceanRoot(),
164
+ // Nullable on purpose: an unbound session sees global memory only.
165
+ activeProject.projectId, options)
166
+ : null;
167
+ if (readPlan && !readPlan.allowed)
168
+ violations.push(`${readPlan.violation ?? "rejected"}: ${readPlan.reason}`);
169
+ let candidates = [];
170
+ if (readPlan?.allowed && readPlan.files.length > 0) {
171
+ candidates = await Promise.all(readPlan.files.map(async (sourcePath) => ({
172
+ identifier: readPlan.rung === "exact-record" && classification.identifier
173
+ ? classification.identifier
174
+ : path.basename(sourcePath, path.extname(sourcePath)),
175
+ recordType: recordTypeForIntent(classification.intent),
176
+ sourcePath,
177
+ freshness: await freshnessOf(sourcePath, oceanRoot()),
178
+ confidence: classification.confidence,
179
+ selectionReason: readPlan.reason,
180
+ })));
181
+ }
182
+ const { references, violations: referenceViolations } = buildSelectedReferences(candidates);
183
+ violations.push(...referenceViolations);
184
+ const packet = {
185
+ activeProject,
186
+ selectedReferences: references,
187
+ sourcePaths: references.map((reference) => reference.sourcePath),
188
+ recordTypes: distinctRecordTypes(references),
189
+ freshness: aggregateFreshness(references),
190
+ confidence: classification.confidence,
191
+ selectionReason: readPlan ? readPlan.reason : rungReason,
192
+ budget: budgetSummary,
193
+ violations,
194
+ ...(readPlan?.allowed && readPlan.rules
195
+ ? { projectRules: readPlan.rules }
196
+ : {}),
197
+ };
198
+ if (JSON.stringify(packet).length > PACKET_CHAR_BOUND) {
199
+ return {
200
+ ...packet,
201
+ selectedReferences: [],
202
+ sourcePaths: [],
203
+ recordTypes: [],
204
+ freshness: "unknown",
205
+ violations: [
206
+ ...violations,
207
+ `packet exceeded the ${PACKET_CHAR_BOUND}-char bound and was cleared to stay bounded`,
208
+ ],
209
+ };
210
+ }
211
+ return packet;
212
+ }
213
+ //# sourceMappingURL=context-packet.js.map