@sanqianx/project-knowledge 5.0.24 → 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 -1353
- 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 +66 -63
- 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
|
@@ -1,119 +1,119 @@
|
|
|
1
|
-
const assert = require('assert');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const os = require('os');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { spawnServer } = require('./helpers/spawn-server');
|
|
6
|
-
const { StorageLayout } = require('../lib/storage-layout');
|
|
7
|
-
const { SettingsStore } = require('../lib/settings-store');
|
|
8
|
-
const { ProjectRegistryStore } = require('../lib/project-registry-store');
|
|
9
|
-
const { ProjectStore } = require('../lib/project-store');
|
|
10
|
-
const { ConversationStore } = require('../lib/conversation-store');
|
|
11
|
-
const { localDate } = require('../lib/conversation-query-service');
|
|
12
|
-
|
|
13
|
-
const ROOT = path.resolve(__dirname, '..', '..');
|
|
14
|
-
const PORT = 8250 + (process.pid % 300);
|
|
15
|
-
const BASE = `http://127.0.0.1:${PORT}`;
|
|
16
|
-
|
|
17
|
-
async function waitForServer() {
|
|
18
|
-
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
19
|
-
try { if ((await fetch(`${BASE}/api/health`)).ok) return; } catch {}
|
|
20
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
21
|
-
}
|
|
22
|
-
throw new Error('server did not start');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
(async () => {
|
|
26
|
-
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pk-conversation-api-'));
|
|
27
|
-
const layout = new StorageLayout({ dataDir });
|
|
28
|
-
const settings = new SettingsStore({ layout });
|
|
29
|
-
const registry = new ProjectRegistryStore({ layout });
|
|
30
|
-
const projects = new ProjectStore({ layout });
|
|
31
|
-
await settings.initialize({ knowledge: { rootPath: path.join(dataDir, 'knowledge') } });
|
|
32
|
-
await registry.initialize();
|
|
33
|
-
const projectId = 'project-conversation-api';
|
|
34
|
-
const repoPath = path.join(dataDir, 'repo');
|
|
35
|
-
const knowledgePath = path.join(dataDir, 'knowledge', projectId);
|
|
36
|
-
fs.mkdirSync(repoPath, { recursive: true });
|
|
37
|
-
fs.mkdirSync(knowledgePath, { recursive: true });
|
|
38
|
-
await projects.create(projectId, {
|
|
39
|
-
displayName: 'Conversation API', storageName: 'conversation-api', repoPath, knowledgePath,
|
|
40
|
-
}, { conversationBaselineCursor: 0, conversation: { lastConsumedCursor: 0, captureStatus: 'captured' } });
|
|
41
|
-
await registry.add(projectId, { displayNameSnapshot: 'Conversation API' });
|
|
42
|
-
const store = new ConversationStore({ layout, projectStore: projects });
|
|
43
|
-
const repoIdentity = { repoId: 'conversation-api-repo' };
|
|
44
|
-
const unsafePrompt = '<script>alert("conversation-body")</script> Keep this as plain text.';
|
|
45
|
-
async function append(eventId, sequence, turnId, role, content, capturedAt) {
|
|
46
|
-
return (await store.appendEvent(projectId, {
|
|
47
|
-
eventId, sequence, source: 'codex', eventType: role === 'user' ? 'user_prompt' : 'assistant_response', role, content,
|
|
48
|
-
sessionId: 'session-api', turnId, repoIdentity, projectPath: repoPath, branch: 'main', headAtCapture: '0'.repeat(40),
|
|
49
|
-
capturedAt, identityConfidence: 'high', captureStatus: 'captured',
|
|
50
|
-
})).event;
|
|
51
|
-
}
|
|
52
|
-
const r1 = await append('r1', 1, 'turn-1', 'user', unsafePrompt, '2026-08-18T01:00:00.000Z');
|
|
53
|
-
const a1 = await append('a1', 2, 'turn-1', 'assistant', 'Rendered only as plain assistant text.', '2026-08-18T01:01:00.000Z');
|
|
54
|
-
const r2 = await append('r2', 3, 'turn-2', 'user', 'Second prompt.', '2026-08-18T02:00:00.000Z');
|
|
55
|
-
const a2 = await append('a2', 4, 'turn-2', 'assistant', 'Second reply.', '2026-08-18T02:01:00.000Z');
|
|
56
|
-
await append('r3', 5, 'turn-3', 'user', 'Third prompt.', '2026-08-18T03:00:00.000Z');
|
|
57
|
-
await append('r4', 6, 'turn-other-date', 'user', 'Other date prompt.', '2026-08-19T03:00:00.000Z');
|
|
58
|
-
const projectEvent = event => ({ eventId: event.eventId, sequence: event.sequence, content: event.content, contentHash: event.contentHash, capturedAt: event.capturedAt });
|
|
59
|
-
store.writeSnapshot(projectId, {
|
|
60
|
-
commitSha: '1'.repeat(40), repoIdentity, parentSha: null, boundaryStartCursor: 0, boundaryEndCursor: 2, status: 'available',
|
|
61
|
-
turns: [{ turnId: 'turn-1', source: 'codex', sessionId: 'session-api', bindingKind: 'direct', userEvents: [projectEvent(r1)], assistantEvents: [projectEvent(a1)] }],
|
|
62
|
-
});
|
|
63
|
-
store.writeSnapshot(projectId, {
|
|
64
|
-
commitSha: '3'.repeat(40), repoIdentity, parentSha: '2'.repeat(40), boundaryStartCursor: 5, boundaryEndCursor: 5, status: 'available',
|
|
65
|
-
turns: [{ turnId: 'turn-2', source: 'codex', sessionId: 'session-api', bindingKind: 'shared-spanning', userEvents: [projectEvent(r2)], assistantEvents: [projectEvent(a2)] }],
|
|
66
|
-
});
|
|
67
|
-
store.writeSnapshot(projectId, {
|
|
68
|
-
commitSha: '2'.repeat(40), repoIdentity, parentSha: '1'.repeat(40), boundaryStartCursor: 2, boundaryEndCursor: 4, status: 'available',
|
|
69
|
-
turns: [{ turnId: 'turn-2', source: 'codex', sessionId: 'session-api', bindingKind: 'shared-spanning', userEvents: [projectEvent(r2)], assistantEvents: [projectEvent(a2)] }],
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const spawned = spawnServer({ root: ROOT, port: PORT, dataDir, tag: 'conversation-api', extraEnv: { KB_SKIP_MIGRATION: '1' } });
|
|
73
|
-
try {
|
|
74
|
-
await waitForServer();
|
|
75
|
-
let response = await fetch(`${BASE}/api/conversations/projects`);
|
|
76
|
-
let body = await response.json();
|
|
77
|
-
assert.strictEqual(response.status, 200);
|
|
78
|
-
assert.deepStrictEqual(body.projects.map(project => project.projectId), [projectId]);
|
|
79
|
-
assert.strictEqual(Object.hasOwn(body.projects[0], 'repoPath'), false, 'project picker payload must not expose raw repository paths');
|
|
80
|
-
|
|
81
|
-
const date = localDate(r1.capturedAt);
|
|
82
|
-
response = await fetch(`${BASE}/api/conversations/turns?projectId=${projectId}&date=${date}&limit=2`);
|
|
83
|
-
body = await response.json();
|
|
84
|
-
assert.strictEqual(response.status, 200, JSON.stringify(body));
|
|
85
|
-
assert.strictEqual(body.turns.length, 2);
|
|
86
|
-
assert(body.nextCursor && body.hasMore);
|
|
87
|
-
const firstPage = body.turns;
|
|
88
|
-
response = await fetch(`${BASE}/api/conversations/turns?projectId=${projectId}&date=${date}&limit=2&cursor=${encodeURIComponent(body.nextCursor)}`);
|
|
89
|
-
const secondPage = await response.json();
|
|
90
|
-
assert.strictEqual(secondPage.turns.length, 1);
|
|
91
|
-
const allTurns = [...firstPage, ...secondPage.turns];
|
|
92
|
-
const turn1 = allTurns.find(turn => turn.turnId === 'turn-1');
|
|
93
|
-
const turn2 = allTurns.find(turn => turn.turnId === 'turn-2');
|
|
94
|
-
const turn3 = allTurns.find(turn => turn.turnId === 'turn-3');
|
|
95
|
-
assert.strictEqual(turn1.userPrompt, unsafePrompt, 'conversation body should remain exact business data');
|
|
96
|
-
assert.strictEqual(turn1.renderAs, 'plain-text', 'payload must instruct hosts to avoid HTML interpretation');
|
|
97
|
-
assert.strictEqual(turn1.annotation.status, 'committed');
|
|
98
|
-
assert.strictEqual(turn1.annotation.commits[0].shortSha, '1111111');
|
|
99
|
-
assert.strictEqual(turn2.annotation.status, 'associated');
|
|
100
|
-
assert.deepStrictEqual(turn2.annotation.commits.map(commit => commit.shortSha), ['2222222'], 'stale shared-spanning annotations after the next user turn are hidden');
|
|
101
|
-
assert.strictEqual(turn3.annotation.status, 'uncommitted');
|
|
102
|
-
assert(allTurns.every(turn => turn.date === date));
|
|
103
|
-
|
|
104
|
-
response = await fetch(`${BASE}/api/conversations/turns?projectId=${projectId}&date=2026-08-19&cursor=${encodeURIComponent(body.nextCursor)}`);
|
|
105
|
-
assert.strictEqual(response.status, 409, 'cursor must be bound to one project/date query');
|
|
106
|
-
response = await fetch(`${BASE}/api/conversations/sessions?projectId=${projectId}`);
|
|
107
|
-
assert.strictEqual(response.status, 404, 'default product API must not expose source/session diagnostic routes');
|
|
108
|
-
|
|
109
|
-
response = await fetch(`${BASE}/api/logs/export`);
|
|
110
|
-
const exportedLogs = await response.text();
|
|
111
|
-
assert(!exportedLogs.includes('conversation-body'), 'conversation content must never leak into structured logs or export');
|
|
112
|
-
console.log('conversation-api-test PASS');
|
|
113
|
-
} finally {
|
|
114
|
-
await spawned.cleanup();
|
|
115
|
-
}
|
|
116
|
-
})().catch(error => {
|
|
117
|
-
console.error(error.stack || error.message);
|
|
118
|
-
process.exitCode = 1;
|
|
119
|
-
});
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawnServer } = require('./helpers/spawn-server');
|
|
6
|
+
const { StorageLayout } = require('../lib/storage-layout');
|
|
7
|
+
const { SettingsStore } = require('../lib/settings-store');
|
|
8
|
+
const { ProjectRegistryStore } = require('../lib/project-registry-store');
|
|
9
|
+
const { ProjectStore } = require('../lib/project-store');
|
|
10
|
+
const { ConversationStore } = require('../lib/conversation-store');
|
|
11
|
+
const { localDate } = require('../lib/conversation-query-service');
|
|
12
|
+
|
|
13
|
+
const ROOT = path.resolve(__dirname, '..', '..');
|
|
14
|
+
const PORT = 8250 + (process.pid % 300);
|
|
15
|
+
const BASE = `http://127.0.0.1:${PORT}`;
|
|
16
|
+
|
|
17
|
+
async function waitForServer() {
|
|
18
|
+
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
19
|
+
try { if ((await fetch(`${BASE}/api/health`)).ok) return; } catch {}
|
|
20
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
21
|
+
}
|
|
22
|
+
throw new Error('server did not start');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
(async () => {
|
|
26
|
+
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pk-conversation-api-'));
|
|
27
|
+
const layout = new StorageLayout({ dataDir });
|
|
28
|
+
const settings = new SettingsStore({ layout });
|
|
29
|
+
const registry = new ProjectRegistryStore({ layout });
|
|
30
|
+
const projects = new ProjectStore({ layout });
|
|
31
|
+
await settings.initialize({ knowledge: { rootPath: path.join(dataDir, 'knowledge') } });
|
|
32
|
+
await registry.initialize();
|
|
33
|
+
const projectId = 'project-conversation-api';
|
|
34
|
+
const repoPath = path.join(dataDir, 'repo');
|
|
35
|
+
const knowledgePath = path.join(dataDir, 'knowledge', projectId);
|
|
36
|
+
fs.mkdirSync(repoPath, { recursive: true });
|
|
37
|
+
fs.mkdirSync(knowledgePath, { recursive: true });
|
|
38
|
+
await projects.create(projectId, {
|
|
39
|
+
displayName: 'Conversation API', storageName: 'conversation-api', repoPath, knowledgePath,
|
|
40
|
+
}, { conversationBaselineCursor: 0, conversation: { lastConsumedCursor: 0, captureStatus: 'captured' } });
|
|
41
|
+
await registry.add(projectId, { displayNameSnapshot: 'Conversation API' });
|
|
42
|
+
const store = new ConversationStore({ layout, projectStore: projects });
|
|
43
|
+
const repoIdentity = { repoId: 'conversation-api-repo' };
|
|
44
|
+
const unsafePrompt = '<script>alert("conversation-body")</script> Keep this as plain text.';
|
|
45
|
+
async function append(eventId, sequence, turnId, role, content, capturedAt) {
|
|
46
|
+
return (await store.appendEvent(projectId, {
|
|
47
|
+
eventId, sequence, source: 'codex', eventType: role === 'user' ? 'user_prompt' : 'assistant_response', role, content,
|
|
48
|
+
sessionId: 'session-api', turnId, repoIdentity, projectPath: repoPath, branch: 'main', headAtCapture: '0'.repeat(40),
|
|
49
|
+
capturedAt, identityConfidence: 'high', captureStatus: 'captured',
|
|
50
|
+
})).event;
|
|
51
|
+
}
|
|
52
|
+
const r1 = await append('r1', 1, 'turn-1', 'user', unsafePrompt, '2026-08-18T01:00:00.000Z');
|
|
53
|
+
const a1 = await append('a1', 2, 'turn-1', 'assistant', 'Rendered only as plain assistant text.', '2026-08-18T01:01:00.000Z');
|
|
54
|
+
const r2 = await append('r2', 3, 'turn-2', 'user', 'Second prompt.', '2026-08-18T02:00:00.000Z');
|
|
55
|
+
const a2 = await append('a2', 4, 'turn-2', 'assistant', 'Second reply.', '2026-08-18T02:01:00.000Z');
|
|
56
|
+
await append('r3', 5, 'turn-3', 'user', 'Third prompt.', '2026-08-18T03:00:00.000Z');
|
|
57
|
+
await append('r4', 6, 'turn-other-date', 'user', 'Other date prompt.', '2026-08-19T03:00:00.000Z');
|
|
58
|
+
const projectEvent = event => ({ eventId: event.eventId, sequence: event.sequence, content: event.content, contentHash: event.contentHash, capturedAt: event.capturedAt });
|
|
59
|
+
store.writeSnapshot(projectId, {
|
|
60
|
+
commitSha: '1'.repeat(40), repoIdentity, parentSha: null, boundaryStartCursor: 0, boundaryEndCursor: 2, status: 'available',
|
|
61
|
+
turns: [{ turnId: 'turn-1', source: 'codex', sessionId: 'session-api', bindingKind: 'direct', userEvents: [projectEvent(r1)], assistantEvents: [projectEvent(a1)] }],
|
|
62
|
+
});
|
|
63
|
+
store.writeSnapshot(projectId, {
|
|
64
|
+
commitSha: '3'.repeat(40), repoIdentity, parentSha: '2'.repeat(40), boundaryStartCursor: 5, boundaryEndCursor: 5, status: 'available',
|
|
65
|
+
turns: [{ turnId: 'turn-2', source: 'codex', sessionId: 'session-api', bindingKind: 'shared-spanning', userEvents: [projectEvent(r2)], assistantEvents: [projectEvent(a2)] }],
|
|
66
|
+
});
|
|
67
|
+
store.writeSnapshot(projectId, {
|
|
68
|
+
commitSha: '2'.repeat(40), repoIdentity, parentSha: '1'.repeat(40), boundaryStartCursor: 2, boundaryEndCursor: 4, status: 'available',
|
|
69
|
+
turns: [{ turnId: 'turn-2', source: 'codex', sessionId: 'session-api', bindingKind: 'shared-spanning', userEvents: [projectEvent(r2)], assistantEvents: [projectEvent(a2)] }],
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const spawned = spawnServer({ root: ROOT, port: PORT, dataDir, tag: 'conversation-api', extraEnv: { KB_SKIP_MIGRATION: '1' } });
|
|
73
|
+
try {
|
|
74
|
+
await waitForServer();
|
|
75
|
+
let response = await fetch(`${BASE}/api/conversations/projects`);
|
|
76
|
+
let body = await response.json();
|
|
77
|
+
assert.strictEqual(response.status, 200);
|
|
78
|
+
assert.deepStrictEqual(body.projects.map(project => project.projectId), [projectId]);
|
|
79
|
+
assert.strictEqual(Object.hasOwn(body.projects[0], 'repoPath'), false, 'project picker payload must not expose raw repository paths');
|
|
80
|
+
|
|
81
|
+
const date = localDate(r1.capturedAt);
|
|
82
|
+
response = await fetch(`${BASE}/api/conversations/turns?projectId=${projectId}&date=${date}&limit=2`);
|
|
83
|
+
body = await response.json();
|
|
84
|
+
assert.strictEqual(response.status, 200, JSON.stringify(body));
|
|
85
|
+
assert.strictEqual(body.turns.length, 2);
|
|
86
|
+
assert(body.nextCursor && body.hasMore);
|
|
87
|
+
const firstPage = body.turns;
|
|
88
|
+
response = await fetch(`${BASE}/api/conversations/turns?projectId=${projectId}&date=${date}&limit=2&cursor=${encodeURIComponent(body.nextCursor)}`);
|
|
89
|
+
const secondPage = await response.json();
|
|
90
|
+
assert.strictEqual(secondPage.turns.length, 1);
|
|
91
|
+
const allTurns = [...firstPage, ...secondPage.turns];
|
|
92
|
+
const turn1 = allTurns.find(turn => turn.turnId === 'turn-1');
|
|
93
|
+
const turn2 = allTurns.find(turn => turn.turnId === 'turn-2');
|
|
94
|
+
const turn3 = allTurns.find(turn => turn.turnId === 'turn-3');
|
|
95
|
+
assert.strictEqual(turn1.userPrompt, unsafePrompt, 'conversation body should remain exact business data');
|
|
96
|
+
assert.strictEqual(turn1.renderAs, 'plain-text', 'payload must instruct hosts to avoid HTML interpretation');
|
|
97
|
+
assert.strictEqual(turn1.annotation.status, 'committed');
|
|
98
|
+
assert.strictEqual(turn1.annotation.commits[0].shortSha, '1111111');
|
|
99
|
+
assert.strictEqual(turn2.annotation.status, 'associated');
|
|
100
|
+
assert.deepStrictEqual(turn2.annotation.commits.map(commit => commit.shortSha), ['2222222'], 'stale shared-spanning annotations after the next user turn are hidden');
|
|
101
|
+
assert.strictEqual(turn3.annotation.status, 'uncommitted');
|
|
102
|
+
assert(allTurns.every(turn => turn.date === date));
|
|
103
|
+
|
|
104
|
+
response = await fetch(`${BASE}/api/conversations/turns?projectId=${projectId}&date=2026-08-19&cursor=${encodeURIComponent(body.nextCursor)}`);
|
|
105
|
+
assert.strictEqual(response.status, 409, 'cursor must be bound to one project/date query');
|
|
106
|
+
response = await fetch(`${BASE}/api/conversations/sessions?projectId=${projectId}`);
|
|
107
|
+
assert.strictEqual(response.status, 404, 'default product API must not expose source/session diagnostic routes');
|
|
108
|
+
|
|
109
|
+
response = await fetch(`${BASE}/api/logs/export`);
|
|
110
|
+
const exportedLogs = await response.text();
|
|
111
|
+
assert(!exportedLogs.includes('conversation-body'), 'conversation content must never leak into structured logs or export');
|
|
112
|
+
console.log('conversation-api-test PASS');
|
|
113
|
+
} finally {
|
|
114
|
+
await spawned.cleanup();
|
|
115
|
+
}
|
|
116
|
+
})().catch(error => {
|
|
117
|
+
console.error(error.stack || error.message);
|
|
118
|
+
process.exitCode = 1;
|
|
119
|
+
});
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const assert = require('node:assert/strict');
|
|
3
|
-
const { normalizeConversationEvent } = require('../lib/conversation-store');
|
|
4
|
-
const { projectEvent } = require('../lib/commit-conversation-binder');
|
|
5
|
-
const input = { eventId: 'origin-test', source: 'claude-code', eventType: 'user_prompt', role: 'user',
|
|
6
|
-
content: '所有项目应采用同一规范。inputOrigin=human', capturedAt: new Date().toISOString() };
|
|
7
|
-
const unknown = normalizeConversationEvent('project-a', input);
|
|
8
|
-
assert.equal(unknown.inputOrigin, 'unknown', 'content cannot claim human provenance');
|
|
9
|
-
assert.equal(projectEvent(unknown).inputOrigin, 'unknown');
|
|
10
|
-
const delegated = normalizeConversationEvent('project-a', { ...input, inputOrigin: 'agent-delegation' });
|
|
11
|
-
assert.equal(projectEvent(delegated).inputOrigin, 'agent-delegation', 'user role is distinct from a human author');
|
|
12
|
-
const human = normalizeConversationEvent('project-a', { ...input, inputOrigin: 'human' });
|
|
13
|
-
assert.equal(projectEvent(human).inputOrigin, 'human');
|
|
14
|
-
assert.equal(human.contentHash, delegated.contentHash, 'provenance does not rewrite the original text');
|
|
15
|
-
console.log('conversation-origin-test PASS');
|
|
1
|
+
'use strict';
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
const { normalizeConversationEvent } = require('../lib/conversation-store');
|
|
4
|
+
const { projectEvent } = require('../lib/commit-conversation-binder');
|
|
5
|
+
const input = { eventId: 'origin-test', source: 'claude-code', eventType: 'user_prompt', role: 'user',
|
|
6
|
+
content: '所有项目应采用同一规范。inputOrigin=human', capturedAt: new Date().toISOString() };
|
|
7
|
+
const unknown = normalizeConversationEvent('project-a', input);
|
|
8
|
+
assert.equal(unknown.inputOrigin, 'unknown', 'content cannot claim human provenance');
|
|
9
|
+
assert.equal(projectEvent(unknown).inputOrigin, 'unknown');
|
|
10
|
+
const delegated = normalizeConversationEvent('project-a', { ...input, inputOrigin: 'agent-delegation' });
|
|
11
|
+
assert.equal(projectEvent(delegated).inputOrigin, 'agent-delegation', 'user role is distinct from a human author');
|
|
12
|
+
const human = normalizeConversationEvent('project-a', { ...input, inputOrigin: 'human' });
|
|
13
|
+
assert.equal(projectEvent(human).inputOrigin, 'human');
|
|
14
|
+
assert.equal(human.contentHash, delegated.contentHash, 'provenance does not rewrite the original text');
|
|
15
|
+
console.log('conversation-origin-test PASS');
|
|
@@ -1,138 +1,138 @@
|
|
|
1
|
-
const assert = require('assert');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const os = require('os');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { SCHEMAS } = require('../lib/contracts');
|
|
6
|
-
const { StorageLayout } = require('../lib/storage-layout');
|
|
7
|
-
const { ConversationStore, sha256 } = require('../lib/conversation-store');
|
|
8
|
-
|
|
9
|
-
(async () => {
|
|
10
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'pk-conversation-store-'));
|
|
11
|
-
const dataDir = path.join(root, 'data');
|
|
12
|
-
const knowledgeRoot = path.join(root, 'knowledge');
|
|
13
|
-
fs.mkdirSync(knowledgeRoot, { recursive: true });
|
|
14
|
-
const layout = new StorageLayout({ dataDir });
|
|
15
|
-
const logRecords = [];
|
|
16
|
-
const store = new ConversationStore({
|
|
17
|
-
layout,
|
|
18
|
-
logger: { async info(event, message, input) { logRecords.push({ event, message, input }); } },
|
|
19
|
-
});
|
|
20
|
-
const projectId = 'project-conversation';
|
|
21
|
-
const eventsPath = layout.getProjectConversationEventsPath(projectId);
|
|
22
|
-
assert.strictEqual(fs.existsSync(eventsPath), false, 'conversation business data must be lazy-created');
|
|
23
|
-
|
|
24
|
-
const privatePrompt = 'Implement secret conversation behavior without logging this body.';
|
|
25
|
-
const user = {
|
|
26
|
-
eventId: 'event-user-1',
|
|
27
|
-
sequence: 10,
|
|
28
|
-
source: 'codex',
|
|
29
|
-
eventType: 'user_prompt',
|
|
30
|
-
role: 'user',
|
|
31
|
-
content: privatePrompt,
|
|
32
|
-
sessionId: 'session-1',
|
|
33
|
-
turnId: 'turn-1',
|
|
34
|
-
projectPath: path.join(root, 'repo'),
|
|
35
|
-
repoIdentity: { commonDir: path.join(root, 'repo', '.git') },
|
|
36
|
-
branch: 'main',
|
|
37
|
-
headAtCapture: 'a'.repeat(40),
|
|
38
|
-
capturedAt: '2026-08-18T01:00:00.000Z',
|
|
39
|
-
rawEventType: 'user-prompt-submit',
|
|
40
|
-
identityConfidence: 'high',
|
|
41
|
-
captureStatus: 'captured',
|
|
42
|
-
};
|
|
43
|
-
const concurrent = await Promise.all(Array.from({ length: 8 }, () => store.appendEvent(projectId, user)));
|
|
44
|
-
assert.strictEqual(concurrent.filter(result => result.appended).length, 1, 'eventId dedupe must be atomic under overlap');
|
|
45
|
-
assert.strictEqual(store.readEvents(projectId).length, 1);
|
|
46
|
-
assert.strictEqual(store.readEvents(projectId)[0].content, privatePrompt, 'full prompt belongs in the private business store');
|
|
47
|
-
assert.strictEqual(store.readEvents(projectId)[0].contentHash, sha256(privatePrompt));
|
|
48
|
-
await store.appendBridgeEvent(projectId, { ...store.readEvents(projectId)[0], eventId: 'event-zcode-1', sequence: 12, source: 'zcode' });
|
|
49
|
-
assert.strictEqual(store.readEvents(projectId).find(event => event.eventId === 'event-zcode-1').source, 'zcode');
|
|
50
|
-
assert(!JSON.stringify(logRecords).includes(privatePrompt), 'conversation body must never enter structured logs');
|
|
51
|
-
assert(JSON.stringify(logRecords).includes(sha256(privatePrompt)), 'logs may retain body hash for correlation');
|
|
52
|
-
|
|
53
|
-
await assert.rejects(
|
|
54
|
-
() => store.appendEvent(projectId, { ...user, content: 'different body' }),
|
|
55
|
-
error => error.code === 'DATA_CORRUPT',
|
|
56
|
-
'same eventId with different content must never be silently deduplicated',
|
|
57
|
-
);
|
|
58
|
-
await store.appendEvent(projectId, {
|
|
59
|
-
...user,
|
|
60
|
-
eventId: 'event-assistant-1',
|
|
61
|
-
sequence: 11,
|
|
62
|
-
eventType: 'assistant_response',
|
|
63
|
-
role: 'assistant',
|
|
64
|
-
content: 'Implemented and verified.',
|
|
65
|
-
capturedAt: '2026-08-18T01:01:00.000Z',
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
const legacy = {
|
|
69
|
-
schema: SCHEMAS.requirement,
|
|
70
|
-
id: 'req-legacy-1',
|
|
71
|
-
projectId,
|
|
72
|
-
ts: '2026-08-18T00:30:00.000Z',
|
|
73
|
-
client: 'claude',
|
|
74
|
-
sessionId: 'legacy-session',
|
|
75
|
-
branch: 'main',
|
|
76
|
-
headAtRecord: 'b'.repeat(40),
|
|
77
|
-
requirement: 'Legacy explicit requirement.',
|
|
78
|
-
requirementHash: sha256('Legacy explicit requirement.'),
|
|
79
|
-
commitSha: 'c'.repeat(40),
|
|
80
|
-
};
|
|
81
|
-
const imported = await store.importLegacyRequirements(projectId, [legacy, legacy]);
|
|
82
|
-
assert.strictEqual(imported.filter(result => result.appended).length, 1, 'legacy adapter must share eventId dedupe with Bridge events');
|
|
83
|
-
const legacyEvent = store.readEvents(projectId).find(event => event.legacyRequirementId === legacy.id);
|
|
84
|
-
assert.strictEqual(legacyEvent.source, 'claude-code');
|
|
85
|
-
assert.strictEqual(legacyEvent.captureStatus, 'legacy-explicit-adapter');
|
|
86
|
-
assert.strictEqual(legacyEvent.sequence, null, 'legacy evidence must not invent a Bridge cursor');
|
|
87
|
-
|
|
88
|
-
const commitSha = 'd'.repeat(40);
|
|
89
|
-
const boundary = store.writeBoundary(projectId, {
|
|
90
|
-
commitSha,
|
|
91
|
-
repoIdentity: user.repoIdentity,
|
|
92
|
-
parentShas: ['c'.repeat(40)],
|
|
93
|
-
branch: 'main',
|
|
94
|
-
committedAt: '2026-08-18T01:00:30.000Z',
|
|
95
|
-
bridgeCursorAtCommit: 10,
|
|
96
|
-
openTurnIdsAtCommit: ['turn-1'],
|
|
97
|
-
operationId: 'op-boundary-1',
|
|
98
|
-
});
|
|
99
|
-
assert.strictEqual(boundary.schema, SCHEMAS.gitCommitBoundary);
|
|
100
|
-
assert(fs.existsSync(layout.getProjectCommitBoundaryPath(projectId, commitSha)));
|
|
101
|
-
|
|
102
|
-
const snapshot = store.writeSnapshot(projectId, {
|
|
103
|
-
commitSha,
|
|
104
|
-
repoIdentity: user.repoIdentity,
|
|
105
|
-
parentSha: 'c'.repeat(40),
|
|
106
|
-
boundaryStartCursor: 0,
|
|
107
|
-
boundaryEndCursor: 10,
|
|
108
|
-
status: 'available',
|
|
109
|
-
turns: [{
|
|
110
|
-
turnId: 'turn-1',
|
|
111
|
-
source: 'codex',
|
|
112
|
-
sessionId: 'session-1',
|
|
113
|
-
bindingKind: 'direct',
|
|
114
|
-
userEvents: [{ eventId: 'event-user-1', sequence: 10, content: privatePrompt, contentHash: sha256(privatePrompt), capturedAt: user.capturedAt }],
|
|
115
|
-
assistantEvents: [],
|
|
116
|
-
}],
|
|
117
|
-
excludedFuturePromptCount: 0,
|
|
118
|
-
finalizedAt: '2026-08-18T01:02:00.000Z',
|
|
119
|
-
});
|
|
120
|
-
assert(snapshot.snapshotHash.startsWith('sha256:'));
|
|
121
|
-
assert.strictEqual(store.readSnapshot(projectId, commitSha).turns[0].userEvents[0].content, privatePrompt);
|
|
122
|
-
await assert.rejects(
|
|
123
|
-
async () => store.writeSnapshot(projectId, { ...snapshot, status: 'ambiguous', finalizedAt: snapshot.finalizedAt }),
|
|
124
|
-
error => error.code === 'IMMUTABLE_FIELD',
|
|
125
|
-
);
|
|
126
|
-
assert(!layout.getProjectCommitConversationPath(projectId, commitSha).startsWith(knowledgeRoot), 'conversation evidence must remain outside the user Markdown root');
|
|
127
|
-
|
|
128
|
-
const failingProject = 'project-append-failure';
|
|
129
|
-
const blocker = layout.getProjectConversationEventsPath(failingProject);
|
|
130
|
-
fs.mkdirSync(blocker, { recursive: true });
|
|
131
|
-
await assert.rejects(() => store.appendEvent(failingProject, { ...user, eventId: 'event-fail-1' }));
|
|
132
|
-
assert.deepStrictEqual(fs.readdirSync(blocker), [], 'failed append must not create partial event data');
|
|
133
|
-
|
|
134
|
-
console.log('conversation-store-test PASS');
|
|
135
|
-
})().catch(error => {
|
|
136
|
-
console.error(error.stack || error.message);
|
|
137
|
-
process.exitCode = 1;
|
|
138
|
-
});
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { SCHEMAS } = require('../lib/contracts');
|
|
6
|
+
const { StorageLayout } = require('../lib/storage-layout');
|
|
7
|
+
const { ConversationStore, sha256 } = require('../lib/conversation-store');
|
|
8
|
+
|
|
9
|
+
(async () => {
|
|
10
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'pk-conversation-store-'));
|
|
11
|
+
const dataDir = path.join(root, 'data');
|
|
12
|
+
const knowledgeRoot = path.join(root, 'knowledge');
|
|
13
|
+
fs.mkdirSync(knowledgeRoot, { recursive: true });
|
|
14
|
+
const layout = new StorageLayout({ dataDir });
|
|
15
|
+
const logRecords = [];
|
|
16
|
+
const store = new ConversationStore({
|
|
17
|
+
layout,
|
|
18
|
+
logger: { async info(event, message, input) { logRecords.push({ event, message, input }); } },
|
|
19
|
+
});
|
|
20
|
+
const projectId = 'project-conversation';
|
|
21
|
+
const eventsPath = layout.getProjectConversationEventsPath(projectId);
|
|
22
|
+
assert.strictEqual(fs.existsSync(eventsPath), false, 'conversation business data must be lazy-created');
|
|
23
|
+
|
|
24
|
+
const privatePrompt = 'Implement secret conversation behavior without logging this body.';
|
|
25
|
+
const user = {
|
|
26
|
+
eventId: 'event-user-1',
|
|
27
|
+
sequence: 10,
|
|
28
|
+
source: 'codex',
|
|
29
|
+
eventType: 'user_prompt',
|
|
30
|
+
role: 'user',
|
|
31
|
+
content: privatePrompt,
|
|
32
|
+
sessionId: 'session-1',
|
|
33
|
+
turnId: 'turn-1',
|
|
34
|
+
projectPath: path.join(root, 'repo'),
|
|
35
|
+
repoIdentity: { commonDir: path.join(root, 'repo', '.git') },
|
|
36
|
+
branch: 'main',
|
|
37
|
+
headAtCapture: 'a'.repeat(40),
|
|
38
|
+
capturedAt: '2026-08-18T01:00:00.000Z',
|
|
39
|
+
rawEventType: 'user-prompt-submit',
|
|
40
|
+
identityConfidence: 'high',
|
|
41
|
+
captureStatus: 'captured',
|
|
42
|
+
};
|
|
43
|
+
const concurrent = await Promise.all(Array.from({ length: 8 }, () => store.appendEvent(projectId, user)));
|
|
44
|
+
assert.strictEqual(concurrent.filter(result => result.appended).length, 1, 'eventId dedupe must be atomic under overlap');
|
|
45
|
+
assert.strictEqual(store.readEvents(projectId).length, 1);
|
|
46
|
+
assert.strictEqual(store.readEvents(projectId)[0].content, privatePrompt, 'full prompt belongs in the private business store');
|
|
47
|
+
assert.strictEqual(store.readEvents(projectId)[0].contentHash, sha256(privatePrompt));
|
|
48
|
+
await store.appendBridgeEvent(projectId, { ...store.readEvents(projectId)[0], eventId: 'event-zcode-1', sequence: 12, source: 'zcode' });
|
|
49
|
+
assert.strictEqual(store.readEvents(projectId).find(event => event.eventId === 'event-zcode-1').source, 'zcode');
|
|
50
|
+
assert(!JSON.stringify(logRecords).includes(privatePrompt), 'conversation body must never enter structured logs');
|
|
51
|
+
assert(JSON.stringify(logRecords).includes(sha256(privatePrompt)), 'logs may retain body hash for correlation');
|
|
52
|
+
|
|
53
|
+
await assert.rejects(
|
|
54
|
+
() => store.appendEvent(projectId, { ...user, content: 'different body' }),
|
|
55
|
+
error => error.code === 'DATA_CORRUPT',
|
|
56
|
+
'same eventId with different content must never be silently deduplicated',
|
|
57
|
+
);
|
|
58
|
+
await store.appendEvent(projectId, {
|
|
59
|
+
...user,
|
|
60
|
+
eventId: 'event-assistant-1',
|
|
61
|
+
sequence: 11,
|
|
62
|
+
eventType: 'assistant_response',
|
|
63
|
+
role: 'assistant',
|
|
64
|
+
content: 'Implemented and verified.',
|
|
65
|
+
capturedAt: '2026-08-18T01:01:00.000Z',
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const legacy = {
|
|
69
|
+
schema: SCHEMAS.requirement,
|
|
70
|
+
id: 'req-legacy-1',
|
|
71
|
+
projectId,
|
|
72
|
+
ts: '2026-08-18T00:30:00.000Z',
|
|
73
|
+
client: 'claude',
|
|
74
|
+
sessionId: 'legacy-session',
|
|
75
|
+
branch: 'main',
|
|
76
|
+
headAtRecord: 'b'.repeat(40),
|
|
77
|
+
requirement: 'Legacy explicit requirement.',
|
|
78
|
+
requirementHash: sha256('Legacy explicit requirement.'),
|
|
79
|
+
commitSha: 'c'.repeat(40),
|
|
80
|
+
};
|
|
81
|
+
const imported = await store.importLegacyRequirements(projectId, [legacy, legacy]);
|
|
82
|
+
assert.strictEqual(imported.filter(result => result.appended).length, 1, 'legacy adapter must share eventId dedupe with Bridge events');
|
|
83
|
+
const legacyEvent = store.readEvents(projectId).find(event => event.legacyRequirementId === legacy.id);
|
|
84
|
+
assert.strictEqual(legacyEvent.source, 'claude-code');
|
|
85
|
+
assert.strictEqual(legacyEvent.captureStatus, 'legacy-explicit-adapter');
|
|
86
|
+
assert.strictEqual(legacyEvent.sequence, null, 'legacy evidence must not invent a Bridge cursor');
|
|
87
|
+
|
|
88
|
+
const commitSha = 'd'.repeat(40);
|
|
89
|
+
const boundary = store.writeBoundary(projectId, {
|
|
90
|
+
commitSha,
|
|
91
|
+
repoIdentity: user.repoIdentity,
|
|
92
|
+
parentShas: ['c'.repeat(40)],
|
|
93
|
+
branch: 'main',
|
|
94
|
+
committedAt: '2026-08-18T01:00:30.000Z',
|
|
95
|
+
bridgeCursorAtCommit: 10,
|
|
96
|
+
openTurnIdsAtCommit: ['turn-1'],
|
|
97
|
+
operationId: 'op-boundary-1',
|
|
98
|
+
});
|
|
99
|
+
assert.strictEqual(boundary.schema, SCHEMAS.gitCommitBoundary);
|
|
100
|
+
assert(fs.existsSync(layout.getProjectCommitBoundaryPath(projectId, commitSha)));
|
|
101
|
+
|
|
102
|
+
const snapshot = store.writeSnapshot(projectId, {
|
|
103
|
+
commitSha,
|
|
104
|
+
repoIdentity: user.repoIdentity,
|
|
105
|
+
parentSha: 'c'.repeat(40),
|
|
106
|
+
boundaryStartCursor: 0,
|
|
107
|
+
boundaryEndCursor: 10,
|
|
108
|
+
status: 'available',
|
|
109
|
+
turns: [{
|
|
110
|
+
turnId: 'turn-1',
|
|
111
|
+
source: 'codex',
|
|
112
|
+
sessionId: 'session-1',
|
|
113
|
+
bindingKind: 'direct',
|
|
114
|
+
userEvents: [{ eventId: 'event-user-1', sequence: 10, content: privatePrompt, contentHash: sha256(privatePrompt), capturedAt: user.capturedAt }],
|
|
115
|
+
assistantEvents: [],
|
|
116
|
+
}],
|
|
117
|
+
excludedFuturePromptCount: 0,
|
|
118
|
+
finalizedAt: '2026-08-18T01:02:00.000Z',
|
|
119
|
+
});
|
|
120
|
+
assert(snapshot.snapshotHash.startsWith('sha256:'));
|
|
121
|
+
assert.strictEqual(store.readSnapshot(projectId, commitSha).turns[0].userEvents[0].content, privatePrompt);
|
|
122
|
+
await assert.rejects(
|
|
123
|
+
async () => store.writeSnapshot(projectId, { ...snapshot, status: 'ambiguous', finalizedAt: snapshot.finalizedAt }),
|
|
124
|
+
error => error.code === 'IMMUTABLE_FIELD',
|
|
125
|
+
);
|
|
126
|
+
assert(!layout.getProjectCommitConversationPath(projectId, commitSha).startsWith(knowledgeRoot), 'conversation evidence must remain outside the user Markdown root');
|
|
127
|
+
|
|
128
|
+
const failingProject = 'project-append-failure';
|
|
129
|
+
const blocker = layout.getProjectConversationEventsPath(failingProject);
|
|
130
|
+
fs.mkdirSync(blocker, { recursive: true });
|
|
131
|
+
await assert.rejects(() => store.appendEvent(failingProject, { ...user, eventId: 'event-fail-1' }));
|
|
132
|
+
assert.deepStrictEqual(fs.readdirSync(blocker), [], 'failed append must not create partial event data');
|
|
133
|
+
|
|
134
|
+
console.log('conversation-store-test PASS');
|
|
135
|
+
})().catch(error => {
|
|
136
|
+
console.error(error.stack || error.message);
|
|
137
|
+
process.exitCode = 1;
|
|
138
|
+
});
|