@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,262 @@
1
+ 'use strict';
2
+ // WB-002 — the Workbench module hosted for real, independently of the
3
+ // knowledge-base product.
4
+ //
5
+ // Two independent hosts are started from the REAL installed module (the copy
6
+ // the installed product 5.0.24 ships; the repository pin is byte-identical to
7
+ // it, proven by parallel-workbench-source-match.js):
8
+ //
9
+ // host A (standalone) no knowledge engine URL, no profile injection:
10
+ // the module must work with nothing but its own
11
+ // isolated data directory — the proof that it does
12
+ // not depend on knowledge-base business objects.
13
+ // host B (integration) KB_KNOWLEDGE_ENGINE_URL points at this lane's
14
+ // controlled integration fixture; the module's own
15
+ // KnowledgeContext contract is verified against
16
+ // that fixture, separately from the standalone
17
+ // host. The fixture is a FIXTURE: it stands in for
18
+ // a knowledge engine and is never counted as real
19
+ // knowledge or real retrieval.
20
+ //
21
+ // The model call itself is NOT faked here and is NOT performed here either:
22
+ // the real model turn lives in parallel-workbench-model-probe.js, which runs
23
+ // one real subscription call through an isolated integration host.
24
+ //
25
+ // Isolation: every directory, port, HOME and Claude config dir of both hosts
26
+ // is isolated; the live product ports are refused; nothing under the real
27
+ // product data directories is read for writing or written.
28
+ const assert = require('node:assert/strict');
29
+ const fs = require('node:fs');
30
+ const path = require('node:path');
31
+ const fixture = require('./helpers/parallel-workbench-fixture');
32
+ const sourceMap = require('./helpers/parallel-workbench-source-map');
33
+
34
+ const results = [];
35
+ function record(caseId, status, detail) {
36
+ results.push({ caseId, status, detail });
37
+ console.log(`CASE ${caseId} ${status} ${JSON.stringify(detail)}`);
38
+ }
39
+
40
+ async function getJson(url, options) {
41
+ const response = await fetch(url, { ...options, signal: AbortSignal.timeout(30000) });
42
+ const body = await response.json().catch(() => null);
43
+ return { status: response.status, ok: response.ok, body };
44
+ }
45
+
46
+ async function startHost(layout, moduleRoot, port, mode, extraEnv = {}) {
47
+ const child = fixture.spawnNode([path.join(__dirname, 'helpers', 'parallel-workbench-host.js')], fixture.hostEnv(layout, {
48
+ PK_WB_MODULE_ROOT: moduleRoot,
49
+ PK_WB_HOST_PORT: String(port),
50
+ PK_WB_HOST_DATA_DIR: layout.dataDir,
51
+ PK_WB_HOST_WORKSPACE: layout.workspace,
52
+ PK_WB_HOST_MODE: mode,
53
+ ...extraEnv,
54
+ }));
55
+ return child;
56
+ }
57
+
58
+ function snapshotRealProfileDir() {
59
+ // Byte-level in-memory SHA256 snapshot; the digests never leave the process.
60
+ return fixture.digestDirectory(fixture.realWorkbenchProfileDir());
61
+ }
62
+
63
+ (async () => {
64
+ const moduleRoot = fixture.resolveHostModuleRoot();
65
+ assert.ok(moduleRoot, 'no claude-ai-workbench module copy was found to host');
66
+ const identity = sourceMap.packageIdentity(moduleRoot);
67
+ const manifest = sourceMap.loadManifest();
68
+ const moduleRootLabel = moduleRoot === sourceMap.resolveInstalledProductPackage()
69
+ ? 'currently-installed-package:installed-product'
70
+ : moduleRoot === sourceMap.resolveRepoTargetPackage()
71
+ ? 'currently-installed-package:repository-pin'
72
+ : 'explicit-override';
73
+ console.log(`hosting module ${moduleRootLabel} ${identity.name}@${identity.version} from ${moduleRoot}`);
74
+
75
+ const realProfilesBefore = snapshotRealProfileDir();
76
+ let standalone;
77
+ let integration;
78
+ let integrationFixture;
79
+
80
+ // ---------- host A: standalone independent host -------------------------
81
+ const layoutA = fixture.createIsolationLayout('parallel-wb-indep');
82
+ const portA = await fixture.freePort();
83
+ const baseA = `http://127.0.0.1:${portA}`;
84
+ standalone = await startHost(layoutA, moduleRoot, portA, 'standalone');
85
+ try {
86
+ await fixture.waitForHttp(`${baseA}/api/claude-workbench/v1/health`, 30000);
87
+
88
+ const health = await getJson(`${baseA}/api/claude-workbench/v1/health`);
89
+ record('WB-002.a-standalone-health', health.ok && health.body && health.body.status === 'ok' ? 'PASS' : 'FAIL', {
90
+ httpStatus: health.status,
91
+ status: health.body && health.body.status,
92
+ version: health.body && health.body.version,
93
+ apiVersion: health.body && health.body.apiVersion,
94
+ expectVersion: identity.version,
95
+ });
96
+ assert.equal(health.body.status, 'ok');
97
+ assert.equal(health.body.version, identity.version);
98
+
99
+ const runtime = await getJson(`${baseA}/api/claude-workbench/v1/runtime`);
100
+ record('WB-002.b-standalone-runtime', runtime.ok && runtime.body && runtime.body.runtime === 'claude-code' ? 'PASS' : 'FAIL', {
101
+ body: runtime.body,
102
+ });
103
+ assert.equal(runtime.body.runtime, 'claude-code');
104
+ assert.equal(runtime.body.transport, 'agent-sdk');
105
+
106
+ const agents = await getJson(`${baseA}/api/claude-workbench/v1/agents`);
107
+ const agentIds = agents.body && Array.isArray(agents.body.agents) ? agents.body.agents.map(agent => agent.id) : [];
108
+ record('WB-002.c-standalone-agents', agents.ok && agentIds.includes('claude-code') ? 'PASS' : 'FAIL', { agentIds });
109
+ assert.ok(agentIds.includes('claude-code'), 'the claude-code agent is not listed');
110
+
111
+ // No knowledge engine, no profile injection: the module must still serve
112
+ // its own profile and project surfaces from the isolated data directory.
113
+ const profiles = await getJson(`${baseA}/api/claude-workbench/v1/profiles`);
114
+ const profileList = Array.isArray(profiles.body && profiles.body.profiles) ? profiles.body.profiles : [];
115
+ record('WB-002.d-standalone-profiles-isolated', profiles.ok && profileList.length === 0 ? 'PASS' : 'FAIL', {
116
+ profileCount: profileList.length,
117
+ detail: 'the isolated store starts empty; the real profile store is never consulted by the standalone host',
118
+ });
119
+ assert.equal(profileList.length, 0, 'the standalone host must not see real profiles');
120
+
121
+ const projects = await getJson(`${baseA}/api/claude-workbench/v1/projects`);
122
+ record('WB-002.e-standalone-projects', projects.ok ? 'PASS' : 'FAIL', { httpStatus: projects.status });
123
+ assert.ok(projects.ok);
124
+
125
+ const session = await getJson(`${baseA}/api/claude-workbench/v1/sessions`, {
126
+ method: 'POST',
127
+ headers: { 'Content-Type': 'application/json' },
128
+ body: JSON.stringify({ context: { contextId: 'wb002-standalone', workspaceRef: 'wb-fixture' } }),
129
+ });
130
+ const created = session.body && session.body.session;
131
+ record('WB-002.f-standalone-session-created', session.status === 201 && created && created.sessionId ? 'PASS' : 'FAIL', {
132
+ httpStatus: session.status,
133
+ sessionId: created && created.sessionId,
134
+ state: created && created.state,
135
+ agentId: created && created.agentId,
136
+ authSource: created && created.authSource,
137
+ });
138
+ assert.equal(session.status, 201);
139
+ assert.ok(created.sessionId);
140
+
141
+ // The session must exist with NO knowledge context: the module works
142
+ // without knowledge-base business objects.
143
+ const knowledge = await getJson(`${baseA}/api/claude-workbench/v1/sessions/${encodeURIComponent(created.sessionId)}/knowledge`);
144
+ record('WB-002.g-standalone-no-knowledge-context', knowledge.ok && knowledge.body && knowledge.body.status === 'not-retrieved' ? 'PASS' : 'FAIL', {
145
+ knowledgeStatus: knowledge.body && knowledge.body.status,
146
+ detail: 'no engine URL is configured, so nothing was retrieved — the session still started',
147
+ });
148
+ assert.equal(knowledge.body.status, 'not-retrieved');
149
+
150
+ // The session artefacts must live in the ISOLATED data directory.
151
+ await fixture.waitForHttp(`${baseA}/api/claude-workbench/v1/health`, 2000).catch(() => {});
152
+ const isolatedFiles = fs.existsSync(layoutA.dataDir) ? fs.readdirSync(layoutA.dataDir) : [];
153
+ record('WB-002.h-standalone-data-isolated', isolatedFiles.length > 0 ? 'PASS' : 'FAIL', {
154
+ dataDir: layoutA.dataDir,
155
+ entries: isolatedFiles.slice(0, 12),
156
+ });
157
+ assert.ok(isolatedFiles.length > 0, 'the standalone host wrote nothing into its isolated data directory');
158
+ } finally {
159
+ standalone.child.kill();
160
+ await standalone.exited;
161
+ }
162
+
163
+ // ---------- host B: integration host + controlled fixture ---------------
164
+ const layoutB = fixture.createIsolationLayout('parallel-wb-integ');
165
+ integrationFixture = await fixture.startIntegrationFixture();
166
+ const portB = await fixture.freePort();
167
+ const baseB = `http://127.0.0.1:${portB}`;
168
+ integration = await startHost(layoutB, moduleRoot, portB, 'integration', {
169
+ PK_WB_ENGINE_URL: integrationFixture.url,
170
+ });
171
+ try {
172
+ await fixture.waitForHttp(`${baseB}/api/claude-workbench/v1/health`, 30000);
173
+
174
+ // The integration contract, verified through the module's own
175
+ // KnowledgeContext against this lane's controlled fixture. The fixture is
176
+ // labelled: it is not real knowledge.
177
+ const { KnowledgeContext } = require(path.join(moduleRoot, 'packages', 'server', 'lib', 'knowledge-context.js'));
178
+ const context = new KnowledgeContext({ engineUrl: integrationFixture.url });
179
+ const retrieved = await context.retrieve(layoutB.workspace, 'integration contract probe', 'project', { sessionId: 'wb002-contract' });
180
+ const requestSeen = integrationFixture.requests[integrationFixture.requests.length - 1] || null;
181
+ const promptCarried = String(retrieved.prompt || '').includes(integrationFixture.marker);
182
+ record('WB-002.i-integration-context-contract', retrieved.status === 'ready' && promptCarried && requestSeen ? 'PASS' : 'FAIL', {
183
+ fixtureKind: 'controlled-integration-fixture (stand-in knowledge engine; never counted as real knowledge or real retrieval)',
184
+ engineUrl: integrationFixture.url,
185
+ retrievedStatus: retrieved.status,
186
+ references: retrieved.references ? retrieved.references.length : null,
187
+ memoryCount: retrieved.memoryCount == null ? null : retrieved.memoryCount,
188
+ promptCarriedMarker: promptCarried,
189
+ requestSeenByFixture: requestSeen && {
190
+ method: requestSeen.method,
191
+ url: requestSeen.url,
192
+ client: requestSeen.body && requestSeen.body.client,
193
+ repoPath: requestSeen.body && requestSeen.body.repoPath,
194
+ query: requestSeen.body && requestSeen.body.query,
195
+ sessionId: requestSeen.body && requestSeen.body.sessionId,
196
+ },
197
+ });
198
+ assert.equal(retrieved.status, 'ready');
199
+ assert.ok(promptCarried, 'the retrieved prompt did not carry the fixture marker');
200
+ assert.equal(requestSeen.body.client, 'workbench', 'the module did not identify itself as the workbench client');
201
+
202
+ // The integration host itself must still create sessions; its knowledge
203
+ // surface stays empty until a real turn retrieves through the engine.
204
+ const session = await getJson(`${baseB}/api/claude-workbench/v1/sessions`, {
205
+ method: 'POST',
206
+ headers: { 'Content-Type': 'application/json' },
207
+ body: JSON.stringify({ context: { contextId: 'wb002-integration', workspaceRef: 'wb-fixture' } }),
208
+ });
209
+ const created = session.body && session.body.session;
210
+ record('WB-002.j-integration-session-created', session.status === 201 && created && created.sessionId ? 'PASS' : 'FAIL', {
211
+ httpStatus: session.status,
212
+ sessionId: created && created.sessionId,
213
+ });
214
+ assert.equal(session.status, 201);
215
+ } finally {
216
+ integration.child.kill();
217
+ await integration.exited;
218
+ await integrationFixture.close();
219
+ }
220
+
221
+ // ---------- the real product surfaces stayed untouched -------------------
222
+ const realProfilesAfter = snapshotRealProfileDir();
223
+ const digestComparison = fixture.compareDigests(realProfilesBefore, realProfilesAfter);
224
+ record('WB-002.k-real-profiles-untouched', digestComparison.byteIdentical ? 'PASS' : 'FAIL', {
225
+ profileDir: fixture.realWorkbenchProfileDir(),
226
+ comparison: 'byte-level SHA256, computed in memory; digests are never printed, logged or persisted',
227
+ fileCount: digestComparison.count,
228
+ changedFiles: digestComparison.changedFiles,
229
+ addedFiles: digestComparison.addedFiles,
230
+ removedFiles: digestComparison.removedFiles,
231
+ });
232
+ assert.ok(digestComparison.byteIdentical, 'the real workbench profile directory changed during the run (byte-level comparison)');
233
+
234
+ const summary = {
235
+ lane: 'test-workbench',
236
+ file: 'parallel-workbench-independent-host.js',
237
+ case: 'WB-002',
238
+ title: '独立宿主',
239
+ module: { label: moduleRootLabel, root: moduleRoot, name: identity.name, version: identity.version, manifestVersion: manifest.terminal.version },
240
+ hosts: {
241
+ standalone: { port: portA, dataDir: layoutA.dataDir, workspace: layoutA.workspace, mode: 'standalone' },
242
+ integration: { port: portB, dataDir: layoutB.dataDir, workspace: layoutB.workspace, mode: 'integration', engineUrl: integrationFixture.url, fixtureKind: 'controlled-integration-fixture' },
243
+ },
244
+ modelCallPerformedHere: false,
245
+ modelProbe: 'parallel-workbench-model-probe.js',
246
+ networkUsed: false,
247
+ forbiddenPortsAvoided: [...fixture.FORBIDDEN_PORTS],
248
+ cases: results,
249
+ };
250
+
251
+ const target = process.env.PARALLEL_WORKBENCH_EVIDENCE;
252
+ if (target) {
253
+ fs.mkdirSync(path.dirname(target), { recursive: true });
254
+ fs.writeFileSync(target, JSON.stringify(summary, null, 2), 'utf8');
255
+ }
256
+ fixture.removeTree(layoutA.root);
257
+ fixture.removeTree(layoutB.root);
258
+ console.log('parallel-workbench-independent-host PASS');
259
+ })().catch(error => {
260
+ console.error(error);
261
+ process.exit(1);
262
+ });
@@ -0,0 +1,340 @@
1
+ 'use strict';
2
+ // P-WB-QA-INTEGRATION-5025 canonical copy of the accepted WB-008 entry
3
+ // (_site/_test/parallel-workbench-runtime-wb008-abort.js, frozen untracked).
4
+ // P-WB-CANONICAL-R2 wiring: the entry goes through the canonical launcher boot
5
+ // (paid gate + explicit real-profile authorization → read-only discovery with
6
+ // the real environment → FULL driver isolation incl. APPDATA/LOCALAPPDATA/
7
+ // CODEX_HOME → carried-through captures), and the real-profile digest helpers
8
+ // come from the launcher's authorized layer instead of the pre-R5 tracked
9
+ // fixture whose whitelist lacked those keys. Case logic and expecteds are
10
+ // unchanged. Real paid model probe (two turns, real network to the provider) —
11
+ // run only explicitly via the integration-5025 runner with
12
+ // PK_WB_ALLOW_PAID_TURNS=1 and an explicit PK_WB_REAL_PROFILE_DIR.
13
+ // WB-008 — 中止隔离(真实浏览器 / 真实 0.13.6 隔离宿主 / 真实 MiniMax)。
14
+ //
15
+ // 本入口用真实 Chrome 打开模块自带的 agent-terminal UI(安装包内
16
+ // apps/agent-terminal),在隔离宿主上跑两个同时进行的真实会话 A / B:
17
+ //
18
+ // * A 与 B 都是真实模型请求,两个会话同时在跑;
19
+ // * 只通过产品自己的 UI 停止 B:切到 B,点发送按钮(忙碌时该按钮就是
20
+ // 产品自己的“停止”控件),不做任何绕过 UI 的中止调用;
21
+ // * 判定只看产品自身的事实:B 的流以 claude/aborted 结束且没有
22
+ // claaude/result、B 的 CLI 进程真的退出、A 的 CLI 进程还在、A 的流
23
+ // 在中止之后继续并且最后以 claude/result 正常完成。
24
+ //
25
+ // 进程普查只读,且只取本用例自己的宿主子进程子树;5757/5760 与任何
26
+ // 未知进程都不触碰。
27
+ //
28
+ // 退出码:0 = 全部证明;3 = 诚实 BLOCKED;1 = 脚本错误。
29
+ const path = require('node:path');
30
+ const crypto = require('node:crypto');
31
+ const launcher = require('./helpers/parallel-workbench-integration-5025-launcher');
32
+ const fixture = require('./helpers/parallel-workbench-integration-5025-fixture');
33
+ const harness = require('./helpers/parallel-workbench-integration-5025-runtime-harness');
34
+
35
+ const PROFILE_ID = String(process.env.PK_WB_MODEL_PROFILE || 'minimax');
36
+ const REQUESTED_MODEL = String(process.env.PK_WB_MODEL_ID || 'MiniMax-M3.1-Flash');
37
+ const TURN_TIMEOUT_MS = Number(process.env.PK_WB_TURN_TIMEOUT_MS || 420000);
38
+
39
+ const results = [];
40
+ function record(caseId, status, detail) {
41
+ results.push({ caseId, status, detail });
42
+ console.log(`CASE ${caseId} ${status} ${JSON.stringify(detail)}`);
43
+ }
44
+ function assertTrue(condition, message) {
45
+ if (!condition) throw new Error(message);
46
+ }
47
+
48
+ // The runtime processes of THIS host child only.
49
+ function runtimeProcesses(hostPid) {
50
+ return harness.runtimeProcesses(hostPid);
51
+ }
52
+
53
+ async function waitForRuntimeProcesses(hostPid, minimum, timeoutMs) {
54
+ const deadline = Date.now() + timeoutMs;
55
+ let last = [];
56
+ while (Date.now() < deadline) {
57
+ last = runtimeProcesses(hostPid);
58
+ if (last.length >= minimum) return last;
59
+ await harness.wait(250);
60
+ }
61
+ return last;
62
+ }
63
+
64
+ (async () => {
65
+ // The single safe entry discipline: gates, real-env discovery, full driver
66
+ // isolation, carried-through captures — before anything else happens.
67
+ const boot = launcher.boot({ prefix: 'parallel-wb008', paid: true, needsRealProfile: true });
68
+ const realProfilesBefore = launcher.digestDirectory(boot.discovery.realProfileDir);
69
+ const host = await harness.startHost({
70
+ prefix: 'parallel-wb008',
71
+ moduleRoot: boot.discovery.moduleRoot,
72
+ realProfileDir: boot.discovery.realProfileDir,
73
+ });
74
+ const identity = harness.moduleIdentity(host.moduleRoot);
75
+ const hostPid = host.child.child.pid;
76
+ let browser = null;
77
+ let outcome = null;
78
+
79
+ const tokenA = `WBRT-ABORT-A-${crypto.randomBytes(5).toString('hex')}`;
80
+ const tokenB = `WBRT-ABORT-B-${crypto.randomBytes(5).toString('hex')}`;
81
+ // A long answer keeps session A running while B is aborted.
82
+ const questionA = `这是 WB-008 中止隔离的真实抽测(第 1 问,会话 A)。请输出 400 行,每行形如 "行 0001 编号 xxxxx",最后一行原样包含标记 ${tokenA}。`;
83
+ const questionB = `这是 WB-008 中止隔离的真实抽测(第 2 问,会话 B)。请输出 200 行,每行形如 "行 0001 编号 xxxxx",最后一行原样包含标记 ${tokenB}。`;
84
+
85
+ const eventsA = [];
86
+ const eventsB = [];
87
+ let abortAt = null;
88
+
89
+ try {
90
+ const opened = await harness.openBrowser(host.base, host.layout, { chrome: boot.discovery.chrome });
91
+ browser = opened.browser;
92
+
93
+ const health = await harness.getJson(`${host.base}${harness.API_BASE}/health`);
94
+ const project = await harness.ensureProject(browser, path.join(host.layout.root, 'wb008-project'), { name: 'wb008-project' });
95
+ record('WB-008.a-real-browser-and-host', health.ok && health.body && health.body.status === 'ok' ? 'PASS' : 'BLOCKED', {
96
+ browser: 'real Chrome via CDP (headless), isolated user-data-dir below the isolated layout',
97
+ chrome: opened.chrome,
98
+ module: identity,
99
+ host: { mode: 'standalone', dataDir: host.layout.dataDir, workspace: host.layout.workspace, port: host.port, hostPid },
100
+ project: { id: project.projectId, name: project.name, createdThroughTheProductForm: project.created === true },
101
+ health: health.body,
102
+ });
103
+ assertTrue(health.ok && health.body && health.body.status === 'ok', 'the isolated host is not healthy');
104
+
105
+ const profiles = await harness.getJson(`${host.base}${harness.API_BASE}/profiles`);
106
+ const listed = Array.isArray(profiles.body && profiles.body.profiles) ? profiles.body.profiles : [];
107
+ const profile = listed.find(item => item.id === PROFILE_ID) || null;
108
+ assertTrue(profile, `the ${PROFILE_ID} profile is not visible through the read-only facade`);
109
+ await harness.selectModel(browser, profile.name, REQUESTED_MODEL);
110
+ const afterModel = await harness.readState(browser);
111
+ record('WB-008.b-model-selected-through-the-real-ui', afterModel.product.currentProfileId === PROFILE_ID && afterModel.product.currentModel === REQUESTED_MODEL ? 'PASS' : 'BLOCKED', {
112
+ profileId: PROFILE_ID,
113
+ profileName: profile.name,
114
+ requestedModel: REQUESTED_MODEL,
115
+ uiProfileId: afterModel.product.currentProfileId,
116
+ uiModel: afterModel.product.currentModel,
117
+ credentialConfigured: profile.credentialConfigured === true,
118
+ credentialWrittenAnywhere: false,
119
+ });
120
+ assertTrue(afterModel.product.currentProfileId === PROFILE_ID, 'the profile was not selected through the UI');
121
+
122
+ // --- session A ------------------------------------------------------
123
+ await harness.clickNewSession(browser);
124
+ const firstSession = await harness.waitForState(browser, state => state.product && state.product.activeSessionId, 'session A', 30000);
125
+ const sessionA = firstSession.product.activeSessionId;
126
+ const streamA = harness.consumeSessionEvents(
127
+ harness.sessionEventsUrl(host.base, sessionA),
128
+ event => eventsA.push(event),
129
+ event => event.type === 'claude/result' || event.type === 'claude/error',
130
+ TURN_TIMEOUT_MS,
131
+ );
132
+ await harness.typeText(browser, questionA);
133
+ await harness.clickSend(browser);
134
+ const aBusy = await harness.waitForState(browser, state => state.busy === true, 'session A to start', 90000);
135
+ const procsA = await waitForRuntimeProcesses(hostPid, 1, 90000);
136
+ record('WB-008.c-session-a-is-running', aBusy.busy === true && procsA.length >= 1 ? 'PASS' : 'BLOCKED', {
137
+ sessionId: sessionA,
138
+ uiSendTitleWhileBusy: aBusy.sendTitle,
139
+ uiSessionState: aBusy.product.sessionState,
140
+ runtimeProcessesOfThisHost: procsA,
141
+ tokenA,
142
+ });
143
+ assertTrue(procsA.length >= 1, 'session A has no runtime process');
144
+
145
+ // --- session B, started while A is still running --------------------
146
+ await harness.clickNewSession(browser);
147
+ const secondSession = await harness.waitForState(browser, state => state.product && state.product.activeSessionId && state.product.activeSessionId !== sessionA, 'session B', 30000);
148
+ const sessionB = secondSession.product.activeSessionId;
149
+ const streamB = harness.consumeSessionEvents(
150
+ harness.sessionEventsUrl(host.base, sessionB),
151
+ event => eventsB.push(event),
152
+ event => event.type === 'claude/result' || event.type === 'claude/aborted' || event.type === 'claude/error',
153
+ TURN_TIMEOUT_MS,
154
+ );
155
+ await harness.typeText(browser, questionB);
156
+ await harness.clickSend(browser);
157
+ const bBusy = await harness.waitForState(browser, state => state.busy === true, 'session B to start', 90000);
158
+ const procsB = await waitForRuntimeProcesses(hostPid, procsA.length + 1, 90000);
159
+ // The processes that were already running for A when B started are A's;
160
+ // the ones that appeared with B are B's. A short-lived helper of A may
161
+ // have exited on its own in between, so the mapping is the intersection
162
+ // and the difference, not the raw first census.
163
+ const aPids = procsA.filter(entry => procsB.some(other => other.pid === entry.pid)).map(entry => entry.pid);
164
+ const bPids = procsB.filter(entry => !procsA.some(other => other.pid === entry.pid)).map(entry => entry.pid);
165
+ const aStillRunning = aPids.length >= 1 && bPids.length >= 1;
166
+ record('WB-008.d-two-sessions-run-at-the-same-time', bBusy.busy === true && aStillRunning ? 'PASS' : 'BLOCKED', {
167
+ sessionA: { id: sessionA, runtimeProcesses: procsA.map(entry => ({ pid: entry.pid, name: entry.name, commandLine: entry.commandLine })) },
168
+ sessionB: { id: sessionB, runtimeProcesses: procsB.map(entry => ({ pid: entry.pid, name: entry.name, commandLine: entry.commandLine })) },
169
+ sessionAProcessPids: aPids,
170
+ sessionBProcessPids: bPids,
171
+ runtimeProcessesAddedForSessionB: bPids,
172
+ sessionAProcessStillAlive: aStillRunning,
173
+ uiSendTitleWhileBusy: bBusy.sendTitle,
174
+ });
175
+ assertTrue(aStillRunning, 'session A stopped running while session B started');
176
+
177
+ // --- abort B through the product's own stop control -----------------
178
+ await harness.openSessionRow(browser, project.projectId, sessionB);
179
+ const bBusyInUi = await harness.waitForState(browser, state => state.busy === true && state.product.activeSessionId === sessionB, 'session B to be busy in the UI', 30000);
180
+ const beforeAbort = runtimeProcesses(hostPid);
181
+ abortAt = Date.now();
182
+ await harness.clickSend(browser);
183
+ const bIdle = await harness.waitForState(browser, state => state.busy === false && state.product.activeSessionId === sessionB, 'session B to become idle after the stop', 60000);
184
+ await harness.wait(3000);
185
+ const afterAbort = runtimeProcesses(hostPid);
186
+ const afterPids = new Set(afterAbort.map(entry => entry.pid));
187
+ const exitedPids = beforeAbort.map(entry => entry.pid).filter(pid => !afterPids.has(pid));
188
+ const survivedA = aPids.filter(pid => afterPids.has(pid));
189
+ const bStillAlive = bPids.filter(pid => afterPids.has(pid));
190
+ const bAborted = eventsB.some(event => event.type === 'claude/aborted');
191
+ const bResulted = eventsB.some(event => event.type === 'claude/result');
192
+ const stopNoteInUi = bIdle.streamText.includes('已停止');
193
+ const abortOk = bAborted && !bResulted && exitedPids.length >= 1
194
+ && survivedA.length === aPids.length && survivedA.length >= 1
195
+ && bStillAlive.length === 0
196
+ && bIdle.busy === false;
197
+ record('WB-008.e-only-b-was-aborted', abortOk ? 'PASS' : 'BLOCKED', {
198
+ abortControl: 'the product\'s own send button, which the product itself turns into “停止” while the turn runs (btn-send.title === "停止")',
199
+ sendTitleWhileBusy: bBusyInUi.sendTitle,
200
+ sessionB: {
201
+ sessionId: sessionB,
202
+ abortedEvent: bAborted,
203
+ resultEvent: bResulted,
204
+ eventTypes: eventsB.map(event => event.type),
205
+ uiSendTitleAfterAbort: bIdle.sendTitle,
206
+ uiSessionStateAfterAbort: bIdle.product.sessionState,
207
+ stopNoteRenderedInTheTranscript: stopNoteInUi,
208
+ },
209
+ processCensus: {
210
+ scope: 'the descendants of this case\'s own host child only',
211
+ beforeAbort: beforeAbort.map(entry => ({ pid: entry.pid, name: entry.name, commandLine: entry.commandLine })),
212
+ afterAbort: afterAbort.map(entry => ({ pid: entry.pid, name: entry.name, commandLine: entry.commandLine })),
213
+ exitedPids,
214
+ sessionAPidsBeforeTheAbort: aPids,
215
+ sessionAPidsStillAlive: survivedA,
216
+ sessionBPidsBeforeTheAbort: bPids,
217
+ sessionBPidsStillAlive: bStillAlive,
218
+ },
219
+ });
220
+ assertTrue(abortOk, 'the abort did not isolate B');
221
+
222
+ // --- A keeps running and finishes on its own ------------------------
223
+ // The UI is put back onto A while A is still running, so the answer of A
224
+ // is observed in the product's own live rendering. The completion gate is
225
+ // the session's own result event, not the marker inside the answer.
226
+ await harness.openSessionRow(browser, project.projectId, sessionA);
227
+ await harness.waitUntil(() => eventsA.some(event => event.type === 'claude/result'), 'the result event of session A', TURN_TIMEOUT_MS, 500);
228
+ const aAnswerInUi = await harness.waitForState(
229
+ browser,
230
+ state => state.product && state.product.activeSessionId === sessionA && state.busy === false
231
+ && state.assistantMessages.some(text => text.includes(tokenA)),
232
+ 'the finished answer of session A in the UI',
233
+ 30000,
234
+ 200,
235
+ );
236
+ const aEventsAfterAbort = eventsA.filter(event => Date.parse(event.at) >= abortAt - 500);
237
+ const aResult = eventsA.find(event => event.type === 'claude/result') || null;
238
+ const aAnswer = aResult ? String(aResult.result || '') : '';
239
+ const aFinishedAfterAbort = aResult ? Date.parse(aResult.at) >= abortAt : false;
240
+ const aOk = aEventsAfterAbort.length > 0 && Boolean(aResult) && aFinishedAfterAbort
241
+ && aAnswer.includes(tokenA);
242
+ record('WB-008.f-session-a-kept-running-and-completed', aOk ? 'PASS' : 'BLOCKED', {
243
+ sessionA: {
244
+ sessionId: sessionA,
245
+ eventCount: eventsA.length,
246
+ eventsAfterTheAbortOfB: aEventsAfterAbort.length,
247
+ eventTypesAfterTheAbortOfB: [...new Set(aEventsAfterAbort.map(event => event.type))],
248
+ resultAt: aResult ? aResult.at : null,
249
+ finishedAfterTheAbortOfB: aFinishedAfterAbort,
250
+ tokenEchoedInTheAnswer: aAnswer.includes(tokenA),
251
+ answerPreview: aAnswer.slice(0, 120),
252
+ tokenEchoedInTheUi: aAnswerInUi.assistantMessages.some(text => text.includes(tokenA)),
253
+ uiSendTitleAfterTheAnswer: aAnswerInUi.sendTitle,
254
+ uiSessionStateAfterTheAnswer: aAnswerInUi.product.sessionState,
255
+ },
256
+ paidTurns: 2,
257
+ });
258
+ assertTrue(aOk, 'session A did not keep running after B was aborted');
259
+
260
+ // --- the UI shows both facts ----------------------------------------
261
+ // The reopened session renders its own history, so the transcript is
262
+ // waited for instead of being read in the middle of the replay.
263
+ await harness.openSessionRow(browser, project.projectId, sessionB);
264
+ const bView = await harness.waitForState(browser, state => state.streamText.includes('已停止'), 'the stopped note of session B in the reopened transcript', 30000, 200);
265
+ const bTranscriptStopped = bView.streamText.includes('已停止');
266
+ const bHasNoAnswer = !bView.assistantMessages.some(text => text.includes(tokenB));
267
+ await harness.openSessionRow(browser, project.projectId, sessionA);
268
+ const aView = await harness.waitForState(browser, state => state.assistantMessages.some(text => text.includes(tokenA)), 'the answer of session A in the reopened transcript', 30000, 200);
269
+ const aHasAnswer = aView.assistantMessages.some(text => text.includes(tokenA));
270
+ const uiOk = bTranscriptStopped && bHasNoAnswer && aHasAnswer;
271
+ record('WB-008.g-the-ui-shows-the-abort-of-b-and-the-answer-of-a', uiOk ? 'PASS' : 'BLOCKED', {
272
+ sessionB: { stoppedNoteInTheTranscript: bTranscriptStopped, answerWithTheTokenPresent: bView.assistantMessages.some(text => text.includes(tokenB)), assistantMessageCount: bView.assistantMessages.length },
273
+ sessionA: { answerWithTheTokenPresent: aHasAnswer, assistantMessageCount: aView.assistantMessages.length },
274
+ reopenedFrom: 'the product\'s own session history (the newest page of the session\'s own history file)',
275
+ });
276
+ assertTrue(uiOk, 'the UI does not show the abort of B and the answer of A');
277
+
278
+ outcome = {
279
+ sessionA: { id: sessionA, tokenA, answered: aAnswer.includes(tokenA), events: eventsA.length, eventsAfterTheAbortOfB: aEventsAfterAbort.length },
280
+ sessionB: { id: sessionB, tokenB, aborted: bAborted, result: bResulted, events: eventsB.length },
281
+ processCensus: { beforeAbort: beforeAbort.map(entry => entry.pid), afterAbort: afterAbort.map(entry => entry.pid), exitedPids },
282
+ paidTurns: 2,
283
+ };
284
+ } catch (error) {
285
+ record('WB-008.runtime', 'BLOCKED', { error: String(error && error.message ? error.message : error) });
286
+ } finally {
287
+ if (browser) { try { await browser.close(); } catch { /* the browser is disposable */ } }
288
+ if (host.child) { await harness.stopHost(host); }
289
+ }
290
+
291
+ const digestComparison = launcher.compareDigests(realProfilesBefore, launcher.digestDirectory(boot.discovery.realProfileDir));
292
+ record('WB-008.h-real-profiles-byte-identical', digestComparison.byteIdentical ? 'PASS' : 'FAIL', {
293
+ profileDir: boot.discovery.realProfileDir,
294
+ comparison: 'byte-level SHA256, computed in memory; digests never printed, logged or persisted',
295
+ fileCount: digestComparison.count,
296
+ changedFiles: digestComparison.changedFiles,
297
+ addedFiles: digestComparison.addedFiles,
298
+ removedFiles: digestComparison.removedFiles,
299
+ });
300
+
301
+ const essential = [
302
+ 'WB-008.a-real-browser-and-host',
303
+ 'WB-008.b-model-selected-through-the-real-ui',
304
+ 'WB-008.c-session-a-is-running',
305
+ 'WB-008.d-two-sessions-run-at-the-same-time',
306
+ 'WB-008.e-only-b-was-aborted',
307
+ 'WB-008.f-session-a-kept-running-and-completed',
308
+ 'WB-008.g-the-ui-shows-the-abort-of-b-and-the-answer-of-a',
309
+ 'WB-008.h-real-profiles-byte-identical',
310
+ ];
311
+ const proven = essential.every(id => (results.find(entry => entry.caseId === id) || {}).status === 'PASS');
312
+
313
+ const summary = {
314
+ lane: 'test-workbench-runtime',
315
+ file: 'parallel-workbench-runtime-wb008-abort.js',
316
+ case: 'WB-008',
317
+ title: '中止隔离(真实浏览器 + 真实模型 + 真实进程退出证明)',
318
+ module: identity,
319
+ host: { mode: 'standalone', engineUrlConfigured: false, dataDir: host.layout.dataDir, workspace: host.layout.workspace, hostPid },
320
+ profile: { id: PROFILE_ID, source: 'original-subscription-store read-only, credential in memory only', credentialWrittenAnywhere: false },
321
+ abortControl: 'the product\'s own send button in its own busy state (停止) — no out-of-band abort call',
322
+ processCensusScope: 'the descendants of this case\'s own host child only; 5757/5760 and every unknown process are never enumerated, signalled or stopped',
323
+ turn: outcome,
324
+ cases: results,
325
+ verdict: proven ? 'PASS (only B was aborted and terminated, A kept running and completed)' : 'BLOCKED',
326
+ paidTurnsSent: outcome ? outcome.paidTurns : 0,
327
+ quotaEndpointQueried: false,
328
+ networkUsed: true,
329
+ };
330
+
331
+ const evidenceTarget = process.env.PARALLEL_WORKBENCH_RUNTIME_EVIDENCE;
332
+ harness.writeEvidence(evidenceTarget, summary);
333
+ if (!proven) console.log(`SCENE KEPT ${host.layout.root}`);
334
+ else fixture.removeTree(host.layout.root);
335
+ console.log(`parallel-workbench-runtime-wb008-abort verdict=${summary.verdict}`);
336
+ process.exitCode = proven ? 0 : 3;
337
+ })().catch(error => {
338
+ console.error(error);
339
+ process.exitCode = 1;
340
+ });