@aiwg/cli 2026.10.3 → 2026.10.5

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 (279) hide show
  1. package/README.md +2 -2
  2. package/agentic/code/providers/model-capabilities.v1.json +96 -4
  3. package/bin/aiwg.mjs +50 -27
  4. package/dist/src/agents/grounding/knowledge-base.js +54 -6
  5. package/dist/src/artifacts/browser-export.js +6 -0
  6. package/dist/src/artifacts/checksum-manifest.js +1 -0
  7. package/dist/src/artifacts/citation-parser.js +173 -92
  8. package/dist/src/artifacts/citation-table-schema.js +59 -0
  9. package/dist/src/artifacts/cli.js +69 -18
  10. package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
  11. package/dist/src/artifacts/corpus-tools/vision-extract.js +2 -0
  12. package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
  13. package/dist/src/artifacts/corpus-views/renderers.js +3 -3
  14. package/dist/src/artifacts/discovery-eval.js +6 -4
  15. package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
  16. package/dist/src/artifacts/fortemi-core-sync.js +166 -40
  17. package/dist/src/artifacts/framework-capabilities.js +171 -0
  18. package/dist/src/artifacts/framework-index-paths.js +73 -0
  19. package/dist/src/artifacts/framework-source-digest.js +75 -0
  20. package/dist/src/artifacts/index-builder.js +286 -131
  21. package/dist/src/artifacts/index-files.js +132 -18
  22. package/dist/src/artifacts/index-preparation.js +130 -0
  23. package/dist/src/artifacts/index-refresh.js +591 -0
  24. package/dist/src/artifacts/index-worker.mjs +11 -0
  25. package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
  26. package/dist/src/artifacts/prebuilt-deployment.js +46 -0
  27. package/dist/src/artifacts/prebuilt-relations.js +80 -0
  28. package/dist/src/artifacts/query-engine.js +410 -88
  29. package/dist/src/artifacts/relations-builder.js +168 -0
  30. package/dist/src/artifacts/relations.js +148 -0
  31. package/dist/src/artifacts/stats.js +5 -2
  32. package/dist/src/artifacts/types.js +30 -10
  33. package/dist/src/artifacts/watcher.js +1 -1
  34. package/dist/src/auth/credential-store.js +2 -1
  35. package/dist/src/catalog/cli.d.mts +5 -0
  36. package/dist/src/channel/manager.d.mts +20 -0
  37. package/dist/src/channel/manager.mjs +19 -2
  38. package/dist/src/cli/command-log.js +2 -3
  39. package/dist/src/cli/handlers/artifacts.js +3 -0
  40. package/dist/src/cli/handlers/auth.js +3 -0
  41. package/dist/src/cli/handlers/decision.js +19 -12
  42. package/dist/src/cli/handlers/diagnose.js +4 -3
  43. package/dist/src/cli/handlers/effect.js +4 -2
  44. package/dist/src/cli/handlers/evidence.js +3 -0
  45. package/dist/src/cli/handlers/help.js +1 -0
  46. package/dist/src/cli/handlers/index.js +3 -1
  47. package/dist/src/cli/handlers/init.js +4 -1
  48. package/dist/src/cli/handlers/lint.js +5 -0
  49. package/dist/src/cli/handlers/models.js +104 -8
  50. package/dist/src/cli/handlers/ralph-launcher.js +2 -0
  51. package/dist/src/cli/handlers/refresh.js +12 -0
  52. package/dist/src/cli/handlers/resource-versions.js +3 -0
  53. package/dist/src/cli/handlers/rules.js +195 -0
  54. package/dist/src/cli/handlers/run.js +4 -0
  55. package/dist/src/cli/handlers/runtime-info.js +1 -2
  56. package/dist/src/cli/handlers/script-runner.js +3 -2
  57. package/dist/src/cli/handlers/session.js +19 -9
  58. package/dist/src/cli/handlers/sessions.js +137 -43
  59. package/dist/src/cli/handlers/setup-manifest.js +39 -44
  60. package/dist/src/cli/handlers/setup.js +41 -0
  61. package/dist/src/cli/handlers/steward.js +11 -0
  62. package/dist/src/cli/handlers/subcommands.js +57 -2
  63. package/dist/src/cli/handlers/uhp.js +3 -0
  64. package/dist/src/cli/handlers/use.js +294 -55
  65. package/dist/src/cli/handlers/utilities.js +26 -10
  66. package/dist/src/cli/handlers/version.js +8 -0
  67. package/dist/src/cli/router.js +15 -1
  68. package/dist/src/cli/services/deployment-verification.js +19 -9
  69. package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
  70. package/dist/src/cli/skill-usage.js +2 -3
  71. package/dist/src/config/aiwg-config.js +87 -37
  72. package/dist/src/config/cli.js +111 -5
  73. package/dist/src/config/decision-config.js +106 -0
  74. package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
  75. package/dist/src/config/gitignore.js +50 -0
  76. package/dist/src/config/index-graph-overrides.js +36 -0
  77. package/dist/src/config/project-artifacts-health.d.mts +37 -0
  78. package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
  79. package/dist/src/config/user-config-dir.d.mts +9 -0
  80. package/dist/src/config/user-config.js +2 -1
  81. package/dist/src/config/user-registry.js +23 -15
  82. package/dist/src/config/user-state.js +7 -0
  83. package/dist/src/decision/ask.js +203 -43
  84. package/dist/src/decision/context-live-qualification.js +2 -2
  85. package/dist/src/decision/driver.js +13 -8
  86. package/dist/src/decision/ensemble/aggregate.js +23 -10
  87. package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
  88. package/dist/src/decision/entry.js +2 -15
  89. package/dist/src/decision/graph-live-qualification.js +1 -1
  90. package/dist/src/decision/graph-run.js +9 -0
  91. package/dist/src/decision/heldout/collector.js +5 -13
  92. package/dist/src/decision/heldout/contract.js +30 -7
  93. package/dist/src/decision/jev-credentials.js +15 -1
  94. package/dist/src/decision/ledger.js +131 -28
  95. package/dist/src/decision/offload-activation.js +137 -0
  96. package/dist/src/decision/offload.js +550 -0
  97. package/dist/src/decision/qualification/load.js +4 -6
  98. package/dist/src/decision/savings.js +28 -0
  99. package/dist/src/effects/types.js +5 -1
  100. package/dist/src/effects/verifiers/git.js +2 -1
  101. package/dist/src/effects/verifiers/tracker.js +26 -14
  102. package/dist/src/extensions/commands/definitions.js +19 -0
  103. package/dist/src/extensions/project-local-remove.js +36 -1
  104. package/dist/src/gates/providers/loader.js +61 -17
  105. package/dist/src/installation/manager-command.d.mts +18 -0
  106. package/dist/src/installation/manager-command.mjs +14 -2
  107. package/dist/src/installation/manager.d.mts +49 -0
  108. package/dist/src/installation/manager.mjs +78 -3
  109. package/dist/src/issues/cli.js +4 -4
  110. package/dist/src/issues/local.js +24 -5
  111. package/dist/src/jobs/runner.js +1 -1
  112. package/dist/src/lint/cli.js +27 -1
  113. package/dist/src/lint/loader.js +2 -1
  114. package/dist/src/lint/reporters.js +25 -1
  115. package/dist/src/lint/rule-activation.js +43 -0
  116. package/dist/src/lint/runner.js +137 -62
  117. package/dist/src/mcp/adapters/codex-runtime.js +7 -1
  118. package/dist/src/mcp/claude-install.d.mts +4 -0
  119. package/dist/src/mcp/claude-install.mjs +41 -0
  120. package/dist/src/mcp/cli.d.mts +1 -0
  121. package/dist/src/mcp/cli.mjs +36 -11
  122. package/dist/src/mcp/config-file.d.mts +28 -0
  123. package/dist/src/mcp/config-layers.d.mts +36 -0
  124. package/dist/src/mcp/credentials.d.mts +51 -0
  125. package/dist/src/mcp/grok-build-config.d.mts +6 -0
  126. package/dist/src/mcp/grok-build-config.mjs +1 -1
  127. package/dist/src/mcp/helpers.mjs +3 -2
  128. package/dist/src/mcp/muse-native-config.d.mts +13 -0
  129. package/dist/src/mcp/muse-native-config.mjs +1 -0
  130. package/dist/src/mcp/omp-config.d.mts +4 -0
  131. package/dist/src/mcp/registry.js +3 -93
  132. package/dist/src/mcp/registry.mjs +3 -87
  133. package/dist/src/mcp/server-config.d.mts +3 -0
  134. package/dist/src/mcp/server-config.mjs +89 -0
  135. package/dist/src/mcp/server.mjs +37 -375
  136. package/dist/src/mcp/test-simple.mjs +2 -1
  137. package/dist/src/mcp/toml-editor.d.mts +4 -0
  138. package/dist/src/mcp/toml-string.d.mts +1 -0
  139. package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
  140. package/dist/src/mcp/tool-filters.d.mts +46 -0
  141. package/dist/src/mcp/tools/decision.mjs +12 -11
  142. package/dist/src/mcp/tools/discovery.mjs +62 -55
  143. package/dist/src/mcp/tools/orchestration.mjs +12 -2
  144. package/dist/src/mcp/tools/worker-invoke.mjs +1340 -23
  145. package/dist/src/mcp/tools/worker-shell-control.mjs +52 -21
  146. package/dist/src/mcp/tools/worker-stdin-runtime.mjs +3 -3
  147. package/dist/src/mission-protocol/codecs.js +8 -0
  148. package/dist/src/models/config-loader.js +7 -2
  149. package/dist/src/models/model-capabilities.v1.json +96 -4
  150. package/dist/src/models/model-discovery.js +76 -11
  151. package/dist/src/models/provider-policy.js +101 -7
  152. package/dist/src/models/qualification-digest.d.mts +2 -0
  153. package/dist/src/models/resolver.js +12 -10
  154. package/dist/src/models/worker-direct-readiness.js +65 -44
  155. package/dist/src/models/worker-dispatch-offer.js +3 -3
  156. package/dist/src/models/worker-health-readiness.js +16 -3
  157. package/dist/src/models/worker-installation.d.mts +39 -0
  158. package/dist/src/models/worker-installation.mjs +135 -28
  159. package/dist/src/process/bounded-child-process.d.mts +84 -0
  160. package/dist/src/process/bounded-child-process.mjs +43 -0
  161. package/dist/src/process/cli-command.d.mts +16 -0
  162. package/dist/src/process/signal-child.d.mts +6 -0
  163. package/dist/src/process/signal-child.mjs +7 -0
  164. package/dist/src/providers/deployed-skill-roots.js +20 -0
  165. package/dist/src/providers/grok-build-acp.js +3 -2
  166. package/dist/src/providers/grok-build-headless.js +12 -4
  167. package/dist/src/providers/grok-build-inspect.js +1 -0
  168. package/dist/src/providers/omp-agent.d.mts +8 -0
  169. package/dist/src/providers/omp-diagnostics.d.mts +1 -0
  170. package/dist/src/providers/omp-paths.d.mts +13 -0
  171. package/dist/src/providers/skill-ownership.d.mts +9 -0
  172. package/dist/src/providers/skill-ownership.mjs +88 -0
  173. package/dist/src/rules/activation.d.mts +21 -0
  174. package/dist/src/rules/activation.mjs +62 -0
  175. package/dist/src/rules/inline-budget.d.mts +26 -0
  176. package/dist/src/rules/inline-budget.mjs +66 -0
  177. package/dist/src/rules/inventory.js +129 -0
  178. package/dist/src/rules/pin-state.js +73 -0
  179. package/dist/src/rules/pins.d.mts +14 -0
  180. package/dist/src/rules/pins.mjs +112 -0
  181. package/dist/src/rules/rule-frontmatter.schema.json +197 -0
  182. package/dist/src/serve/mission-conductor.js +38 -12
  183. package/dist/src/serve/worker-invocation.js +30 -10
  184. package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
  185. package/dist/src/sessions/adapter-registry.js +23 -19
  186. package/dist/src/sessions/adapters/claude.js +39 -54
  187. package/dist/src/sessions/adapters/codex.js +95 -67
  188. package/dist/src/sessions/adapters/copilot.js +4 -4
  189. package/dist/src/sessions/adapters/cursor.js +40 -57
  190. package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
  191. package/dist/src/sessions/adapters/factory.js +31 -27
  192. package/dist/src/sessions/adapters/generic.js +10 -10
  193. package/dist/src/sessions/adapters/grok-build.js +11 -11
  194. package/dist/src/sessions/adapters/grokbot.js +24 -13
  195. package/dist/src/sessions/adapters/hermes.js +27 -29
  196. package/dist/src/sessions/adapters/muse-workspace-scan.js +31 -11
  197. package/dist/src/sessions/adapters/muse.js +150 -93
  198. package/dist/src/sessions/adapters/omp.js +28 -14
  199. package/dist/src/sessions/adapters/openclaw.js +89 -79
  200. package/dist/src/sessions/adapters/opencode.js +27 -24
  201. package/dist/src/sessions/adapters/openhuman.js +18 -60
  202. package/dist/src/sessions/adapters/pi.js +29 -14
  203. package/dist/src/sessions/adapters/warp.js +3 -3
  204. package/dist/src/sessions/adapters/windsurf.js +19 -48
  205. package/dist/src/sessions/batch-contracts.js +16 -8
  206. package/dist/src/sessions/batch-import.js +252 -12
  207. package/dist/src/sessions/contracts.js +50 -3
  208. package/dist/src/sessions/coverage-preflight.js +27 -2
  209. package/dist/src/sessions/export-plan.js +4 -3
  210. package/dist/src/sessions/import-exclusions.js +72 -0
  211. package/dist/src/sessions/import-lease.js +34 -18
  212. package/dist/src/sessions/import-limits.js +37 -0
  213. package/dist/src/sessions/importer.js +60 -38
  214. package/dist/src/sessions/lifecycle.js +45 -0
  215. package/dist/src/sessions/policy.js +145 -26
  216. package/dist/src/sessions/readers.js +106 -53
  217. package/dist/src/sessions/refresh.js +105 -0
  218. package/dist/src/sessions/repository.js +151 -54
  219. package/dist/src/sessions/workspace-discovery.js +292 -60
  220. package/dist/src/sessions/workspace-matching.js +107 -0
  221. package/dist/src/skills/importer.js +1 -0
  222. package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
  223. package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
  224. package/dist/src/smiths/context-pipeline/finalization.js +6 -5
  225. package/dist/src/smiths/context-pipeline/generator.js +21 -4
  226. package/dist/src/smiths/context-pipeline/parallelism-section.js +7 -15
  227. package/dist/src/smiths/mcpsmith/generator.js +1 -1
  228. package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
  229. package/dist/src/smiths/toolsmith/runtime-discovery.mjs +76 -18
  230. package/dist/src/storage/backends/fs.js +18 -76
  231. package/dist/src/storage/backends/obsidian.js +1 -0
  232. package/dist/src/tracker/forge-http.js +2 -1
  233. package/dist/src/update/checker.d.mts +2 -0
  234. package/dist/src/update/service.d.mts +22 -0
  235. package/dist/src/utils/contained-filesystem.d.mts +37 -0
  236. package/dist/src/utils/contained-filesystem.mjs +683 -0
  237. package/dist/src/utils/temporary-directory.js +36 -0
  238. package/dist/src/writing/example-generator.js +1 -1
  239. package/package.json +15 -2
  240. package/schemas/models/model-catalog.v1.schema.json +1 -1
  241. package/schemas/models/provider-model-capabilities.v1.schema.json +6 -0
  242. package/tools/_resolve-impl.mjs +13 -15
  243. package/tools/agents/deploy-agents.mjs +49 -21
  244. package/tools/agents/providers/base.mjs +104 -134
  245. package/tools/agents/providers/claude.mjs +8 -24
  246. package/tools/agents/providers/codex.mjs +18 -11
  247. package/tools/agents/providers/cursor.mjs +19 -18
  248. package/tools/agents/providers/grok-build.mjs +3 -1
  249. package/tools/agents/providers/hermes.mjs +57 -47
  250. package/tools/agents/providers/openclaw.mjs +9 -4
  251. package/tools/agents/providers/pi.mjs +5 -0
  252. package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
  253. package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
  254. package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
  255. package/tools/agents/providers/rule-delivery.mjs +1270 -0
  256. package/tools/agents/providers/warp.mjs +20 -26
  257. package/tools/cli/decision-offload-report.mjs +40 -0
  258. package/tools/cli/workspace-status.mjs +862 -0
  259. package/tools/plugin/package-plugins.mjs +186 -8
  260. package/tools/plugin/plugin-links.mjs +114 -0
  261. package/tools/providers/antigravity-transport.mjs +3 -1
  262. package/tools/providers/deepseek-harness-transport.mjs +6 -4
  263. package/tools/providers/omp-transport.mjs +4 -2
  264. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
  265. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
  266. package/tools/ralph-external/lib/omp-adapter.mjs +1 -1
  267. package/tools/ralph-external/lib/pi-adapter.mjs +0 -4
  268. package/tools/ralph-external/lib/provider-adapter.mjs +59 -1
  269. package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
  270. package/tools/ralph-external/lib/session-environment.mjs +28 -0
  271. package/tools/ralph-external/lib/systemd-client.mjs +49 -0
  272. package/tools/ralph-external/lib/worker-health.mjs +1 -1
  273. package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
  274. package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
  275. package/tools/ralph-external/process-monitor.mjs +1017 -0
  276. package/tools/ralph-external/session-launcher.mjs +5057 -0
  277. package/tools/scripts/resource-preflight.mjs +541 -0
  278. package/tools/skills/deploy-skills-codex.mjs +2 -1
  279. package/vscode-extension/schemas/aiwg.config.v1.json +1194 -231
