@aiwg/cli 2026.10.1 → 2026.10.4

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 (361) hide show
  1. package/README.md +2 -2
  2. package/agentic/code/providers/capability-matrix.yaml +270 -0
  3. package/agentic/code/providers/model-capabilities.v1.json +3183 -24
  4. package/bin/aiwg.mjs +135 -47
  5. package/dist/src/a2a/codecs.js +9 -0
  6. package/dist/src/agents/grounding/knowledge-base.js +54 -6
  7. package/dist/src/artifacts/browser-export.js +6 -0
  8. package/dist/src/artifacts/checksum-manifest.js +1 -0
  9. package/dist/src/artifacts/citation-parser.js +173 -92
  10. package/dist/src/artifacts/citation-table-schema.js +59 -0
  11. package/dist/src/artifacts/cli.js +57 -18
  12. package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
  13. package/dist/src/artifacts/corpus-tools/vision-extract.js +107 -15
  14. package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
  15. package/dist/src/artifacts/corpus-views/renderers.js +3 -3
  16. package/dist/src/artifacts/discovery-eval.js +6 -4
  17. package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
  18. package/dist/src/artifacts/fortemi-core-sync.js +166 -40
  19. package/dist/src/artifacts/framework-capabilities.js +171 -0
  20. package/dist/src/artifacts/framework-index-paths.js +73 -0
  21. package/dist/src/artifacts/framework-source-digest.js +75 -0
  22. package/dist/src/artifacts/index-builder.js +285 -131
  23. package/dist/src/artifacts/index-files.js +132 -18
  24. package/dist/src/artifacts/index-preparation.js +65 -0
  25. package/dist/src/artifacts/index-refresh.js +576 -0
  26. package/dist/src/artifacts/index-worker.mjs +9 -0
  27. package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
  28. package/dist/src/artifacts/prebuilt-deployment.js +46 -0
  29. package/dist/src/artifacts/prebuilt-relations.js +80 -0
  30. package/dist/src/artifacts/query-engine.js +410 -88
  31. package/dist/src/artifacts/relations-builder.js +168 -0
  32. package/dist/src/artifacts/relations.js +148 -0
  33. package/dist/src/artifacts/repair.js +9 -4
  34. package/dist/src/artifacts/stats.js +5 -2
  35. package/dist/src/artifacts/types.js +30 -10
  36. package/dist/src/artifacts/watcher.js +1 -1
  37. package/dist/src/auth/credential-store.js +2 -1
  38. package/dist/src/catalog/cli.d.mts +5 -0
  39. package/dist/src/channel/manager.d.mts +20 -0
  40. package/dist/src/channel/manager.mjs +78 -12
  41. package/dist/src/cli/child-process-result.js +40 -0
  42. package/dist/src/cli/command-log.js +2 -3
  43. package/dist/src/cli/env.js +38 -16
  44. package/dist/src/cli/flag-parsing.js +236 -0
  45. package/dist/src/cli/handlers/artifacts.js +3 -0
  46. package/dist/src/cli/handlers/auth.js +3 -0
  47. package/dist/src/cli/handlers/decision.js +218 -5
  48. package/dist/src/cli/handlers/diagnose.js +13 -5
  49. package/dist/src/cli/handlers/effect.js +4 -2
  50. package/dist/src/cli/handlers/evidence.js +3 -0
  51. package/dist/src/cli/handlers/feedback.js +14 -5
  52. package/dist/src/cli/handlers/help.js +1 -0
  53. package/dist/src/cli/handlers/index.js +3 -1
  54. package/dist/src/cli/handlers/init.js +248 -30
  55. package/dist/src/cli/handlers/lint.js +5 -0
  56. package/dist/src/cli/handlers/mc.js +23 -0
  57. package/dist/src/cli/handlers/models.js +169 -10
  58. package/dist/src/cli/handlers/ralph-launcher.js +2 -0
  59. package/dist/src/cli/handlers/ralph.js +3 -0
  60. package/dist/src/cli/handlers/refresh.js +28 -0
  61. package/dist/src/cli/handlers/regenerate.js +14 -0
  62. package/dist/src/cli/handlers/resource-versions.js +3 -0
  63. package/dist/src/cli/handlers/rules.js +195 -0
  64. package/dist/src/cli/handlers/run.js +4 -0
  65. package/dist/src/cli/handlers/runtime-info.js +25 -11
  66. package/dist/src/cli/handlers/script-runner.js +19 -8
  67. package/dist/src/cli/handlers/session.js +406 -62
  68. package/dist/src/cli/handlers/sessions.js +243 -76
  69. package/dist/src/cli/handlers/setup-manifest.js +474 -79
  70. package/dist/src/cli/handlers/setup.js +180 -39
  71. package/dist/src/cli/handlers/steward.js +11 -0
  72. package/dist/src/cli/handlers/subcommands.js +60 -3
  73. package/dist/src/cli/handlers/team.js +18 -8
  74. package/dist/src/cli/handlers/uhp.js +3 -0
  75. package/dist/src/cli/handlers/use.js +307 -55
  76. package/dist/src/cli/handlers/utilities.js +125 -26
  77. package/dist/src/cli/handlers/version.js +8 -0
  78. package/dist/src/cli/prompt-utils.js +72 -0
  79. package/dist/src/cli/provider-resolution.js +11 -0
  80. package/dist/src/cli/router.js +15 -1
  81. package/dist/src/cli/services/deployment-verification.js +19 -9
  82. package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
  83. package/dist/src/cli/skill-usage.js +2 -3
  84. package/dist/src/config/aiwg-config.js +349 -35
  85. package/dist/src/config/cli.js +256 -23
  86. package/dist/src/config/decision-config.js +106 -0
  87. package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
  88. package/dist/src/config/dispatch-thresholds-runtime.mjs +38 -0
  89. package/dist/src/config/gitignore.js +50 -0
  90. package/dist/src/config/index-graph-overrides.js +36 -0
  91. package/dist/src/config/project-artifacts-health.d.mts +37 -0
  92. package/dist/src/config/project-artifacts-health.mjs +14 -3
  93. package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
  94. package/dist/src/config/user-config-dir.d.mts +9 -0
  95. package/dist/src/config/user-config.js +38 -11
  96. package/dist/src/config/user-registry.js +23 -15
  97. package/dist/src/config/user-state.js +7 -0
  98. package/dist/src/decision/adapters/jev.js +75 -27
  99. package/dist/src/decision/ask.js +203 -43
  100. package/dist/src/decision/context-live-qualification.js +2 -2
  101. package/dist/src/decision/driver.js +14 -7
  102. package/dist/src/decision/ensemble/aggregate.js +23 -10
  103. package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
  104. package/dist/src/decision/entry.js +25 -6
  105. package/dist/src/decision/graph-live-qualification.js +1 -1
  106. package/dist/src/decision/graph-run.js +9 -0
  107. package/dist/src/decision/heldout/collector.js +15 -14
  108. package/dist/src/decision/heldout/contract.js +30 -7
  109. package/dist/src/decision/index.js +1 -0
  110. package/dist/src/decision/interop.js +60 -8
  111. package/dist/src/decision/jev-credentials.js +15 -1
  112. package/dist/src/decision/ledger.js +942 -0
  113. package/dist/src/decision/offload-activation.js +137 -0
  114. package/dist/src/decision/offload.js +550 -0
  115. package/dist/src/decision/qualification/load.js +4 -6
  116. package/dist/src/decision/savings.js +28 -0
  117. package/dist/src/decision/validate.js +6 -3
  118. package/dist/src/effects/dispatch-verifier.js +252 -0
  119. package/dist/src/effects/index.js +1 -0
  120. package/dist/src/effects/types.js +5 -1
  121. package/dist/src/effects/verifiers/git.js +2 -1
  122. package/dist/src/effects/verifiers/tracker.js +26 -14
  123. package/dist/src/extensions/commands/definitions.js +19 -0
  124. package/dist/src/extensions/project-local-remove.js +36 -1
  125. package/dist/src/features/installer.js +2 -2
  126. package/dist/src/flow/graph-metadata.js +5 -0
  127. package/dist/src/gates/providers/loader.js +64 -18
  128. package/dist/src/installation/manager-command.d.mts +18 -0
  129. package/dist/src/installation/manager-command.mjs +15 -3
  130. package/dist/src/installation/manager.d.mts +49 -0
  131. package/dist/src/installation/manager.mjs +95 -5
  132. package/dist/src/issues/cli.js +4 -4
  133. package/dist/src/issues/local.js +24 -5
  134. package/dist/src/jobs/runner.js +1 -1
  135. package/dist/src/lint/cli.js +27 -1
  136. package/dist/src/lint/loader.js +2 -1
  137. package/dist/src/lint/reporters.js +25 -1
  138. package/dist/src/lint/rule-activation.js +43 -0
  139. package/dist/src/lint/runner.js +137 -62
  140. package/dist/src/mcp/adapters/codex-runtime.js +366 -56
  141. package/dist/src/mcp/claude-install.d.mts +4 -0
  142. package/dist/src/mcp/claude-install.mjs +41 -0
  143. package/dist/src/mcp/cli.d.mts +1 -0
  144. package/dist/src/mcp/cli.mjs +620 -85
  145. package/dist/src/mcp/config-file.d.mts +28 -0
  146. package/dist/src/mcp/config-file.mjs +279 -0
  147. package/dist/src/mcp/config-layers.d.mts +36 -0
  148. package/dist/src/mcp/config-layers.mjs +348 -0
  149. package/dist/src/mcp/credentials.d.mts +51 -0
  150. package/dist/src/mcp/credentials.mjs +484 -0
  151. package/dist/src/mcp/grok-build-config.d.mts +6 -0
  152. package/dist/src/mcp/grok-build-config.mjs +17 -27
  153. package/dist/src/mcp/helpers.mjs +109 -4
  154. package/dist/src/mcp/muse-native-config.d.mts +13 -0
  155. package/dist/src/mcp/muse-native-config.mjs +1 -0
  156. package/dist/src/mcp/omp-config.d.mts +4 -0
  157. package/dist/src/mcp/omp-config.mjs +50 -29
  158. package/dist/src/mcp/profiles.js +97 -23
  159. package/dist/src/mcp/profiles.mjs +99 -27
  160. package/dist/src/mcp/registry.js +248 -131
  161. package/dist/src/mcp/registry.mjs +169 -100
  162. package/dist/src/mcp/server-config.d.mts +3 -0
  163. package/dist/src/mcp/server-config.mjs +89 -0
  164. package/dist/src/mcp/server.mjs +127 -82
  165. package/dist/src/mcp/test-simple.mjs +2 -1
  166. package/dist/src/mcp/toml-editor.d.mts +4 -0
  167. package/dist/src/mcp/toml-string.d.mts +1 -0
  168. package/dist/src/mcp/toml-string.mjs +9 -0
  169. package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
  170. package/dist/src/mcp/toml-strip-mcp.mjs +135 -0
  171. package/dist/src/mcp/tool-filters.d.mts +46 -0
  172. package/dist/src/mcp/tool-filters.mjs +357 -0
  173. package/dist/src/mcp/tools/agentic-sandbox.mjs +3 -1
  174. package/dist/src/mcp/tools/decision.mjs +22 -3
  175. package/dist/src/mcp/tools/discovery.mjs +62 -55
  176. package/dist/src/mcp/tools/orchestration.mjs +50 -2
  177. package/dist/src/mcp/tools/subsystems.mjs +39 -8
  178. package/dist/src/mcp/tools/worker-invoke.mjs +2856 -0
  179. package/dist/src/mcp/tools/worker-shell-control.mjs +289 -0
  180. package/dist/src/mcp/tools/worker-stdin-runtime.mjs +79 -0
  181. package/dist/src/memory/project-registry.js +2 -2
  182. package/dist/src/mission-protocol/codecs.js +647 -1
  183. package/dist/src/mission-protocol/types.js +110 -0
  184. package/dist/src/mission-protocol/worker-status.js +2 -0
  185. package/dist/src/models/config-loader.js +76 -14
  186. package/dist/src/models/grok-build-models.js +93 -41
  187. package/dist/src/models/model-capabilities.v1.json +3183 -24
  188. package/dist/src/models/model-discovery.js +931 -67
  189. package/dist/src/models/provider-policy.js +719 -11
  190. package/dist/src/models/qualification-digest.d.mts +2 -0
  191. package/dist/src/models/qualification-digest.mjs +17 -0
  192. package/dist/src/models/resolver.js +261 -0
  193. package/dist/src/models/worker-direct-readiness.js +550 -0
  194. package/dist/src/models/worker-dispatch-offer.js +169 -0
  195. package/dist/src/models/worker-health-readiness.js +144 -0
  196. package/dist/src/models/worker-installation.d.mts +39 -0
  197. package/dist/src/models/worker-installation.mjs +986 -0
  198. package/dist/src/models/wrapper-route.js +18 -0
  199. package/dist/src/ops/registry.js +9 -14
  200. package/dist/src/plugins/standalone-packager.js +4 -3
  201. package/dist/src/process/bounded-child-process.d.mts +84 -0
  202. package/dist/src/process/bounded-child-process.mjs +430 -0
  203. package/dist/src/process/cli-command.d.mts +16 -0
  204. package/dist/src/process/cli-command.mjs +75 -0
  205. package/dist/src/process/signal-child.d.mts +6 -0
  206. package/dist/src/process/signal-child.mjs +7 -0
  207. package/dist/src/providers/capability-matrix.js +8 -0
  208. package/dist/src/providers/capability-matrix.yaml +270 -0
  209. package/dist/src/providers/deployed-skill-roots.js +20 -0
  210. package/dist/src/providers/grok-build-acp.js +3 -2
  211. package/dist/src/providers/grok-build-headless.js +53 -23
  212. package/dist/src/providers/grok-build-inspect.js +1 -0
  213. package/dist/src/providers/omp-agent.d.mts +8 -0
  214. package/dist/src/providers/omp-diagnostics.d.mts +1 -0
  215. package/dist/src/providers/omp-paths.d.mts +13 -0
  216. package/dist/src/providers/provider-definitions.js +9 -1
  217. package/dist/src/providers/provider-definitions.mjs +9 -2
  218. package/dist/src/providers/skill-ownership.d.mts +9 -0
  219. package/dist/src/providers/skill-ownership.mjs +88 -0
  220. package/dist/src/rules/activation.d.mts +21 -0
  221. package/dist/src/rules/activation.mjs +62 -0
  222. package/dist/src/rules/inline-budget.d.mts +26 -0
  223. package/dist/src/rules/inline-budget.mjs +66 -0
  224. package/dist/src/rules/inventory.js +129 -0
  225. package/dist/src/rules/pin-state.js +73 -0
  226. package/dist/src/rules/pins.d.mts +14 -0
  227. package/dist/src/rules/pins.mjs +112 -0
  228. package/dist/src/rules/rule-frontmatter.schema.json +197 -0
  229. package/dist/src/serve/executor-registry.js +16 -24
  230. package/dist/src/serve/fleet-mission-conductor.js +9 -0
  231. package/dist/src/serve/mission-conductor.js +47 -12
  232. package/dist/src/serve/shared-host-scheduler.js +2 -0
  233. package/dist/src/serve/worker-invocation.js +558 -0
  234. package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
  235. package/dist/src/serve/worker-stdin-frame.mjs +107 -0
  236. package/dist/src/sessions/adapter-registry.js +49 -0
  237. package/dist/src/sessions/adapters/claude.js +39 -54
  238. package/dist/src/sessions/adapters/codex.js +82 -67
  239. package/dist/src/sessions/adapters/copilot.js +4 -4
  240. package/dist/src/sessions/adapters/cursor.js +40 -57
  241. package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
  242. package/dist/src/sessions/adapters/factory.js +31 -27
  243. package/dist/src/sessions/adapters/generic.js +43 -9
  244. package/dist/src/sessions/adapters/grok-build.js +11 -11
  245. package/dist/src/sessions/adapters/grokbot-locator.js +111 -0
  246. package/dist/src/sessions/adapters/grokbot.js +434 -24
  247. package/dist/src/sessions/adapters/hermes.js +27 -29
  248. package/dist/src/sessions/adapters/muse-workspace-scan.js +128 -0
  249. package/dist/src/sessions/adapters/muse.js +200 -104
  250. package/dist/src/sessions/adapters/omp.js +28 -14
  251. package/dist/src/sessions/adapters/openclaw.js +89 -79
  252. package/dist/src/sessions/adapters/opencode.js +27 -24
  253. package/dist/src/sessions/adapters/openhuman.js +18 -60
  254. package/dist/src/sessions/adapters/pi.js +30 -14
  255. package/dist/src/sessions/adapters/warp.js +3 -3
  256. package/dist/src/sessions/adapters/windsurf.js +19 -48
  257. package/dist/src/sessions/batch-contracts.js +25 -5
  258. package/dist/src/sessions/batch-import.js +72 -51
  259. package/dist/src/sessions/candidates.js +47 -11
  260. package/dist/src/sessions/contracts.js +75 -2
  261. package/dist/src/sessions/coverage-preflight.js +134 -0
  262. package/dist/src/sessions/discovery.js +3 -0
  263. package/dist/src/sessions/export-plan.js +4 -3
  264. package/dist/src/sessions/grok-build-cli.js +67 -5
  265. package/dist/src/sessions/import-exclusions.js +72 -0
  266. package/dist/src/sessions/import-lease.js +34 -18
  267. package/dist/src/sessions/import-limits.js +37 -0
  268. package/dist/src/sessions/importer.js +84 -46
  269. package/dist/src/sessions/index.js +2 -0
  270. package/dist/src/sessions/lifecycle.js +45 -0
  271. package/dist/src/sessions/policy.js +133 -25
  272. package/dist/src/sessions/readers.js +106 -53
  273. package/dist/src/sessions/refresh.js +106 -0
  274. package/dist/src/sessions/repository.js +199 -63
  275. package/dist/src/sessions/workspace-discovery.js +315 -88
  276. package/dist/src/sessions/workspace-matching.js +107 -0
  277. package/dist/src/skills/adapters/clawhub.js +10 -8
  278. package/dist/src/skills/adapters/openclaw.js +10 -7
  279. package/dist/src/skills/importer.js +1 -0
  280. package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
  281. package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
  282. package/dist/src/smiths/context-pipeline/finalization.js +6 -5
  283. package/dist/src/smiths/context-pipeline/generator.js +21 -4
  284. package/dist/src/smiths/context-pipeline/parallelism-section.js +195 -14
  285. package/dist/src/smiths/context-pipeline/workspace-context.js +116 -9
  286. package/dist/src/smiths/hook-bridge/codex-translator.js +6 -5
  287. package/dist/src/smiths/mcpsmith/example.js +1 -1
  288. package/dist/src/smiths/mcpsmith/generator.js +1 -1
  289. package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
  290. package/dist/src/smiths/toolsmith/runtime-discovery.mjs +85 -31
  291. package/dist/src/storage/backends/fs.js +18 -76
  292. package/dist/src/storage/backends/logseq.js +68 -7
  293. package/dist/src/storage/backends/obsidian.js +1 -0
  294. package/dist/src/storage/fortemi-qualification-receipt.js +2 -2
  295. package/dist/src/testing/generators/test-code-generator.js +57 -30
  296. package/dist/src/tracker/forge-http.js +2 -1
  297. package/dist/src/uhp/client.js +11 -1
  298. package/dist/src/uhp/mission.js +5 -0
  299. package/dist/src/update/checker.d.mts +2 -0
  300. package/dist/src/update/checker.mjs +5 -5
  301. package/dist/src/update/notifier.mjs +54 -2
  302. package/dist/src/update/service.d.mts +22 -0
  303. package/dist/src/utils/contained-filesystem.d.mts +37 -0
  304. package/dist/src/utils/contained-filesystem.mjs +683 -0
  305. package/dist/src/utils/temporary-directory.js +36 -0
  306. package/dist/src/writing/example-generator.js +1 -1
  307. package/dist/src/writing/fidelity.js +18 -1
  308. package/package.json +32 -2
  309. package/schemas/mission-v1.schema.json +2429 -0
  310. package/schemas/models/model-catalog.v1.schema.json +301 -0
  311. package/schemas/models/model-config-compatibility.v1.schema.json +281 -0
  312. package/schemas/models/model-policy.v1.schema.json +14 -0
  313. package/schemas/models/provider-model-capabilities.v1.schema.json +1072 -0
  314. package/tools/_resolve-impl.mjs +39 -2
  315. package/tools/agents/deploy-agents.mjs +64 -23
  316. package/tools/agents/providers/base.mjs +153 -166
  317. package/tools/agents/providers/claude.mjs +8 -24
  318. package/tools/agents/providers/codex.mjs +18 -11
  319. package/tools/agents/providers/cursor.mjs +19 -18
  320. package/tools/agents/providers/grok-build.mjs +3 -1
  321. package/tools/agents/providers/grokbot.mjs +11 -12
  322. package/tools/agents/providers/hermes.mjs +62 -49
  323. package/tools/agents/providers/omp.mjs +5 -2
  324. package/tools/agents/providers/openclaw-translator.mjs +19 -5
  325. package/tools/agents/providers/openclaw.mjs +9 -4
  326. package/tools/agents/providers/pi.mjs +5 -0
  327. package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
  328. package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
  329. package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
  330. package/tools/agents/providers/rule-delivery.mjs +1270 -0
  331. package/tools/agents/providers/warp.mjs +20 -26
  332. package/tools/cli/decision-offload-report.mjs +40 -0
  333. package/tools/cli/workspace-status.mjs +862 -0
  334. package/tools/plugin/package-plugins.mjs +186 -8
  335. package/tools/plugin/plugin-links.mjs +114 -0
  336. package/tools/providers/antigravity-transport.mjs +3 -1
  337. package/tools/providers/deepseek-harness-transport.mjs +360 -0
  338. package/tools/providers/omp-transport.mjs +194 -0
  339. package/tools/ralph-external/lib/claude-adapter.mjs +389 -0
  340. package/tools/ralph-external/lib/codex-adapter.mjs +432 -0
  341. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
  342. package/tools/ralph-external/lib/deepseek-harness-adapter.mjs +397 -0
  343. package/tools/ralph-external/lib/factory-adapter.mjs +503 -0
  344. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
  345. package/tools/ralph-external/lib/muse-adapter.mjs +364 -0
  346. package/tools/ralph-external/lib/omp-adapter.mjs +315 -0
  347. package/tools/ralph-external/lib/opencode-adapter.mjs +575 -0
  348. package/tools/ralph-external/lib/openhuman-adapter.mjs +75 -0
  349. package/tools/ralph-external/lib/pi-adapter.mjs +329 -0
  350. package/tools/ralph-external/lib/provider-adapter.mjs +748 -0
  351. package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
  352. package/tools/ralph-external/lib/session-environment.mjs +28 -0
  353. package/tools/ralph-external/lib/systemd-client.mjs +49 -0
  354. package/tools/ralph-external/lib/worker-health.mjs +439 -0
  355. package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
  356. package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
  357. package/tools/ralph-external/process-monitor.mjs +1017 -0
  358. package/tools/ralph-external/session-launcher.mjs +5057 -0
  359. package/tools/scripts/resource-preflight.mjs +541 -0
  360. package/tools/skills/deploy-skills-codex.mjs +2 -1
  361. package/vscode-extension/schemas/aiwg.config.v1.json +2916 -0
