@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,309 @@
|
|
|
1
|
+
/** Closed dsh capability-tool registration proof (no dsh runtime required). */
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
4
|
+
import { ToolArgsError } from '@deepseek-ai/dsh-tools';
|
|
5
|
+
// @ts-expect-error Runtime dsh plugins are JavaScript modules registered by path.
|
|
6
|
+
import { apply, embeddedBookingToolDefinitions } from '../src/booking-surface/dsh-plugin.js';
|
|
7
|
+
const registered = [];
|
|
8
|
+
apply({
|
|
9
|
+
tools: {
|
|
10
|
+
register(tool) { registered.push(tool); },
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
assert.equal(registered.length, 13);
|
|
14
|
+
assert.deepEqual(registered.map((tool) => tool.name), [
|
|
15
|
+
'booking_search_hotels',
|
|
16
|
+
'booking_run_search',
|
|
17
|
+
'booking_refine_results',
|
|
18
|
+
'booking_focus_hotel',
|
|
19
|
+
'booking_select_hotel',
|
|
20
|
+
'booking_find_room_offers',
|
|
21
|
+
'booking_view_offers',
|
|
22
|
+
'booking_compare_offers',
|
|
23
|
+
'booking_select_offer',
|
|
24
|
+
'booking_prepare_booking',
|
|
25
|
+
'booking_prepare_checkout',
|
|
26
|
+
'booking_observe_booking',
|
|
27
|
+
'booking_finish_turn',
|
|
28
|
+
]);
|
|
29
|
+
assert.deepEqual(registered, embeddedBookingToolDefinitions);
|
|
30
|
+
assert.ok(!registered.some((tool) => /gotry_book|trade|payment/i.test(String(tool.name))));
|
|
31
|
+
// One concrete closed schema per tool: no top-level anyOf union, no decision
|
|
32
|
+
// envelope, and the tool name alone selects the action kind. Weaker providers
|
|
33
|
+
// cannot be trusted to pick a union branch, so every action tool advertises
|
|
34
|
+
// const kind + that kind's input + mandatory intent.
|
|
35
|
+
const finishTool = registered.find((tool) => tool.name === 'booking_finish_turn');
|
|
36
|
+
const finishParameters = finishTool.parameters;
|
|
37
|
+
assert.equal(finishParameters.type, 'object');
|
|
38
|
+
assert.ok(!Object.prototype.hasOwnProperty.call(finishParameters, 'anyOf'));
|
|
39
|
+
assert.equal(finishParameters.properties.kind.const, 'terminal');
|
|
40
|
+
assert.deepEqual(finishParameters.required, ['kind']);
|
|
41
|
+
assert.ok(!Object.prototype.hasOwnProperty.call(finishParameters.properties, 'decision'));
|
|
42
|
+
const actionTools = registered.filter((tool) => tool.name !== 'booking_finish_turn');
|
|
43
|
+
const TOOL_KIND = {
|
|
44
|
+
booking_search_hotels: 'search.patch',
|
|
45
|
+
booking_run_search: 'search.run',
|
|
46
|
+
booking_refine_results: 'results.view.patch',
|
|
47
|
+
booking_focus_hotel: 'hotel.focus',
|
|
48
|
+
booking_select_hotel: 'hotel.select',
|
|
49
|
+
booking_find_room_offers: 'offers.query',
|
|
50
|
+
booking_view_offers: 'offers.view.patch',
|
|
51
|
+
booking_compare_offers: 'offers.compare',
|
|
52
|
+
booking_select_offer: 'offer.select',
|
|
53
|
+
booking_prepare_booking: 'offer.check',
|
|
54
|
+
booking_prepare_checkout: 'checkout.prepare',
|
|
55
|
+
booking_observe_booking: 'order.observe',
|
|
56
|
+
};
|
|
57
|
+
for (const tool of actionTools) {
|
|
58
|
+
const parameters = tool.parameters;
|
|
59
|
+
assert.equal(parameters.type, 'object');
|
|
60
|
+
assert.ok(!Object.prototype.hasOwnProperty.call(parameters, 'anyOf'), `${tool.name} must not advertise a union`);
|
|
61
|
+
assert.ok(!Object.prototype.hasOwnProperty.call(parameters.properties ?? {}, 'decision'), `${tool.name} must not advertise a decision envelope`);
|
|
62
|
+
assert.deepEqual(parameters.required, ['kind', 'input', 'intent']);
|
|
63
|
+
assert.equal(parameters.additionalProperties, false);
|
|
64
|
+
assert.equal(parameters.properties.kind.const, TOOL_KIND[String(tool.name)], `${tool.name} advertises exactly its own action kind`);
|
|
65
|
+
}
|
|
66
|
+
const prepare = registered.find((tool) => tool.name === 'booking_prepare_booking');
|
|
67
|
+
assert.equal(prepare.parameters.properties.kind.const, 'offer.check', 'prepare-booking advertises offer.check only');
|
|
68
|
+
assert.ok(!JSON.stringify(prepare.parameters).includes('book"'), 'schema has no Book discriminator');
|
|
69
|
+
const search = registered.find((tool) => tool.name === 'booking_search_hotels');
|
|
70
|
+
const rawExecuteSearch = search.execute;
|
|
71
|
+
let concludedTurns = 0;
|
|
72
|
+
const executeSearch = (args) => rawExecuteSearch(args, {
|
|
73
|
+
concludeTurn() { concludedTurns += 1; },
|
|
74
|
+
});
|
|
75
|
+
await assert.rejects(executeSearch({ decision: { kind: 'operation' } }), (error) => error instanceof ToolArgsError
|
|
76
|
+
&& error.name === 'ToolArgsError'
|
|
77
|
+
&& error.code === 'INVALID_ARGS'
|
|
78
|
+
&& /decision_schema_violation/.test(error.message), 'schema violations must surface as dsh ToolArgsError with stable INVALID_ARGS code');
|
|
79
|
+
await assert.rejects(executeSearch({ kind: 'search.patch', input: { patch: { destination: { query: 'Dubai' } } } }), (error) => error instanceof ToolArgsError && error.code === 'INVALID_ARGS', 'every fresh operation requires a semantic intent before the tool can conclude the turn');
|
|
80
|
+
assert.equal(concludedTurns, 0, 'an invalid tool call stays in the same turn for local schema repair');
|
|
81
|
+
const validateSearchTool = new Ajv2020({ allErrors: true, strict: true }).compile(search.parameters);
|
|
82
|
+
// The advertised dialect is single-kind now: a terminal proposal is accepted by
|
|
83
|
+
// the execute compat seam but is no longer part of this tool's advertised schema.
|
|
84
|
+
assert.equal(validateSearchTool({ kind: 'terminal' }), false, 'the terminal kind moved to the dedicated finish tool');
|
|
85
|
+
for (const invalidProposal of [
|
|
86
|
+
{ kind: 'search.patch', input: { patch: {} }, intent: { target: 'search.results' } },
|
|
87
|
+
{ kind: 'search.patch', input: { patch: { stay: { checkIn: 'tomorrow' } } }, intent: { target: 'search.results' } },
|
|
88
|
+
{ kind: 'search.patch', input: { patch: { starRating: { strength: 'must', value: { min: 9 } } } }, intent: { target: 'search.results' } },
|
|
89
|
+
{ kind: 'search.patch', input: { patch: { facilities: { strength: 'must', value: { allOf: [] } } } }, intent: { target: 'search.results' } },
|
|
90
|
+
{ kind: 'search.patch', input: { patch: { facilities: { strength: 'must', value: { allOf: ['breakfast', 'breakfast'] } } } }, intent: { target: 'search.results' } },
|
|
91
|
+
]) {
|
|
92
|
+
// dsh's advertised dialect cannot express every canonical constraint. The
|
|
93
|
+
// execute seam must still reject it before concludeTurn so the model gets
|
|
94
|
+
// an INVALID_ARGS repair opportunity in the same bounded run.
|
|
95
|
+
assert.equal(validateSearchTool(invalidProposal), true, 'the intentionally reduced advertised dialect exposes this regression case');
|
|
96
|
+
await assert.rejects(executeSearch(invalidProposal), (error) => error instanceof ToolArgsError && error.code === 'INVALID_ARGS', 'full canonical semantic constraints are enforced before concludeTurn');
|
|
97
|
+
}
|
|
98
|
+
assert.equal(concludedTurns, 0, 'canonical semantic failures never conclude the dsh turn');
|
|
99
|
+
const compare = registered.find((tool) => tool.name === 'booking_compare_offers');
|
|
100
|
+
const rawExecuteCompare = compare.execute;
|
|
101
|
+
let compareConcludedTurns = 0;
|
|
102
|
+
const executeCompare = (args) => rawExecuteCompare(args, {
|
|
103
|
+
concludeTurn() { compareConcludedTurns += 1; },
|
|
104
|
+
});
|
|
105
|
+
const validateCompareTool = new Ajv2020({ allErrors: true, strict: true }).compile(compare.parameters);
|
|
106
|
+
const compositeCompareProposal = {
|
|
107
|
+
kind: 'offers.compare',
|
|
108
|
+
input: { offerRefs: ['offer-a', 'offer-b', 'offer-c'], requestedCount: 3 },
|
|
109
|
+
intent: {
|
|
110
|
+
target: 'offers.compared',
|
|
111
|
+
offerCriteria: {
|
|
112
|
+
meals: { strength: 'must', value: ['breakfast'] },
|
|
113
|
+
freeCancellation: { strength: 'must', value: true },
|
|
114
|
+
targetCount: 3,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
assert.equal(validateCompareTool(compositeCompareProposal), true, JSON.stringify(validateCompareTool.errors));
|
|
119
|
+
assert.deepEqual(await executeCompare(compositeCompareProposal), { accepted: true, decisionKind: 'operation', actionKind: 'offers.compare' }, 'a shallow action may carry a typed multi-step goal without runtime-owned metadata');
|
|
120
|
+
assert.equal(compareConcludedTurns, 1);
|
|
121
|
+
const reducedIntentCounterexample = {
|
|
122
|
+
...compositeCompareProposal,
|
|
123
|
+
intent: { target: 'offers.compared', offerCriteria: { targetCount: 0 } },
|
|
124
|
+
};
|
|
125
|
+
assert.equal(validateCompareTool(reducedIntentCounterexample), true, 'the reduced model dialect cannot express targetCount minimum');
|
|
126
|
+
await assert.rejects(executeCompare(reducedIntentCounterexample), (error) => error instanceof ToolArgsError && error.code === 'INVALID_ARGS' && /must be >= 1/.test(error.message), 'the execute seam returns a precise repairable error for an invalid intent constraint');
|
|
127
|
+
assert.equal(compareConcludedTurns, 1, 'invalid intent does not conclude the model turn');
|
|
128
|
+
await assert.rejects(executeCompare({ ...compositeCompareProposal, intent: { ...compositeCompareProposal.intent, payment: true } }), (error) => error instanceof ToolArgsError && error.code === 'INVALID_ARGS', 'the intent envelope is closed against write/payment fields');
|
|
129
|
+
assert.equal(compareConcludedTurns, 1);
|
|
130
|
+
const compactSearchProposal = {
|
|
131
|
+
kind: 'search.patch',
|
|
132
|
+
input: {
|
|
133
|
+
patch: {
|
|
134
|
+
budget: {
|
|
135
|
+
strength: 'must',
|
|
136
|
+
value: { max: { amount: '1000' } },
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
intent: { target: 'search.results' },
|
|
141
|
+
};
|
|
142
|
+
assert.equal(validateSearchTool(compactSearchProposal), true, JSON.stringify(validateSearchTool.errors));
|
|
143
|
+
assert.ok(!JSON.stringify(search.parameters).includes('sourceFactRef'), 'model-facing proposal does not ask the model to invent Money provenance');
|
|
144
|
+
assert.deepEqual(await executeSearch(compactSearchProposal), { accepted: true, decisionKind: 'operation', actionKind: 'search.patch' }, 'the tool execution seam accepts the same shallow proposal advertised to the model');
|
|
145
|
+
assert.equal(concludedTurns, 1, 'the first accepted proposal marks the current dsh turn as concluded');
|
|
146
|
+
const compactTerminalProposal = { kind: 'terminal' };
|
|
147
|
+
assert.deepEqual(await executeSearch(compactTerminalProposal), { accepted: true, decisionKind: 'terminal' }, 'terminal output remains a shallow proposal on the compat seam with runtime-owned evidence');
|
|
148
|
+
assert.equal(concludedTurns, 2, 'an accepted terminal proposal concludes the current dsh turn');
|
|
149
|
+
await assert.rejects(executeSearch({
|
|
150
|
+
decision: { kind: 'terminal', terminal: { status: 'completed', summary: 'forged', factRefs: [] } },
|
|
151
|
+
}), (error) => error instanceof ToolArgsError && error.code === 'INVALID_ARGS', 'legacy model-authored terminal decisions are outside the compatibility contract');
|
|
152
|
+
assert.equal(concludedTurns, 2, 'a rejected legacy terminal does not conclude the dsh turn');
|
|
153
|
+
for (const decision of [compactSearchProposal, JSON.stringify(compactSearchProposal)]) {
|
|
154
|
+
assert.deepEqual(await executeSearch({ decision }), { accepted: true, decisionKind: 'operation', actionKind: 'search.patch' }, 'one exact provider wrapper is normalized without widening semantic authority');
|
|
155
|
+
}
|
|
156
|
+
assert.deepEqual(await executeSearch({ decision: { kind: 'operation', action: compactSearchProposal } }), { accepted: true, decisionKind: 'operation', actionKind: 'search.patch' }, 'the one-layer operation wrapper preserves nested compact intent without widening authority');
|
|
157
|
+
assert.deepEqual(await executeSearch({ kind: 'operation', action: { kind: compactSearchProposal.kind, input: compactSearchProposal.input }, intent: compactSearchProposal.intent }), { accepted: true, decisionKind: 'operation', actionKind: 'search.patch' }, 'the direct-hoisted compact operation accepted by the parent is accepted by the plugin too');
|
|
158
|
+
await assert.rejects(executeSearch({ decision: compactSearchProposal, extra: true }), (error) => error instanceof ToolArgsError && error.code === 'INVALID_ARGS', 'provider compatibility remains closed to extra sibling fields');
|
|
159
|
+
const validSearchDecision = {
|
|
160
|
+
decision: {
|
|
161
|
+
kind: 'operation',
|
|
162
|
+
intent: { target: 'search.results' },
|
|
163
|
+
action: {
|
|
164
|
+
schemaVersion: 'booking.surface',
|
|
165
|
+
kind: 'search.patch',
|
|
166
|
+
actionId: 'action-model-1',
|
|
167
|
+
contextRef: 'ctx-model-1',
|
|
168
|
+
expectedRevision: 0,
|
|
169
|
+
reason: 'Apply the requested budget.',
|
|
170
|
+
factRefs: [],
|
|
171
|
+
input: {
|
|
172
|
+
patch: {
|
|
173
|
+
budget: {
|
|
174
|
+
strength: 'must',
|
|
175
|
+
value: { max: { amount: '1000', currency: 'AED', sourceFactRef: 'fact-budget-1' } },
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
assert.equal(validateSearchTool(validSearchDecision), false, 'runtime-owned action metadata is absent from the advertised model schema');
|
|
183
|
+
assert.equal(validateSearchTool({ decision: JSON.stringify(validSearchDecision.decision) }), false, 'the advertised model-facing schema remains canonical and object-only');
|
|
184
|
+
assert.deepEqual(await executeSearch({ decision: JSON.stringify(validSearchDecision.decision) }), { accepted: true, decisionKind: 'operation', actionKind: 'search.patch', actionId: 'action-model-1' }, 'the execution seam normalizes one provider-stringified action plus mandatory intent before applying the canonical schema');
|
|
185
|
+
await assert.rejects(executeSearch({ decision: JSON.stringify({ kind: 'operation', action: validSearchDecision.decision.action }) }), (error) => error instanceof ToolArgsError && error.code === 'INVALID_ARGS', 'a fresh legacy full action cannot silently degrade a multi-step request by omitting intent');
|
|
186
|
+
await assert.rejects(executeSearch(Object.create({ decision: JSON.stringify(validSearchDecision.decision), injected: 'prototype-value' })), (error) => error instanceof ToolArgsError && error.code === 'INVALID_ARGS', 'an inherited stringified decision cannot be materialized into an executable own property');
|
|
187
|
+
await assert.rejects(executeSearch(Object.create({ decision: validSearchDecision.decision })), (error) => error instanceof ToolArgsError && error.code === 'INVALID_ARGS', 'an inherited canonical decision cannot satisfy the model-facing schema');
|
|
188
|
+
for (const decision of [
|
|
189
|
+
'not JSON',
|
|
190
|
+
`${JSON.stringify(validSearchDecision.decision)} trailing text`,
|
|
191
|
+
JSON.stringify(null),
|
|
192
|
+
JSON.stringify([]),
|
|
193
|
+
JSON.stringify(1),
|
|
194
|
+
JSON.stringify(JSON.stringify(validSearchDecision.decision)),
|
|
195
|
+
]) {
|
|
196
|
+
await assert.rejects(executeSearch({ decision }), (error) => error instanceof ToolArgsError && error.code === 'INVALID_ARGS', 'non-canonical decision strings remain rejected at the tool execution seam');
|
|
197
|
+
}
|
|
198
|
+
await assert.rejects(executeSearch({
|
|
199
|
+
decision: JSON.stringify({
|
|
200
|
+
...validSearchDecision.decision,
|
|
201
|
+
action: JSON.stringify(validSearchDecision.decision.action),
|
|
202
|
+
}),
|
|
203
|
+
}), (error) => error instanceof ToolArgsError && error.code === 'INVALID_ARGS', 'the compatibility seam does not normalize a stringified action');
|
|
204
|
+
assert.equal(validateSearchTool({ decision: { kind: 'operation', action: {
|
|
205
|
+
schemaVersion: 'booking.surface', kind: 'search.run', actionId: 'action-model-v2', contextRef: 'ctx-model-1', expectedRevision: 0,
|
|
206
|
+
reason: 'Run the authoritative workspace search.', factRefs: [], input: {},
|
|
207
|
+
} } }), false, 'legacy full actions are compatibility input only and stay out of the advertised schema');
|
|
208
|
+
const unsafeFactRefSearchDecision = {
|
|
209
|
+
decision: {
|
|
210
|
+
kind: 'operation',
|
|
211
|
+
action: {
|
|
212
|
+
schemaVersion: 'booking.surface', kind: 'search.run', actionId: 'action-model-v3', contextRef: 'ctx-model-1', expectedRevision: 1,
|
|
213
|
+
reason: 'Run the authoritative workspace search after applying the destination.', factRefs: ['draft:destination=Dubai'], input: {},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
assert.equal(validateSearchTool(unsafeFactRefSearchDecision), false, 'model-facing tool schema rejects non-canonical factRefs before the planner authority boundary');
|
|
218
|
+
await assert.rejects(executeSearch(unsafeFactRefSearchDecision), (error) => error instanceof ToolArgsError
|
|
219
|
+
&& error.name === 'ToolArgsError'
|
|
220
|
+
&& error.code === 'INVALID_ARGS'
|
|
221
|
+
&& /decision_schema_violation/.test(error.message), 'unsafe factRefs must surface as a repairable dsh INVALID_ARGS result');
|
|
222
|
+
const reservedFactRefSearchDecision = {
|
|
223
|
+
...unsafeFactRefSearchDecision,
|
|
224
|
+
decision: {
|
|
225
|
+
...unsafeFactRefSearchDecision.decision,
|
|
226
|
+
action: {
|
|
227
|
+
...unsafeFactRefSearchDecision.decision.action,
|
|
228
|
+
factRefs: ['modelref:reserved-by-runtime'],
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
assert.equal(validateSearchTool(reservedFactRefSearchDecision), false, 'model-facing tool schema excludes the runtime-owned modelref namespace');
|
|
233
|
+
await assert.rejects(executeSearch(reservedFactRefSearchDecision), (error) => error instanceof ToolArgsError
|
|
234
|
+
&& error.name === 'ToolArgsError'
|
|
235
|
+
&& error.code === 'INVALID_ARGS'
|
|
236
|
+
&& /decision_schema_violation/.test(error.message), 'reserved modelref factRefs must surface as dsh INVALID_ARGS before the planner authority boundary');
|
|
237
|
+
assert.equal(validateSearchTool({
|
|
238
|
+
...unsafeFactRefSearchDecision,
|
|
239
|
+
decision: {
|
|
240
|
+
...unsafeFactRefSearchDecision.decision,
|
|
241
|
+
action: { ...unsafeFactRefSearchDecision.decision.action, factRefs: ['f'.repeat(513)] },
|
|
242
|
+
},
|
|
243
|
+
}), false, 'model-facing tool schema enforces the runtime factRef length boundary');
|
|
244
|
+
const unsafeActionIdSearchDecision = {
|
|
245
|
+
...unsafeFactRefSearchDecision,
|
|
246
|
+
decision: {
|
|
247
|
+
...unsafeFactRefSearchDecision.decision,
|
|
248
|
+
action: {
|
|
249
|
+
...unsafeFactRefSearchDecision.decision.action,
|
|
250
|
+
actionId: 'action/model-v3',
|
|
251
|
+
factRefs: [],
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
assert.equal(validateSearchTool(unsafeActionIdSearchDecision), false, 'model-facing tool schema rejects non-canonical actionIds before the planner authority boundary');
|
|
256
|
+
await assert.rejects(executeSearch(unsafeActionIdSearchDecision), (error) => error instanceof ToolArgsError
|
|
257
|
+
&& error.name === 'ToolArgsError'
|
|
258
|
+
&& error.code === 'INVALID_ARGS'
|
|
259
|
+
&& /decision_schema_violation/.test(error.message), 'unsafe actionIds must surface as a repairable dsh INVALID_ARGS result');
|
|
260
|
+
assert.equal(validateSearchTool({
|
|
261
|
+
...validSearchDecision,
|
|
262
|
+
decision: {
|
|
263
|
+
...validSearchDecision.decision,
|
|
264
|
+
action: {
|
|
265
|
+
...validSearchDecision.decision.action,
|
|
266
|
+
input: { patch: { unknownCriterion: { strength: 'must', value: true } } },
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
}), false, 'model-facing tool schema closes nested search criteria');
|
|
270
|
+
assert.equal(validateSearchTool({
|
|
271
|
+
...validSearchDecision,
|
|
272
|
+
decision: {
|
|
273
|
+
...validSearchDecision.decision,
|
|
274
|
+
action: {
|
|
275
|
+
...validSearchDecision.decision.action,
|
|
276
|
+
input: {
|
|
277
|
+
patch: {
|
|
278
|
+
budget: {
|
|
279
|
+
strength: 'must',
|
|
280
|
+
value: { max: { amount: 1000, currency: 'AED', sourceFactRef: 'fact-budget-1' } },
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
}), false, 'model-facing Money.amount rejects numbers');
|
|
287
|
+
assert.equal(validateSearchTool({
|
|
288
|
+
...validSearchDecision,
|
|
289
|
+
decision: {
|
|
290
|
+
...validSearchDecision.decision,
|
|
291
|
+
action: {
|
|
292
|
+
...validSearchDecision.decision.action,
|
|
293
|
+
input: { patch: { holder: { email: 'x@example.com' } } },
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
}), false, 'model-facing tool schema rejects nested holder data');
|
|
297
|
+
// The dedicated finish tool accepts only the shallow terminal proposal.
|
|
298
|
+
const rawExecuteFinish = finishTool.execute;
|
|
299
|
+
let finishConcludedTurns = 0;
|
|
300
|
+
const executeFinish = (args) => rawExecuteFinish(args, {
|
|
301
|
+
concludeTurn() { finishConcludedTurns += 1; },
|
|
302
|
+
});
|
|
303
|
+
assert.deepEqual(await executeFinish({ kind: 'terminal' }), { accepted: true, decisionKind: 'terminal' }, 'the finish tool accepts exactly the shallow terminal proposal');
|
|
304
|
+
assert.deepEqual(await executeFinish({ decision: { kind: 'terminal' } }), { accepted: true, decisionKind: 'terminal' }, 'the finish tool normalizes the one-layer provider terminal wrapper');
|
|
305
|
+
assert.equal(finishConcludedTurns, 2);
|
|
306
|
+
await assert.rejects(executeFinish({ kind: 'terminal', status: 'completed', summary: 'forged' }), (error) => error instanceof ToolArgsError && error.code === 'INVALID_ARGS', 'model-authored terminal metadata stays rejected on the dedicated finish tool');
|
|
307
|
+
await assert.rejects(executeFinish({ kind: 'search.patch', input: {}, intent: {} }), (error) => error instanceof ToolArgsError && error.code === 'INVALID_ARGS', 'the finish tool never accepts an operation proposal');
|
|
308
|
+
assert.equal(finishConcludedTurns, 2);
|
|
309
|
+
console.log('BOOKING COPILOT DSH PLUGIN PROOF: per-kind typed tools/no union/no Book OK');
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/** Real SDK initialize / provider-stall proof with a controllable CLI startup gate. */
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { createServer } from 'node:http';
|
|
4
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { dirname, join } from 'node:path';
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
8
|
+
import { createDshEmbeddedBookingPlanner, createRealRunPort, PLANNER_BOOT_BUDGET_MS } from '../src/booking-surface/dsh-planner.js';
|
|
9
|
+
const root = mkdtempSync(join(tmpdir(), 'gotry-506-readiness-'));
|
|
10
|
+
const priorWarmup = process.env.GOTRY_BOOKING_COPILOT_WARMUP;
|
|
11
|
+
process.env.GOTRY_BOOKING_COPILOT_WARMUP = '0'; // this proof owns exactly one port per case
|
|
12
|
+
const actualBin = pathToFileURL(join(dirname(fileURLToPath(import.meta.resolve('@deepseek-ai/dsh/package.json'))), 'lib/bin.js')).href;
|
|
13
|
+
const requests = [];
|
|
14
|
+
const sockets = new Set();
|
|
15
|
+
const server = createServer((req, res) => {
|
|
16
|
+
let text = '';
|
|
17
|
+
req.on('data', (part) => { text += part.toString('utf8'); });
|
|
18
|
+
req.on('end', () => {
|
|
19
|
+
requests.push(text);
|
|
20
|
+
res.writeHead(200, { 'content-type': 'text/event-stream' });
|
|
21
|
+
res.flushHeaders(); // the real SDK has a provider connection that never completes
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
server.on('connection', (socket) => { sockets.add(socket); socket.on('close', () => sockets.delete(socket)); });
|
|
25
|
+
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
|
|
26
|
+
const address = server.address();
|
|
27
|
+
assert.ok(address && typeof address !== 'string');
|
|
28
|
+
const env = { PATH: process.env.PATH, DEEPSEEK_API_KEY: 'fixture-model-key', DEEPSEEK_BASE_URL: `http://127.0.0.1:${address.port}/v1` };
|
|
29
|
+
const workspace = {
|
|
30
|
+
schemaVersion: 'booking.surface', contextRef: 'ctx-506', surface: 'tenant', revision: 0,
|
|
31
|
+
locale: 'en', currency: 'AED', searchDraft: {}, results: { status: 'idle' },
|
|
32
|
+
visibleHotels: [], loadedOffers: [], shortlistedOfferRefs: [],
|
|
33
|
+
capabilities: { surface: 'tenant', allowedActions: ['search.run'] },
|
|
34
|
+
};
|
|
35
|
+
const task = {
|
|
36
|
+
schemaVersion: 'booking.surface', taskId: 'task-506', contextRef: workspace.contextRef,
|
|
37
|
+
surface: 'tenant', revision: 0, allowedActions: ['search.run'], userTurnCount: 1,
|
|
38
|
+
lastTurnId: 'turn-506', operationCount: 0, phase: 'planning', lastSequence: 0,
|
|
39
|
+
availability: { initialized: true, recoveryStarted: false, availabilityPhase: 'need_offers', activeHotelOrdinal: 0, hotelRefs: [], hotels: {}, attempts: [], queryReservations: [] },
|
|
40
|
+
workspaceSnapshot: workspace,
|
|
41
|
+
};
|
|
42
|
+
const turn = { schemaVersion: 'booking.surface', kind: 'user.turn', taskId: task.taskId, turnId: task.lastTurnId, workspace, request: { text: 'STALL_PROVIDER_PROOF_506' } };
|
|
43
|
+
async function bounded(promise, ms, label) {
|
|
44
|
+
let timer;
|
|
45
|
+
try {
|
|
46
|
+
return await Promise.race([promise, new Promise((_, reject) => { timer = setTimeout(() => reject(new Error(label)), ms); })]);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
clearTimeout(timer);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function until(check, ms, label) {
|
|
53
|
+
const limit = Date.now() + ms;
|
|
54
|
+
while (!check()) {
|
|
55
|
+
if (Date.now() >= limit)
|
|
56
|
+
throw new Error(label);
|
|
57
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function alive(pid) { try {
|
|
61
|
+
process.kill(pid, 0);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return false;
|
|
66
|
+
} }
|
|
67
|
+
function gate(name) {
|
|
68
|
+
const marker = join(root, `${name}-started.json`);
|
|
69
|
+
const release = join(root, `${name}-release`);
|
|
70
|
+
const dshBin = join(root, `${name}-dsh.mjs`);
|
|
71
|
+
writeFileSync(dshBin, `import { existsSync, writeFileSync } from 'node:fs'\nwriteFileSync(${JSON.stringify(marker)}, JSON.stringify({pid:process.pid,workerPid:process.ppid,startupPhase:'before_initialize'}))\nconst deadline=Date.now()+15000\nwhile(!existsSync(${JSON.stringify(release)})){if(Date.now()>deadline)process.exit(73);await new Promise(r=>setTimeout(r,10))}\nconst {runCli}=await import(${JSON.stringify(actualBin)});await runCli()\n`);
|
|
72
|
+
return { marker, release, dshBin, pids: () => JSON.parse(readFileSync(marker, 'utf8')) };
|
|
73
|
+
}
|
|
74
|
+
let port;
|
|
75
|
+
let planner;
|
|
76
|
+
let phase = 'initial';
|
|
77
|
+
let evidence = {};
|
|
78
|
+
try {
|
|
79
|
+
const ready = gate('ready');
|
|
80
|
+
port = await createRealRunPort({ stateRoot: root, env, dshBin: ready.dshBin, maxTokens: 512 });
|
|
81
|
+
phase = 'awaiting_real_initialize';
|
|
82
|
+
let warmupSettled = false;
|
|
83
|
+
const warming = port.warmup().then(() => { warmupSettled = true; });
|
|
84
|
+
void warming.catch(() => undefined); // the bounded await below owns setup failure reporting
|
|
85
|
+
await until(() => existsSync(ready.marker) || warmupSettled, 5_000, 'real dsh never entered startup gate');
|
|
86
|
+
assert.equal(warmupSettled, false, 'warmup must not report ready before the real runtime starts and initializes');
|
|
87
|
+
const readyPids = ready.pids();
|
|
88
|
+
evidence = { ...readyPids, providerRequests: requests.length };
|
|
89
|
+
assert.equal(requests.length, 0, 'initialize never consumes a provider call');
|
|
90
|
+
// A known cold-start delay is outside the provider-stall measurement.
|
|
91
|
+
await new Promise((resolve) => setTimeout(resolve, 1_800));
|
|
92
|
+
assert.equal(warmupSettled, false, 'startup gate still prevents false readiness after 1800ms');
|
|
93
|
+
writeFileSync(ready.release, 'release');
|
|
94
|
+
await bounded(warming, 10_000, 'runtime initialize did not complete');
|
|
95
|
+
assert.equal(requests.length, 0, 'completed warmup still makes zero model calls');
|
|
96
|
+
assert.ok(alive(readyPids.pid) && alive(readyPids.workerPid), 'both owned processes are alive after initialize');
|
|
97
|
+
// Our own boot bill must stay readable next to the provider budget it shares
|
|
98
|
+
// a wire window with, so the port reports what the handshake actually cost.
|
|
99
|
+
const readyBoot = port.bootObservation?.();
|
|
100
|
+
assert.ok(readyBoot, 'the real port must observe its own initialize handshake');
|
|
101
|
+
const bootMsAtFirstTurn = readyBoot.initializeMs;
|
|
102
|
+
assert.equal(readyBoot.mode, 'started', 'the first request in a fresh worker performs the handshake');
|
|
103
|
+
assert.ok(bootMsAtFirstTurn > 0, `real initialize cost must be measured: ${JSON.stringify(readyBoot)}`);
|
|
104
|
+
evidence = { ...readyPids, providerRequests: requests.length, boot: readyBoot };
|
|
105
|
+
const readyPort = port;
|
|
106
|
+
const readyMetrics = [];
|
|
107
|
+
planner = await createDshEmbeddedBookingPlanner({
|
|
108
|
+
runPortFactory: () => readyPort,
|
|
109
|
+
turnTimeoutMs: 1_500,
|
|
110
|
+
onMetric: (metric) => readyMetrics.push(metric),
|
|
111
|
+
});
|
|
112
|
+
phase = 'provider_stall';
|
|
113
|
+
const started = Date.now();
|
|
114
|
+
const resultPromise = planner.plannerFactory(task).next({ turn, task });
|
|
115
|
+
await until(() => requests.length === 1, 3_000, 'initialized real provider was not reached');
|
|
116
|
+
const providerObservedMs = Date.now() - started;
|
|
117
|
+
const modelRequest = JSON.parse(requests[0]);
|
|
118
|
+
// 0.2.0-rc.2 起工具条目是平铺 {name}(旧形态是 {function:{name}} 包裹);
|
|
119
|
+
// 断言的是 booking 插件在场,两种线型都接受。
|
|
120
|
+
assert.ok(modelRequest.tools.some((tool) => tool.function?.name === 'booking_run_search' || tool.name === 'booking_run_search'), 'real initialized runtime includes the GoTry booking plugin');
|
|
121
|
+
const result = await bounded(resultPromise, 3_000, 'planner did not return a bounded timeout');
|
|
122
|
+
const elapsedMs = Date.now() - started;
|
|
123
|
+
assert.equal(result[0]?.kind === 'error' && result[0].error.code, 'PLANNER_PROVIDER_TIMEOUT');
|
|
124
|
+
assert.ok(elapsedMs < 3_000, `provider deadline remains bounded: ${elapsedMs}ms`);
|
|
125
|
+
const stalledTurnMetric = readyMetrics[0];
|
|
126
|
+
assert.ok(stalledTurnMetric, 'the provider-stall turn must report its own metric');
|
|
127
|
+
assert.equal(stalledTurnMetric.bootMode, 'reused', 'a turn on an initialized port reuses the runtime instead of starting one');
|
|
128
|
+
assert.equal(stalledTurnMetric.bootMs, 0, 'a reused runtime contributes no boot time to the turn');
|
|
129
|
+
phase = 'ready_cleanup';
|
|
130
|
+
const cleanupAt = Date.now();
|
|
131
|
+
await bounded(planner.close(), 3_000, 'ready tree cleanup exceeded budget');
|
|
132
|
+
planner = undefined;
|
|
133
|
+
port = undefined;
|
|
134
|
+
assert.ok(!alive(readyPids.pid) && !alive(readyPids.workerPid), 'ready runtime and worker both reaped');
|
|
135
|
+
console.log('DSH READINESS READY:', JSON.stringify({ startupDelayMs: 1800, warmupProviderCalls: 0, providerRequests: requests.length, firstTurnBootMs: bootMsAtFirstTurn, reusedTurnBootMs: stalledTurnMetric.bootMs, providerObservedMs, elapsedMs, cleanupMs: Date.now() - cleanupAt, treeReaped: true }));
|
|
136
|
+
const cold = gate('cold');
|
|
137
|
+
phase = 'cold_start_timeout';
|
|
138
|
+
const coldPort = await createRealRunPort({ stateRoot: root, env, dshBin: cold.dshBin, maxTokens: 512 });
|
|
139
|
+
port = coldPort;
|
|
140
|
+
planner = await createDshEmbeddedBookingPlanner({ runPortFactory: () => coldPort, turnTimeoutMs: 1_500 });
|
|
141
|
+
const coldAt = Date.now();
|
|
142
|
+
const coldResultPromise = planner.plannerFactory(task).next({ turn, task });
|
|
143
|
+
await until(() => existsSync(cold.marker), 3_000, 'cold real runtime never entered startup gate');
|
|
144
|
+
const coldPids = cold.pids();
|
|
145
|
+
evidence = { ...coldPids, providerRequests: requests.length };
|
|
146
|
+
const coldResult = await bounded(coldResultPromise, 3_000, 'cold-start timeout did not settle');
|
|
147
|
+
const coldElapsedMs = Date.now() - coldAt;
|
|
148
|
+
// The handshake never completed, so no provider call happened: the failure is
|
|
149
|
+
// ours to own and is typed as a local boot timeout, not a provider stall.
|
|
150
|
+
assert.equal(coldResult[0]?.kind === 'error' && coldResult[0].error.code, 'PLANNER_BOOT_TIMEOUT');
|
|
151
|
+
assert.equal(requests.length, 1, 'cold blocked startup reaches no provider');
|
|
152
|
+
await bounded(planner.close(), 3_000, 'cold tree cleanup exceeded budget');
|
|
153
|
+
planner = undefined;
|
|
154
|
+
port = undefined;
|
|
155
|
+
assert.ok(!alive(coldPids.pid) && !alive(coldPids.workerPid), 'cold runtime and worker both reaped');
|
|
156
|
+
console.log('DSH READINESS COLD:', JSON.stringify({ providerRequests: 0, elapsedMs: coldElapsedMs, code: 'PLANNER_BOOT_TIMEOUT', treeReaped: true }));
|
|
157
|
+
// The local boot budget must govern its own phase. With a model-stall budget
|
|
158
|
+
// wider than the boot budget, only the explicit initialize deadline can
|
|
159
|
+
// settle this turn inside the asserted window; the SDK's 10s fallback or the
|
|
160
|
+
// 13.3s stall budget would both land later and fail the bound below.
|
|
161
|
+
phase = 'boot_budget_bound';
|
|
162
|
+
const budgetGate = gate('boot-budget');
|
|
163
|
+
port = await createRealRunPort({ stateRoot: root, env, dshBin: budgetGate.dshBin, maxTokens: 512 });
|
|
164
|
+
planner = await createDshEmbeddedBookingPlanner({ runPortFactory: () => port, turnTimeoutMs: 20_000 });
|
|
165
|
+
const budgetAt = Date.now();
|
|
166
|
+
const budgetResultPromise = planner.plannerFactory(task).next({ turn, task });
|
|
167
|
+
await until(() => existsSync(budgetGate.marker), 3_000, 'boot-budget runtime never entered startup gate');
|
|
168
|
+
const budgetPids = budgetGate.pids();
|
|
169
|
+
const budgetResult = await bounded(budgetResultPromise, 15_000, 'boot budget did not settle the turn');
|
|
170
|
+
const budgetElapsedMs = Date.now() - budgetAt;
|
|
171
|
+
assert.equal(budgetResult[0]?.kind === 'error' && budgetResult[0].error.code, 'PLANNER_BOOT_TIMEOUT');
|
|
172
|
+
assert.ok(budgetElapsedMs >= PLANNER_BOOT_BUDGET_MS, `the boot budget must be consumed before it fires (${budgetElapsedMs}ms)`);
|
|
173
|
+
assert.ok(budgetElapsedMs < PLANNER_BOOT_BUDGET_MS + 6_000, `the local boot budget, not the 13.3s stall budget, must settle this turn (${budgetElapsedMs}ms)`);
|
|
174
|
+
assert.equal(requests.length, 1, 'a blocked handshake makes no provider request');
|
|
175
|
+
await bounded(planner.close(), 3_000, 'boot-budget tree cleanup exceeded budget');
|
|
176
|
+
planner = undefined;
|
|
177
|
+
port = undefined;
|
|
178
|
+
assert.ok(!alive(budgetPids.pid) && !alive(budgetPids.workerPid), 'boot-budget runtime and worker both reaped');
|
|
179
|
+
console.log('DSH READINESS BOOT BUDGET:', JSON.stringify({ budgetMs: PLANNER_BOOT_BUDGET_MS, elapsedMs: budgetElapsedMs, code: 'PLANNER_BOOT_TIMEOUT', providerRequests: 0, treeReaped: true }));
|
|
180
|
+
phase = 'initialize_failure';
|
|
181
|
+
const failedMarker = join(root, 'failed-start.json');
|
|
182
|
+
const failedBin = join(root, 'failed-dsh.mjs');
|
|
183
|
+
writeFileSync(failedBin, `import { writeFileSync } from 'node:fs'\nwriteFileSync(${JSON.stringify(failedMarker)}, JSON.stringify({pid:process.pid,workerPid:process.ppid,exitCode:23,signal:null}))\nprocess.stderr.write('PRIVATE_FIXTURE_DIAGNOSTIC');process.exit(23)\n`);
|
|
184
|
+
port = await createRealRunPort({ stateRoot: root, env, dshBin: failedBin, maxTokens: 512 });
|
|
185
|
+
await assert.rejects(bounded(port.warmup(), 5_000, 'failed initialize did not settle'), { message: 'HARNESS_START_FAILED' });
|
|
186
|
+
const failed = JSON.parse(readFileSync(failedMarker, 'utf8'));
|
|
187
|
+
assert.equal(failed.exitCode, 23);
|
|
188
|
+
assert.equal(failed.signal, null);
|
|
189
|
+
assert.equal(requests.length, 1, 'failed initialize makes no provider calls');
|
|
190
|
+
await bounded(port.close(), 3_000, 'failed-start cleanup exceeded budget');
|
|
191
|
+
port = undefined;
|
|
192
|
+
assert.ok(!alive(failed.pid) && !alive(failed.workerPid), 'failed runtime and worker both reaped');
|
|
193
|
+
console.log('DSH READINESS FAILURE CLASSIFICATION:', JSON.stringify({ exitCode: failed.exitCode, signal: failed.signal, error: 'HARNESS_START_FAILED', rawStderrExposed: false, providerRequests: 0, treeReaped: true }));
|
|
194
|
+
// Opt-in distribution run: boot fresh runtimes back to back so the local
|
|
195
|
+
// initialize budget can be set from measured percentiles instead of a guess.
|
|
196
|
+
// Each sample is a new worker plus a new dsh home, so the page cache is the
|
|
197
|
+
// only warm input; the numbers are observations, not a service guarantee.
|
|
198
|
+
const bootSampleCount = Number(process.env.GOTRY_BOOT_SAMPLES ?? 0);
|
|
199
|
+
if (Number.isSafeInteger(bootSampleCount) && bootSampleCount > 0) {
|
|
200
|
+
phase = 'boot_sampling';
|
|
201
|
+
const samples = [];
|
|
202
|
+
for (let index = 1; index <= bootSampleCount; index += 1) {
|
|
203
|
+
const samplePort = await createRealRunPort({ stateRoot: root, env, maxTokens: 512 });
|
|
204
|
+
try {
|
|
205
|
+
await bounded(samplePort.warmup(), 30_000, `boot sample ${index} initialize did not complete`);
|
|
206
|
+
const observation = samplePort.bootObservation?.();
|
|
207
|
+
assert.ok(observation, `boot sample ${index} must observe its initialize handshake`);
|
|
208
|
+
assert.equal(observation.mode, 'started', `boot sample ${index} boots a fresh worker`);
|
|
209
|
+
samples.push(observation.initializeMs);
|
|
210
|
+
}
|
|
211
|
+
finally {
|
|
212
|
+
await bounded(samplePort.close(), 10_000, `boot sample ${index} cleanup exceeded budget`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const sorted = [...samples].sort((left, right) => left - right);
|
|
216
|
+
const percentile = (fraction) => sorted[Math.min(sorted.length - 1, Math.floor(fraction * sorted.length))];
|
|
217
|
+
console.log('DSH BOOT SAMPLES:', JSON.stringify({ count: sorted.length, minMs: sorted[0], p50Ms: percentile(0.5), p95Ms: percentile(0.95), maxMs: sorted.at(-1) }));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
console.error('DSH READINESS FAILURE:', JSON.stringify({ phase, ...evidence, providerRequests: requests.length, error: error instanceof Error ? error.message : 'unknown' }));
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
const cleanup = await Promise.allSettled([planner?.close(), port?.close()]);
|
|
226
|
+
for (const socket of sockets)
|
|
227
|
+
socket.destroy();
|
|
228
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
229
|
+
rmSync(root, { recursive: true, force: true });
|
|
230
|
+
if (priorWarmup === undefined)
|
|
231
|
+
delete process.env.GOTRY_BOOKING_COPILOT_WARMUP;
|
|
232
|
+
else
|
|
233
|
+
process.env.GOTRY_BOOKING_COPILOT_WARMUP = priorWarmup;
|
|
234
|
+
const failedCleanup = cleanup.find((result) => result.status === 'rejected');
|
|
235
|
+
if (failedCleanup?.status === 'rejected')
|
|
236
|
+
throw failedCleanup.reason;
|
|
237
|
+
}
|
|
238
|
+
console.log('BOOKING COPILOT DSH READINESS PROOF: real initialize, provider stall, cold timeout and process cleanup OK');
|