@danceiny/gotry 0.0.1-rc.8 → 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 -217
- 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 -49
- 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 +968 -0
- 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 +126 -0
- package/dist/capabilities/session/adapters/ctrip-flight.js +141 -0
- 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 +54 -0
- 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 +50 -0
- 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 +107 -0
- package/dist/capabilities/session/static-flight-golden.js +150 -0
- package/dist/capabilities/session/transport.js +109 -0
- 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 +686 -0
- 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 +72 -0
- 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 +68 -10
- 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 +67 -0
- 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 +480 -0
- package/dist/scripts/ledger-workflow-crash.js +32 -0
- package/dist/scripts/managed-dsh-cleanup-diagnostic-proof.js +184 -0
- package/dist/scripts/managed-dsh-close-failure-proof.js +172 -0
- package/dist/scripts/managed-dsh-run-port-proof.js +153 -0
- package/dist/scripts/managed-dsh-terminal-proof.js +252 -0
- package/dist/scripts/map-tools-vendor-package-proof.js +365 -0
- package/dist/scripts/memory-capture-tests.js +242 -61
- package/dist/scripts/memory-decay-tests.js +49 -0
- package/dist/scripts/memory-lifecycle-tests.js +657 -0
- package/dist/scripts/memory-lifecycle.js +196 -0
- package/dist/scripts/memory-metrics.js +29 -31
- 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 +43 -42
- 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 +31 -0
- package/dist/scripts/session-attach-poc.js +62 -0
- 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 +60 -0
- package/dist/scripts/session-link-tests.js +478 -0
- package/dist/scripts/session-login.js +29 -0
- package/dist/scripts/session-tests.js +862 -0
- 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 +703 -219
- 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 +261 -0
- package/dist/scripts/state-cli.js +585 -0
- 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 +59 -0
- 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 +86 -0
- 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 +2305 -731
- 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 +358 -162
- package/dist/src/memory-capture.js +180 -32
- package/dist/src/memory-decay.js +47 -0
- 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 +767 -0
- 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 +59 -0
- 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 +538 -360
- package/dist/src/wish-pool.js +42 -12
- package/dist/src/write-gate.js +495 -0
- package/dist/src/z3-shared.js +284 -0
- package/extension/README.md +21 -0
- package/extension/README.zh-CN.md +21 -0
- package/extension/background.js +463 -0
- package/extension/content-bridge.js +270 -0
- package/extension/content-main.js +111 -0
- package/extension/manifest.json +60 -0
- package/package.json +407 -14
- package/schemas/booking.intent.schema.json +193 -0
- package/schemas/booking.surface.schema.json +2622 -0
- package/ts/capabilities/agent-reach-deep.ts +114 -0
- package/ts/capabilities/agent-reach.ts +198 -0
- package/ts/capabilities/anything.ts +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 +296 -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 +2107 -224
- 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 +278 -40
- 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 +360 -89
- package/ts/src/wish-pool.ts +79 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 行程 HTML 渲染器(issue #442,父 #438;共享契约层抽取见 issue #564)。
|
|
3
|
+
*
|
|
4
|
+
* 纯有界渲染器:拒绝畸形输入,产出单文件自包含 HTML。本模块不执行 IO、
|
|
5
|
+
* 不启动服务、不解析 Markdown、不做夜数/预算/时间运算——只把显式行程结构
|
|
6
|
+
* 与已注册事实投影成可导航、可展开的安全文档。
|
|
7
|
+
*
|
|
8
|
+
* 输入校验、事实归一化与证据卡渲染的单一事实源在 `itinerary-doc-shared.ts`
|
|
9
|
+
* (两个文档投影共用,防第二实现漂移);本模块只保留单页文档的排版与组装。
|
|
10
|
+
*
|
|
11
|
+
* 两条永不合流的证据面:
|
|
12
|
+
* 1. 计划面(planner:trip_start/trip_end/stays/od_segments)——用户排期意图;
|
|
13
|
+
* 2. 证据面(facts:调用方从注册表选出的 BookableFact)——exact-date 查询结论,
|
|
14
|
+
* 逐条保留 tier/bookability/source/query_id/fetched_at/as_of。
|
|
15
|
+
* 目的地级酒店库存绝不宣称「某家酒店有房/可订」;无任何整体 verified 徽章。
|
|
16
|
+
*
|
|
17
|
+
* 渲染闸锚点纪律:本模块不嵌入 `<!-- fact:<id> -->` 锚点(那属 Markdown 产物闸),
|
|
18
|
+
* 事实 id 只作为可见文本展示,避免整篇 HTML 被当作锚点行解析。
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
DOC_BASE_CSS,
|
|
23
|
+
DOC_NARROW_CSS,
|
|
24
|
+
EVIDENCE_BOUNDARY_NOTICE,
|
|
25
|
+
MODE_LABEL,
|
|
26
|
+
TWO_PLANE_STATEMENT,
|
|
27
|
+
buildDateIndex,
|
|
28
|
+
docBytesError,
|
|
29
|
+
escapeHtml,
|
|
30
|
+
factIdSlug,
|
|
31
|
+
hotelFactsForStay,
|
|
32
|
+
leftoverFacts,
|
|
33
|
+
normalizeDocInput,
|
|
34
|
+
renderDateList,
|
|
35
|
+
renderFact,
|
|
36
|
+
renderFactCountsLine,
|
|
37
|
+
renderHotelEvidenceSection,
|
|
38
|
+
renderHotelNote,
|
|
39
|
+
renderLeftoverBlock,
|
|
40
|
+
renderPlannedStay,
|
|
41
|
+
renderPolicyNote,
|
|
42
|
+
renderSegment,
|
|
43
|
+
routeFactsFor,
|
|
44
|
+
timeEl,
|
|
45
|
+
type ItineraryDocResult,
|
|
46
|
+
type NormalizedFact,
|
|
47
|
+
type NormalizedFlight,
|
|
48
|
+
type NormalizedHotel,
|
|
49
|
+
type NormalizedItinerary,
|
|
50
|
+
type NormalizedPolicy,
|
|
51
|
+
} from './itinerary-doc-shared.ts'
|
|
52
|
+
|
|
53
|
+
// API 面稳定性:既有消费方(itinerary-artifact.ts / 测试)继续从本模块导入
|
|
54
|
+
export { ITINERARY_DOC_LIMITS as ITINERARY_HTML_LIMITS } from './itinerary-doc-shared.ts'
|
|
55
|
+
export type { ItineraryDocInput as ItineraryHtmlInput, ItineraryDocResult as ItineraryHtmlResult } from './itinerary-doc-shared.ts'
|
|
56
|
+
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// 单页文档排版(组装共享层的内容原语;CSS 为本形态私有)
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
const CSS = DOC_BASE_CSS + `
|
|
62
|
+
.wrap{max-width:64rem;margin:0 auto;padding:1rem}
|
|
63
|
+
header.doc{border-bottom:1px solid var(--line);padding-bottom:.75rem}
|
|
64
|
+
nav.toc{border:1px solid var(--line);border-radius:6px;padding:.6rem .8rem;margin:1rem 0;background:var(--card)}
|
|
65
|
+
nav.toc h2{font-size:1rem}
|
|
66
|
+
nav.toc ul{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:.35rem .9rem}
|
|
67
|
+
nav.toc ul ul{margin-left:.9rem;font-size:.92rem;display:block}
|
|
68
|
+
section{margin:1.75rem 0}
|
|
69
|
+
` + DOC_NARROW_CSS + `
|
|
70
|
+
@media (max-width:480px){
|
|
71
|
+
.wrap{padding:.6rem}
|
|
72
|
+
}`
|
|
73
|
+
|
|
74
|
+
function render(input: { title: string; itinerary: NormalizedItinerary; facts: NormalizedFact[] }): string {
|
|
75
|
+
const { title, itinerary, facts } = input
|
|
76
|
+
const stays = itinerary.stays
|
|
77
|
+
const segs = itinerary.od_segments
|
|
78
|
+
const hotelFacts = facts.filter((f): f is NormalizedHotel => f.kind === 'hotel')
|
|
79
|
+
const policyFacts = facts.filter((f): f is NormalizedPolicy => f.kind === 'policy')
|
|
80
|
+
const flightFacts = facts.filter((f): f is NormalizedFlight => f.kind === 'flight' || f.kind === 'train')
|
|
81
|
+
|
|
82
|
+
const stayBlocks = stays.map((stay, i) => renderPlannedStay(stay, `stay-${i + 1}`, hotelFactsForStay(hotelFacts, stay)))
|
|
83
|
+
const segBlocks = segs.map((seg, i) => renderSegment(seg, `seg-${i + 1}`, routeFactsFor(facts, seg), itinerary.trip_start))
|
|
84
|
+
|
|
85
|
+
const stayDateItems = stays.filter((s, i) => stays.findIndex(x => x.check_in === s.check_in) === i)
|
|
86
|
+
.map(s => `<li>${timeEl(s.check_in)} — <a href="#stay-${stays.indexOf(s) + 1}">计划住宿 ${escapeHtml(s.place)}</a></li>`)
|
|
87
|
+
.join('')
|
|
88
|
+
const segDateItems = [...new Set(segs.map(s => s.date))].sort()
|
|
89
|
+
.map(d => `<li>${timeEl(d)} — <a href="#segments">交通段</a></li>`)
|
|
90
|
+
.join('')
|
|
91
|
+
|
|
92
|
+
const dateList = renderDateList(buildDateIndex(itinerary))
|
|
93
|
+
const leftovers = leftoverFacts(facts, itinerary)
|
|
94
|
+
const leftoverBlock = renderLeftoverBlock(leftovers, itinerary.trip_start)
|
|
95
|
+
const hotelNote = renderHotelNote(hotelFacts)
|
|
96
|
+
const policyNote = renderPolicyNote(policyFacts)
|
|
97
|
+
|
|
98
|
+
return `<!DOCTYPE html>
|
|
99
|
+
<html lang="zh-CN">
|
|
100
|
+
<head>
|
|
101
|
+
<meta charset="utf-8">
|
|
102
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
103
|
+
<meta name="referrer" content="no-referrer">
|
|
104
|
+
<title>${escapeHtml(title)}</title>
|
|
105
|
+
<style>${CSS}</style>
|
|
106
|
+
</head>
|
|
107
|
+
<body>
|
|
108
|
+
<a class="skip" href="#main">跳到主要内容</a>
|
|
109
|
+
<div class="wrap">
|
|
110
|
+
<header class="doc">
|
|
111
|
+
<h1>${escapeHtml(title)}</h1>
|
|
112
|
+
<p>行程 ${timeEl(itinerary.trip_start)} → ${timeEl(itinerary.trip_end)}</p>
|
|
113
|
+
${TWO_PLANE_STATEMENT}
|
|
114
|
+
</header>
|
|
115
|
+
<main id="main">
|
|
116
|
+
${EVIDENCE_BOUNDARY_NOTICE}
|
|
117
|
+
<nav class="toc" aria-label="行程导航">
|
|
118
|
+
<h2>导航</h2>
|
|
119
|
+
<ul>
|
|
120
|
+
<li><a href="#dates">按日期</a><ul>${dateList.startsWith('<p') ? '<li class="muted">无</li>' : ''}</ul></li>
|
|
121
|
+
<li><a href="#segments">航班/交通段</a><ul>${segDateItems || '<li class="muted">无</li>'}</ul></li>
|
|
122
|
+
<li><a href="#stays">住宿</a><ul>${stayDateItems || '<li class="muted">无</li>'}</ul></li>
|
|
123
|
+
<li><a href="#facts">事实与证据</a></li>
|
|
124
|
+
</ul>
|
|
125
|
+
</nav>
|
|
126
|
+
<section id="dates"><h2>按日期行程</h2>
|
|
127
|
+
${dateList}
|
|
128
|
+
</section>
|
|
129
|
+
<section id="segments"><h2>航班/交通段</h2>
|
|
130
|
+
<p class="muted">下列条目是计划段(用户排期意图);每段内另列可回溯到该 route + date 的已注册事实。计划段不因存在事实而变成可下单。</p>
|
|
131
|
+
${segBlocks.join('') || '<p class="muted missing">没有显式交通段。</p>'}
|
|
132
|
+
</section>
|
|
133
|
+
<section id="stays"><h2>住宿</h2>
|
|
134
|
+
${hotelNote}
|
|
135
|
+
${stayBlocks.join('') || '<p class="muted missing">没有显式住宿段。</p>'}
|
|
136
|
+
${renderHotelEvidenceSection(hotelFacts, itinerary.trip_start)}
|
|
137
|
+
</section>
|
|
138
|
+
<section id="facts"><h2>事实与证据</h2>
|
|
139
|
+
${renderFactCountsLine(facts)}
|
|
140
|
+
${policyFacts.map(f => renderFact(f, `policy-${factIdSlug(f.fact.fact_id)}`, itinerary.trip_start)).join('')}
|
|
141
|
+
${policyNote}
|
|
142
|
+
${leftoverBlock}
|
|
143
|
+
<h3>全部航班/车次事实(含负事实与冲突)</h3>
|
|
144
|
+
${flightFacts.map(f => renderFact(f, `flight-${factIdSlug(f.fact.fact_id)}`, itinerary.trip_start)).join('') || '<p class="muted missing">本次输入没有航班/车次事实。</p>'}
|
|
145
|
+
</section>
|
|
146
|
+
</main>
|
|
147
|
+
</div>
|
|
148
|
+
</body>
|
|
149
|
+
</html>
|
|
150
|
+
`
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// 入口
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* 纯渲染入口。`input` 是任意运行时数据:结构、枚举、真实日历日期、有限数值、
|
|
159
|
+
* 容量与文档大小全部现场校验;任何一项不合法即返回 `ok:false` + 结构化 errors,
|
|
160
|
+
* 绝不静默丢弃核心行程后仍返回「成功」。校验与归一化的单一事实源在
|
|
161
|
+
* `itinerary-doc-shared.ts`,与 deck 投影共用同一拒绝语义。
|
|
162
|
+
*/
|
|
163
|
+
export function renderItineraryHtml(input: unknown): ItineraryDocResult {
|
|
164
|
+
const normalized = normalizeDocInput(input)
|
|
165
|
+
if (!normalized.ok) return normalized
|
|
166
|
+
const html = render(normalized)
|
|
167
|
+
// 输出大小硬上限:超限即整体失败(检查实现于共享层)
|
|
168
|
+
const bytesError = docBytesError(html)
|
|
169
|
+
if (bytesError !== undefined) return { ok: false, errors: bytesError }
|
|
170
|
+
return { ok: true, html }
|
|
171
|
+
}
|
package/ts/src/loop.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Stage 1 对话循环(S2 段1,docs/stage1-top-down-design.md §2.3)。
|
|
2
|
+
* Stage 1 对话循环(S2 段1,docs/design/stage1-top-down-design.md §2.3)。
|
|
3
3
|
*
|
|
4
4
|
* 自顶向下:循环只依赖两个端口——LlmPort(mock/真)与确定性工具(interview_next)。
|
|
5
5
|
* 求解/渲染接线是 S2 后续段;本段验收:重放用户开场白,系统一轮内完成
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import type { InterviewQuestion, TripState, TravelerProfile, Turn, CalendarState, SolveResult } from './contracts.ts'
|
|
10
10
|
import type { JourneySpecTS } from './unified.ts'
|
|
11
|
+
import { buildTimeAnchor, isRealIsoDate, planningWindowBounds, parsePlanningWindow, type PlanningWindow, type TimeAnchor } from './time-anchor.ts'
|
|
11
12
|
import type { TravelSlotExtraction } from './travel-slots.ts'
|
|
12
13
|
import { anythingSearch } from '../capabilities/anything.ts'
|
|
13
14
|
|
|
@@ -95,6 +96,98 @@ export function validateSpec(spec: JourneySpecTS): string | null {
|
|
|
95
96
|
return null
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
export interface PlanningWindowRejection {
|
|
100
|
+
segmentId: string
|
|
101
|
+
optionId: string
|
|
102
|
+
date: string
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface PlanningWindowCheck {
|
|
106
|
+
spec: JourneySpecTS
|
|
107
|
+
rejected: PlanningWindowRejection[]
|
|
108
|
+
error?: string
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface PlanningWindowOptions {
|
|
112
|
+
/** Registered feasibility tool opts into the dated-candidate default future floor. */
|
|
113
|
+
defaultDatedFuture?: boolean
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function isoDateInBounds(date: string, bounds: { start: string; end: string }): boolean {
|
|
117
|
+
const m = date.match(/^(\d{4})-(\d{2})-(\d{2})$/)
|
|
118
|
+
if (!m || !isRealIsoDate(Number(m[1]), Number(m[2]), Number(m[3]))) return false
|
|
119
|
+
return date >= bounds.start && date <= bounds.end
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function hasDatedCandidates(spec: JourneySpecTS): boolean {
|
|
123
|
+
return spec.segments.some(segment => segment.date !== undefined || segment.options.some(option => option.date !== undefined))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Enforce a named-year future window at the planner boundary. Historical
|
|
128
|
+
* lookup/backtesting remains an explicit bypass; an expired named year fails
|
|
129
|
+
* instead of being silently rolled into a different year.
|
|
130
|
+
*/
|
|
131
|
+
export function applyPlanningWindow(
|
|
132
|
+
spec: JourneySpecTS,
|
|
133
|
+
window: PlanningWindow | null,
|
|
134
|
+
anchor: TimeAnchor,
|
|
135
|
+
options: PlanningWindowOptions = {},
|
|
136
|
+
): PlanningWindowCheck {
|
|
137
|
+
// Dated recommendations are future-oriented even without optional named-year context.
|
|
138
|
+
// Fully dateless legacy calculations remain untouched; historical bypass is explicit.
|
|
139
|
+
if (window?.intent === 'historical') return { spec, rejected: [] }
|
|
140
|
+
const implicitFutureFloor = !window && options.defaultDatedFuture === true && hasDatedCandidates(spec)
|
|
141
|
+
if (!window && !implicitFutureFloor) return { spec, rejected: [] }
|
|
142
|
+
|
|
143
|
+
const currentYear = Number(anchor.today.slice(0, 4))
|
|
144
|
+
if (window && window.requestedYear < currentYear) {
|
|
145
|
+
return {
|
|
146
|
+
spec,
|
|
147
|
+
rejected: [],
|
|
148
|
+
error: `${window.requestedYear} 年已结束(当前规划参考日 ${anchor.today}),不会自动改用其他年份;如需历史查询请明确说明“历史/回测”。`,
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const bounds = window
|
|
153
|
+
? planningWindowBounds(window, anchor)
|
|
154
|
+
: { start: anchor.today, end: '9999-12-31' }
|
|
155
|
+
if (!bounds) return { spec, rejected: [], error: `无法建立 ${window!.requestedYear} 年的未来规划窗口。` }
|
|
156
|
+
|
|
157
|
+
const rejected: PlanningWindowRejection[] = []
|
|
158
|
+
const guardedSegments = spec.segments.map(segment => ({
|
|
159
|
+
...segment,
|
|
160
|
+
options: segment.options.filter(option => {
|
|
161
|
+
const date = option.date ?? segment.date
|
|
162
|
+
if (date && isoDateInBounds(date, bounds)) return true
|
|
163
|
+
rejected.push({ segmentId: segment.id, optionId: option.id, date: date ?? '未提供明确日期' })
|
|
164
|
+
return false
|
|
165
|
+
}),
|
|
166
|
+
}))
|
|
167
|
+
|
|
168
|
+
if (guardedSegments.some(segment => segment.options.length === 0)) {
|
|
169
|
+
return {
|
|
170
|
+
spec: { ...spec, segments: guardedSegments },
|
|
171
|
+
rejected,
|
|
172
|
+
error: implicitFutureFloor
|
|
173
|
+
? `带日期的未来推荐必须不早于当前规划参考日 ${bounds.start};没有带明确日期且在未来窗口内的可接受方案。`
|
|
174
|
+
: `${window!.requestedYear} 年未来规划窗口为 ${bounds.start} 至 ${bounds.end};没有带明确日期且落在窗口内的可接受方案。`,
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return { spec: { ...spec, segments: guardedSegments }, rejected }
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function latestPlanningWindow(history: Turn[], userMsg: string, anchor: TimeAnchor): PlanningWindow | null {
|
|
181
|
+
const turns = [...history, { role: 'user' as const, text: userMsg }]
|
|
182
|
+
for (let i = turns.length - 1; i >= 0; i--) {
|
|
183
|
+
const turn = turns[i]
|
|
184
|
+
if (turn?.role !== 'user') continue
|
|
185
|
+
const parsed = parsePlanningWindow(turn.text, anchor)
|
|
186
|
+
if (parsed) return parsed
|
|
187
|
+
}
|
|
188
|
+
return null
|
|
189
|
+
}
|
|
190
|
+
|
|
98
191
|
/** 求解端口:S3 起由 unified 求解器实现;循环只认此签名(确定性责任)。 */
|
|
99
192
|
export type SolvePort = (spec: JourneySpecTS) => Promise<SolveResult>
|
|
100
193
|
|
|
@@ -110,6 +203,51 @@ export interface AsyncTicket {
|
|
|
110
203
|
etaLabel: string
|
|
111
204
|
}
|
|
112
205
|
|
|
206
|
+
export const ASYNC_TERMINAL_SCHEMA = 'gotry_async_terminal.v1' as const
|
|
207
|
+
|
|
208
|
+
export interface AsyncTerminalOutcome {
|
|
209
|
+
schema: typeof ASYNC_TERMINAL_SCHEMA
|
|
210
|
+
ticket_id: string
|
|
211
|
+
status: 'succeeded' | 'failed'
|
|
212
|
+
passed: number
|
|
213
|
+
total: 4
|
|
214
|
+
checks: Record<string, boolean>
|
|
215
|
+
failed_checks: string[]
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function asyncTerminalOutcome(ticketId: string, checks: Record<string, boolean>): AsyncTerminalOutcome {
|
|
219
|
+
const failedChecks = Object.entries(checks).filter(([, passed]) => !passed).map(([name]) => name)
|
|
220
|
+
return {
|
|
221
|
+
schema: ASYNC_TERMINAL_SCHEMA,
|
|
222
|
+
ticket_id: ticketId,
|
|
223
|
+
status: failedChecks.length === 0 ? 'succeeded' : 'failed',
|
|
224
|
+
passed: Object.values(checks).filter(Boolean).length,
|
|
225
|
+
total: 4,
|
|
226
|
+
checks,
|
|
227
|
+
failed_checks: failedChecks,
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function terminalOutcomeFromState(ticketId: string, state: TripState): AsyncTerminalOutcome {
|
|
232
|
+
if (!state.spec || !state.solve) {
|
|
233
|
+
return asyncTerminalOutcome(ticketId, {
|
|
234
|
+
'1_承诺时间后必有明确产物': false,
|
|
235
|
+
'2_产物通过自检清单': false,
|
|
236
|
+
'3_待决问题全部是简单选择题': false,
|
|
237
|
+
'4_做不到的诚实说': false,
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return asyncTerminalOutcome(ticketId, {
|
|
242
|
+
'1_承诺时间后必有明确产物': Boolean(state.solve.legs?.length || state.solve.verdicts?.length),
|
|
243
|
+
'2_产物通过自检清单': state.solve.feasible
|
|
244
|
+
? (state.solve.legs?.every(l => (l as Record<string, unknown>)['energy_pct'] !== undefined) ?? false)
|
|
245
|
+
: Boolean(state.solve.unsat_core?.length),
|
|
246
|
+
'3_待决问题全部是简单选择题': state.gates.every(g => g.id === 'budget' || g.options.length >= 2),
|
|
247
|
+
'4_做不到的诚实说': state.solve.feasible || Boolean(state.solve.suggestions?.length),
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
|
|
113
251
|
export function isComplex(state: TripState): boolean {
|
|
114
252
|
/** 复杂度判据(架构占位,后续可细化):多段或多约束即深规划 */
|
|
115
253
|
const p = state.profile
|
|
@@ -134,21 +272,19 @@ export async function collectDeepPlanning(
|
|
|
134
272
|
state: TripState,
|
|
135
273
|
ticket: AsyncTicket,
|
|
136
274
|
solve: SolvePort,
|
|
137
|
-
): Promise<{ reply: string; state: TripState }> {
|
|
275
|
+
): Promise<{ reply: string; state: TripState; outcome: AsyncTerminalOutcome }> {
|
|
138
276
|
const spec = state.spec
|
|
139
|
-
if (!spec)
|
|
277
|
+
if (!spec) {
|
|
278
|
+
const outcome = terminalOutcomeFromState(ticket.id, state)
|
|
279
|
+
return { reply: '(内部状态缺失 spec——深度规划未就绪,这是不应发生的路径)', state, outcome }
|
|
280
|
+
}
|
|
140
281
|
state.solve = await solve(spec)
|
|
141
282
|
state.gates = state.gates.filter(g => !g.id.startsWith('async-'))
|
|
142
283
|
|
|
143
284
|
// 不失望四条自检(交付物自带,总纲 3.6)
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
'3_待决问题全部是简单选择题': state.gates.every(g => g.id === 'budget' || g.options.length >= 2),
|
|
148
|
-
'4_做不到的诚实说': state.solve.feasible || Boolean(state.solve.suggestions?.length),
|
|
149
|
-
}
|
|
150
|
-
const head = `# 回访交付:${ticket.objective}\n(工单 ${ticket.id},不失望四条:${Object.values(checks).every(Boolean) ? '4/4 ✅' : '有未达项 ❌'})`
|
|
151
|
-
return { reply: `${head}\n\n${renderSolve(state)}`, state }
|
|
285
|
+
const outcome = terminalOutcomeFromState(ticket.id, state)
|
|
286
|
+
const head = `# 回访交付:${ticket.objective}\n(工单 ${ticket.id},不失望四条:${outcome.status === 'succeeded' ? '4/4 ✅' : '有未达项 ❌'})`
|
|
287
|
+
return { reply: `${head}\n\n${renderSolve(state)}`, state, outcome }
|
|
152
288
|
}
|
|
153
289
|
|
|
154
290
|
/** 求解结果 → 人话(模板;S4 可由 LLM 润色,数字与判定不可改) */
|
|
@@ -158,13 +294,20 @@ export function renderSolve(state: TripState): string {
|
|
|
158
294
|
const lines: string[] = []
|
|
159
295
|
if (s.feasible) {
|
|
160
296
|
lines.push(`**方案可行,机票合计 ¥${s.money_cny}**`)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
297
|
+
// §7-1 issue #559 卡点 4:从 s.skeleton_notes 字符串编码里推断 unscaffolded 的 seg.id,
|
|
298
|
+
// 不动 unified.ts/journey.ts(内核冻结面 #234),用户面给 ⚠ 前缀与「枢纽对未在骨架覆盖」补充。
|
|
299
|
+
// skeleton_notes 单条形态:`<seg.id>: [骨架:openflights] ❌ ...` 或 `<seg.id>: [骨架:openflights] ✅ ...`
|
|
300
|
+
const unscaffoldedSegIds = new Set<string>()
|
|
301
|
+
for (const n of s.skeleton_notes ?? []) {
|
|
302
|
+
const m = /^([^:]+):\s*\[骨架:openflights\]\s*❌/.exec(n)
|
|
303
|
+
if (m) unscaffoldedSegIds.add(m[1])
|
|
165
304
|
}
|
|
166
|
-
for (const
|
|
167
|
-
|
|
305
|
+
for (const lg of s.legs ?? []) {
|
|
306
|
+
const l = lg as Record<string, string | number | boolean>
|
|
307
|
+
const unscaffoldedFlag = unscaffoldedSegIds.has(String(l['leg'])) ? '⚠ ' : ''
|
|
308
|
+
const unscaffoldedNote = unscaffoldedSegIds.has(String(l['leg'])) ? '(枢纽对未在骨架覆盖,实时源以新航线为准)' : ''
|
|
309
|
+
lines.push(`- ${unscaffoldedFlag}${l['leg']} ${l['service']}:${l['dep']} 起飞,${l['wake']} 出发,${l['arrive_stay']} 到,`
|
|
310
|
+
+ `门到门 ${l['door_to_door']},落地精力 ${l['energy_pct']}%,¥${l['price_cny']}${unscaffoldedNote ? ` ${unscaffoldedNote}` : ''}`)
|
|
168
311
|
}
|
|
169
312
|
for (const n of s.skeleton_notes ?? []) lines.push(`- ${n}`)
|
|
170
313
|
for (const f of s.red_flags ?? []) lines.push(`- ⚠️ ${f}`)
|
|
@@ -172,6 +315,9 @@ export function renderSolve(state: TripState): string {
|
|
|
172
315
|
lines.push(`**当前约束下不可行——冲突:${(s.unsat_core ?? []).join('、')}`)
|
|
173
316
|
for (const sg of s.suggestions ?? []) lines.push(`- 放宽「${sg.relax}」可解(约 ¥${sg.money_cny})`)
|
|
174
317
|
}
|
|
318
|
+
for (const e of s.work_window_exclusions ?? []) {
|
|
319
|
+
lines.push(`- 已排除 ${e.option}(工作窗口):${e.reason}`)
|
|
320
|
+
}
|
|
175
321
|
if (state.gates.length) {
|
|
176
322
|
lines.push('', '**待你决定(选择题)**')
|
|
177
323
|
for (const g of state.gates) {
|
|
@@ -188,7 +334,9 @@ export async function runTurn(
|
|
|
188
334
|
llm: LlmPort,
|
|
189
335
|
history: Turn[] = [],
|
|
190
336
|
solve: SolvePort | null = null,
|
|
337
|
+
now: Date = new Date(),
|
|
191
338
|
): Promise<{ reply: string; state: TripState }> {
|
|
339
|
+
const anchor = buildTimeAnchor(now)
|
|
192
340
|
const facts = await llm.extractFacts([...history, { role: 'user', text: userMsg }], state)
|
|
193
341
|
|
|
194
342
|
const conflicts: string[] = []
|
|
@@ -236,7 +384,10 @@ export async function runTurn(
|
|
|
236
384
|
} else if (ar.verdict === 'miss') {
|
|
237
385
|
parts.push(`**${poiProbe} → miss** (酒店-be Anything 一切正常但无候选)\n${ar.evidence}`)
|
|
238
386
|
} else {
|
|
239
|
-
|
|
387
|
+
// 用户面只给人话原因 + 来源 tag:evidence 里的上游原话是溯源契约(能力层测试锁),
|
|
388
|
+
// 其中的进程噪音(如 `(exit null)`)不该渲染进回复。
|
|
389
|
+
const sourceTag = ar.evidence.match(/^\[[^\]]+\]/)?.[0]
|
|
390
|
+
parts.push(`**${poiProbe} → 实时查询不可用**(${ar.error ?? 'hbcli 不可达'})——这条不阻塞本轮规划,只影响实时数据源,其余结论照常带来源标注${sourceTag ? `\n${sourceTag}` : ''}`)
|
|
240
391
|
}
|
|
241
392
|
}
|
|
242
393
|
|
|
@@ -244,7 +395,22 @@ export async function runTurn(
|
|
|
244
395
|
|
|
245
396
|
// 约束齐备(无阻塞问题)→ 翻译 spec → **校验闸 + 日期一致性闸** → 求解 → 渲染
|
|
246
397
|
if (blocking.length === 0 && solve) {
|
|
247
|
-
const
|
|
398
|
+
const extractedSpec = await llm.extractSpec([...history, { role: 'user', text: userMsg }], state)
|
|
399
|
+
const planning = latestPlanningWindow(history, userMsg, anchor)
|
|
400
|
+
const planningCheck = extractedSpec ? applyPlanningWindow(extractedSpec, planning, anchor) : null
|
|
401
|
+
if (planningCheck?.error) {
|
|
402
|
+
state.spec = undefined
|
|
403
|
+
state.solve = undefined
|
|
404
|
+
parts.push(`⚠️ ${planningCheck.error}`)
|
|
405
|
+
return { reply: parts.join('\n\n'), state }
|
|
406
|
+
}
|
|
407
|
+
if (planningCheck?.rejected.length) {
|
|
408
|
+
const rejected = planningCheck.rejected
|
|
409
|
+
.map(r => `${r.segmentId}/${r.optionId}(${r.date})`)
|
|
410
|
+
.join('、')
|
|
411
|
+
parts.push(`已排除规划窗口外的候选:${rejected}`)
|
|
412
|
+
}
|
|
413
|
+
const spec = planningCheck?.spec ?? extractedSpec
|
|
248
414
|
// 场景路由:erhai 候选标记 → 候选求解(洱海金标准的引擎判定)——纯 TS unify 路径
|
|
249
415
|
if (spec && (spec as unknown as { note?: string }).note === 'erhai-candidates') {
|
|
250
416
|
const { readFile } = await import('node:fs/promises')
|
|
@@ -268,11 +434,10 @@ export async function runTurn(
|
|
|
268
434
|
// D-10 切片 C:spec↔槽位日期一致性闸(ADR-10 翻译≠造数的执行点)。
|
|
269
435
|
// LLM 翻译的 spec 日期与代码层换算的槽位日期分歧时不求解、不猜,追问确认——
|
|
270
436
|
// 日期是求解关键输入,错日期 = 错判决。槽位缺失/unresolved 不参与(不造假阳性)。
|
|
271
|
-
const ext = await llm.extractSlots([...history, { role: 'user', text: userMsg }])
|
|
437
|
+
const ext = await llm.extractSlots([...history, { role: 'user', text: userMsg }], now)
|
|
272
438
|
if (ext) {
|
|
273
439
|
const { resolveSlots, specDateMismatches } = await import('./slot-spec.ts')
|
|
274
|
-
const
|
|
275
|
-
const mismatches = specDateMismatches(spec, resolveSlots(ext, buildTimeAnchor()))
|
|
440
|
+
const mismatches = specDateMismatches(spec, resolveSlots(ext, anchor))
|
|
276
441
|
if (mismatches.length > 0) {
|
|
277
442
|
const lines = mismatches.map(m => `- ${m.specAt}:翻译说 ${m.specDate},你的原话换算是 ${m.slotDate}(${m.field})`)
|
|
278
443
|
parts.push(`⚠️ 日期分歧,确认后我再算(以你为准):\n${lines.join('\n')}`)
|
|
@@ -292,42 +457,112 @@ export async function runTurn(
|
|
|
292
457
|
return { reply: parts.join('\n\n'), state }
|
|
293
458
|
}
|
|
294
459
|
|
|
295
|
-
// ---- 异步工单持久化(S5 编排半段):真正的「一小时后」必须跨进程存续 ----
|
|
296
|
-
//
|
|
297
|
-
//
|
|
460
|
+
// ---- 异步工单持久化(S5 编排半段 → ADR-15 durable 工单):真正的「一小时后」必须跨进程存续 ----
|
|
461
|
+
// 权威 = 账本 workflow_runs/workflow_steps(单事务;崩溃后 done 步骤不重执行);
|
|
462
|
+
// {id}.json / {id}.deliverable.md 降级为视图(AGENTS.md 清扫合同 + 人工检视),
|
|
463
|
+
// tmp+rename 原子写——此前裸 writeFile 的非原子缺口(RFC §1.1)就此关闭。
|
|
298
464
|
|
|
299
465
|
import { join } from 'node:path'
|
|
300
|
-
import { mkdir } from 'node:fs/promises'
|
|
301
|
-
import {
|
|
466
|
+
import { mkdir, rename, writeFile, readFile } from 'node:fs/promises'
|
|
467
|
+
import { ensureLedger, openLedgerIfExists, type StateLedger } from './state-ledger.ts'
|
|
302
468
|
|
|
303
|
-
|
|
469
|
+
async function asyncDir(stateRoot: string): Promise<string> {
|
|
470
|
+
const root = stateRoot === '.' ? process.cwd() : stateRoot
|
|
471
|
+
const dir = join(root, 'gotry-state', 'async')
|
|
472
|
+
await mkdir(dir, { recursive: true })
|
|
473
|
+
return dir
|
|
474
|
+
}
|
|
304
475
|
|
|
305
|
-
async function
|
|
306
|
-
|
|
307
|
-
|
|
476
|
+
async function atomicWrite(path: string, text: string): Promise<void> {
|
|
477
|
+
const tmp = `${path}.tmp`
|
|
478
|
+
await writeFile(tmp, text, 'utf-8')
|
|
479
|
+
await rename(tmp, path)
|
|
308
480
|
}
|
|
309
481
|
|
|
310
|
-
export async function persistAsyncTicket(ticket: AsyncTicket, state: TripState): Promise<string> {
|
|
311
|
-
const
|
|
312
|
-
|
|
482
|
+
export async function persistAsyncTicket(ticket: AsyncTicket, state: TripState, stateRoot = '.'): Promise<string> {
|
|
483
|
+
const ledger = ensureLedger(stateRoot)
|
|
484
|
+
ledger.createWorkflowRun({ id: ticket.id, goal: ticket.objective, ticket, state })
|
|
485
|
+
const dir = await asyncDir(stateRoot)
|
|
486
|
+
const p = join(dir, `${ticket.id}.json`)
|
|
487
|
+
await atomicWrite(p, JSON.stringify({ ticket, state }, null, 2))
|
|
313
488
|
return p
|
|
314
489
|
}
|
|
315
490
|
|
|
316
|
-
export async function loadAsyncTicket(ticketId: string): Promise<{ ticket: AsyncTicket; state: TripState } | null> {
|
|
491
|
+
export async function loadAsyncTicket(ticketId: string, stateRoot = '.'): Promise<{ ticket: AsyncTicket; state: TripState } | null> {
|
|
492
|
+
// 账本优先;无账本的 root 回退旧 json 文件(只读,兼容存量已交付工单)
|
|
493
|
+
const ledger = openLedgerIfExists(stateRoot)
|
|
494
|
+
const run = ledger?.getWorkflowRun(ticketId)
|
|
495
|
+
if (run) {
|
|
496
|
+
return {
|
|
497
|
+
ticket: JSON.parse(run.ticket_json) as AsyncTicket,
|
|
498
|
+
state: JSON.parse(run.state_json) as TripState,
|
|
499
|
+
}
|
|
500
|
+
}
|
|
317
501
|
try {
|
|
318
|
-
const p = await
|
|
502
|
+
const p = join(await asyncDir(stateRoot), `${ticketId}.json`)
|
|
319
503
|
return JSON.parse(await readFile(p, 'utf-8')) as { ticket: AsyncTicket; state: TripState }
|
|
320
504
|
} catch {
|
|
321
505
|
return null
|
|
322
506
|
}
|
|
323
507
|
}
|
|
324
508
|
|
|
325
|
-
export async function settleAsyncTicket(
|
|
326
|
-
|
|
327
|
-
|
|
509
|
+
export async function settleAsyncTicket(
|
|
510
|
+
ticketId: string,
|
|
511
|
+
reply: string,
|
|
512
|
+
stateRoot = '.',
|
|
513
|
+
outcome?: AsyncTerminalOutcome,
|
|
514
|
+
): Promise<string> {
|
|
515
|
+
const ledger = openLedgerIfExists(stateRoot)
|
|
516
|
+
const terminalOutcome = outcome ?? recoverAsyncTerminalOutcome(ledger, ticketId)
|
|
517
|
+
if (ledger && !terminalOutcome) {
|
|
518
|
+
throw new Error(`workflow ${ticketId} 缺少可恢复的 ${ASYNC_TERMINAL_SCHEMA},拒绝误结算`)
|
|
519
|
+
}
|
|
520
|
+
if (terminalOutcome?.status === 'failed') ledger?.failWorkflowRun(ticketId, reply, terminalOutcome)
|
|
521
|
+
else ledger?.settleWorkflowRun(ticketId, reply, terminalOutcome)
|
|
522
|
+
const dir = await asyncDir(stateRoot)
|
|
523
|
+
const p = join(dir, `${ticketId}.deliverable.md`)
|
|
524
|
+
await atomicWrite(p, reply)
|
|
328
525
|
return p
|
|
329
526
|
}
|
|
330
527
|
|
|
528
|
+
function recoverAsyncTerminalOutcome(ledger: StateLedger | null, ticketId: string): AsyncTerminalOutcome | undefined {
|
|
529
|
+
if (!ledger) return undefined
|
|
530
|
+
const run = ledger.getWorkflowRun(ticketId)
|
|
531
|
+
if (!run) return undefined
|
|
532
|
+
|
|
533
|
+
try {
|
|
534
|
+
const state = JSON.parse(run.state_json) as TripState
|
|
535
|
+
if (!state.spec) return terminalOutcomeFromState(ticketId, state)
|
|
536
|
+
|
|
537
|
+
const solveStep = ledger.getWorkflowStep(ticketId, 'solve')
|
|
538
|
+
if (solveStep?.status !== 'done' || !solveStep.result) return undefined
|
|
539
|
+
state.solve = JSON.parse(solveStep.result) as SolveResult
|
|
540
|
+
state.gates = state.gates.filter(g => !g.id.startsWith('async-'))
|
|
541
|
+
return terminalOutcomeFromState(ticketId, state)
|
|
542
|
+
} catch {
|
|
543
|
+
return undefined
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* 可日志化 solve 端口(ADR-15 步骤日志,intent-before-execute):
|
|
549
|
+
* 先记 intent 再执行,done 后结果落账本——任意进程恢复时 done 的步骤直接复用
|
|
550
|
+
* 结果不重执行(exactly-once:LLM/求解调用不重复花钱),intent 悬挂的重试。
|
|
551
|
+
*/
|
|
552
|
+
export function makeJournaledSolvePort(ledger: StateLedger, runId: string, solve: SolvePort, opts?: { onRealSolve?: () => void }): SolvePort {
|
|
553
|
+
return async spec => {
|
|
554
|
+
const step = ledger.getWorkflowStep(runId, 'solve')
|
|
555
|
+
if (step?.status === 'done' && step.result) {
|
|
556
|
+
return JSON.parse(step.result) as Awaited<ReturnType<SolvePort>>
|
|
557
|
+
}
|
|
558
|
+
ledger.markStepIntent(runId, 'solve')
|
|
559
|
+
opts?.onRealSolve?.()
|
|
560
|
+
const r = await solve(spec)
|
|
561
|
+
ledger.markStepDone(runId, 'solve', r)
|
|
562
|
+
return r
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
331
566
|
/**
|
|
332
567
|
* probePoi:从 user msg 探测"查 POI/酒店" 信号,返回关键词。
|
|
333
568
|
* 触发的不是关键词,是**结构**;关键词抓取有方向性——
|
|
@@ -340,6 +575,9 @@ export async function settleAsyncTicket(ticketId: string, reply: string): Promis
|
|
|
340
575
|
* 2026-08-28 巡检收紧:旧版 ≤24 直通把访谈答案整句当关键词,垃圾 hbcli 调用+证据噪音)。
|
|
341
576
|
* 不是意图(改求解器),只是 datasources 编排层的"提早调 anything"提示。
|
|
342
577
|
*/
|
|
578
|
+
/** 住宿名称段上界:路径 2 已要求含拉丁/数字且被标点截断,旧 24 字会把
|
|
579
|
+
* 「The Title East Wing Rawai」这类真实酒店名截残后回显给用户(2026-09-22)。 */
|
|
580
|
+
const POI_NAME_MAX = 48
|
|
343
581
|
export function probePoi(msg: string): string | null {
|
|
344
582
|
const trimmed = msg.trim()
|
|
345
583
|
if (!trimmed) return null
|
|
@@ -355,7 +593,7 @@ export function probePoi(msg: string): string | null {
|
|
|
355
593
|
if (/(酒店|民宿|客栈|饭店)/.test(trimmed)) {
|
|
356
594
|
const parts = trimmed.split(/(酒店|民宿|客栈|饭店)/)
|
|
357
595
|
const after = cutAtPunct((parts[2] ?? '').replace(/^[的::\s]+/, ''))
|
|
358
|
-
if (after.length >= 2 && /[A-Za-z0-9]/.test(after)) return after.slice(0,
|
|
596
|
+
if (after.length >= 2 && /[A-Za-z0-9]/.test(after)) return after.length > POI_NAME_MAX ? `${after.slice(0, POI_NAME_MAX - 1)}…` : after
|
|
359
597
|
const nounAt = trimmed.search(/(酒店|民宿|客栈|饭店)/)
|
|
360
598
|
const pre = trimmed.slice(Math.max(0, nounAt - 6), nounAt).match(/[一-龥]{2,6}$/)
|
|
361
599
|
if (pre && !/(机票|车票|和|做|订|的)$/.test(pre[0])) return pre[0]
|