@@ -1,49 +1,459 @@
1
1
  /**
2
- * Grok Bot session adapter — manual-export only until product evidence exists
3
- * for a native session filesystem or API locator.
2
+ * Grok Bot session adapter — manual-export interchange plus an evidence-gated
3
+ * native locator for the Grok Bot desktop app's local transcript cache.
4
4
  *
5
- * Auto-discover is unsupported and must never scrape invented ~/.grokbot,
6
- * Cursor session paths, or other unverified roots.
5
+ * Native discovery (#2970) is Linux-only and requires an explicitly
6
+ * authorized `sand-client-persistence` root (`--grokbot-root`); there is no
7
+ * default path and nothing is guessed for macOS/Windows, where the layout is
8
+ * unverified. The store is one JSON document per key, named `<base32
9
+ * key>.blob` (RFC 4648 section 6, lowercase, unpadded). Discovery decodes
10
+ * ONLY filenames to classify them; it never opens a file whose decoded key
11
+ * is outside the allowlist below, and it never recurses or follows symlinks.
7
12
  *
8
- * Authorized imports reuse the AIWG generic session interchange format so
9
- * operators can supply an explicit export without inventing a Grok schema.
13
+ * Allowlisted keys (see grokbot-locator.ts):
14
+ * - `...transcript.replicas.<conversation-uuid>` — one conversation; the
15
+ * only key class ever selected as an importable source.
16
+ * - `...roster.last-roster` — account conversation index; never opened by
17
+ * this adapter (no session content), kept out of discover() results.
18
+ * Every other key (composer-drafts, send-journal, experiments, ui-layout,
19
+ * sand-secrets, box-secrets, cookies, local/session storage, etc.) is never
20
+ * decoded content, never opened, and `~/.grokbot` (daemon credentials) is
21
+ * never referenced.
22
+ *
23
+ * Grok Bot conversations carry no workspace/cwd field — there is no native
24
+ * evidence linking a conversation to a project directory. Following the
25
+ * established precedent for other account-scoped, non-workspace-scoped
26
+ * native locators (`pi`, `deepseek-harness`: no per-source workspace
27
+ * filtering in workspace-discovery.ts), every authorized conversation under
28
+ * `--grokbot-root` surfaces to whichever workspace runs discovery; this
29
+ * adapter never guesses a workspace link and `inspect()` never sets
30
+ * `declaredWorkspaceId` for native sources.
31
+ *
32
+ * The replica is a bounded client-side cache (~200 entries) that can evict
33
+ * its oldest entries as new ones arrive, so a re-read is not append-only —
34
+ * the file may shrink, and Grok Bot may rewrite it via a rename-swap that
35
+ * changes its inode. This adapter declares `sourceContinuity: 'snapshot'`
36
+ * (contracts.ts), which tells the shared importer to skip its generic
37
+ * size-shrink / file-identity / prefix-rewrite checks (those assume
38
+ * append-only growth) for this source. Idempotent, non-duplicating re-import
39
+ * across the sliding window then relies on two things instead: (1)
40
+ * `workspace-discovery.ts` assigns a conversation-id–derived,
41
+ * content-independent `sourceId` for native sources (mirroring the existing
42
+ * OMP convention), so the importer's checkpoint continuity survives content
43
+ * changes; (2) `stream()` resumes from the native, monotonic `seq` carried
44
+ * by each entry (not a position/byte offset, which the sliding window would
45
+ * invalidate) and tags every record's `sourceCursor` as `native:<seq>` — an
46
+ * opaque provider-native resume token the importer stores verbatim
47
+ * (`positionKind: 'provider-native'`), never reinterpreted as a byte offset.
48
+ * Combined with the catalog's identity-keyed `INSERT OR IGNORE`, re-importing
49
+ * after the window has slid can only add genuinely new entries, never
50
+ * duplicate ones.
10
51
  *
11
52
  * @see docs/providers/grokbot-sessions.md
12
- * @issue #215
53
+ * @issue #215, #2970
13
54
  */
