@sanqianx/project-knowledge 5.0.23 → 5.0.25

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 (401) hide show
  1. package/.agents/plugins/marketplace.json +20 -20
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/CHANGELOG.md +1361 -1346
  4. package/LICENSE +201 -201
  5. package/NOTICE +5 -5
  6. package/README.md +349 -349
  7. package/_modules/knowledge-engine/package.json +1 -1
  8. package/_modules/knowledge-engine/src/coordinator.js +48 -3
  9. package/_modules/knowledge-engine/src/runtime/automation-config.js +140 -140
  10. package/_modules/knowledge-engine/src/runtime/commit-conversation-binder.js +238 -238
  11. package/_modules/knowledge-engine/src/runtime/commit-processing-ledger.js +26 -26
  12. package/_modules/knowledge-engine/src/runtime/commit-prompt.js +193 -193
  13. package/_modules/knowledge-engine/src/runtime/commit-reconciler.js +394 -394
  14. package/_modules/knowledge-engine/src/runtime/contracts.js +282 -282
  15. package/_modules/knowledge-engine/src/runtime/conversation-store.js +389 -389
  16. package/_modules/knowledge-engine/src/runtime/evidence-bundle.js +228 -228
  17. package/_modules/knowledge-engine/src/runtime/git-runner.js +62 -62
  18. package/_modules/knowledge-engine/src/runtime/knowledge-promotion.js +505 -505
  19. package/_modules/knowledge-engine/src/runtime/knowledge-retrieval-service.js +400 -400
  20. package/_modules/knowledge-engine/src/runtime/markdown-knowledge-indexer.js +171 -171
  21. package/_modules/knowledge-engine/src/runtime/scanner.js +298 -298
  22. package/_modules/knowledge-engine/ui/app.js +23 -2
  23. package/_site/README.md +30 -30
  24. package/_site/_test/agent-capture-mcp-ui-test.js +68 -68
  25. package/_site/_test/agent-capture-ui-test.js +101 -101
  26. package/_site/_test/ai-profile-resolver-test.js +137 -137
  27. package/_site/_test/ai-profile-test.js +75 -75
  28. package/_site/_test/ai-vendor-presets-test.js +91 -91
  29. package/_site/_test/automatic-context-test.js +70 -70
  30. package/_site/_test/automation-queue-test.js +14 -14
  31. package/_site/_test/automation-ui-test.js +40 -40
  32. package/_site/_test/background-task-registry-test.js +43 -43
  33. package/_site/_test/baseline-schema-test.js +91 -91
  34. package/_site/_test/bin-cli-test.js +230 -230
  35. package/_site/_test/bridge-adapter-test.js +95 -95
  36. package/_site/_test/bridge-consumer-service-test.js +310 -310
  37. package/_site/_test/candidate-bin-content-test.js +35 -35
  38. package/_site/_test/candidate-product-assets-test.js +13 -13
  39. package/_site/_test/candidate-release-contract-test.js +14 -14
  40. package/_site/_test/capture-hook-dedup-test.js +158 -158
  41. package/_site/_test/cdp-browser-failure-test.js +103 -103
  42. package/_site/_test/chat-claudecodeui-match-test.js +92 -92
  43. package/_site/_test/claude-executable-discovery-test.js +90 -90
  44. package/_site/_test/claude-session-lifecycle-sweep-test.js +252 -252
  45. package/_site/_test/claude-workbench-test.js +174 -174
  46. package/_site/_test/cli-instance-management-test.js +62 -62
  47. package/_site/_test/client-capture-restart-e2e-test.js +189 -189
  48. package/_site/_test/codex-conversation-projection-test.js +60 -60
  49. package/_site/_test/codex-marketplace-scoped-path-test.js +186 -186
  50. package/_site/_test/codex-notify-capture-e2e-test.js +101 -101
  51. package/_site/_test/codex-notify-fanout-test.js +141 -134
  52. package/_site/_test/codex-notify-upgrade-test.js +253 -253
  53. package/_site/_test/commit-boundary-freeze-test.js +147 -147
  54. package/_site/_test/commit-conversation-binding-test.js +99 -99
  55. package/_site/_test/commit-evidence-test.js +110 -110
  56. package/_site/_test/configuration-generation-test.js +22 -22
  57. package/_site/_test/context-pack-test.js +200 -200
  58. package/_site/_test/conversation-api-test.js +119 -119
  59. package/_site/_test/conversation-origin-test.js +15 -15
  60. package/_site/_test/conversation-store-test.js +138 -138
  61. package/_site/_test/cross-repo-isolation-e2e-test.js +187 -187
  62. package/_site/_test/desktop-browser-compat-test.js +28 -28
  63. package/_site/_test/desktop-hook-runtime-regression-test.js +204 -204
  64. package/_site/_test/embedding-config-test.js +32 -32
  65. package/_site/_test/explicit-commit-processor-test.js +37 -37
  66. package/_site/_test/fixtures/fake-browser-exit.js +10 -10
  67. package/_site/_test/fixtures/fake-browser-hang.js +10 -10
  68. package/_site/_test/fixtures/integration-busy-worker.cjs +6 -6
  69. package/_site/_test/fixtures/make-git-repos.js +92 -92
  70. package/_site/_test/fixtures/retrieval-gold-queries.json +32 -32
  71. package/_site/_test/folder-picker-output-test.js +19 -19
  72. package/_site/_test/fresh-hook-reimport-test.js +53 -53
  73. package/_site/_test/fresh-product-data-test.js +56 -56
  74. package/_site/_test/full-integration-e2e-test.js +167 -167
  75. package/_site/_test/git-path-compatibility-test.js +80 -80
  76. package/_site/_test/git-validation-test.js +81 -81
  77. package/_site/_test/glm-ui-refresh-review-test.js +313 -404
  78. package/_site/_test/harness-isolation-test.js +138 -0
  79. package/_site/_test/helpers/cdp-browser.js +383 -0
  80. package/_site/_test/helpers/cdp-network-capture.js +66 -0
  81. package/_site/_test/helpers/find-chrome.js +34 -0
  82. package/_site/_test/helpers/free-port.js +8 -0
  83. package/_site/_test/helpers/logging-ui-fixture.js +131 -0
  84. package/_site/_test/helpers/module-fixture.js +282 -0
  85. package/_site/_test/helpers/parallel-engine-advanced-engine-child.js +45 -0
  86. package/_site/_test/helpers/parallel-engine-advanced-fault-writer.js +171 -0
  87. package/_site/_test/helpers/parallel-engine-advanced-harness.js +245 -0
  88. package/_site/_test/helpers/parallel-engine-advanced-standin-analyzer.js +80 -0
  89. package/_site/_test/helpers/parallel-engine-advanced-ui-host.js +177 -0
  90. package/_site/_test/helpers/parallel-engine-final-harness.js +314 -0
  91. package/_site/_test/helpers/parallel-engine-final-standin-analyzer.js +159 -0
  92. package/_site/_test/helpers/parallel-knowledge-fixture.js +255 -0
  93. package/_site/_test/helpers/parallel-logging-acceptance-fixture.js +111 -0
  94. package/_site/_test/helpers/parallel-logging-acceptance-proxy.js +106 -0
  95. package/_site/_test/helpers/parallel-logging-fix-r2-proxy.js +205 -0
  96. package/_site/_test/helpers/parallel-logging-integration-5025-env.js +432 -0
  97. package/_site/_test/helpers/parallel-memory-analyzer.js +81 -0
  98. package/_site/_test/helpers/parallel-memory-event-bridge.js +128 -0
  99. package/_site/_test/helpers/parallel-memory-shell.js +470 -0
  100. package/_site/_test/helpers/parallel-nav-isolation-env.js +389 -0
  101. package/_site/_test/helpers/parallel-nav-module-ui.js +186 -0
  102. package/_site/_test/helpers/parallel-nav-proxy.js +148 -0
  103. package/_site/_test/helpers/parallel-nav-shell.js +361 -0
  104. package/_site/_test/helpers/parallel-qa-engine-endpoint-guard.js +206 -0
  105. package/_site/_test/helpers/parallel-qa-engine-endpoint-isolate.js +257 -0
  106. package/_site/_test/helpers/parallel-retrieval-followup-corpus.js +293 -0
  107. package/_site/_test/helpers/parallel-retrieval-followup-next-session.js +70 -0
  108. package/_site/_test/helpers/parallel-retrieval-followup-r2-shell.js +737 -0
  109. package/_site/_test/helpers/parallel-retrieval-followup-r2-wb-host.js +82 -0
  110. package/_site/_test/helpers/parallel-retrieval-followup-shell.js +279 -0
  111. package/_site/_test/helpers/parallel-workbench-fixture.js +371 -0
  112. package/_site/_test/helpers/parallel-workbench-host.js +80 -0
  113. package/_site/_test/helpers/parallel-workbench-integration-5025-fixture.js +408 -0
  114. package/_site/_test/helpers/parallel-workbench-integration-5025-harness.js +593 -0
  115. package/_site/_test/helpers/parallel-workbench-integration-5025-host.js +70 -0
  116. package/_site/_test/helpers/parallel-workbench-integration-5025-launcher.js +240 -0
  117. package/_site/_test/helpers/parallel-workbench-integration-5025-runtime-harness.js +630 -0
  118. package/_site/_test/helpers/parallel-workbench-source-map.js +196 -0
  119. package/_site/_test/helpers/spawn-server.js +106 -0
  120. package/_site/_test/helpers/version-visibility-fixture.js +251 -0
  121. package/_site/_test/hook-runtime-endpoint-test.js +88 -88
  122. package/_site/_test/hook-status-repair-api-test.js +152 -152
  123. package/_site/_test/hook-trigger-test.js +85 -85
  124. package/_site/_test/hook-worktree-test.js +42 -42
  125. package/_site/_test/import-preflight-api-test.js +234 -234
  126. package/_site/_test/index-builder-test.js +41 -41
  127. package/_site/_test/index-error-display-contract-test.js +10 -10
  128. package/_site/_test/index-writer-concurrency-test.js +135 -135
  129. package/_site/_test/integration-adapters-test.js +262 -262
  130. package/_site/_test/integration-auto-repair-test.js +113 -113
  131. package/_site/_test/integration-capture-setup-test.js +118 -118
  132. package/_site/_test/integration-intent-test.js +81 -81
  133. package/_site/_test/integration-runtime-binding-a2-test.js +566 -566
  134. package/_site/_test/integration-runtime-binding-a3-test.js +227 -227
  135. package/_site/_test/integration-runtime-binding-test.js +473 -473
  136. package/_site/_test/integration-status-scan-test.js +29 -29
  137. package/_site/_test/integration-surface-coverage-test.js +164 -164
  138. package/_site/_test/kb-framework-test.js +57 -57
  139. package/_site/_test/kbpath-follow-test.js +63 -63
  140. package/_site/_test/knowledge-db-test.js +144 -144
  141. package/_site/_test/knowledge-engine-client-test.js +120 -109
  142. package/_site/_test/knowledge-language-control-test.js +170 -170
  143. package/_site/_test/knowledge-maintenance-test.js +40 -40
  144. package/_site/_test/knowledge-promotion-recovery-test.js +260 -260
  145. package/_site/_test/knowledge-query-test.js +54 -54
  146. package/_site/_test/knowledge-retrieval-service-test.js +77 -77
  147. package/_site/_test/knowledge-scopes-test.js +37 -37
  148. package/_site/_test/knowledge-storage-location-test.js +28 -28
  149. package/_site/_test/knowledge-storage-startup-test.js +52 -52
  150. package/_site/_test/knowledge-store-logs-supervision-test.js +89 -89
  151. package/_site/_test/launcher-readiness-test.js +80 -80
  152. package/_site/_test/legacy-claude-session-alias-test.js +106 -106
  153. package/_site/_test/legacy-routes-removed-test.js +47 -47
  154. package/_site/_test/legacy-workbench-exclusion-test.js +92 -92
  155. package/_site/_test/live-gateway-test.js +48 -48
  156. package/_site/_test/local-marketplace-test.js +16 -16
  157. package/_site/_test/log-redaction-test.js +58 -58
  158. package/_site/_test/logging-api-test.js +89 -89
  159. package/_site/_test/logging-sse-no-gap-test.js +88 -88
  160. package/_site/_test/logging-ui-test.js +107 -107
  161. package/_site/_test/markdown-delta-overlay-test.js +85 -85
  162. package/_site/_test/markdown-maintenance-api-test.js +75 -75
  163. package/_site/_test/markdown-maintenance-test.js +11 -11
  164. package/_site/_test/mcp-server-test.js +149 -149
  165. package/_site/_test/model-context-windows-test.js +89 -89
  166. package/_site/_test/module-agent-start-proxy-test.js +65 -65
  167. package/_site/_test/module-artifact-integrity-test.js +27 -27
  168. package/_site/_test/module-boundary-test.js +30 -30
  169. package/_site/_test/module-bridge-eventbridge-test.js +202 -202
  170. package/_site/_test/module-dynamic-port-test.js +118 -118
  171. package/_site/_test/module-index-proxy-test.js +33 -33
  172. package/_site/_test/module-model-configuration-test.js +44 -44
  173. package/_site/_test/module-process-lifecycle-test.js +35 -35
  174. package/_site/_test/module-stream-proxy-test.js +34 -34
  175. package/_site/_test/module-terminal-restart-test.js +63 -63
  176. package/_site/_test/module-wiring-unbind-race-test.js +59 -59
  177. package/_site/_test/native-context-integrations-test.js +86 -86
  178. package/_site/_test/non-release-ci-test.js +34 -34
  179. package/_site/_test/offline-boundary-isolation-test.js +32 -32
  180. package/_site/_test/orphaned-claim-recovery-test.js +80 -80
  181. package/_site/_test/package-boundary-test.js +43 -43
  182. package/_site/_test/package-startup-test.js +56 -56
  183. package/_site/_test/packaged-ui-smoke-test.js +88 -88
  184. package/_site/_test/parallel-ci-r9-posix-socket-budget-test.js +156 -0
  185. package/_site/_test/parallel-engine-advanced-eng005-test.js +136 -0
  186. package/_site/_test/parallel-engine-advanced-eng006-test.js +171 -0
  187. package/_site/_test/parallel-engine-advanced-eng007-reconcile-test.js +231 -0
  188. package/_site/_test/parallel-engine-advanced-eng009-test.js +208 -0
  189. package/_site/_test/parallel-engine-advanced-eng010-test.js +213 -0
  190. package/_site/_test/parallel-engine-advanced-eng011-ui-probe.js +459 -0
  191. package/_site/_test/parallel-engine-final-eng002-test.js +255 -0
  192. package/_site/_test/parallel-engine-final-eng007-test.js +269 -0
  193. package/_site/_test/parallel-engine-final-eng011-test.js +197 -0
  194. package/_site/_test/parallel-engine-final-eng012-test.js +354 -0
  195. package/_site/_test/parallel-knowledge-client-error-code-test.js +184 -0
  196. package/_site/_test/parallel-knowledge-engine-test.js +353 -0
  197. package/_site/_test/parallel-knowledge-retrieval-test.js +292 -0
  198. package/_site/_test/parallel-knowledge-vector-test.js +168 -0
  199. package/_site/_test/parallel-linux-ci-r7-log007-day-test.js +110 -0
  200. package/_site/_test/parallel-logging-acceptance-log003-test.js +224 -0
  201. package/_site/_test/parallel-logging-acceptance-log004-test.js +231 -0
  202. package/_site/_test/parallel-logging-acceptance-log005-test.js +208 -0
  203. package/_site/_test/parallel-logging-acceptance-log006-test.js +214 -0
  204. package/_site/_test/parallel-logging-acceptance-log007-test.js +223 -0
  205. package/_site/_test/parallel-logging-acceptance-log008-test.js +180 -0
  206. package/_site/_test/parallel-logging-fix-r2-crash-probe.js +271 -0
  207. package/_site/_test/parallel-logging-fix-r2-gap-test.js +294 -0
  208. package/_site/_test/parallel-logging-fix-r2-normalhook-test.js +330 -0
  209. package/_site/_test/parallel-logging-integration-5025-portability-test.js +554 -0
  210. package/_site/_test/parallel-memory-candidate-test.js +301 -0
  211. package/_site/_test/parallel-memory-manual-test.js +197 -0
  212. package/_site/_test/parallel-memory-scope-test.js +228 -0
  213. package/_site/_test/parallel-memory-source-test.js +227 -0
  214. package/_site/_test/parallel-memory-sync-test.js +238 -0
  215. package/_site/_test/parallel-memory-view-r8-reload-gate-test.js +123 -0
  216. package/_site/_test/parallel-nav-failure-test.js +119 -0
  217. package/_site/_test/parallel-nav-isolation-test.js +308 -0
  218. package/_site/_test/parallel-nav-isolation-vector-path-test.js +90 -0
  219. package/_site/_test/parallel-nav-offline-test.js +272 -0
  220. package/_site/_test/parallel-nav-operability-test.js +257 -0
  221. package/_site/_test/parallel-nav-switch-test.js +247 -0
  222. package/_site/_test/parallel-nav-theme-test.js +130 -0
  223. package/_site/_test/parallel-nav-viewport-test.js +310 -0
  224. package/_site/_test/parallel-qa-engine-endpoint-guard-test.js +273 -0
  225. package/_site/_test/parallel-retrieval-followup-citation-test.js +219 -0
  226. package/_site/_test/parallel-retrieval-followup-corpus-test.js +287 -0
  227. package/_site/_test/parallel-workbench-independent-host.js +262 -0
  228. package/_site/_test/parallel-workbench-integration-5025-abort-probe.js +340 -0
  229. package/_site/_test/parallel-workbench-integration-5025-drafts-probe.js +588 -0
  230. package/_site/_test/parallel-workbench-integration-5025-env-check.js +317 -0
  231. package/_site/_test/parallel-workbench-integration-5025-model-probe.js +342 -0
  232. package/_site/_test/parallel-workbench-integration-5025-ordering-probe.js +326 -0
  233. package/_site/_test/parallel-workbench-model-probe.js +336 -0
  234. package/_site/_test/parallel-workbench-offline-test.js +169 -0
  235. package/_site/_test/parallel-workbench-provenance-trace.js +134 -0
  236. package/_site/_test/parallel-workbench-source-match.js +206 -0
  237. package/_site/_test/parallel-workbench-standalone-turn.js +349 -0
  238. package/_site/_test/path-consistency-test.js +151 -151
  239. package/_site/_test/pending-sweep-test.js +9 -9
  240. package/_site/_test/post-commit-automation-test.js +87 -87
  241. package/_site/_test/pr-consumer-contract-test.js +55 -55
  242. package/_site/_test/product-brand-contract-test.js +25 -25
  243. package/_site/_test/product-diagnostics-ui-test.js +93 -93
  244. package/_site/_test/product-empty-state-test.js +148 -0
  245. package/_site/_test/product-import-ui-test.js +219 -104
  246. package/_site/_test/product-navigation-test.js +152 -0
  247. package/_site/_test/product-progress-status-test.js +84 -84
  248. package/_site/_test/project-delete-recovery-test.js +64 -64
  249. package/_site/_test/project-domain-create-test.js +91 -0
  250. package/_site/_test/project-domain-scope-retrieval-test.js +214 -0
  251. package/_site/_test/project-git-init-confirm-test.js +115 -0
  252. package/_site/_test/project-goal-editor-test.js +144 -144
  253. package/_site/_test/project-import-duplicate-alias-test.js +134 -0
  254. package/_site/_test/project-import-unicode-restart-test.js +140 -0
  255. package/_site/_test/project-lifecycle-transaction-test.js +106 -106
  256. package/_site/_test/project-module-partial-recovery-test.js +222 -0
  257. package/_site/_test/project-remove-running-guard-test.js +79 -79
  258. package/_site/_test/project-store-test.js +90 -90
  259. package/_site/_test/project-unbind-late-read-ui-test.js +250 -0
  260. package/_site/_test/project-unbind-writer-guard-test.js +231 -0
  261. package/_site/_test/prompt-settings-test.js +115 -115
  262. package/_site/_test/protected-architecture-gate-test.js +122 -122
  263. package/_site/_test/refactor-characterization-test.js +36 -36
  264. package/_site/_test/registry-events-test.js +38 -38
  265. package/_site/_test/release-version-sync-test.js +115 -115
  266. package/_site/_test/requirement-binding-test.js +113 -113
  267. package/_site/_test/requirement-recorder-test.js +173 -173
  268. package/_site/_test/run-all-tests.js +173 -161
  269. package/_site/_test/runtime-endpoint-test.js +64 -64
  270. package/_site/_test/scanner-test.js +83 -83
  271. package/_site/_test/server-security-test.js +96 -96
  272. package/_site/_test/sessions-stream-test.js +93 -93
  273. package/_site/_test/shared-contracts-test.js +58 -58
  274. package/_site/_test/shared-scope-root-binding-test.js +74 -67
  275. package/_site/_test/simple-import-test.js +98 -98
  276. package/_site/_test/startup-analysis-disabled-test.js +16 -16
  277. package/_site/_test/storage-foundation-test.js +78 -78
  278. package/_site/_test/structured-logger-test.js +101 -101
  279. package/_site/_test/t07b-cln-005-coverage-test.js +212 -0
  280. package/_site/_test/team-knowledge-store-sparse-checkout-test.js +196 -196
  281. package/_site/_test/team-knowledge-store-test.js +132 -132
  282. package/_site/_test/test-server-cleanup-test.js +35 -35
  283. package/_site/_test/test-server-module-isolation-test.js +37 -37
  284. package/_site/_test/tracking-start-test.js +144 -144
  285. package/_site/_test/vector-storage-root-test.js +75 -75
  286. package/_site/_test/version-visibility-browser-probe.js +249 -0
  287. package/_site/_test/version-visibility-test.js +92 -0
  288. package/_site/_test/workbench-context-receipts-test.js +22 -22
  289. package/_site/_test/workbench-permission-test.js +117 -117
  290. package/_site/_test/zcode-capture-integration-test.js +112 -112
  291. package/_site/lib/ai-profile-resolver.js +78 -78
  292. package/_site/lib/ai-vendor-presets.js +83 -83
  293. package/_site/lib/ai-workspace.js +104 -104
  294. package/_site/lib/atomic-file.js +204 -204
  295. package/_site/lib/automation-config.js +140 -140
  296. package/_site/lib/bridge-adapter.js +260 -260
  297. package/_site/lib/bridge-consumer-service.js +402 -402
  298. package/_site/lib/capture-hook-config.js +108 -108
  299. package/_site/lib/claude-cli-runner.js +1599 -1599
  300. package/_site/lib/cli-instances.js +43 -43
  301. package/_site/lib/codex-context-status.js +17 -17
  302. package/_site/lib/codex-notify-config.js +193 -193
  303. package/_site/lib/commit-conversation-binder.js +247 -247
  304. package/_site/lib/commit-processing-ledger.js +27 -27
  305. package/_site/lib/commit-prompt.js +202 -202
  306. package/_site/lib/commit-reconciler.js +444 -444
  307. package/_site/lib/context-pack-builder.js +299 -299
  308. package/_site/lib/contracts.js +291 -291
  309. package/_site/lib/conversation-exclusions.js +204 -204
  310. package/_site/lib/conversation-query-service.js +161 -161
  311. package/_site/lib/conversation-store.js +387 -387
  312. package/_site/lib/data-dir.js +13 -13
  313. package/_site/lib/embedding-service.js +85 -85
  314. package/_site/lib/engine-connection.js +93 -93
  315. package/_site/lib/engine-knowledge-client.js +76 -72
  316. package/_site/lib/evidence-bundle.js +228 -228
  317. package/_site/lib/folder-picker-output.js +24 -24
  318. package/_site/lib/git-path.js +14 -14
  319. package/_site/lib/git-runner.js +62 -62
  320. package/_site/lib/github-team-store.js +1175 -1175
  321. package/_site/lib/hook-manager.js +203 -203
  322. package/_site/lib/index-builder.js +157 -157
  323. package/_site/lib/index-service.js +173 -173
  324. package/_site/lib/integration-auto-repair.js +77 -77
  325. package/_site/lib/integration-installer.js +1240 -1240
  326. package/_site/lib/integration-intent.js +88 -88
  327. package/_site/lib/integration-status-scan.js +99 -99
  328. package/_site/lib/integration-status-worker.js +14 -14
  329. package/_site/lib/kb-framework.js +191 -191
  330. package/_site/lib/kb-validator.js +124 -124
  331. package/_site/lib/knowledge-db.js +363 -363
  332. package/_site/lib/knowledge-promotion.js +435 -435
  333. package/_site/lib/knowledge-retrieval-service.js +401 -401
  334. package/_site/lib/knowledge-schema.js +121 -121
  335. package/_site/lib/knowledge-tool-runtime.js +366 -366
  336. package/_site/lib/live-gateway.js +78 -78
  337. package/_site/lib/llm-client.js +173 -173
  338. package/_site/lib/markdown-knowledge-indexer.js +230 -230
  339. package/_site/lib/model-context-windows.js +82 -82
  340. package/_site/lib/module-bridge.js +874 -874
  341. package/_site/lib/module-port-resolver.js +57 -57
  342. package/_site/lib/post-commit-automation.js +141 -141
  343. package/_site/lib/product-data.js +35 -35
  344. package/_site/lib/project-lifecycle-service.js +554 -554
  345. package/_site/lib/project-registry-store.js +129 -129
  346. package/_site/lib/project-store.js +268 -268
  347. package/_site/lib/prompt-registry.js +148 -148
  348. package/_site/lib/public-version-info.js +37 -0
  349. package/_site/lib/registry-events.js +16 -16
  350. package/_site/lib/requirement-adapters.js +10 -10
  351. package/_site/lib/requirement-binder.js +112 -112
  352. package/_site/lib/requirement-recorder.js +278 -278
  353. package/_site/lib/runtime-control.js +89 -89
  354. package/_site/lib/runtime-endpoint.js +160 -160
  355. package/_site/lib/scanner.js +298 -298
  356. package/_site/lib/server-app.js +1672 -1664
  357. package/_site/lib/settings-store.js +153 -153
  358. package/_site/lib/storage-layout.js +166 -166
  359. package/_site/lib/structured-logger.js +574 -574
  360. package/_site/lib/zcode-context-integration.js +60 -60
  361. package/_site/scripts/codex-notify-fanout.cjs +95 -95
  362. package/_site/scripts/folder-picker.ps1 +156 -156
  363. package/_site/scripts/hook-trigger.js +158 -158
  364. package/_site/scripts/install-module-candidates.js +69 -69
  365. package/_site/scripts/pack-module-candidates.js +89 -89
  366. package/_site/scripts/sync-product-content.js +28 -28
  367. package/_site/scripts/sync-release-version.js +164 -164
  368. package/_site/scripts/vendor-modules.js +49 -49
  369. package/_site/scripts/verify-module-runtime.js +50 -50
  370. package/_site/scripts/verify-product-runtime.js +36 -36
  371. package/_site/scripts/verify-retrieval-gold.js +83 -83
  372. package/_site/server.js +15 -15
  373. package/_site/start.bat +26 -26
  374. package/_site/stop.bat +11 -11
  375. package/bin/project-knowledge-integrations.js +254 -254
  376. package/bin/project-knowledge-kb.js +67 -67
  377. package/bin/project-knowledge-mcp.js +201 -201
  378. package/bin/project-knowledge.js +269 -170
  379. package/claude-prompts.json +21 -21
  380. package/docs/README.zh-CN.md +207 -205
  381. package/docs/assets/architecture.svg +181 -181
  382. package/docs/assets/logo.svg +49 -49
  383. package/docs/fresh-v5-start.md +44 -44
  384. package/docs/leader-glm-workbench-playbook.md +122 -122
  385. package/docs/project-registry-schema.md +33 -33
  386. package/docs/release-5.0.16-acceptance.md +176 -176
  387. package/docs/release-5.0.17-acceptance.md +117 -117
  388. package/docs/release-5.0.19-acceptance.md +39 -39
  389. package/docs/team-knowledge-mode-a-plan.md +73 -73
  390. package/docs/testing-strategy.md +33 -33
  391. package/module-runtime-manifest.json +70 -67
  392. package/package.json +113 -110
  393. package/plugins/project-knowledge/.claude-plugin/plugin.json +1 -1
  394. package/plugins/project-knowledge/.codex-plugin/plugin.json +1 -1
  395. package/plugins/project-knowledge/.mcp.json +1 -1
  396. package/templates/change.md +30 -30
  397. package/templates/project-readme.md +52 -52
  398. package/ui/index.html +3 -2
  399. package/ui/version-info.css +44 -0
  400. package/ui/version-info.js +24 -0
  401. package/vendor-manifest.json +55 -55
