@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.
- package/.agents/plugins/marketplace.json +20 -20
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +1361 -1346
- package/LICENSE +201 -201
- package/NOTICE +5 -5
- package/README.md +349 -349
- package/_modules/knowledge-engine/package.json +1 -1
- package/_modules/knowledge-engine/src/coordinator.js +48 -3
- package/_modules/knowledge-engine/src/runtime/automation-config.js +140 -140
- package/_modules/knowledge-engine/src/runtime/commit-conversation-binder.js +238 -238
- package/_modules/knowledge-engine/src/runtime/commit-processing-ledger.js +26 -26
- package/_modules/knowledge-engine/src/runtime/commit-prompt.js +193 -193
- package/_modules/knowledge-engine/src/runtime/commit-reconciler.js +394 -394
- package/_modules/knowledge-engine/src/runtime/contracts.js +282 -282
- package/_modules/knowledge-engine/src/runtime/conversation-store.js +389 -389
- package/_modules/knowledge-engine/src/runtime/evidence-bundle.js +228 -228
- package/_modules/knowledge-engine/src/runtime/git-runner.js +62 -62
- package/_modules/knowledge-engine/src/runtime/knowledge-promotion.js +505 -505
- package/_modules/knowledge-engine/src/runtime/knowledge-retrieval-service.js +400 -400
- package/_modules/knowledge-engine/src/runtime/markdown-knowledge-indexer.js +171 -171
- package/_modules/knowledge-engine/src/runtime/scanner.js +298 -298
- package/_modules/knowledge-engine/ui/app.js +23 -2
- package/_site/README.md +30 -30
- package/_site/_test/agent-capture-mcp-ui-test.js +68 -68
- package/_site/_test/agent-capture-ui-test.js +101 -101
- package/_site/_test/ai-profile-resolver-test.js +137 -137
- package/_site/_test/ai-profile-test.js +75 -75
- package/_site/_test/ai-vendor-presets-test.js +91 -91
- package/_site/_test/automatic-context-test.js +70 -70
- package/_site/_test/automation-queue-test.js +14 -14
- package/_site/_test/automation-ui-test.js +40 -40
- package/_site/_test/background-task-registry-test.js +43 -43
- package/_site/_test/baseline-schema-test.js +91 -91
- package/_site/_test/bin-cli-test.js +230 -230
- package/_site/_test/bridge-adapter-test.js +95 -95
- package/_site/_test/bridge-consumer-service-test.js +310 -310
- package/_site/_test/candidate-bin-content-test.js +35 -35
- package/_site/_test/candidate-product-assets-test.js +13 -13
- package/_site/_test/candidate-release-contract-test.js +14 -14
- package/_site/_test/capture-hook-dedup-test.js +158 -158
- package/_site/_test/cdp-browser-failure-test.js +103 -103
- package/_site/_test/chat-claudecodeui-match-test.js +92 -92
- package/_site/_test/claude-executable-discovery-test.js +90 -90
- package/_site/_test/claude-session-lifecycle-sweep-test.js +252 -252
- package/_site/_test/claude-workbench-test.js +174 -174
- package/_site/_test/cli-instance-management-test.js +62 -62
- package/_site/_test/client-capture-restart-e2e-test.js +189 -189
- package/_site/_test/codex-conversation-projection-test.js +60 -60
- package/_site/_test/codex-marketplace-scoped-path-test.js +186 -186
- package/_site/_test/codex-notify-capture-e2e-test.js +101 -101
- package/_site/_test/codex-notify-fanout-test.js +141 -134
- package/_site/_test/codex-notify-upgrade-test.js +253 -253
- package/_site/_test/commit-boundary-freeze-test.js +147 -147
- package/_site/_test/commit-conversation-binding-test.js +99 -99
- package/_site/_test/commit-evidence-test.js +110 -110
- package/_site/_test/configuration-generation-test.js +22 -22
- package/_site/_test/context-pack-test.js +200 -200
- package/_site/_test/conversation-api-test.js +119 -119
- package/_site/_test/conversation-origin-test.js +15 -15
- package/_site/_test/conversation-store-test.js +138 -138
- package/_site/_test/cross-repo-isolation-e2e-test.js +187 -187
- package/_site/_test/desktop-browser-compat-test.js +28 -28
- package/_site/_test/desktop-hook-runtime-regression-test.js +204 -204
- package/_site/_test/embedding-config-test.js +32 -32
- package/_site/_test/explicit-commit-processor-test.js +37 -37
- package/_site/_test/fixtures/fake-browser-exit.js +10 -10
- package/_site/_test/fixtures/fake-browser-hang.js +10 -10
- package/_site/_test/fixtures/integration-busy-worker.cjs +6 -6
- package/_site/_test/fixtures/make-git-repos.js +92 -92
- package/_site/_test/fixtures/retrieval-gold-queries.json +32 -32
- package/_site/_test/folder-picker-output-test.js +19 -19
- package/_site/_test/fresh-hook-reimport-test.js +53 -53
- package/_site/_test/fresh-product-data-test.js +56 -56
- package/_site/_test/full-integration-e2e-test.js +167 -167
- package/_site/_test/git-path-compatibility-test.js +80 -80
- package/_site/_test/git-validation-test.js +81 -81
- package/_site/_test/glm-ui-refresh-review-test.js +313 -404
- package/_site/_test/harness-isolation-test.js +138 -0
- package/_site/_test/helpers/cdp-browser.js +383 -0
- package/_site/_test/helpers/cdp-network-capture.js +66 -0
- package/_site/_test/helpers/find-chrome.js +34 -0
- package/_site/_test/helpers/free-port.js +8 -0
- package/_site/_test/helpers/logging-ui-fixture.js +131 -0
- package/_site/_test/helpers/module-fixture.js +282 -0
- package/_site/_test/helpers/parallel-engine-advanced-engine-child.js +45 -0
- package/_site/_test/helpers/parallel-engine-advanced-fault-writer.js +171 -0
- package/_site/_test/helpers/parallel-engine-advanced-harness.js +245 -0
- package/_site/_test/helpers/parallel-engine-advanced-standin-analyzer.js +80 -0
- package/_site/_test/helpers/parallel-engine-advanced-ui-host.js +177 -0
- package/_site/_test/helpers/parallel-engine-final-harness.js +314 -0
- package/_site/_test/helpers/parallel-engine-final-standin-analyzer.js +159 -0
- package/_site/_test/helpers/parallel-knowledge-fixture.js +255 -0
- package/_site/_test/helpers/parallel-logging-acceptance-fixture.js +111 -0
- package/_site/_test/helpers/parallel-logging-acceptance-proxy.js +106 -0
- package/_site/_test/helpers/parallel-logging-fix-r2-proxy.js +205 -0
- package/_site/_test/helpers/parallel-logging-integration-5025-env.js +432 -0
- package/_site/_test/helpers/parallel-memory-analyzer.js +81 -0
- package/_site/_test/helpers/parallel-memory-event-bridge.js +128 -0
- package/_site/_test/helpers/parallel-memory-shell.js +470 -0
- package/_site/_test/helpers/parallel-nav-isolation-env.js +389 -0
- package/_site/_test/helpers/parallel-nav-module-ui.js +186 -0
- package/_site/_test/helpers/parallel-nav-proxy.js +148 -0
- package/_site/_test/helpers/parallel-nav-shell.js +361 -0
- package/_site/_test/helpers/parallel-qa-engine-endpoint-guard.js +206 -0
- package/_site/_test/helpers/parallel-qa-engine-endpoint-isolate.js +257 -0
- package/_site/_test/helpers/parallel-retrieval-followup-corpus.js +293 -0
- package/_site/_test/helpers/parallel-retrieval-followup-next-session.js +70 -0
- package/_site/_test/helpers/parallel-retrieval-followup-r2-shell.js +737 -0
- package/_site/_test/helpers/parallel-retrieval-followup-r2-wb-host.js +82 -0
- package/_site/_test/helpers/parallel-retrieval-followup-shell.js +279 -0
- package/_site/_test/helpers/parallel-workbench-fixture.js +371 -0
- package/_site/_test/helpers/parallel-workbench-host.js +80 -0
- package/_site/_test/helpers/parallel-workbench-integration-5025-fixture.js +408 -0
- package/_site/_test/helpers/parallel-workbench-integration-5025-harness.js +593 -0
- package/_site/_test/helpers/parallel-workbench-integration-5025-host.js +70 -0
- package/_site/_test/helpers/parallel-workbench-integration-5025-launcher.js +240 -0
- package/_site/_test/helpers/parallel-workbench-integration-5025-runtime-harness.js +630 -0
- package/_site/_test/helpers/parallel-workbench-source-map.js +196 -0
- package/_site/_test/helpers/spawn-server.js +106 -0
- package/_site/_test/helpers/version-visibility-fixture.js +251 -0
- package/_site/_test/hook-runtime-endpoint-test.js +88 -88
- package/_site/_test/hook-status-repair-api-test.js +152 -152
- package/_site/_test/hook-trigger-test.js +85 -85
- package/_site/_test/hook-worktree-test.js +42 -42
- package/_site/_test/import-preflight-api-test.js +234 -234
- package/_site/_test/index-builder-test.js +41 -41
- package/_site/_test/index-error-display-contract-test.js +10 -10
- package/_site/_test/index-writer-concurrency-test.js +135 -135
- package/_site/_test/integration-adapters-test.js +262 -262
- package/_site/_test/integration-auto-repair-test.js +113 -113
- package/_site/_test/integration-capture-setup-test.js +118 -118
- package/_site/_test/integration-intent-test.js +81 -81
- package/_site/_test/integration-runtime-binding-a2-test.js +566 -566
- package/_site/_test/integration-runtime-binding-a3-test.js +227 -227
- package/_site/_test/integration-runtime-binding-test.js +473 -473
- package/_site/_test/integration-status-scan-test.js +29 -29
- package/_site/_test/integration-surface-coverage-test.js +164 -164
- package/_site/_test/kb-framework-test.js +57 -57
- package/_site/_test/kbpath-follow-test.js +63 -63
- package/_site/_test/knowledge-db-test.js +144 -144
- package/_site/_test/knowledge-engine-client-test.js +120 -109
- package/_site/_test/knowledge-language-control-test.js +170 -170
- package/_site/_test/knowledge-maintenance-test.js +40 -40
- package/_site/_test/knowledge-promotion-recovery-test.js +260 -260
- package/_site/_test/knowledge-query-test.js +54 -54
- package/_site/_test/knowledge-retrieval-service-test.js +77 -77
- package/_site/_test/knowledge-scopes-test.js +37 -37
- package/_site/_test/knowledge-storage-location-test.js +28 -28
- package/_site/_test/knowledge-storage-startup-test.js +52 -52
- package/_site/_test/knowledge-store-logs-supervision-test.js +89 -89
- package/_site/_test/launcher-readiness-test.js +80 -80
- package/_site/_test/legacy-claude-session-alias-test.js +106 -106
- package/_site/_test/legacy-routes-removed-test.js +47 -47
- package/_site/_test/legacy-workbench-exclusion-test.js +92 -92
- package/_site/_test/live-gateway-test.js +48 -48
- package/_site/_test/local-marketplace-test.js +16 -16
- package/_site/_test/log-redaction-test.js +58 -58
- package/_site/_test/logging-api-test.js +89 -89
- package/_site/_test/logging-sse-no-gap-test.js +88 -88
- package/_site/_test/logging-ui-test.js +107 -107
- package/_site/_test/markdown-delta-overlay-test.js +85 -85
- package/_site/_test/markdown-maintenance-api-test.js +75 -75
- package/_site/_test/markdown-maintenance-test.js +11 -11
- package/_site/_test/mcp-server-test.js +149 -149
- package/_site/_test/model-context-windows-test.js +89 -89
- package/_site/_test/module-agent-start-proxy-test.js +65 -65
- package/_site/_test/module-artifact-integrity-test.js +27 -27
- package/_site/_test/module-boundary-test.js +30 -30
- package/_site/_test/module-bridge-eventbridge-test.js +202 -202
- package/_site/_test/module-dynamic-port-test.js +118 -118
- package/_site/_test/module-index-proxy-test.js +33 -33
- package/_site/_test/module-model-configuration-test.js +44 -44
- package/_site/_test/module-process-lifecycle-test.js +35 -35
- package/_site/_test/module-stream-proxy-test.js +34 -34
- package/_site/_test/module-terminal-restart-test.js +63 -63
- package/_site/_test/module-wiring-unbind-race-test.js +59 -59
- package/_site/_test/native-context-integrations-test.js +86 -86
- package/_site/_test/non-release-ci-test.js +34 -34
- package/_site/_test/offline-boundary-isolation-test.js +32 -32
- package/_site/_test/orphaned-claim-recovery-test.js +80 -80
- package/_site/_test/package-boundary-test.js +43 -43
- package/_site/_test/package-startup-test.js +56 -56
- package/_site/_test/packaged-ui-smoke-test.js +88 -88
- package/_site/_test/parallel-ci-r9-posix-socket-budget-test.js +156 -0
- package/_site/_test/parallel-engine-advanced-eng005-test.js +136 -0
- package/_site/_test/parallel-engine-advanced-eng006-test.js +171 -0
- package/_site/_test/parallel-engine-advanced-eng007-reconcile-test.js +231 -0
- package/_site/_test/parallel-engine-advanced-eng009-test.js +208 -0
- package/_site/_test/parallel-engine-advanced-eng010-test.js +213 -0
- package/_site/_test/parallel-engine-advanced-eng011-ui-probe.js +459 -0
- package/_site/_test/parallel-engine-final-eng002-test.js +255 -0
- package/_site/_test/parallel-engine-final-eng007-test.js +269 -0
- package/_site/_test/parallel-engine-final-eng011-test.js +197 -0
- package/_site/_test/parallel-engine-final-eng012-test.js +354 -0
- package/_site/_test/parallel-knowledge-client-error-code-test.js +184 -0
- package/_site/_test/parallel-knowledge-engine-test.js +353 -0
- package/_site/_test/parallel-knowledge-retrieval-test.js +292 -0
- package/_site/_test/parallel-knowledge-vector-test.js +168 -0
- package/_site/_test/parallel-linux-ci-r7-log007-day-test.js +110 -0
- package/_site/_test/parallel-logging-acceptance-log003-test.js +224 -0
- package/_site/_test/parallel-logging-acceptance-log004-test.js +231 -0
- package/_site/_test/parallel-logging-acceptance-log005-test.js +208 -0
- package/_site/_test/parallel-logging-acceptance-log006-test.js +214 -0
- package/_site/_test/parallel-logging-acceptance-log007-test.js +223 -0
- package/_site/_test/parallel-logging-acceptance-log008-test.js +180 -0
- package/_site/_test/parallel-logging-fix-r2-crash-probe.js +271 -0
- package/_site/_test/parallel-logging-fix-r2-gap-test.js +294 -0
- package/_site/_test/parallel-logging-fix-r2-normalhook-test.js +330 -0
- package/_site/_test/parallel-logging-integration-5025-portability-test.js +554 -0
- package/_site/_test/parallel-memory-candidate-test.js +301 -0
- package/_site/_test/parallel-memory-manual-test.js +197 -0
- package/_site/_test/parallel-memory-scope-test.js +228 -0
- package/_site/_test/parallel-memory-source-test.js +227 -0
- package/_site/_test/parallel-memory-sync-test.js +238 -0
- package/_site/_test/parallel-memory-view-r8-reload-gate-test.js +123 -0
- package/_site/_test/parallel-nav-failure-test.js +119 -0
- package/_site/_test/parallel-nav-isolation-test.js +308 -0
- package/_site/_test/parallel-nav-isolation-vector-path-test.js +90 -0
- package/_site/_test/parallel-nav-offline-test.js +272 -0
- package/_site/_test/parallel-nav-operability-test.js +257 -0
- package/_site/_test/parallel-nav-switch-test.js +247 -0
- package/_site/_test/parallel-nav-theme-test.js +130 -0
- package/_site/_test/parallel-nav-viewport-test.js +310 -0
- package/_site/_test/parallel-qa-engine-endpoint-guard-test.js +273 -0
- package/_site/_test/parallel-retrieval-followup-citation-test.js +219 -0
- package/_site/_test/parallel-retrieval-followup-corpus-test.js +287 -0
- package/_site/_test/parallel-workbench-independent-host.js +262 -0
- package/_site/_test/parallel-workbench-integration-5025-abort-probe.js +340 -0
- package/_site/_test/parallel-workbench-integration-5025-drafts-probe.js +588 -0
- package/_site/_test/parallel-workbench-integration-5025-env-check.js +317 -0
- package/_site/_test/parallel-workbench-integration-5025-model-probe.js +342 -0
- package/_site/_test/parallel-workbench-integration-5025-ordering-probe.js +326 -0
- package/_site/_test/parallel-workbench-model-probe.js +336 -0
- package/_site/_test/parallel-workbench-offline-test.js +169 -0
- package/_site/_test/parallel-workbench-provenance-trace.js +134 -0
- package/_site/_test/parallel-workbench-source-match.js +206 -0
- package/_site/_test/parallel-workbench-standalone-turn.js +349 -0
- package/_site/_test/path-consistency-test.js +151 -151
- package/_site/_test/pending-sweep-test.js +9 -9
- package/_site/_test/post-commit-automation-test.js +87 -87
- package/_site/_test/pr-consumer-contract-test.js +55 -55
- package/_site/_test/product-brand-contract-test.js +25 -25
- package/_site/_test/product-diagnostics-ui-test.js +93 -93
- package/_site/_test/product-empty-state-test.js +148 -0
- package/_site/_test/product-import-ui-test.js +219 -104
- package/_site/_test/product-navigation-test.js +152 -0
- package/_site/_test/product-progress-status-test.js +84 -84
- package/_site/_test/project-delete-recovery-test.js +64 -64
- package/_site/_test/project-domain-create-test.js +91 -0
- package/_site/_test/project-domain-scope-retrieval-test.js +214 -0
- package/_site/_test/project-git-init-confirm-test.js +115 -0
- package/_site/_test/project-goal-editor-test.js +144 -144
- package/_site/_test/project-import-duplicate-alias-test.js +134 -0
- package/_site/_test/project-import-unicode-restart-test.js +140 -0
- package/_site/_test/project-lifecycle-transaction-test.js +106 -106
- package/_site/_test/project-module-partial-recovery-test.js +222 -0
- package/_site/_test/project-remove-running-guard-test.js +79 -79
- package/_site/_test/project-store-test.js +90 -90
- package/_site/_test/project-unbind-late-read-ui-test.js +250 -0
- package/_site/_test/project-unbind-writer-guard-test.js +231 -0
- package/_site/_test/prompt-settings-test.js +115 -115
- package/_site/_test/protected-architecture-gate-test.js +122 -122
- package/_site/_test/refactor-characterization-test.js +36 -36
- package/_site/_test/registry-events-test.js +38 -38
- package/_site/_test/release-version-sync-test.js +115 -115
- package/_site/_test/requirement-binding-test.js +113 -113
- package/_site/_test/requirement-recorder-test.js +173 -173
- package/_site/_test/run-all-tests.js +173 -161
- package/_site/_test/runtime-endpoint-test.js +64 -64
- package/_site/_test/scanner-test.js +83 -83
- package/_site/_test/server-security-test.js +96 -96
- package/_site/_test/sessions-stream-test.js +93 -93
- package/_site/_test/shared-contracts-test.js +58 -58
- package/_site/_test/shared-scope-root-binding-test.js +74 -67
- package/_site/_test/simple-import-test.js +98 -98
- package/_site/_test/startup-analysis-disabled-test.js +16 -16
- package/_site/_test/storage-foundation-test.js +78 -78
- package/_site/_test/structured-logger-test.js +101 -101
- package/_site/_test/t07b-cln-005-coverage-test.js +212 -0
- package/_site/_test/team-knowledge-store-sparse-checkout-test.js +196 -196
- package/_site/_test/team-knowledge-store-test.js +132 -132
- package/_site/_test/test-server-cleanup-test.js +35 -35
- package/_site/_test/test-server-module-isolation-test.js +37 -37
- package/_site/_test/tracking-start-test.js +144 -144
- package/_site/_test/vector-storage-root-test.js +75 -75
- package/_site/_test/version-visibility-browser-probe.js +249 -0
- package/_site/_test/version-visibility-test.js +92 -0
- package/_site/_test/workbench-context-receipts-test.js +22 -22
- package/_site/_test/workbench-permission-test.js +117 -117
- package/_site/_test/zcode-capture-integration-test.js +112 -112
- package/_site/lib/ai-profile-resolver.js +78 -78
- package/_site/lib/ai-vendor-presets.js +83 -83
- package/_site/lib/ai-workspace.js +104 -104
- package/_site/lib/atomic-file.js +204 -204
- package/_site/lib/automation-config.js +140 -140
- package/_site/lib/bridge-adapter.js +260 -260
- package/_site/lib/bridge-consumer-service.js +402 -402
- package/_site/lib/capture-hook-config.js +108 -108
- package/_site/lib/claude-cli-runner.js +1599 -1599
- package/_site/lib/cli-instances.js +43 -43
- package/_site/lib/codex-context-status.js +17 -17
- package/_site/lib/codex-notify-config.js +193 -193
- package/_site/lib/commit-conversation-binder.js +247 -247
- package/_site/lib/commit-processing-ledger.js +27 -27
- package/_site/lib/commit-prompt.js +202 -202
- package/_site/lib/commit-reconciler.js +444 -444
- package/_site/lib/context-pack-builder.js +299 -299
- package/_site/lib/contracts.js +291 -291
- package/_site/lib/conversation-exclusions.js +204 -204
- package/_site/lib/conversation-query-service.js +161 -161
- package/_site/lib/conversation-store.js +387 -387
- package/_site/lib/data-dir.js +13 -13
- package/_site/lib/embedding-service.js +85 -85
- package/_site/lib/engine-connection.js +93 -93
- package/_site/lib/engine-knowledge-client.js +76 -72
- package/_site/lib/evidence-bundle.js +228 -228
- package/_site/lib/folder-picker-output.js +24 -24
- package/_site/lib/git-path.js +14 -14
- package/_site/lib/git-runner.js +62 -62
- package/_site/lib/github-team-store.js +1175 -1175
- package/_site/lib/hook-manager.js +203 -203
- package/_site/lib/index-builder.js +157 -157
- package/_site/lib/index-service.js +173 -173
- package/_site/lib/integration-auto-repair.js +77 -77
- package/_site/lib/integration-installer.js +1240 -1240
- package/_site/lib/integration-intent.js +88 -88
- package/_site/lib/integration-status-scan.js +99 -99
- package/_site/lib/integration-status-worker.js +14 -14
- package/_site/lib/kb-framework.js +191 -191
- package/_site/lib/kb-validator.js +124 -124
- package/_site/lib/knowledge-db.js +363 -363
- package/_site/lib/knowledge-promotion.js +435 -435
- package/_site/lib/knowledge-retrieval-service.js +401 -401
- package/_site/lib/knowledge-schema.js +121 -121
- package/_site/lib/knowledge-tool-runtime.js +366 -366
- package/_site/lib/live-gateway.js +78 -78
- package/_site/lib/llm-client.js +173 -173
- package/_site/lib/markdown-knowledge-indexer.js +230 -230
- package/_site/lib/model-context-windows.js +82 -82
- package/_site/lib/module-bridge.js +874 -874
- package/_site/lib/module-port-resolver.js +57 -57
- package/_site/lib/post-commit-automation.js +141 -141
- package/_site/lib/product-data.js +35 -35
- package/_site/lib/project-lifecycle-service.js +554 -554
- package/_site/lib/project-registry-store.js +129 -129
- package/_site/lib/project-store.js +268 -268
- package/_site/lib/prompt-registry.js +148 -148
- package/_site/lib/public-version-info.js +37 -0
- package/_site/lib/registry-events.js +16 -16
- package/_site/lib/requirement-adapters.js +10 -10
- package/_site/lib/requirement-binder.js +112 -112
- package/_site/lib/requirement-recorder.js +278 -278
- package/_site/lib/runtime-control.js +89 -89
- package/_site/lib/runtime-endpoint.js +160 -160
- package/_site/lib/scanner.js +298 -298
- package/_site/lib/server-app.js +1672 -1664
- package/_site/lib/settings-store.js +153 -153
- package/_site/lib/storage-layout.js +166 -166
- package/_site/lib/structured-logger.js +574 -574
- package/_site/lib/zcode-context-integration.js +60 -60
- package/_site/scripts/codex-notify-fanout.cjs +95 -95
- package/_site/scripts/folder-picker.ps1 +156 -156
- package/_site/scripts/hook-trigger.js +158 -158
- package/_site/scripts/install-module-candidates.js +69 -69
- package/_site/scripts/pack-module-candidates.js +89 -89
- package/_site/scripts/sync-product-content.js +28 -28
- package/_site/scripts/sync-release-version.js +164 -164
- package/_site/scripts/vendor-modules.js +49 -49
- package/_site/scripts/verify-module-runtime.js +50 -50
- package/_site/scripts/verify-product-runtime.js +36 -36
- package/_site/scripts/verify-retrieval-gold.js +83 -83
- package/_site/server.js +15 -15
- package/_site/start.bat +26 -26
- package/_site/stop.bat +11 -11
- package/bin/project-knowledge-integrations.js +254 -254
- package/bin/project-knowledge-kb.js +67 -67
- package/bin/project-knowledge-mcp.js +201 -201
- package/bin/project-knowledge.js +269 -170
- package/claude-prompts.json +21 -21
- package/docs/README.zh-CN.md +207 -205
- package/docs/assets/architecture.svg +181 -181
- package/docs/assets/logo.svg +49 -49
- package/docs/fresh-v5-start.md +44 -44
- package/docs/leader-glm-workbench-playbook.md +122 -122
- package/docs/project-registry-schema.md +33 -33
- package/docs/release-5.0.16-acceptance.md +176 -176
- package/docs/release-5.0.17-acceptance.md +117 -117
- package/docs/release-5.0.19-acceptance.md +39 -39
- package/docs/team-knowledge-mode-a-plan.md +73 -73
- package/docs/testing-strategy.md +33 -33
- package/module-runtime-manifest.json +70 -67
- package/package.json +113 -110
- package/plugins/project-knowledge/.claude-plugin/plugin.json +1 -1
- package/plugins/project-knowledge/.codex-plugin/plugin.json +1 -1
- package/plugins/project-knowledge/.mcp.json +1 -1
- package/templates/change.md +30 -30
- package/templates/project-readme.md +52 -52
- package/ui/index.html +3 -2
- package/ui/version-info.css +44 -0
- package/ui/version-info.js +24 -0
- package/vendor-manifest.json +55 -55
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
// _site/_test/helpers/parallel-nav-isolation-env.js
|
|
2
|
+
//
|
|
3
|
+
// P-NAV-VECTOR-ISOLATION-R7: the driver-level environment whitelist for the
|
|
4
|
+
// NAV lane harness. The incident this exists for: parallel-nav-shell.js used
|
|
5
|
+
// to create the real engine WITHOUT an explicit vectorUrl, so the engine kept
|
|
6
|
+
// its constructor default (http://127.0.0.1:8787 = the developer's LIVE
|
|
7
|
+
// desktop vector-hub) and NAV-007's core-source-regression run wrote a real
|
|
8
|
+
// collection there (2026-10-23T18:00:54Z; recovered by
|
|
9
|
+
// P-VECTOR-INCIDENT-RECOVERY-R6). Driving full-env directory variables from a
|
|
10
|
+
// test is NOT enough to override constructor defaults, so the harness now
|
|
11
|
+
// (a) passes an explicit vectorUrl (see parallel-nav-shell.js) and (b) puts
|
|
12
|
+
// the whole driver — and every child that inherits process.env — under the
|
|
13
|
+
// explicit whitelist below.
|
|
14
|
+
//
|
|
15
|
+
// Contract (call order matters, enforced by the harness):
|
|
16
|
+
// 1. the harness resolves Chrome under the REAL environment first
|
|
17
|
+
// (read-only discovery);
|
|
18
|
+
// 2. enterNavIsolation() builds the whitelist environment and mutates
|
|
19
|
+
// process.env — BEFORE any engine/product module is loaded or any child
|
|
20
|
+
// is spawned;
|
|
21
|
+
// 3. restore() puts the original values back after cleanup, and the stable
|
|
22
|
+
// parent temp root SURVIVES serial harness runs in one process (only the
|
|
23
|
+
// process-exit hook removes it), so a later os.tmpdir() never points at
|
|
24
|
+
// a deleted directory.
|
|
25
|
+
//
|
|
26
|
+
// The whitelist keeps only OS-critical variables; every directory the product
|
|
27
|
+
// family consults (HOME/USERPROFILE/HOMEDRIVE+HOMEPATH/CODEX_HOME/
|
|
28
|
+
// CLAUDE_CONFIG_DIR/APPDATA/LOCALAPPDATA/TEMP/TMP/TMPDIR/KB_DATA_DIR/
|
|
29
|
+
// KB_KNOWLEDGE_DATA_DIR/AI_CODING_EVENT_BRIDGE_HOME/AGENT_TERMINAL_DATA_DIR/
|
|
30
|
+
// CLAUDE_AI_WORKBENCH_DATA_DIR/PSModuleAnalysisCachePath) points into one
|
|
31
|
+
// short repo-external fixture root, and the Chrome default-profile shape
|
|
32
|
+
// (<isolated USERPROFILE>/AppData/Local/Google/Chrome/User Data) is created
|
|
33
|
+
// so Chrome's own default paths resolve inside isolation. Ambient module
|
|
34
|
+
// URLs, model-provider credentials, proxy variables and CLI-injection
|
|
35
|
+
// variables are deleted; the harness adds its explicit lane endpoints only
|
|
36
|
+
// AFTER the whitelist is in place.
|
|
37
|
+
//
|
|
38
|
+
// P-NAV-POSIX-CI-R9: on POSIX the fixture path length is a hard correctness
|
|
39
|
+
// constraint, not cosmetics. Chrome places its SingletonSocket under TMPDIR
|
|
40
|
+
// (<root>/com.google.Chrome.<rand>/SingletonSocket) and the product's
|
|
41
|
+
// runtime-control channel lives at <dataDir>/runtime-control.sock
|
|
42
|
+
// (_site/lib/runtime-control.js) — both are UNIX domain sockets bound by the
|
|
43
|
+
// 108-byte sun_path limit. The descriptive R7 component names
|
|
44
|
+
// (pk-nav-r7-<pid>-<rand>/<full-tag>-<rand>) blew that budget on GH runners
|
|
45
|
+
// (CI 40011886791: a 120-char SingletonSocket aborted Chrome with SIGABRT
|
|
46
|
+
// and starved product bootstrap). POSIX now uses short components (pkn…/n…/t
|
|
47
|
+
// layout), keeps the full tag in an on-disk owner.json, prefers the external
|
|
48
|
+
// configured temp root when one is available, and fails fast when the
|
|
49
|
+
// worst-case socket path would exceed the budget. The real-home leak scan is
|
|
50
|
+
// also placement-aware: on hosted runners the owned temp root legitimately
|
|
51
|
+
// lives UNDER the real home (/home/runner/work/_temp), so a value is a leak
|
|
52
|
+
// only when it is outside the owned root while still referencing the real
|
|
53
|
+
// home (canonical containment), never merely because of a shared prefix.
|
|
54
|
+
|
|
55
|
+
'use strict';
|
|
56
|
+
|
|
57
|
+
const fs = require('node:fs');
|
|
58
|
+
const os = require('node:os');
|
|
59
|
+
const path = require('node:path');
|
|
60
|
+
|
|
61
|
+
// Captured at module load, before any isolation changes the environment.
|
|
62
|
+
const REAL_HOME = os.homedir();
|
|
63
|
+
const REAL_USERPROFILE = process.env.USERPROFILE || REAL_HOME;
|
|
64
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..', '..');
|
|
65
|
+
|
|
66
|
+
/** OS-critical variables a Windows child may keep. Nothing else survives. */
|
|
67
|
+
const OS_WHITELIST = [
|
|
68
|
+
'SystemRoot', 'SystemDrive', 'windir', 'ComSpec', 'PATHEXT', 'PATH',
|
|
69
|
+
'OS', 'NUMBER_OF_PROCESSORS',
|
|
70
|
+
'PROCESSOR_ARCHITECTURE', 'PROCESSOR_IDENTIFIER', 'PROCESSOR_LEVEL', 'PROCESSOR_REVISION',
|
|
71
|
+
'COMPUTERNAME', 'USERNAME', 'USERDOMAIN',
|
|
72
|
+
'PROGRAMDATA', 'ProgramFiles', 'ProgramFiles(x86)', 'ProgramW6432',
|
|
73
|
+
'CommonProgramFiles', 'CommonProgramFiles(x86)', 'CommonProgramW6432',
|
|
74
|
+
'DriverData',
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
/** Directory variables the isolation overrides; every one lands inside the fixture root. */
|
|
78
|
+
const ISOLATION_KEYS = [
|
|
79
|
+
'HOME', 'USERPROFILE', 'HOMEDRIVE', 'HOMEPATH',
|
|
80
|
+
'CODEX_HOME', 'CLAUDE_CONFIG_DIR', 'APPDATA', 'LOCALAPPDATA',
|
|
81
|
+
'TEMP', 'TMP', 'TMPDIR',
|
|
82
|
+
'KB_DATA_DIR', 'KB_KNOWLEDGE_DATA_DIR',
|
|
83
|
+
'AI_CODING_EVENT_BRIDGE_HOME', 'AGENT_TERMINAL_DATA_DIR', 'CLAUDE_AI_WORKBENCH_DATA_DIR',
|
|
84
|
+
'PSModuleAnalysisCachePath',
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
/** Deleted outright: ambient module URLs (incl. the engine's own env var), model-provider credentials, proxies, CLI injection. */
|
|
88
|
+
const AMBIENT_DELETES = [
|
|
89
|
+
'KB_KNOWLEDGE_ENGINE_URL', 'KB_VECTORHUB_URL', 'KB_TERMINAL_URL', 'KB_EVENTBRIDGE_URL',
|
|
90
|
+
'KB_EVENT_BRIDGE_URL', 'KB_VECTOR_HUB_URL', 'KB_WORKBENCH_RUNTIME_TOKEN', 'KB_MODULES_REGISTER',
|
|
91
|
+
'KB_CHROME_PATH',
|
|
92
|
+
'ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_BASE_URL', 'ANTHROPIC_MODEL',
|
|
93
|
+
'ANTHROPIC_DEFAULT_THINKING_MODEL', 'ANTHROPIC_DEFAULT_HAIKU_MODEL',
|
|
94
|
+
'ANTHROPIC_DEFAULT_SONNET_MODEL', 'ANTHROPIC_DEFAULT_OPUS_MODEL', 'API_TIMEOUT_MS',
|
|
95
|
+
'CLAUDE_CODE_OAUTH_TOKEN', 'CLAUDE_CODE_USE_BEDROCK', 'CLAUDE_CODE_USE_VERTEX', 'CLAUDE_CODE_USE_FOUNDRY',
|
|
96
|
+
'CLAUDE_CODE_ENTRYPOINT', 'CLAUDE_CODE_SSE_PORT', 'CLAUDECODE',
|
|
97
|
+
'MINIMAX_API_KEY', 'MINIMAX_BASE_URL', 'MINIMAX_MODEL',
|
|
98
|
+
'OPENAI_API_KEY', 'OPENAI_BASE_URL',
|
|
99
|
+
'NODE_OPTIONS', 'NODE_PATH', 'NODE_PRELOAD',
|
|
100
|
+
'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'NO_PROXY',
|
|
101
|
+
'http_proxy', 'https_proxy', 'all_proxy', 'no_proxy',
|
|
102
|
+
'PSModulePath',
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
function isInside(candidate, parent) {
|
|
106
|
+
const relative = path.relative(parent, candidate);
|
|
107
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Windows environment names arrive case-preserved (git-bash exports
|
|
111
|
+
// SYSTEMROOT while cmd exports SystemRoot); membership must not depend on
|
|
112
|
+
// the caller's casing.
|
|
113
|
+
const upper = value => String(value).toUpperCase();
|
|
114
|
+
const OS_WHITELIST_UPPER = () => new Set(OS_WHITELIST.map(upper));
|
|
115
|
+
const isOsWhitelisted = key => OS_WHITELIST_UPPER().has(upper(key));
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* A short repo-external temp root (same convention as the R5 lane). The
|
|
119
|
+
* fallback chain is resolved from values captured at MODULE LOAD — a poisoned
|
|
120
|
+
* or later-mutated process.env.TEMP must not steer the run root into the real
|
|
121
|
+
* home (the negative case proves exactly this).
|
|
122
|
+
*/
|
|
123
|
+
const LOADED_TEMP_ROOT = (process.env.PK_TEST_TEMP_ROOT || process.env.TEMP || process.env.TMP || os.tmpdir());
|
|
124
|
+
|
|
125
|
+
// R9 POSIX socket budget: sun_path is 108 bytes including the NUL terminator,
|
|
126
|
+
// so 107 characters is the hard ceiling. The worst-case consumer is Chrome's
|
|
127
|
+
// SingletonSocket under the isolated TMPDIR; the suffix rand is observed at
|
|
128
|
+
// ~6 chars but budgeted at 12, and the budget counts UTF-8 BYTES, not chars.
|
|
129
|
+
const POSIX_SOCKET_CHAR_LIMIT = 107;
|
|
130
|
+
const CHROME_RAND_MAX_CHARS = 12;
|
|
131
|
+
|
|
132
|
+
/** Worst-case POSIX socket paths the fixture root must be able to host. */
|
|
133
|
+
function worstCasePosixSocketPaths(root) {
|
|
134
|
+
const base = String(root).replaceAll('\\', '/').replace(/\/+$/, '');
|
|
135
|
+
return {
|
|
136
|
+
chromeSingletonSocket: `${base}/t/com.google.Chrome.${'x'.repeat(CHROME_RAND_MAX_CHARS)}/SingletonSocket`,
|
|
137
|
+
productRuntimeControlSock: `${base}/data/runtime-control.sock`,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function posixSocketBudgetVerdict(root, limit = POSIX_SOCKET_CHAR_LIMIT) {
|
|
142
|
+
const paths = worstCasePosixSocketPaths(root);
|
|
143
|
+
const chromeSocketBytes = Buffer.byteLength(paths.chromeSingletonSocket, 'utf8');
|
|
144
|
+
const controlSocketBytes = Buffer.byteLength(paths.productRuntimeControlSock, 'utf8');
|
|
145
|
+
return {
|
|
146
|
+
ok: chromeSocketBytes <= limit && controlSocketBytes <= limit,
|
|
147
|
+
limit, chromeSocketBytes, controlSocketBytes, paths,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Fail fast — with the computed path in the message — when the budget cannot hold. */
|
|
152
|
+
function assertPosixSocketBudget(root, what) {
|
|
153
|
+
const verdict = posixSocketBudgetVerdict(root);
|
|
154
|
+
if (!verdict.ok) {
|
|
155
|
+
throw new Error(
|
|
156
|
+
`POSIX socket path budget exceeded for ${what}: worst-case Chrome SingletonSocket is ${verdict.chromeSocketBytes} UTF-8 bytes, limit is ${verdict.limit} — ${verdict.paths.chromeSingletonSocket}`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
return verdict;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const IS_POSIX = process.platform !== 'win32';
|
|
163
|
+
|
|
164
|
+
function resolveNavTempRoot() {
|
|
165
|
+
const sharedRoot = path.join(path.dirname(REPO_ROOT), '.project-knowledge-test-temp');
|
|
166
|
+
// Windows keeps the R7 order (shared sibling root first). POSIX prefers the
|
|
167
|
+
// external configured temp (PK_TEST_TEMP_ROOT, then the OS temp captured at
|
|
168
|
+
// module load) over the long sibling shared root: every byte of the root is
|
|
169
|
+
// spent by every socket beneath it (R9).
|
|
170
|
+
const candidates = IS_POSIX
|
|
171
|
+
? [
|
|
172
|
+
LOADED_TEMP_ROOT === process.env.PK_TEST_TEMP_ROOT ? LOADED_TEMP_ROOT : null,
|
|
173
|
+
LOADED_TEMP_ROOT,
|
|
174
|
+
fs.existsSync(sharedRoot) ? sharedRoot : null,
|
|
175
|
+
]
|
|
176
|
+
: [
|
|
177
|
+
LOADED_TEMP_ROOT === process.env.PK_TEST_TEMP_ROOT ? LOADED_TEMP_ROOT : null,
|
|
178
|
+
fs.existsSync(sharedRoot) ? sharedRoot : null,
|
|
179
|
+
LOADED_TEMP_ROOT,
|
|
180
|
+
];
|
|
181
|
+
const chosen = candidates.find(value => value && String(value).trim());
|
|
182
|
+
const root = path.resolve(String(chosen).trim());
|
|
183
|
+
if (isInside(root, REPO_ROOT)) throw new Error(`refusing to use a temp root inside the repository: ${root}`);
|
|
184
|
+
return root;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let stableRoot = null;
|
|
188
|
+
let exitHookArmed = false;
|
|
189
|
+
/** Per-process parent temp: stable across serial harness runs, removed at process exit. */
|
|
190
|
+
function stableTempRoot() {
|
|
191
|
+
if (!stableRoot) {
|
|
192
|
+
const prefix = IS_POSIX ? `pkn${process.pid.toString(36)}` : `pk-nav-r7-${process.pid}-`;
|
|
193
|
+
stableRoot = fs.mkdtempSync(path.join(resolveNavTempRoot(), prefix));
|
|
194
|
+
if (!exitHookArmed) {
|
|
195
|
+
exitHookArmed = true;
|
|
196
|
+
process.on('exit', () => { try { fs.rmSync(stableRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }); } catch { /* best effort */ } });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return stableRoot;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function buildLayout(root) {
|
|
203
|
+
const layout = {
|
|
204
|
+
home: path.join(root, 'home'),
|
|
205
|
+
codexHome: path.join(root, 'codex-home'),
|
|
206
|
+
claudeConfigDir: path.join(root, 'claude-config'),
|
|
207
|
+
appData: path.join(root, 'appdata'),
|
|
208
|
+
localAppData: path.join(root, 'localappdata'),
|
|
209
|
+
// 't' on POSIX: three fewer bytes on every Chrome socket path (R9).
|
|
210
|
+
temp: path.join(root, IS_POSIX ? 't' : 'temp'),
|
|
211
|
+
dataDir: path.join(root, 'kb-data'),
|
|
212
|
+
knowledgeDataDir: path.join(root, 'kb-knowledge-data'),
|
|
213
|
+
bridgeHome: path.join(root, 'bridge-home'),
|
|
214
|
+
agentTerminalDataDir: path.join(root, 'agent-terminal'),
|
|
215
|
+
workbenchDataDir: path.join(root, 'workbench-data'),
|
|
216
|
+
};
|
|
217
|
+
for (const dir of Object.values(layout)) fs.mkdirSync(dir, { recursive: true });
|
|
218
|
+
// Chrome's default-profile shape under the isolated USERPROFILE: Chrome
|
|
219
|
+
// resolves it even when launched with an explicit --user-data-dir, and no
|
|
220
|
+
// Node/GUI child keeps the real USERPROFILE.
|
|
221
|
+
layout.chromeDefaultProfileDir = path.join(layout.home, 'AppData', 'Local', 'Google', 'Chrome', 'User Data');
|
|
222
|
+
fs.mkdirSync(layout.chromeDefaultProfileDir, { recursive: true });
|
|
223
|
+
layout.psCachePath = path.join(root, 'ps-module-analysis-cache.bin');
|
|
224
|
+
return layout;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function isolationEnvMap(layout) {
|
|
228
|
+
const parsed = path.parse(layout.home);
|
|
229
|
+
return {
|
|
230
|
+
HOME: layout.home,
|
|
231
|
+
USERPROFILE: layout.home,
|
|
232
|
+
HOMEDRIVE: parsed.root,
|
|
233
|
+
HOMEPATH: layout.home.slice(parsed.root.length),
|
|
234
|
+
CODEX_HOME: layout.codexHome,
|
|
235
|
+
CLAUDE_CONFIG_DIR: layout.claudeConfigDir,
|
|
236
|
+
APPDATA: layout.appData,
|
|
237
|
+
LOCALAPPDATA: layout.localAppData,
|
|
238
|
+
TEMP: layout.temp,
|
|
239
|
+
TMP: layout.temp,
|
|
240
|
+
TMPDIR: layout.temp,
|
|
241
|
+
KB_DATA_DIR: layout.dataDir,
|
|
242
|
+
KB_KNOWLEDGE_DATA_DIR: layout.knowledgeDataDir,
|
|
243
|
+
AI_CODING_EVENT_BRIDGE_HOME: layout.bridgeHome,
|
|
244
|
+
AGENT_TERMINAL_DATA_DIR: layout.agentTerminalDataDir,
|
|
245
|
+
CLAUDE_AI_WORKBENCH_DATA_DIR: layout.workbenchDataDir,
|
|
246
|
+
KB_MODULES_AUTOSTART: '0',
|
|
247
|
+
PSModuleAnalysisCachePath: layout.psCachePath,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Whitelist the driver environment in place. Returns the per-run isolation
|
|
253
|
+
* context; the caller MUST call restore() after cleanup (serial runs reuse
|
|
254
|
+
* the stable parent temp, which is only removed at process exit).
|
|
255
|
+
*/
|
|
256
|
+
function enterNavIsolation({ tag = 'run' } = {}) {
|
|
257
|
+
// POSIX shortens the case component to 'n<rand6>' (R9 socket budget);
|
|
258
|
+
// Windows keeps the descriptive '<tag>-<rand6>' names.
|
|
259
|
+
const casePrefix = IS_POSIX ? 'n' : `${tag}-`;
|
|
260
|
+
const root = fs.mkdtempSync(path.join(stableTempRoot(), casePrefix));
|
|
261
|
+
if (IS_POSIX) {
|
|
262
|
+
try {
|
|
263
|
+
assertPosixSocketBudget(root, `isolation root for tag ${tag}`);
|
|
264
|
+
} catch (error) {
|
|
265
|
+
try { fs.rmSync(root, { recursive: true, force: true }); } catch { /* best effort */ }
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const layout = buildLayout(root);
|
|
270
|
+
|
|
271
|
+
// The shortened POSIX component names must not cost post-mortem ownership
|
|
272
|
+
// binding anything: the full tag, pid and creation time stay on disk.
|
|
273
|
+
fs.writeFileSync(path.join(root, 'owner.json'), JSON.stringify({
|
|
274
|
+
schema: 'qa-autopilot/nav-isolation-owner/v1',
|
|
275
|
+
tag,
|
|
276
|
+
platform: process.platform,
|
|
277
|
+
pid: process.pid,
|
|
278
|
+
createdAt: new Date().toISOString(),
|
|
279
|
+
}, null, 2) + '\n', 'utf8');
|
|
280
|
+
|
|
281
|
+
const previous = {};
|
|
282
|
+
for (const key of Object.keys(process.env)) previous[key] = process.env[key];
|
|
283
|
+
|
|
284
|
+
// R9 redirection evidence (booleans only — original values may carry real
|
|
285
|
+
// secrets): which isolation keys pointed at the real home BEFORE the
|
|
286
|
+
// whitelist, so the report can prove every one was redirected into the
|
|
287
|
+
// owned root rather than merely overwritten with another leak.
|
|
288
|
+
const originalInsideRealHome = {};
|
|
289
|
+
for (const key of ISOLATION_KEYS) {
|
|
290
|
+
const original = previous[key];
|
|
291
|
+
originalInsideRealHome[key] = original !== undefined
|
|
292
|
+
&& (isInside(path.resolve(String(original)), REAL_HOME) || isInside(path.resolve(String(original)), REAL_USERPROFILE));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
for (const key of Object.keys(process.env)) {
|
|
296
|
+
if (!isOsWhitelisted(key)) delete process.env[key];
|
|
297
|
+
}
|
|
298
|
+
Object.assign(process.env, isolationEnvMap(layout));
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
tag, root, layout,
|
|
302
|
+
posixShortRoot: IS_POSIX,
|
|
303
|
+
originalInsideRealHome,
|
|
304
|
+
envMap: isolationEnvMap(layout),
|
|
305
|
+
realHome: REAL_HOME,
|
|
306
|
+
realUserProfile: REAL_USERPROFILE,
|
|
307
|
+
/** Put the original environment back; keeps the stable parent temp alive. */
|
|
308
|
+
restore() {
|
|
309
|
+
for (const key of Object.keys(process.env)) delete process.env[key];
|
|
310
|
+
Object.assign(process.env, previous);
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Placement-aware real-home scan (R9). A non-OS-whitelisted env value is a
|
|
317
|
+
* leak ONLY when it references the real home while NOT being canonically
|
|
318
|
+
* inside the owned isolation root: on hosted runners the owned temp root
|
|
319
|
+
* legitimately lives UNDER the real home (/home/runner/work/_temp), and that
|
|
320
|
+
* placement must not fail the scan. Canonical containment decides; a raw
|
|
321
|
+
* substring match outside the owned root still flags (values that merely
|
|
322
|
+
* mention the real home are suspect even when not resolvable paths).
|
|
323
|
+
*
|
|
324
|
+
* R7 correction: the canonical-containment arms (both the owned-root pardon
|
|
325
|
+
* and the real-home accusation) apply ONLY to path-shaped values. Resolving a
|
|
326
|
+
* non-path value such as a numeric flag ('0'/'1') anchors it at the process
|
|
327
|
+
* CWD, and on Linux runners the CWD lives under the real home — that turned
|
|
328
|
+
* KB_MODULES_AUTOSTART='0' into a phantom path leak. Non-path values keep the
|
|
329
|
+
* substring mention test only.
|
|
330
|
+
*/
|
|
331
|
+
const PATH_SHAPED_RE = /^(?:[A-Za-z]:[\\/]|\\\\|\/|~[\\/\\]|\.{1,2}[\\/\\])/;
|
|
332
|
+
function realHomeReferenceKeys(env, { root = null, realHome = REAL_HOME, realUserProfile = REAL_USERPROFILE } = {}) {
|
|
333
|
+
return Object.entries(env)
|
|
334
|
+
.filter(([key]) => !isOsWhitelisted(key))
|
|
335
|
+
.filter(([, value]) => {
|
|
336
|
+
const text = String(value);
|
|
337
|
+
const pathShaped = PATH_SHAPED_RE.test(text);
|
|
338
|
+
if (pathShaped && root && isInside(path.resolve(text), root)) return false;
|
|
339
|
+
return text.includes(realHome) || text.includes(realUserProfile)
|
|
340
|
+
|| (pathShaped && (isInside(path.resolve(text), realHome) || isInside(path.resolve(text), realUserProfile)));
|
|
341
|
+
})
|
|
342
|
+
.map(([key]) => key);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* The isolation invariants, as one assertable record. `env` defaults to the
|
|
347
|
+
* live process environment, so a harness or a negative case can prove the
|
|
348
|
+
* whitelist property at any moment.
|
|
349
|
+
*/
|
|
350
|
+
function navIsolationReport(env = process.env, isolation = null) {
|
|
351
|
+
const layout = isolation ? isolation.layout : null;
|
|
352
|
+
const root = isolation ? isolation.root : null;
|
|
353
|
+
const findings = {
|
|
354
|
+
isolationKeys: Object.fromEntries(ISOLATION_KEYS.map(key => [key, env[key] ?? null])),
|
|
355
|
+
allIsolationKeysInsideRoot: layout
|
|
356
|
+
? ISOLATION_KEYS.filter(key => key !== 'HOMEDRIVE' && key !== 'HOMEPATH').every(key => env[key] && isInside(path.resolve(env[key]), root))
|
|
357
|
+
: null,
|
|
358
|
+
ambientDeleted: Object.fromEntries([...AMBIENT_DELETES, 'KB_MODULES_AUTOSTART'].map(key => [key, Object.keys(env).some(name => upper(name) === upper(key))])),
|
|
359
|
+
noAmbientSurvives: [...AMBIENT_DELETES].every(key => !Object.keys(env).some(name => upper(name) === upper(key))),
|
|
360
|
+
osWhitelistHeld: Object.keys(env).filter(key => !isOsWhitelisted(key) && !ISOLATION_KEYS.includes(key) && key !== 'KB_MODULES_AUTOSTART'),
|
|
361
|
+
// Real-home references are tolerated ONLY inside the OS allowlist (the
|
|
362
|
+
// real PATH names real tool locations) or, since R9, inside the owned
|
|
363
|
+
// isolation root itself (hosted runners place RUNNER_TEMP under the real
|
|
364
|
+
// home — placement, not identity); every other variable must be free of
|
|
365
|
+
// them. See realHomeReferenceKeys for the canonical rule.
|
|
366
|
+
referencesRealHome: realHomeReferenceKeys(env, { root }),
|
|
367
|
+
// Keys that pointed at the real home before the whitelist and have NOT
|
|
368
|
+
// been redirected into the owned root (empty list = redirection proven).
|
|
369
|
+
redirectionFailures: layout && isolation && isolation.originalInsideRealHome
|
|
370
|
+
? Object.entries(isolation.originalInsideRealHome)
|
|
371
|
+
.filter(([, wasReal]) => wasReal)
|
|
372
|
+
.filter(([key]) => !(env[key] && isInside(path.resolve(String(env[key])), root)))
|
|
373
|
+
.map(([key]) => key)
|
|
374
|
+
: null,
|
|
375
|
+
posixSocketBudget: root ? posixSocketBudgetVerdict(root) : null,
|
|
376
|
+
chromeDefaultProfileDirExists: layout ? fs.existsSync(layout.chromeDefaultProfileDir) : null,
|
|
377
|
+
psCacheParentExists: layout ? fs.existsSync(path.dirname(layout.psCachePath)) : null,
|
|
378
|
+
};
|
|
379
|
+
if (root) findings.stableRootExists = fs.existsSync(path.dirname(root));
|
|
380
|
+
return findings;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
module.exports = {
|
|
384
|
+
enterNavIsolation, navIsolationReport, isolationEnvMap, realHomeReferenceKeys,
|
|
385
|
+
OS_WHITELIST, ISOLATION_KEYS, AMBIENT_DELETES,
|
|
386
|
+
REAL_HOME, REAL_USERPROFILE, resolveNavTempRoot,
|
|
387
|
+
worstCasePosixSocketPaths, posixSocketBudgetVerdict, assertPosixSocketBudget,
|
|
388
|
+
POSIX_SOCKET_CHAR_LIMIT, CHROME_RAND_MAX_CHARS,
|
|
389
|
+
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// _site/_test/helpers/parallel-nav-module-ui.js
|
|
2
|
+
//
|
|
3
|
+
// Test double for an embedded module UI (the Agent workbench, the knowledge
|
|
4
|
+
// index, the event bridge). The product embeds a module in an iframe and
|
|
5
|
+
// talks to it with the kb:* postMessage protocol; the module answers
|
|
6
|
+
// "kb:ready" and then receives kb:theme, kb:scope, kb:select-project and
|
|
7
|
+
// kb:visibility.
|
|
8
|
+
//
|
|
9
|
+
// This fixture is a real HTTP service: the product proxies the iframe
|
|
10
|
+
// request to it, the page it serves is the one the browser really loads, and
|
|
11
|
+
// the postMessage traffic is really delivered. It is NOT the real module
|
|
12
|
+
// UI — the cases that use it verify the SHELL side of the protocol (the
|
|
13
|
+
// shell must deliver kb:theme, must not re-send kb:select-project after a
|
|
14
|
+
// theme change, and must map the right project to the frame), so the
|
|
15
|
+
// substituted part is stated in the case report.
|
|
16
|
+
//
|
|
17
|
+
// The fixture also answers the module registry API the product calls when it
|
|
18
|
+
// registers a project, so the project→module mapping under test is a real
|
|
19
|
+
// registration round trip.
|
|
20
|
+
|
|
21
|
+
'use strict';
|
|
22
|
+
|
|
23
|
+
const http = require('node:http');
|
|
24
|
+
const crypto = require('node:crypto');
|
|
25
|
+
const fs = require('node:fs');
|
|
26
|
+
const path = require('node:path');
|
|
27
|
+
|
|
28
|
+
const PAGE = `<!doctype html>
|
|
29
|
+
<html lang="zh-CN"><head><meta charset="utf-8"><title>module ui fixture</title></head>
|
|
30
|
+
<body>
|
|
31
|
+
<p>module ui fixture</p>
|
|
32
|
+
<script>
|
|
33
|
+
// The messages the shell really sent to this frame, in order.
|
|
34
|
+
window.__navMessages = [];
|
|
35
|
+
window.addEventListener('message', function (event) {
|
|
36
|
+
var data = event.data;
|
|
37
|
+
if (!data || typeof data !== 'object') return;
|
|
38
|
+
window.__navMessages.push({ type: String(data.type || ''), payload: data, at: Date.now() });
|
|
39
|
+
});
|
|
40
|
+
// The shell only starts talking to a frame after the frame announced
|
|
41
|
+
// itself; that announcement is the "ready" edge of the protocol.
|
|
42
|
+
window.parent.postMessage({ type: 'kb:ready' }, '*');
|
|
43
|
+
</script>
|
|
44
|
+
</body></html>`;
|
|
45
|
+
|
|
46
|
+
function sendJson(res, status, payload) {
|
|
47
|
+
const body = JSON.stringify(payload);
|
|
48
|
+
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8', 'Content-Length': Buffer.byteLength(body) });
|
|
49
|
+
res.end(body);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createModuleUiFixture({ tag = 'module-ui' } = {}) {
|
|
53
|
+
const state = { pageLoads: 0, requests: [], projects: [], nextId: 1 };
|
|
54
|
+
let server;
|
|
55
|
+
|
|
56
|
+
server = http.createServer(async (req, res) => {
|
|
57
|
+
const url = new URL(req.url, 'http://127.0.0.1');
|
|
58
|
+
const chunks = [];
|
|
59
|
+
for await (const chunk of req) chunks.push(chunk);
|
|
60
|
+
const raw = Buffer.concat(chunks);
|
|
61
|
+
let body = null;
|
|
62
|
+
if (raw.length) { try { body = JSON.parse(raw.toString('utf8')); } catch { body = null; } }
|
|
63
|
+
state.requests.push({ method: req.method, path: url.pathname + url.search, body, at: Date.now() });
|
|
64
|
+
|
|
65
|
+
// The health probes the product uses to recognise a module. The same
|
|
66
|
+
// double stands in for the knowledge index and the event bridge, so the
|
|
67
|
+
// body carries both identity markers.
|
|
68
|
+
if (req.method === 'GET' && url.pathname === '/api/claude-workbench/v1/health') {
|
|
69
|
+
sendJson(res, 200, { status: 'ok', apiVersion: 'test-double' });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (req.method === 'GET' && url.pathname === '/api/health') {
|
|
73
|
+
sendJson(res, 200, { ok: true, projects: state.projects.filter(project => project.sourceDir), registeredProjects: state.projects.filter(project => project.id).length });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Navigation owns no real journals. An enabled engine can safely poll
|
|
78
|
+
// this empty Bridge contract without reaching the production bridge.
|
|
79
|
+
if (req.method === 'GET' && url.pathname === '/api/journals') {
|
|
80
|
+
sendJson(res, 200, { journals: [] });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// The module registry API the product uses when it registers a project.
|
|
85
|
+
if (req.method === 'GET' && url.pathname === '/api/claude-workbench/v1/projects') {
|
|
86
|
+
sendJson(res, 200, { projects: state.projects });
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (req.method === 'POST' && url.pathname === '/api/claude-workbench/v1/projects') {
|
|
90
|
+
const id = `terminal-${state.nextId++}`;
|
|
91
|
+
state.projects.push({ id, name: body?.name || id, rootPath: body?.rootPath || '' });
|
|
92
|
+
sendJson(res, 201, { id, name: body?.name || id, rootPath: body?.rootPath || '' });
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (req.method === 'DELETE' && url.pathname.startsWith('/api/claude-workbench/v1/projects/')) {
|
|
96
|
+
const id = decodeURIComponent(url.pathname.split('/').pop());
|
|
97
|
+
state.projects = state.projects.filter(project => project.id !== id);
|
|
98
|
+
res.writeHead(204); res.end();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// The knowledge-index registry: the product imports a project by name and
|
|
102
|
+
// source directory, and lists the imported ones before importing again.
|
|
103
|
+
if (req.method === 'GET' && url.pathname === '/api/projects') {
|
|
104
|
+
sendJson(res, 200, { projects: state.projects.filter(project => project.sourceDir) });
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (req.method === 'POST' && url.pathname === '/api/import') {
|
|
108
|
+
const project = { name: body?.projectName || '', sourceDir: body?.sourceDir || '', hasIndex: true };
|
|
109
|
+
state.projects.push(project);
|
|
110
|
+
sendJson(res, 200, { ok: true, project: project.name });
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// The vector contract subset the REAL engine's index step calls
|
|
114
|
+
// (coordinator.js requestIndex -> POST /api/sync {project, sourceDir}).
|
|
115
|
+
// The vector module is NOT the subject under test — this double keeps the
|
|
116
|
+
// engine's index step off its built-in default vectorUrl (the LIVE
|
|
117
|
+
// desktop hub on 127.0.0.1:8787); every request body is recorded so the
|
|
118
|
+
// harness can assert project/sourceDir never leave the fixture.
|
|
119
|
+
if (req.method === 'POST' && url.pathname === '/api/sync') {
|
|
120
|
+
if (!body?.project || !body?.sourceDir) { sendJson(res, 400, { error: "Body must include 'project' and 'sourceDir'." }); return; }
|
|
121
|
+
let filesTracked = 0;
|
|
122
|
+
try {
|
|
123
|
+
filesTracked = fs.readdirSync(body.sourceDir, { withFileTypes: true })
|
|
124
|
+
.filter(entry => entry.isFile() && ['.md', '.txt', '.html'].includes(path.extname(entry.name).toLowerCase())).length;
|
|
125
|
+
} catch { filesTracked = 0; }
|
|
126
|
+
state.projects = state.projects.filter(project => project.name !== body.project);
|
|
127
|
+
state.projects.push({ name: body.project, sourceDir: body.sourceDir, hasIndex: true });
|
|
128
|
+
sendJson(res, 200, { ok: true, project: body.project, filesTracked });
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (req.method === 'DELETE' && url.pathname === '/api/project') {
|
|
132
|
+
const name = url.searchParams.get('name');
|
|
133
|
+
state.projects = state.projects.filter(project => project.name !== name);
|
|
134
|
+
sendJson(res, 200, { ok: true });
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
// The development-event registry: the product registers a work tree and
|
|
138
|
+
// reads the journal back by the id the registry answered with. The real
|
|
139
|
+
// registry answers with the workspace id, and the engine only accepts
|
|
140
|
+
// that shape as a session baseline key.
|
|
141
|
+
if (req.method === 'POST' && url.pathname === '/api/projects') {
|
|
142
|
+
const id = `sha256:${crypto.createHash('sha256').update(String(body?.path || '')).digest('hex')}`;
|
|
143
|
+
state.projects.push({ id, path: body?.path || '', auto: false, store: body?.store || '' });
|
|
144
|
+
sendJson(res, 201, { project: { id, path: body?.path || '' } });
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (req.method === 'DELETE' && url.pathname === '/api/projects') {
|
|
148
|
+
const id = url.searchParams.get('id');
|
|
149
|
+
state.projects = state.projects.filter(project => project.id !== id);
|
|
150
|
+
sendJson(res, 200, { ok: true });
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (req.method !== 'GET') {
|
|
154
|
+
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
155
|
+
res.end('not found');
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// Every other GET is an embedded UI document: count it, because a
|
|
159
|
+
// reloaded frame is a rebuilt task for the module.
|
|
160
|
+
state.pageLoads += 1;
|
|
161
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' });
|
|
162
|
+
res.end(PAGE);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
tag,
|
|
167
|
+
state,
|
|
168
|
+
/** How many times the embedded UI document was really served. */
|
|
169
|
+
pageLoads: () => state.pageLoads,
|
|
170
|
+
/** Every POST /api/sync body the engine really sent (vector stand-in evidence). */
|
|
171
|
+
vectorSyncs: () => state.requests.filter(request => request.method === 'POST' && request.path === '/api/sync'),
|
|
172
|
+
/** Every POST /api/import body (the engine's scope-registration vector call). */
|
|
173
|
+
vectorImports: () => state.requests.filter(request => request.method === 'POST' && request.path === '/api/import'),
|
|
174
|
+
async start() {
|
|
175
|
+
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
|
176
|
+
this.url = `http://127.0.0.1:${server.address().port}`;
|
|
177
|
+
return this.url;
|
|
178
|
+
},
|
|
179
|
+
async close() {
|
|
180
|
+
server.closeAllConnections();
|
|
181
|
+
await new Promise(resolve => server.close(resolve));
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = { createModuleUiFixture };
|