@sanqianx/project-knowledge 5.0.24 → 5.0.26

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 -1353
  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 +68 -65
  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,326 @@
1
+ 'use strict';
2
+ // P-WB-QA-INTEGRATION-5025 canonical copy of the accepted WB-004 entry
3
+ // (_site/_test/parallel-workbench-runtime-wb004-ordering.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, expecteds and the
10
+ // two-level model evidence are unchanged. Real paid model probe (two turns,
11
+ // real network to the provider) — run only explicitly via the integration-5025
12
+ // runner with PK_WB_ALLOW_PAID_TURNS=1 and an explicit PK_WB_REAL_PROFILE_DIR.
13
+ // WB-004 — 连续回复次序(真实浏览器 / 真实 0.13.6 隔离宿主 / 真实 MiniMax).
14
+ //
15
+ // 本入口用真实 Chrome 打开模块自带的 agent-terminal UI(安装包内
16
+ // apps/agent-terminal),在隔离宿主上跑本轮真实 Q1 与 Q2:
17
+ //
18
+ // * Q1 结束后立即发 Q2(同一会话);
19
+ // * 故障注入 = 把本轮真实 Q1 的一条真实事件在 Q2 进行中【延迟重放】回
20
+ // 同一条 SSE 流。重放由页面内的记录器完成,记录器只记录与重放,不做
21
+ // 过滤、不改顺序、不替产品判断;
22
+ // * 判定只看产品自身的 UI 与活动状态:Q1 正文不在 Q2 之后重复、Q2 不被
23
+ // 错误置为 idle。
24
+ //
25
+ // 退出码:0 = 全部证明;3 = 诚实 BLOCKED;1 = 脚本错误。
26
+ const fs = require('node:fs');
27
+ const path = require('node:path');
28
+ const crypto = require('node:crypto');
29
+ const launcher = require('./helpers/parallel-workbench-integration-5025-launcher');
30
+ const fixture = require('./helpers/parallel-workbench-integration-5025-fixture');
31
+ const harness = require('./helpers/parallel-workbench-integration-5025-runtime-harness');
32
+
33
+ const PROFILE_ID = String(process.env.PK_WB_MODEL_PROFILE || 'minimax');
34
+ const REQUESTED_MODEL = String(process.env.PK_WB_MODEL_ID || 'MiniMax-M3.1-Flash');
35
+ const TURN_TIMEOUT_MS = Number(process.env.PK_WB_TURN_TIMEOUT_MS || 420000);
36
+
37
+ const results = [];
38
+ function record(caseId, status, detail) {
39
+ results.push({ caseId, status, detail });
40
+ console.log(`CASE ${caseId} ${status} ${JSON.stringify(detail)}`);
41
+ }
42
+
43
+ // The vendor-returned model of this run: the assistant message.model of the
44
+ // real CLI transcript under the ISOLATED CLAUDE_CONFIG_DIR. Only model
45
+ // strings and booleans leave here.
46
+ function scanTranscript(roots, sessionIds, tokens) {
47
+ const matched = [];
48
+ const models = new Set();
49
+ let tokenEchoed = false;
50
+ const walk = root => {
51
+ let entries;
52
+ try { entries = fs.readdirSync(root, { withFileTypes: true }); } catch { return; }
53
+ for (const entry of entries) {
54
+ const full = path.join(root, entry.name);
55
+ if (entry.isDirectory()) { walk(full); continue; }
56
+ if (!entry.isFile() || !full.endsWith('.jsonl')) continue;
57
+ let text;
58
+ try { text = fs.readFileSync(full, 'utf8'); } catch { continue; }
59
+ if (!sessionIds.some(id => text.includes(id))) continue;
60
+ matched.push(path.basename(full));
61
+ for (const line of text.split(/\r?\n/)) {
62
+ let entry;
63
+ try { entry = JSON.parse(line); } catch { continue; }
64
+ if (entry.type !== 'assistant' || !entry.message) continue;
65
+ if (typeof entry.message.model === 'string' && entry.message.model) models.add(entry.message.model);
66
+ const content = Array.isArray(entry.message.content) ? entry.message.content : [];
67
+ for (const block of content) {
68
+ if (block && block.type === 'text' && typeof block.text === 'string' && tokens.some(token => block.text.includes(token))) tokenEchoed = true;
69
+ }
70
+ }
71
+ }
72
+ };
73
+ for (const root of roots) walk(root);
74
+ return { transcriptFilesMatched: matched, assistantModelValues: [...models], tokenEchoedInTranscript: tokenEchoed };
75
+ }
76
+
77
+ (async () => {
78
+ // The single safe entry discipline: gates, real-env discovery, full driver
79
+ // isolation, carried-through captures — before anything else happens.
80
+ const boot = launcher.boot({ prefix: 'parallel-wb004', paid: true, needsRealProfile: true });
81
+ const realProfilesBefore = launcher.digestDirectory(boot.discovery.realProfileDir);
82
+ const host = await harness.startHost({
83
+ prefix: 'parallel-wb004',
84
+ moduleRoot: boot.discovery.moduleRoot,
85
+ realProfileDir: boot.discovery.realProfileDir,
86
+ });
87
+ const identity = harness.moduleIdentity(host.moduleRoot);
88
+ let browser = null;
89
+ let ui = null;
90
+ let outcome = null;
91
+ let blocked = null;
92
+
93
+ try {
94
+ const opened = await harness.openBrowser(host.base, host.layout, { chrome: boot.discovery.chrome });
95
+ browser = opened.browser;
96
+ ui = opened;
97
+
98
+ const health = await harness.getJson(`${host.base}${harness.API_BASE}/health`);
99
+ const project = await harness.ensureProject(browser, path.join(host.layout.root, 'wb004-project'), { name: 'wb004-project' });
100
+ record('WB-004.a-real-browser-and-host', health.ok && health.body && health.body.status === 'ok' ? 'PASS' : 'BLOCKED', {
101
+ browser: 'real Chrome via CDP (headless), isolated user-data-dir below the isolated layout',
102
+ chrome: opened.chrome,
103
+ module: identity,
104
+ host: { mode: 'standalone', dataDir: host.layout.dataDir, workspace: host.layout.workspace, port: host.port },
105
+ project: { id: project.projectId, name: project.name, createdThroughTheProductForm: project.created === true },
106
+ health: health.body,
107
+ });
108
+ assertTrue(health.ok && health.body && health.body.status === 'ok', 'the isolated host is not healthy');
109
+
110
+ const profiles = await harness.getJson(`${host.base}${harness.API_BASE}/profiles`);
111
+ const listed = Array.isArray(profiles.body && profiles.body.profiles) ? profiles.body.profiles : [];
112
+ const profile = listed.find(item => item.id === PROFILE_ID) || null;
113
+ assertTrue(profile, `the ${PROFILE_ID} profile is not visible through the read-only facade`);
114
+ const credentialConfigured = profile.credentialConfigured === true;
115
+
116
+ await harness.selectModel(browser, profile.name, REQUESTED_MODEL);
117
+ const afterModel = await harness.readState(browser);
118
+ record('WB-004.b-model-selected-through-the-real-ui', credentialConfigured && afterModel.product.currentProfileId === PROFILE_ID && afterModel.product.currentModel === REQUESTED_MODEL ? 'PASS' : 'BLOCKED', {
119
+ profileId: PROFILE_ID,
120
+ profileName: profile.name,
121
+ requestedModel: REQUESTED_MODEL,
122
+ uiProfileId: afterModel.product.currentProfileId,
123
+ uiModel: afterModel.product.currentModel,
124
+ modelLabel: afterModel.modelLabel,
125
+ credentialConfigured,
126
+ credentialWrittenAnywhere: false,
127
+ });
128
+ assertTrue(credentialConfigured, 'the profile has no configured credential');
129
+
130
+ const token1 = `WBRT-ORDER-A-${crypto.randomBytes(5).toString('hex')}`;
131
+ const token2 = `WBRT-ORDER-B-${crypto.randomBytes(5).toString('hex')}`;
132
+ const question1 = `这是 WB-004 连续回复次序的真实抽测(第 1 问)。请只回复一行,原样包含以下随机标记,不要添加任何其他内容:${token1}`;
133
+ const question2 = `这是 WB-004 连续回复次序的真实抽测(第 2 问)。请先输出一行、原样包含以下随机标记:${token2};然后再写三行简短说明,说明这是第 2 问。`;
134
+
135
+ // --- Q1 -------------------------------------------------------------
136
+ await harness.typeText(browser, question1);
137
+ await harness.clickSend(browser);
138
+ const q1Busy = await harness.waitForState(browser, state => state.busy === true, 'Q1 to start', 90000);
139
+ const q1Done = await harness.waitForState(browser, state => state.busy === false && state.product.liveRevision > 0, 'Q1 to end', TURN_TIMEOUT_MS);
140
+ const q1Text = q1Done.streamText;
141
+ const q1Answers = q1Done.assistantMessages.filter(text => text.includes(token1));
142
+ const q1Count = q1Answers.length;
143
+ record('WB-004.c-q1-answered-by-the-real-model', q1Count === 1 ? 'PASS' : 'BLOCKED', {
144
+ uniqueToken: token1,
145
+ assistantAnswersCarryingTheToken: q1Count,
146
+ answerPreview: q1Answers.length ? q1Answers[0].slice(0, 200) : q1Text.slice(-200),
147
+ uiSessionState: q1Done.product.sessionState,
148
+ paidTurns: 1,
149
+ });
150
+ assertTrue(q1Count === 1, `Q1 was not answered with the unique token (${q1Count} assistant answers carry it)`);
151
+
152
+ // --- Q2 immediately --------------------------------------------------
153
+ const q1ResultEvent = (await harness.recordedEvents(browser))
154
+ .filter(event => event.type === 'claude/result' && event.data.includes(token1))
155
+ .pop() || null;
156
+ const q2SentAt = Date.now();
157
+ await harness.typeText(browser, question2);
158
+ await harness.clickSend(browser);
159
+ const q2Busy = await harness.waitForState(browser, state => state.busy === true, 'Q2 to start', 90000);
160
+ const gapMs = q1ResultEvent ? q2SentAt - q1ResultEvent.at : null;
161
+ record('WB-004.d-q2-sent-immediately-after-q1', gapMs !== null && gapMs < 10000 ? 'PASS' : 'BLOCKED', {
162
+ q1ResultEventAt: q1ResultEvent ? q1ResultEvent.at : null,
163
+ q2SentAt,
164
+ gapMs,
165
+ sameSession: q1Busy.product.activeSessionId === q2Busy.product.activeSessionId,
166
+ sessionId: q2Busy.product.activeSessionId,
167
+ });
168
+ assertTrue(gapMs !== null && gapMs < 10000, 'Q2 was not sent immediately after Q1');
169
+
170
+ // The fault injection lands while Q2 is still running: the replay is
171
+ // issued as soon as the UI reports Q2 busy, and the product's answer to
172
+ // the stale event is read back immediately afterwards.
173
+ const beforeReplay = await harness.readState(browser);
174
+ assertTrue(beforeReplay.busy === true, 'Q2 was no longer running when the fault injection was due');
175
+ const events = await harness.recordedEvents(browser);
176
+ const q1ResultIndex = events.findIndex(event => event.type === 'claude/result' && event.data.includes(token1));
177
+ assertTrue(q1ResultIndex >= 0, 'the real Q1 result event was not recorded by the product stream');
178
+ const q1ResultData = events[q1ResultIndex].data;
179
+ const replay = await harness.replayRecordedEvent(browser, q1ResultIndex, `/sessions/${q2Busy.product.activeSessionId}/events`);
180
+ await harness.wait(400);
181
+ const afterReplay = await harness.readState(browser);
182
+ const replayedEvents = await harness.recordedEvents(browser);
183
+ const matches = event => event.type === 'claude/result' && event.data === q1ResultData;
184
+ const delivered = replayedEvents.filter(matches).length - events.filter(matches).length;
185
+
186
+ const q1AfterReplay = afterReplay.assistantMessages.filter(text => text.includes(token1)).length;
187
+ const q2DuringReplay = afterReplay.assistantMessages.filter(text => text.includes(token2)).length;
188
+ const assistantCountBefore = beforeReplay.assistantMessages.length;
189
+ const assistantCountAfter = afterReplay.assistantMessages.length;
190
+ const staleDropped = q1AfterReplay === 1 && assistantCountAfter === assistantCountBefore;
191
+ const stillBusy = afterReplay.busy === true && afterReplay.product.sessionState !== 'idle';
192
+ record('WB-004.e-stale-q1-message-replayed-into-the-live-q2-turn', staleDropped && stillBusy ? 'PASS' : 'BLOCKED', {
193
+ injectedEvent: { index: q1ResultIndex, type: events[q1ResultIndex].type, sequence: (() => { try { return JSON.parse(q1ResultData).sequence; } catch { return null; } })() },
194
+ replayResult: replay,
195
+ timesTheSameRealQ1EventReachedTheProduct: delivered,
196
+ productLiveRevisionBefore: beforeReplay.product.liveRevision,
197
+ productLiveRevisionAfter: afterReplay.product.liveRevision,
198
+ staleEventAcceptedByTheProduct: !staleDropped,
199
+ assistantMessagesBeforeReplay: assistantCountBefore,
200
+ assistantMessagesAfterReplay: assistantCountAfter,
201
+ q1BodyOccurrencesAfterReplay: q1AfterReplay,
202
+ q2BodyOccurrencesDuringReplay: q2DuringReplay,
203
+ uiBusyAfterReplay: afterReplay.busy,
204
+ uiSessionStateAfterReplay: afterReplay.product.sessionState,
205
+ uiStatusTextAfterReplay: afterReplay.statusText,
206
+ note: 'the replayed frame is byte-identical to the real Q1 result frame of this round and is delivered to the very same session SSE stream the product reads; whether it is rendered is decided by the product\'s own session-sequence guard (apps/agent-terminal/app.js)',
207
+ });
208
+ assertTrue(replay.replayed === true && replay.delivered >= 1, 'the replayed Q1 message never reached the product');
209
+ assertTrue(delivered >= 1, 'the replayed Q1 message did not reach the product a second time');
210
+ assertTrue(staleDropped, 'the product rendered the stale Q1 event (Q1 body repeated)');
211
+ assertTrue(stillBusy, 'Q2 was wrongly put into idle by the stale Q1 event');
212
+
213
+ // --- Q2 completes ----------------------------------------------------
214
+ const q2Done = await harness.waitForState(browser, state => state.busy === false && state.assistantMessages.filter(text => text.includes(token2)).length === 1, 'Q2 to end', TURN_TIMEOUT_MS);
215
+ const finalAnswers = q2Done.assistantMessages;
216
+ const q1Indexes = finalAnswers.map((text, index) => (text.includes(token1) ? index : -1)).filter(index => index >= 0);
217
+ const q2Indexes = finalAnswers.map((text, index) => (text.includes(token2) ? index : -1)).filter(index => index >= 0);
218
+ const q1Final = q1Indexes.length;
219
+ const q2Final = q2Indexes.length;
220
+ const order = q1Final === 1 && q2Final === 1 && q1Indexes[0] < q2Indexes[0];
221
+ record('WB-004.f-q2-completed-with-its-own-answer', order ? 'PASS' : 'BLOCKED', {
222
+ assistantMessageCount: finalAnswers.length,
223
+ q1Answers: q1Final,
224
+ q2Answers: q2Final,
225
+ q1BeforeQ2InTheTranscript: order,
226
+ uiSessionState: q2Done.product.sessionState,
227
+ paidTurns: 2,
228
+ });
229
+ assertTrue(order, 'the final transcript does not hold exactly one Q1 and one Q2 answer in order');
230
+
231
+ // --- the vendor-returned model of this run ---------------------------
232
+ const sessionId = q2Done.product.activeSessionId;
233
+ const initEvents = (await harness.recordedEvents(browser)).filter(event => event.type === 'claude/init');
234
+ const claudeSessionIds = initEvents.map(event => { try { return JSON.parse(event.data).claudeSessionId; } catch { return null; } }).filter(Boolean);
235
+ const transcript = scanTranscript(
236
+ [host.layout.claudeConfigDir, path.join(host.layout.home, '.claude')],
237
+ claudeSessionIds,
238
+ [token1, token2],
239
+ );
240
+ const vendorModelOk = transcript.assistantModelValues.length > 0
241
+ && transcript.assistantModelValues.every(model => model.replace(/\s+/g, '').toLowerCase() === REQUESTED_MODEL.replace(/\s+/g, '').toLowerCase())
242
+ && transcript.tokenEchoedInTranscript;
243
+ record('WB-004.g-vendor-returned-model-from-the-real-transcript', vendorModelOk ? 'PASS' : 'BLOCKED', {
244
+ requestedModel: REQUESTED_MODEL,
245
+ assistantModelValues: transcript.assistantModelValues,
246
+ tokenEchoedInTranscript: transcript.tokenEchoedInTranscript,
247
+ transcriptFilesMatched: transcript.transcriptFilesMatched,
248
+ claudeSessionIds,
249
+ workbenchSessionId: sessionId,
250
+ });
251
+
252
+ outcome = {
253
+ workbenchSessionId: sessionId,
254
+ q1Token: token1,
255
+ q2Token: token2,
256
+ gapMs,
257
+ q1FinalOccurrences: q1Final,
258
+ q2FinalOccurrences: q2Final,
259
+ vendorReturnedModels: transcript.assistantModelValues,
260
+ paidTurns: 2,
261
+ };
262
+ } catch (error) {
263
+ blocked = { message: String(error && error.message ? error.message : error) };
264
+ record('WB-004.runtime', 'BLOCKED', { error: blocked.message });
265
+ } finally {
266
+ if (browser) { try { await browser.close(); } catch { /* the browser is disposable */ } }
267
+ if (host.child) { await harness.stopHost(host); }
268
+ }
269
+
270
+ const digestComparison = launcher.compareDigests(realProfilesBefore, launcher.digestDirectory(boot.discovery.realProfileDir));
271
+ record('WB-004.h-real-profiles-byte-identical', digestComparison.byteIdentical ? 'PASS' : 'FAIL', {
272
+ profileDir: boot.discovery.realProfileDir,
273
+ comparison: 'byte-level SHA256, computed in memory; digests never printed, logged or persisted',
274
+ fileCount: digestComparison.count,
275
+ changedFiles: digestComparison.changedFiles,
276
+ addedFiles: digestComparison.addedFiles,
277
+ removedFiles: digestComparison.removedFiles,
278
+ });
279
+
280
+ const essential = [
281
+ 'WB-004.a-real-browser-and-host',
282
+ 'WB-004.b-model-selected-through-the-real-ui',
283
+ 'WB-004.c-q1-answered-by-the-real-model',
284
+ 'WB-004.d-q2-sent-immediately-after-q1',
285
+ 'WB-004.e-stale-q1-message-replayed-into-the-live-q2-turn',
286
+ 'WB-004.f-q2-completed-with-its-own-answer',
287
+ 'WB-004.g-vendor-returned-model-from-the-real-transcript',
288
+ 'WB-004.h-real-profiles-byte-identical',
289
+ ];
290
+ const proven = essential.every(id => (results.find(entry => entry.caseId === id) || {}).status === 'PASS');
291
+
292
+ const summary = {
293
+ lane: 'test-workbench-runtime',
294
+ file: 'parallel-workbench-runtime-wb004-ordering.js',
295
+ case: 'WB-004',
296
+ title: '连续回复次序(真实浏览器 + 真实模型 + 故障注入)',
297
+ module: identity,
298
+ host: { mode: 'standalone', engineUrlConfigured: false, dataDir: host.layout.dataDir, workspace: host.layout.workspace },
299
+ profile: { id: PROFILE_ID, source: 'original-subscription-store read-only, credential in memory only', credentialWrittenAnywhere: false },
300
+ faultInjection: 'a real Q1 event of this round, recorded on the product\'s own SSE stream, is delivered a second time to that same stream while Q2 is running',
301
+ turn: outcome,
302
+ cases: results,
303
+ verdict: proven ? 'PASS (ordering proven: no repeated Q1 body, no wrongly idle Q2)' : 'BLOCKED',
304
+ paidTurnsSent: outcome ? outcome.paidTurns : 0,
305
+ quotaEndpointQueried: false,
306
+ networkUsed: true,
307
+ };
308
+
309
+ const evidenceTarget = process.env.PARALLEL_WORKBENCH_RUNTIME_EVIDENCE;
310
+ harness.writeEvidence(evidenceTarget, summary);
311
+ if (!proven) {
312
+ // A real API error keeps the scene: the isolated layout is left on disk.
313
+ console.log(`SCENE KEPT ${host.layout.root}`);
314
+ } else {
315
+ fixture.removeTree(host.layout.root);
316
+ }
317
+ console.log(`parallel-workbench-runtime-wb004-ordering verdict=${summary.verdict}`);
318
+ process.exitCode = proven ? 0 : 3;
319
+ })().catch(error => {
320
+ console.error(error);
321
+ process.exitCode = 1;
322
+ });
323
+
324
+ function assertTrue(condition, message) {
325
+ if (!condition) throw new Error(message);
326
+ }
@@ -0,0 +1,336 @@
1
+ 'use strict';
2
+ // WB-003 — real subscription model probe through an isolated real host.
3
+ //
4
+ // What this probe does:
5
+ // * it hosts the REAL installed claude-ai-workbench module (the copy the
6
+ // installed product ships) as an isolated integration host, with this
7
+ // lane's controlled knowledge-engine fixture behind KB_KNOWLEDGE_ENGINE_URL;
8
+ // * it injects the ORIGINAL subscription profile store through a read-only
9
+ // facade: the credential is read from its real location into memory and
10
+ // is never written to any file, log, report or clone. No write method on
11
+ // the store is possible;
12
+ // * it asks ONE unique random question and requires, as evidence:
13
+ // - the SDK-REPORTED model from the system/init event — the runner
14
+ // copies it from the CLI's own init message, so it is what the SDK
15
+ // reports for the session, NOT by itself proof of what the vendor
16
+ // returned (the profile label alone never counts as evidence either);
17
+ // the vendor-RETURNED model is message.model of real assistant
18
+ // messages in the CLI transcript, extracted by
19
+ // parallel-workbench-standalone-turn.js,
20
+ // - the vendor claude session id,
21
+ // - the answer echoing the unique token of this round (request
22
+ // correlation),
23
+ // - the controlled integration fixture having received this session's
24
+ // context request (integration-context correlation).
25
+ //
26
+ // What this probe does NOT do:
27
+ // * it sends NO MiniMax request. The user confirmed the MiniMax quota is
28
+ // exhausted (real 429, vendor code 2067); the positive MiniMax-M3.1-Flash
29
+ // acceptance stays BLOCKED on the preserved 429 evidence and no attempt
30
+ // is made to turn it green, and a GLM answer is never presented as a
31
+ // MiniMax answer;
32
+ // * it never queries a quota endpoint, never switches provider or plan,
33
+ // and never fakes a model response.
34
+ //
35
+ // Exit codes: 0 = every sub-item positively verified;
36
+ // 3 = at least one sub-item honestly BLOCKED (recorded);
37
+ // 1 = script error.
38
+ const assert = require('node:assert/strict');
39
+ const fs = require('node:fs');
40
+ const path = require('node:path');
41
+ const crypto = require('node:crypto');
42
+ const fixture = require('./helpers/parallel-workbench-fixture');
43
+ const sourceMap = require('./helpers/parallel-workbench-source-map');
44
+
45
+ const PROFILE_ID = String(process.env.PK_WB_MODEL_PROFILE || 'glm');
46
+ const REQUESTED_MODEL = String(process.env.PK_WB_MODEL_ID || 'glm-5.3');
47
+ const EFFORT = String(process.env.PK_WB_MODEL_EFFORT || 'max');
48
+ const TURN_TIMEOUT_MS = Number(process.env.PK_WB_TURN_TIMEOUT_MS || 360000);
49
+ const MINIMAX_429_EVIDENCE = 'qa-autopilot/artifacts/parallel-20261023/minimax-429-handoff/minimax-result.json';
50
+
51
+ const results = [];
52
+ function record(caseId, status, detail) {
53
+ results.push({ caseId, status, detail });
54
+ console.log(`CASE ${caseId} ${status} ${JSON.stringify(detail)}`);
55
+ }
56
+
57
+ async function getJson(url, options) {
58
+ const response = await fetch(url, { ...options, signal: AbortSignal.timeout(30000) });
59
+ const body = await response.json().catch(() => null);
60
+ return { status: response.status, ok: response.ok, body };
61
+ }
62
+
63
+ // Consume the session SSE stream until the predicate is satisfied.
64
+ async function consumeEvents(url, onEvent, until, timeoutMs) {
65
+ const controller = new AbortController();
66
+ const timer = setTimeout(() => controller.abort(new Error(`event stream timed out after ${timeoutMs}ms`)), timeoutMs);
67
+ let done = null;
68
+ const finished = new Promise(resolve => { done = resolve; });
69
+ (async () => {
70
+ try {
71
+ const response = await fetch(url, { signal: controller.signal });
72
+ if (!response.ok) throw new Error(`event stream HTTP ${response.status}`);
73
+ const reader = response.body.getReader();
74
+ const decoder = new TextDecoder();
75
+ let buffer = '';
76
+ for (;;) {
77
+ const { value, done: streamDone } = await reader.read();
78
+ if (streamDone) break;
79
+ buffer += decoder.decode(value, { stream: true });
80
+ let index;
81
+ while ((index = buffer.indexOf('\n')) >= 0) {
82
+ const line = buffer.slice(0, index).replace(/\r$/, '');
83
+ buffer = buffer.slice(index + 1);
84
+ if (!line.startsWith('data:')) continue;
85
+ const payload = line.slice(5).trim();
86
+ if (!payload) continue;
87
+ let event;
88
+ try { event = JSON.parse(payload); } catch { continue; }
89
+ try { onEvent(event); } catch { /* listener errors never kill the stream */ }
90
+ if (until(event)) { controller.abort(); return; }
91
+ }
92
+ }
93
+ } catch { /* aborted or ended */ }
94
+ finally { done && done(); }
95
+ })();
96
+ await finished;
97
+ clearTimeout(timer);
98
+ }
99
+
100
+ (async () => {
101
+ const moduleRoot = fixture.resolveHostModuleRoot();
102
+ assert.ok(moduleRoot, 'no claude-ai-workbench module copy was found to host');
103
+ const identity = sourceMap.packageIdentity(moduleRoot);
104
+ const moduleRootLabel = moduleRoot === sourceMap.resolveInstalledProductPackage()
105
+ ? 'currently-installed-package:installed-product'
106
+ : 'currently-installed-package:repository-pin';
107
+
108
+ // ---------- the MiniMax positive acceptance stays BLOCKED ---------------
109
+ // No new MiniMax request is attempted. The preserved evidence of the real
110
+ // 429 (vendor code 2067) is referenced, not re-run.
111
+ let minimaxEvidence = null;
112
+ try {
113
+ minimaxEvidence = JSON.parse(fs.readFileSync(path.resolve(fixture.REPO_ROOT, MINIMAX_429_EVIDENCE), 'utf8'));
114
+ } catch { minimaxEvidence = null; }
115
+ const minimaxBlocked = Boolean(
116
+ minimaxEvidence
117
+ && minimaxEvidence.isError === true
118
+ && /429/.test(String(minimaxEvidence.result || '')),
119
+ );
120
+ record('WB-003.x-minimax-positive', 'BLOCKED', {
121
+ rule: '模型返回的确是MiniMax-M3.1-Flash',
122
+ status: 'BLOCKED',
123
+ reason: 'the MiniMax subscription quota is exhausted; the real API rejected the lanes with 429 (vendor code 2067, Token Plan 用量上限)',
124
+ newMiniMaxRequestAttempted: false,
125
+ glmEvidenceCountedAsMiniMax: false,
126
+ preservedEvidence: MINIMAX_429_EVIDENCE,
127
+ preservedEvidenceVerified: Boolean(minimaxEvidence),
128
+ preservedIsError: minimaxEvidence ? minimaxEvidence.isError : null,
129
+ preservedAt: minimaxEvidence ? minimaxEvidence.at : null,
130
+ note: 'per the user instruction the probe does not send further MiniMax requests and does not present GLM responses as MiniMax evidence',
131
+ });
132
+
133
+ // ---------- the real subscription turn ----------------------------------
134
+ const layout = fixture.createIsolationLayout('parallel-wb-model');
135
+ const integrationFixture = await fixture.startIntegrationFixture();
136
+ const port = await fixture.freePort();
137
+ const base = `http://127.0.0.1:${port}`;
138
+ const child = fixture.spawnNode([path.join(__dirname, 'helpers', 'parallel-workbench-host.js')], fixture.hostEnv(layout, {
139
+ PK_WB_MODULE_ROOT: moduleRoot,
140
+ PK_WB_HOST_PORT: String(port),
141
+ PK_WB_HOST_DATA_DIR: layout.dataDir,
142
+ PK_WB_HOST_WORKSPACE: layout.workspace,
143
+ PK_WB_HOST_MODE: 'integration',
144
+ PK_WB_ENGINE_URL: integrationFixture.url,
145
+ PK_WB_PROFILE_SOURCE: 'real-readonly',
146
+ }));
147
+
148
+ const token = `WB003-${PROFILE_ID.toUpperCase()}-${crypto.randomBytes(5).toString('hex')}`;
149
+ const question = [
150
+ '这是一次真实订阅模型抽测(Workbench WB-003)。',
151
+ `请只回复一行,原样包含以下随机标记,不要添加任何其他内容:${token}`,
152
+ ].join('\n');
153
+
154
+ let glmOutcome;
155
+ try {
156
+ await fixture.waitForHttp(`${base}/api/claude-workbench/v1/health`, 30000);
157
+
158
+ // The original profile store, read through the read-only facade: the
159
+ // masked public shape only, never the credential.
160
+ const profiles = await getJson(`${base}/api/claude-workbench/v1/profiles`);
161
+ const listed = Array.isArray(profiles.body && profiles.body.profiles) ? profiles.body.profiles : [];
162
+ const target = listed.find(profile => profile.id === PROFILE_ID) || null;
163
+ record('WB-003.a-profile-injected-readonly', target ? 'PASS' : 'BLOCKED', {
164
+ profileId: PROFILE_ID,
165
+ credentialConfigured: target ? target.credentialConfigured === true : null,
166
+ baseUrl: target ? target.baseUrl : null,
167
+ defaultModel: target ? target.models && target.models.default : null,
168
+ credentialMaterialLogged: false,
169
+ storeWritesPossible: false,
170
+ });
171
+ assert.ok(target, `the ${PROFILE_ID} profile was not visible through the read-only facade`);
172
+ assert.equal(target.credentialConfigured, true, 'the original credential is not configured');
173
+
174
+ const session = await getJson(`${base}/api/claude-workbench/v1/sessions`, {
175
+ method: 'POST',
176
+ headers: { 'Content-Type': 'application/json' },
177
+ body: JSON.stringify({
178
+ context: { contextId: 'wb003-model-probe', workspaceRef: 'wb-fixture' },
179
+ aiProfileId: PROFILE_ID,
180
+ model: REQUESTED_MODEL,
181
+ effort: EFFORT,
182
+ }),
183
+ });
184
+ const created = session.body && session.body.session;
185
+ record('WB-003.b-session-with-subscription-profile', session.status === 201 && created ? 'PASS' : 'BLOCKED', {
186
+ httpStatus: session.status,
187
+ sessionId: created && created.sessionId,
188
+ model: created && created.model,
189
+ authSource: created && created.authSource,
190
+ providerName: created && created.providerName,
191
+ });
192
+ assert.equal(session.status, 201);
193
+ const sessionId = created.sessionId;
194
+ assert.equal(created.authSource, 'profile-injected');
195
+
196
+ // Stream the session events and run the unique question.
197
+ const events = [];
198
+ let settle = null;
199
+ const settled = new Promise(resolve => { settle = resolve; });
200
+ const isTerminal = event => event.type === 'claude/result';
201
+ const streamUrl = `${base}/api/claude-workbench/v1/sessions/${encodeURIComponent(sessionId)}/events`;
202
+ const consume = consumeEvents(streamUrl, event => {
203
+ if (['claude/init', 'claude/result', 'claude/error', 'claude/aborted'].includes(event.type)) events.push(event);
204
+ }, isTerminal, TURN_TIMEOUT_MS + 60000).catch(() => {});
205
+
206
+ const send = await getJson(`${base}/api/claude-workbench/v1/sessions/${encodeURIComponent(sessionId)}/input`, {
207
+ method: 'POST',
208
+ headers: { 'Content-Type': 'application/json' },
209
+ body: JSON.stringify({ text: question }),
210
+ });
211
+ record('WB-003.c-unique-question-accepted', send.status === 202 ? 'PASS' : 'BLOCKED', {
212
+ httpStatus: send.status,
213
+ uniqueToken: token,
214
+ });
215
+ assert.equal(send.status, 202);
216
+
217
+ const deadline = Date.now() + TURN_TIMEOUT_MS;
218
+ while (Date.now() < deadline && !events.some(isTerminal)) {
219
+ await new Promise(resolve => setTimeout(resolve, 250));
220
+ }
221
+ settle && settle();
222
+ await Promise.race([consume, new Promise(resolve => setTimeout(resolve, 5000))]);
223
+
224
+ const init = events.find(event => event.type === 'claude/init') || null;
225
+ const result = events.find(event => event.type === 'claude/result') || null;
226
+ const error = events.find(event => event.type === 'claude/error') || null;
227
+ // SDK-reported model: the system/init string the CLI produced for this
228
+ // session. Vendor-RETURNED evidence is the assistant message.model of the
229
+ // CLI transcript (see parallel-workbench-standalone-turn.js).
230
+ const sdkReportedModel = init ? init.model : null;
231
+ const vendorSessionId = init ? init.claudeSessionId : null;
232
+ const answer = result ? String(result.result || '') : '';
233
+ const tokenEchoed = answer.includes(token);
234
+ const isError = result ? result.isError === true : true;
235
+
236
+ glmOutcome = {
237
+ requestedModel: REQUESTED_MODEL,
238
+ effort: EFFORT,
239
+ sdkReportedModel,
240
+ sdkReportedModelMatchesRequest: typeof sdkReportedModel === 'string'
241
+ ? sdkReportedModel.replace(/\s+/g, '').toLowerCase() === REQUESTED_MODEL.replace(/\s+/g, '').toLowerCase()
242
+ : false,
243
+ vendorClaudeSessionId: vendorSessionId,
244
+ workbenchSessionId: sessionId,
245
+ isError,
246
+ answerPreview: answer.slice(0, 300),
247
+ tokenEchoed,
248
+ usage: result && result.usage ? {
249
+ inputTokens: result.usage.input_tokens,
250
+ outputTokens: result.usage.output_tokens,
251
+ } : null,
252
+ errorEvent: error,
253
+ };
254
+
255
+ // S1: the SDK-reported model, verbatim — labelled as what it is.
256
+ record('WB-003.d1-sdk-model-reported', typeof sdkReportedModel === 'string' && sdkReportedModel.length > 0 ? 'PASS' : 'BLOCKED', {
257
+ sdkReportedModel,
258
+ note: 'the system/init model string the SDK reports for the session; it is NOT by itself vendor-returned evidence — the vendor-returned model is the assistant message.model of the CLI transcript (parallel-workbench-standalone-turn.js)',
259
+ });
260
+
261
+ // S2: the response is correlated to THIS round.
262
+ record('WB-003.d2-response-correlated', !isError && tokenEchoed ? 'PASS' : 'BLOCKED', {
263
+ isError,
264
+ uniqueToken: token,
265
+ tokenEchoedInAnswer: tokenEchoed,
266
+ vendorClaudeSessionId: vendorSessionId,
267
+ workbenchSessionId: sessionId,
268
+ });
269
+
270
+ // S3: no stand-in anywhere in the chain.
271
+ record('WB-003.d3-no-standin', !isError && typeof sdkReportedModel === 'string' && sdkReportedModel.length > 0 ? 'PASS' : 'BLOCKED', {
272
+ profileSource: 'original-subscription-store-read-only-in-memory',
273
+ standInEmbedderUsed: false,
274
+ mockProviderUsed: false,
275
+ lexicalFallbackUsed: false,
276
+ providerSwitched: false,
277
+ });
278
+
279
+ // S4: the integration context was consumed by the SAME real turn.
280
+ const knowledge = await getJson(`${base}/api/claude-workbench/v1/sessions/${encodeURIComponent(sessionId)}/knowledge`);
281
+ const fixtureRequest = integrationFixture.requests.find(request => request.body && request.body.sessionId === sessionId) || null;
282
+ const queryCarriedToken = Boolean(fixtureRequest && String(fixtureRequest.body.query || '').includes(token));
283
+ record('WB-003.d4-integration-context-correlated', knowledge.ok && knowledge.body && knowledge.body.status === 'ready' && fixtureRequest ? 'PASS' : 'BLOCKED', {
284
+ knowledgeStatus: knowledge.body && knowledge.body.status,
285
+ fixtureKind: 'controlled-integration-fixture',
286
+ fixtureSawSession: Boolean(fixtureRequest),
287
+ fixtureSawUniqueTokenInQuery: queryCarriedToken,
288
+ });
289
+ } finally {
290
+ child.child.kill();
291
+ await child.exited;
292
+ await integrationFixture.close();
293
+ }
294
+
295
+ const glmPass = ['WB-003.d1-sdk-model-reported', 'WB-003.d2-response-correlated', 'WB-003.d3-no-standin']
296
+ .every(id => (results.find(entry => entry.caseId === id) || {}).status === 'PASS');
297
+
298
+ if (!glmPass) {
299
+ console.log('WB-003 GLM turn did not produce positive evidence — recording the real outcome, no retry loop, no provider switch');
300
+ }
301
+
302
+ const summary = {
303
+ lane: 'test-workbench',
304
+ file: 'parallel-workbench-model-probe.js',
305
+ case: 'WB-003',
306
+ title: '真实模型抽测',
307
+ module: { label: moduleRootLabel, root: moduleRoot, name: identity.name, version: identity.version },
308
+ host: { mode: 'integration', engineFixture: 'controlled-integration-fixture', dataDir: layout.dataDir, workspace: layout.workspace },
309
+ profile: { id: PROFILE_ID, source: 'original-subscription-store read-only, credential in memory only', credentialWrittenAnywhere: false },
310
+ glmTurn: glmOutcome,
311
+ minimaxPositiveAcceptance: {
312
+ status: 'BLOCKED',
313
+ reason: 'MiniMax subscription quota exhausted — real 429 with vendor code 2067 (Token Plan 用量上限), preserved in the handoff evidence; no further MiniMax request attempted per user instruction',
314
+ evidence: MINIMAX_429_EVIDENCE,
315
+ glmCountedAsMiniMax: false,
316
+ },
317
+ verdict: glmPass ? 'PARTIAL (GLM methodology PASS; MiniMax positive acceptance BLOCKED)' : 'BLOCKED',
318
+ networkUsed: true,
319
+ quotaEndpointQueried: false,
320
+ cases: results,
321
+ };
322
+
323
+ const target = process.env.PARALLEL_WORKBENCH_EVIDENCE;
324
+ if (target) {
325
+ fs.mkdirSync(path.dirname(target), { recursive: true });
326
+ fs.writeFileSync(target, JSON.stringify(summary, null, 2), 'utf8');
327
+ }
328
+ fixture.removeTree(layout.root);
329
+ console.log(`parallel-workbench-model-probe verdict=${summary.verdict}`);
330
+ // The MiniMax positive acceptance is BLOCKED, so the probe never exits 0,
331
+ // even when every GLM methodology sub-item passes.
332
+ process.exitCode = 3;
333
+ })().catch(error => {
334
+ console.error(error);
335
+ process.exitCode = 1;
336
+ });