@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,18 +1,20 @@
1
- import { SESSION_CONTRACT_VERSION, SessionEventSchema, SessionSchema, assertSupportedSchemaMajor, sha256, stableEventId, stableSessionId, SessionContractError, } from './contracts.js';
2
- import { redactSessionText, sanitizeNativeExtensions } from './policy.js';
1
+ import { SESSION_CONTRACT_VERSION, SessionEventSchema, SessionSchema, assertSupportedSchemaMajor, sha256, stableEventId, stableSessionId, SessionContractError, schemaIssueDetail, assertSessionResourceLimit, } from './contracts.js';
2
+ import { assertImportByteLimit, DEFAULT_IMPORT_BYTE_LIMITS, resolveImportLimits } from './import-limits.js';
3
+ import { redactSessionValue, sanitizeSessionEvent, sanitizeNativeExtensions } from './policy.js';
3
4
  import { fingerprintSourcePrefix } from './readers.js';
5
+ import { DEFAULT_INACTIVITY_THRESHOLD_MS } from './lifecycle.js';
4
6
  import { classifySessionEventOrigin, deriveSessionIntent, } from './origin.js';
5
- const DEFAULT_LIMITS = {
7
+ export const DEFAULT_IMPORT_LIMITS = {
6
8
  maxRecords: 1_000_000,
7
- maxRecordBytes: 8 * 1024 * 1024,
8
- maxTotalBytes: 1024 * 1024 * 1024,
9
+ ...DEFAULT_IMPORT_BYTE_LIMITS,
9
10
  batchSize: 1_000,
10
11
  };
11
12
  export class SessionImportFailure extends SessionContractError {
12
13
  failureReceipt;
13
14
  constructor(failureReceipt, cause) {
14
- super(cause instanceof SessionContractError ? cause.code : 'IMPORT_INTERRUPTED', cause instanceof Error ? cause.message : 'session import failed');
15
+ super(cause instanceof SessionContractError ? cause.code : 'IMPORT_INTERRUPTED', cause instanceof Error ? cause.message : 'session import failed', cause instanceof SessionContractError ? cause.detail : undefined);
15
16
  this.failureReceipt = failureReceipt;
17
+ this.failureReceipt = redactSessionValue(failureReceipt).value;
16
18
  this.name = 'SessionImportFailure';
17
19
  }
18
20
  }
@@ -30,7 +32,9 @@ export class IncrementalSessionImporter {
30
32
  if (request.workspaceId !== request.selectedSource.authorizedScope.workspaceId) {
31
33
  throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'session import workspace is outside the authorized source scope');
32
34
  }
33
- const limits = { ...DEFAULT_LIMITS, ...request.limits };
35
+ const limits = { ...DEFAULT_IMPORT_LIMITS, ...resolveImportLimits(undefined, {
36
+ maxRecordBytes: request.limits?.maxRecordBytes, maxSourceBytes: request.limits?.maxTotalBytes,
37
+ }), ...request.limits };
34
38
  const previous = request.batchRunId
35
39
  ? this.repository.getBatchCheckpoint(request.source.sourceId, request.adapter.adapterVersion, request.batchRunId) ?? this.repository.getCheckpoint(request.source.sourceId, request.adapter.adapterVersion)
36
40
  : this.repository.getCheckpoint(request.source.sourceId, request.adapter.adapterVersion);
@@ -40,17 +44,17 @@ export class IncrementalSessionImporter {
40
44
  : undefined;
41
45
  const sourceGeneration = continuity?.sourceGeneration ?? sourceGenerationDigest(request);
42
46
  if (previous?.sourceGeneration && previous.sourceGeneration !== sourceGeneration) {
43
- throw new SessionContractError('SCHEMA_DRIFT', 'session source generation changed; explicit restart or migration is required');
47
+ throw new SessionContractError('SCHEMA_DRIFT', 'session source generation changed; explicit restart or migration is required', schemaIssueDetail('sourceGeneration'));
44
48
  }
45
49
  if (previous?.adapterVersion && previous.adapterVersion !== request.adapter.adapterVersion) {
46
- throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint adapter version changed; checkpoint migration is required');
50
+ throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint adapter version changed; checkpoint migration is required', schemaIssueDetail('adapterVersion'));
47
51
  }
48
52
  if (previous?.sourceSchemaVersion
49
53
  && previous.sourceSchemaVersion !== request.source.sourceSchemaVersion) {
50
- throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint source schema changed; checkpoint migration is required');
54
+ throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint source schema changed; checkpoint migration is required', schemaIssueDetail('sourceSchemaVersion'));
51
55
  }
52
56
  if (previous?.policyVersion && previous.policyVersion !== request.policyVersion) {
53
- throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint policy version changed; checkpoint migration is required');
57
+ throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint policy version changed; checkpoint migration is required', schemaIssueDetail('policyVersion'));
54
58
  }
55
59
  const checkpoint = {
56
60
  cursor: previous?.cursor ?? '',
@@ -65,6 +69,7 @@ export class IncrementalSessionImporter {
65
69
  policyVersion: request.policyVersion,
66
70
  continuity: continuity
67
71
  ? (!previous || previous.positionKind === 'byte-offset'
72
+ || previous.positionKind === 'provider-native'
68
73
  ? continuity.outcome
69
74
  : 'unverified')
70
75
  : (previous ? 'unverified' : 'new-generation'),
@@ -75,6 +80,7 @@ export class IncrementalSessionImporter {
75
80
  };
76
81
  const receipts = [];
77
82
  let batch = [];
83
+ let batchBytes = 0;
78
84
  let batchStart = checkpoint.recordsRead;
79
85
  let durableCheckpoint = { ...checkpoint };
80
86
  const importStarted = performance.now();
@@ -86,12 +92,14 @@ export class IncrementalSessionImporter {
86
92
  if (request.signal?.aborted) {
87
93
  throw new SessionContractError('IMPORT_INTERRUPTED', 'session import was cancelled at a durable batch boundary');
88
94
  }
95
+ const identities = batch.map(recordIdentity);
96
+ const batchDigests = identities.map(identity => identity.digest);
89
97
  const runId = sha256([
90
98
  request.source.sourceId, request.adapter.adapterVersion,
91
99
  batchStart, checkpoint.recordsRead,
92
- ...batch.map((record) => sha256(JSON.stringify(record))),
100
+ ...batchDigests,
93
101
  ].join('\0'));
94
- const normalized = normalizeBatch(request, batch, runId, sourceObservedAt);
102
+ const normalized = normalizeBatch(request, batch, batchDigests, runId, sourceObservedAt);
95
103
  const run = {
96
104
  contractVersion: SESSION_CONTRACT_VERSION,
97
105
  importRunId: runId,
@@ -122,7 +130,7 @@ export class IncrementalSessionImporter {
122
130
  receipt.metrics = {
123
131
  records: batch.length,
124
132
  normalizedEvents: normalized.events.length,
125
- normalizedBytes: batch.reduce((total, record) => total + Buffer.byteLength(JSON.stringify(record)), 0),
133
+ normalizedBytes: identities.reduce((bytes, identity) => bytes + identity.bytes, 0),
126
134
  durationMs: performance.now() - importStarted,
127
135
  batchLatencyMs: performance.now() - flushStarted,
128
136
  heapUsedBytes: peakHeap,
@@ -132,26 +140,40 @@ export class IncrementalSessionImporter {
132
140
  receipts.push(receipt);
133
141
  durableCheckpoint = { ...checkpoint };
134
142
  batch = [];
143
+ batchBytes = 0;
135
144
  batchStart = checkpoint.recordsRead;
136
145
  };
137
146
  try {
147
+ if (continuity)
148
+ assertImportByteLimit('maxTotalBytes', limits, continuity.size);
138
149
  for await (const record of request.adapter.stream(request.selectedSource, previous ? { value: previous.cursor } : undefined)) {
139
150
  if (request.signal?.aborted) {
140
151
  throw new SessionContractError('IMPORT_INTERRUPTED', 'session import was cancelled and can resume from its durable checkpoint');
141
152
  }
142
- const bytes = Buffer.byteLength(JSON.stringify(record));
143
- if (bytes > limits.maxRecordBytes
144
- || checkpoint.bytesRead + bytes > limits.maxTotalBytes
145
- || checkpoint.recordsRead + 1 > limits.maxRecords) {
146
- throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'session import exceeded an authorized resource limit');
147
- }
153
+ const bytes = recordBytes(record);
154
+ assertImportByteLimit('maxRecordBytes', limits, bytes);
155
+ if (!continuity)
156
+ assertImportByteLimit('maxTotalBytes', limits, checkpoint.bytesRead + bytes);
157
+ assertSessionResourceLimit('maxRecords', limits.maxRecords, checkpoint.recordsRead + 1);
158
+ // Bound batches by bytes as well as record count for large records.
159
+ if (batchBytes + bytes > 4 * 1024 * 1024)
160
+ flush();
148
161
  batch.push(record);
162
+ batchBytes += bytes;
149
163
  checkpoint.recordsRead += 1;
150
164
  if (record.sourceCursor?.startsWith('byte:')) {
151
165
  checkpoint.positionKind = 'byte-offset';
152
166
  checkpoint.cursor = record.sourceCursor;
153
167
  checkpoint.bytesRead = Number(record.sourceCursor.slice(5));
154
168
  }
169
+ else if (record.sourceCursor?.startsWith('native:')) {
170
+ // An opaque provider-native resume token (e.g. a monotonic native
171
+ // sequence) — never a byte offset. Stored verbatim; `bytesRead`
172
+ // still accumulates so resource limits remain meaningful.
173
+ checkpoint.positionKind = 'provider-native';
174
+ checkpoint.cursor = record.sourceCursor;
175
+ checkpoint.bytesRead += bytes;
176
+ }
155
177
  else {
156
178
  checkpoint.bytesRead += bytes;
157
179
  checkpoint.cursor = String(checkpoint.recordsRead);
@@ -186,6 +208,7 @@ export class IncrementalSessionImporter {
186
208
  : durableCheckpoint,
187
209
  errorCode: error instanceof SessionContractError
188
210
  ? error.code : 'IMPORT_INTERRUPTED',
211
+ detail: error instanceof SessionContractError ? error.detail : undefined,
189
212
  }, error);
190
213
  }
191
214
  if (request.publish !== false && request.source.consistency === 'complete') {
@@ -198,20 +221,30 @@ export class IncrementalSessionImporter {
198
221
  return receipts;
199
222
  }
200
223
  }
201
- function normalizeBatch(request, records, importRunId, sourceObservedAt) {
224
+ /** Keep large temporary JSON strings out of the asynchronous import frame. */
225
+ function recordBytes(record) {
226
+ return Buffer.byteLength(JSON.stringify(record));
227
+ }
228
+ /** Compute flush-time identity once, preserving adapters that mutate yielded objects. */
229
+ function recordIdentity(record) {
230
+ const serialized = JSON.stringify(record);
231
+ return { bytes: Buffer.byteLength(serialized), digest: sha256(serialized) };
232
+ }
233
+ function normalizeBatch(request, records, digests, importRunId, sourceObservedAt) {
202
234
  const sessions = new Map();
203
- const events = records.map((record) => {
235
+ const events = records.map((record, index) => {
204
236
  const sessionId = stableSessionId(request.source.provider, request.source.sourceId, record.nativeSessionId);
205
- const redacted = redactSessionText(record.text);
206
237
  const native = sanitizeNativeExtensions(record.extensions ?? {});
207
- const digest = sha256(JSON.stringify(record));
238
+ const lineage = request.source.provider === 'codex' && native.value.lineage
239
+ ? { lineage: native.value.lineage } : {};
240
+ const digest = digests[index];
208
241
  const origin = classifySessionEventOrigin(request.source.provider, record);
209
242
  const lifecycle = sessionLifecycle(request, record, sourceObservedAt);
210
243
  const lifecycleEvidence = sessionLifecycleEvidence(request, record, sourceObservedAt);
211
244
  const extensions = {
212
- [`native.${request.source.provider}`]: native.value,
245
+ [`native.${request.source.provider}`]: record.extensions ?? {},
213
246
  };
214
- const event = SessionEventSchema.parse({
247
+ const event = sanitizeSessionEvent(SessionEventSchema.parse({
215
248
  contractVersion: SESSION_CONTRACT_VERSION,
216
249
  eventId: stableEventId(request.source.provider, request.source.sourceId, record, digest),
217
250
  sessionId, sourceId: request.source.sourceId, importRunId,
@@ -229,17 +262,13 @@ function normalizeBatch(request, records, importRunId, sourceObservedAt) {
229
262
  model: record.model ?? null,
230
263
  entities: record.entities ?? [],
231
264
  extractionState: record.extractionState ?? null,
232
- searchableText: redacted.text, digest, rawReference: record.rawReference,
265
+ searchableText: record.text, digest, rawReference: record.rawReference,
233
266
  adapterVersion: request.adapter.adapterVersion,
234
267
  consistency: request.source.consistency,
235
- sensitivity: {
236
- classification: redacted.sensitivity === 'sensitive' || native.sensitivity === 'sensitive'
237
- ? 'sensitive' : 'none',
238
- classes: [...new Set([...redacted.classes, ...native.classes])].sort(),
239
- },
268
+ sensitivity: { classification: 'none', classes: [] },
240
269
  opaque: !isKnownEventKind(record.kind),
241
270
  extensions,
242
- });
271
+ }));
243
272
  if (!sessions.has(sessionId)) {
244
273
  sessions.set(sessionId, SessionSchema.parse({
245
274
  contractVersion: SESSION_CONTRACT_VERSION,
@@ -251,6 +280,7 @@ function normalizeBatch(request, records, importRunId, sourceObservedAt) {
251
280
  sourceDigest: sha256(`${sourceGenerationDigest(request)}\0${record.nativeSessionId}`),
252
281
  extensions: {
253
282
  [`native.${request.source.provider}`]: {
283
+ ...lineage,
254
284
  lifecycleEvidence,
255
285
  },
256
286
  },
@@ -270,6 +300,7 @@ function normalizeBatch(request, records, importRunId, sourceObservedAt) {
270
300
  session.lifecycle = selected.lifecycle;
271
301
  session.extensions[nativeKey] = {
272
302
  ...nativeRecord,
303
+ ...lineage,
273
304
  lifecycleEvidence: selected.evidence,
274
305
  };
275
306
  }
@@ -278,7 +309,7 @@ function normalizeBatch(request, records, importRunId, sourceObservedAt) {
278
309
  for (const session of sessions.values()) {
279
310
  session.intent = deriveSessionIntent(events.filter((event) => event.sessionId === session.sessionId));
280
311
  }
281
- return { sessions: [...sessions.values()], events };
312
+ return { sessions: redactSessionValue([...sessions.values()]).value, events };
282
313
  }
283
314
  const KNOWN_EVENT_KINDS = new Set(['message', 'tool-call', 'tool-result', 'artifact', 'attachment', 'summary']);
284
315
  function isKnownEventKind(kind) {
@@ -354,6 +385,7 @@ function sessionLifecycleEvidence(request, record, sourceObservedAt) {
354
385
  state: 'active',
355
386
  observedAt: observedAt ?? request.source.authorizedAt,
356
387
  confidence: 'provisional',
388
+ thresholdMs,
357
389
  };
358
390
  }
359
391
  function nativeLifecycle(extensions) {
@@ -407,7 +439,6 @@ function evidenceTimestamp(value) {
407
439
  const parsed = Date.parse(String(value.observedAt ?? ''));
408
440
  return Number.isFinite(parsed) ? parsed : Number.NEGATIVE_INFINITY;
409
441
  }
410
- const DEFAULT_INACTIVITY_THRESHOLD_MS = 24 * 60 * 60 * 1000;
411
442
  function inactivityThresholdMs(request) {
412
443
  return request.inactivityThresholdMs ?? DEFAULT_INACTIVITY_THRESHOLD_MS;
413
444
  }
@@ -435,33 +466,40 @@ async function sourceContinuity(request, previous) {
435
466
  selectedPath: request.selectedSource.locator,
436
467
  allowedRoots: request.selectedSource.authorizedScope.allowedRoots,
437
468
  };
469
+ // Snapshot sources are rewritten wholesale on every save (no stable
470
+ // append-only byte region, and the file identity may legitimately change
471
+ // via rename-swap): skip the shrink, file-identity, and prefix-rewrite
472
+ // throws below. Idempotency instead rests on the adapter's own monotonic
473
+ // native cursor plus the catalog's identity-keyed inserts.
474
+ const isSnapshot = request.adapter.sourceContinuity === 'snapshot';
438
475
  const skipPrefixBytes = await request.adapter.mutablePrefixBytes?.(request.selectedSource) ?? 0;
439
476
  const metadata = await fingerprintSourcePrefix(authorization, 0);
440
- if (previous?.sourceSize !== undefined) {
477
+ if (!isSnapshot && previous?.sourceSize !== undefined) {
441
478
  if (metadata.size < previous.sourceSize) {
442
- throw new SessionContractError('SCHEMA_DRIFT', 'session source was truncated before its durable checkpoint');
479
+ throw new SessionContractError('SCHEMA_DRIFT', 'session source was truncated before its durable checkpoint', schemaIssueDetail('sourceSize'));
443
480
  }
444
481
  if (previous.sourceFileIdentity
445
482
  && previous.sourceFileIdentity !== metadata.fileIdentity) {
446
- throw new SessionContractError('SCHEMA_DRIFT', 'session source file generation was replaced or rotated');
483
+ throw new SessionContractError('SCHEMA_DRIFT', 'session source file generation was replaced or rotated', schemaIssueDetail('sourceFileIdentity'));
447
484
  }
448
485
  const priorPrefix = await fingerprintSourcePrefix(authorization, previous.sourceSize, skipPrefixBytes);
449
486
  if (previous.prefixDigest && priorPrefix.digest !== previous.prefixDigest) {
450
- throw new SessionContractError('SCHEMA_DRIFT', 'session source prefix was rewritten before its durable checkpoint');
487
+ throw new SessionContractError('SCHEMA_DRIFT', 'session source prefix was rewritten before its durable checkpoint', schemaIssueDetail('prefixDigest'));
451
488
  }
452
489
  }
453
490
  const current = await fingerprintSourcePrefix(authorization, metadata.size, skipPrefixBytes);
454
- const generation = previous?.sourceGeneration ?? sha256([
455
- sourceGenerationDigest(request),
456
- metadata.fileIdentity,
457
- ].join('\0'));
491
+ const generation = previous?.sourceGeneration ?? (isSnapshot
492
+ ? sourceGenerationDigest(request)
493
+ : sha256([sourceGenerationDigest(request), metadata.fileIdentity].join('\0')));
458
494
  return {
459
495
  sourceGeneration: generation,
460
496
  outcome: !previous
461
497
  ? 'new-generation'
462
- : metadata.size === previous.sourceSize
463
- && current.digest === previous.prefixDigest
464
- ? 'unchanged-replay' : 'validated-append',
498
+ : isSnapshot
499
+ ? (current.digest === previous.prefixDigest ? 'unchanged-replay' : 'validated-append')
500
+ : metadata.size === previous.sourceSize
501
+ && current.digest === previous.prefixDigest
502
+ ? 'unchanged-replay' : 'validated-append',
465
503
  size: metadata.size,
466
504
  mtimeMs: metadata.mtimeMs,
467
505
  fileIdentity: metadata.fileIdentity,
@@ -42,4 +42,6 @@ export * from './adapters/warp.js';
42
42
  export * from './adapters/windsurf.js';
43
43
  export * from './adapters/muse.js';
44
44
  export * from './adapters/omp.js';
45
+ export * from './adapter-registry.js';
46
+ export * from './coverage-preflight.js';
45
47
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,45 @@
1
+ export const DEFAULT_INACTIVITY_THRESHOLD_MS = 24 * 60 * 60 * 1000;
2
+ export function effectiveSessionLifecycle(session) {
3
+ if (session.lifecycle !== 'active' && session.lifecycle !== 'inactive')
4
+ return session;
5
+ const nativeKey = `native.${session.provider}`;
6
+ const native = objectRecord(session.extensions[nativeKey]);
7
+ const evidence = objectRecord(native.lifecycleEvidence);
8
+ if (evidence.basis !== 'open-provisional-source'
9
+ && evidence.basis !== 'inactivity-threshold')
10
+ return session;
11
+ const observedAt = session.updatedAt ?? evidence.observedAt;
12
+ const timestamp = typeof observedAt === 'string' ? Date.parse(observedAt) : NaN;
13
+ if (!Number.isFinite(timestamp))
14
+ return session;
15
+ const thresholdMs = typeof evidence.thresholdMs === 'number'
16
+ && Number.isFinite(evidence.thresholdMs)
17
+ && evidence.thresholdMs >= 0
18
+ ? evidence.thresholdMs : DEFAULT_INACTIVITY_THRESHOLD_MS;
19
+ const inactive = Date.now() - timestamp > thresholdMs;
20
+ const lifecycle = inactive ? 'inactive' : 'active';
21
+ return {
22
+ ...session,
23
+ lifecycle,
24
+ extensions: {
25
+ ...session.extensions,
26
+ [nativeKey]: {
27
+ ...native,
28
+ lifecycleEvidence: {
29
+ ...evidence,
30
+ basis: inactive ? 'inactivity-threshold' : 'open-provisional-source',
31
+ state: lifecycle,
32
+ observedAt,
33
+ confidence: inactive ? 'medium' : 'provisional',
34
+ thresholdMs,
35
+ },
36
+ },
37
+ },
38
+ };
39
+ }
40
+ function objectRecord(value) {
41
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
42
+ ? value
43
+ : {};
44
+ }
45
+ //# sourceMappingURL=lifecycle.js.map
@@ -27,7 +27,9 @@ export async function authorizeSourceFile(input) {
27
27
  throw new SessionContractError('SOURCE_NOT_REGULAR_FILE', 'session source must be a regular file');
28
28
  }
29
29
  if (input.maxBytes !== undefined && stat.size > input.maxBytes) {
30
- throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'session source exceeds the authorized byte limit');
30
+ throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'session source exceeds the authorized byte limit', {
31
+ limit: 'maxBytes', configured: input.maxBytes, observed: stat.size,
32
+ });
31
33
  }
32
34
  return {
33
35
  canonicalPath,
@@ -54,21 +56,51 @@ async function safeRealpath(path, root = false) {
54
56
  throw new SessionContractError('SOURCE_NOT_AUTHORIZED', root ? 'an authorized source root is inaccessible' : 'selected session source is inaccessible');
55
57
  }
56
58
  }
59
+ // Ambiguous token components are bounded; whitespace and key blocks use disjoint scans.
57
60
  const SECRET_PATTERNS = [
58
- ['private-key', /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/g],
59
- ['bearer-token', /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}\b/gi],
60
- ['authorization', /\bAuthorization\s*:\s*[^\r\n]+/gi],
61
- ['cookie', /\b(?:Cookie|Set-Cookie)\s*:\s*[^\r\n]+/gi],
62
- ['credential', /\b(?:api[_-]?key|token|password|secret)\s*(?:[:=]|\s)\s*["']?[^\s"',;]{6,}/gi],
63
- ['provider-token', /\b(?:sk-(?:proj-)?|gh[pousr]_|github_pat_|xox[baprs]-|AKIA)[A-Za-z0-9._~+/=-]{8,}\b/g],
61
+ ['bearer-token', /\bBearer[ \t]+[A-Za-z0-9._~+/=-]{12,}/gi],
62
+ ['authorization', /\bAuthorization[ \t]*:[ \t]*[^\r\n]+/gi],
63
+ ['cookie', /\b(?:Cookie|Set-Cookie)[ \t]*:[ \t]*[^\r\n]+/gi],
64
+ ['environment-assignment', /\b(?:AWS_SECRET_ACCESS_KEY|[A-Z][A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|PASSWD|API_KEY))[ \t]*=[ \t]*(?:"[^"\r\n]+"|'[^'\r\n]+'|[^\s]+)/gi],
65
+ ['credential', /\b(?:api[_-]?key|token|password|secret)[ \t]*[:=][ \t]*["']?[^\s"',;]{6,}/gi],
66
+ ['credential', /^[ \t]*password[ \t]+[^\s]+[ \t]*$/gim],
67
+ // Space-separated values only when they look generated (20+ chars, letters and digits), so prose stays.
68
+ ['credential', /\b(?:api[_-]?key|token|secret)[ \t]+(?=[A-Za-z0-9._~+/=-]*[0-9])(?=[A-Za-z0-9._~+/=-]*[A-Za-z])[A-Za-z0-9._~+/=-]{20,}/gi],
69
+ ['provider-token', /\b(?:sk-(?:ant-|proj-)?|[sr]k_(?:live|test)_|gh[pousr]_|github_pat_|glpat-|xai-|npm_|hf_|xox[baprs]-|AKIA|AIza)[A-Za-z0-9._~+/=-]{8,}/g],
70
+ ['provider-token', /https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9_-]+\/[A-Za-z0-9_-]+\/[A-Za-z0-9_-]+/g],
64
71
  ['connection-string', /\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis):\/\/[^\s]+/gi],
65
72
  ['credential-url', /\bhttps?:\/\/[^/\s:@]+:[^/\s@]+@[^\s]+/gi],
66
- ['environment-assignment', /\b[A-Z][A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|PASSWD|API_KEY)\s*=\s*(?:"[^"]+"|'[^']+'|[^\s]+)/g],
67
73
  ['fixture-credential', /\bredaction-canary-[A-Za-z0-9_-]+\b/gi],
68
- ['email', /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi],
74
+ ['email', /\b[A-Z0-9._%+-]{1,64}@[A-Z0-9.-]{1,253}\.[A-Z]{2,63}\b/gi],
69
75
  ];
76
+ function redactJwts(input, mark) {
77
+ // Consume even incomplete candidates. Retrying at each inner `eyJ` in an
78
+ // unterminated segment would repeatedly scan the remainder of a large record.
79
+ return input.replace(/\beyJ[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+){0,2}/g, (candidate) => {
80
+ const parts = candidate.split('.');
81
+ return parts.length === 3 && parts[0].length >= 8 && parts[1].length >= 2 && parts[2].length >= 8
82
+ ? mark('provider-token') : candidate;
83
+ });
84
+ }
85
+ const PRIVATE_KEY_BEGIN = /-----BEGIN ((?:(?:RSA|EC|DSA|OPENSSH|ENCRYPTED) )?PRIVATE KEY|PGP PRIVATE KEY BLOCK)-----/g;
86
+ function redactPrivateKeys(input, mark) {
87
+ const parts = [];
88
+ let cursor = 0;
89
+ PRIVATE_KEY_BEGIN.lastIndex = 0;
90
+ let match;
91
+ while ((match = PRIVATE_KEY_BEGIN.exec(input))) {
92
+ parts.push(input.slice(cursor, match.index), mark('private-key'));
93
+ const endMarker = `-----END ${match[1]}-----`;
94
+ const end = input.indexOf(endMarker, PRIVATE_KEY_BEGIN.lastIndex);
95
+ // An unfinished key block is sensitive through the end of the record.
96
+ cursor = end < 0 ? input.length : end + endMarker.length;
97
+ PRIVATE_KEY_BEGIN.lastIndex = cursor;
98
+ }
99
+ parts.push(input.slice(cursor));
100
+ return parts.join('');
101
+ }
70
102
  const SENSITIVE_KEY = /(?:authorization|auth[_-]?header|cookie|credential|password|passwd|secret|token|api[_-]?key|private[_-]?key|connection[_-]?string)/i;
71
- const CONTENT_KEY = /(?:^|[_-])(?:text|content|prompt|command|arguments?|args|result|output|body|request|response|source|code|query)(?:$|[_-])/i;
103
+ const CONTENT_KEY = /(?:^|[_-])(?:data|text|content|prompt|command|arguments?|args|result|output|body|request|response|source|code|query)(?:$|[_-])/i;
72
104
  const PATH_KEY = /(?:^|[_-])(?:path|cwd|directory|filename|file)(?:$|[_-])/i;
73
105
  const SAFE_STRING_KEY = /(?:^|[_-])(?:id|kind|type|role|status|state|lifecycle|reason|name|product|provider|version|schema|model|format|class|mode|event|operation|tool|method|language|scope|consistency|disposition|phase|visibility|protocol|category|classification|decision|capability|permission|transition|hash|digest)(?:$|[_-])/i;
74
106
  const CONTROL_CHARACTERS = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g;
@@ -103,23 +135,28 @@ export function sanitizeNativeExtensions(input, overrides = {}) {
103
135
  return marker('depth-limit');
104
136
  if (value === null || typeof value === 'boolean' || typeof value === 'number')
105
137
  return value;
138
+ if (typeof value === 'string' && /^\[REDACTED:[a-z-]+\]$/.test(value))
139
+ return value;
140
+ if (SENSITIVE_KEY.test(classifiedKey))
141
+ return marker('sensitive-field');
142
+ if (CONTENT_KEY.test(classifiedKey))
143
+ return marker('content');
144
+ if (PATH_KEY.test(classifiedKey))
145
+ return marker('path');
106
146
  if (typeof value === 'bigint')
107
147
  return String(value);
108
148
  if (typeof value !== 'string' && typeof value !== 'object') {
109
149
  return marker('unsupported-value');
110
150
  }
111
151
  if (typeof value === 'string') {
112
- if (SENSITIVE_KEY.test(classifiedKey))
113
- return marker('sensitive-field');
114
- if (CONTENT_KEY.test(classifiedKey))
115
- return marker('content');
116
- if (PATH_KEY.test(classifiedKey))
117
- return marker('path');
118
152
  if (Buffer.byteLength(value) > limits.maxStringBytes)
119
153
  return marker('string-limit');
120
154
  const redacted = redactSessionText(value);
121
155
  for (const classification of redacted.classes)
122
156
  classes.add(classification);
157
+ for (const [classification, count] of Object.entries(redacted.decisions)) {
158
+ decisions[classification] = (decisions[classification] ?? 0) + count;
159
+ }
123
160
  if (redacted.classes.length) {
124
161
  decisions['classified-value'] = (decisions['classified-value'] ?? 0) + 1;
125
162
  return redacted.text.replace(CONTROL_CHARACTERS, ' ');
@@ -142,8 +179,15 @@ export function sanitizeNativeExtensions(input, overrides = {}) {
142
179
  const output = {};
143
180
  const entries = Object.entries(value);
144
181
  for (const [childKey, childValue] of entries.slice(0, limits.maxObjectKeys)) {
145
- output[childKey.replace(/\r?\n/g, ' ').replace(CONTROL_CHARACTERS, '')] =
146
- visit(childValue, childKey, depth + 1);
182
+ const safeKey = redactSessionText(childKey);
183
+ for (const classification of safeKey.classes)
184
+ classes.add(classification);
185
+ for (const [classification, count] of Object.entries(safeKey.decisions)) {
186
+ decisions[classification] = (decisions[classification] ?? 0) + count;
187
+ }
188
+ Object.defineProperty(output, safeKey.text.replace(/\r?\n/g, ' ').replace(CONTROL_CHARACTERS, ''), {
189
+ value: visit(childValue, childKey, depth + 1), enumerable: true, configurable: true, writable: true,
190
+ });
147
191
  }
148
192
  if (entries.length > limits.maxObjectKeys)
149
193
  output.__truncated__ = marker('object-key-limit');
@@ -165,15 +209,79 @@ export function sanitizeNativeExtensions(input, overrides = {}) {
165
209
  };
166
210
  }
167
211
  export function redactSessionText(input) {
168
- const classes = new Set();
169
- let text = input;
212
+ const decisions = {};
213
+ const mark = (classification) => {
214
+ decisions[classification] = (decisions[classification] ?? 0) + 1;
215
+ return `[REDACTED:${classification}]`;
216
+ };
217
+ let text = redactJwts(redactPrivateKeys(input, mark), mark);
170
218
  for (const [classification, pattern] of SECRET_PATTERNS) {
171
- text = text.replace(pattern, () => {
172
- classes.add(classification);
173
- return `[REDACTED:${classification}]`;
174
- });
219
+ // Every email match requires an at-sign. Avoid costly bounded local-part
220
+ // backtracking on large records containing only email-shaped near matches.
221
+ if (classification === 'email' && !text.includes('@'))
222
+ continue;
223
+ text = text.replace(pattern, () => mark(classification));
175
224
  }
176
- return { text, sensitivity: classes.size ? 'sensitive' : 'none', classes: [...classes].sort() };
225
+ const classes = Object.keys(decisions).sort();
226
+ return { text, sensitivity: classes.length ? 'sensitive' : 'none', classes, decisions };
227
+ }
228
+ /** Redact every string, including object keys, without changing canonical field shapes. */
229
+ export function redactSessionValue(input) {
230
+ const decisions = {};
231
+ const active = new WeakSet();
232
+ const text = (value) => {
233
+ const result = redactSessionText(value);
234
+ for (const [key, count] of Object.entries(result.decisions)) {
235
+ decisions[key] = (decisions[key] ?? 0) + count;
236
+ }
237
+ return result.text;
238
+ };
239
+ const visit = (value, depth) => {
240
+ if (typeof value === 'string')
241
+ return text(value);
242
+ if (!value || typeof value !== 'object')
243
+ return value;
244
+ if (depth > 64 || active.has(value))
245
+ return '[REDACTED:structure-limit]';
246
+ active.add(value);
247
+ try {
248
+ return Array.isArray(value)
249
+ ? value.map((item) => visit(item, depth + 1))
250
+ : Object.fromEntries(Object.entries(value).map(([key, item]) => [text(key), visit(item, depth + 1)]));
251
+ }
252
+ finally {
253
+ active.delete(value);
254
+ }
255
+ };
256
+ const value = visit(input, 0);
257
+ return { value, classes: Object.keys(decisions).sort(), decisions };
258
+ }
259
+ /** Shared import boundary for canonical text, metadata and bounded native attributes. */
260
+ export function sanitizeSessionEvent(input) {
261
+ const native = sanitizeNativeExtensions(input.extensions);
262
+ const result = redactSessionValue({ ...input, extensions: native.value });
263
+ const event = result.value;
264
+ const classes = [...new Set([...event.sensitivity.classes, ...native.classes, ...result.classes])].sort();
265
+ event.sensitivity = {
266
+ classification: classes.length ? 'sensitive' : 'none', classes,
267
+ };
268
+ // Counts contain classifications only, never matched input or excerpts.
269
+ const prior = event.extensions['native.redaction'];
270
+ const counts = {};
271
+ for (const item of Array.isArray(prior?.decisions) ? prior.decisions : []) {
272
+ if (item && typeof item.classification === 'string' && Number.isSafeInteger(item.count) && item.count >= 0) {
273
+ counts[item.classification] = item.count;
274
+ }
275
+ }
276
+ for (const decisions of [native.decisions, result.decisions]) {
277
+ for (const [key, count] of Object.entries(decisions)) {
278
+ counts[key] = (counts[key] ?? 0) + count;
279
+ }
280
+ }
281
+ event.extensions['native.redaction'] = {
282
+ decisions: Object.entries(counts).map(([classification, count]) => ({ classification, count })),
283
+ };
284
+ return event;
177
285
  }
178
286
  export function contentFreeAuditEvent(event) {
179
287
  return Object.freeze({ ...event, counts: { ...event.counts } });