@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,255 @@
1
+ 'use strict';
2
+ // Lane-owned fixture helpers for the parallel knowledge verification batch.
3
+ // Portability contract: nothing here may hard-code a developer path.
4
+ // The temp root is resolved as PK_TEST_TEMP_ROOT -> TEMP/TMP/TMPDIR ->
5
+ // os.tmpdir(), and every artefact is created with mkdtemp outside the
6
+ // repository, so the same helpers work on a developer machine and on CI.
7
+ // Isolation contract: the helpers themselves clear the ambient module URLs
8
+ // and the live data directories, so a test entry point never depends on the
9
+ // caller remembering `env -u`, and never reaches the developer's real
10
+ // 5757/5790/8787/8790 endpoints or the real HOME.
11
+ const fs = require('node:fs');
12
+ const os = require('node:os');
13
+ const path = require('node:path');
14
+ const crypto = require('node:crypto');
15
+ const net = require('node:net');
16
+ const { execFileSync, spawn } = require('node:child_process');
17
+
18
+ const REPO_ROOT = path.resolve(__dirname, '..', '..', '..');
19
+
20
+ // The developer's live surfaces. A test may never bind or talk to them.
21
+ const FORBIDDEN_PORTS = new Set([5757, 5790, 8787, 8790]);
22
+
23
+ // Ambient module addresses that would otherwise point a test at a live
24
+ // instance. The product falls back to them when no explicit URL is given.
25
+ const AMBIENT_MODULE_URLS = [
26
+ 'KB_KNOWLEDGE_ENGINE_URL',
27
+ 'KB_VECTORHUB_URL',
28
+ 'KB_TERMINAL_URL',
29
+ 'KB_EVENTBRIDGE_URL',
30
+ 'KB_EVENT_BRIDGE_URL',
31
+ ];
32
+
33
+ function isInside(candidate, parent) {
34
+ const relative = path.relative(parent, candidate);
35
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
36
+ }
37
+
38
+ function resolveTempRoot() {
39
+ const candidates = [process.env.PK_TEST_TEMP_ROOT, process.env.TEMP, process.env.TMP, process.env.TMPDIR];
40
+ const chosen = candidates.find(value => value && String(value).trim());
41
+ const root = path.resolve(chosen ? String(chosen).trim() : os.tmpdir());
42
+ if (isInside(root, REPO_ROOT)) {
43
+ throw new Error(`refusing to use a temp root inside the repository: ${root}`);
44
+ }
45
+ return root;
46
+ }
47
+
48
+ function ensureTempRoot() {
49
+ const root = resolveTempRoot();
50
+ fs.mkdirSync(root, { recursive: true });
51
+ return root;
52
+ }
53
+
54
+ function makeTempDir(prefix = 'parallel-knowledge') {
55
+ const root = ensureTempRoot();
56
+ const dir = fs.mkdtempSync(path.join(root, `${prefix}-`));
57
+ if (isInside(dir, REPO_ROOT)) throw new Error(`refusing to create a fixture inside the repository: ${dir}`);
58
+ return dir;
59
+ }
60
+
61
+ function git(cwd, args) {
62
+ return execFileSync('git', args, { cwd, windowsHide: true, encoding: 'utf8' }).trim();
63
+ }
64
+
65
+ function sha256Buffer(value) {
66
+ return crypto.createHash('sha256').update(value).digest('hex');
67
+ }
68
+
69
+ function sha256File(filePath) {
70
+ return fs.existsSync(filePath) ? sha256Buffer(fs.readFileSync(filePath)) : null;
71
+ }
72
+
73
+ // A real Git worktree with real commits. `files` are committed in order.
74
+ function makeRepo(root, name, commits = []) {
75
+ const repoPath = path.join(root, name);
76
+ fs.mkdirSync(repoPath, { recursive: true });
77
+ git(repoPath, ['init', '-q']);
78
+ git(repoPath, ['config', 'user.email', 'parallel-knowledge@local.invalid']);
79
+ git(repoPath, ['config', 'user.name', 'Parallel Knowledge Test']);
80
+ git(repoPath, ['config', 'commit.gpgsign', 'false']);
81
+ const shas = [];
82
+ for (const commit of commits) {
83
+ for (const [file, content] of Object.entries(commit.files || {})) {
84
+ const target = path.join(repoPath, file);
85
+ fs.mkdirSync(path.dirname(target), { recursive: true });
86
+ fs.writeFileSync(target, content, 'utf8');
87
+ }
88
+ git(repoPath, ['add', '-A']);
89
+ git(repoPath, ['commit', '-q', '-m', commit.message || 'fixture commit']);
90
+ shas.push(git(repoPath, ['rev-parse', 'HEAD']));
91
+ }
92
+ return { repoPath, shas, head: shas[shas.length - 1] || null };
93
+ }
94
+
95
+ function makeKnowledgeDir(root, name) {
96
+ const knowledgePath = path.join(root, name);
97
+ fs.mkdirSync(knowledgePath, { recursive: true });
98
+ return knowledgePath;
99
+ }
100
+
101
+ // Relative path -> sha256 for every file below `dir`.
102
+ function hashTree(dir) {
103
+ const out = {};
104
+ if (!fs.existsSync(dir)) return out;
105
+ const walk = current => {
106
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
107
+ const absolute = path.join(current, entry.name);
108
+ if (entry.isDirectory()) walk(absolute);
109
+ else out[path.relative(dir, absolute).replace(/\\/g, '/')] = sha256File(absolute);
110
+ }
111
+ };
112
+ walk(dir);
113
+ return out;
114
+ }
115
+
116
+ function assertAllowedPort(port, what = 'port') {
117
+ if (FORBIDDEN_PORTS.has(Number(port))) {
118
+ throw new Error(`refusing to use the live ${what} ${port}`);
119
+ }
120
+ return Number(port);
121
+ }
122
+
123
+ function assertAllowedEndpoint(url, what = 'endpoint') {
124
+ const parsed = new URL(String(url));
125
+ const port = parsed.port ? Number(parsed.port) : (parsed.protocol === 'https:' ? 443 : 80);
126
+ if (FORBIDDEN_PORTS.has(port)) throw new Error(`refusing to contact the live ${what} ${url}`);
127
+ return url;
128
+ }
129
+
130
+ async function freePort() {
131
+ return new Promise((resolve, reject) => {
132
+ const server = net.createServer();
133
+ server.unref();
134
+ server.on('error', reject);
135
+ server.listen(0, '127.0.0.1', () => {
136
+ const { port } = server.address();
137
+ server.close(() => {
138
+ try { resolve(assertAllowedPort(port)); } catch (error) { reject(error); }
139
+ });
140
+ });
141
+ });
142
+ }
143
+
144
+ async function waitForHttp(url, timeoutMs = 20000) {
145
+ assertAllowedEndpoint(url, 'service');
146
+ const until = Date.now() + timeoutMs;
147
+ let lastError = null;
148
+ while (Date.now() < until) {
149
+ try {
150
+ const response = await fetch(url, { signal: AbortSignal.timeout(2000) });
151
+ if (response.ok) return true;
152
+ lastError = new Error(`HTTP ${response.status}`);
153
+ } catch (error) { lastError = error; }
154
+ await new Promise(resolve => setTimeout(resolve, 100));
155
+ }
156
+ throw new Error(`Timed out waiting for ${url}: ${lastError && lastError.message}`);
157
+ }
158
+
159
+ // The environment a child may inherit: no ambient module address, no live
160
+ // data directory, no real HOME.
161
+ function sanitizedEnv(extra = {}) {
162
+ const env = { ...process.env, ...extra };
163
+ for (const key of AMBIENT_MODULE_URLS) delete env[key];
164
+ return env;
165
+ }
166
+
167
+ function spawnNode(args, env = {}) {
168
+ const child = spawn(process.execPath, args, {
169
+ env: sanitizedEnv(env),
170
+ windowsHide: true,
171
+ stdio: ['ignore', 'pipe', 'pipe'],
172
+ });
173
+ const output = { stdout: '', stderr: '' };
174
+ child.stdout.on('data', chunk => { output.stdout += chunk; });
175
+ child.stderr.on('data', chunk => { output.stderr += chunk; });
176
+ const exited = new Promise(resolve => child.once('close', code => resolve({ code, ...output })));
177
+ return { child, exited, output };
178
+ }
179
+
180
+ async function eventually(fn, timeoutMs = 15000, intervalMs = 25) {
181
+ const until = Date.now() + timeoutMs;
182
+ while (Date.now() < until) {
183
+ if (await fn()) return true;
184
+ await new Promise(resolve => setTimeout(resolve, intervalMs));
185
+ }
186
+ return false;
187
+ }
188
+
189
+ function removeTree(dir) {
190
+ if (!dir) return;
191
+ fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
192
+ }
193
+
194
+ // Isolate the current process. This MUST run before any product module is
195
+ // required, because the product reads its data directories and module
196
+ // addresses from the environment. It returns the isolated paths so the
197
+ // caller can clean them up.
198
+ function isolateProcess() {
199
+ if (isolateProcess.done) return isolateProcess.state;
200
+ const root = makeTempDir('parallel-knowledge-isolate');
201
+ const dataHome = path.join(root, 'kb-data');
202
+ const knowledgeHome = path.join(root, 'kb-knowledge-data');
203
+ const bridgeHome = path.join(root, 'event-bridge-home');
204
+ const home = path.join(root, 'home');
205
+ for (const dir of [dataHome, knowledgeHome, bridgeHome, home]) fs.mkdirSync(dir, { recursive: true });
206
+
207
+ for (const key of AMBIENT_MODULE_URLS) delete process.env[key];
208
+ process.env.KB_DATA_DIR = dataHome;
209
+ process.env.KB_KNOWLEDGE_DATA_DIR = knowledgeHome;
210
+ process.env.AI_CODING_EVENT_BRIDGE_HOME = bridgeHome;
211
+ process.env.KB_MODULES_AUTOSTART = '0';
212
+
213
+ // The real HOME must never be read or written by a lane test.
214
+ process.env.HOME = home;
215
+ process.env.USERPROFILE = home;
216
+ const parsed = path.parse(home);
217
+ process.env.HOMEDRIVE = parsed.root;
218
+ process.env.HOMEPATH = parsed.root === path.sep ? home.slice(1) : home.slice(parsed.root.length);
219
+
220
+ isolateProcess.state = { root, dataHome, knowledgeHome, bridgeHome, home };
221
+ isolateProcess.done = true;
222
+ return isolateProcess.state;
223
+ }
224
+
225
+ function cleanupIsolatedProcess() {
226
+ if (!isolateProcess.done) return;
227
+ removeTree(isolateProcess.state.root);
228
+ isolateProcess.done = false;
229
+ isolateProcess.state = null;
230
+ }
231
+
232
+ module.exports = {
233
+ REPO_ROOT,
234
+ FORBIDDEN_PORTS,
235
+ AMBIENT_MODULE_URLS,
236
+ resolveTempRoot,
237
+ ensureTempRoot,
238
+ makeTempDir,
239
+ makeRepo,
240
+ makeKnowledgeDir,
241
+ hashTree,
242
+ sha256File,
243
+ sha256Buffer,
244
+ git,
245
+ freePort,
246
+ waitForHttp,
247
+ spawnNode,
248
+ sanitizedEnv,
249
+ eventually,
250
+ removeTree,
251
+ assertAllowedPort,
252
+ assertAllowedEndpoint,
253
+ isolateProcess,
254
+ cleanupIsolatedProcess,
255
+ };
@@ -0,0 +1,111 @@
1
+ 'use strict';
2
+ // P-LOG-ACCEPTANCE-01 lane-exclusive fixture (prefix parallel-logging-acceptance-*).
3
+ //
4
+ // Builds an isolated product data dir with N registered projects and
5
+ // pre-written structured log records, mirroring helpers/logging-ui-fixture.js
6
+ // (read-only reference) but with multi-project isolation support for
7
+ // LOG-003/LOG-005/LOG-006. All directories live under the lane temp root the
8
+ // driver installed into TEMP/TMP/TMPDIR (D:/SanQian.Xu/.project-knowledge-test-temp),
9
+ // never the developer's real HOME.
10
+
11
+ const fs = require('node:fs');
12
+ const path = require('node:path');
13
+ const { StorageLayout } = require('../../lib/storage-layout');
14
+ const { SettingsStore } = require('../../lib/settings-store');
15
+ const { ProjectRegistryStore } = require('../../lib/project-registry-store');
16
+ const { ProjectStore } = require('../../lib/project-store');
17
+ const { Logger } = require('../../lib/structured-logger');
18
+ const { controlPath } = require('../../lib/runtime-control');
19
+ const { makeRepo } = require('../fixtures/make-git-repos');
20
+
21
+ // The product's runtime-control listens on a filesystem unix socket at
22
+ // <dataDir>/runtime-control.sock on POSIX (runtime-control.js:14; Windows uses
23
+ // a hashed named pipe). Linux sun_path allows 107 chars (macOS 103), so the
24
+ // dataDir path this fixture builds under os.tmpdir() must leave room for the
25
+ // 21-char socket name. CI 40075242420 (linux24): the old 'pk-logging-acc-'
26
+ // prefix made the logfixr2-normal dataDir 88 chars -> a 109-char socket ->
27
+ // net.listen refused it, the server exited 1 before the health gate with empty
28
+ // pipes, and normalhook timed out. 'pk-la-' keeps every lane case <= 100 even
29
+ // on the CI temp root (/home/runner/work/_temp, 23 chars).
30
+ const POSIX_SOCKET_CHAR_LIMIT = 103; // strictest common budget (macOS); all lane cases fit
31
+
32
+ function assertPosixSocketBudget(dataDir) {
33
+ if (process.platform === 'win32') return dataDir; // named pipe, no sun_path
34
+ const socketPath = controlPath(dataDir);
35
+ if (socketPath.length > POSIX_SOCKET_CHAR_LIMIT) {
36
+ throw new Error(`fixture dataDir makes the POSIX control socket ${socketPath.length} chars (limit ${POSIX_SOCKET_CHAR_LIMIT}): ${dataDir}`);
37
+ }
38
+ return dataDir;
39
+ }
40
+
41
+ async function createLoggingAcceptanceFixture(options = {}) {
42
+ const caseId = options.caseId || 'case';
43
+ const dataDir = assertPosixSocketBudget(fs.mkdtempSync(path.join(require('node:os').tmpdir(), `pk-la-${caseId}-`)));
44
+ if (options.freshProduct !== false) require('../../lib/product-data').initializeProductData(dataDir);
45
+ const knowledgeRoot = path.join(dataDir, 'knowledge-root');
46
+ fs.mkdirSync(knowledgeRoot, { recursive: true });
47
+ const layout = new StorageLayout({ dataDir });
48
+ const settingsStore = new SettingsStore({ layout });
49
+ const registryStore = new ProjectRegistryStore({ layout });
50
+ const projectStore = new ProjectStore({ layout });
51
+
52
+ await settingsStore.initialize({
53
+ knowledge: { rootPath: knowledgeRoot },
54
+ logging: { levels: ['trace', 'debug', 'info', 'warn', 'error', 'fatal'], retentionDays: 365, maxTotalSizeMB: 2048 },
55
+ });
56
+ await registryStore.initialize();
57
+
58
+ const projects = [];
59
+ for (const spec of options.projects || []) {
60
+ const repo = makeRepo({ kind: 'one-commit' });
61
+ const knowledgePath = path.join(knowledgeRoot, spec.projectId);
62
+ fs.mkdirSync(knowledgePath, { recursive: true });
63
+ await projectStore.create(spec.projectId, {
64
+ displayName: spec.displayName,
65
+ storageName: spec.projectId,
66
+ repoPath: repo.path,
67
+ knowledgePath,
68
+ }, {
69
+ trackingStartCommit: repo.headCommit,
70
+ lastAnalyzedCommit: repo.headCommit,
71
+ hook: { managedVersion: 2, migrationVersion: 2, lastVerifiedAt: new Date().toISOString() },
72
+ index: { dirty: false, generation: 1 },
73
+ });
74
+ await registryStore.add(spec.projectId, { displayName: spec.displayName });
75
+ projects.push({ projectId: spec.projectId, displayName: spec.displayName, repo, knowledgePath, headCommit: repo.headCommit });
76
+ }
77
+
78
+ const settings = settingsStore.read().logging;
79
+ const logger = new Logger({ layout, settingsProvider: () => settings });
80
+
81
+ return {
82
+ caseId,
83
+ dataDir,
84
+ layout,
85
+ projects,
86
+ logger,
87
+ cleanup() {
88
+ for (const project of projects) { try { project.repo.cleanup(); } catch {} }
89
+ try { fs.rmSync(dataDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); } catch {}
90
+ },
91
+ };
92
+ }
93
+
94
+ // Fixture-time log writer: records carry the caller's fields verbatim so
95
+ // write-time redaction (LOG-006) happens through the real normalizeRecord path.
96
+ async function writeProjectLogs(logger, projectId, records) {
97
+ for (const record of records) {
98
+ await logger.log(record.level || 'info', record.event, record.message, {
99
+ projectId,
100
+ projectSlug: projectId,
101
+ projectDisplayName: record.projectDisplayName || projectId,
102
+ operationId: record.operationId || '',
103
+ component: record.component || 'acceptance-driver',
104
+ commitSha: record.commitSha || '',
105
+ context: record.context || {},
106
+ ...(record.error ? { error: record.error } : {}),
107
+ });
108
+ }
109
+ }
110
+
111
+ module.exports = { createLoggingAcceptanceFixture, writeProjectLogs, assertPosixSocketBudget, POSIX_SOCKET_CHAR_LIMIT };
@@ -0,0 +1,106 @@
1
+ 'use strict';
2
+ // P-LOG-ACCEPTANCE-01 lane-exclusive observation proxy (prefix
3
+ // parallel-logging-acceptance-*). The real product page points at this proxy;
4
+ // the proxy forwards to the isolated server. It exists only to observe and
5
+ // shape the network the real browser sees:
6
+ // * count open connections per pathname prefix (LOG-004: the diagnostics
7
+ // dialog's SSE stream must actually disconnect when the dialog closes);
8
+ // * optionally delay a pathname prefix (LOG-008: the real 3s UI read timeout
9
+ // fires against a slow /api/logs).
10
+ // SSE responses are piped unbuffered both ways - the page's EventSource
11
+ // behaves exactly as against the origin server.
12
+
13
+ const http = require('node:http');
14
+ const net = require('node:net');
15
+
16
+ // One fresh connection per forwarded request: avoids keep-alive reset races
17
+ // between the proxy and the isolated origin during bursty page loads.
18
+ const proxyAgent = new http.Agent({ keepAlive: false });
19
+
20
+ function createObservationProxy({ targetPort, targetHost = '127.0.0.1', port, host = '127.0.0.1' }) {
21
+ const state = {
22
+ activeByPrefix: new Map(),
23
+ requests: [],
24
+ upgrades: [],
25
+ delays: new Map(),
26
+ setDelay(prefix, ms) { if (ms === null) state.delays.delete(prefix); else state.delays.set(prefix, ms); },
27
+ snapshot() {
28
+ return {
29
+ activeByPrefix: Object.fromEntries(state.activeByPrefix),
30
+ requests: state.requests.slice(-200),
31
+ upgrades: state.upgrades.slice(-200),
32
+ };
33
+ },
34
+ };
35
+
36
+ const server = http.createServer((req, res) => {
37
+ const pathname = String(req.url || '').split('?')[0];
38
+ const entry = { method: req.method, pathname, url: String(req.url || ''), openedAt: new Date().toISOString(), status: null, closedAt: null };
39
+ state.requests.push(entry);
40
+ for (const [prefix] of state.activeByPrefix) {
41
+ if (pathname.startsWith(prefix)) state.activeByPrefix.set(prefix, state.activeByPrefix.get(prefix) + 1);
42
+ }
43
+ const trackedPrefix = [...state.activeByPrefix.keys()].find(prefix => pathname.startsWith(prefix));
44
+ const closeTracking = () => {
45
+ if (entry.closedAt) return;
46
+ entry.closedAt = new Date().toISOString();
47
+ if (trackedPrefix) state.activeByPrefix.set(trackedPrefix, Math.max(0, state.activeByPrefix.get(trackedPrefix) - 1));
48
+ };
49
+ res.on('close', closeTracking);
50
+
51
+ const forward = () => {
52
+ const upstream = http.request({ host: targetHost, port: targetPort, method: req.method, path: req.url, agent: proxyAgent, headers: { ...req.headers, host: `${targetHost}:${targetPort}` } }, upstreamRes => {
53
+ entry.status = upstreamRes.statusCode;
54
+ res.writeHead(upstreamRes.statusCode || 502, upstreamRes.headers);
55
+ upstreamRes.pipe(res);
56
+ });
57
+ upstream.on('error', error => { entry.status = 502; entry.error = String(error.message || error); if (!res.headersSent) res.writeHead(502, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: false, error: { message: 'proxy upstream failure' } })); });
58
+ req.pipe(upstream);
59
+ };
60
+
61
+ const delay = [...state.delays.entries()].find(([prefix]) => pathname.startsWith(prefix));
62
+ if (delay) setTimeout(forward, delay[1]);
63
+ else forward();
64
+ });
65
+
66
+ // WebSocket passthrough (the product page multiplexes /api/live over WS);
67
+ // host/origin are rewritten to the target so the server's same-origin
68
+ // security check still passes while the page stays pointed at the proxy.
69
+ server.on('upgrade', (req, socket, head) => {
70
+ const pathname = String(req.url || '').split('?')[0];
71
+ const entry = { method: 'UPGRADE', pathname, url: String(req.url || ''), openedAt: new Date().toISOString(), closedAt: null };
72
+ state.upgrades.push(entry);
73
+ const target = `${targetHost}:${targetPort}`;
74
+ const headers = [];
75
+ for (let i = 0; i < req.rawHeaders.length; i += 2) {
76
+ const name = req.rawHeaders[i].toLowerCase();
77
+ const value = name === 'host' || name === 'origin' ? (name === 'host' ? target : `http://${target}`) : req.rawHeaders[i + 1];
78
+ headers.push(`${req.rawHeaders[i]}: ${value}`);
79
+ }
80
+ const upstream = net.connect(targetPort, targetHost, () => {
81
+ upstream.write(`${req.method} ${req.url} HTTP/1.1\r\n${headers.join('\r\n')}\r\n\r\n`);
82
+ if (head && head.length) upstream.write(head);
83
+ socket.pipe(upstream);
84
+ upstream.pipe(socket);
85
+ });
86
+ const kill = () => { entry.closedAt = entry.closedAt || new Date().toISOString(); socket.destroy(); upstream.destroy(); };
87
+ socket.on('error', kill);
88
+ upstream.on('error', kill);
89
+ socket.on('close', kill);
90
+ upstream.on('close', kill);
91
+ });
92
+
93
+ return new Promise(resolve => server.listen(port, host, () => resolve({
94
+ port: server.address().port,
95
+ url: `http://${host}:${server.address().port}`,
96
+ state,
97
+ trackPrefix(prefix) { state.activeByPrefix.set(prefix, 0); },
98
+ setDelay(prefix, ms) { state.setDelay(prefix, ms); },
99
+ async close() {
100
+ server.closeAllConnections?.();
101
+ await new Promise(done => server.close(done));
102
+ },
103
+ })));
104
+ }
105
+
106
+ module.exports = { createObservationProxy };
@@ -0,0 +1,205 @@
1
+ 'use strict';
2
+ // P-LOG-SAFETY-R2 lane-exclusive observation proxy (prefix
3
+ // parallel-logging-fix-r2-*). Distinct from the R1 proxy (read-only reuse of
4
+ // that file is not extended): this one adds a SNAPSHOT BARRIER for the
5
+ // deterministic LOG-004 handoff proof.
6
+ //
7
+ // Forwarding rules (only real traffic is delayed/released, never rewritten):
8
+ // - HTTP: forwarded to the isolated server with host rewritten. GET
9
+ // /api/logs?... (the diagnostics snapshot) can be held AFTER the server
10
+ // produced the response: the exact original bytes are released later.
11
+ // - WebSocket /api/live: raw upgrade passthrough (host/origin rewritten to
12
+ // the target) with text-frame TAPPING in both directions so the wire
13
+ // timeline (subscribe frames with streamCursor, gateway event frames) is
14
+ // auditable evidence.
15
+
16
+ const http = require('node:http');
17
+ const crypto = require('node:crypto');
18
+
19
+ function createGapProxy(options) {
20
+ const { targetPort, port } = options;
21
+ const targetHost = '127.0.0.1';
22
+ const events = [];
23
+ let seq = 0;
24
+ const record = (kind, detail = {}) => {
25
+ events.push({ seq: ++seq, at: new Date().toISOString(), kind, ...detail });
26
+ };
27
+
28
+ let barrierArmed = false;
29
+ let barrierResolve = null;
30
+ const barrierHeld = new Promise(resolve => { barrierResolve = resolve; });
31
+ let barrierInfo = null;
32
+ let releaseResolve = null;
33
+ const released = new Promise(resolve => { releaseResolve = resolve; });
34
+
35
+ const sockets = new Set();
36
+ const server = http.createServer((req, res) => {
37
+ const url = req.url || '/';
38
+ const isSnapshot = req.method === 'GET' && url.startsWith('/api/logs?');
39
+ const held = barrierArmed && isSnapshot;
40
+ if (held) barrierArmed = false;
41
+ record('request', { method: req.method, url, held });
42
+
43
+ const upstream = http.request({
44
+ host: targetHost,
45
+ port: targetPort,
46
+ method: req.method,
47
+ path: url,
48
+ headers: { ...req.headers, host: `${targetHost}:${targetPort}` },
49
+ agent: false,
50
+ }, upstreamRes => {
51
+ const chunks = [];
52
+ upstreamRes.on('data', chunk => chunks.push(chunk));
53
+ upstreamRes.on('end', () => {
54
+ const body = Buffer.concat(chunks);
55
+ const deliver = () => {
56
+ const headers = { ...upstreamRes.headers };
57
+ delete headers['transfer-encoding'];
58
+ delete headers['content-length'];
59
+ headers['content-length'] = String(body.length);
60
+ res.writeHead(upstreamRes.statusCode, headers);
61
+ res.end(body);
62
+ record('response', { method: req.method, url, status: upstreamRes.statusCode, byteLength: body.length, releasedFromBarrier: held });
63
+ };
64
+ if (!held) return deliver();
65
+ barrierInfo = {
66
+ url,
67
+ status: upstreamRes.statusCode,
68
+ byteLength: body.length,
69
+ sha256: crypto.createHash('sha256').update(body).digest('hex'),
70
+ heldAt: new Date().toISOString(),
71
+ };
72
+ try {
73
+ const parsed = JSON.parse(body.toString('utf8'));
74
+ barrierInfo.entries = (parsed.entries || []).length;
75
+ barrierInfo.newestEntryId = (parsed.entries || [])[0]?.id || null;
76
+ barrierInfo.streamCursor = parsed.streamCursor || '';
77
+ barrierInfo.health = parsed.health || null;
78
+ } catch (error) { barrierInfo.parseError = String(error.message); }
79
+ record('barrier-held', { ...barrierInfo });
80
+ barrierResolve(barrierInfo);
81
+ released.then(() => {
82
+ record('barrier-released', { url, at: new Date().toISOString() });
83
+ deliver();
84
+ });
85
+ });
86
+ });
87
+ upstream.on('error', error => {
88
+ record('upstream-error', { url, error: String(error.message) });
89
+ if (!res.headersSent) res.writeHead(502, { 'content-type': 'application/json' });
90
+ res.end(JSON.stringify({ error: 'proxy upstream error' }));
91
+ });
92
+ req.pipe(upstream);
93
+ });
94
+
95
+ // Minimal RFC6455 text-frame tap (no negotiation of compression happens:
96
+ // the gateway's ws server runs with default options).
97
+ const makeFrameTap = direction => {
98
+ let tapBuffer = Buffer.alloc(0);
99
+ return chunk => {
100
+ const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
101
+ tapBuffer = Buffer.concat([tapBuffer, buf]);
102
+ for (;;) {
103
+ if (tapBuffer.length < 2) break;
104
+ const second = tapBuffer[1];
105
+ const opcode = tapBuffer[0] & 0x0f;
106
+ const masked = (second & 0x80) !== 0;
107
+ let len = second & 0x7f;
108
+ let offset = 2;
109
+ if (len === 126) { if (tapBuffer.length < offset + 2) break; len = tapBuffer.readUInt16BE(offset); offset += 2; }
110
+ else if (len === 127) { if (tapBuffer.length < offset + 8) break; len = Number(tapBuffer.readBigUInt64BE(offset)); offset += 8; }
111
+ let maskKey = null;
112
+ if (masked) { if (tapBuffer.length < offset + 4) break; maskKey = tapBuffer.subarray(offset, offset + 4); offset += 4; }
113
+ if (tapBuffer.length < offset + len) break;
114
+ let payload = tapBuffer.subarray(offset, offset + len);
115
+ if (masked) { const out = Buffer.alloc(len); for (let i = 0; i < len; i++) out[i] = payload[i] ^ maskKey[i % 4]; payload = out; }
116
+ tapBuffer = tapBuffer.subarray(offset + len);
117
+ if (opcode === 0x1) {
118
+ const text = payload.toString('utf8');
119
+ const detail = { direction, length: len };
120
+ try {
121
+ const message = JSON.parse(text);
122
+ detail.type = message.type || null;
123
+ if (message.type === 'subscribe' || message.type === 'unsubscribe') { detail.id = message.id; detail.route = String(message.route || '').slice(0, 400); }
124
+ if (message.type === 'event') { detail.eventId = message.id; detail.event = message.event; detail.cursor = message.cursor || null; detail.dataSnippet = String(message.data || '').slice(0, 1200); }
125
+ if (message.type === 'open' || message.type === 'error') detail.id = message.id;
126
+ } catch { detail.textSnippet = text.slice(0, 200); }
127
+ record('ws-frame', detail);
128
+ }
129
+ }
130
+ };
131
+ };
132
+
133
+ server.on('upgrade', (req, socket, head) => {
134
+ record('upgrade', { url: req.url || '/', origin: req.headers.origin || null });
135
+ const upstream = http.request({
136
+ host: targetHost,
137
+ port: targetPort,
138
+ method: 'GET',
139
+ path: req.url,
140
+ headers: {
141
+ ...req.headers,
142
+ host: `${targetHost}:${targetPort}`,
143
+ origin: `http://${targetHost}:${targetPort}`,
144
+ },
145
+ agent: false,
146
+ });
147
+ upstream.on('upgrade', (upstreamRes, upstreamSocket, upstreamHead) => {
148
+ socket.write(
149
+ `HTTP/1.1 101 Switching Protocols\r\n` +
150
+ `Upgrade: websocket\r\n` +
151
+ `Connection: Upgrade\r\n` +
152
+ `Sec-WebSocket-Accept: ${upstreamRes.headers['sec-websocket-accept']}\r\n` +
153
+ (upstreamRes.headers['sec-websocket-protocol'] ? `Sec-WebSocket-Protocol: ${upstreamRes.headers['sec-websocket-protocol']}\r\n` : '') +
154
+ `\r\n`
155
+ );
156
+ if (upstreamHead?.length) socket.write(upstreamHead);
157
+ if (head?.length) upstreamSocket.write(head);
158
+ const clientTap = makeFrameTap('client->server');
159
+ const serverTap = makeFrameTap('server->client');
160
+ socket.on('data', chunk => { clientTap(chunk); upstreamSocket.write(chunk); });
161
+ upstreamSocket.on('data', chunk => { serverTap(chunk); socket.write(chunk); });
162
+ const kill = () => { try { socket.destroy(); } catch {} try { upstreamSocket.destroy(); } catch {} sockets.delete(socket); sockets.delete(upstreamSocket); };
163
+ socket.on('error', kill); upstreamSocket.on('error', kill);
164
+ socket.on('close', kill); upstreamSocket.on('close', kill);
165
+ sockets.add(socket); sockets.add(upstreamSocket);
166
+ });
167
+ upstream.on('error', error => {
168
+ record('upgrade-error', { error: String(error.message) });
169
+ socket.destroy();
170
+ });
171
+ upstream.end();
172
+ });
173
+
174
+ return new Promise((resolve, reject) => {
175
+ server.once('error', reject);
176
+ server.listen(port, '127.0.0.1', () => resolve({
177
+ url: `http://127.0.0.1:${server.address().port}`,
178
+ events,
179
+ armSnapshotBarrier() { barrierArmed = true; record('barrier-armed', {}); },
180
+ async waitBarrier(timeoutMs = 10000) {
181
+ const timer = new Promise((_, reject) => setTimeout(() => reject(new Error('barrier timeout')), timeoutMs));
182
+ return Promise.race([barrierHeld, timer]);
183
+ },
184
+ releaseBarrier() { record('barrier-release-command', {}); releaseResolve(); },
185
+ get barrierInfo() { return barrierInfo; },
186
+ close() {
187
+ return new Promise(resolve => {
188
+ for (const socket of sockets) { try { socket.destroy(); } catch {} }
189
+ server.close(() => resolve());
190
+ setTimeout(resolve, 2000);
191
+ server.closeAllConnections?.();
192
+ });
193
+ },
194
+ }));
195
+ });
196
+ }
197
+
198
+ function decodeStreamCursor(value) {
199
+ if (!value) return null;
200
+ try {
201
+ return JSON.parse(Buffer.from(String(value), 'base64url').toString('utf8'));
202
+ } catch { return null; }
203
+ }
204
+
205
+ module.exports = { createGapProxy, decodeStreamCursor };