@@ -0,0 +1,737 @@
1
+ 'use strict';
2
+ // Lane-owned helpers for the P-RETRIEVAL-FOLLOWUP-R2 round (RET-007-R2).
3
+ //
4
+ // PROVENANCE / OWNERSHIP
5
+ // ----------------------
6
+ // This file is a LANE-EXCLUSIVE SNAPSHOT of the P-MEMORY-INJECTION-02 lane's
7
+ // helper. That lane is still writing its file, so this lane COPIES the pieces
8
+ // it needs instead of importing them: the source is read-only here and is
9
+ // never edited, and the SHA-256 of the source is computed at run time so the
10
+ // copy can always be checked against its origin.
11
+ //
12
+ // piece source (read-only) owner
13
+ // -------------------------------------- ----------------------------------------- --------------------------
14
+ // isolation, read-only profile facade, _site/_test/helpers/parallel-memory-injection-shell.js P-MEMORY-INJECTION-02
15
+ // recording proxy, transcript scan,
16
+ // real Workbench turn
17
+ // host child shape _site/_test/helpers/parallel-memory-injection-wb-host.js P-MEMORY-INJECTION-02
18
+ //
19
+ // The chain helpers of THIS lane (real Git, real hook, real engine, real
20
+ // vector hub, real subscription profile) are NOT copied: they already live in
21
+ // the lane-owned _site/_test/helpers/parallel-retrieval-followup-shell.js and
22
+ // are required from there.
23
+ //
24
+ // Isolation contract (batch rules): every artefact lives under the shared
25
+ // external temp root (D:/SanQian.Xu/.project-knowledge-test-temp); the
26
+ // Workbench data dir, Bridge home, HOME/USERPROFILE and CLAUDE_CONFIG_DIR are
27
+ // isolated; the live product ports (5757/5760/5789/5790/8787/8790) are
28
+ // refused; ambient module addresses and every ambient Anthropic credential
29
+ // variable are dropped.
30
+ //
31
+ // Credential contract: the original subscription credential is read from its
32
+ // real location READ-ONLY and stays in memory. It is never written to a file,
33
+ // a log, a report or a clone, and the profile store handed to the host refuses
34
+ // every write method.
35
+ const fs = require('node:fs');
36
+ const os = require('node:os');
37
+ const path = require('node:path');
38
+ const http = require('node:http');
39
+ const net = require('node:net');
40
+ const crypto = require('node:crypto');
41
+ const { spawn, execFileSync } = require('node:child_process');
42
+
43
+ // The chain helpers of THIS lane (own file, not a snapshot).
44
+ const chain = require('./parallel-retrieval-followup-shell');
45
+
46
+ const REPO_ROOT = path.resolve(__dirname, '..', '..', '..');
47
+ const ENGINE_ENTRY = path.join(REPO_ROOT, '_modules', 'knowledge-engine', 'src', 'server.js');
48
+ const BRIDGE_CONSOLE_ENTRY = path.join(REPO_ROOT, 'node_modules', '@sanqianx', 'ai-coding-event-bridge-console', 'src', 'server.js');
49
+ const BRIDGE_REPO_CONTEXT = path.join(REPO_ROOT, 'node_modules', '@sanqianx', 'ai-coding-event-bridge-console', 'src', 'bridge', 'core', 'repo-context.js');
50
+
51
+ // The developer's live surfaces. A lane test may never bind or talk to them.
52
+ const FORBIDDEN_PORTS = new Set([5757, 5760, 5789, 5790, 8787, 8790]);
53
+
54
+ // Ambient module addresses that would point a test at a live instance.
55
+ const AMBIENT_MODULE_URLS = [
56
+ 'KB_KNOWLEDGE_ENGINE_URL',
57
+ 'KB_VECTORHUB_URL',
58
+ 'KB_TERMINAL_URL',
59
+ 'KB_EVENTBRIDGE_URL',
60
+ 'KB_EVENT_BRIDGE_URL',
61
+ 'KB_WORKBENCH_RUNTIME_TOKEN',
62
+ 'KB_MODULES_AUTOSTART',
63
+ 'KB_ENGINE_RUNTIME_TOKEN',
64
+ 'AI_CODING_EVENT_BRIDGE_HOME',
65
+ ];
66
+
67
+ // Ambient Anthropic credentials: the lane may only use the credential it
68
+ // injects itself, never one that happens to sit in the shell.
69
+ const AMBIENT_MODEL_ENV = [
70
+ 'ANTHROPIC_API_KEY',
71
+ 'ANTHROPIC_AUTH_TOKEN',
72
+ 'ANTHROPIC_BASE_URL',
73
+ 'ANTHROPIC_MODEL',
74
+ 'ANTHROPIC_DEFAULT_THINKING_MODEL',
75
+ 'ANTHROPIC_DEFAULT_HAIKU_MODEL',
76
+ 'ANTHROPIC_DEFAULT_SONNET_MODEL',
77
+ 'ANTHROPIC_DEFAULT_OPUS_MODEL',
78
+ 'API_TIMEOUT_MS',
79
+ 'CLAUDE_CODE_OAUTH_TOKEN',
80
+ 'CLAUDE_CODE_USE_BEDROCK',
81
+ 'CLAUDE_CODE_USE_VERTEX',
82
+ 'CLAUDE_CODE_USE_FOUNDRY',
83
+ ];
84
+
85
+ // Captured before any isolation rewrites HOME.
86
+ const REAL_HOME = os.homedir();
87
+ const REAL_USERPROFILE = process.env.USERPROFILE || REAL_HOME;
88
+
89
+ const SNAPSHOT_SOURCES = [
90
+ { piece: 'isolation, read-only profile facade, recording proxy, transcript scan, real Workbench turn', source: '_site/_test/helpers/parallel-memory-injection-shell.js', owner: 'P-MEMORY-INJECTION-02 lane (untracked, still being written)' },
91
+ { piece: 'host child shape', source: '_site/_test/helpers/parallel-memory-injection-wb-host.js', owner: 'P-MEMORY-INJECTION-02 lane (untracked, still being written)' },
92
+ { piece: 'chain helpers (real Git, real engine, real vector hub, real subscription profile)', source: '_site/_test/helpers/parallel-retrieval-followup-shell.js', owner: 'this lane (P-RETRIEVAL-FOLLOWUP)' },
93
+ ];
94
+
95
+ function sha256File(file) {
96
+ try { return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex'); } catch { return null; }
97
+ }
98
+
99
+ // The provenance of every snapshot, with the source hash computed now.
100
+ function snapshotProvenance() {
101
+ return SNAPSHOT_SOURCES.map(entry => {
102
+ const absolute = path.join(REPO_ROOT, ...entry.source.split('/'));
103
+ return { ...entry, sourceSha256: sha256File(absolute), sourceExists: fs.existsSync(absolute) };
104
+ });
105
+ }
106
+
107
+ function isInside(candidate, parent) {
108
+ const relative = path.relative(parent, candidate);
109
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
110
+ }
111
+
112
+ // The batch contract pins an external temp root next to the repositories.
113
+ function resolveTempRoot() {
114
+ const sharedRoot = path.join(path.dirname(REPO_ROOT), '.project-knowledge-test-temp');
115
+ const candidates = [
116
+ process.env.PK_TEST_TEMP_ROOT,
117
+ fs.existsSync(sharedRoot) ? sharedRoot : null,
118
+ process.env.TEMP,
119
+ process.env.TMP,
120
+ process.env.TMPDIR,
121
+ ];
122
+ const chosen = candidates.find(value => value && String(value).trim());
123
+ const root = path.resolve(chosen ? String(chosen).trim() : os.tmpdir());
124
+ if (isInside(root, REPO_ROOT)) throw new Error(`refusing to use a temp root inside the repository: ${root}`);
125
+ return root;
126
+ }
127
+
128
+ function makeTempDir(prefix = 'parallel-memory-injection') {
129
+ const root = resolveTempRoot();
130
+ fs.mkdirSync(root, { recursive: true });
131
+ const dir = fs.mkdtempSync(path.join(root, `${prefix}-`));
132
+ if (isInside(dir, REPO_ROOT)) throw new Error(`refusing to create a fixture inside the repository: ${dir}`);
133
+ return dir;
134
+ }
135
+
136
+ function removeTree(dir) {
137
+ if (!dir) return;
138
+ fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
139
+ }
140
+
141
+ // Windows keeps a directory locked for a short while after the process that
142
+ // used it exits (and the AV scanner may hold it too), so a single rmSync can
143
+ // fail with EPERM even though nothing is still running. Retry with a delay
144
+ // and report the outcome instead of throwing, so a leftover scratch folder
145
+ // never turns a green evidence run into a script error.
146
+ async function removeTreeAsync(dir, { attempts = 12, delayMs = 500 } = {}) {
147
+ if (!dir) return { removed: true, path: dir, error: null };
148
+ let lastError = null;
149
+ for (let attempt = 1; attempt <= attempts; attempt += 1) {
150
+ try {
151
+ fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
152
+ return { removed: true, path: dir, error: null, attempts: attempt };
153
+ } catch (error) {
154
+ lastError = error;
155
+ if (!fs.existsSync(dir)) return { removed: true, path: dir, error: null, attempts: attempt };
156
+ await new Promise(resolve => setTimeout(resolve, delayMs));
157
+ }
158
+ }
159
+ return { removed: false, path: dir, attempts, error: { code: lastError.code, message: lastError.message } };
160
+ }
161
+
162
+ async function freePort() {
163
+ return new Promise((resolve, reject) => {
164
+ const server = net.createServer();
165
+ server.unref();
166
+ server.on('error', reject);
167
+ server.listen(0, '127.0.0.1', () => {
168
+ const { port } = server.address();
169
+ server.close(() => {
170
+ if (FORBIDDEN_PORTS.has(Number(port))) { reject(new Error(`refusing to use the live port ${port}`)); return; }
171
+ resolve(Number(port));
172
+ });
173
+ });
174
+ });
175
+ }
176
+
177
+ async function waitForHttp(url, timeoutMs = 30000) {
178
+ const parsed = new URL(url);
179
+ const port = parsed.port ? Number(parsed.port) : (parsed.protocol === 'https:' ? 443 : 80);
180
+ if (FORBIDDEN_PORTS.has(port)) throw new Error(`refusing to contact the live service ${url}`);
181
+ const until = Date.now() + timeoutMs;
182
+ let lastError = null;
183
+ while (Date.now() < until) {
184
+ try {
185
+ const response = await fetch(url, { signal: AbortSignal.timeout(2000) });
186
+ if (response.ok) return true;
187
+ lastError = new Error(`HTTP ${response.status}`);
188
+ } catch (error) { lastError = error; }
189
+ await new Promise(resolve => setTimeout(resolve, 100));
190
+ }
191
+ throw new Error(`Timed out waiting for ${url}: ${lastError && lastError.message}`);
192
+ }
193
+
194
+ function git(cwd, args) {
195
+ const result = execFileSync('git', ['-C', cwd, ...args], { encoding: 'utf8', windowsHide: true, env: { ...process.env, GIT_TERMINAL_PROMPT: '0' } });
196
+ return String(result || '').trim();
197
+ }
198
+
199
+ // A real Git repository with a real commit: the engine resolves a project by
200
+ // the Git toplevel, and the Bridge derives the repo identity from it.
201
+ function makeGitRepo(root, name) {
202
+ const repo = path.join(root, name);
203
+ fs.mkdirSync(repo, { recursive: true });
204
+ git(repo, ['init', '-q', '-b', 'main']);
205
+ git(repo, ['config', 'user.email', 'parallel-memory-injection@local.invalid']);
206
+ git(repo, ['config', 'user.name', 'Parallel Memory Injection Lane']);
207
+ git(repo, ['config', 'commit.gpgsign', 'false']);
208
+ fs.writeFileSync(path.join(repo, 'README.md'), `# ${name}\n`, 'utf8');
209
+ git(repo, ['add', '.']);
210
+ git(repo, ['commit', '-qm', 'baseline']);
211
+ return { path: repo, headCommit: git(repo, ['rev-parse', 'HEAD']) };
212
+ }
213
+
214
+ function sanitizedEnv(extra = {}) {
215
+ const env = { ...process.env, ...extra };
216
+ for (const key of AMBIENT_MODULE_URLS) delete env[key];
217
+ for (const key of AMBIENT_MODEL_ENV) delete env[key];
218
+ return env;
219
+ }
220
+
221
+ function spawnNode(args, env = {}) {
222
+ const child = spawn(process.execPath, args, { env: sanitizedEnv(env), windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] });
223
+ const output = { stdout: '', stderr: '' };
224
+ child.stdout.on('data', chunk => { output.stdout += chunk; });
225
+ child.stderr.on('data', chunk => { output.stderr += chunk; });
226
+ const exited = new Promise(resolve => child.once('close', code => resolve({ code, ...output })));
227
+ return { child, exited, output };
228
+ }
229
+
230
+ // The isolated directories of one run.
231
+ function createIsolationLayout(prefix = 'parallel-memory-injection') {
232
+ const root = makeTempDir(prefix);
233
+ const layout = {
234
+ root,
235
+ dataDir: path.join(root, 'workbench-data'),
236
+ knowledgeDataDir: path.join(root, 'knowledge-data'),
237
+ bridgeHome: path.join(root, 'bridge-home'),
238
+ engineDataDir: path.join(root, 'engine-data'),
239
+ brainKnowledgeDir: path.join(root, 'brain-knowledge'),
240
+ projectKnowledgeDir: path.join(root, 'project-knowledge'),
241
+ controlKnowledgeDir: path.join(root, 'control-knowledge'),
242
+ home: path.join(root, 'home'),
243
+ claudeConfigDir: path.join(root, 'claude-config'),
244
+ temp: path.join(root, 'temp'),
245
+ repos: path.join(root, 'repos'),
246
+ };
247
+ for (const dir of Object.values(layout)) {
248
+ if (typeof dir === 'string') fs.mkdirSync(dir, { recursive: true });
249
+ }
250
+ return layout;
251
+ }
252
+
253
+ // The environment a host child may inherit.
254
+ function hostEnv(layout, extra = {}) {
255
+ const parsed = path.parse(layout.home);
256
+ return sanitizedEnv({
257
+ // The real profile directory is passed explicitly: the child's isolated
258
+ // HOME would otherwise hide it from the read-only profile facade.
259
+ PK_WB_REAL_PROFILE_DIR: realWorkbenchProfileDir(),
260
+ HOME: layout.home,
261
+ USERPROFILE: layout.home,
262
+ HOMEDRIVE: parsed.root,
263
+ HOMEPATH: parsed.root === path.sep ? layout.home.slice(1) : layout.home.slice(parsed.root.length),
264
+ CLAUDE_CONFIG_DIR: layout.claudeConfigDir,
265
+ TEMP: layout.temp,
266
+ TMP: layout.temp,
267
+ TMPDIR: layout.temp,
268
+ KB_DATA_DIR: layout.knowledgeDataDir,
269
+ KB_KNOWLEDGE_DATA_DIR: layout.knowledgeDataDir,
270
+ AI_CODING_EVENT_BRIDGE_HOME: layout.bridgeHome,
271
+ AGENT_TERMINAL_DATA_DIR: layout.dataDir,
272
+ CLAUDE_AI_WORKBENCH_DATA_DIR: layout.dataDir,
273
+ KB_MODULES_AUTOSTART: '0',
274
+ ...extra,
275
+ });
276
+ }
277
+
278
+ // Where the product keeps its profiles. Resolved from the real home, never
279
+ // from an isolated one.
280
+ function realWorkbenchProfileDir() {
281
+ const override = String(process.env.PK_WB_REAL_PROFILE_DIR || '').trim();
282
+ if (override) return path.resolve(override);
283
+ return path.join(REAL_HOME, '.project-knowledge-v5', 'workbench');
284
+ }
285
+
286
+ // Byte-level snapshot of a flat directory. The SHA-256 digests stay in the
287
+ // process memory: only counts and file NAMES ever leave compareDigests, so a
288
+ // digest of a credential file is never printed, logged or persisted.
289
+ function digestDirectory(dir) {
290
+ if (!dir || !fs.existsSync(dir)) return null;
291
+ const entries = {};
292
+ for (const name of fs.readdirSync(dir)) {
293
+ try {
294
+ const file = path.join(dir, name);
295
+ if (!fs.statSync(file).isFile()) continue;
296
+ entries[name] = crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
297
+ } catch { entries[name] = null; }
298
+ }
299
+ return entries;
300
+ }
301
+
302
+ function compareDigests(before, after) {
303
+ const names = new Set([...Object.keys(before || {}), ...Object.keys(after || {})]);
304
+ const changed = [], added = [], removed = [], unreadable = [];
305
+ for (const name of names) {
306
+ const a = before ? before[name] : undefined;
307
+ const b = after ? after[name] : undefined;
308
+ if (a === undefined && b !== undefined) added.push(name);
309
+ else if (a !== undefined && b === undefined) removed.push(name);
310
+ else if (a === null || b === null) unreadable.push(name);
311
+ else if (a !== b) changed.push(name);
312
+ }
313
+ return {
314
+ count: names.size,
315
+ byteIdentical: changed.length === 0 && added.length === 0 && removed.length === 0 && unreadable.length === 0,
316
+ changedFiles: changed,
317
+ addedFiles: added,
318
+ removedFiles: removed,
319
+ unreadableFiles: unreadable,
320
+ };
321
+ }
322
+
323
+ // A profile store that reads the original profiles and credentials but
324
+ // refuses every write. The credential stays in memory: it is never written
325
+ // anywhere by this lane.
326
+ function createReadOnlyProfileStore(moduleRoot, layout, options = {}) {
327
+ const profileDir = options.profileDir || realWorkbenchProfileDir();
328
+ const profileFile = path.join(profileDir, 'profiles.v2.json');
329
+ const credentialFile = path.join(profileDir, 'credentials.v1.json');
330
+ if (!fs.existsSync(profileFile)) throw new Error(`the original profile file is missing: ${profileFile}`);
331
+ const { ProfileStore } = require(path.join(moduleRoot, 'packages', 'server', 'lib', 'profile-store.js'));
332
+ const store = new ProfileStore({ dataDir: layout.dataDir, profileFile, credentialFile });
333
+ const WRITE_METHODS = new Set(['save', 'delete', 'setCredential', 'setEmbeddingConfig', 'setMaintenanceDefault', 'importLegacy', 'importEmbedding']);
334
+ return new Proxy(store, {
335
+ get(target, property) {
336
+ if (WRITE_METHODS.has(property)) {
337
+ return () => { throw Object.assign(new Error('the profile store of this fixture is read-only'), { status: 409 }); };
338
+ }
339
+ const value = target[property];
340
+ return typeof value === 'function' ? value.bind(target) : value;
341
+ },
342
+ });
343
+ }
344
+
345
+ // The module that is hosted: the copy the installed product ships, unless the
346
+ // caller pins another one explicitly.
347
+ function resolveWorkbenchModuleRoot(options = {}) {
348
+ const override = String(options.moduleRoot || process.env.PK_WB_MODULE_ROOT || '').trim();
349
+ if (override) return path.resolve(override);
350
+ const appData = String(process.env.APPDATA || '').trim();
351
+ if (appData) {
352
+ const candidate = path.join(appData, 'npm', 'node_modules', '@sanqianx', 'project-knowledge', 'node_modules', 'claude-ai-workbench');
353
+ if (fs.existsSync(path.join(candidate, 'packages', 'server', 'package.json'))) return candidate;
354
+ }
355
+ const repoPin = path.join(REPO_ROOT, 'node_modules', 'claude-ai-workbench');
356
+ if (fs.existsSync(path.join(repoPin, 'packages', 'server', 'package.json'))) return repoPin;
357
+ return null;
358
+ }
359
+
360
+ function packageIdentity(moduleRoot) {
361
+ try {
362
+ const manifest = JSON.parse(fs.readFileSync(path.join(moduleRoot, 'packages', 'server', 'package.json'), 'utf8'));
363
+ return { name: manifest.name, version: manifest.version };
364
+ } catch { return { name: null, version: null }; }
365
+ }
366
+
367
+ function safeJson(raw) {
368
+ const text = Buffer.isBuffer(raw) ? raw.toString('utf8') : String(raw || '');
369
+ try { return JSON.parse(text); } catch { return text.length > 200000 ? `${text.slice(0, 200000)}…[truncated]` : text; }
370
+ }
371
+
372
+ // An observation-only transparent proxy in front of the real engine: it
373
+ // forwards every request byte-for-byte and records both directions. It never
374
+ // rewrites, filters or synthesises a response, so what the Workbench module
375
+ // received is what the engine produced.
376
+ async function startRecordingProxy({ target, label = 'engine' }) {
377
+ const targetUrl = new URL(target);
378
+ const records = [];
379
+ const server = http.createServer((req, res) => {
380
+ const chunks = [];
381
+ req.on('data', chunk => chunks.push(chunk));
382
+ req.on('end', async () => {
383
+ const body = Buffer.concat(chunks);
384
+ const entry = { label, method: req.method, url: req.url, requestBytes: body.length, requestBody: safeJson(body), at: new Date().toISOString() };
385
+ try {
386
+ const headers = { ...req.headers };
387
+ delete headers.host;
388
+ delete headers['accept-encoding'];
389
+ const response = await fetch(new URL(req.url, targetUrl), {
390
+ method: req.method,
391
+ headers,
392
+ body: body.length ? body : undefined,
393
+ signal: AbortSignal.timeout(15000),
394
+ });
395
+ const text = await response.text();
396
+ entry.responseStatus = response.status;
397
+ entry.responseBody = safeJson(text);
398
+ records.push(entry);
399
+ res.writeHead(response.status, { 'Content-Type': response.headers.get('content-type') || 'application/json' });
400
+ res.end(text);
401
+ } catch (error) {
402
+ entry.error = error.message;
403
+ records.push(entry);
404
+ res.writeHead(502, { 'Content-Type': 'application/json' });
405
+ res.end(JSON.stringify({ ok: false, error: 'PROXY_TARGET_UNAVAILABLE' }));
406
+ }
407
+ });
408
+ });
409
+ await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
410
+ const port = server.address().port;
411
+ if (FORBIDDEN_PORTS.has(Number(port))) throw new Error(`refusing to use the live port ${port}`);
412
+ return {
413
+ url: `http://127.0.0.1:${port}`,
414
+ records,
415
+ requests: () => records.filter(entry => entry.method === 'POST'),
416
+ close: () => new Promise(done => server.close(() => done())),
417
+ };
418
+ }
419
+
420
+ // An ISOLATED stand-in for the vector hub. The engine must never reach the
421
+ // live 8787 instance, and this lane makes no vector claim: the stand-in
422
+ // answers an empty result set, so retrieval stays lexical-only. Vector
423
+ // evidence is explicitly NOT produced here.
424
+ async function startVectorHubStandIn() {
425
+ const requests = [];
426
+ const server = http.createServer((req, res) => {
427
+ requests.push({ url: req.url, at: new Date().toISOString() });
428
+ res.writeHead(200, { 'Content-Type': 'application/json' });
429
+ res.end(JSON.stringify({ ok: true, results: [], model: 'lane-stand-in-lexical-only', dimensions: 0 }));
430
+ });
431
+ await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
432
+ const port = server.address().port;
433
+ if (FORBIDDEN_PORTS.has(Number(port))) throw new Error(`refusing to use the live port ${port}`);
434
+ return {
435
+ url: `http://127.0.0.1:${port}`,
436
+ requests,
437
+ kind: 'isolated-stand-in (empty result set, lexical-only, NOT vector evidence)',
438
+ close: () => new Promise(done => server.close(() => done())),
439
+ };
440
+ }
441
+
442
+ // The REAL knowledge engine, in-process, on its own isolated data dir.
443
+ async function startRealEngine({ dataDir, runtimeToken, debug = true, consume = false, bridgeUrl, vectorUrl }) {
444
+ // Fail-closed endpoint guard: the coordinator ignores env and silently
445
+ // defaults its vector/bridge legs to LIVE 8787/8790, so an engine without
446
+ // explicit OWN endpoints is refused instead of defaulting live.
447
+ const { assertOwnedEndpoint } = require('./parallel-qa-engine-endpoint-guard');
448
+ assertOwnedEndpoint(vectorUrl, { role: 'vectorUrl' });
449
+ assertOwnedEndpoint(bridgeUrl, { role: 'bridgeUrl' });
450
+ const { createEngineServer } = require(ENGINE_ENTRY);
451
+ const engine = createEngineServer({
452
+ dataDir,
453
+ runtimeToken,
454
+ debug: debug === true,
455
+ consume: consume === true,
456
+ bridgeUrl,
457
+ vectorUrl,
458
+ });
459
+ await engine.ready;
460
+ await new Promise(resolve => engine.server.listen(0, '127.0.0.1', resolve));
461
+ const port = engine.server.address().port;
462
+ if (FORBIDDEN_PORTS.has(Number(port))) throw new Error(`refusing to use the live port ${port}`);
463
+ const url = `http://127.0.0.1:${port}`;
464
+ const api = async (method, route, body) => {
465
+ const response = await fetch(url + route, {
466
+ method,
467
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${runtimeToken}` },
468
+ body: body === undefined ? undefined : JSON.stringify(body),
469
+ signal: AbortSignal.timeout(20000),
470
+ });
471
+ return { status: response.status, body: await response.json().catch(() => null) };
472
+ };
473
+ return {
474
+ engine,
475
+ url,
476
+ token: runtimeToken,
477
+ api,
478
+ close: () => engine.close().catch(() => {}),
479
+ };
480
+ }
481
+
482
+ // The REAL event-bridge console, in-process, on its own isolated home.
483
+ async function startBridgeConsole({ home, runtimeToken }) {
484
+ const { createConsoleServer } = require(BRIDGE_CONSOLE_ENTRY);
485
+ const handler = createConsoleServer({ home, runtimeToken });
486
+ const server = http.createServer(handler);
487
+ await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
488
+ const port = server.address().port;
489
+ if (FORBIDDEN_PORTS.has(Number(port))) throw new Error(`refusing to use the live port ${port}`);
490
+ const url = `http://127.0.0.1:${port}`;
491
+ const api = async (method, route, body) => {
492
+ const response = await fetch(url + route, {
493
+ method,
494
+ headers: { 'Content-Type': 'application/json', ...(route.startsWith('/api/internal/') ? { Authorization: `Bearer ${runtimeToken}` } : {}) },
495
+ body: body === undefined ? undefined : JSON.stringify(body),
496
+ signal: AbortSignal.timeout(20000),
497
+ });
498
+ return { status: response.status, body: await response.json().catch(() => null) };
499
+ };
500
+ return { url, api, close: () => new Promise(done => server.close(() => done())) };
501
+ }
502
+
503
+ // The repo identity the real Bridge derives for a directory.
504
+ async function resolveRepoIdentity(dir) {
505
+ const { resolveRepoContext } = require(BRIDGE_REPO_CONTEXT);
506
+ return resolveRepoContext(dir);
507
+ }
508
+
509
+ function walkFiles(root, visit) {
510
+ let names;
511
+ try { names = fs.readdirSync(root, { withFileTypes: true }); } catch { return; }
512
+ for (const entry of names) {
513
+ const full = path.join(root, entry.name);
514
+ if (entry.isDirectory()) walkFiles(full, visit);
515
+ else if (entry.isFile()) visit(full);
516
+ }
517
+ }
518
+
519
+ // Read the CLI transcript of THIS run and report only what the evidence
520
+ // layers need: whether the request that carried the round token also carried
521
+ // the unique convention, and which model the vendor returned. The transcript
522
+ // itself is never copied anywhere.
523
+ function scanClaudeTranscript(roots, { claudeSessionId, token, marker }) {
524
+ const files = [];
525
+ const userMessages = [];
526
+ const assistantModels = new Set();
527
+ let assistantCount = 0;
528
+ for (const root of roots) {
529
+ walkFiles(root, file => {
530
+ if (!file.endsWith('.jsonl')) return;
531
+ let text;
532
+ try { text = fs.readFileSync(file, 'utf8'); } catch { return; }
533
+ if (claudeSessionId && !text.includes(claudeSessionId)) return;
534
+ files.push(path.basename(file));
535
+ for (const line of text.split(/\r?\n/)) {
536
+ let entry;
537
+ try { entry = JSON.parse(line); } catch { continue; }
538
+ const sessionMatches = !claudeSessionId || entry.sessionId === claudeSessionId;
539
+ if (entry.type === 'user' && entry.message && sessionMatches) {
540
+ const content = Array.isArray(entry.message.content) ? entry.message.content : [];
541
+ const text_ = content.map(block => (block && typeof block.text === 'string' ? block.text : '')).join('\n');
542
+ if (!text_) continue;
543
+ const markerAt = marker ? text_.indexOf(marker) : -1;
544
+ userMessages.push({
545
+ sessionId: entry.sessionId || null,
546
+ hasToken: token ? text_.includes(token) : null,
547
+ hasMarker: marker ? markerAt >= 0 : null,
548
+ hasKnowledgeContext: text_.includes('<knowledge_context>'),
549
+ length: text_.length,
550
+ markerExcerpt: markerAt >= 0 ? text_.slice(Math.max(0, markerAt - 120), markerAt + 260) : null,
551
+ });
552
+ }
553
+ if (entry.type === 'assistant' && entry.message && sessionMatches) {
554
+ assistantCount += 1;
555
+ if (typeof entry.message.model === 'string' && entry.message.model) assistantModels.add(entry.message.model);
556
+ }
557
+ }
558
+ });
559
+ }
560
+ return {
561
+ transcriptFiles: files,
562
+ userMessages,
563
+ assistantCount,
564
+ assistantModelValues: [...assistantModels],
565
+ };
566
+ }
567
+
568
+ async function getJson(url, options) {
569
+ const response = await fetch(url, { ...options, signal: AbortSignal.timeout(30000) });
570
+ const body = await response.json().catch(() => null);
571
+ return { status: response.status, ok: response.ok, body };
572
+ }
573
+
574
+ // Consume the Workbench session event stream until the terminal event arrives.
575
+ async function consumeSessionEvents(url, onEvent, until, timeoutMs) {
576
+ const controller = new AbortController();
577
+ const timer = setTimeout(() => controller.abort(new Error(`event stream timed out after ${timeoutMs}ms`)), timeoutMs);
578
+ let done = null;
579
+ const finished = new Promise(resolve => { done = resolve; });
580
+ (async () => {
581
+ try {
582
+ const response = await fetch(url, { signal: controller.signal });
583
+ if (!response.ok) throw new Error(`event stream HTTP ${response.status}`);
584
+ const reader = response.body.getReader();
585
+ const decoder = new TextDecoder();
586
+ let buffer = '';
587
+ for (;;) {
588
+ const { value, done: streamDone } = await reader.read();
589
+ if (streamDone) break;
590
+ buffer += decoder.decode(value, { stream: true });
591
+ let index;
592
+ while ((index = buffer.indexOf('\n')) >= 0) {
593
+ const line = buffer.slice(0, index).replace(/\r$/, '');
594
+ buffer = buffer.slice(index + 1);
595
+ if (!line.startsWith('data:')) continue;
596
+ const payload = line.slice(5).trim();
597
+ if (!payload) continue;
598
+ let event;
599
+ try { event = JSON.parse(payload); } catch { continue; }
600
+ try { onEvent(event); } catch { /* listener errors never kill the stream */ }
601
+ if (until(event)) { controller.abort(); return; }
602
+ }
603
+ }
604
+ } catch { /* aborted or ended */ }
605
+ finally { done && done(); }
606
+ })();
607
+ await finished;
608
+ clearTimeout(timer);
609
+ }
610
+
611
+ // One real turn against the hosted Workbench module.
612
+ //
613
+ // sessionInputOrigin is what the real client sends when it creates a session
614
+ // (packages/client: opts.inputOrigin || 'human'). It is OMITTED when the
615
+ // caller did not declare one, exactly like the module's own dispatcher child.
616
+ // inputOptions is the option object the input endpoint receives; the module's
617
+ // dispatcher passes { inputOrigin: 'agent-delegation', permissionMode, effort }.
618
+ async function runWorkbenchTurn({
619
+ base, profileId, model, effort, workspaceRef, contextId, question,
620
+ sessionInputOrigin, inputOptions, timeoutMs = 420000,
621
+ }) {
622
+ const sessionBody = {
623
+ context: { contextId, workspaceRef },
624
+ aiProfileId: profileId,
625
+ model,
626
+ effort,
627
+ };
628
+ if (sessionInputOrigin !== undefined) sessionBody.inputOrigin = sessionInputOrigin;
629
+ const session = await getJson(`${base}/api/claude-workbench/v1/sessions`, {
630
+ method: 'POST',
631
+ headers: { 'Content-Type': 'application/json' },
632
+ body: JSON.stringify(sessionBody),
633
+ });
634
+ if (session.status !== 201) throw Object.assign(new Error(`the session was not created: HTTP ${session.status} ${JSON.stringify(session.body)}`), { status: session.status });
635
+ const sessionId = session.body.session.sessionId;
636
+
637
+ const events = [];
638
+ // The CLI's own stderr (provider refusals, 429s, crashes) arrives as
639
+ // claude/stderr events. They are collected separately so a failed turn can
640
+ // report the REAL provider error instead of "isError with no diagnostics".
641
+ const stderrEvents = [];
642
+ const isTerminal = event => event.type === 'claude/result';
643
+ const streamUrl = `${base}/api/claude-workbench/v1/sessions/${encodeURIComponent(sessionId)}/events`;
644
+ const consume = consumeSessionEvents(streamUrl, event => {
645
+ if (event.type === 'claude/stderr') { stderrEvents.push(String(event.text || event.message || '')); return; }
646
+ if (['claude/init', 'claude/result', 'claude/error', 'claude/aborted'].includes(event.type)) events.push(event);
647
+ }, isTerminal, timeoutMs + 60000).catch(() => {});
648
+
649
+ const inputBody = { text: question };
650
+ if (inputOptions !== undefined) inputBody.options = inputOptions;
651
+ const send = await getJson(`${base}/api/claude-workbench/v1/sessions/${encodeURIComponent(sessionId)}/input`, {
652
+ method: 'POST',
653
+ headers: { 'Content-Type': 'application/json' },
654
+ body: JSON.stringify(inputBody),
655
+ });
656
+ if (send.status !== 202) {
657
+ await Promise.race([consume, new Promise(resolve => setTimeout(resolve, 2000))]);
658
+ throw Object.assign(new Error(`the input was not accepted: HTTP ${send.status} ${JSON.stringify(send.body)}`), { status: send.status });
659
+ }
660
+
661
+ const deadline = Date.now() + timeoutMs;
662
+ while (Date.now() < deadline && !events.some(isTerminal)) {
663
+ await new Promise(resolve => setTimeout(resolve, 250));
664
+ }
665
+ await Promise.race([consume, new Promise(resolve => setTimeout(resolve, 5000))]);
666
+
667
+ const init = events.find(event => event.type === 'claude/init') || null;
668
+ const result = events.find(event => event.type === 'claude/result') || null;
669
+ const error = events.find(event => event.type === 'claude/error') || null;
670
+ return {
671
+ sessionId,
672
+ events,
673
+ init,
674
+ result,
675
+ error,
676
+ stderrEvents,
677
+ claudeSessionId: init ? init.claudeSessionId : null,
678
+ sdkReportedModel: init ? init.model : null,
679
+ answer: result ? String(result.result || '') : '',
680
+ isError: result ? result.isError === true : true,
681
+ usage: result && result.usage ? { inputTokens: result.usage.input_tokens, outputTokens: result.usage.output_tokens } : null,
682
+ };
683
+ }
684
+
685
+ async function eventually(fn, timeoutMs = 15000, intervalMs = 100) {
686
+ const until = Date.now() + timeoutMs;
687
+ let last;
688
+ while (Date.now() < until) {
689
+ try { last = await fn(); if (last) return last; } catch (error) { last = error.message; }
690
+ await new Promise(resolve => setTimeout(resolve, intervalMs));
691
+ }
692
+ return null;
693
+ }
694
+
695
+ module.exports = {
696
+ chain,
697
+ REPO_ROOT,
698
+ ENGINE_ENTRY,
699
+ BRIDGE_CONSOLE_ENTRY,
700
+ FORBIDDEN_PORTS,
701
+ AMBIENT_MODULE_URLS,
702
+ AMBIENT_MODEL_ENV,
703
+ REAL_HOME,
704
+ REAL_USERPROFILE,
705
+ SNAPSHOT_SOURCES,
706
+ snapshotProvenance,
707
+ sha256File,
708
+ isInside,
709
+ resolveTempRoot,
710
+ makeTempDir,
711
+ removeTree,
712
+ removeTreeAsync,
713
+ freePort,
714
+ waitForHttp,
715
+ git,
716
+ makeGitRepo,
717
+ sanitizedEnv,
718
+ spawnNode,
719
+ createIsolationLayout,
720
+ hostEnv,
721
+ realWorkbenchProfileDir,
722
+ digestDirectory,
723
+ compareDigests,
724
+ createReadOnlyProfileStore,
725
+ resolveWorkbenchModuleRoot,
726
+ packageIdentity,
727
+ startRecordingProxy,
728
+ startVectorHubStandIn,
729
+ startRealEngine,
730
+ startBridgeConsole,
731
+ resolveRepoIdentity,
732
+ scanClaudeTranscript,
733
+ getJson,
734
+ consumeSessionEvents,
735
+ runWorkbenchTurn,
736
+ eventually,
737
+ };