@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
package/ts/src/unified.ts
CHANGED
|
@@ -7,8 +7,12 @@
|
|
|
7
7
|
|
|
8
8
|
import { checkConnectivity } from '../scripts/skeleton-check.ts'
|
|
9
9
|
import type { Candidate, Choice, MotivationProfile, Service, TransferMode, TravelRequest, TrueCost } from './model.ts'
|
|
10
|
-
import { evaluateChoice, minToHhmm, hhmmToMin, requiredUsableHours, trueCostToDict, LATEST_ARRIVE_STAY_MIN } from './model.ts'
|
|
10
|
+
import { evaluateChoice, minToHhmm, hhmmToMin, requiredUsableHours, trueCostToDict, LATEST_ARRIVE_STAY_MIN, doorToDoorFromMove, NonpositiveDurationError } from './model.ts'
|
|
11
11
|
import type { LegReport } from './journey.ts'
|
|
12
|
+
import { withZ3 } from './z3-shared.ts'
|
|
13
|
+
import { t as i18nT } from './i18n.ts'
|
|
14
|
+
import { resolveOffsetForLocalDate, describeResolveFailure, isKnownZoneLoose, readWallParts, wallPartsToIso } from './tz-resolver.ts'
|
|
15
|
+
import { FLIGHT_PACK_VERSION } from './flight-pack-contract.ts'
|
|
12
16
|
|
|
13
17
|
export interface AnchorsSpec {
|
|
14
18
|
arriveByMin?: number
|
|
@@ -34,6 +38,9 @@ export interface MoveSpecTS {
|
|
|
34
38
|
tzOffsetMin?: number
|
|
35
39
|
/** M-1:出发地 UTC 偏移(工作窗口换算用) */
|
|
36
40
|
originTzOffsetMin?: number
|
|
41
|
+
/** Issue #343 v2:dep/arr 的 IANA zone(从 option.date + 服务 HH:MM 解析;只读镜像,不算术)。 */
|
|
42
|
+
ianaDepZone?: string
|
|
43
|
+
ianaArrZone?: string
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
/** M-1:旅行者工作窗口(家时区) */
|
|
@@ -42,6 +49,14 @@ export interface WorkWindowSpec {
|
|
|
42
49
|
startMin: number
|
|
43
50
|
endMin: number
|
|
44
51
|
workdays?: number[]
|
|
52
|
+
/** Issue #343 v2:home IANA zone;overrides numeric `homeTzOffsetMin` when set.
|
|
53
|
+
* Used by `workWindowBlocks` to derive a date-sensitive home offset instead of a
|
|
54
|
+
* single numeric per-trip shift(原 M-1 数值写法).Mutually exclusive with
|
|
55
|
+
* `homeTzOffsetMin` at v2 parse time;不在同一 pack 里同时给两值。 */
|
|
56
|
+
homeZone?: string
|
|
57
|
+
/** Issue #343 v2:可选,home 工作窗口的本地表达(hh:mm);缺省回退 start/end 的 min-of-day。 */
|
|
58
|
+
startHHmm?: string
|
|
59
|
+
endHHmm?: string
|
|
45
60
|
}
|
|
46
61
|
|
|
47
62
|
export interface StaySpecTS {
|
|
@@ -55,6 +70,8 @@ export interface StaySpecTS {
|
|
|
55
70
|
export interface SegmentOptionTS {
|
|
56
71
|
id: string
|
|
57
72
|
label: string
|
|
73
|
+
/** 候选级日期;缺省时使用所属 segment.date */
|
|
74
|
+
date?: string
|
|
58
75
|
move?: MoveSpecTS
|
|
59
76
|
stay?: StaySpecTS
|
|
60
77
|
score?: number
|
|
@@ -83,20 +100,13 @@ export interface JourneySpecTS {
|
|
|
83
100
|
workWindow?: WorkWindowSpec
|
|
84
101
|
/** 骨架层开关(§7-1):true 时对带 route 提示的段做通航性三值标注 */
|
|
85
102
|
skeletonHub?: boolean
|
|
103
|
+
/** Internal only; symbol-keyed so JSON/public tool schemas never expose pack version. */
|
|
104
|
+
readonly [FLIGHT_PACK_VERSION]?: 1 | 2
|
|
86
105
|
}
|
|
87
106
|
|
|
107
|
+
// Z3 运行时收敛到 z3-shared(单一 WASM 实例 + 单一 Context + 会话级互斥)——
|
|
108
|
+
// 此前模块级 z3Promise 三份并存是 WASM race/OOM 的根因(见 z3-shared.ts 头注)。
|
|
88
109
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
89
|
-
let z3Promise: Promise<any> | null = null
|
|
90
|
-
|
|
91
|
-
async function getZ3(): Promise<any> {
|
|
92
|
-
// 延迟到首次实际调用 solveUnified 才加载:避免 dsh 加载 GoTry 模块时启动
|
|
93
|
-
// WASM worker,引起 worker 线程内存冲突(z3-built.wasm 多线程 unsafe)。
|
|
94
|
-
if (!z3Promise) {
|
|
95
|
-
const { init } = await import('z3-solver')
|
|
96
|
-
z3Promise = (async () => (await init()).Context('main'))()
|
|
97
|
-
}
|
|
98
|
-
return z3Promise
|
|
99
|
-
}
|
|
100
110
|
|
|
101
111
|
/** 航班数据包(data/flights_2026.json 形态)→ 段链 */
|
|
102
112
|
const SEGMENT_ROUTES: Record<string, string> = {
|
|
@@ -116,6 +126,7 @@ export function segmentsFromCandidate(req: TravelRequest, candidates: Candidate[
|
|
|
116
126
|
const options: SegmentOptionTS[] = candidates.map(c => ({
|
|
117
127
|
id: c.id,
|
|
118
128
|
label: c.name,
|
|
129
|
+
date: c.date,
|
|
119
130
|
score: c.imageryMatch,
|
|
120
131
|
bestMonths: c.bestMonths,
|
|
121
132
|
minDays: c.minDaysForPurpose,
|
|
@@ -151,6 +162,17 @@ export function segmentsFromCandidate(req: TravelRequest, candidates: Candidate[
|
|
|
151
162
|
}
|
|
152
163
|
|
|
153
164
|
export function parseFlightPackToSpec(pack: Record<string, unknown>): JourneySpecTS {
|
|
165
|
+
// Issue #343 v2:IANA path;v1(无 version 字段或 version===1)走原数值口径。
|
|
166
|
+
const version = pack['version'] === undefined ? 1 : Number(pack['version'])
|
|
167
|
+
if (version === 2) return parseFlightPackToSpecV2(pack)
|
|
168
|
+
if (version !== 1) {
|
|
169
|
+
throw Object.assign(new Error(`parseFlightPackToSpec: unsupported pack version ${version}`), { code: 'unsupported_pack_version' })
|
|
170
|
+
}
|
|
171
|
+
return parseFlightPackToSpecV1(pack)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** v1 数值口径(path 保留为 export,便于 §7-1/§6 直接复用与差分) */
|
|
175
|
+
export function parseFlightPackToSpecV1(pack: Record<string, unknown>): JourneySpecTS {
|
|
154
176
|
const legs = (pack['legs'] as Array<Record<string, unknown>>).map(l => ({
|
|
155
177
|
id: String(l['id']),
|
|
156
178
|
role: ((l['services'] as unknown[]).length === 1 ? 'fixed' : 'choice') as 'fixed' | 'choice',
|
|
@@ -184,6 +206,7 @@ export function parseFlightPackToSpec(pack: Record<string, unknown>): JourneySpe
|
|
|
184
206
|
endMin: Number(ww['end_min']),
|
|
185
207
|
workdays: (ww['workdays'] as number[] | undefined) ?? [0, 1, 2, 3, 4],
|
|
186
208
|
} : undefined,
|
|
209
|
+
[FLIGHT_PACK_VERSION]: 1,
|
|
187
210
|
}
|
|
188
211
|
// M-1:班次的星期标注挂到 Option(缺省=不受工作窗口约束)
|
|
189
212
|
for (const l of pack['legs'] as Array<Record<string, unknown>>) {
|
|
@@ -198,31 +221,235 @@ export function parseFlightPackToSpec(pack: Record<string, unknown>): JourneySpe
|
|
|
198
221
|
return spec
|
|
199
222
|
}
|
|
200
223
|
|
|
224
|
+
/** Issue #343 v2:IANA zone + 当地日期解析;数值 tz_* 字段不可用(混合形态解析时拒收)。 */
|
|
225
|
+
export function parseFlightPackToSpecV2(pack: Record<string, unknown>): JourneySpecTS {
|
|
226
|
+
const legsRaw = pack['legs'] as Array<Record<string, unknown>>
|
|
227
|
+
if (!Array.isArray(legsRaw) || legsRaw.length === 0) {
|
|
228
|
+
throw Object.assign(new Error('parseFlightPackToSpecV2: legs missing/empty'), { code: 'v2_legs_missing' })
|
|
229
|
+
}
|
|
230
|
+
const meta = (pack['meta'] ?? {}) as Record<string, unknown>
|
|
231
|
+
const ww = meta['work_window'] as Record<string, unknown> | undefined
|
|
232
|
+
|
|
233
|
+
const legs: SegmentTS[] = []
|
|
234
|
+
for (const l of legsRaw) {
|
|
235
|
+
// v2 不允许混合数值 tz_offset_min/origin_tz_offset_min(早 fail)
|
|
236
|
+
if ('tz_offset_min' in l || 'origin_tz_offset_min' in l) {
|
|
237
|
+
throw Object.assign(new Error(`parseFlightPackToSpecV2: leg ${l['id']} has legacy tz_offset_min/origin_tz_offset_min;v2 pack must use iana_*_zone + *_local_date`), { code: 'v2_legacy_offset_mixed' })
|
|
238
|
+
}
|
|
239
|
+
const legDate = l['date'] ? String(l['date']) : ''
|
|
240
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(legDate)) {
|
|
241
|
+
throw Object.assign(new Error(`parseFlightPackToSpecV2: leg ${l['id']} requires ISO date`), { code: 'v2_leg_date_missing' })
|
|
242
|
+
}
|
|
243
|
+
const ianaDepZone = String(l['iana_dep_zone'] ?? '')
|
|
244
|
+
const ianaArrZone = String(l['iana_arr_zone'] ?? '')
|
|
245
|
+
if (!ianaDepZone || !ianaArrZone) {
|
|
246
|
+
throw Object.assign(new Error(`parseFlightPackToSpecV2: leg ${l['id']} needs iana_dep_zone and iana_arr_zone`), { code: 'v2_zone_missing' })
|
|
247
|
+
}
|
|
248
|
+
const options: SegmentOptionTS[] = (l['services'] as Array<Record<string, unknown>>).map(sv => {
|
|
249
|
+
const svcDepDate = sv['dep_local_date'] ? String(sv['dep_local_date']) : legDate
|
|
250
|
+
const svcArrDate = sv['arr_local_date'] ? String(sv['arr_local_date']) : legDate
|
|
251
|
+
const depZone = sv['iana_dep_zone'] ? String(sv['iana_dep_zone']) : ianaDepZone
|
|
252
|
+
const arrZone = sv['iana_arr_zone'] ? String(sv['iana_arr_zone']) : ianaArrZone
|
|
253
|
+
const depR = resolveOffsetForLocalDate(depZone, svcDepDate, String(sv['dep']))
|
|
254
|
+
if (!depR.ok) {
|
|
255
|
+
throw Object.assign(new Error(`parseFlightPackToSpecV2: dep zone/date failure: ${describeResolveFailure(depR)}`), { code: 'v2_dep_resolve_failed' })
|
|
256
|
+
}
|
|
257
|
+
const arrR = resolveOffsetForLocalDate(arrZone, svcArrDate, String(sv['arr']))
|
|
258
|
+
if (!arrR.ok) {
|
|
259
|
+
throw Object.assign(new Error(`parseFlightPackToSpecV2: arr zone/date failure: ${describeResolveFailure(arrR)}`), { code: 'v2_arr_resolve_failed' })
|
|
260
|
+
}
|
|
261
|
+
// Issue #343 root 复核:d2d canonical 输入必须在 parse 边界校验严格 chronologically after,
|
|
262
|
+
// 否则把病态数据喂给 model.doorToDoorFromMove → throw NonpositiveDurationError → 被
|
|
263
|
+
// solveUnified WASM guard 误报为 wasm_runtime_error。
|
|
264
|
+
if (arrR.utcMs <= depR.utcMs) {
|
|
265
|
+
throw Object.assign(new Error(
|
|
266
|
+
`parseFlightPackToSpecV2: service ${String(sv['id'])} arrival instant (${new Date(arrR.utcMs).toISOString()}) must be strictly after departure instant (${new Date(depR.utcMs).toISOString()})`,
|
|
267
|
+
), { code: 'v2_arr_not_after_dep' })
|
|
268
|
+
}
|
|
269
|
+
// tzOffsetMin = arr - dep(positive = 抵达地更靠东)
|
|
270
|
+
const tzOffsetMin = arrR.offsetMin - depR.offsetMin
|
|
271
|
+
return {
|
|
272
|
+
id: String(sv['id']),
|
|
273
|
+
label: `${String(sv['id'])} ${String(l['note'] ?? '').slice(0, 18)}`,
|
|
274
|
+
date: svcDepDate,
|
|
275
|
+
move: {
|
|
276
|
+
hub: String(l['hub'] ?? ''),
|
|
277
|
+
services: [{
|
|
278
|
+
id: String(sv['id']),
|
|
279
|
+
depMin: hhmmToMin(String(sv['dep'])),
|
|
280
|
+
arrMin: hhmmToMin(String(sv['arr'])),
|
|
281
|
+
priceCny: Number(sv['price_cny']),
|
|
282
|
+
// Issue #343 v2:把"墙上时刻"投影成真实 UTC instant 留给 evaluate / workWindow 用;
|
|
283
|
+
// d2d 公式走 (arrUtcMs - depUtcMs)/60000,跨日 / DST / 反向日界线都正确。
|
|
284
|
+
depUtcMs: depR.utcMs,
|
|
285
|
+
arrUtcMs: arrR.utcMs,
|
|
286
|
+
}],
|
|
287
|
+
bufferMin: Number(l['buffer_min']),
|
|
288
|
+
originTransferMin: Number(l['origin_transfer_min']),
|
|
289
|
+
destTransferMin: Number(l['dest_transfer_min']),
|
|
290
|
+
redEye: Boolean(l['red_eye']),
|
|
291
|
+
redEyeDurationMin: Number(sv['red_eye_duration_min'] ?? l['red_eye_duration_min'] ?? 0),
|
|
292
|
+
groundRecoveryMin: l['ground_recovery_min'] ? Number(l['ground_recovery_min']) : undefined,
|
|
293
|
+
tzOffsetMin,
|
|
294
|
+
originTzOffsetMin: depR.offsetMin,
|
|
295
|
+
ianaDepZone: depZone,
|
|
296
|
+
ianaArrZone: arrZone,
|
|
297
|
+
},
|
|
298
|
+
depWeekday: weekdayFromYmd(svcDepDate),
|
|
299
|
+
}
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
const role = ((l['services'] as unknown[]).length === 1 ? 'fixed' : 'choice') as 'fixed' | 'choice'
|
|
303
|
+
legs.push({
|
|
304
|
+
id: String(l['id']),
|
|
305
|
+
role,
|
|
306
|
+
note: l['note'] ? String(l['note']) : undefined,
|
|
307
|
+
date: legDate,
|
|
308
|
+
anchors: { arriveByMin: l['arrive_by'] ? hhmmToMin(String(l['arrive_by'])) : undefined },
|
|
309
|
+
route: routeHint(l['id'] as string),
|
|
310
|
+
options,
|
|
311
|
+
})
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// work_window:home_zone 必需(v2 的 home 偏移走 IANA,数值字段不再权威);同时给两值报 reject。
|
|
315
|
+
let workWindow: WorkWindowSpec | undefined
|
|
316
|
+
if (ww) {
|
|
317
|
+
const hasHomeZone = Boolean(ww['home_zone'])
|
|
318
|
+
const hasHomeNum = ww['home_tz_offset_min'] !== undefined
|
|
319
|
+
if (hasHomeZone && hasHomeNum) {
|
|
320
|
+
throw Object.assign(new Error('parseFlightPackToSpecV2: work_window cannot combine home_zone and home_tz_offset_min'), { code: 'v2_ww_conflict' })
|
|
321
|
+
}
|
|
322
|
+
if (!hasHomeZone) {
|
|
323
|
+
throw Object.assign(new Error('parseFlightPackToSpecV2: work_window.home_zone is required for v2'), { code: 'v2_ww_home_zone_required' })
|
|
324
|
+
}
|
|
325
|
+
// 在 v2 parse 边界即时验证 home_zone 是已知 IANA zone(root counterexample #3:不许 fail-open)。
|
|
326
|
+
// 用 tz-resolver 的 isKnownZoneLoose:接受 Intl.DateTimeFormat 能识别的合法 alias(US/Pacific / ROK / Etc/GMT+8)。
|
|
327
|
+
const homeZone = String(ww['home_zone'])
|
|
328
|
+
if (!isKnownZoneLoose(homeZone)) {
|
|
329
|
+
throw Object.assign(new Error(`parseFlightPackToSpecV2: work_window.home_zone '${homeZone}' is not a known IANA zone`), { code: 'v2_home_zone_unknown' })
|
|
330
|
+
}
|
|
331
|
+
const startHHmm = ww['start'] ? String(ww['start']) : undefined
|
|
332
|
+
const endHHmm = ww['end'] ? String(ww['end']) : undefined
|
|
333
|
+
const startMin = ww['start_min'] !== undefined ? Number(ww['start_min']) : (startHHmm ? hhmmToMin(startHHmm) : hhmmToMin('10:00'))
|
|
334
|
+
const endMin = ww['end_min'] !== undefined ? Number(ww['end_min']) : (endHHmm ? hhmmToMin(endHHmm) : hhmmToMin('19:00'))
|
|
335
|
+
// numeric homeTzOffsetMin 仅占位:在 v2 工作窗口过滤时按 home_zone + dep_date 重算;预填 leg-date 占位 0
|
|
336
|
+
workWindow = {
|
|
337
|
+
homeTzOffsetMin: 0,
|
|
338
|
+
startMin,
|
|
339
|
+
endMin,
|
|
340
|
+
workdays: (ww['workdays'] as number[] | undefined) ?? [0, 1, 2, 3, 4],
|
|
341
|
+
homeZone,
|
|
342
|
+
startHHmm,
|
|
343
|
+
endHHmm,
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return { segments: legs, workWindow, [FLIGHT_PACK_VERSION]: 2 }
|
|
348
|
+
}
|
|
349
|
+
|
|
201
350
|
const WEEKDAY_IDX: Record<string, number> = { mon: 0, tue: 1, wed: 2, thu: 3, fri: 4, sat: 5, sun: 6 }
|
|
202
351
|
const WEEKDAY_CN: Record<string, string> = { mon: '一', tue: '二', wed: '三', thu: '四', fri: '五', sat: '六', sun: '日' }
|
|
203
352
|
|
|
204
|
-
/**
|
|
353
|
+
/** YYYY-MM-DD → mon/tue/wed/thu/fri/sat/sun;v2 路径给 option 标 depWeekday 用。 */
|
|
354
|
+
export function weekdayFromYmd(ymd: string): string {
|
|
355
|
+
const m = ymd.match(/^(\d{4})-(\d{2})-(\d{2})$/)
|
|
356
|
+
if (!m) throw new Error(`weekdayFromYmd: bad ymd ${ymd}`)
|
|
357
|
+
const dow = new Date(Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3]))).getUTCDay()
|
|
358
|
+
return ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'][dow]!
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/** M-1:工作日的工作窗口内起飞 → 排除理由;否则 null(与 py _work_window_blocks 对齐)。
|
|
362
|
+
*
|
|
363
|
+
* Issue #343 v2:home zone 存在时,必须把"出发真实 UTC instant"投影到 home zone
|
|
364
|
+
* 取该 zone 的 date/weekday/HH:MM(root counterexample #2:Tokyo 06:30 ≈ LA 14:30 prev day);
|
|
365
|
+
* 不再用 dep-local date 直接当 home date。
|
|
366
|
+
*
|
|
367
|
+
* root 评审复核:`depUtcMs` 是已知确定的 instant,直接 `readWallParts(homeZone, depUtcMs)`
|
|
368
|
+
* 取墙时分量 — 不要把 projected wall time 再喂给 `resolveOffsetForLocalDate` 重做 ±14h 扫描,
|
|
369
|
+
* 否则秋回退日 01:30 会被错误判为 overlap_ambiguous 而 fail-open(已知 instant 永远唯一)。
|
|
370
|
+
*
|
|
371
|
+
* dep/arr 的"墙上本地输入"(leg-level `dep`/`arr` 字段)由 parseFlightPackToSpecV2 在
|
|
372
|
+
* 解析时已用 `resolveOffsetForLocalDate` 拒过 gap/overlap,所以 leg 进 solver 时
|
|
373
|
+
* 两个 instant 一定 strict-after 且偏移已知。
|
|
374
|
+
*
|
|
375
|
+
* home zone 未知时由 v2 parse 边界直接抛 v2_home_zone_unknown 拒绝;
|
|
376
|
+
* 这里兜底只在 v1 legacy 路径出现。
|
|
377
|
+
*/
|
|
205
378
|
function workWindowBlocks(spec: JourneySpecTS, option: SegmentOptionTS): string | null {
|
|
206
379
|
const ww = spec.workWindow
|
|
207
380
|
if (!ww || !option.move || !option.depWeekday) return null
|
|
208
|
-
|
|
209
|
-
const
|
|
210
|
-
const
|
|
211
|
-
const
|
|
212
|
-
|
|
381
|
+
|
|
382
|
+
const svc = option.move.services[0]
|
|
383
|
+
const dep = svc.depMin
|
|
384
|
+
const homeZone = ww.homeZone
|
|
385
|
+
|
|
386
|
+
let homeOffsetMin = ww.homeTzOffsetMin
|
|
387
|
+
let homeDepMin: number | undefined
|
|
388
|
+
let homeDepWeekday: string | undefined
|
|
389
|
+
|
|
390
|
+
if (homeZone) {
|
|
391
|
+
if (typeof svc.depUtcMs !== 'number') {
|
|
392
|
+
// v2 路径必然填了 depUtcMs;没填 = 数据病,按 red flag 排除该 option。
|
|
393
|
+
return i18nT('un.workwindow_unresolvable_reason', {
|
|
394
|
+
reason: `home zone ${homeZone} requires UTC dep instant (v2 path bug)`,
|
|
395
|
+
})
|
|
396
|
+
}
|
|
397
|
+
// 已知 instant 直接投影到 home zone 的墙时 — 无歧义,不必也不应重走 ±14h 扫描。
|
|
398
|
+
const homeParts = readWallParts(homeZone, svc.depUtcMs)
|
|
399
|
+
const homeIso = wallPartsToIso(homeParts)
|
|
400
|
+
homeDepMin = hhmmToMin(homeIso.hhmm)
|
|
401
|
+
homeDepWeekday = weekdayFromYmd(homeIso.ymd)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const effectiveDep = homeDepMin ?? dep
|
|
405
|
+
const effectiveWeekday = homeDepWeekday ?? option.depWeekday
|
|
406
|
+
if (!((ww.workdays ?? [0, 1, 2, 3, 4]).includes(WEEKDAY_IDX[effectiveWeekday]))) return null
|
|
407
|
+
|
|
408
|
+
// v2:startMin/endMin 与 homeDepMin 都是 home-zone 墙时分量(已 IANA 归一),
|
|
409
|
+
// 直接比较;不再做 originTzOffsetMin 偏移(root 复核该 shift 是 v1 数值残留,会让窗口错位)。
|
|
410
|
+
// v1 路径(无 homeZone, 走数值 homeTzOffsetMin)保留 shift 兼容旧 spec。
|
|
411
|
+
let start: number, end: number
|
|
412
|
+
if (homeZone) {
|
|
413
|
+
start = ww.startMin
|
|
414
|
+
end = ww.endMin
|
|
415
|
+
} else {
|
|
416
|
+
const shift = (option.move.originTzOffsetMin ?? 480) - homeOffsetMin
|
|
417
|
+
start = ww.startMin + shift
|
|
418
|
+
end = ww.endMin + shift
|
|
419
|
+
}
|
|
420
|
+
if (start <= effectiveDep && effectiveDep <= end) {
|
|
213
421
|
const hhmm = (m: number) => `${String(Math.floor(m / 60)).padStart(2, '0')}:${String(m % 60).padStart(2, '0')}`
|
|
214
|
-
return
|
|
422
|
+
return i18nT('un.workwindow_reason', {
|
|
423
|
+
wd: WEEKDAY_CN[effectiveWeekday] ?? '', dep: hhmm(effectiveDep), start: hhmm(start), end: hhmm(end),
|
|
424
|
+
})
|
|
215
425
|
}
|
|
216
426
|
return null
|
|
217
427
|
}
|
|
218
428
|
|
|
219
|
-
/** D-5:时区感知的段核算(与 py unified._evaluate_option_move 对齐)。
|
|
429
|
+
/** D-5:时区感知的段核算(与 py unified._evaluate_option_move 对齐)。
|
|
430
|
+
*
|
|
431
|
+
* Issue #343 d2d 算术唯一权威入口 = model.ts 的 `doorToDoorFromMove`。
|
|
432
|
+
* v2 路径会传 Service.depUtcMs/arrUtcMs(由 parseFlightPackToSpecV2 用 tz-resolver 投出),
|
|
433
|
+
* 此时 d2d 用真实 UTC instant 差 — 跨日 / 反向日界线 / DST 切换都正确;v2 arr<=dep 抛
|
|
434
|
+
* NonpositiveDurationError → 该 option 当作不可行(由 solveUnifiedInner 转 unsat_core)。
|
|
435
|
+
* v1 数值口径无 UTC 字段,走老 (arrMin - depMin) - tzOffsetMin;字节不变。
|
|
436
|
+
*/
|
|
220
437
|
function evaluateOptionMove(segId: string, mv: MoveSpecTS): LegReport & { d2d_min?: number } {
|
|
221
438
|
const svc = mv.services[0]
|
|
439
|
+
let d2dMin: number
|
|
440
|
+
try {
|
|
441
|
+
d2dMin = doorToDoorFromMove(svc, mv).doorToDoorMin
|
|
442
|
+
} catch (e) {
|
|
443
|
+
if (e instanceof NonpositiveDurationError) {
|
|
444
|
+
// d2d 病态输入 → 把这条 option 抛红 flag;这里走结构化 reject 而非吞错。
|
|
445
|
+
throw Object.assign(new Error(`evaluateOptionMove ${segId}/${svc.id}: ${e.message}`), {
|
|
446
|
+
code: e.code,
|
|
447
|
+
kind: e.kind,
|
|
448
|
+
})
|
|
449
|
+
}
|
|
450
|
+
throw e
|
|
451
|
+
}
|
|
222
452
|
const wake = svc.depMin - mv.bufferMin - mv.originTransferMin
|
|
223
|
-
// 真实时长 = (到达−出发) − 时差;EK329: 215−(−240)=455min=7h35m
|
|
224
|
-
const trueFlight = (svc.arrMin - svc.depMin) - (mv.tzOffsetMin ?? 0)
|
|
225
|
-
const d2d = mv.bufferMin + mv.originTransferMin + trueFlight + mv.destTransferMin
|
|
226
453
|
const wakeDisplay = wake < 0 ? `${minToHhmm(wake + 1440)}(前一日)` : minToHhmm(wake)
|
|
227
454
|
const arriveStay = svc.arrMin + mv.destTransferMin
|
|
228
455
|
|
|
@@ -238,7 +465,7 @@ function evaluateOptionMove(segId: string, mv: MoveSpecTS): LegReport & { d2d_mi
|
|
|
238
465
|
if (wake < 5 * 60) energy -= 30
|
|
239
466
|
else if (wake < 6 * 60) energy -= 25
|
|
240
467
|
if (arriveStay > 21 * 60) energy -= 10
|
|
241
|
-
if (
|
|
468
|
+
if (d2dMin > 6 * 60) energy -= 10
|
|
242
469
|
energy = Math.max(0, energy)
|
|
243
470
|
}
|
|
244
471
|
return {
|
|
@@ -247,18 +474,22 @@ function evaluateOptionMove(segId: string, mv: MoveSpecTS): LegReport & { d2d_mi
|
|
|
247
474
|
wake: wakeDisplay,
|
|
248
475
|
wakeMin: wake,
|
|
249
476
|
arrive_stay: minToHhmm(arriveStay),
|
|
250
|
-
door_to_door: `${Math.floor(
|
|
251
|
-
d2d_min:
|
|
477
|
+
door_to_door: `${Math.floor(d2dMin / 60)}h${String(d2dMin % 60).padStart(2, '0')}m`,
|
|
478
|
+
d2d_min: d2dMin,
|
|
252
479
|
energy_pct: Math.round(energy),
|
|
253
480
|
price_cny: svc.priceCny,
|
|
254
481
|
}
|
|
255
482
|
}
|
|
256
483
|
|
|
484
|
+
function releaseZ3(resource: { release?: () => void } | null | undefined): void {
|
|
485
|
+
if (typeof resource?.release === 'function') resource.release()
|
|
486
|
+
}
|
|
487
|
+
|
|
257
488
|
/** 航班链形态求解:按 Option 选择,锚点命名约束,core 剥竖线(D-2 修复) */
|
|
258
489
|
export async function solveUnified(spec: JourneySpecTS): Promise<{
|
|
259
490
|
feasible: boolean
|
|
260
491
|
money_cny?: number
|
|
261
|
-
legs?: Array<LegReport & { leg: string }>
|
|
492
|
+
legs?: Array<LegReport & { leg: string; d2d_min?: number }>
|
|
262
493
|
red_flags?: string[]
|
|
263
494
|
unsat_core?: string[]
|
|
264
495
|
suggestions?: Array<{ relax: string; money_cny: number }>
|
|
@@ -278,7 +509,7 @@ export async function solveUnified(spec: JourneySpecTS): Promise<{
|
|
|
278
509
|
async function solveUnifiedInner(spec: JourneySpecTS): Promise<{
|
|
279
510
|
feasible: boolean
|
|
280
511
|
money_cny?: number
|
|
281
|
-
legs?: Array<LegReport & { leg: string }>
|
|
512
|
+
legs?: Array<LegReport & { leg: string; d2d_min?: number }>
|
|
282
513
|
red_flags?: string[]
|
|
283
514
|
unsat_core?: string[]
|
|
284
515
|
suggestions?: Array<{ relax: string; money_cny: number }>
|
|
@@ -310,7 +541,9 @@ async function solveUnifiedInner(spec: JourneySpecTS): Promise<{
|
|
|
310
541
|
}
|
|
311
542
|
}
|
|
312
543
|
|
|
313
|
-
|
|
544
|
+
// Z3 会话进互斥门(骨架/预过滤等网络段留在门外):solveUnifiedInner 的判定段
|
|
545
|
+
// 从 here 到 return 全部独占共享实例。
|
|
546
|
+
return withZ3('unified.solveUnifiedInner', async z3 => {
|
|
314
547
|
const { Bool, If, Int, Solver, Sum } = z3
|
|
315
548
|
const wakeFloor = spec.defaultWakeFloorMin ?? hhmmToMin('06:00')
|
|
316
549
|
|
|
@@ -348,50 +581,77 @@ async function solveUnifiedInner(spec: JourneySpecTS): Promise<{
|
|
|
348
581
|
}
|
|
349
582
|
|
|
350
583
|
const s = new Solver()
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
for (const seg of spec.segments) {
|
|
357
|
-
for (let i = 0; i < allSels[seg.id].length; i++) {
|
|
358
|
-
const v = await maybeAwait(model.eval(allSels[seg.id][i], true))
|
|
359
|
-
if (String(v) !== 'true') continue
|
|
360
|
-
const o = seg.options[i]
|
|
361
|
-
out.push({ leg: seg.id, ...evaluateOptionMove(seg.id, o.move!) })
|
|
362
|
-
break
|
|
363
|
-
}
|
|
584
|
+
let sOpen = true
|
|
585
|
+
const closeS = () => {
|
|
586
|
+
if (sOpen) {
|
|
587
|
+
releaseZ3(s)
|
|
588
|
+
sOpen = false
|
|
364
589
|
}
|
|
365
|
-
return out
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
async function maybeAwait<T>(v: T | Promise<T>): Promise<T> {
|
|
369
|
-
return v instanceof Promise ? await v : v
|
|
370
590
|
}
|
|
591
|
+
try {
|
|
592
|
+
for (const sel of Object.values(allSels)) s.add(exactlyOne(sel))
|
|
593
|
+
for (const [name, expr] of Object.entries(assertions)) s.addAndTrack(expr, name)
|
|
594
|
+
|
|
595
|
+
const report = async (model: any): Promise<Array<LegReport & { leg: string }>> => {
|
|
596
|
+
const out: Array<LegReport & { leg: string }> = []
|
|
597
|
+
for (const seg of spec.segments) {
|
|
598
|
+
for (let i = 0; i < allSels[seg.id].length; i++) {
|
|
599
|
+
const v = await maybeAwait(model.eval(allSels[seg.id][i], true))
|
|
600
|
+
if (String(v) !== 'true') continue
|
|
601
|
+
const o = seg.options[i]
|
|
602
|
+
out.push({ leg: seg.id, ...evaluateOptionMove(seg.id, o.move!) })
|
|
603
|
+
break
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return out
|
|
607
|
+
}
|
|
371
608
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
const redFlags = legs
|
|
376
|
-
.filter(l => spec.segments.find(sg => sg.id === l.leg)?.options.some(o => o.move?.redEye) && l.energy_pct < 50)
|
|
377
|
-
.map(l => `${l.leg} 落地精力仅 ${l.energy_pct}%(红眼后直奔事务,当日不宜安排重要会议)`)
|
|
378
|
-
return { feasible: true, money_cny: money, legs, red_flags: redFlags, work_window_exclusions: exclusions, skeleton_notes: skeletonNotes.length ? skeletonNotes : undefined }
|
|
379
|
-
}
|
|
609
|
+
async function maybeAwait<T>(v: T | Promise<T>): Promise<T> {
|
|
610
|
+
return v instanceof Promise ? await v : v
|
|
611
|
+
}
|
|
380
612
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
613
|
+
if (String(await s.check()) !== 'unsat') {
|
|
614
|
+
const model = s.model()
|
|
615
|
+
try {
|
|
616
|
+
const legs = await report(model)
|
|
617
|
+
const money = legs.reduce((a, l) => a + l.price_cny, 0)
|
|
618
|
+
const redFlags = legs
|
|
619
|
+
.filter(l => spec.segments.find(sg => sg.id === l.leg)?.options.some(o => o.move?.redEye) && l.energy_pct < 50)
|
|
620
|
+
.map(l => i18nT('un.redflag_redeye', { leg: l.leg, pct: l.energy_pct }))
|
|
621
|
+
return { feasible: true, money_cny: money, legs, red_flags: redFlags, work_window_exclusions: exclusions, skeleton_notes: skeletonNotes.length ? skeletonNotes : undefined }
|
|
622
|
+
} finally {
|
|
623
|
+
releaseZ3(model)
|
|
624
|
+
}
|
|
388
625
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
626
|
+
|
|
627
|
+
const core = coreOf(s).sort()
|
|
628
|
+
const suggestions: Array<{ relax: string; money_cny: number }> = []
|
|
629
|
+
closeS()
|
|
630
|
+
for (const name of core) {
|
|
631
|
+
const s2 = new Solver()
|
|
632
|
+
try {
|
|
633
|
+
for (const sel of Object.values(allSels)) s2.add(exactlyOne(sel))
|
|
634
|
+
for (const [n2, expr] of Object.entries(assertions)) {
|
|
635
|
+
if (n2 !== name) s2.addAndTrack(expr, n2)
|
|
636
|
+
}
|
|
637
|
+
if (String(await s2.check()) !== 'unsat') {
|
|
638
|
+
const model = s2.model()
|
|
639
|
+
try {
|
|
640
|
+
const legs = await report(model)
|
|
641
|
+
suggestions.push({ relax: name, money_cny: legs.reduce((a, l) => a + l.price_cny, 0) })
|
|
642
|
+
} finally {
|
|
643
|
+
releaseZ3(model)
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
} finally {
|
|
647
|
+
releaseZ3(s2)
|
|
648
|
+
}
|
|
392
649
|
}
|
|
650
|
+
return { feasible: false, unsat_core: core, suggestions }
|
|
651
|
+
} finally {
|
|
652
|
+
closeS()
|
|
393
653
|
}
|
|
394
|
-
|
|
654
|
+
})
|
|
395
655
|
}
|
|
396
656
|
|
|
397
657
|
// ---- 候选形态求解(枚举过滤,与 py solve_choice_segment 对齐) --------------------
|
|
@@ -504,7 +764,7 @@ export function solveChoiceSegment(spec: JourneySpecTS, req: TravelRequest): Rec
|
|
|
504
764
|
conds['budget_cny'] = Math.min(...budgetDropped.map(c => c.cost.moneyCny))
|
|
505
765
|
}
|
|
506
766
|
if (opt.bestMonths?.length) conds['best_months'] = opt.bestMonths
|
|
507
|
-
wish = { name: opt.label, conditions: conds, reason:
|
|
767
|
+
wish = { name: opt.label, conditions: conds, reason: i18nT('sg.wish_reason_window', { days: windowDays, minDays }) }
|
|
508
768
|
}
|
|
509
769
|
verdicts.push({
|
|
510
770
|
candidate_id: opt.id, name: opt.label, feasible: false, imagery_match: opt.score ?? 0,
|
|
@@ -524,57 +784,63 @@ function renderCandidateMarkdown(req: TravelRequest, verdicts: Array<Record<stri
|
|
|
524
784
|
const feasible = verdicts.filter(v => v['feasible']).sort((a, b) => (b['imagery_match'] as number) - (a['imagery_match'] as number))
|
|
525
785
|
const parked = verdicts.filter(v => !v['feasible'])
|
|
526
786
|
const lines: string[] = []
|
|
527
|
-
lines.push(
|
|
528
|
-
lines.push(
|
|
529
|
-
|
|
787
|
+
lines.push(i18nT('md.header', { note: req.note }))
|
|
788
|
+
lines.push(i18nT('md.constraints', {
|
|
789
|
+
days: req.windowDays, budget: req.budgetCny,
|
|
790
|
+
hours: requiredUsableHours(req.motivation).toFixed(1),
|
|
791
|
+
}))
|
|
530
792
|
lines.push('')
|
|
531
793
|
for (const v of parked) {
|
|
532
794
|
const core = (v['unsat_core'] as string[]) ?? []
|
|
533
|
-
lines.push(
|
|
795
|
+
lines.push(i18nT('md.infeasible', { name: String(v['name']), core: core.join('、') }))
|
|
534
796
|
const sug = ((v['suggestions'] as Array<Record<string, unknown>>) ?? [])[0]
|
|
535
|
-
if (sug) lines.push(
|
|
797
|
+
if (sug) lines.push(i18nT('md.relax_one', { relax: String(sug['relax']), money: String(sug['resulting_money_cny'] ?? '') }))
|
|
536
798
|
const wish = v['wish_pool'] as Record<string, unknown> | null
|
|
537
799
|
if (wish) {
|
|
538
800
|
const conds = wish['conditions'] as Record<string, unknown>
|
|
539
|
-
const budgetNote = conds['budget_cny'] ?
|
|
801
|
+
const budgetNote = conds['budget_cny'] ? i18nT('md.wish_budget', { cny: String(conds['budget_cny']) }) : ''
|
|
540
802
|
const months = conds['best_months'] as number[] | undefined
|
|
541
|
-
const season = months ?
|
|
542
|
-
lines.push(
|
|
803
|
+
const season = months ? i18nT('md.wish_season', { months: String(months) }) : ''
|
|
804
|
+
lines.push(i18nT('md.wish_pool', { days: String(conds['days']), budgetNote, season }))
|
|
543
805
|
}
|
|
544
806
|
}
|
|
545
807
|
lines.push('')
|
|
546
808
|
for (const v of feasible) {
|
|
547
809
|
const ch = v['chosen'] as Record<string, unknown>
|
|
548
|
-
const
|
|
549
|
-
lines.push(
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
810
|
+
const tc = v['true_cost'] as Record<string, unknown>
|
|
811
|
+
lines.push(i18nT('md.feasible_unified', {
|
|
812
|
+
name: String(v['name']), svc: String(ch['out_service']), mode: String(ch['out_transfer']),
|
|
813
|
+
wake: String(tc['wake']), energy: String(tc['energy_arrival_pct']), d2d: String(tc['door_to_door_out']),
|
|
814
|
+
hours: String(tc['usable_hours']), money: String(tc['money_cny']),
|
|
815
|
+
}))
|
|
553
816
|
}
|
|
554
817
|
if (feasible.length) {
|
|
555
818
|
lines.push('')
|
|
556
819
|
const best = feasible[0]
|
|
557
820
|
const alt = feasible[1]
|
|
558
|
-
lines.push(
|
|
559
|
-
+ (alt ?
|
|
821
|
+
lines.push(i18nT('md.recommend', { best: String(best['name']), match: ((best['imagery_match'] as number) * 100).toFixed(0) })
|
|
822
|
+
+ (alt ? i18nT('md.alt', {
|
|
823
|
+
name: String(alt['name']), money: String((alt['true_cost'] as Record<string, unknown>)['money_cny']),
|
|
824
|
+
match: ((alt['imagery_match'] as number) * 100).toFixed(0),
|
|
825
|
+
}) : ''))
|
|
560
826
|
}
|
|
561
827
|
lines.push('')
|
|
562
|
-
lines.push('
|
|
828
|
+
lines.push(i18nT('md.decide_two'))
|
|
563
829
|
if (feasible.length >= 2) {
|
|
564
|
-
lines.push(
|
|
830
|
+
lines.push(i18nT('md.q_choice', { a: String(feasible[0]['name']), b: String(feasible[1]['name']) }))
|
|
565
831
|
} else if (feasible.length === 1) {
|
|
566
|
-
lines.push(
|
|
832
|
+
lines.push(i18nT('md.q_single', { name: String(feasible[0]['name']) }))
|
|
567
833
|
} else {
|
|
568
|
-
lines.push('
|
|
834
|
+
lines.push(i18nT('md.q_none'))
|
|
569
835
|
}
|
|
570
836
|
const p0 = parked[0]
|
|
571
837
|
const p0wish = p0?.['wish_pool'] as Record<string, unknown> | null
|
|
572
838
|
if (p0wish) {
|
|
573
839
|
const conds = p0wish['conditions'] as Record<string, unknown>
|
|
574
|
-
lines.push(
|
|
840
|
+
lines.push(i18nT('md.q_wish', { name: String(p0!['name']), days: String(conds['days']) }))
|
|
575
841
|
} else if (feasible.length) {
|
|
576
842
|
const ch = feasible[0]['chosen'] as Record<string, unknown>
|
|
577
|
-
lines.push(
|
|
843
|
+
lines.push(i18nT('md.q_depart', { id: String(ch['out_service']), dep: '' }))
|
|
578
844
|
}
|
|
579
845
|
return lines.join('\n')
|
|
580
846
|
}
|