@@ -1,5 +1,5 @@
1
1
  import { open } from 'node:fs/promises';
2
- import { SessionContractError } from '../contracts.js';
2
+ import { schemaIssueDetail, SessionContractError } from '../contracts.js';
3
3
  import { authorizeSourceFile } from '../policy.js';
4
4
  // Discovery inspects metadata, not an entire import. Bound allocation before
5
5
  // assembling a line (readline would buffer an arbitrarily large single line).
@@ -9,18 +9,31 @@ export const MUSE_WORKSPACE_SCAN_LIMITS = Object.freeze({
9
9
  maxTotalBytes: 32 * 1024 * 1024,
10
10
  maxNestingDepth: 64,
11
11
  });
12
- export async function* museWorkspaceScanLines(authorization, limits, progress) {
12
+ export const MUSE_WORKSPACE_TAIL_BYTES = 1024 * 1024;
13
+ export class MuseWorkspaceLimitError extends SessionContractError {
14
+ limitName;
15
+ limitValue;
16
+ constructor(limitName, limitValue, observed) {
17
+ super('RESOURCE_LIMIT_EXCEEDED', `Muse workspace inspection exceeded ${limitName}=${limitValue}`, { limit: limitName, configured: limitValue, observed });
18
+ this.limitName = limitName;
19
+ this.limitValue = limitValue;
20
+ }
21
+ }
22
+ export async function* museWorkspaceScanLines(authorization, limits, progress, tailBytes) {
13
23
  const allowed = await authorizeSourceFile(authorization);
14
24
  const handle = await open(allowed.canonicalPath, 'r');
15
25
  try {
16
26
  const buffer = Buffer.allocUnsafe(64 * 1024);
17
- let position = 0;
27
+ let position = tailBytes === undefined ? 0 : Math.max(0, allowed.size - tailBytes);
28
+ // A tail window may start inside a record (or a UTF-8 code point).
29
+ // Discard that fragment before parsing any complete JSONL records.
30
+ let skipPartialLine = position > 0;
18
31
  let parts = [];
19
32
  let length = 0;
20
33
  while (position < allowed.size) {
21
34
  const remaining = limits.maxTotalBytes - progress.bytesInspected;
22
35
  if (remaining <= 0)
23
- throw scanLimit();
36
+ throw scanLimit('maxTotalBytes', limits.maxTotalBytes, allowed.size);
24
37
  const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, allowed.size - position, remaining), position);
25
38
  if (bytesRead === 0) {
26
39
  throw new SessionContractError('TRUNCATED_SOURCE', 'Muse source changed during inspection');
@@ -31,8 +44,15 @@ export async function* museWorkspaceScanLines(authorization, limits, progress) {
31
44
  const newline = buffer.indexOf(10, start);
32
45
  const end = newline >= 0 && newline < bytesRead ? newline + 1 : bytesRead;
33
46
  const segment = buffer.subarray(start, end);
47
+ if (skipPartialLine) {
48
+ progress.bytesInspected += segment.length;
49
+ start = end;
50
+ if (newline >= 0 && newline < bytesRead)
51
+ skipPartialLine = false;
52
+ continue;
53
+ }
34
54
  if (length + segment.length > limits.maxRecordBytes)
35
- throw scanLimit();
55
+ throw scanLimit('maxRecordBytes', limits.maxRecordBytes, length + segment.length);
36
56
  parts.push(Buffer.from(segment));
37
57
  length += segment.length;
38
58
  progress.bytesInspected += segment.length;
@@ -43,7 +63,7 @@ export async function* museWorkspaceScanLines(authorization, limits, progress) {
43
63
  length = 0;
44
64
  if (line.trim() !== '') {
45
65
  if (progress.recordsInspected >= limits.maxRecords)
46
- throw scanLimit();
66
+ throw scanLimit('maxRecords', limits.maxRecords, progress.recordsInspected + 1);
47
67
  const value = parseMuseScanJson(line, limits.maxNestingDepth);
48
68
  progress.recordsInspected += 1;
49
69
  yield value;
@@ -55,7 +75,7 @@ export async function* museWorkspaceScanLines(authorization, limits, progress) {
55
75
  const line = Buffer.concat(parts, length).toString('utf8');
56
76
  if (line.trim() !== '') {
57
77
  if (progress.recordsInspected >= limits.maxRecords)
58
- throw scanLimit();
78
+ throw scanLimit('maxRecords', limits.maxRecords, progress.recordsInspected + 1);
59
79
  let value;
60
80
  try {
61
81
  value = parseMuseScanJson(line, limits.maxNestingDepth);
@@ -85,7 +105,7 @@ export function parseMuseScanJson(line, maxDepth) {
85
105
  value = JSON.parse(line);
86
106
  }
87
107
  catch {
88
- throw new SessionContractError('MALFORMED_SOURCE', 'Muse inspection encountered malformed JSON');
108
+ throw new SessionContractError('MALFORMED_SOURCE', 'Muse inspection encountered malformed JSON', schemaIssueDetail('<root>', 'invalid_json'));
89
109
  }
90
110
  // Check depth iteratively, including decoded retained children, before zod
91
111
  // validation. Deep input must produce a limit diagnostic, not stack overflow.
@@ -93,7 +113,7 @@ export function parseMuseScanJson(line, maxDepth) {
93
113
  while (pending.length > 0) {
94
114
  const current = pending.pop();
95
115
  if (current.depth > maxDepth)
96
- throw scanLimit();
116
+ throw scanLimit('maxNestingDepth', maxDepth, current.depth);
97
117
  if (current.value !== null && typeof current.value === 'object') {
98
118
  for (const child of Object.values(current.value)) {
99
119
  pending.push({ value: child, depth: current.depth + 1 });
@@ -102,7 +122,7 @@ export function parseMuseScanJson(line, maxDepth) {
102
122
  }
103
123
  return value;
104
124
  }
105
- function scanLimit() {
106
- return new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'bounded Muse workspace inspection limit exceeded');
125
+ function scanLimit(limit, configured, observed) {
126
+ return new MuseWorkspaceLimitError(limit, configured, observed);
107
127
  }
108
128
  //# sourceMappingURL=muse-workspace-scan.js.map
@@ -42,9 +42,10 @@
42
42
  import { opendir } from 'node:fs/promises';
43
43
  import { resolve } from 'node:path';
44
44
  import { z } from 'zod';
45
- import { SessionContractError, SessionErrorCodeSchema, assertSupportedSchemaMajor, } from '../contracts.js';
46
- import { readBoundedJson, readBoundedJsonLines, } from '../readers.js';
47
- import { MUSE_WORKSPACE_SCAN_LIMITS, museWorkspaceScanLines, parseMuseScanJson, } from './muse-workspace-scan.js';
45
+ import { matchSessionWorkspace, WORKSPACE_MATCH_KINDS } from '../workspace-matching.js';
46
+ import { schemaRejectionDetail, schemaIssueDetail, SessionContractError, SessionErrorCodeSchema, SessionRejectionDetailSchema, assertSupportedSchemaMajor, } from '../contracts.js';
47
+ import { readBoundedJson, streamBoundedJsonLines, } from '../readers.js';
48
+ import { MUSE_WORKSPACE_SCAN_LIMITS, MUSE_WORKSPACE_TAIL_BYTES, MuseWorkspaceLimitError, museWorkspaceScanLines, parseMuseScanJson, } from './muse-workspace-scan.js';
48
49
  export const MUSE_ADAPTER_VERSION = '1.0.0';
49
50
  export const MUSE_EXPORT_SCHEMA_VERSION = '1.0.0';
50
51
  export const MUSE_EXPORT_SCHEMA_MAJOR = 1;
@@ -173,7 +174,7 @@ export class MuseSessionAdapter {
173
174
  for (const root of [...scope.allowedRoots].sort()) {
174
175
  for await (const locator of museNativeSessionLogs(resolve(root))) {
175
176
  if (++count > this.maxFiles) {
176
- throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Muse native session discovery exceeded the authorized file limit');
177
+ throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Muse native session discovery exceeded the authorized file limit', { limit: 'maxFiles', configured: this.maxFiles, observed: count });
177
178
  }
178
179
  yield { provider: 'muse', locator, locatorClass: MUSE_NATIVE_LOCATOR_CLASS };
179
180
  }
@@ -203,7 +204,7 @@ export class MuseSessionAdapter {
203
204
  const native = await this.readNative(source);
204
205
  const start = parseEventCursor(cursor?.value);
205
206
  let outputIndex = 0;
206
- for (const record of native.records) {
207
+ for await (const record of this.readNativeRecords(source)) {
207
208
  const position = outputIndex;
208
209
  outputIndex += 1;
209
210
  if (position < start)
@@ -254,13 +255,13 @@ export class MuseSessionAdapter {
254
255
  }
255
256
  catch (error) {
256
257
  if (error instanceof SessionContractError && error.code === 'SCHEMA_DRIFT') {
257
- throw new SessionContractError('MALFORMED_SOURCE', 'muse export document is not valid JSON');
258
+ throw new SessionContractError('MALFORMED_SOURCE', 'muse export document is not valid JSON', error.detail);
258
259
  }
259
260
  throw error;
260
261
  }
261
262
  const parsed = MuseTrajectorySchema.safeParse(value);
262
263
  if (!parsed.success) {
263
- throw new SessionContractError('MALFORMED_SOURCE', 'input is not a declared muse export trajectory document');
264
+ throw new SessionContractError('MALFORMED_SOURCE', 'input is not a declared muse export trajectory document', schemaRejectionDetail(parsed.error));
264
265
  }
265
266
  assertSupportedSchemaMajor(`${parsed.data.export_schema_version}.0.0`, MUSE_EXPORT_SCHEMA_MAJOR);
266
267
  return { trajectory: parsed.data, bytesRead };
@@ -333,80 +334,89 @@ export class MuseSessionAdapter {
333
334
  };
334
335
  }
335
336
  async readNative(source) {
336
- this.assertSupportedLocator(source);
337
- let input;
337
+ const records = this.readNativeRecords(source);
338
+ let result = await records.next();
339
+ while (!result.done)
340
+ result = await records.next();
341
+ return result.value;
342
+ }
343
+ async *readNativeRecords(source) {
338
344
  try {
339
- input = await readBoundedJsonLines({ selectedPath: source.locator, allowedRoots: source.authorizedScope.allowedRoots }, { consistency: 'complete', limits: this.limits });
340
- }
341
- catch (error) {
342
- if (error instanceof SessionContractError && error.code === 'SCHEMA_DRIFT') {
343
- throw new SessionContractError('MALFORMED_SOURCE', 'Muse native session log contains malformed JSONL');
344
- }
345
- throw error;
346
- }
347
- const diagnostics = {};
348
- const records = [];
349
- let fallbackSessionId = nativeSessionIdFromLocator(source.locator) ?? source.sourceId;
350
- let complete = false;
351
- for (const line of input.records) {
352
- const value = line.value;
353
- if (isOmissionMarker(value)) {
354
- diagnostics.omitted_records = (diagnostics.omitted_records ?? 0) + 1;
355
- if (asObject(value).retained_marker === 'omitted_live_only') {
356
- diagnostics.omitted_live_only = (diagnostics.omitted_live_only ?? 0) + 1;
357
- }
358
- continue;
359
- }
360
- const retained = MuseRetainedFrameSchema.safeParse(value);
361
- if (retained.success) {
362
- diagnostics.retained_frames = (diagnostics.retained_frames ?? 0) + 1;
363
- for (const child of retained.data.children) {
364
- let decoded;
365
- try {
366
- decoded = JSON.parse(child.record_json);
345
+ this.assertSupportedLocator(source);
346
+ const input = await streamBoundedJsonLines({ selectedPath: source.locator, allowedRoots: source.authorizedScope.allowedRoots }, { consistency: 'complete', limits: this.limits });
347
+ const diagnostics = {};
348
+ let records = 0;
349
+ let fallbackSessionId = nativeSessionIdFromLocator(source.locator) ?? source.sourceId;
350
+ let complete = false;
351
+ for await (const line of input) {
352
+ const value = line.value;
353
+ if (isOmissionMarker(value)) {
354
+ diagnostics.omitted_records = (diagnostics.omitted_records ?? 0) + 1;
355
+ if (asObject(value).retained_marker === 'omitted_live_only') {
356
+ diagnostics.omitted_live_only = (diagnostics.omitted_live_only ?? 0) + 1;
367
357
  }
368
- catch {
369
- throw new SessionContractError('MALFORMED_SOURCE', 'Muse retained frame child record is malformed JSON');
370
- }
371
- const event = recordEventFromEnvelope(decoded);
372
- if (!event) {
373
- throw new SessionContractError('MALFORMED_SOURCE', 'Muse retained frame child record is malformed');
358
+ continue;
359
+ }
360
+ const retained = MuseRetainedFrameSchema.safeParse(value);
361
+ if (retained.success) {
362
+ diagnostics.retained_frames = (diagnostics.retained_frames ?? 0) + 1;
363
+ for (const child of retained.data.children) {
364
+ let decoded;
365
+ try {
366
+ decoded = JSON.parse(child.record_json);
367
+ }
368
+ catch {
369
+ throw new SessionContractError('MALFORMED_SOURCE', 'Muse retained frame child record is malformed JSON', schemaIssueDetail('children.record_json', 'invalid_json'));
370
+ }
371
+ const envelope = MuseEnvelopeSchema.safeParse(decoded);
372
+ if (!envelope.success) {
373
+ throw new SessionContractError('MALFORMED_SOURCE', 'Muse retained frame child record is malformed', schemaRejectionDetail(envelope.error));
374
+ }
375
+ const event = { kind: 'record', envelope: envelope.data };
376
+ fallbackSessionId = streamSessionIdOf(event.envelope) ?? fallbackSessionId;
377
+ complete ||= event.envelope.payload_type === 'session.end';
378
+ diagnostics.retained_frame_children = (diagnostics.retained_frame_children ?? 0) + 1;
379
+ records += 1;
380
+ yield {
381
+ event,
382
+ line,
383
+ retainedFrame: retained.data.retained_frame,
384
+ retainedChildIndex: child.child_index,
385
+ };
374
386
  }
375
- fallbackSessionId = streamSessionIdOf(event.envelope) ?? fallbackSessionId;
376
- complete ||= event.envelope.payload_type === 'session.end';
377
- diagnostics.retained_frame_children = (diagnostics.retained_frame_children ?? 0) + 1;
378
- records.push({
379
- event,
380
- line,
381
- retainedFrame: retained.data.retained_frame,
382
- retainedChildIndex: child.child_index,
383
- });
387
+ continue;
384
388
  }
385
- continue;
389
+ const envelope = MuseEnvelopeSchema.safeParse(value);
390
+ if (!envelope.success) {
391
+ throw new SessionContractError('MALFORMED_SOURCE', 'Muse native session log line is not a supported record shape', schemaRejectionDetail('retained_frame' in asObject(value) ? retained.error : envelope.error));
392
+ }
393
+ const event = { kind: 'record', envelope: envelope.data };
394
+ fallbackSessionId = streamSessionIdOf(event.envelope) ?? fallbackSessionId;
395
+ complete ||= event.envelope.payload_type === 'session.end';
396
+ records += 1;
397
+ yield { event, line };
386
398
  }
387
- const event = recordEventFromEnvelope(value);
388
- if (!event) {
389
- throw new SessionContractError('MALFORMED_SOURCE', 'Muse native session log line is not a supported record shape');
399
+ if (records === 0) {
400
+ throw new SessionContractError('MALFORMED_SOURCE', 'Muse native session log contains no record envelopes', schemaIssueDetail('<root>'));
390
401
  }
391
- fallbackSessionId = streamSessionIdOf(event.envelope) ?? fallbackSessionId;
392
- complete ||= event.envelope.payload_type === 'session.end';
393
- records.push({ event, line });
402
+ return {
403
+ complete,
404
+ context: {
405
+ locatorClass: MUSE_NATIVE_LOCATOR_CLASS,
406
+ sourceLabel: 'muse native session log',
407
+ bytesRead: input.bytesRead,
408
+ fallbackSessionId,
409
+ spawns: new Map(),
410
+ diagnostics,
411
+ },
412
+ };
394
413
  }
395
- if (records.length === 0) {
396
- throw new SessionContractError('MALFORMED_SOURCE', 'Muse native session log contains no record envelopes');
414
+ catch (error) {
415
+ if (error instanceof SessionContractError && error.code === 'SCHEMA_DRIFT') {
416
+ throw new SessionContractError('MALFORMED_SOURCE', 'Muse native session log contains malformed JSONL', error.detail);
417
+ }
418
+ throw error;
397
419
  }
398
- return {
399
- records,
400
- complete,
401
- context: {
402
- locatorClass: MUSE_NATIVE_LOCATOR_CLASS,
403
- sourceLabel: 'muse native session log',
404
- bytesRead: input.bytesRead,
405
- fallbackSessionId,
406
- spawns: new Map(),
407
- diagnostics,
408
- },
409
- };
410
420
  }
411
421
  assertSupportedLocator(source) {
412
422
  if (source.locatorClass !== MUSE_LOCATOR_CLASS && source.locatorClass !== MUSE_NATIVE_LOCATOR_CLASS) {
@@ -500,10 +510,18 @@ async function* museNativeSessionLogs(root) {
500
510
  }
501
511
  export const MuseWorkspaceInspectionSchema = z.object({
502
512
  outcome: z.enum(['matched', 'mismatch', 'inconclusive']),
513
+ attribution: z.enum(['matched', 'mismatch', 'unattributed']),
514
+ matchKind: z.enum(WORKSPACE_MATCH_KINDS).optional(),
503
515
  reasonCode: z.string().nullable(),
504
516
  recordsInspected: z.number().int().nonnegative(),
505
517
  bytesInspected: z.number().int().nonnegative(),
506
518
  completeInspection: z.boolean(),
519
+ limit: z.object({
520
+ name: z.enum(['maxRecords', 'maxRecordBytes', 'maxTotalBytes', 'maxNestingDepth']),
521
+ value: z.number().int().positive(),
522
+ }).optional(),
523
+ fallback: z.literal('bounded-tail').optional(),
524
+ detail: SessionRejectionDetailSchema.optional(),
507
525
  });
508
526
  /** Positive metadata evidence selects a source; import validates its full history. */
509
527
  export async function inspectMuseNativeWorkspace(source, workspacePath, limitsInput) {
@@ -511,25 +529,35 @@ export async function inspectMuseNativeWorkspace(source, workspacePath, limitsIn
511
529
  const progress = { recordsInspected: 0, bytesInspected: 0 };
512
530
  let sawWorkspaceEvidence = false;
513
531
  let retainedChildren = 0;
514
- const receipt = (outcome, reasonCode, completeInspection = false) => ({ outcome, reasonCode, ...progress, completeInspection });
532
+ let limit;
533
+ let fallback;
534
+ const authorization = { selectedPath: source.locator, allowedRoots: source.authorizedScope.allowedRoots };
535
+ const receipt = (outcome, reasonCode, completeInspection = false) => ({
536
+ outcome, attribution: outcome === 'inconclusive' ? 'unattributed' : outcome,
537
+ reasonCode, ...progress, completeInspection,
538
+ ...(limit ? { limit } : {}), ...(fallback ? { fallback } : {}),
539
+ });
540
+ const envelopesFromLine = (value) => nativeEnvelopesFromLine(value, limits, (count) => {
541
+ retainedChildren += count;
542
+ if (retainedChildren > limits.maxRecords) {
543
+ throw new MuseWorkspaceLimitError('maxRecords', limits.maxRecords, retainedChildren);
544
+ }
545
+ });
515
546
  try {
516
- if (Object.values(limits).some((limit) => !Number.isSafeInteger(limit) || limit <= 0)) {
547
+ if ([limits.maxRecords, limits.maxRecordBytes, limits.maxTotalBytes, limits.maxNestingDepth]
548
+ .some((limit) => !Number.isSafeInteger(limit) || limit <= 0)) {
517
549
  throw new SessionContractError('INVALID_ARGUMENT', 'Muse inspection limits must be positive safe integers');
518
550
  }
519
- for await (const value of museWorkspaceScanLines({ selectedPath: source.locator, allowedRoots: source.authorizedScope.allowedRoots }, limits, progress)) {
520
- const envelopes = nativeEnvelopesFromLine(value, limits, (count) => {
521
- retainedChildren += count;
522
- if (retainedChildren > limits.maxRecords) {
523
- throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Muse retained child inspection limit exceeded');
524
- }
525
- });
551
+ for await (const value of museWorkspaceScanLines(authorization, limits, progress)) {
552
+ const envelopes = envelopesFromLine(value);
526
553
  for (const envelope of envelopes) {
527
554
  const candidate = workspaceEvidenceOf(envelope);
528
555
  if (!candidate)
529
556
  continue;
530
557
  sawWorkspaceEvidence = true;
531
- if (workspacePathEquals(candidate, workspacePath))
532
- return receipt('matched', null);
558
+ const matchKind = await matchSessionWorkspace(candidate, workspacePath);
559
+ if (matchKind)
560
+ return { ...receipt('matched', null), matchKind };
533
561
  }
534
562
  }
535
563
  return sawWorkspaceEvidence
@@ -537,15 +565,47 @@ export async function inspectMuseNativeWorkspace(source, workspacePath, limitsIn
537
565
  : receipt('inconclusive', 'MUSE_WORKSPACE_EVIDENCE_ABSENT', true);
538
566
  }
539
567
  catch (error) {
540
- return receipt('inconclusive', error instanceof SessionContractError
541
- ? error.code : 'MUSE_SOURCE_UNREADABLE');
568
+ if (error instanceof MuseWorkspaceLimitError) {
569
+ limit = { name: error.limitName, value: error.limitValue };
570
+ fallback = 'bounded-tail';
571
+ const tailProgress = { recordsInspected: 0, bytesInspected: 0 };
572
+ const tailBytes = Math.min(MUSE_WORKSPACE_TAIL_BYTES, limits.maxTotalBytes);
573
+ retainedChildren = 0;
574
+ let matchKind;
575
+ try {
576
+ for await (const value of museWorkspaceScanLines(authorization, { ...limits, maxTotalBytes: tailBytes }, tailProgress, tailBytes)) {
577
+ for (const envelope of envelopesFromLine(value)) {
578
+ const candidate = workspaceEvidenceOf(envelope);
579
+ if (!candidate)
580
+ continue;
581
+ matchKind = await matchSessionWorkspace(candidate, workspacePath);
582
+ if (matchKind)
583
+ break;
584
+ }
585
+ if (matchKind)
586
+ break;
587
+ }
588
+ }
589
+ catch {
590
+ // Preserve the original limit receipt when the fallback cannot attribute.
591
+ }
592
+ progress.recordsInspected += tailProgress.recordsInspected;
593
+ progress.bytesInspected += tailProgress.bytesInspected;
594
+ if (matchKind)
595
+ return { ...receipt('matched', null), matchKind };
596
+ }
597
+ return {
598
+ ...receipt('inconclusive', error instanceof SessionContractError
599
+ ? error.code : 'MUSE_SOURCE_UNREADABLE'),
600
+ ...(error instanceof SessionContractError && error.detail ? { detail: error.detail } : {}),
601
+ };
542
602
  }
543
603
  }
544
604
  export async function museNativeSourceMatchesWorkspace(source, workspacePath) {
545
605
  const result = await inspectMuseNativeWorkspace(source, workspacePath);
546
606
  if (result.outcome === 'inconclusive') {
547
607
  const code = SessionErrorCodeSchema.safeParse(result.reasonCode);
548
- throw new SessionContractError(code.success ? code.data : 'SCHEMA_DRIFT', `Muse workspace inspection inconclusive: ${result.reasonCode}`);
608
+ throw new SessionContractError(code.success ? code.data : 'SCHEMA_DRIFT', `Muse workspace inspection inconclusive: ${result.reasonCode}`, result.detail);
549
609
  }
550
610
  return result.outcome === 'matched';
551
611
  }
@@ -555,7 +615,7 @@ function nativeEnvelopesFromLine(value, limits, countChildren) {
555
615
  const retained = MuseRetainedFrameSchema.safeParse(value);
556
616
  if (!retained.success) {
557
617
  if (value === null || typeof value !== 'object' || Array.isArray(value)) {
558
- throw new SessionContractError('MALFORMED_SOURCE', 'Muse inspection encountered a non-object native record');
618
+ throw new SessionContractError('MALFORMED_SOURCE', 'Muse inspection encountered a non-object native record', schemaIssueDetail('<root>'));
559
619
  }
560
620
  // Native logs carry record kinds the envelope schema does not model (for
561
621
  // example runtime.session mode changes without payload.event.kind). They
@@ -567,7 +627,7 @@ function nativeEnvelopesFromLine(value, limits, countChildren) {
567
627
  const envelopes = [];
568
628
  for (const child of retained.data.children) {
569
629
  if (Buffer.byteLength(child.record_json) > limits.maxRecordBytes) {
570
- throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Muse retained child inspection byte limit exceeded');
630
+ throw new MuseWorkspaceLimitError('maxRecordBytes', limits.maxRecordBytes, Buffer.byteLength(child.record_json));
571
631
  }
572
632
  const childEvent = recordEventFromEnvelope(parseMuseScanJson(child.record_json, limits.maxNestingDepth));
573
633
  if (childEvent)
@@ -593,9 +653,6 @@ function workspaceEvidenceOf(envelope) {
593
653
  }
594
654
  return null;
595
655
  }
596
- function workspacePathEquals(candidate, workspacePath) {
597
- return resolve(candidate) === workspacePath;
598
- }
599
656
  function nativeSessionIdFromLocator(locator) {
600
657
  const parts = locator.replaceAll('\\', '/').split('/');
601
658
  const leaf = parts.at(-1);
@@ -742,7 +799,7 @@ function parseEventCursor(value) {
742
799
  if (value === undefined || value === '')
743
800
  return 0;
744
801
  if (!/^\d+$/.test(value))
745
- throw new SessionContractError('SCHEMA_DRIFT', 'muse event cursor is invalid');
802
+ throw new SessionContractError('SCHEMA_DRIFT', 'muse event cursor is invalid', schemaIssueDetail('cursor'));
746
803
  return Number(value);
747
804
  }
748
805
  //# sourceMappingURL=muse.js.map
@@ -1,8 +1,8 @@
1
1
  import { opendir } from 'node:fs/promises';
2
2
  import { resolve } from 'node:path';
3
3
  import { z } from 'zod';
4
- import { SessionContractError, } from '../contracts.js';
5
- import { readBoundedJsonLines, streamBoundedJsonLines } from '../readers.js';
4
+ import { schemaRejectionDetail, schemaIssueDetail, SessionContractError, } from '../contracts.js';
5
+ import { streamBoundedJsonLines } from '../readers.js';
6
6
  export const OMP_ADAPTER_VERSION = '1.0.0';
7
7
  export const OMP_SOURCE_SCHEMA_VERSION = '3.0.0';
8
8
  const Title = z.object({ type: z.literal('title'), v: z.literal(1), title: z.string(), source: z.enum(['auto', 'user']).optional(), updatedAt: z.string(), pad: z.string() });
@@ -30,7 +30,7 @@ export class OmpSessionAdapter {
30
30
  for (const root of [...scope.allowedRoots].sort()) {
31
31
  for await (const locator of jsonlFiles(resolve(root))) {
32
32
  if (++count > this.maxFiles)
33
- throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'OMP discovery file limit exceeded');
33
+ throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'OMP discovery file limit exceeded', { limit: 'maxFiles', configured: this.maxFiles, observed: count });
34
34
  yield { provider: 'omp', locator, locatorClass: 'omp-session-v3-jsonl' };
35
35
  }
36
36
  }
@@ -38,13 +38,23 @@ export class OmpSessionAdapter {
38
38
  async inspect(source) {
39
39
  let input;
40
40
  try {
41
- input = await readBoundedJsonLines({ selectedPath: source.locator,
41
+ input = await streamBoundedJsonLines({ selectedPath: source.locator,
42
42
  allowedRoots: source.authorizedScope.allowedRoots }, { consistency: 'provisional', limits: this.limits });
43
43
  }
44
44
  catch (error) {
45
45
  throw normalizeAuthorizationError(error);
46
46
  }
47
- const header = parseHeader(input.records[titleOffset(input.records[0]) ? 1 : 0]?.value);
47
+ let header;
48
+ let prefix = true;
49
+ for await (const line of input) {
50
+ if (prefix) {
51
+ prefix = false;
52
+ if (titleOffset(line))
53
+ continue;
54
+ }
55
+ header ??= parseHeader(line.value);
56
+ }
57
+ header ??= parseHeader(undefined);
48
58
  if (input.incompleteTail)
49
59
  throw new SessionContractError('TRUNCATED_SOURCE', 'OMP session has a truncated JSONL tail');
50
60
  return { sourceSchemaVersion: `${header.version}.0.0`, consistency: 'complete', operationalState: 'available' };
@@ -63,13 +73,13 @@ export class OmpSessionAdapter {
63
73
  let prefix = true;
64
74
  const start = cursor?.value.replace(/^byte:/, '') ?? '0';
65
75
  if (!/^\d+$/.test(start))
66
- throw new SessionContractError('SCHEMA_DRIFT', 'invalid OMP cursor');
76
+ throw new SessionContractError('SCHEMA_DRIFT', 'invalid OMP cursor', schemaIssueDetail('cursor'));
67
77
  let end = 0;
68
78
  const ids = new Set();
69
79
  for await (const line of input) {
70
80
  end = line.byteOffset + line.byteLength;
71
81
  if (Number(start) > line.byteOffset && Number(start) < end) {
72
- throw new SessionContractError('SCHEMA_DRIFT', 'OMP cursor is not a record boundary');
82
+ throw new SessionContractError('SCHEMA_DRIFT', 'OMP cursor is not a record boundary', schemaIssueDetail('cursor'));
73
83
  }
74
84
  if (prefix) {
75
85
  prefix = false;
@@ -83,7 +93,7 @@ export class OmpSessionAdapter {
83
93
  }
84
94
  const parsed = Entry.safeParse(line.value);
85
95
  if (!parsed.success)
86
- throw new SessionContractError('MALFORMED_SOURCE', 'OMP session entry is malformed');
96
+ throw new SessionContractError('MALFORMED_SOURCE', 'OMP session entry is malformed', schemaRejectionDetail(parsed.error));
87
97
  const entry = parsed.data;
88
98
  if (ids.has(entry.id))
89
99
  throw new SessionContractError('DUPLICATE_NATIVE_ID', 'OMP session contains a duplicate entry id');
@@ -107,9 +117,9 @@ export class OmpSessionAdapter {
107
117
  nativeType: entry.type, parentId: entry.parentId, ...metadata(entry, message) } };
108
118
  }
109
119
  if (!sessionId)
110
- throw new SessionContractError('MALFORMED_SOURCE', 'OMP session header is missing');
120
+ throw new SessionContractError('MALFORMED_SOURCE', 'OMP session header is missing', schemaIssueDetail('header'));
111
121
  if (Number(start) > end)
112
- throw new SessionContractError('SCHEMA_DRIFT', 'OMP cursor exceeds source size');
122
+ throw new SessionContractError('SCHEMA_DRIFT', 'OMP cursor exceeds source size', schemaIssueDetail('cursor'));
113
123
  if (input.incompleteTail)
114
124
  throw new SessionContractError('TRUNCATED_SOURCE', 'OMP session has a truncated JSONL tail');
115
125
  }
@@ -125,7 +135,7 @@ const KNOWN = new Set(['message', 'thinking_level_change', 'model_change', 'comp
125
135
  function parseHeader(value) {
126
136
  const parsed = Header.safeParse(value);
127
137
  if (!parsed.success)
128
- throw new SessionContractError('MALFORMED_SOURCE', 'OMP session header is malformed');
138
+ throw new SessionContractError('MALFORMED_SOURCE', 'OMP session header is malformed', schemaRejectionDetail(parsed.error));
129
139
  if (parsed.data.version !== 3)
130
140
  throw new SessionContractError('UNKNOWN_SCHEMA_MAJOR', `unsupported OMP session version: ${parsed.data.version}`);
131
141
  return parsed.data;
@@ -168,8 +178,12 @@ function normalizeAuthorizationError(error) {
168
178
  function titleOffset(line) {
169
179
  if (object(line?.value).type !== 'title')
170
180
  return 0;
171
- if (!Title.safeParse(line?.value).success || line?.byteLength !== 256) {
172
- throw new SessionContractError('MALFORMED_SOURCE', 'OMP title slot is malformed');
181
+ const parsed = Title.safeParse(line?.value);
182
+ if (!parsed.success) {
183
+ throw new SessionContractError('MALFORMED_SOURCE', 'OMP title slot is malformed', schemaRejectionDetail(parsed.error));
184
+ }
185
+ if (line?.byteLength !== 256) {
186
+ throw new SessionContractError('MALFORMED_SOURCE', 'OMP title slot is malformed', schemaIssueDetail('title'));
173
187
  }
174
188
  return 256;
175
189
  }
@@ -198,6 +212,6 @@ export async function readOmpSessionHeader(source) {
198
212
  }
199
213
  return parseHeader(line.value);
200
214
  }
201
- throw new SessionContractError('MALFORMED_SOURCE', 'OMP header is missing');
215
+ throw new SessionContractError('MALFORMED_SOURCE', 'OMP header is missing', schemaIssueDetail('header'));
202
216
  }
203
217
  //# sourceMappingURL=omp.js.map