@danceiny/gotry 0.0.1-rc.9 → 0.2.0-rc.27
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 +1744 -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 +135 -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 +546 -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 +509 -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/resilience.js +191 -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-tests.js +116 -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 +123 -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 +1479 -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/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 +564 -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-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 +131 -0
- package/dist/scripts/g5-guard.js +189 -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/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 +161 -0
- package/dist/scripts/nightly-evidence.js +286 -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/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 +20 -18
- package/dist/scripts/replay-real.js +18 -18
- package/dist/scripts/replay.js +52 -82
- 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-zones-tests.js +358 -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 +241 -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 +550 -199
- 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 +210 -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 +23 -19
- 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 +41 -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/companions.js +48 -74
- package/dist/src/contracts.js +21 -43
- package/dist/src/dsh-llm.js +97 -111
- 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 +2227 -882
- 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 +319 -160
- 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/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-zones.js +507 -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 +435 -516
- 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 +535 -359
- 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 +255 -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 +568 -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 +13 -3
- package/ts/src/dsh-llm.ts +44 -11
- package/ts/src/flyai-setup-tool.ts +77 -0
- package/ts/src/index.ts +1948 -198
- 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 +223 -25
- 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 +912 -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 +354 -88
- package/ts/src/wish-pool.ts +79 -0
- package/dist/scripts/session-attach-wait.js +0 -41
|
@@ -1,143 +1,280 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* hotelbyte-cli(hbcli)能力层封装:进程内 spawn bun + hbcli --json。
|
|
3
|
+
*
|
|
4
|
+
* 拿到外部资源信息能力的三条路径:
|
|
5
|
+
* 1. 实时 hbcli 调用(能力强,延迟 1-3s,需凭证 + 证书有效)
|
|
6
|
+
* 2. 静态数据包(data/hotels_2026.json)——证据标注 [静态包:估算]
|
|
7
|
+
* 3. 简易降级返回(什么都不查得到时,提供"工具暂不可用"而不是抛错)
|
|
8
|
+
*
|
|
9
|
+
* 证书/凭证/网络问题一律降级,不抛到调用方——能力层契约:永远返回一种结果。
|
|
10
|
+
*
|
|
11
|
+
* §7-1/§7-2 L4 不变量:证据链标注([实时API:hbcli@<ts>] / [静态包:估算]);
|
|
12
|
+
* 估算必须显式标记。这是 L4 与 L1 透明卡片的接缝。
|
|
13
|
+
*/
|
|
14
|
+
import { homedir } from 'node:os';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { spawnBounded } from './spawn-bounded.js';
|
|
17
|
+
/**
|
|
18
|
+
* hbcli 二进制候选路径(gotry setup 按官方脚本装到 ~/.local/bin/hbcli,
|
|
19
|
+
* symlink 指向 ~/.staicli/current/hbcli——当 PATH 不含 ~/.local/bin 时裸名
|
|
20
|
+
* spawn 仍 ENOENT,按已知安装位回退)。仅对默认名 'hbcli' 扩展;显式自定义
|
|
21
|
+
* 名(如测试注入的不存在路径)不扩展,保持配置即所用的可测性。
|
|
22
|
+
*/
|
|
23
|
+
export function hbcliBinCandidates(bin, homeDir = homedir()) {
|
|
24
|
+
if (bin !== 'hbcli')
|
|
25
|
+
return [bin];
|
|
26
|
+
return [bin, join(homeDir, '.local/bin/hbcli'), join(homeDir, '.staicli/current/hbcli')];
|
|
27
|
+
}
|
|
28
|
+
/** 单个候选的一次 spawn 封装:失败不抛,返回降级结果(spawnError 标记 ENOENT 类失败供上层换候选) */
|
|
29
|
+
async function attemptHbcli(bin, args, opts) {
|
|
3
30
|
const started = Date.now();
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
HOTELBYTE_ENV: env
|
|
9
|
-
};
|
|
10
|
-
if (opts.token) envVars['HOTELBYTE_TOKEN'] = opts.token;
|
|
11
|
-
return new Promise((resolve)=>{
|
|
12
|
-
let stdout = '';
|
|
13
|
-
let stderr = '';
|
|
14
|
-
let settled = false;
|
|
15
|
-
const child = spawn(bin, args, {
|
|
16
|
-
env: {
|
|
17
|
-
...process.env,
|
|
18
|
-
...envVars
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
const timer = setTimeout(()=>{
|
|
22
|
-
if (!settled) {
|
|
23
|
-
settled = true;
|
|
24
|
-
child.kill('SIGKILL');
|
|
25
|
-
resolve({
|
|
26
|
-
via: 'hbcli-error',
|
|
27
|
-
exitCode: -1,
|
|
28
|
-
result: null,
|
|
29
|
-
evidence: `[实时API:hbcli@timeout@${new Date().toISOString()}]`,
|
|
30
|
-
latencyMs: Date.now() - started,
|
|
31
|
-
error: `timeout after ${timeoutMs}ms`
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
}, timeoutMs);
|
|
35
|
-
child.stdout.on('data', (d)=>{
|
|
36
|
-
stdout += d.toString();
|
|
37
|
-
});
|
|
38
|
-
child.stderr.on('data', (d)=>{
|
|
39
|
-
stderr += d.toString();
|
|
40
|
-
});
|
|
41
|
-
child.on('close', (code)=>{
|
|
42
|
-
if (settled) return;
|
|
43
|
-
settled = true;
|
|
44
|
-
clearTimeout(timer);
|
|
45
|
-
const latencyMs = Date.now() - started;
|
|
46
|
-
if (code !== 0) {
|
|
47
|
-
resolve({
|
|
48
|
-
via: 'hbcli-error',
|
|
49
|
-
exitCode: code ?? -1,
|
|
50
|
-
result: null,
|
|
51
|
-
evidence: `[实时API:hbcli@error@${new Date().toISOString()}]`,
|
|
52
|
-
stderr: stderr.slice(0, 2000),
|
|
53
|
-
latencyMs,
|
|
54
|
-
error: stderr.trim().slice(0, 200) || `exit ${code}`
|
|
55
|
-
});
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
const jStart = stdout.search(/[\{\[]/);
|
|
59
|
-
const jsonStr = jStart >= 0 ? stdout.slice(jStart) : '';
|
|
60
|
-
let result = null;
|
|
61
|
-
if (jsonStr) {
|
|
62
|
-
try {
|
|
63
|
-
result = JSON.parse(jsonStr);
|
|
64
|
-
} catch {}
|
|
65
|
-
}
|
|
66
|
-
resolve({
|
|
67
|
-
via: 'hbcli-realtime',
|
|
68
|
-
exitCode: 0,
|
|
69
|
-
result,
|
|
70
|
-
evidence: `[实时API:hbcli@${new Date().toISOString()}]`,
|
|
71
|
-
stdout: stdout.slice(0, 2000),
|
|
72
|
-
latencyMs
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
child.on('error', (e)=>{
|
|
76
|
-
if (settled) return;
|
|
77
|
-
settled = true;
|
|
78
|
-
clearTimeout(timer);
|
|
79
|
-
resolve({
|
|
80
|
-
via: 'hbcli-error',
|
|
81
|
-
exitCode: -1,
|
|
82
|
-
result: null,
|
|
83
|
-
evidence: `[实时API:hbcli@spawn_error@${new Date().toISOString()}]`,
|
|
84
|
-
latencyMs: Date.now() - started,
|
|
85
|
-
error: e.message
|
|
86
|
-
});
|
|
87
|
-
});
|
|
31
|
+
const r = await spawnBounded(bin, args, {
|
|
32
|
+
env: { ...process.env, ...opts.envVars },
|
|
33
|
+
timeoutMs: opts.timeoutMs,
|
|
34
|
+
signal: opts.signal,
|
|
88
35
|
});
|
|
36
|
+
const latencyMs = Date.now() - started;
|
|
37
|
+
const ts = new Date().toISOString();
|
|
38
|
+
const cleanupError = r.groupReaped === false ? 'process group cleanup incomplete' : undefined;
|
|
39
|
+
if (r.aborted) {
|
|
40
|
+
return {
|
|
41
|
+
via: 'hbcli-error', exitCode: r.code ?? -1, result: null,
|
|
42
|
+
evidence: `[实时API:hbcli@abort@${ts}]`,
|
|
43
|
+
stderr: r.stderr.slice(0, 2000), latencyMs,
|
|
44
|
+
error: ['aborted by host signal', cleanupError].filter(Boolean).join('; '),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (r.error || r.groupReaped === false || r.code !== 0) {
|
|
48
|
+
const error = [r.error, r.stderr.trim().slice(0, 200), cleanupError].filter(Boolean).join('; ');
|
|
49
|
+
const failureKind = r.timedOut ? 'timeout' : /ENOENT/.test(r.error ?? '') ? 'spawn_error' : 'error';
|
|
50
|
+
return {
|
|
51
|
+
via: 'hbcli-error', exitCode: r.code ?? -1, result: null,
|
|
52
|
+
evidence: `[实时API:hbcli@${failureKind}@${ts}]`,
|
|
53
|
+
stderr: r.stderr.slice(0, 2000),
|
|
54
|
+
latencyMs, error: error || `exit ${r.code}`,
|
|
55
|
+
spawnError: /ENOENT/.test(r.error ?? ''),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// 尝试 JSON 解析
|
|
59
|
+
const jStart = r.stdout.search(/[\{\[]/);
|
|
60
|
+
const jsonStr = jStart >= 0 ? r.stdout.slice(jStart) : '';
|
|
61
|
+
let result = null;
|
|
62
|
+
if (jsonStr) {
|
|
63
|
+
try {
|
|
64
|
+
result = JSON.parse(jsonStr);
|
|
65
|
+
}
|
|
66
|
+
catch { /* 非 JSON 输出,留给调用方处理 */ }
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
via: 'hbcli-realtime', exitCode: 0, result,
|
|
70
|
+
evidence: `[实时API:hbcli@${ts}]`,
|
|
71
|
+
stdout: r.stdout.slice(0, 2000), latencyMs,
|
|
72
|
+
};
|
|
89
73
|
}
|
|
74
|
+
/** 通用 hbcli JSON 调用封装:失败不抛,而是返回降级结果 */
|
|
75
|
+
export async function callHbcliJson(args, opts = {}) {
|
|
76
|
+
const env = opts.env ?? 'uat';
|
|
77
|
+
const envVars = { HOTELBYTE_ENV: env };
|
|
78
|
+
if (opts.token)
|
|
79
|
+
envVars['HOTELBYTE_TOKEN'] = opts.token;
|
|
80
|
+
const callOpts = { timeoutMs: opts.timeoutMs ?? 15_000, env, envVars, signal: opts.signal };
|
|
81
|
+
const candidates = hbcliBinCandidates(opts.hbcliBin ?? 'hbcli');
|
|
82
|
+
// abort 状态不可触发 fallback 候选;spawn 前的 pre-aborted 调用整体零 spawn
|
|
83
|
+
if (opts.signal?.aborted) {
|
|
84
|
+
return attemptHbcli(candidates[0], args, callOpts);
|
|
85
|
+
}
|
|
86
|
+
let last;
|
|
87
|
+
for (const bin of candidates) {
|
|
88
|
+
last = await attemptHbcli(bin, args, callOpts);
|
|
89
|
+
// spawn 级失败(ENOENT 等)且还有候选 → 换下一个已知安装位;其余失败(退码/超时/abort)无重试意义
|
|
90
|
+
if (!(last.spawnError && candidates.indexOf(bin) < candidates.length - 1))
|
|
91
|
+
return last;
|
|
92
|
+
}
|
|
93
|
+
return last;
|
|
94
|
+
}
|
|
95
|
+
/** 上游 i18n 名称容错读取(zh 优先,中文优先产品口径;回退 en/ar) */
|
|
96
|
+
function i18nName(v) {
|
|
97
|
+
if (!v)
|
|
98
|
+
return '';
|
|
99
|
+
if (typeof v === 'string')
|
|
100
|
+
return v;
|
|
101
|
+
return v.zh || v.en || v.ar || '';
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* 实时 hotelList 响应 → 模型可读紧凑行(id/名称/星级/最低价)。
|
|
105
|
+
* issue #195:render 面只透 summary,若 summary 不含数据,模型只看到
|
|
106
|
+
* 「实时返回」四个字——工具调用等于白调。id 必须在行内,hotel-rates 的入参
|
|
107
|
+
* 就来自这里。上游 wire:HotelListResp{list:[Hotel{id,name:{en,zh},star,minPrice:{amount,currency}}]}。
|
|
108
|
+
*/
|
|
109
|
+
function hotelLines(result, max = 8) {
|
|
110
|
+
const list = result?.list;
|
|
111
|
+
if (!Array.isArray(list) || list.length === 0)
|
|
112
|
+
return '';
|
|
113
|
+
const lines = [];
|
|
114
|
+
for (const item of list.slice(0, max)) {
|
|
115
|
+
const h = item;
|
|
116
|
+
const name = i18nName(h.name) || `hotel ${h.id ?? '?'}`;
|
|
117
|
+
const star = typeof h.star === 'number' && h.star > 0 ? ` ${h.star}★` : '';
|
|
118
|
+
const price = h.minPrice && typeof h.minPrice.amount === 'number'
|
|
119
|
+
? ` 低至 ${h.minPrice.amount}${h.minPrice.currency ? ` ${h.minPrice.currency}` : ''}`
|
|
120
|
+
: '';
|
|
121
|
+
lines.push(`- id=${h.id ?? '?'} ${name}${star}${price}`);
|
|
122
|
+
}
|
|
123
|
+
if (list.length > max)
|
|
124
|
+
lines.push(`…(共 ${list.length} 家,仅列前 ${max})`);
|
|
125
|
+
return lines.join('\n');
|
|
126
|
+
}
|
|
127
|
+
/** 高层语义化封装:酒店列表查询(down-tier to 静态包 + 证据链标注) */
|
|
90
128
|
export async function searchHotels(query, opts = {}) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
'
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (query.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
]));
|
|
129
|
+
// 旗标对齐上游 CLI(hotelbyte-com/hotelbyte-cli):--destination-name + --room-occupancies;
|
|
130
|
+
// hotel-list 支持日期旗标(--check-in/--check-out,上游帮助文档在册)——issue #195 教训:
|
|
131
|
+
// 「日期不传上游」会让实时价退化为当前窗口价,用户要下周五的房给的是今天的价。
|
|
132
|
+
const hbArgs = ['search', 'hotel-list', '--json', '--page-size', '10'];
|
|
133
|
+
if (query.destination)
|
|
134
|
+
hbArgs.push('--destination-name', query.destination);
|
|
135
|
+
if (query.checkIn)
|
|
136
|
+
hbArgs.push('--check-in', query.checkIn);
|
|
137
|
+
if (query.checkOut)
|
|
138
|
+
hbArgs.push('--check-out', query.checkOut);
|
|
139
|
+
if (query.adults)
|
|
140
|
+
hbArgs.push('--room-occupancies', JSON.stringify([{ adultCount: query.adults, childrenAges: [] }]));
|
|
105
141
|
const live = await callHbcliJson(hbArgs, opts);
|
|
106
|
-
|
|
142
|
+
// 取消立即结束:不读静态包、不产出命中/无结果/估算,也不计上游故障
|
|
143
|
+
if (opts.signal?.aborted || /abort/i.test(live.error ?? '')) {
|
|
144
|
+
const ts = new Date().toISOString();
|
|
145
|
+
const cleanupError = /cleanup incomplete/i.test(live.error ?? '') ? `; ${live.error}` : '';
|
|
107
146
|
return {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
147
|
+
via: 'hbcli-error', exitCode: live.exitCode, result: null,
|
|
148
|
+
evidence: `[实时API:hbcli@abort@${ts}]`,
|
|
149
|
+
latencyMs: live.latencyMs, error: `aborted by host signal${cleanupError}`, summary: `酒店「${query.destination}」检索已取消,未产生事实`,
|
|
111
150
|
};
|
|
112
151
|
}
|
|
152
|
+
if (live.via === 'hbcli-realtime') {
|
|
153
|
+
// summary 即模型面(render 只透 summary):必须自带紧凑数据行 + 证据链,
|
|
154
|
+
// 否则模型只看到「实时返回」占位一句话(issue #195 实锤)。
|
|
155
|
+
const list = live.result?.list;
|
|
156
|
+
const n = Array.isArray(list) ? list.length : 0;
|
|
157
|
+
const dateTag = query.checkIn
|
|
158
|
+
? `入住 ${query.checkIn}${query.checkOut ? ` → 退房 ${query.checkOut}` : ''}`
|
|
159
|
+
: '未传日期(上游按当前窗口价)';
|
|
160
|
+
const lines = hotelLines(live.result);
|
|
161
|
+
const summary = `${query.destination}:hbcli 实时 ${n} 家(${dateTag})${lines ? `\n${lines}` : ''}\n${live.evidence}`;
|
|
162
|
+
return { ...live, hotels: live.result, summary };
|
|
163
|
+
}
|
|
164
|
+
// 降级原因人话化(issue #24):hbcli 未安装时按 gotry setup 指引(npm 安装期已
|
|
165
|
+
// 自动跑过官方脚本;PATH 未含 ~/.local/bin 时上方候选路径也已兜住),
|
|
166
|
+
// 裸 "spawn hbcli ENOENT" 读起来像工具坏了——实际静态包降级是设计行为
|
|
167
|
+
const rawReason = live.error ?? live.via;
|
|
168
|
+
const reason = /ENOENT/i.test(rawReason) ? '未安装 hbcli(可选实时源;npx @danceiny/gotry setup 可按官方脚本安装)' : rawReason;
|
|
169
|
+
// 降级:读静态包,按目的地过滤命中的住宿块(issue #24)——整包倾倒会把无关场景
|
|
170
|
+
// (深圳/普吉/曼谷/云南/大理混装)灌给模型且不指明哪块相关;包内无该目的地时明示
|
|
171
|
+
// 「无数据」而不是伪装成可用结果。
|
|
113
172
|
const fallback = opts.fallbackPath;
|
|
114
173
|
if (fallback) {
|
|
115
174
|
try {
|
|
116
175
|
const { readFile } = await import('node:fs/promises');
|
|
117
176
|
const pack = JSON.parse(await readFile(fallback, 'utf-8'));
|
|
177
|
+
const kw = query.destination.trim();
|
|
178
|
+
const stays = Array.isArray(pack.stays) ? pack.stays : [];
|
|
179
|
+
const matched = kw ? stays.filter(s => JSON.stringify(s).includes(kw)) : stays;
|
|
180
|
+
if (matched.length) {
|
|
181
|
+
return {
|
|
182
|
+
...live,
|
|
183
|
+
hotels: { stays: matched },
|
|
184
|
+
// summary 即模型面:静态块内容(note/options/recommendation)直接内联,
|
|
185
|
+
// 模型不必再猜「命中 N 块」里有什么(issue #195)
|
|
186
|
+
summary: `${query.destination}:hbcli 实时源不可用(${reason}),已降级到静态包(公开渠道估算,非实时),命中 ${matched.length} 个住宿块:\n${JSON.stringify(matched).slice(0, 1600)}`,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
118
189
|
return {
|
|
119
190
|
...live,
|
|
120
|
-
hotels:
|
|
121
|
-
summary: `${query.destination}:hbcli
|
|
191
|
+
hotels: null,
|
|
192
|
+
summary: `${query.destination}:hbcli 实时源不可用(${reason}),且静态包无「${query.destination}」住宿数据(静态包仅覆盖内置场景)`,
|
|
122
193
|
};
|
|
123
|
-
}
|
|
194
|
+
}
|
|
195
|
+
catch { /* 静态包读不到也优雅降级 */ }
|
|
124
196
|
}
|
|
125
|
-
return {
|
|
126
|
-
...live,
|
|
127
|
-
summary: `${query.destination}:hbcli 不可用且无静态包(仅返回错误)`
|
|
128
|
-
};
|
|
197
|
+
return { ...live, summary: `${query.destination}:hbcli 实时源不可用(${reason})且无静态包(仅返回错误)` };
|
|
129
198
|
}
|
|
199
|
+
/** 高层语义化封装:目的地列表(无数据依赖,通常 hbcli dest 命令可独立调通) */
|
|
130
200
|
export async function listDestinations(opts = {}) {
|
|
131
|
-
const r = await callHbcliJson([
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
201
|
+
const r = await callHbcliJson(['search', 'destinations', '--json'], opts);
|
|
202
|
+
return { ...r, destinations: r.result };
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* 高层语义化封装:房型报价(hotel-rates;上游 hotelRates 创建后端 session,
|
|
206
|
+
* 产出带 RatePkgId 的房型列表——check-avail 与 book 的入参来源)。
|
|
207
|
+
*
|
|
208
|
+
* 价格面无静态降级(fail-closed):hotel-list 的静态包降级是定性住宿信息,
|
|
209
|
+
* 房价是钱——估算价格冒充实时价是红线;不可用就诚实失败,证据链标注,
|
|
210
|
+
* 与 bookable-facts 证据分级(live_inventory 才可进确认卡)同口径。
|
|
211
|
+
*/
|
|
212
|
+
export async function hotelRates(query, opts = {}) {
|
|
213
|
+
const hbArgs = ['search', 'hotel-rates', '--json', '--hotel-id', query.hotelId];
|
|
214
|
+
if (query.checkIn)
|
|
215
|
+
hbArgs.push('--check-in', query.checkIn);
|
|
216
|
+
if (query.checkOut)
|
|
217
|
+
hbArgs.push('--check-out', query.checkOut);
|
|
218
|
+
if (query.countryCode)
|
|
219
|
+
hbArgs.push('--country-code', query.countryCode);
|
|
220
|
+
if (query.nationalityCode)
|
|
221
|
+
hbArgs.push('--nationality-code', query.nationalityCode);
|
|
222
|
+
if (query.residencyCode)
|
|
223
|
+
hbArgs.push('--residency-code', query.residencyCode);
|
|
224
|
+
if (query.adults)
|
|
225
|
+
hbArgs.push('--room-occupancies', JSON.stringify([{ adultCount: query.adults, childrenAges: [] }]));
|
|
226
|
+
const live = await callHbcliJson(hbArgs, opts);
|
|
227
|
+
const summary = live.via === 'hbcli-realtime'
|
|
228
|
+
? `hotel ${query.hotelId}:hbcli 实时报价${query.checkIn ? `(${query.checkIn} → ${query.checkOut ?? '?'})` : ''}`
|
|
229
|
+
: `hotel ${query.hotelId}:hbcli 实时报价不可用(${live.error ?? live.via});价格面无静态降级,fail-closed 不估算`;
|
|
230
|
+
return { ...live, rates: live.result, summary };
|
|
231
|
+
}
|
|
232
|
+
/** 高层语义化封装:下单前实时验价(check-avail;入参 RatePkgId 来自 hotel-rates 产物)。价格面同上:不可用即诚实失败。 */
|
|
233
|
+
export async function checkAvail(query, opts = {}) {
|
|
234
|
+
const live = await callHbcliJson(['search', 'check-avail', '--json', '--rate-pkg-id', query.ratePkgId], opts);
|
|
235
|
+
const summary = live.via === 'hbcli-realtime'
|
|
236
|
+
? `ratePkg ${query.ratePkgId}:hbcli 实时验价(库存与价格以后端为准)`
|
|
237
|
+
: `ratePkg ${query.ratePkgId}:hbcli 实时验价不可用(${live.error ?? live.via});价格面无静态降级,fail-closed`;
|
|
238
|
+
return { ...live, avail: live.result, summary };
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* C 端客户邮箱验证码登录(hotelbyte-cli auth customer-* 两步流;后端
|
|
242
|
+
* loginByCustomerEmailCode:验证通过即登录,新邮箱即注册,无需密码):
|
|
243
|
+
* step 1 customerSendCode —— 邮箱收码(外发邮件动作,仅用户明示要登录/注册时调用);
|
|
244
|
+
* step 2 customerLogin —— 用户从收件箱报码,验证即建档/登录。
|
|
245
|
+
*
|
|
246
|
+
* 与检索族的本质差异:customerLogin 写 hbcli 共享凭据仓(customer 档,
|
|
247
|
+
* ~/.staicli/credentials.json)——hbcli 侧 customer 档是最低优先级凭据(无
|
|
248
|
+
* portal/API-key 凭据时才生效),登录不劫持既有凭据。本层不透出 token 值
|
|
249
|
+
* (CLI 只回 token_saved,凭据留在 CLI 仓内,不进 gotry 会话/日志)。
|
|
250
|
+
* auth 命令族要求 hbcli 带 register 支持(npm 0.0.4 起;0.0.3 报 unknown
|
|
251
|
+
* command,经 hbcli-error 面优雅失败)。
|
|
252
|
+
*/
|
|
253
|
+
export async function customerSendCode(email, opts = {}) {
|
|
254
|
+
const live = await callHbcliJson(['--json', 'auth', 'customer-send-code', '--email', email], opts);
|
|
255
|
+
const r = live.result;
|
|
256
|
+
const summary = live.via === 'hbcli-realtime'
|
|
257
|
+
? `验证码已发送到 ${email}${r?.expiresIn ? `(有效期 ${r.expiresIn} 秒)` : ''}——请让用户从邮箱查收并报码,再走 gotry_hotel_auth_login`
|
|
258
|
+
: `验证码发送失败(${live.error ?? live.via});服务端有发码限流,勿立即重试`;
|
|
259
|
+
return { ...live, summary };
|
|
260
|
+
}
|
|
261
|
+
export async function customerLogin(query, opts = {}) {
|
|
262
|
+
const hbArgs = ['--json', 'auth', 'customer-login', '--email', query.email, '--code', query.code];
|
|
263
|
+
if (query.ttl && query.ttl > 0)
|
|
264
|
+
hbArgs.push('--ttl', String(query.ttl));
|
|
265
|
+
if (query.attributionToken)
|
|
266
|
+
hbArgs.push('--attribution-token', query.attributionToken);
|
|
267
|
+
const live = await callHbcliJson(hbArgs, opts);
|
|
268
|
+
const r = live.result;
|
|
269
|
+
let summary;
|
|
270
|
+
if (live.via === 'hbcli-realtime' && r?.status === 'logged_in') {
|
|
271
|
+
const attribution = typeof r.attributionBound === 'boolean'
|
|
272
|
+
? `;顾问归因绑定${r.attributionBound ? '成功' : '未成(登录不受影响,归因可重试)'}`
|
|
273
|
+
: '';
|
|
274
|
+
summary = `hotelbyte 客户登录成功(${query.email};新邮箱即注册${attribution})——凭据已存入 hbcli customer 档,后续酒店检索/报价走实时源`;
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
summary = `hotelbyte 客户登录失败(${live.error ?? live.via});验证码是一次性凭据,失败后需重新发码`;
|
|
278
|
+
}
|
|
279
|
+
return { ...live, summary };
|
|
140
280
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
//# sourceURL=/Users/bytedance/work/gotry/ts/capabilities/hbcli.ts
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HotelByte 交易 bridge 与 unknown 查单对账契约层(issue #232 契约/模块切片)。
|
|
3
|
+
*
|
|
4
|
+
* ⚠ 非运行时激活:本模块是纯函数契约层,不 spawn 子进程、不发起网络请求、
|
|
5
|
+
* 不读取任何凭据,与真实 HotelByte 二进制/UAT 零接触(见 issue #318 的
|
|
6
|
+
* GOTRY_HBCLI_LIVE 显式 opt-in 门——真实调用面在 M5 Entry 后才允许接线,
|
|
7
|
+
* 且必须挂在该门之后)。一切行为由 fixture/合成数据证明(hotelbyte-reconcile-tests)。
|
|
8
|
+
*
|
|
9
|
+
* 词汇与边界(上游文档为准):
|
|
10
|
+
* - docs/design/write-gate-production-design.md §3(SupplierOutcome/WriteEffectIntent/
|
|
11
|
+
* attempt_id/fencing)、§5.3(dispatch 未知态)、§6(准入矩阵)、§7(unknown 查单/恢复窗口);
|
|
12
|
+
* - docs/design/fact-writegate-seam.md(读侧 fact 与写侧 SupplierOutcome 两闸分离)。
|
|
13
|
+
*
|
|
14
|
+
* 与 #231(WriteGate outbox)的接口边界:本模块只消费 outbox intent 的**引用 ID 字符串**
|
|
15
|
+
* (`outboxIntentRef` 占位)与审批凭证摘要,不实现、不触碰 outbox/dispatch 持久化——
|
|
16
|
+
* attempt 领取、fencing token、账本事务由 #231 落地;两者以该 ID 关联(#231 未合前为占位)。
|
|
17
|
+
*
|
|
18
|
+
* 对账红线(全部结构性编码,见 advanceReconciliation):
|
|
19
|
+
* - unknown/无法确认的查单结果必须有显式对账分类——不静默当成功,也不静默丢弃;
|
|
20
|
+
* - CLI exit0 非成功证明:timeout/kill/坏响应/无绑定成功/部分确认一律 unknown;
|
|
21
|
+
* - 查单 miss 在恢复窗口内(后端 180s Phase1 + 最长 10min Phase2)保持 unknown,禁止重订;
|
|
22
|
+
* - 时间届满本身不是无订单证明——只转人工,不自动 reconciled_failed;
|
|
23
|
+
* - 只有绑定 attempt/customerReferenceNo 的权威终态负证据(供应商终态取消/失败、
|
|
24
|
+
* 绑定 attempt 的人工无订单确认)才允许新 intent;
|
|
25
|
+
* - 同 ref 多单/参考号不匹配 = 冲突,显式 manual_reconcile;
|
|
26
|
+
* - 终态后出现反证只置 conflict 交人工,不静默翻转;
|
|
27
|
+
* - 每次推进追加审计事件(带 probeDigest),记录不可变(pure function)。
|
|
28
|
+
*/
|
|
29
|
+
import { createHash } from 'node:crypto';
|
|
30
|
+
/** HotelByte 后端恢复窗口:Phase1 180s + Phase2 最长 10min(600s);上界 780s。 */
|
|
31
|
+
export const RECOVERY_WINDOW_PHASE1_SECONDS = 180;
|
|
32
|
+
export const RECOVERY_WINDOW_PHASE2_MAX_SECONDS = 600;
|
|
33
|
+
export const RECOVERY_WINDOW_TOTAL_MAX_SECONDS = RECOVERY_WINDOW_PHASE1_SECONDS + RECOVERY_WINDOW_PHASE2_MAX_SECONDS;
|
|
34
|
+
function requireBinding(key, value) {
|
|
35
|
+
const v = (value ?? '').trim();
|
|
36
|
+
if (!v)
|
|
37
|
+
throw new Error(`hotelbyte-reconcile: 对账记录缺绑定键 ${key}(fail-closed;无绑定键即无法追溯)`);
|
|
38
|
+
return v;
|
|
39
|
+
}
|
|
40
|
+
function probeDigest(input) {
|
|
41
|
+
return createHash('sha256').update(JSON.stringify(input ?? null)).digest('hex');
|
|
42
|
+
}
|
|
43
|
+
/** 恢复窗口上界:enterUnknownAt + 780s(180s Phase1 + 最长 10min Phase2)。 */
|
|
44
|
+
export function recoveryWindowEndsAt(enteredUnknownAt) {
|
|
45
|
+
return new Date(Date.parse(enteredUnknownAt) + RECOVERY_WINDOW_TOTAL_MAX_SECONDS * 1000).toISOString();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 进入 unknown:booking 结果不可判定(超时/被杀/坏响应/部分确认)即建对账记录。
|
|
49
|
+
* 绑定键缺失直接抛错——没有绑定键的对账是不可追溯的黑洞,红线拒绝。
|
|
50
|
+
*/
|
|
51
|
+
export function enterUnknown(seed) {
|
|
52
|
+
const attemptId = requireBinding('attemptId', seed.attemptId);
|
|
53
|
+
const customerReferenceNo = requireBinding('customerReferenceNo', seed.customerReferenceNo);
|
|
54
|
+
const intentIdemKey = requireBinding('intentIdemKey', seed.intentIdemKey);
|
|
55
|
+
const requestFingerprintSha256 = requireBinding('requestFingerprintSha256', seed.requestFingerprintSha256);
|
|
56
|
+
const recoveryWindowEndsAtV = requireBinding('recoveryWindowEndsAt', seed.recoveryWindowEndsAt);
|
|
57
|
+
const enteredUnknownAt = requireBinding('enteredUnknownAt', seed.enteredUnknownAt);
|
|
58
|
+
if (Number.isNaN(Date.parse(enteredUnknownAt)) || Number.isNaN(Date.parse(recoveryWindowEndsAtV))) {
|
|
59
|
+
throw new Error('hotelbyte-reconcile: enteredUnknownAt/recoveryWindowEndsAt 必须是可解析时间');
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
attemptId,
|
|
63
|
+
customerReferenceNo,
|
|
64
|
+
intentIdemKey,
|
|
65
|
+
requestFingerprintSha256,
|
|
66
|
+
enteredUnknownAt,
|
|
67
|
+
recoveryWindowEndsAt: recoveryWindowEndsAtV,
|
|
68
|
+
factId: seed.factId,
|
|
69
|
+
approvalReceiptDigest: seed.approvalReceiptDigest,
|
|
70
|
+
outboxIntentRef: seed.outboxIntentRef,
|
|
71
|
+
status: 'unknown',
|
|
72
|
+
nextAction: 'keep_querying',
|
|
73
|
+
conflict: false,
|
|
74
|
+
history: [{
|
|
75
|
+
at: enteredUnknownAt,
|
|
76
|
+
action: 'enter_unknown',
|
|
77
|
+
from: 'unknown',
|
|
78
|
+
to: 'unknown',
|
|
79
|
+
reason: 'booking 结果不可判定,进入显式对账(禁止盲目重试/重订)',
|
|
80
|
+
probeDigest: probeDigest(seed),
|
|
81
|
+
}],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function parseBookPayload(stdout) {
|
|
85
|
+
const start = stdout.search(/[{[]/);
|
|
86
|
+
if (start < 0)
|
|
87
|
+
return { json: null, parseError: 'no-json-marker' };
|
|
88
|
+
try {
|
|
89
|
+
const parsed = JSON.parse(stdout.slice(start));
|
|
90
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
91
|
+
return { json: null, parseError: 'non-object-json' };
|
|
92
|
+
}
|
|
93
|
+
return { json: parsed };
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
return { json: null, parseError: `unparseable:${e.message.slice(0, 80)}` };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* book 执行分类(验收 §3:CLI exit0 非成功证明;timeout/kill/坏响应/结果不确定归 unknown):
|
|
101
|
+
* - timedOut / signal / 非零退码且无显式业务结论 → unknown;
|
|
102
|
+
* - exit0 + 非 JSON → unknown;exit0 + 显式业务失败 → supplier_failed;
|
|
103
|
+
* - exit0 + success 但缺/错 customerReferenceNo 绑定 → unknown(不可归因即不可当成功);
|
|
104
|
+
* - exit0 + pending/部分确认 → unknown;
|
|
105
|
+
* - exit0 + success + 参考号匹配本 attempt → binding_verified。
|
|
106
|
+
*/
|
|
107
|
+
export function classifyBookExecution(r, expectedReferenceNo) {
|
|
108
|
+
if (r.timedOut)
|
|
109
|
+
return { kind: 'unknown', reason: 'timeout(30s abort;后端可能仍在处理,进入 unknown 对账)' };
|
|
110
|
+
if (r.signal)
|
|
111
|
+
return { kind: 'unknown', reason: `process_killed:${r.signal}(副作用不可判定)` };
|
|
112
|
+
const { json, parseError } = parseBookPayload(r.stdout);
|
|
113
|
+
if (r.exitCode !== 0) {
|
|
114
|
+
if (json?.status === 'failed')
|
|
115
|
+
return { kind: 'supplier_failed', reason: 'nonzero-exit + explicit failed payload', payload: json };
|
|
116
|
+
return { kind: 'unknown', reason: `nonzero_exit:${r.exitCode ?? 'null'}(无显式业务结论,不假定无副作用${parseError ? `;${parseError}` : ''})` };
|
|
117
|
+
}
|
|
118
|
+
if (!json)
|
|
119
|
+
return { kind: 'unknown', reason: `exit0_non_json(坏响应,不可当成功;${parseError ?? 'empty'})` };
|
|
120
|
+
if (json.status === 'failed')
|
|
121
|
+
return { kind: 'supplier_failed', reason: 'explicit failed payload', payload: json };
|
|
122
|
+
if (json.status === 'success') {
|
|
123
|
+
const ref = typeof json.customerReferenceNo === 'string' ? json.customerReferenceNo.trim() : '';
|
|
124
|
+
if (!ref)
|
|
125
|
+
return { kind: 'unknown', reason: 'exit0_success_missing_reference(无绑定成功不可归因)' };
|
|
126
|
+
if (ref !== expectedReferenceNo)
|
|
127
|
+
return { kind: 'unknown', reason: `exit0_success_reference_mismatch(返回 ${ref},本 attempt ${expectedReferenceNo})` };
|
|
128
|
+
return { kind: 'binding_verified', customerReferenceNo: ref, payload: json };
|
|
129
|
+
}
|
|
130
|
+
// pending / 其它一切不确定状态:部分确认 ≠ 成功
|
|
131
|
+
return { kind: 'unknown', reason: `partial_or_indeterminate(status=${String(json.status ?? 'absent')})` };
|
|
132
|
+
}
|
|
133
|
+
/** 供应商终态取消/失败词汇(权威负证据来源;迟到自动取消按回执收敛)。 */
|
|
134
|
+
const SUPPLIER_TERMINAL_NEGATIVE = new Set(['cancelled', 'canceled', 'auto_canceled', 'failed', 'expired']);
|
|
135
|
+
/**
|
|
136
|
+
* 对账状态机推进(纯函数;原状态不可变,返回带追加审计事件的新状态)。
|
|
137
|
+
* 转移表对齐 write-gate-production-design §7 矩阵,红线见模块头注释。
|
|
138
|
+
*/
|
|
139
|
+
export function advanceReconciliation(state, probe, now) {
|
|
140
|
+
const digest = probeDigest(probe);
|
|
141
|
+
const append = (next, action, reason) => ({
|
|
142
|
+
...next,
|
|
143
|
+
history: [...state.history, { at: now, action, from: state.status, to: next.status, reason, probeDigest: digest }],
|
|
144
|
+
});
|
|
145
|
+
// 终态吸收 + 反证只置 conflict(不静默翻转)
|
|
146
|
+
if (state.status !== 'unknown') {
|
|
147
|
+
if (probe.kind === 'hit' && probe.orders.length > 0) {
|
|
148
|
+
return append({ ...state, conflict: true, nextAction: 'manual_reconcile' }, 'conflict_late_hit_after_terminal', `终态(${state.status})后出现订单证据 → 显式冲突交人工复核,不静默翻转`);
|
|
149
|
+
}
|
|
150
|
+
return { ...state };
|
|
151
|
+
}
|
|
152
|
+
if (probe.kind === 'manual_no_order') {
|
|
153
|
+
const evidence = (probe.operatorEvidence ?? '').trim();
|
|
154
|
+
if (!evidence)
|
|
155
|
+
throw new Error('hotelbyte-reconcile: 人工无订单确认必须携带非空操作证据(绑定 attempt 可追溯)');
|
|
156
|
+
return append({ ...state, status: 'reconciled_failed', nextAction: 'terminal', conflict: false }, 'manual_no_order_confirmed', `人工权威负证据:${evidence.slice(0, 200)}`);
|
|
157
|
+
}
|
|
158
|
+
const withinWindow = Date.parse(now) <= Date.parse(state.recoveryWindowEndsAt);
|
|
159
|
+
switch (probe.kind) {
|
|
160
|
+
case 'miss': {
|
|
161
|
+
if (withinWindow) {
|
|
162
|
+
return append({ ...state, status: 'unknown', nextAction: 'keep_querying', conflict: false }, 'query_miss_within_window', '恢复窗口内查无 → 保持 unknown;供应商可能迟到建单,禁止重订');
|
|
163
|
+
}
|
|
164
|
+
return append({ ...state, status: 'unknown', nextAction: 'manual_reconcile', conflict: false }, 'query_miss_after_window', '窗口届满查无 → 仍 unknown(时间届满本身不是无订单证明),转人工');
|
|
165
|
+
}
|
|
166
|
+
case 'hit': {
|
|
167
|
+
if (probe.orders.length > 1) {
|
|
168
|
+
return append({ ...state, status: 'unknown', nextAction: 'manual_reconcile', conflict: true }, 'conflict_multiple_orders', `同参考号返回 ${probe.orders.length} 笔订单 → 冲突,人工对账(不静默择一)`);
|
|
169
|
+
}
|
|
170
|
+
const order = probe.orders[0];
|
|
171
|
+
if (order.customerReferenceNo !== state.customerReferenceNo) {
|
|
172
|
+
return append({ ...state, status: 'unknown', nextAction: 'manual_reconcile', conflict: true }, 'conflict_reference_mismatch', `返回订单参考号 ${order.customerReferenceNo} ≠ 本 attempt ${state.customerReferenceNo} → 冲突,人工对账`);
|
|
173
|
+
}
|
|
174
|
+
const status = order.supplierStatus.toLowerCase();
|
|
175
|
+
if (SUPPLIER_TERMINAL_NEGATIVE.has(status)) {
|
|
176
|
+
return append({ ...state, status: 'reconciled_failed', nextAction: 'terminal', conflict: false }, 'supplier_terminal_negative', `绑定 attempt 的供应商终态(${order.supplierStatus},orderRef=${order.orderRef})→ 权威负证据,收敛 reconciled_failed`);
|
|
177
|
+
}
|
|
178
|
+
if (status === 'confirmed' || status === 'success' || status === 'completed') {
|
|
179
|
+
return append({ ...state, status: 'reconciled_success', nextAction: 'terminal', conflict: false }, 'supplier_terminal_success', `绑定 attempt 的供应商终态成功(orderRef=${order.orderRef})→ reconciled_success(含迟到成功,不重订)`);
|
|
180
|
+
}
|
|
181
|
+
return append({ ...state, status: 'unknown', nextAction: 'keep_querying', conflict: false }, 'hit_not_terminal', `订单存在但非终态(${order.supplierStatus})→ 保持 unknown 继续查`);
|
|
182
|
+
}
|
|
183
|
+
case 'query_failed':
|
|
184
|
+
return append({ ...state, status: 'unknown', nextAction: 'keep_querying', conflict: false }, 'query_failed', `查询自身失败(${probe.reason.slice(0, 120)})→ 探针失败不证明订单不存在,保持 unknown`);
|
|
185
|
+
case 'malformed':
|
|
186
|
+
return append({ ...state, status: 'unknown', nextAction: 'keep_querying', conflict: false }, 'query_malformed', `畸形响应(${probe.reason.slice(0, 120)})→ 不静默当 miss,保持 unknown`);
|
|
187
|
+
case 'permission_denied':
|
|
188
|
+
return append({ ...state, status: 'unknown', nextAction: 'manual_reconcile', conflict: false }, 'query_permission_denied', '查询权限拒绝 → 自助查单不可用,转人工对账');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/** 是否允许对同一请求发起新 intent:仅当收敛自绑定 attempt 的权威终态负证据且无未决冲突。 */
|
|
192
|
+
export function newIntentAllowed(state) {
|
|
193
|
+
return state.status === 'reconciled_failed' && !state.conflict;
|
|
194
|
+
}
|