14
- import { SessionContractError, } from '../contracts.js';
55
+ import { opendir } from 'node:fs/promises';
56
+ import { resolve } from 'node:path';
57
+ import { z } from 'zod';
58
+ import { schemaRejectionDetail, schemaIssueDetail, SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
59
+ import { readBoundedJson } from '../readers.js';
15
60
  import { GenericSessionInterchangeAdapter, } from './generic.js';
61
+ import { classifyGrokbotBlobName, grokbotConversationIdFromLocator, } from './grokbot-locator.js';
16
62
  export const GROKBOT_ADAPTER_VERSION = '1.0.0';
17
63
  export const GROKBOT_EXPORT_SCHEMA_VERSION = '1.0.0';
64
+ export const GROKBOT_NATIVE_SCHEMA_VERSION = '1.0.0';
65
+ export const GROKBOT_NATIVE_SCHEMA_MAJOR = 1;
18
66
  export const GROKBOT_LOCATOR_CLASS = 'manual-export';
67
+ export const GROKBOT_NATIVE_REPLICA_LOCATOR_CLASS = 'grokbot-native-transcript-replica';
68
+ const GrokbotEntryBaseSchema = z.object({
69
+ kind: z.string().min(1),
70
+ id: z.string().min(1),
71
+ timestampMs: z.number().int().nonnegative(),
72
+ requestId: z.string().optional(),
73
+ seq: z.number().int().nonnegative(),
74
+ }).passthrough();
75
+ const SendMessageEntrySchema = GrokbotEntryBaseSchema.extend({
76
+ kind: z.literal('send-message'),
77
+ respondedValue: z.unknown().optional(),
78
+ secretProvided: z.boolean().optional(),
79
+ message: z.discriminatedUnion('type', [
80
+ z.object({ type: z.literal('text'), content: z.string() }).passthrough(),
81
+ z.object({
82
+ type: z.literal('widget'),
83
+ widget: z.object({ prompt: z.string() }).passthrough(),
84
+ }).passthrough(),
85
+ z.object({
86
+ type: z.literal('email-draft'),
87
+ draft: z.object({ subject: z.string().optional(), body: z.string() }).passthrough(),
88
+ }).passthrough(),
89
+ z.object({
90
+ type: z.literal('slack-draft'),
91
+ draft: z.object({ target: z.string().optional(), body: z.string() }).passthrough(),
92
+ }).passthrough(),
93
+ z.object({
94
+ type: z.literal('attachment'),
95
+ url: z.string(),
96
+ file_name: z.string(),
97
+ }).passthrough(),
98
+ z.object({
99
+ type: z.literal('user-form'),
100
+ formRequest: z.object({ title: z.string() }).passthrough(),
101
+ }).passthrough(),
102
+ z.object({
103
+ type: z.literal('secret-request'),
104
+ secretRequest: z.object({ label: z.string() }).passthrough(),
105
+ }).passthrough(),
106
+ ]),
107
+ });
108
+ const MessageEntrySchema = GrokbotEntryBaseSchema.extend({
109
+ kind: z.literal('message'),
110
+ role: z.enum(['user', 'assistant']),
111
+ content: z.string(),
112
+ fromAgent: z.object({ id: z.string().optional(), name: z.string().optional() })
113
+ .passthrough().optional(),
114
+ });
115
+ const UserAttachmentEntrySchema = GrokbotEntryBaseSchema.extend({
116
+ kind: z.literal('user-attachment'),
117
+ file_name: z.string(),
118
+ file_path: z.string().optional(),
119
+ width: z.number().optional(),
120
+ height: z.number().optional(),
121
+ byteSize: z.number().optional(),
122
+ });
123
+ const EventEntrySchema = GrokbotEntryBaseSchema.extend({
124
+ kind: z.literal('event'),
125
+ event: z.record(z.string(), z.unknown()),
126
+ });
127
+ const NoticeEntrySchema = GrokbotEntryBaseSchema.extend({
128
+ kind: z.literal('notice'),
129
+ text: z.string(),
130
+ });
131
+ const GrokbotReplicaValueSchema = z.object({
132
+ entries: z.array(GrokbotEntryBaseSchema),
133
+ epochHint: z.string().nullish(),
134
+ acceptedSequenceHint: z.number().int().nonnegative().nullish(),
135
+ persistedAt: z.number().int().nonnegative().nullish(),
136
+ precedingTimestampMs: z.number().int().nonnegative().nullish(),
137
+ }).passthrough();
138
+ const GrokbotReplicaDocumentSchema = z.object({
139
+ schemaVersion: z.number().int().nonnegative(),
140
+ value: GrokbotReplicaValueSchema,
141
+ }).passthrough();
19
142
  export class GrokbotSessionAdapter {
143
+ limits;
144
+ maxFiles;
20
145
  provider = 'grokbot';
21
146
  adapterVersion = GROKBOT_ADAPTER_VERSION;
22
- disposition = 'manual-only';
23
- supportedOperations = ['inspect', 'stream'];
24
- acquisitionModes = ['manual-export'];
147
+ disposition = 'implemented';
148
+ supportedOperations = ['discover', 'inspect', 'stream'];
149
+ acquisitionModes = ['manual-export', 'jsonl'];
150
+ /**
151
+ * The replica is rewritten wholesale (sliding window, possible
152
+ * rename-swap), not appended to. See the class-level doc comment.
153
+ */
154
+ sourceContinuity = 'snapshot';
25
155
  interchange;
26
- constructor(limits) {
156
+ constructor(limits, maxFiles = 10_000) {
157
+ this.limits = limits;
158
+ this.maxFiles = maxFiles;
27
159
  this.interchange = new GenericSessionInterchangeAdapter(limits);
28
160
  }
29
- async *discover(_scope) {
30
- throw new SessionContractError('UNSUPPORTED_OPERATION', 'Grok Bot session auto-discover is unsupported until a verified native locator exists; '
31
- + 'select an authorized manual export explicitly. AIWG does not scrape ~/.grokbot, '
32
- + '~/grokbot-skills, or Cursor session paths.');
161
+ async *discover(scope) {
162
+ if (scope.allowedRoots.length === 0) {
163
+ throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'Grok Bot native discovery requires an explicitly authorized sand-client-persistence root; '
164
+ + 'pass --grokbot-root. There is no default root and nothing is guessed for macOS or Windows.');
165
+ }
166
+ let count = 0;
167
+ for (const root of [...scope.allowedRoots].sort()) {
168
+ let directory;
169
+ try {
170
+ directory = await opendir(resolve(root));
171
+ }
172
+ catch {
173
+ continue;
174
+ }
175
+ for await (const dirent of directory) {
176
+ // Never recurse, never follow symlinks: only plain files directly
177
+ // under the authorized root are even filename-classified.
178
+ if (!dirent.isFile())
179
+ continue;
180
+ const classification = classifyGrokbotBlobName(dirent.name);
181
+ if (classification?.kind !== 'transcript-replica')
182
+ continue;
183
+ if (++count > this.maxFiles) {
184
+ throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Grok Bot native discovery exceeded the authorized file limit', { limit: 'maxFiles', configured: this.maxFiles, observed: count });
185
+ }
186
+ yield {
187
+ provider: 'grokbot',
188
+ locator: resolve(root, dirent.name),
189
+ locatorClass: GROKBOT_NATIVE_REPLICA_LOCATOR_CLASS,
190
+ };
191
+ }
192
+ }
33
193
  }
34
194
  async inspect(source) {
35
- this.assertManualExport(source);
36
- return this.interchange.inspect(source);
195
+ if (source.locatorClass === GROKBOT_LOCATOR_CLASS) {
196
+ return this.interchange.inspect(source);
197
+ }
198
+ this.assertNativeReplica(source);
199
+ const native = await this.readReplica(source);
200
+ return {
201
+ sourceSchemaVersion: GROKBOT_NATIVE_SCHEMA_VERSION,
202
+ consistency: native.truncated ? 'provisional' : 'complete',
203
+ operationalState: 'available',
204
+ // No native evidence links a conversation to a workspace/cwd; never
205
+ // fabricate one.
206
+ };
37
207
  }
38
208
  async *stream(source, cursor) {
39
- this.assertManualExport(source);
40
- yield* this.interchange.stream(source, cursor);
209
+ if (source.locatorClass === GROKBOT_LOCATOR_CLASS) {
210
+ yield* this.interchange.stream(source, cursor);
211
+ return;
212
+ }
213
+ this.assertNativeReplica(source);
214
+ const native = await this.readReplica(source);
215
+ const conversationId = grokbotConversationIdFromLocator(source.locator);
216
+ if (!conversationId) {
217
+ throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'Grok Bot native locator does not decode to an authorized transcript-replica key');
218
+ }
219
+ const lastSeq = parseNativeCursor(cursor?.value);
220
+ const seen = new Set();
221
+ for (const entry of native.entries) {
222
+ if (seen.has(entry.seq)) {
223
+ throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Grok Bot transcript replica contains a duplicate entry sequence');
224
+ }
225
+ seen.add(entry.seq);
226
+ if (entry.seq <= lastSeq)
227
+ continue;
228
+ const mapped = mapGrokbotEntry(entry);
229
+ if (!mapped)
230
+ continue; // Unrecognized entry kind: skipped, never fabricated.
231
+ yield {
232
+ nativeSessionId: conversationId,
233
+ nativeEventId: mapped.nativeEventId,
234
+ sequence: mapped.sequence,
235
+ kind: mapped.kind,
236
+ role: mapped.role,
237
+ participant: mapped.participant,
238
+ text: mapped.text,
239
+ occurredAt: mapped.occurredAt,
240
+ // Opaque provider-native resume token: the importer stores this
241
+ // verbatim (positionKind: 'provider-native') and never reinterprets
242
+ // it as a byte offset. It encodes the last imported native `seq`,
243
+ // which is monotonic and stable across the bounded cache evicting
244
+ // old entries. See the class-level doc comment for the full
245
+ // rationale.
246
+ sourceCursor: `native:${entry.seq}`,
247
+ rawReference: { locatorClass: GROKBOT_NATIVE_REPLICA_LOCATOR_CLASS, sequence: entry.seq },
248
+ extensions: mapped.extensions,
249
+ };
250
+ }
251
+ }
252
+ assertNativeReplica(source) {
253
+ if (source.locatorClass !== GROKBOT_NATIVE_REPLICA_LOCATOR_CLASS) {
254
+ throw new SessionContractError('UNSUPPORTED_OPERATION', `Grok Bot supports only locatorClass "${GROKBOT_LOCATOR_CLASS}" or `
255
+ + `"${GROKBOT_NATIVE_REPLICA_LOCATOR_CLASS}" (got "${source.locatorClass}"). `
256
+ + 'Do not reuse Cursor or other provider locators.');
257
+ }
258
+ // Enforce the filename allowlist before any read, not only in discover():
259
+ // a caller can hand inspect()/stream() any path with the native class.
260
+ if (!grokbotConversationIdFromLocator(source.locator)) {
261
+ throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'Grok Bot native locator does not decode to an authorized transcript-replica key');
262
+ }
41
263
  }
42
- assertManualExport(source) {
43
- if (source.locatorClass !== GROKBOT_LOCATOR_CLASS) {
44
- throw new SessionContractError('UNSUPPORTED_OPERATION', `Grok Bot supports only locatorClass "${GROKBOT_LOCATOR_CLASS}" until native session evidence exists `
45
- + `(got "${source.locatorClass}"). Do not reuse Cursor session locators.`);
264
+ async readReplica(source) {
265
+ const { value } = await readBoundedJson({
266
+ selectedPath: source.locator,
267
+ allowedRoots: source.authorizedScope.allowedRoots,
268
+ }, this.limits);
269
+ const parsed = GrokbotReplicaDocumentSchema.safeParse(value);
270
+ if (!parsed.success) {
271
+ throw malformedGrokbotSource('transcript replica', parsed.error);
46
272
  }
273
+ assertSupportedSchemaMajor(`${parsed.data.schemaVersion}.0.0`, GROKBOT_NATIVE_SCHEMA_MAJOR);
274
+ const { entries, precedingTimestampMs } = parsed.data.value;
275
+ // The resume cursor is the last yielded seq, so stream in seq order even
276
+ // if the cache stores entries out of order.
277
+ entries.sort((left, right) => left.seq - right.seq);
278
+ // A window that no longer starts at seq 1 has evicted history, whether or
279
+ // not the optional precedingTimestampMs hint is present.
280
+ const truncated = entries.length > 0
281
+ && (entries[0].seq > 1 || (precedingTimestampMs ?? 0) > 0);
282
+ return { entries, truncated };
283
+ }
284
+ }
285
+ function parseNativeCursor(value) {
286
+ if (!value)
287
+ return 0;
288
+ const raw = value.startsWith('native:') ? value.slice('native:'.length) : value;
289
+ if (!/^\d+$/.test(raw))
290
+ throw new SessionContractError('SCHEMA_DRIFT', 'invalid Grok Bot native cursor', schemaIssueDetail('cursor'));
291
+ return Number(raw);
292
+ }
293
+ function mapGrokbotEntry(entry) {
294
+ const occurredAt = new Date(entry.timestampMs).toISOString();
295
+ switch (entry.kind) {
296
+ case 'send-message':
297
+ return mapSendMessage(entry, occurredAt);
298
+ case 'message':
299
+ return mapMessage(entry, occurredAt);
300
+ case 'user-attachment':
301
+ return mapUserAttachment(entry, occurredAt);
302
+ case 'event':
303
+ return mapEvent(entry, occurredAt);
304
+ case 'notice':
305
+ return mapNotice(entry, occurredAt);
306
+ default:
307
+ // Unrecognized entry kind: safely skipped, never fabricated into a record.
308
+ return null;
47
309
  }
48
310
  }
311
+ function malformedGrokbotSource(subject, error) {
312
+ // Include only schema paths and codes, never Zod messages or input values.
313
+ const issues = error.issues.map((issue) => `${issue.path.join('.') || '<root>'} (${issue.code})`);
314
+ return new SessionContractError('MALFORMED_SOURCE', `Grok Bot ${subject} is malformed at ${issues.join(', ')}`, schemaRejectionDetail(error));
315
+ }
316
+ function mapSendMessage(raw, occurredAt) {
317
+ const messageType = z.object({ type: z.string().min(1) }).safeParse(raw.message);
318
+ if (messageType.success && !SendMessageEntrySchema.shape.message.options.some((option) => option.shape.type.value === messageType.data.type)) {
319
+ // Like unknown entry kinds, unknown message types are skipped without inventing content.
320
+ // Known types still go through their full schema validation below.
321
+ return null;
322
+ }
323
+ const parsed = SendMessageEntrySchema.safeParse(raw);
324
+ if (!parsed.success) {
325
+ throw malformedGrokbotSource('send-message entry', parsed.error);
326
+ }
327
+ const entry = parsed.data;
328
+ const { message } = entry;
329
+ let text;
330
+ switch (message.type) {
331
+ case 'text':
332
+ text = message.content;
333
+ break;
334
+ case 'widget':
335
+ text = `[widget] ${message.widget.prompt} -> ${stringOrEmpty(entry.respondedValue)}`;
336
+ break;
337
+ case 'email-draft':
338
+ text = `[email-draft] ${message.draft.subject ?? ''}\n${message.draft.body}`;
339
+ break;
340
+ case 'slack-draft':
341
+ text = `[slack-draft] ${message.draft.target ?? ''}\n${message.draft.body}`;
342
+ break;
343
+ case 'attachment':
344
+ text = `[attachment] ${message.file_name} (${message.url})`;
345
+ break;
346
+ case 'user-form':
347
+ text = `[user-form] ${message.formRequest.title}`;
348
+ break;
349
+ case 'secret-request':
350
+ text = `[secret-request] ${message.secretRequest.label} (value not exported)`;
351
+ break;
352
+ default:
353
+ text = '';
354
+ }
355
+ return {
356
+ nativeEventId: entry.id,
357
+ sequence: entry.seq,
358
+ kind: `grokbot.send-message.${message.type}`,
359
+ role: 'assistant',
360
+ text,
361
+ occurredAt,
362
+ extensions: {
363
+ nativeKind: 'send-message',
364
+ messageType: message.type,
365
+ secretProvided: entry.secretProvided,
366
+ provenance: { acquisition: GROKBOT_NATIVE_REPLICA_LOCATOR_CLASS },
367
+ },
368
+ };
369
+ }
370
+ function mapMessage(raw, occurredAt) {
371
+ const parsed = MessageEntrySchema.safeParse(raw);
372
+ if (!parsed.success) {
373
+ throw malformedGrokbotSource('message entry', parsed.error);
374
+ }
375
+ const entry = parsed.data;
376
+ return {
377
+ nativeEventId: entry.id,
378
+ sequence: entry.seq,
379
+ kind: 'grokbot.message',
380
+ role: entry.role,
381
+ participant: entry.fromAgent?.name ?? entry.fromAgent?.id,
382
+ text: entry.content,
383
+ occurredAt,
384
+ extensions: {
385
+ nativeKind: 'message',
386
+ fromAgent: entry.fromAgent,
387
+ provenance: { acquisition: GROKBOT_NATIVE_REPLICA_LOCATOR_CLASS },
388
+ },
389
+ };
390
+ }
391
+ function mapUserAttachment(raw, occurredAt) {
392
+ const parsed = UserAttachmentEntrySchema.safeParse(raw);
393
+ if (!parsed.success) {
394
+ throw malformedGrokbotSource('user-attachment entry', parsed.error);
395
+ }
396
+ const entry = parsed.data;
397
+ return {
398
+ nativeEventId: entry.id,
399
+ sequence: entry.seq,
400
+ kind: 'grokbot.user-attachment',
401
+ role: 'user',
402
+ text: `[user-attachment] ${entry.file_name} `
403
+ + `(${entry.width ?? '?'}x${entry.height ?? '?'}, ${entry.byteSize ?? '?'} bytes)`,
404
+ occurredAt,
405
+ extensions: {
406
+ nativeKind: 'user-attachment',
407
+ fileName: entry.file_name,
408
+ filePath: entry.file_path,
409
+ width: entry.width,
410
+ height: entry.height,
411
+ byteSize: entry.byteSize,
412
+ provenance: { acquisition: GROKBOT_NATIVE_REPLICA_LOCATOR_CLASS },
413
+ },
414
+ };
415
+ }
416
+ function mapEvent(raw, occurredAt) {
417
+ const parsed = EventEntrySchema.safeParse(raw);
418
+ if (!parsed.success) {
419
+ throw malformedGrokbotSource('event entry', parsed.error);
420
+ }
421
+ const entry = parsed.data;
422
+ const label = stringOrEmpty(entry.event.kind) || stringOrEmpty(entry.event.type) || 'event';
423
+ return {
424
+ nativeEventId: entry.id,
425
+ sequence: entry.seq,
426
+ kind: 'grokbot.event',
427
+ role: 'system',
428
+ text: `[event] ${label}`,
429
+ occurredAt,
430
+ extensions: {
431
+ nativeKind: 'event',
432
+ event: entry.event,
433
+ provenance: { acquisition: GROKBOT_NATIVE_REPLICA_LOCATOR_CLASS },
434
+ },
435
+ };
436
+ }
437
+ function mapNotice(raw, occurredAt) {
438
+ const parsed = NoticeEntrySchema.safeParse(raw);
439
+ if (!parsed.success) {
440
+ throw malformedGrokbotSource('notice entry', parsed.error);
441
+ }
442
+ const entry = parsed.data;
443
+ return {
444
+ nativeEventId: entry.id,
445
+ sequence: entry.seq,
446
+ kind: 'grokbot.notice',
447
+ role: 'system',
448
+ text: entry.text,
449
+ occurredAt,
450
+ extensions: {
451
+ nativeKind: 'notice',
452
+ provenance: { acquisition: GROKBOT_NATIVE_REPLICA_LOCATOR_CLASS },
453
+ },
454
+ };
455
+ }
456
+ function stringOrEmpty(value) {
457
+ return typeof value === 'string' ? value : '';
458
+ }
49
459
  //# sourceMappingURL=grokbot.js.map
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
3
- import { readBoundedJsonLines, streamBoundedJsonLines, } from '../readers.js';
2
+ import { schemaRejectionDetail, schemaIssueDetail, SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
3
+ import { streamBoundedJsonLines, } from '../readers.js';
4
4
  export const HERMES_ADAPTER_VERSION = '1.0.0';
5
5
  export const HERMES_EXPORT_SCHEMA_VERSION = '1.0.0';
6
6
  export const HERMES_NATIVE_SCHEMA_VERSION = '23.0.0';
@@ -73,6 +73,15 @@ export class HermesSessionAdapter {
73
73
  // Native exports and consistent snapshots require explicit selection.
74
74
  }
75
75
  async inspect(source) {
76
+ if (source.locatorClass === 'hermes-export-jsonl'
77
+ || source.locatorClass === 'hermes-consistent-snapshot-jsonl') {
78
+ const records = this.stream(source);
79
+ let result = await records.next();
80
+ while (!result.done)
81
+ result = await records.next();
82
+ if (result.value)
83
+ return result.value;
84
+ }
76
85
  const parsed = await this.readSource(source);
77
86
  return {
78
87
  sourceSchemaVersion: parsed.schemaVersion,
@@ -94,21 +103,23 @@ export class HermesSessionAdapter {
94
103
  let outputIndex = 0;
95
104
  let schema = null;
96
105
  let sawRecord = false;
106
+ let complete = true;
97
107
  for await (const line of input) {
98
108
  sawRecord = true;
99
109
  const parsed = ExportSchema.safeParse(line.value);
100
110
  if (!parsed.success) {
101
- throw new SessionContractError('MALFORMED_SOURCE', 'Hermes export is malformed');
111
+ throw new SessionContractError('MALFORMED_SOURCE', 'Hermes export is malformed', schemaRejectionDetail(parsed.error));
102
112
  }
113
+ complete &&= ended(parsed.data);
103
114
  const currentSchema = declaredSchema(parsed.data);
104
115
  if (schema && schema !== currentSchema) {
105
- throw new SessionContractError('SCHEMA_DRIFT', 'mixed Hermes export schemas');
116
+ throw new SessionContractError('SCHEMA_DRIFT', 'mixed Hermes export schemas', schemaIssueDetail('schemaVersion'));
106
117
  }
107
118
  schema = currentSchema;
108
119
  assertSupportedSchemaMajor(schema, 23);
109
120
  if (source.locatorClass === 'hermes-consistent-snapshot-jsonl'
110
121
  && parsed.data.snapshotConsistency !== 'sqlite-backup') {
111
- throw new SessionContractError('SCHEMA_DRIFT', 'Hermes snapshot lacks sqlite3.backup() consistency evidence');
122
+ throw new SessionContractError('SCHEMA_DRIFT', 'Hermes snapshot lacks sqlite3.backup() consistency evidence', schemaIssueDetail('snapshotConsistency'));
112
123
  }
113
124
  for (const record of normalizeSession(parsed.data, line, source.locatorClass)) {
114
125
  if (outputIndex++ >= start)
@@ -116,16 +127,21 @@ export class HermesSessionAdapter {
116
127
  }
117
128
  }
118
129
  if (!sawRecord)
119
- throw new SessionContractError('MALFORMED_SOURCE', 'Hermes source is empty');
130
+ throw new SessionContractError('MALFORMED_SOURCE', 'Hermes source is empty', schemaIssueDetail('<root>'));
120
131
  if (input.incompleteTail && source.locatorClass !== 'hermes-export-jsonl') {
121
132
  throw new SessionContractError('TRUNCATED_SOURCE', 'Hermes consistent snapshot is truncated');
122
133
  }
123
- return;
134
+ return {
135
+ sourceSchemaVersion: HERMES_EXPORT_SCHEMA_VERSION,
136
+ consistency: complete ? 'complete' : 'provisional',
137
+ operationalState: 'available',
138
+ };
124
139
  }
125
140
  const parsed = await this.readSource(source);
126
141
  const start = parseCursor(cursor?.value);
127
142
  for (const record of parsed.records.slice(start))
128
143
  yield record;
144
+ return undefined;
129
145
  }
130
146
  async readSource(source) {
131
147
  if (source.locatorClass === 'hermes-state-db') {
@@ -143,37 +159,19 @@ export class HermesSessionAdapter {
143
159
  value, sequence, byteOffset: sequence, byteLength: Buffer.byteLength(JSON.stringify(value)),
144
160
  }));
145
161
  }
146
- else if (source.locatorClass === 'hermes-export-jsonl'
147
- || source.locatorClass === 'hermes-consistent-snapshot-jsonl') {
148
- const result = await readBoundedJsonLines({
149
- selectedPath: source.locator,
150
- allowedRoots: source.authorizedScope.allowedRoots,
151
- }, {
152
- consistency: source.locatorClass === 'hermes-export-jsonl' ? 'provisional' : 'complete',
153
- limits: this.limits,
154
- });
155
- input = result.records;
156
- if (result.incompleteTail && source.locatorClass !== 'hermes-export-jsonl') {
157
- throw new SessionContractError('TRUNCATED_SOURCE', 'Hermes consistent snapshot is truncated');
158
- }
159
- }
160
162
  else {
161
163
  throw new SessionContractError('UNSUPPORTED_OPERATION', 'unsupported Hermes source class');
162
164
  }
163
165
  if (input.length === 0)
164
- throw new SessionContractError('MALFORMED_SOURCE', 'Hermes source is empty');
166
+ throw new SessionContractError('MALFORMED_SOURCE', 'Hermes source is empty', schemaIssueDetail('<root>'));
165
167
  const parsed = input.map((line) => {
166
168
  const result = ExportSchema.safeParse(line.value);
167
169
  if (!result.success)
168
- throw new SessionContractError('MALFORMED_SOURCE', 'Hermes export is malformed');
170
+ throw new SessionContractError('MALFORMED_SOURCE', 'Hermes export is malformed', schemaRejectionDetail(result.error));
169
171
  return { line, value: result.data };
170
172
  });
171
173
  const schemaVersion = declaredVersion(parsed.map(({ value }) => value));
172
174
  assertSupportedSchemaMajor(schemaVersion, 23);
173
- if (source.locatorClass === 'hermes-consistent-snapshot-jsonl'
174
- && parsed.some(({ value }) => value.snapshotConsistency !== 'sqlite-backup')) {
175
- throw new SessionContractError('SCHEMA_DRIFT', 'Hermes snapshot lacks sqlite3.backup() consistency evidence');
176
- }
177
175
  return {
178
176
  schemaVersion: HERMES_EXPORT_SCHEMA_VERSION,
179
177
  consistency: parsed.every(({ value }) => ended(value)) ? 'complete' : 'provisional',
@@ -291,7 +289,7 @@ function ended(value) {
291
289
  function declaredVersion(values) {
292
290
  const versions = new Set(values.map(declaredSchema));
293
291
  if (versions.size !== 1)
294
- throw new SessionContractError('SCHEMA_DRIFT', 'mixed Hermes export schemas');
292
+ throw new SessionContractError('SCHEMA_DRIFT', 'mixed Hermes export schemas', schemaIssueDetail('schemaVersion'));
295
293
  return [...versions][0];
296
294
  }
297
295
  function declaredSchema(value) {
@@ -330,7 +328,7 @@ function parseCursor(value) {
330
328
  if (!value)
331
329
  return 0;
332
330
  if (!/^\d+$/.test(value))
333
- throw new SessionContractError('SCHEMA_DRIFT', 'invalid Hermes cursor');
331
+ throw new SessionContractError('SCHEMA_DRIFT', 'invalid Hermes cursor', schemaIssueDetail('cursor'));
334
332
  return Number(value);
335
333
  }
336
334
  const SESSION_KEYS = new Set([