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