@danceiny/gotry 0.0.1-rc.9 → 0.2.0-rc.28
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/README.md +172 -219
- package/README.zh-CN.md +222 -0
- package/bin/gotry-backend.js +53 -0
- package/bin/gotry-booking-copilot.js +53 -0
- package/bin/gotry-bootstrap.js +1788 -0
- package/bin/gotry-inner.js +733 -83
- package/bin/gotry-process-liveness.d.ts +27 -0
- package/bin/gotry-process-liveness.js +97 -0
- package/bin/gotry-runtime-resolution.d.ts +27 -0
- package/bin/gotry-runtime-resolution.js +49 -0
- package/bin/gotry.js +1 -1
- package/client/client.js +203 -0
- package/cordis.gotry-patch.yml +80 -56
- package/data/airline-airports.json +39 -0
- package/data/stations-12306-verify.json +524 -0
- package/dist/capabilities/agent-reach-deep.js +45 -122
- package/dist/capabilities/agent-reach.js +101 -146
- package/dist/capabilities/anything.js +161 -94
- package/dist/capabilities/artifacts.js +370 -0
- package/dist/capabilities/channel-health.js +131 -0
- package/dist/capabilities/channel-registry.js +256 -0
- package/dist/capabilities/doctor.js +553 -0
- package/dist/capabilities/effect.js +394 -0
- package/dist/capabilities/external-event.js +149 -0
- package/dist/capabilities/fact-log.js +55 -0
- package/dist/capabilities/flyai-config.js +337 -0
- package/dist/capabilities/flyai.js +947 -110
- package/dist/capabilities/fx-contract.js +293 -0
- package/dist/capabilities/geo-atlas.js +246 -0
- package/dist/capabilities/ground-transfer.js +676 -0
- package/dist/capabilities/hbcli.js +260 -123
- package/dist/capabilities/hotelbyte-transaction.js +194 -0
- package/dist/capabilities/incident-log.js +131 -61
- package/dist/capabilities/itinerary-artifact.js +190 -0
- package/dist/capabilities/itinerary-deck-artifact.js +194 -0
- package/dist/capabilities/itinerary-deck-export.js +339 -0
- package/dist/capabilities/lavish-local.js +924 -0
- package/dist/capabilities/model-override.js +38 -0
- package/dist/capabilities/opensky.js +54 -43
- package/dist/capabilities/provider-detail-sanitize.js +64 -0
- package/dist/capabilities/resilience.js +191 -0
- package/dist/capabilities/route-provider-conformance.js +341 -0
- package/dist/capabilities/session/action-cache.js +50 -44
- package/dist/capabilities/session/adapters/ctrip-flight.js +120 -62
- package/dist/capabilities/session/adapters/ctrip-hotel.js +222 -0
- package/dist/capabilities/session/adapters/dida-portal.js +274 -0
- package/dist/capabilities/session/adapters/meituan-local.js +35 -54
- package/dist/capabilities/session/adapters/rail-12306.js +342 -0
- package/dist/capabilities/session/benchmark.js +189 -0
- package/dist/capabilities/session/bridge-ledger.js +224 -0
- package/dist/capabilities/session/extension-bridge.js +636 -0
- package/dist/capabilities/session/extension-channel.js +109 -0
- package/dist/capabilities/session/extension-distribution.js +230 -0
- package/dist/capabilities/session/extract.js +31 -21
- package/dist/capabilities/session/golden-score.js +114 -0
- package/dist/capabilities/session/health-watch.js +215 -0
- package/dist/capabilities/session/read-guard.js +75 -24
- package/dist/capabilities/session/static-flight-golden.js +150 -0
- package/dist/capabilities/session/transport.js +97 -83
- package/dist/capabilities/session/wizard.js +117 -0
- package/dist/capabilities/session-consent.js +178 -0
- package/dist/capabilities/session-login.js +170 -0
- package/dist/capabilities/session-search.js +635 -44
- package/dist/capabilities/spawn-bounded.js +203 -0
- package/dist/capabilities/sponsor-plugin.js +341 -0
- package/dist/capabilities/visa-policy.js +140 -0
- package/dist/capabilities/weather.js +243 -138
- package/dist/data/session-golden-20.json +25 -0
- package/dist/data/sf-golden-manifest.json +103 -0
- package/dist/data/sf-static-routes.json +91 -0
- package/dist/scripts/action-cache-tests.js +24 -59
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +242 -0
- package/dist/scripts/agent-planning-turn-deadline-tests.js +185 -0
- package/dist/scripts/agent-reach-deep-tests.js +30 -40
- package/dist/scripts/agent-reach-error-tests.js +354 -0
- package/dist/scripts/agent-reach-tests.js +26 -18
- package/dist/scripts/agent-reach-wrapper-tests.js +50 -50
- package/dist/scripts/anything-path-measure-tests.js +133 -0
- package/dist/scripts/anything-path-measure.js +150 -0
- package/dist/scripts/anything-tests.js +250 -60
- package/dist/scripts/artifact-client-contract-tests.js +220 -0
- package/dist/scripts/artifacts-capability-tests.js +727 -0
- package/dist/scripts/async-collect.js +50 -11
- package/dist/scripts/benchmark-environment-bridge-e2e.js +741 -0
- package/dist/scripts/benchmark-environment-bridge-tests.js +1452 -0
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +126 -0
- package/dist/scripts/booking-copilot-availability-policy-tests.js +265 -0
- package/dist/scripts/booking-copilot-bin-proof-tests.js +66 -0
- package/dist/scripts/booking-copilot-cleanup-proof-helper.js +76 -0
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +144 -0
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +424 -0
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +2839 -0
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +309 -0
- package/dist/scripts/booking-copilot-dsh-readiness-proof.js +238 -0
- package/dist/scripts/booking-copilot-dsh-warmer-proof.js +126 -0
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +156 -0
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +70 -0
- package/dist/scripts/booking-copilot-intent-proof-tests.js +422 -0
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +184 -0
- package/dist/scripts/booking-copilot-planner-live-probe.js +95 -0
- package/dist/scripts/booking-copilot-proof-runtime.js +42 -0
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +210 -0
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +2349 -0
- package/dist/scripts/booking-copilot-server-proof-tests.js +784 -0
- package/dist/scripts/booking-copilot-startup-proof-tests.js +94 -0
- package/dist/scripts/booking-executor-tests.js +80 -0
- package/dist/scripts/booking-recovery-chain-tests.js +182 -0
- package/dist/scripts/booking-saga-tests.js +125 -0
- package/dist/scripts/booking-surface-contract-proof-tests.js +212 -0
- package/dist/scripts/bootstrap-tests.js +1549 -0
- package/dist/scripts/bridge-events-tests.js +239 -0
- package/dist/scripts/bridge-ledger-tests.js +428 -0
- package/dist/scripts/build-changelog.js +238 -0
- package/dist/scripts/build-metrics-report.js +331 -0
- package/dist/scripts/changelog-tests.js +118 -0
- package/dist/scripts/channel-probe-tests.js +103 -0
- package/dist/scripts/channel-probe.js +169 -0
- package/dist/scripts/channel-registry-tests.js +208 -0
- package/dist/scripts/city-scenario-tier-tests.js +234 -0
- package/dist/scripts/companion-tests.js +25 -70
- package/dist/scripts/dida-cookie-probe.js +87 -0
- package/dist/scripts/dida-runner-stop-tests.js +311 -0
- package/dist/scripts/dida-sw-probe.js +114 -0
- package/dist/scripts/diff-test.js +17 -12
- package/dist/scripts/doctor-tests.js +600 -0
- package/dist/scripts/drill-multiuser-ledger-tests.js +1076 -0
- package/dist/scripts/drill-sdk-descendant-cleanup-tests.js +606 -0
- package/dist/scripts/drill-w2a-sensor-tests.js +788 -0
- package/dist/scripts/dsh-artifact-e2e.js +279 -0
- package/dist/scripts/dsh-artifact-web-e2e.js +675 -0
- package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
- package/dist/scripts/dsh-llm-tests.js +437 -0
- package/dist/scripts/dsh-runtime-closure-tests.js +182 -0
- package/dist/scripts/dsh-runtime-closure.js +162 -0
- package/dist/scripts/dsh-session-v3-migration-proof.js +109 -0
- package/dist/scripts/dsh-subprocess-local-proof.js +150 -0
- package/dist/scripts/dsh-target-closure-proof.js +178 -0
- package/dist/scripts/effect-tests.js +539 -0
- package/dist/scripts/engine-run.js +1 -3
- package/dist/scripts/engine-tests.js +16 -10
- package/dist/scripts/evaluation-cadence-tests.js +174 -0
- package/dist/scripts/evaluation-contract-tests.js +268 -0
- package/dist/scripts/extension-distribution-cli.js +39 -0
- package/dist/scripts/extension-distribution-tests.js +443 -0
- package/dist/scripts/extension-tests.js +988 -0
- package/dist/scripts/external-event-tests.js +350 -0
- package/dist/scripts/fact-gate-tests.js +869 -0
- package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
- package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
- package/dist/scripts/flight-malformed-tests.js +159 -0
- package/dist/scripts/flyai-setup-tests.js +228 -0
- package/dist/scripts/flyai-setup-tool-tests.js +54 -0
- package/dist/scripts/flyai-tests.js +692 -0
- package/dist/scripts/flyai-tool-registration-contract-tests.js +91 -0
- package/dist/scripts/fx-contract-tests.js +201 -0
- package/dist/scripts/g5-guard-tests.js +150 -0
- package/dist/scripts/g5-guard.js +193 -0
- package/dist/scripts/gen-lunar-anchors.js +106 -0
- package/dist/scripts/geo-atlas-tests.js +283 -0
- package/dist/scripts/gotry-backend-kernel-tests.js +206 -0
- package/dist/scripts/gotry-try-demo-tests.js +79 -0
- package/dist/scripts/gotry-try-demo.js +49 -0
- package/dist/scripts/ground-transfer-tests.js +956 -0
- package/dist/scripts/hbcli-e2e-tests.js +159 -0
- package/dist/scripts/hbcli-live-optin-tests.js +96 -0
- package/dist/scripts/hbcli-release-contract-tests.js +122 -0
- package/dist/scripts/hbcli-tests.js +121 -69
- package/dist/scripts/health-watch-cli.js +73 -0
- package/dist/scripts/hotel-date-gate-tests.js +496 -0
- package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
- package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
- package/dist/scripts/i18n-tests.js +41 -0
- package/dist/scripts/incident-tests.js +281 -87
- package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
- package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
- package/dist/scripts/issue-271-liveness-tests.js +569 -0
- package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
- package/dist/scripts/issue-289-model-retry-tests.js +441 -0
- package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
- package/dist/scripts/issue-338-home-city-e2e.js +577 -0
- package/dist/scripts/issue343-real-entry-e2e.js +160 -0
- package/dist/scripts/issue436-persisted-routing-e2e.js +247 -0
- package/dist/scripts/itinerary-artifact-tests.js +368 -0
- package/dist/scripts/itinerary-deck-artifact-tests.js +416 -0
- package/dist/scripts/itinerary-deck-export-tests.js +552 -0
- package/dist/scripts/itinerary-deck-tests.js +435 -0
- package/dist/scripts/itinerary-html-tests.js +426 -0
- package/dist/scripts/journey-tests.js +22 -27
- package/dist/scripts/kernel-manifest-evidence.js +109 -0
- package/dist/scripts/kernel-manifest-gate.js +126 -0
- package/dist/scripts/kernel-manifest-tests.js +212 -0
- package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
- package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
- package/dist/scripts/kernel-manifest.js +369 -0
- package/dist/scripts/lavish-local-tests.js +1226 -0
- package/dist/scripts/lavish-product-tools-tests.js +537 -0
- package/dist/scripts/ledger-repair-apply-tests.js +316 -0
- package/dist/scripts/ledger-repair-plan-tests.js +375 -0
- package/dist/scripts/ledger-tests.js +382 -246
- package/dist/scripts/ledger-workflow-crash.js +10 -20
- package/dist/scripts/m3-cohort-tests.js +714 -0
- package/dist/scripts/m3-cohort.js +310 -0
- package/dist/scripts/managed-dsh-cleanup-diagnostic-proof.js +184 -0
- package/dist/scripts/managed-dsh-close-failure-proof.js +172 -0
- package/dist/scripts/managed-dsh-run-port-proof.js +153 -0
- package/dist/scripts/managed-dsh-terminal-proof.js +252 -0
- package/dist/scripts/map-tools-vendor-package-proof.js +365 -0
- package/dist/scripts/memory-capture-tests.js +242 -61
- package/dist/scripts/memory-decay-tests.js +18 -52
- package/dist/scripts/memory-lifecycle-tests.js +657 -0
- package/dist/scripts/memory-lifecycle.js +196 -0
- package/dist/scripts/memory-metrics.js +24 -12
- package/dist/scripts/memory-value-report.js +631 -0
- package/dist/scripts/memory-value-tests.js +328 -0
- package/dist/scripts/metrics-report-tests.js +151 -0
- package/dist/scripts/model-override-e2e.js +137 -0
- package/dist/scripts/nightly-evidence-tests.js +203 -0
- package/dist/scripts/nightly-evidence.js +303 -0
- package/dist/scripts/nudge-digest.js +39 -28
- package/dist/scripts/observability-tests.js +381 -0
- package/dist/scripts/onboarding-tests.js +161 -0
- package/dist/scripts/opensky-check.js +18 -13
- package/dist/scripts/opensky-tests.js +28 -28
- package/dist/scripts/outcome-projection-tests.js +427 -0
- package/dist/scripts/persona-sim-tests.js +603 -0
- package/dist/scripts/persona-sim.js +1081 -0
- package/dist/scripts/persona-surface-guard-tests.js +49 -0
- package/dist/scripts/pnpm-dsh-closure-proof.js +17 -0
- package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
- package/dist/scripts/price-drift-tests.js +340 -0
- package/dist/scripts/price-drift-watch.js +447 -0
- package/dist/scripts/probe-poi-tests.js +53 -45
- package/dist/scripts/product-metrics.js +483 -0
- package/dist/scripts/publish-preverify.js +110 -0
- package/dist/scripts/realtime-pricing-tests.js +76 -0
- package/dist/scripts/recall-tests.js +513 -0
- package/dist/scripts/replay-async.js +22 -18
- package/dist/scripts/replay-real.js +18 -18
- package/dist/scripts/replay.js +54 -82
- package/dist/scripts/route-provider-conformance-tests.js +391 -0
- package/dist/scripts/session-attach-diagnose.js +20 -20
- package/dist/scripts/session-attach-poc.js +34 -37
- package/dist/scripts/session-benchmark.js +180 -0
- package/dist/scripts/session-dida-live-e2e.js +199 -0
- package/dist/scripts/session-extract-tests.js +14 -35
- package/dist/scripts/session-link-tests.js +478 -0
- package/dist/scripts/session-login.js +27 -46
- package/dist/scripts/session-tests.js +796 -96
- package/dist/scripts/session-zone-crash.js +49 -0
- package/dist/scripts/session-zone-ledger-tests.js +432 -0
- package/dist/scripts/session-zone-observation-tests.js +492 -0
- package/dist/scripts/session-zone-observe.js +135 -0
- package/dist/scripts/session-zone-wiring-tests.js +566 -0
- package/dist/scripts/session-zones-tests.js +374 -0
- package/dist/scripts/sf-live-benchmark.js +367 -0
- package/dist/scripts/sf-live-challenge-stop-tests.js +453 -0
- package/dist/scripts/sf-live-cli-tests.js +92 -0
- package/dist/scripts/sf-live-options.js +46 -0
- package/dist/scripts/sf-manifest-tests.js +30 -0
- package/dist/scripts/sf-manifest.js +33 -0
- package/dist/scripts/sf-soft-score-tests.js +67 -0
- package/dist/scripts/sf-summary-tests.js +324 -0
- package/dist/scripts/sf-summary.js +525 -0
- package/dist/scripts/share-tests.js +432 -0
- package/dist/scripts/skeleton-check.js +16 -14
- package/dist/scripts/skeleton-integration-test.js +28 -11
- package/dist/scripts/skills-contract-tests.js +51 -46
- package/dist/scripts/smoke-session-gate-tests.js +21 -0
- package/dist/scripts/smoke.js +690 -246
- package/dist/scripts/spawn-bounded-abort-tests.js +244 -0
- package/dist/scripts/sponsor-reuse-tests.js +262 -0
- package/dist/scripts/state-cli-tests.js +226 -101
- package/dist/scripts/state-cli.js +565 -198
- package/dist/scripts/state-repair-cli.js +66 -0
- package/dist/scripts/state-repair-tests.js +140 -0
- package/dist/scripts/static-golden-tests.js +165 -0
- package/dist/scripts/time-eval-tests.js +212 -192
- package/dist/scripts/travel-timeline-tests.js +29 -93
- package/dist/scripts/turn-handoff-collect-tests.js +169 -0
- package/dist/scripts/turn-handoff-collect.js +161 -0
- package/dist/scripts/turn-policy-tests.js +55 -0
- package/dist/scripts/typed-contract-canary.js +176 -0
- package/dist/scripts/tz-resolver-sanity.js +50 -0
- package/dist/scripts/tz-resolver-tests.js +584 -0
- package/dist/scripts/unified-tests.js +60 -20
- package/dist/scripts/visa-policy-tests.js +62 -0
- package/dist/scripts/weather-tests.js +276 -41
- package/dist/scripts/wish-channel-gate-tests.js +54 -0
- package/dist/scripts/write-gate-tests.js +522 -0
- package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
- package/dist/scripts/z3-lifecycle-tests.js +170 -0
- package/dist/scripts/z3-race-repeat-tests.js +39 -0
- package/dist/scripts/z3-race-tests.js +46 -0
- package/dist/src/artifact-gate.js +829 -0
- package/dist/src/backend/kernel.js +91 -0
- package/dist/src/backend/modules/booking-copilot.js +92 -0
- package/dist/src/backend/modules/booking-executor.js +124 -0
- package/dist/src/backend/modules/session-search.js +551 -0
- package/dist/src/benchmark-agent-conformance.js +578 -0
- package/dist/src/benchmark-environment-bridge.js +515 -0
- package/dist/src/benchmark-headless-child-diagnostics.js +166 -0
- package/dist/src/benchmark-tool-isolation.js +151 -0
- package/dist/src/bookable-facts.js +533 -0
- package/dist/src/booking-saga.js +97 -0
- package/dist/src/booking-surface/availability-policy.js +697 -0
- package/dist/src/booking-surface/booking-intent.js +43 -0
- package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
- package/dist/src/booking-surface/canonical-schema.js +116 -0
- package/dist/src/booking-surface/contracts.js +75 -0
- package/dist/src/booking-surface/dsh-planner.js +1854 -0
- package/dist/src/booking-surface/dsh-plugin.js +378 -0
- package/dist/src/booking-surface/dsh-worker.js +98 -0
- package/dist/src/booking-surface/error-codes.js +231 -0
- package/dist/src/booking-surface/index.js +9 -0
- package/dist/src/booking-surface/managed-dsh-cleanup-diagnostic.js +97 -0
- package/dist/src/booking-surface/managed-dsh-run-port.js +244 -0
- package/dist/src/booking-surface/profile.js +24 -0
- package/dist/src/booking-surface/recovery-chain.js +367 -0
- package/dist/src/booking-surface/runtime.js +1968 -0
- package/dist/src/booking-surface/server.js +674 -0
- package/dist/src/booking-surface/startup.js +168 -0
- package/dist/src/booking-surface/validation.js +261 -0
- package/dist/src/bridge.js +20 -13
- package/dist/src/city-scenario-tier.js +378 -0
- package/dist/src/companions.js +48 -74
- package/dist/src/contracts.js +21 -43
- package/dist/src/dsh-llm.js +171 -115
- package/dist/src/engine.js +276 -193
- package/dist/src/evaluation-cadence.js +97 -0
- package/dist/src/evaluation-contracts.js +283 -0
- package/dist/src/flight-pack-adapter.js +54 -0
- package/dist/src/flight-pack-contract.js +2 -0
- package/dist/src/flyai-setup-tool.js +70 -0
- package/dist/src/gotry-backend.js +125 -0
- package/dist/src/hotel-date-gate.js +118 -0
- package/dist/src/i18n.js +88 -0
- package/dist/src/index.js +2385 -881
- package/dist/src/itinerary-deck.js +139 -0
- package/dist/src/itinerary-doc-shared.js +661 -0
- package/dist/src/itinerary-html.js +131 -0
- package/dist/src/journey.js +155 -108
- package/dist/src/lavish-tools.js +400 -0
- package/dist/src/ledger-repair-apply.js +538 -0
- package/dist/src/ledger-repair-plan.js +463 -0
- package/dist/src/loop.js +331 -161
- package/dist/src/m3-cohort.js +1577 -0
- package/dist/src/memory-capture.js +180 -32
- package/dist/src/memory-decay.js +30 -14
- package/dist/src/memory-lifecycle.js +1310 -0
- package/dist/src/memory-utility.js +27 -40
- package/dist/src/mock-llm.js +36 -61
- package/dist/src/model.js +81 -36
- package/dist/src/outcome-projection.js +746 -0
- package/dist/src/realtime-pricing.js +103 -0
- package/dist/src/recall/card.js +74 -0
- package/dist/src/recall/evaluator.js +140 -0
- package/dist/src/recall/integration.js +17 -0
- package/dist/src/recall/tick.js +166 -0
- package/dist/src/session-link/plan-it-action.js +48 -0
- package/dist/src/session-link/session-link.js +248 -0
- package/dist/src/session-zone-ledger.js +312 -0
- package/dist/src/session-zone-observation.js +354 -0
- package/dist/src/session-zone-wiring.js +658 -0
- package/dist/src/session-zones.js +510 -0
- package/dist/src/share/adapters.js +51 -0
- package/dist/src/share/share-consent.js +76 -0
- package/dist/src/share/share-deck.js +58 -0
- package/dist/src/share/share-token.js +146 -0
- package/dist/src/slot-spec.js +84 -106
- package/dist/src/state-ledger.js +696 -524
- package/dist/src/state-repair.js +299 -0
- package/dist/src/subagent-job-id-guard.js +58 -0
- package/dist/src/time-anchor.js +231 -33
- package/dist/src/tool-packet.js +20 -8
- package/dist/src/travel-slots.js +58 -35
- package/dist/src/travel-timeline.js +44 -63
- package/dist/src/turn-deadline.js +287 -0
- package/dist/src/turn-policy.js +118 -0
- package/dist/src/tz-resolver.js +143 -0
- package/dist/src/unified.js +600 -360
- package/dist/src/wish-pool.js +42 -12
- package/dist/src/write-gate.js +495 -0
- package/dist/src/z3-shared.js +284 -0
- package/extension/README.md +21 -0
- package/extension/README.zh-CN.md +21 -0
- package/extension/background.js +463 -0
- package/extension/content-bridge.js +270 -0
- package/extension/content-main.js +111 -0
- package/extension/manifest.json +60 -0
- package/package.json +407 -14
- package/schemas/booking.intent.schema.json +193 -0
- package/schemas/booking.surface.schema.json +2622 -0
- package/ts/capabilities/agent-reach-deep.ts +114 -0
- package/ts/capabilities/agent-reach.ts +198 -0
- package/ts/capabilities/anything.ts +282 -0
- package/ts/capabilities/artifacts.ts +428 -0
- package/ts/capabilities/fact-log.ts +54 -0
- package/ts/capabilities/flyai-config.ts +388 -0
- package/ts/capabilities/flyai.ts +1274 -0
- package/ts/capabilities/hbcli.ts +253 -71
- package/ts/capabilities/incident-log.ts +88 -59
- package/ts/capabilities/itinerary-artifact.ts +214 -0
- package/ts/capabilities/itinerary-deck-artifact.ts +218 -0
- package/ts/capabilities/itinerary-deck-export.ts +378 -0
- package/ts/capabilities/opensky.ts +121 -0
- package/ts/capabilities/session/action-cache.ts +148 -0
- package/ts/capabilities/session/adapters/ctrip-flight.ts +181 -0
- package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
- package/ts/capabilities/session/adapters/dida-portal.ts +338 -0
- package/ts/capabilities/session/adapters/meituan-local.ts +70 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +392 -0
- package/ts/capabilities/session/benchmark.ts +276 -0
- package/ts/capabilities/session/bridge-ledger.ts +333 -0
- package/ts/capabilities/session/extension-bridge.ts +794 -0
- package/ts/capabilities/session/extension-channel.ts +144 -0
- package/ts/capabilities/session/extension-distribution.ts +266 -0
- package/ts/capabilities/session/extract.ts +54 -0
- package/ts/capabilities/session/golden-score.ts +139 -0
- package/ts/capabilities/session/health-watch.ts +264 -0
- package/ts/capabilities/session/read-guard.ts +141 -0
- package/ts/capabilities/session/static-flight-golden.ts +209 -0
- package/ts/capabilities/session/transport.ts +146 -0
- package/ts/capabilities/session/wizard.ts +140 -0
- package/ts/capabilities/session-consent.ts +211 -0
- package/ts/capabilities/session-login.ts +208 -0
- package/ts/capabilities/session-search.ts +893 -0
- package/ts/capabilities/weather.ts +330 -0
- package/ts/dsh-runtime/vendor/README.md +68 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +405 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +404 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +1584 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/icon.svg +5 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +595 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +58 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +131 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/geo.js +232 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +120 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +40 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/staticmap-route.js +115 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +267 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +90 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +23 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/geo.d.ts +61 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/staticmap-route.d.ts +31 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +75 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +68 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +97 -0
- package/ts/package.json +293 -3
- package/ts/scripts/async-collect.ts +91 -0
- package/ts/scripts/map-tools-vendor-package-proof.ts +404 -0
- package/ts/scripts/skeleton-check.ts +1 -1
- package/ts/scripts/skeleton-integration-test.ts +23 -1
- package/ts/scripts/state-cli.ts +586 -0
- package/ts/scripts/turn-handoff-collect.ts +178 -0
- package/ts/src/artifact-gate.ts +991 -0
- package/ts/src/benchmark-agent-conformance.ts +619 -0
- package/ts/src/benchmark-environment-bridge.ts +538 -0
- package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
- package/ts/src/benchmark-tool-isolation.ts +166 -0
- package/ts/src/bookable-facts.ts +837 -0
- package/ts/src/booking-saga.ts +133 -0
- package/ts/src/booking-surface/availability-policy.ts +522 -0
- package/ts/src/booking-surface/booking-intent.ts +86 -0
- package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
- package/ts/src/booking-surface/canonical-schema.js +116 -0
- package/ts/src/booking-surface/contracts.ts +475 -0
- package/ts/src/booking-surface/dsh-planner.ts +1949 -0
- package/ts/src/booking-surface/dsh-plugin.js +378 -0
- package/ts/src/booking-surface/dsh-worker.ts +110 -0
- package/ts/src/booking-surface/error-codes.ts +241 -0
- package/ts/src/booking-surface/index.ts +9 -0
- package/ts/src/booking-surface/managed-dsh-cleanup-diagnostic.ts +151 -0
- package/ts/src/booking-surface/managed-dsh-run-port.ts +273 -0
- package/ts/src/booking-surface/profile.ts +42 -0
- package/ts/src/booking-surface/recovery-chain.ts +409 -0
- package/ts/src/booking-surface/runtime.ts +1793 -0
- package/ts/src/booking-surface/server.ts +733 -0
- package/ts/src/booking-surface/startup.ts +201 -0
- package/ts/src/booking-surface/validation.ts +217 -0
- package/ts/src/companions.ts +113 -0
- package/ts/src/contracts.ts +17 -3
- package/ts/src/dsh-llm.ts +152 -17
- package/ts/src/flyai-setup-tool.ts +77 -0
- package/ts/src/index.ts +2123 -200
- package/ts/src/itinerary-deck.ts +189 -0
- package/ts/src/itinerary-doc-shared.ts +741 -0
- package/ts/src/itinerary-html.ts +171 -0
- package/ts/src/loop.ts +234 -26
- package/ts/src/memory-capture.ts +283 -0
- package/ts/src/memory-decay.ts +55 -0
- package/ts/src/memory-utility.ts +70 -0
- package/ts/src/mock-llm.ts +4 -8
- package/ts/src/model.ts +88 -2
- package/ts/src/recall/card.ts +106 -0
- package/ts/src/recall/evaluator.ts +170 -0
- package/ts/src/recall/integration.ts +35 -0
- package/ts/src/recall/tick.ts +213 -0
- package/ts/src/session-link/plan-it-action.ts +78 -0
- package/ts/src/session-link/session-link.ts +275 -0
- package/ts/src/share/adapters.ts +93 -0
- package/ts/src/share/share-consent.ts +105 -0
- package/ts/src/share/share-deck.ts +85 -0
- package/ts/src/share/share-token.ts +166 -0
- package/ts/src/slot-spec.ts +172 -0
- package/ts/src/state-ledger.ts +1166 -0
- package/ts/src/subagent-job-id-guard.ts +72 -0
- package/ts/src/time-anchor.ts +338 -0
- package/ts/src/tool-packet.ts +32 -0
- package/ts/src/travel-slots.ts +219 -0
- package/ts/src/travel-timeline.ts +83 -0
- package/ts/src/turn-deadline.ts +366 -0
- package/ts/src/turn-policy.ts +154 -0
- package/ts/src/unified.ts +444 -105
- package/ts/src/wish-pool.ts +79 -0
- package/dist/scripts/session-attach-wait.js +0 -41
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { parsePnpmDshLock, parsePnpmRootDshImporter, REQUIRED_DSH_RUNTIME_PACKAGE_COUNT, validateDshRuntimeClosure, validatePnpmRootDshImporter, } from './dsh-runtime-closure.js';
|
|
4
|
+
const VERSION = '0.2.0-rc.2';
|
|
5
|
+
function validInput() {
|
|
6
|
+
return {
|
|
7
|
+
dependencies: {
|
|
8
|
+
'@deepseek-ai/dsh': VERSION,
|
|
9
|
+
'@deepseek-ai/dsh-session-title': VERSION,
|
|
10
|
+
},
|
|
11
|
+
lockPackages: {
|
|
12
|
+
'node_modules/@deepseek-ai/dsh': { version: VERSION },
|
|
13
|
+
'node_modules/@deepseek-ai/dsh-session-title': { version: VERSION },
|
|
14
|
+
},
|
|
15
|
+
runtimeVersion: VERSION,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
test('accepts an exact coherent DSH prerelease closure', () => {
|
|
19
|
+
assert.deepEqual(validateDshRuntimeClosure(validInput()), {
|
|
20
|
+
names: ['@deepseek-ai/dsh', '@deepseek-ai/dsh-session-title'],
|
|
21
|
+
version: VERSION,
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
test('rejects a lock package missing from the published direct dependencies', () => {
|
|
25
|
+
const input = validInput();
|
|
26
|
+
input.dependencies = { '@deepseek-ai/dsh': VERSION };
|
|
27
|
+
assert.throws(() => validateDshRuntimeClosure(input), /DSH closure 未精确声明/);
|
|
28
|
+
});
|
|
29
|
+
test('rejects an extra ranged DSH dependency absent from the tested lock closure', () => {
|
|
30
|
+
const input = validInput();
|
|
31
|
+
input.dependencies['@deepseek-ai/dsh-web'] = '^0.2.0-rc.2';
|
|
32
|
+
assert.throws(() => validateDshRuntimeClosure(input), /DSH manifest 与 lock 集合不一致/);
|
|
33
|
+
});
|
|
34
|
+
test('rejects a nested DSH package from another prerelease', () => {
|
|
35
|
+
const input = validInput();
|
|
36
|
+
input.lockPackages['node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/dsh-session-title'] = {
|
|
37
|
+
version: '0.1.5-alpha.2',
|
|
38
|
+
};
|
|
39
|
+
assert.throws(() => validateDshRuntimeClosure(input), /DSH lock 版本漂移/);
|
|
40
|
+
});
|
|
41
|
+
test('rejects drift from the runtime spawn guard version', () => {
|
|
42
|
+
const input = validInput();
|
|
43
|
+
input.runtimeVersion = '0.1.5-alpha.2';
|
|
44
|
+
assert.throws(() => validateDshRuntimeClosure(input), /DSH runtime guard 版本漂移/);
|
|
45
|
+
});
|
|
46
|
+
test('parses each pnpm DSH resolution once across packages and snapshots', () => {
|
|
47
|
+
const lock = `
|
|
48
|
+
packages:
|
|
49
|
+
'@deepseek-ai/dsh@0.2.0-rc.2': {}
|
|
50
|
+
'@deepseek-ai/dsh-session@0.2.0-rc.2': {}
|
|
51
|
+
snapshots:
|
|
52
|
+
'@deepseek-ai/dsh@0.2.0-rc.2': {}
|
|
53
|
+
`;
|
|
54
|
+
assert.deepEqual(parsePnpmDshLock(lock), {
|
|
55
|
+
'node_modules/@deepseek-ai/dsh': { version: VERSION },
|
|
56
|
+
'node_modules/@deepseek-ai/dsh-session': { version: VERSION },
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
test('preserves two pnpm resolutions of the same DSH package for mixed-version rejection', () => {
|
|
60
|
+
const lock = `
|
|
61
|
+
packages:
|
|
62
|
+
'@deepseek-ai/dsh@0.2.0-rc.2': {}
|
|
63
|
+
'@deepseek-ai/dsh-session@0.2.0-rc.2': {}
|
|
64
|
+
'@deepseek-ai/dsh-session@0.1.5-alpha.2': {}
|
|
65
|
+
`;
|
|
66
|
+
const parsed = parsePnpmDshLock(lock);
|
|
67
|
+
assert.equal(Object.keys(parsed).length, 3);
|
|
68
|
+
assert.throws(() => validateDshRuntimeClosure({
|
|
69
|
+
dependencies: {
|
|
70
|
+
'@deepseek-ai/dsh': VERSION,
|
|
71
|
+
'@deepseek-ai/dsh-session': VERSION,
|
|
72
|
+
},
|
|
73
|
+
lockPackages: parsed,
|
|
74
|
+
runtimeVersion: VERSION,
|
|
75
|
+
}), /DSH lock 版本漂移/);
|
|
76
|
+
});
|
|
77
|
+
test('parses only the root pnpm importer DSH entries', () => {
|
|
78
|
+
const lock = `
|
|
79
|
+
lockfileVersion: '9.0'
|
|
80
|
+
importers:
|
|
81
|
+
.:
|
|
82
|
+
dependencies:
|
|
83
|
+
'@deepseek-ai/dsh':
|
|
84
|
+
specifier: 0.2.0-rc.2
|
|
85
|
+
version: 0.2.0-rc.2(peer@1.0.0)
|
|
86
|
+
'@deepseek-ai/dsh-session':
|
|
87
|
+
specifier: 0.2.0-rc.2
|
|
88
|
+
version: 0.2.0-rc.2
|
|
89
|
+
ts:
|
|
90
|
+
dependencies:
|
|
91
|
+
'@deepseek-ai/dsh-web':
|
|
92
|
+
specifier: 0.1.5-alpha.2
|
|
93
|
+
version: 0.1.5-alpha.2
|
|
94
|
+
packages: {}
|
|
95
|
+
`;
|
|
96
|
+
assert.deepEqual(parsePnpmRootDshImporter(lock), {
|
|
97
|
+
'@deepseek-ai/dsh': { specifier: VERSION, resolvedVersion: VERSION },
|
|
98
|
+
'@deepseek-ai/dsh-session': { specifier: VERSION, resolvedVersion: VERSION },
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
test('rejects a pnpm root importer missing a published DSH dependency', () => {
|
|
102
|
+
assert.throws(() => validatePnpmRootDshImporter({
|
|
103
|
+
dependencies: validInput().dependencies,
|
|
104
|
+
importerEntries: {
|
|
105
|
+
'@deepseek-ai/dsh': { specifier: VERSION, resolvedVersion: VERSION },
|
|
106
|
+
},
|
|
107
|
+
runtimeVersion: VERSION,
|
|
108
|
+
}), /pnpm root importer 与 manifest 集合不一致/);
|
|
109
|
+
});
|
|
110
|
+
test('rejects a ranged DSH specifier in the pnpm root importer', () => {
|
|
111
|
+
assert.throws(() => validatePnpmRootDshImporter({
|
|
112
|
+
dependencies: validInput().dependencies,
|
|
113
|
+
importerEntries: {
|
|
114
|
+
'@deepseek-ai/dsh': { specifier: VERSION, resolvedVersion: VERSION },
|
|
115
|
+
'@deepseek-ai/dsh-session-title': { specifier: '^0.2.0-rc.2', resolvedVersion: VERSION },
|
|
116
|
+
},
|
|
117
|
+
runtimeVersion: VERSION,
|
|
118
|
+
}), /pnpm root importer 未精确锁定/);
|
|
119
|
+
});
|
|
120
|
+
test('rejects a drifted resolved DSH version in the pnpm root importer', () => {
|
|
121
|
+
assert.throws(() => validatePnpmRootDshImporter({
|
|
122
|
+
dependencies: validInput().dependencies,
|
|
123
|
+
importerEntries: {
|
|
124
|
+
'@deepseek-ai/dsh': { specifier: VERSION, resolvedVersion: '0.1.5-alpha.2' },
|
|
125
|
+
'@deepseek-ai/dsh-session-title': { specifier: VERSION, resolvedVersion: VERSION },
|
|
126
|
+
},
|
|
127
|
+
runtimeVersion: VERSION,
|
|
128
|
+
}), /pnpm root importer resolved 版本漂移/);
|
|
129
|
+
});
|
|
130
|
+
test('rejects a missing resolved DSH version in the pnpm root importer', () => {
|
|
131
|
+
const lock = `
|
|
132
|
+
importers:
|
|
133
|
+
.:
|
|
134
|
+
dependencies:
|
|
135
|
+
'@deepseek-ai/dsh':
|
|
136
|
+
specifier: 0.2.0-rc.2
|
|
137
|
+
`;
|
|
138
|
+
assert.throws(() => parsePnpmRootDshImporter(lock), /pnpm root importer version 不可用/);
|
|
139
|
+
});
|
|
140
|
+
test('rejects duplicate resolved DSH versions in the pnpm root importer', () => {
|
|
141
|
+
const lock = `
|
|
142
|
+
importers:
|
|
143
|
+
.:
|
|
144
|
+
dependencies:
|
|
145
|
+
'@deepseek-ai/dsh':
|
|
146
|
+
specifier: 0.2.0-rc.2
|
|
147
|
+
version: 0.2.0-rc.2
|
|
148
|
+
version: 0.1.5-alpha.2
|
|
149
|
+
`;
|
|
150
|
+
assert.throws(() => parsePnpmRootDshImporter(lock), /pnpm root importer version 不可用/);
|
|
151
|
+
});
|
|
152
|
+
test('rejects a coherent closure below the required package count', () => {
|
|
153
|
+
assert.throws(() => validateDshRuntimeClosure({
|
|
154
|
+
...validInput(),
|
|
155
|
+
expectedPackageCount: REQUIRED_DSH_RUNTIME_PACKAGE_COUNT,
|
|
156
|
+
}), /DSH closure 包数量不符/);
|
|
157
|
+
});
|
|
158
|
+
test('rejects a coherent root importer below the required package count', () => {
|
|
159
|
+
assert.throws(() => validatePnpmRootDshImporter({
|
|
160
|
+
dependencies: validInput().dependencies,
|
|
161
|
+
importerEntries: {
|
|
162
|
+
'@deepseek-ai/dsh': { specifier: VERSION, resolvedVersion: VERSION },
|
|
163
|
+
'@deepseek-ai/dsh-session-title': { specifier: VERSION, resolvedVersion: VERSION },
|
|
164
|
+
},
|
|
165
|
+
runtimeVersion: VERSION,
|
|
166
|
+
expectedPackageCount: REQUIRED_DSH_RUNTIME_PACKAGE_COUNT,
|
|
167
|
+
}), /pnpm root importer 包数量不符/);
|
|
168
|
+
});
|
|
169
|
+
test('rejects duplicate DSH entries in the pnpm root importer', () => {
|
|
170
|
+
const lock = `
|
|
171
|
+
importers:
|
|
172
|
+
.:
|
|
173
|
+
dependencies:
|
|
174
|
+
'@deepseek-ai/dsh':
|
|
175
|
+
specifier: 0.2.0-rc.2
|
|
176
|
+
version: 0.2.0-rc.2
|
|
177
|
+
'@deepseek-ai/dsh':
|
|
178
|
+
specifier: 0.2.0-rc.2
|
|
179
|
+
version: 0.2.0-rc.2
|
|
180
|
+
`;
|
|
181
|
+
assert.throws(() => parsePnpmRootDshImporter(lock), /pnpm root importer DSH 依赖重复/);
|
|
182
|
+
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
export const REQUIRED_DSH_RUNTIME_PACKAGE_COUNT = 279;
|
|
2
|
+
function yamlScalar(value) {
|
|
3
|
+
const trimmed = value.trim().replace(/\s+#.*$/, '');
|
|
4
|
+
const quoted = trimmed.match(/^(['"])(.*)\1$/);
|
|
5
|
+
return quoted?.[2] ?? trimmed;
|
|
6
|
+
}
|
|
7
|
+
function indentation(line) {
|
|
8
|
+
return line.match(/^ */)?.[0].length ?? 0;
|
|
9
|
+
}
|
|
10
|
+
function resolvedPnpmVersion(value) {
|
|
11
|
+
return yamlScalar(value).replace(/\(.+$/, '');
|
|
12
|
+
}
|
|
13
|
+
export function parsePnpmRootDshImporter(text) {
|
|
14
|
+
const lines = text.split(/\r?\n/);
|
|
15
|
+
const importers = lines
|
|
16
|
+
.map((line, index) => line === 'importers:' ? index : -1)
|
|
17
|
+
.filter((index) => index >= 0);
|
|
18
|
+
if (importers.length !== 1)
|
|
19
|
+
throw new Error('pnpm lock importers 区块不可用');
|
|
20
|
+
const importersStart = importers[0];
|
|
21
|
+
const importersEnd = lines.findIndex((line, index) => index > importersStart && line.trim() !== '' && indentation(line) === 0);
|
|
22
|
+
const rootCandidates = lines
|
|
23
|
+
.map((line, index) => index > importersStart && (importersEnd < 0 || index < importersEnd)
|
|
24
|
+
&& /^ {2}(?:\.|'\.'|"\."):\s*$/.test(line) ? index : -1)
|
|
25
|
+
.filter((index) => index >= 0);
|
|
26
|
+
if (rootCandidates.length !== 1)
|
|
27
|
+
throw new Error('pnpm root importer 不唯一');
|
|
28
|
+
const rootStart = rootCandidates[0];
|
|
29
|
+
const rootEndRelative = lines.slice(rootStart + 1, importersEnd < 0 ? lines.length : importersEnd)
|
|
30
|
+
.findIndex((line) => line.trim() !== '' && indentation(line) <= 2);
|
|
31
|
+
const rootEnd = rootEndRelative < 0
|
|
32
|
+
? (importersEnd < 0 ? lines.length : importersEnd)
|
|
33
|
+
: rootStart + 1 + rootEndRelative;
|
|
34
|
+
const dependencyBlocks = lines
|
|
35
|
+
.map((line, index) => index > rootStart && index < rootEnd && /^ {4}dependencies:\s*$/.test(line) ? index : -1)
|
|
36
|
+
.filter((index) => index >= 0);
|
|
37
|
+
if (dependencyBlocks.length !== 1)
|
|
38
|
+
throw new Error('pnpm root importer dependencies 区块不可用');
|
|
39
|
+
const dependenciesStart = dependencyBlocks[0];
|
|
40
|
+
const dependenciesEndRelative = lines.slice(dependenciesStart + 1, rootEnd)
|
|
41
|
+
.findIndex((line) => line.trim() !== '' && indentation(line) <= 4);
|
|
42
|
+
const dependenciesEnd = dependenciesEndRelative < 0 ? rootEnd : dependenciesStart + 1 + dependenciesEndRelative;
|
|
43
|
+
const entries = lines
|
|
44
|
+
.map((line, index) => {
|
|
45
|
+
if (index <= dependenciesStart || index >= dependenciesEnd)
|
|
46
|
+
return null;
|
|
47
|
+
const match = line.match(/^ {6}(['"]?)([^'":\s]+)\1:\s*$/);
|
|
48
|
+
return match ? { index, name: match[2] } : null;
|
|
49
|
+
})
|
|
50
|
+
.filter((entry) => entry !== null);
|
|
51
|
+
const importerEntries = {};
|
|
52
|
+
for (let entryIndex = 0; entryIndex < entries.length; entryIndex += 1) {
|
|
53
|
+
const entry = entries[entryIndex];
|
|
54
|
+
if (entry.name !== '@deepseek-ai/dsh' && !entry.name.startsWith('@deepseek-ai/dsh-'))
|
|
55
|
+
continue;
|
|
56
|
+
if (Object.hasOwn(importerEntries, entry.name))
|
|
57
|
+
throw new Error(`pnpm root importer DSH 依赖重复:${entry.name}`);
|
|
58
|
+
const end = entries[entryIndex + 1]?.index ?? dependenciesEnd;
|
|
59
|
+
const block = lines.slice(entry.index + 1, end);
|
|
60
|
+
const specifiers = block
|
|
61
|
+
.map((line) => line.match(/^ {8}specifier:\s*(.+)$/)?.[1])
|
|
62
|
+
.filter((value) => value !== undefined);
|
|
63
|
+
if (specifiers.length !== 1)
|
|
64
|
+
throw new Error(`pnpm root importer specifier 不可用:${entry.name}`);
|
|
65
|
+
const versions = block
|
|
66
|
+
.map((line) => line.match(/^ {8}version:\s*(.+)$/)?.[1])
|
|
67
|
+
.filter((value) => value !== undefined);
|
|
68
|
+
if (versions.length !== 1)
|
|
69
|
+
throw new Error(`pnpm root importer version 不可用:${entry.name}`);
|
|
70
|
+
importerEntries[entry.name] = {
|
|
71
|
+
specifier: yamlScalar(specifiers[0]),
|
|
72
|
+
resolvedVersion: resolvedPnpmVersion(versions[0]),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return importerEntries;
|
|
76
|
+
}
|
|
77
|
+
export function validatePnpmRootDshImporter(input) {
|
|
78
|
+
const manifestNames = Object.keys(input.dependencies)
|
|
79
|
+
.filter((name) => name === '@deepseek-ai/dsh' || name.startsWith('@deepseek-ai/dsh-'))
|
|
80
|
+
.sort();
|
|
81
|
+
const importerNames = Object.keys(input.importerEntries).sort();
|
|
82
|
+
const missing = manifestNames.filter((name) => !importerNames.includes(name));
|
|
83
|
+
const extra = importerNames.filter((name) => !manifestNames.includes(name));
|
|
84
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
85
|
+
throw new Error(`pnpm root importer 与 manifest 集合不一致:missing=${missing.join(',') || '-'};extra=${extra.join(',') || '-'}`);
|
|
86
|
+
}
|
|
87
|
+
for (const name of importerNames) {
|
|
88
|
+
const importerEntry = input.importerEntries[name];
|
|
89
|
+
if (input.dependencies[name] !== input.runtimeVersion || importerEntry.specifier !== input.runtimeVersion) {
|
|
90
|
+
throw new Error(`pnpm root importer 未精确锁定:${name}@${input.runtimeVersion}`);
|
|
91
|
+
}
|
|
92
|
+
if (importerEntry.resolvedVersion !== input.runtimeVersion) {
|
|
93
|
+
throw new Error(`pnpm root importer resolved 版本漂移:${name}@${importerEntry.resolvedVersion}(expected ${input.runtimeVersion})`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (input.expectedPackageCount !== undefined && importerNames.length !== input.expectedPackageCount) {
|
|
97
|
+
throw new Error(`pnpm root importer 包数量不符:${importerNames.length}(expected ${input.expectedPackageCount})`);
|
|
98
|
+
}
|
|
99
|
+
return { names: importerNames, version: input.runtimeVersion };
|
|
100
|
+
}
|
|
101
|
+
export function parsePnpmDshLock(text) {
|
|
102
|
+
const packages = {};
|
|
103
|
+
const seen = new Set();
|
|
104
|
+
const variants = new Map();
|
|
105
|
+
const entry = /^\s{2}['"]?(\@deepseek-ai\/dsh(?:-[^@'":\s]+)?)@([^'":\s(]+)(?:\([^'"]*\))?['"]?:/gm;
|
|
106
|
+
for (const match of text.matchAll(entry)) {
|
|
107
|
+
const name = match[1];
|
|
108
|
+
const version = match[2];
|
|
109
|
+
const identity = `${name}@${version}`;
|
|
110
|
+
if (seen.has(identity))
|
|
111
|
+
continue;
|
|
112
|
+
seen.add(identity);
|
|
113
|
+
const variant = variants.get(name) ?? 0;
|
|
114
|
+
variants.set(name, variant + 1);
|
|
115
|
+
const prefix = variant === 0 ? '' : `.pnpm-variant-${variant}/node_modules/`;
|
|
116
|
+
packages[`node_modules/${prefix}${name}`] = { version };
|
|
117
|
+
}
|
|
118
|
+
return packages;
|
|
119
|
+
}
|
|
120
|
+
export function validateDshRuntimeClosure(input) {
|
|
121
|
+
const dshVersion = input.dependencies['@deepseek-ai/dsh'];
|
|
122
|
+
if (typeof dshVersion !== 'string' || !/^\d+\.\d+\.\d+-[0-9A-Za-z.-]+$/.test(dshVersion)) {
|
|
123
|
+
throw new Error('@deepseek-ai/dsh 必须以精确 prerelease 版本声明');
|
|
124
|
+
}
|
|
125
|
+
if (dshVersion !== input.runtimeVersion) {
|
|
126
|
+
throw new Error(`DSH runtime guard 版本漂移:${input.runtimeVersion}(manifest ${dshVersion})`);
|
|
127
|
+
}
|
|
128
|
+
const dshNodes = Object.entries(input.lockPackages)
|
|
129
|
+
.flatMap(([path, metadata]) => {
|
|
130
|
+
const match = path.match(/(?:^|\/)node_modules\/(\@deepseek-ai\/dsh(?:-[^/]+)?)$/);
|
|
131
|
+
return match ? [{ name: match[1], version: metadata.version }] : [];
|
|
132
|
+
})
|
|
133
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
134
|
+
if (dshNodes.length === 0)
|
|
135
|
+
throw new Error('package-lock 未解析到 DSH runtime closure');
|
|
136
|
+
for (const dependency of dshNodes) {
|
|
137
|
+
if (dependency.version !== dshVersion) {
|
|
138
|
+
throw new Error(`DSH lock 版本漂移:${dependency.name}@${dependency.version ?? 'missing'}(expected ${dshVersion})`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const lockNames = [...new Set(dshNodes.map((dependency) => dependency.name))].sort();
|
|
142
|
+
const manifestNames = Object.keys(input.dependencies)
|
|
143
|
+
.filter((name) => name === '@deepseek-ai/dsh' || name.startsWith('@deepseek-ai/dsh-'))
|
|
144
|
+
.sort();
|
|
145
|
+
const missing = lockNames.filter((name) => !manifestNames.includes(name));
|
|
146
|
+
const extra = manifestNames.filter((name) => !lockNames.includes(name));
|
|
147
|
+
if (missing.length > 0) {
|
|
148
|
+
throw new Error(`DSH closure 未精确声明:${missing[0]}@${dshVersion}`);
|
|
149
|
+
}
|
|
150
|
+
if (extra.length > 0) {
|
|
151
|
+
throw new Error(`DSH manifest 与 lock 集合不一致:missing=${missing.join(',') || '-'};extra=${extra.join(',') || '-'}`);
|
|
152
|
+
}
|
|
153
|
+
for (const name of manifestNames) {
|
|
154
|
+
if (input.dependencies[name] !== dshVersion) {
|
|
155
|
+
throw new Error(`DSH closure 未精确声明:${name}@${dshVersion}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (input.expectedPackageCount !== undefined && lockNames.length !== input.expectedPackageCount) {
|
|
159
|
+
throw new Error(`DSH closure 包数量不符:${lockNames.length}(expected ${input.expectedPackageCount})`);
|
|
160
|
+
}
|
|
161
|
+
return { names: lockNames, version: dshVersion };
|
|
162
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session V3 确定性迁移契约(issue #268):在隔离临时目录下用真实发布的 V2 编解码器
|
|
3
|
+
* 生成 V2 header/events,序列化为 JSONL 字节写入磁盘,保存确切源字节快照;通过
|
|
4
|
+
* sessionFormatCatalog 从磁盘读取并解析这些字节,分类为 migration-required,迁移为
|
|
5
|
+
* V3,编码独立 V3 继任者写入磁盘;然后用 catalog validation:current 完全解码 V3
|
|
6
|
+
* 继任者;迁移后重新读取 V2 源文件做字节比较,证明迁移不修改源工件字节。
|
|
7
|
+
*
|
|
8
|
+
* 这是确定性格式兼容性证据(隔离文件字节,非内存 structuredClone),不是创始人状态
|
|
9
|
+
* 迁移,也不是 M5/M6 准入。
|
|
10
|
+
*
|
|
11
|
+
* 运行:cd ts && npx tsx scripts/dsh-session-v3-migration-proof.ts
|
|
12
|
+
*/
|
|
13
|
+
import assert from 'node:assert/strict';
|
|
14
|
+
import { writeFileSync, readFileSync, mkdtempSync, rmSync } from 'node:fs';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { tmpdir } from 'node:os';
|
|
17
|
+
import { createSessionFormatCatalogWithChildren } from '@deepseek-ai/dsh-session-format-catalog';
|
|
18
|
+
import { releasedV2SessionFormatCodec } from '@deepseek-ai/dsh-session-format-v1-to-v2';
|
|
19
|
+
// 最小有效 V2 工件:一个 turn 包含一个 step,turn 正常完成。
|
|
20
|
+
// V2 事件处置要求 turn/start 需 ["turn"],step/start|step/end 需 ["turn","step"],
|
|
21
|
+
// turn/end 需 ["turn","reason"],reason.kind="completed" 是最简有效种类。
|
|
22
|
+
const v2Header = {
|
|
23
|
+
version: 2,
|
|
24
|
+
id: 's-min-v2',
|
|
25
|
+
createdAt: 1700000000000,
|
|
26
|
+
isSeeded: false,
|
|
27
|
+
delegationDepth: 0,
|
|
28
|
+
};
|
|
29
|
+
const v2Events = [
|
|
30
|
+
{ type: 'turn/start', seq: 0, time: 1700000000000, data: { turn: 1 } },
|
|
31
|
+
{ type: 'step/start', seq: 1, time: 1700000000001, data: { turn: 1, step: 1 } },
|
|
32
|
+
{ type: 'step/end', seq: 2, time: 1700000000002, data: { turn: 1, step: 1 } },
|
|
33
|
+
{ type: 'turn/end', seq: 3, time: 1700000000003, data: { turn: 1, reason: { kind: 'completed' } } },
|
|
34
|
+
];
|
|
35
|
+
// 1. 用真实发布的 V2 编解码器编码 header + 事件行为 JSON 对象,序列化为 JSONL 字节。
|
|
36
|
+
const encodedHeader = releasedV2SessionFormatCodec.encodeHeader(v2Header, 0);
|
|
37
|
+
const encodedRows = v2Events.map((e) => releasedV2SessionFormatCodec.encodeEvent(e));
|
|
38
|
+
function toLine(obj) {
|
|
39
|
+
return JSON.stringify(obj);
|
|
40
|
+
}
|
|
41
|
+
const sourceLines = [toLine(encodedHeader), ...encodedRows.map(toLine)];
|
|
42
|
+
const sourceJsonl = sourceLines.join('\n') + '\n';
|
|
43
|
+
const tmp = mkdtempSync(join(tmpdir(), 'session-v3-migration-proof-'));
|
|
44
|
+
const sourcePath = join(tmp, 'source.v2.jsonl');
|
|
45
|
+
const successorPath = join(tmp, 'successor.current.jsonl');
|
|
46
|
+
// 0.2.0-rc.2 起 catalog current = V4;V3→V4 边要求显式历史子代理证据,
|
|
47
|
+
// 本 fixture 是无父会话,声明空数组(createSessionFormatCatalogWithChildren)。
|
|
48
|
+
const catalog = createSessionFormatCatalogWithChildren([]);
|
|
49
|
+
try {
|
|
50
|
+
// 2. 写 V2 源字节到磁盘,保存确切源字节快照。
|
|
51
|
+
writeFileSync(sourcePath, sourceJsonl, 'utf8');
|
|
52
|
+
const sourceBytes = readFileSync(sourcePath);
|
|
53
|
+
// 3. 从磁盘读取并解析 V2 源字节:首行 = header,其余 = 事件行。
|
|
54
|
+
const parsedLines = readFileSync(sourcePath, 'utf8').split('\n').filter((l) => l.length > 0);
|
|
55
|
+
assert.ok(parsedLines.length === sourceLines.length, `磁盘行数必须等于源行数(${parsedLines.length} vs ${sourceLines.length})`);
|
|
56
|
+
const headerValue = JSON.parse(parsedLines[0]);
|
|
57
|
+
const rowValues = parsedLines.slice(1).map((l) => JSON.parse(l));
|
|
58
|
+
// 4. catalog 当前版本 = 4(0.2.0-rc.2 起);V2 header 分类为 migration-required。
|
|
59
|
+
assert.equal(catalog.currentVersion, 4, 'catalog 当前版本必须为 4(0.2.0-rc.2 起 V4 为 current)');
|
|
60
|
+
const headerRead = catalog.readHeader(headerValue);
|
|
61
|
+
assert.equal(headerRead.status, 'migration-required', `V2 header 必须分类为 migration-required(当前 ${headerRead.status})`);
|
|
62
|
+
// 5. 创建恢复器,逐行解码,完成恢复为 V3 工件(validation:transformed,迁移后校验)。
|
|
63
|
+
const restore = catalog.createRestore(headerValue, {
|
|
64
|
+
recovery: 'recoverable',
|
|
65
|
+
validation: 'transformed',
|
|
66
|
+
});
|
|
67
|
+
for (const row of rowValues) {
|
|
68
|
+
restore.decodeRow(row);
|
|
69
|
+
}
|
|
70
|
+
const artifact = restore.finish();
|
|
71
|
+
// 6. 迁移链工件版本 = 4(V2→V3 插入 system head 后,V3→V4 不减事件)。
|
|
72
|
+
assert.equal(artifact.header.version, 4, '恢复后工件版本必须为 4');
|
|
73
|
+
assert.ok(artifact.events.length >= v2Events.length + 1, `迁移链事件数必须 ≥ V2 + 1(插入 system head),当前 ${artifact.events.length}`);
|
|
74
|
+
// 插入的 system head(原 step/start 后)必须是 system/message。
|
|
75
|
+
const inserted = artifact.events.find((event) => event.type === 'system/message');
|
|
76
|
+
assert.ok(inserted, '迁移链必须包含插入的 system/message head');
|
|
77
|
+
// 7. 编码 current(V4)继任者为 JSONL 字节,写入独立磁盘文件。
|
|
78
|
+
const currentHeaderRecord = catalog.encodeCurrentHeader(artifact.header, artifact.inheritedEventCount);
|
|
79
|
+
const currentRowRecords = artifact.events.map((e) => catalog.encodeCurrentEvent(e));
|
|
80
|
+
const successorLines = [toLine(currentHeaderRecord), ...currentRowRecords.map(toLine)];
|
|
81
|
+
const successorJsonl = successorLines.join('\n') + '\n';
|
|
82
|
+
writeFileSync(successorPath, successorJsonl, 'utf8');
|
|
83
|
+
// 8. 从磁盘读取 V3 继任者,用 catalog validation:current 完全解码。
|
|
84
|
+
const successorParsed = readFileSync(successorPath, 'utf8').split('\n').filter((l) => l.length > 0);
|
|
85
|
+
assert.ok(successorParsed.length === successorLines.length, `V3 磁盘行数必须等于继任者行数`);
|
|
86
|
+
const successorHeaderValue = JSON.parse(successorParsed[0]);
|
|
87
|
+
const successorRowValues = successorParsed.slice(1).map((l) => JSON.parse(l));
|
|
88
|
+
const successorRead = catalog.readHeader(successorHeaderValue);
|
|
89
|
+
assert.equal(successorRead.status, 'current', `V3 继任者必须重新打开为 current(当前 ${successorRead.status})`);
|
|
90
|
+
const successorRestore = catalog.createRestore(successorHeaderValue, {
|
|
91
|
+
recovery: 'recoverable',
|
|
92
|
+
validation: 'current',
|
|
93
|
+
});
|
|
94
|
+
for (const row of successorRowValues) {
|
|
95
|
+
successorRestore.decodeRow(row);
|
|
96
|
+
}
|
|
97
|
+
const successorArtifact = successorRestore.finish();
|
|
98
|
+
assert.equal(successorArtifact.header.version, 4, 'current 继任者完全解码后版本必须为 4');
|
|
99
|
+
assert.equal(successorArtifact.events.length, artifact.events.length, 'V3 继任者完全解码后事件数必须与迁移工件一致');
|
|
100
|
+
// 9. 迁移后重新读取 V2 源文件字节,与快照字节比较——证明迁移不修改源工件字节。
|
|
101
|
+
const sourceBytesAfter = readFileSync(sourcePath);
|
|
102
|
+
assert.ok(Buffer.compare(sourceBytes, sourceBytesAfter) === 0, '迁移后 V2 源文件字节必须与快照完全一致——迁移不修改源工件字节');
|
|
103
|
+
console.log(`SESSION V3 MIGRATION PROOF: V2 source ${sourceBytes.length}B (${v2Events.length} events) on disk → ` +
|
|
104
|
+
`current V4(${artifact.events.length} events, +system/message), successor ${successorJsonl.length}B reopens as current ` +
|
|
105
|
+
`and fully decodes (${successorArtifact.events.length} events), source bytes unchanged after migration`);
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
109
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-subprocess-local 确定性证明(issue #268):仅通过公共 API 挂载 provider,
|
|
3
|
+
* 执行真实的本地托管子进程与 PTY,观察真实输出,终止它,await 公共 waitForExit,
|
|
4
|
+
* 并证明派生后代已消失。平台中立(Ubuntu CI + macOS)——不调用私有
|
|
5
|
+
* selectContainmentMode()/disposeManagedProcesses(),不断言主机是 macOS fallback,
|
|
6
|
+
* 不用已退出的顶层进程作为后代清理替代。
|
|
7
|
+
*
|
|
8
|
+
* 公共挂载模式:await ctx.plugin(LocalSubprocessRuntime) → ctx.subprocess 可用。
|
|
9
|
+
* spawn 一个活跃的 node 父进程(被 provider 以 detached:true 启动,自成进程组领袖),
|
|
10
|
+
* 该父进程用 child_process.spawn 创建一个非分离的真实长运行子进程(继承父进程组),
|
|
11
|
+
* 并将子进程 PID 输出到 stdout。证明通过收集 stdout 读取子进程 PID,调用公共
|
|
12
|
+
* terminate() + await waitForExit(有界 abort 信号),断言父进程结果和子进程 PID
|
|
13
|
+
* 都已消失(进程组信号终止整个组)。
|
|
14
|
+
*
|
|
15
|
+
* 真实 PTY 验证在 CI 支持平台(Linux/macOS)都执行:spawnTerminal 启动 node 子进程,
|
|
16
|
+
* 子进程向 stdout 写入 'pty-line\n' 并正常退出;断言 PTY 输出包含 'pty-line' 且 exitCode=0。
|
|
17
|
+
* 目标 node-pty 包的 Darwin 预构建同时带 `pty.node` 与 `spawn-helper`,Linux x64/arm64
|
|
18
|
+
* 预构建只带 `pty.node`。本证明在两个平台都跑真实 PTY 行为;filesystem 上
|
|
19
|
+
* spawn-helper 存在性与 0755 模式断言仅在 Darwin 下生效。Linux 报告
|
|
20
|
+
* "helper not-applicable",同时仍验证真实 PTY 输出与成功退出,不 stat 不存在的文件。
|
|
21
|
+
*
|
|
22
|
+
* finally 中 await ctx.fiber.dispose() 并移除临时文件。不向子进程传递整个环境变量。
|
|
23
|
+
*
|
|
24
|
+
* 运行:cd ts && npx tsx scripts/dsh-subprocess-local-proof.ts
|
|
25
|
+
*/
|
|
26
|
+
import assert from 'node:assert/strict';
|
|
27
|
+
import { writeFileSync, mkdtempSync, rmSync, existsSync, statSync } from 'node:fs';
|
|
28
|
+
import { join, dirname } from 'node:path';
|
|
29
|
+
import { tmpdir } from 'node:os';
|
|
30
|
+
import { fileURLToPath } from 'node:url';
|
|
31
|
+
import { Context } from '@deepseek-ai/cordis';
|
|
32
|
+
import { LocalSubprocessRuntime } from '@deepseek-ai/dsh-subprocess-local';
|
|
33
|
+
const ctx = new Context();
|
|
34
|
+
const fiber = ctx.plugin(LocalSubprocessRuntime);
|
|
35
|
+
await fiber;
|
|
36
|
+
const tmp = mkdtempSync(join(tmpdir(), 'subprocess-local-proof-'));
|
|
37
|
+
let childPid;
|
|
38
|
+
let childGoneAfter = false;
|
|
39
|
+
let parentOutcome;
|
|
40
|
+
let rangeEmpty;
|
|
41
|
+
let ptyExitCode = null;
|
|
42
|
+
let ptyOutput = '';
|
|
43
|
+
let helperFact;
|
|
44
|
+
let helperMode;
|
|
45
|
+
try {
|
|
46
|
+
// 1. 父进程脚本:spawn 一个非分离的长运行子进程(继承父进程组),输出子进程 PID。
|
|
47
|
+
// 父进程自身保持活跃(setInterval),直到被 provider 终止。
|
|
48
|
+
const parentScript = join(tmp, 'parent.mjs');
|
|
49
|
+
writeFileSync(parentScript, [
|
|
50
|
+
"import { spawn } from 'node:child_process'",
|
|
51
|
+
"const child = spawn(process.execPath, ['-e', 'setInterval(() => {}, 60000)'], { stdio: 'inherit' })",
|
|
52
|
+
"process.stdout.write('CHILD_PID=' + child.pid + '\\n')",
|
|
53
|
+
'setInterval(() => {}, 60000)',
|
|
54
|
+
'',
|
|
55
|
+
].join('\n'));
|
|
56
|
+
// 2. 通过公共 ctx.subprocess.spawn 启动父进程。不传 env——让 provider 用
|
|
57
|
+
// scrubbedParentEnv() 清洗环境(保留 PATH/HOME,剥离凭据/DSH_*),不传递整个环境变量。
|
|
58
|
+
const handle = ctx.subprocess.spawn({
|
|
59
|
+
argv: [process.execPath, parentScript],
|
|
60
|
+
cwd: tmp,
|
|
61
|
+
stdio: { stdin: 'ignore', stdout: { maxBytes: 1 << 20 }, stderr: { maxBytes: 1 << 20 } },
|
|
62
|
+
graceMs: 8000,
|
|
63
|
+
});
|
|
64
|
+
const stdoutReader = handle.collected.stdout;
|
|
65
|
+
assert.ok(stdoutReader, 'collect 模式下 collected.stdout reader 必须存在');
|
|
66
|
+
// 3. 通过观察父进程输出获取子进程 PID(有界轮询,不假设即时可用)。
|
|
67
|
+
let observedChildPid;
|
|
68
|
+
const pollDeadline = Date.now() + 5000;
|
|
69
|
+
while (Date.now() < pollDeadline && observedChildPid === undefined) {
|
|
70
|
+
const read = stdoutReader.readFrom(0);
|
|
71
|
+
const m = /CHILD_PID=(\d+)/.exec(read.text);
|
|
72
|
+
if (m)
|
|
73
|
+
observedChildPid = Number(m[1]);
|
|
74
|
+
else
|
|
75
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
76
|
+
}
|
|
77
|
+
assert.ok(observedChildPid !== undefined && observedChildPid > 0, `必须从父进程 stdout 观察到有效 CHILD_PID(当前 ${observedChildPid})`);
|
|
78
|
+
childPid = observedChildPid;
|
|
79
|
+
// 4. 终止前:子进程必须存活(父进程已 spawn 它)。
|
|
80
|
+
assert.ok(isPidAlive(childPid), `终止前子进程 PID ${childPid} 必须存活`);
|
|
81
|
+
// 5. 调用公共 terminate() + await 公共 waitForExit(有界 abort 信号)。
|
|
82
|
+
// 进程组信号(process.kill(-parentPid, SIGTERM))终止整个组,包括非分离子进程。
|
|
83
|
+
handle.terminate();
|
|
84
|
+
rangeEmpty = await handle.waitForExit(AbortSignal.timeout(8000));
|
|
85
|
+
assert.equal(rangeEmpty, true, 'waitForExit 必须返回 true——托管范围(含后代)已空');
|
|
86
|
+
// 6. 断言直接进程结果:父进程被信号终止(非正常退出)。
|
|
87
|
+
parentOutcome = await handle.done;
|
|
88
|
+
assert.ok(parentOutcome.exitCode === null && parentOutcome.signal !== null, `父进程必须被信号终止(exitCode=${parentOutcome.exitCode}, signal=${parentOutcome.signal})`);
|
|
89
|
+
// 7. 断言子进程 PID 不再存在——后代清理的真实证据(非已退出顶层进程替代)。
|
|
90
|
+
childGoneAfter = !isPidAlive(childPid);
|
|
91
|
+
assert.ok(childGoneAfter, `provider 终止后子进程 PID ${childPid} 必须已消失——进程组包含的真实后代清理证据`);
|
|
92
|
+
// 8. 真实 PTY 验证(每个支持平台都执行):spawnTerminal 用 native PTY 后端 fork/exec,
|
|
93
|
+
// 子进程写 'pty-line\n' 并正常退出。原生 PTY 输出经 PTY 终端层到达 PTY 主面,
|
|
94
|
+
// 再被 node-pty 回调成 output 'data' 事件——这是跨平台的 PTY 行为证明。
|
|
95
|
+
const ptyScript = join(tmp, 'pty.mjs');
|
|
96
|
+
writeFileSync(ptyScript, "process.stdout.write('pty-line\\n'); process.exit(0);\n");
|
|
97
|
+
const term = await ctx.subprocess.spawnTerminal({
|
|
98
|
+
argv: [process.execPath, ptyScript],
|
|
99
|
+
cwd: tmp,
|
|
100
|
+
rows: 24,
|
|
101
|
+
cols: 80,
|
|
102
|
+
terminalType: 'xterm-256color',
|
|
103
|
+
graceMs: 8000,
|
|
104
|
+
});
|
|
105
|
+
assert.equal(typeof term.pid, 'number', `PTY pid 必须是数字(当前 ${typeof term.pid})`);
|
|
106
|
+
assert.ok(term.pid > 0, `PTY pid 必须为正(当前 ${term.pid})`);
|
|
107
|
+
term.output.on('data', (d) => {
|
|
108
|
+
ptyOutput += d.toString();
|
|
109
|
+
});
|
|
110
|
+
const termOutcome = await term.done;
|
|
111
|
+
ptyExitCode = termOutcome.exitCode;
|
|
112
|
+
assert.equal(ptyExitCode, 0, `PTY 必须正常退出(exitCode=${ptyExitCode})`);
|
|
113
|
+
assert.equal(termOutcome.signal, null, `PTY 正常退出不得携带 signal(当前 ${termOutcome.signal})`);
|
|
114
|
+
assert.ok(ptyOutput.includes('pty-line'), `PTY 输出必须包含 pty-line(当前 ${JSON.stringify(ptyOutput)})`);
|
|
115
|
+
await term.terminate();
|
|
116
|
+
// 9. node-pty spawn-helper 文件系统断言仅在 Darwin 下生效。
|
|
117
|
+
// 已检查的目标包事实:darwin-{x64,arm64}/prebuilds 同时带 pty.node 与
|
|
118
|
+
// spawn-helper,linux-{x64,arm64}/prebuilds 仅带 pty.node。本证明不构造、
|
|
119
|
+
// 不复制、不 stat 实际不存在的 helper。
|
|
120
|
+
helperMode = null;
|
|
121
|
+
if (process.platform === 'darwin') {
|
|
122
|
+
const ptyEntry = fileURLToPath(import.meta.resolve('node-pty'));
|
|
123
|
+
const ptyRoot = dirname(dirname(ptyEntry));
|
|
124
|
+
const helperPath = join(ptyRoot, 'prebuilds', `${process.platform}-${process.arch}`, 'spawn-helper');
|
|
125
|
+
assert.ok(existsSync(helperPath), `Darwin prebuilds 必须携带 spawn-helper(实际不存在:${helperPath})——ensure-spawn-helper.mjs 的终局面契约`);
|
|
126
|
+
helperMode = statSync(helperPath).mode & 0o777;
|
|
127
|
+
assert.equal(helperMode, 0o755, `Darwin spawn-helper 权限模式必须是 0755(当前 0o${helperMode.toString(8)})——ensure-spawn-helper.mjs 的终局面契约`);
|
|
128
|
+
helperFact = `helper=darwin 0o${helperMode.toString(8)}`;
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
helperFact = `helper=not-applicable:${process.platform}-${process.arch}; real PTY output='pty-line' present, exitCode=0`;
|
|
132
|
+
assert.ok(ptyExitCode === 0 && ptyOutput.includes('pty-line'), '非 Darwin 平台必须仍证明真实 PTY 行为:PTY 输出包含 pty-line 且 exitCode=0');
|
|
133
|
+
}
|
|
134
|
+
console.log(`SUBPROCESS LOCAL PROOF: parent killed by signal ${parentOutcome.signal}, child PID ${childPid} gone after terminate=${childGoneAfter}, ` +
|
|
135
|
+
`waitForExit rangeEmpty=${rangeEmpty}, PTY pid=${term.pid} exitCode=${ptyExitCode} output-has-pty-line=${ptyOutput.includes('pty-line')}, ` +
|
|
136
|
+
`${helperFact}`);
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
await ctx.fiber.dispose();
|
|
140
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
141
|
+
}
|
|
142
|
+
function isPidAlive(pid) {
|
|
143
|
+
try {
|
|
144
|
+
process.kill(pid, 0);
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|