@danceiny/gotry 0.0.1-rc.9 → 0.2.0-rc.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -219
- package/README.zh-CN.md +222 -0
- package/bin/gotry-backend.js +53 -0
- package/bin/gotry-booking-copilot.js +53 -0
- package/bin/gotry-bootstrap.js +1788 -0
- package/bin/gotry-inner.js +733 -83
- package/bin/gotry-process-liveness.d.ts +27 -0
- package/bin/gotry-process-liveness.js +97 -0
- package/bin/gotry-runtime-resolution.d.ts +27 -0
- package/bin/gotry-runtime-resolution.js +49 -0
- package/bin/gotry.js +1 -1
- package/client/client.js +203 -0
- package/cordis.gotry-patch.yml +80 -56
- package/data/airline-airports.json +39 -0
- package/data/stations-12306-verify.json +524 -0
- package/dist/capabilities/agent-reach-deep.js +45 -122
- package/dist/capabilities/agent-reach.js +101 -146
- package/dist/capabilities/anything.js +161 -94
- package/dist/capabilities/artifacts.js +370 -0
- package/dist/capabilities/channel-health.js +131 -0
- package/dist/capabilities/channel-registry.js +256 -0
- package/dist/capabilities/doctor.js +553 -0
- package/dist/capabilities/effect.js +394 -0
- package/dist/capabilities/external-event.js +149 -0
- package/dist/capabilities/fact-log.js +55 -0
- package/dist/capabilities/flyai-config.js +337 -0
- package/dist/capabilities/flyai.js +947 -110
- package/dist/capabilities/fx-contract.js +293 -0
- package/dist/capabilities/geo-atlas.js +246 -0
- package/dist/capabilities/ground-transfer.js +676 -0
- package/dist/capabilities/hbcli.js +260 -123
- package/dist/capabilities/hotelbyte-transaction.js +194 -0
- package/dist/capabilities/incident-log.js +131 -61
- package/dist/capabilities/itinerary-artifact.js +190 -0
- package/dist/capabilities/itinerary-deck-artifact.js +194 -0
- package/dist/capabilities/itinerary-deck-export.js +339 -0
- package/dist/capabilities/lavish-local.js +924 -0
- package/dist/capabilities/model-override.js +38 -0
- package/dist/capabilities/opensky.js +54 -43
- package/dist/capabilities/provider-detail-sanitize.js +64 -0
- package/dist/capabilities/resilience.js +191 -0
- package/dist/capabilities/route-provider-conformance.js +341 -0
- package/dist/capabilities/session/action-cache.js +50 -44
- package/dist/capabilities/session/adapters/ctrip-flight.js +120 -62
- package/dist/capabilities/session/adapters/ctrip-hotel.js +222 -0
- package/dist/capabilities/session/adapters/dida-portal.js +274 -0
- package/dist/capabilities/session/adapters/meituan-local.js +35 -54
- package/dist/capabilities/session/adapters/rail-12306.js +342 -0
- package/dist/capabilities/session/benchmark.js +189 -0
- package/dist/capabilities/session/bridge-ledger.js +224 -0
- package/dist/capabilities/session/extension-bridge.js +636 -0
- package/dist/capabilities/session/extension-channel.js +109 -0
- package/dist/capabilities/session/extension-distribution.js +230 -0
- package/dist/capabilities/session/extract.js +31 -21
- package/dist/capabilities/session/golden-score.js +114 -0
- package/dist/capabilities/session/health-watch.js +215 -0
- package/dist/capabilities/session/read-guard.js +75 -24
- package/dist/capabilities/session/static-flight-golden.js +150 -0
- package/dist/capabilities/session/transport.js +97 -83
- package/dist/capabilities/session/wizard.js +117 -0
- package/dist/capabilities/session-consent.js +178 -0
- package/dist/capabilities/session-login.js +170 -0
- package/dist/capabilities/session-search.js +635 -44
- package/dist/capabilities/spawn-bounded.js +203 -0
- package/dist/capabilities/sponsor-plugin.js +341 -0
- package/dist/capabilities/visa-policy.js +140 -0
- package/dist/capabilities/weather.js +243 -138
- package/dist/data/session-golden-20.json +25 -0
- package/dist/data/sf-golden-manifest.json +103 -0
- package/dist/data/sf-static-routes.json +91 -0
- package/dist/scripts/action-cache-tests.js +24 -59
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +242 -0
- package/dist/scripts/agent-planning-turn-deadline-tests.js +185 -0
- package/dist/scripts/agent-reach-deep-tests.js +30 -40
- package/dist/scripts/agent-reach-error-tests.js +354 -0
- package/dist/scripts/agent-reach-tests.js +26 -18
- package/dist/scripts/agent-reach-wrapper-tests.js +50 -50
- package/dist/scripts/anything-path-measure-tests.js +133 -0
- package/dist/scripts/anything-path-measure.js +150 -0
- package/dist/scripts/anything-tests.js +250 -60
- package/dist/scripts/artifact-client-contract-tests.js +220 -0
- package/dist/scripts/artifacts-capability-tests.js +727 -0
- package/dist/scripts/async-collect.js +50 -11
- package/dist/scripts/benchmark-environment-bridge-e2e.js +741 -0
- package/dist/scripts/benchmark-environment-bridge-tests.js +1452 -0
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +126 -0
- package/dist/scripts/booking-copilot-availability-policy-tests.js +265 -0
- package/dist/scripts/booking-copilot-bin-proof-tests.js +66 -0
- package/dist/scripts/booking-copilot-cleanup-proof-helper.js +76 -0
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +144 -0
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +424 -0
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +2839 -0
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +309 -0
- package/dist/scripts/booking-copilot-dsh-readiness-proof.js +238 -0
- package/dist/scripts/booking-copilot-dsh-warmer-proof.js +126 -0
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +156 -0
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +70 -0
- package/dist/scripts/booking-copilot-intent-proof-tests.js +422 -0
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +184 -0
- package/dist/scripts/booking-copilot-planner-live-probe.js +95 -0
- package/dist/scripts/booking-copilot-proof-runtime.js +42 -0
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +210 -0
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +2349 -0
- package/dist/scripts/booking-copilot-server-proof-tests.js +784 -0
- package/dist/scripts/booking-copilot-startup-proof-tests.js +94 -0
- package/dist/scripts/booking-executor-tests.js +80 -0
- package/dist/scripts/booking-recovery-chain-tests.js +182 -0
- package/dist/scripts/booking-saga-tests.js +125 -0
- package/dist/scripts/booking-surface-contract-proof-tests.js +212 -0
- package/dist/scripts/bootstrap-tests.js +1549 -0
- package/dist/scripts/bridge-events-tests.js +239 -0
- package/dist/scripts/bridge-ledger-tests.js +428 -0
- package/dist/scripts/build-changelog.js +238 -0
- package/dist/scripts/build-metrics-report.js +331 -0
- package/dist/scripts/changelog-tests.js +118 -0
- package/dist/scripts/channel-probe-tests.js +103 -0
- package/dist/scripts/channel-probe.js +169 -0
- package/dist/scripts/channel-registry-tests.js +208 -0
- package/dist/scripts/city-scenario-tier-tests.js +234 -0
- package/dist/scripts/companion-tests.js +25 -70
- package/dist/scripts/dida-cookie-probe.js +87 -0
- package/dist/scripts/dida-runner-stop-tests.js +311 -0
- package/dist/scripts/dida-sw-probe.js +114 -0
- package/dist/scripts/diff-test.js +17 -12
- package/dist/scripts/doctor-tests.js +600 -0
- package/dist/scripts/drill-multiuser-ledger-tests.js +1076 -0
- package/dist/scripts/drill-sdk-descendant-cleanup-tests.js +606 -0
- package/dist/scripts/drill-w2a-sensor-tests.js +788 -0
- package/dist/scripts/dsh-artifact-e2e.js +279 -0
- package/dist/scripts/dsh-artifact-web-e2e.js +675 -0
- package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
- package/dist/scripts/dsh-llm-tests.js +437 -0
- package/dist/scripts/dsh-runtime-closure-tests.js +182 -0
- package/dist/scripts/dsh-runtime-closure.js +162 -0
- package/dist/scripts/dsh-session-v3-migration-proof.js +109 -0
- package/dist/scripts/dsh-subprocess-local-proof.js +150 -0
- package/dist/scripts/dsh-target-closure-proof.js +178 -0
- package/dist/scripts/effect-tests.js +539 -0
- package/dist/scripts/engine-run.js +1 -3
- package/dist/scripts/engine-tests.js +16 -10
- package/dist/scripts/evaluation-cadence-tests.js +174 -0
- package/dist/scripts/evaluation-contract-tests.js +268 -0
- package/dist/scripts/extension-distribution-cli.js +39 -0
- package/dist/scripts/extension-distribution-tests.js +443 -0
- package/dist/scripts/extension-tests.js +988 -0
- package/dist/scripts/external-event-tests.js +350 -0
- package/dist/scripts/fact-gate-tests.js +869 -0
- package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
- package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
- package/dist/scripts/flight-malformed-tests.js +159 -0
- package/dist/scripts/flyai-setup-tests.js +228 -0
- package/dist/scripts/flyai-setup-tool-tests.js +54 -0
- package/dist/scripts/flyai-tests.js +692 -0
- package/dist/scripts/flyai-tool-registration-contract-tests.js +91 -0
- package/dist/scripts/fx-contract-tests.js +201 -0
- package/dist/scripts/g5-guard-tests.js +150 -0
- package/dist/scripts/g5-guard.js +193 -0
- package/dist/scripts/gen-lunar-anchors.js +106 -0
- package/dist/scripts/geo-atlas-tests.js +283 -0
- package/dist/scripts/gotry-backend-kernel-tests.js +206 -0
- package/dist/scripts/gotry-try-demo-tests.js +79 -0
- package/dist/scripts/gotry-try-demo.js +49 -0
- package/dist/scripts/ground-transfer-tests.js +956 -0
- package/dist/scripts/hbcli-e2e-tests.js +159 -0
- package/dist/scripts/hbcli-live-optin-tests.js +96 -0
- package/dist/scripts/hbcli-release-contract-tests.js +122 -0
- package/dist/scripts/hbcli-tests.js +121 -69
- package/dist/scripts/health-watch-cli.js +73 -0
- package/dist/scripts/hotel-date-gate-tests.js +496 -0
- package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
- package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
- package/dist/scripts/i18n-tests.js +41 -0
- package/dist/scripts/incident-tests.js +281 -87
- package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
- package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
- package/dist/scripts/issue-271-liveness-tests.js +569 -0
- package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
- package/dist/scripts/issue-289-model-retry-tests.js +441 -0
- package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
- package/dist/scripts/issue-338-home-city-e2e.js +577 -0
- package/dist/scripts/issue343-real-entry-e2e.js +160 -0
- package/dist/scripts/issue436-persisted-routing-e2e.js +247 -0
- package/dist/scripts/itinerary-artifact-tests.js +368 -0
- package/dist/scripts/itinerary-deck-artifact-tests.js +416 -0
- package/dist/scripts/itinerary-deck-export-tests.js +552 -0
- package/dist/scripts/itinerary-deck-tests.js +435 -0
- package/dist/scripts/itinerary-html-tests.js +426 -0
- package/dist/scripts/journey-tests.js +22 -27
- package/dist/scripts/kernel-manifest-evidence.js +109 -0
- package/dist/scripts/kernel-manifest-gate.js +126 -0
- package/dist/scripts/kernel-manifest-tests.js +212 -0
- package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
- package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
- package/dist/scripts/kernel-manifest.js +369 -0
- package/dist/scripts/lavish-local-tests.js +1226 -0
- package/dist/scripts/lavish-product-tools-tests.js +537 -0
- package/dist/scripts/ledger-repair-apply-tests.js +316 -0
- package/dist/scripts/ledger-repair-plan-tests.js +375 -0
- package/dist/scripts/ledger-tests.js +382 -246
- package/dist/scripts/ledger-workflow-crash.js +10 -20
- package/dist/scripts/m3-cohort-tests.js +714 -0
- package/dist/scripts/m3-cohort.js +310 -0
- package/dist/scripts/managed-dsh-cleanup-diagnostic-proof.js +184 -0
- package/dist/scripts/managed-dsh-close-failure-proof.js +172 -0
- package/dist/scripts/managed-dsh-run-port-proof.js +153 -0
- package/dist/scripts/managed-dsh-terminal-proof.js +252 -0
- package/dist/scripts/map-tools-vendor-package-proof.js +365 -0
- package/dist/scripts/memory-capture-tests.js +242 -61
- package/dist/scripts/memory-decay-tests.js +18 -52
- package/dist/scripts/memory-lifecycle-tests.js +657 -0
- package/dist/scripts/memory-lifecycle.js +196 -0
- package/dist/scripts/memory-metrics.js +24 -12
- package/dist/scripts/memory-value-report.js +631 -0
- package/dist/scripts/memory-value-tests.js +328 -0
- package/dist/scripts/metrics-report-tests.js +151 -0
- package/dist/scripts/model-override-e2e.js +137 -0
- package/dist/scripts/nightly-evidence-tests.js +203 -0
- package/dist/scripts/nightly-evidence.js +303 -0
- package/dist/scripts/nudge-digest.js +39 -28
- package/dist/scripts/observability-tests.js +381 -0
- package/dist/scripts/onboarding-tests.js +161 -0
- package/dist/scripts/opensky-check.js +18 -13
- package/dist/scripts/opensky-tests.js +28 -28
- package/dist/scripts/outcome-projection-tests.js +427 -0
- package/dist/scripts/persona-sim-tests.js +603 -0
- package/dist/scripts/persona-sim.js +1081 -0
- package/dist/scripts/persona-surface-guard-tests.js +49 -0
- package/dist/scripts/pnpm-dsh-closure-proof.js +17 -0
- package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
- package/dist/scripts/price-drift-tests.js +340 -0
- package/dist/scripts/price-drift-watch.js +447 -0
- package/dist/scripts/probe-poi-tests.js +53 -45
- package/dist/scripts/product-metrics.js +483 -0
- package/dist/scripts/publish-preverify.js +110 -0
- package/dist/scripts/realtime-pricing-tests.js +76 -0
- package/dist/scripts/recall-tests.js +513 -0
- package/dist/scripts/replay-async.js +22 -18
- package/dist/scripts/replay-real.js +18 -18
- package/dist/scripts/replay.js +54 -82
- package/dist/scripts/route-provider-conformance-tests.js +391 -0
- package/dist/scripts/session-attach-diagnose.js +20 -20
- package/dist/scripts/session-attach-poc.js +34 -37
- package/dist/scripts/session-benchmark.js +180 -0
- package/dist/scripts/session-dida-live-e2e.js +199 -0
- package/dist/scripts/session-extract-tests.js +14 -35
- package/dist/scripts/session-link-tests.js +478 -0
- package/dist/scripts/session-login.js +27 -46
- package/dist/scripts/session-tests.js +796 -96
- package/dist/scripts/session-zone-crash.js +49 -0
- package/dist/scripts/session-zone-ledger-tests.js +432 -0
- package/dist/scripts/session-zone-observation-tests.js +492 -0
- package/dist/scripts/session-zone-observe.js +135 -0
- package/dist/scripts/session-zone-wiring-tests.js +566 -0
- package/dist/scripts/session-zones-tests.js +374 -0
- package/dist/scripts/sf-live-benchmark.js +367 -0
- package/dist/scripts/sf-live-challenge-stop-tests.js +453 -0
- package/dist/scripts/sf-live-cli-tests.js +92 -0
- package/dist/scripts/sf-live-options.js +46 -0
- package/dist/scripts/sf-manifest-tests.js +30 -0
- package/dist/scripts/sf-manifest.js +33 -0
- package/dist/scripts/sf-soft-score-tests.js +67 -0
- package/dist/scripts/sf-summary-tests.js +324 -0
- package/dist/scripts/sf-summary.js +525 -0
- package/dist/scripts/share-tests.js +432 -0
- package/dist/scripts/skeleton-check.js +16 -14
- package/dist/scripts/skeleton-integration-test.js +28 -11
- package/dist/scripts/skills-contract-tests.js +51 -46
- package/dist/scripts/smoke-session-gate-tests.js +21 -0
- package/dist/scripts/smoke.js +690 -246
- package/dist/scripts/spawn-bounded-abort-tests.js +244 -0
- package/dist/scripts/sponsor-reuse-tests.js +262 -0
- package/dist/scripts/state-cli-tests.js +226 -101
- package/dist/scripts/state-cli.js +565 -198
- package/dist/scripts/state-repair-cli.js +66 -0
- package/dist/scripts/state-repair-tests.js +140 -0
- package/dist/scripts/static-golden-tests.js +165 -0
- package/dist/scripts/time-eval-tests.js +212 -192
- package/dist/scripts/travel-timeline-tests.js +29 -93
- package/dist/scripts/turn-handoff-collect-tests.js +169 -0
- package/dist/scripts/turn-handoff-collect.js +161 -0
- package/dist/scripts/turn-policy-tests.js +55 -0
- package/dist/scripts/typed-contract-canary.js +176 -0
- package/dist/scripts/tz-resolver-sanity.js +50 -0
- package/dist/scripts/tz-resolver-tests.js +584 -0
- package/dist/scripts/unified-tests.js +60 -20
- package/dist/scripts/visa-policy-tests.js +62 -0
- package/dist/scripts/weather-tests.js +276 -41
- package/dist/scripts/wish-channel-gate-tests.js +54 -0
- package/dist/scripts/write-gate-tests.js +522 -0
- package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
- package/dist/scripts/z3-lifecycle-tests.js +170 -0
- package/dist/scripts/z3-race-repeat-tests.js +39 -0
- package/dist/scripts/z3-race-tests.js +46 -0
- package/dist/src/artifact-gate.js +829 -0
- package/dist/src/backend/kernel.js +91 -0
- package/dist/src/backend/modules/booking-copilot.js +92 -0
- package/dist/src/backend/modules/booking-executor.js +124 -0
- package/dist/src/backend/modules/session-search.js +551 -0
- package/dist/src/benchmark-agent-conformance.js +578 -0
- package/dist/src/benchmark-environment-bridge.js +515 -0
- package/dist/src/benchmark-headless-child-diagnostics.js +166 -0
- package/dist/src/benchmark-tool-isolation.js +151 -0
- package/dist/src/bookable-facts.js +533 -0
- package/dist/src/booking-saga.js +97 -0
- package/dist/src/booking-surface/availability-policy.js +697 -0
- package/dist/src/booking-surface/booking-intent.js +43 -0
- package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
- package/dist/src/booking-surface/canonical-schema.js +116 -0
- package/dist/src/booking-surface/contracts.js +75 -0
- package/dist/src/booking-surface/dsh-planner.js +1854 -0
- package/dist/src/booking-surface/dsh-plugin.js +378 -0
- package/dist/src/booking-surface/dsh-worker.js +98 -0
- package/dist/src/booking-surface/error-codes.js +231 -0
- package/dist/src/booking-surface/index.js +9 -0
- package/dist/src/booking-surface/managed-dsh-cleanup-diagnostic.js +97 -0
- package/dist/src/booking-surface/managed-dsh-run-port.js +244 -0
- package/dist/src/booking-surface/profile.js +24 -0
- package/dist/src/booking-surface/recovery-chain.js +367 -0
- package/dist/src/booking-surface/runtime.js +1968 -0
- package/dist/src/booking-surface/server.js +674 -0
- package/dist/src/booking-surface/startup.js +168 -0
- package/dist/src/booking-surface/validation.js +261 -0
- package/dist/src/bridge.js +20 -13
- package/dist/src/city-scenario-tier.js +378 -0
- package/dist/src/companions.js +48 -74
- package/dist/src/contracts.js +21 -43
- package/dist/src/dsh-llm.js +171 -115
- package/dist/src/engine.js +276 -193
- package/dist/src/evaluation-cadence.js +97 -0
- package/dist/src/evaluation-contracts.js +283 -0
- package/dist/src/flight-pack-adapter.js +54 -0
- package/dist/src/flight-pack-contract.js +2 -0
- package/dist/src/flyai-setup-tool.js +70 -0
- package/dist/src/gotry-backend.js +125 -0
- package/dist/src/hotel-date-gate.js +118 -0
- package/dist/src/i18n.js +88 -0
- package/dist/src/index.js +2385 -881
- package/dist/src/itinerary-deck.js +139 -0
- package/dist/src/itinerary-doc-shared.js +661 -0
- package/dist/src/itinerary-html.js +131 -0
- package/dist/src/journey.js +155 -108
- package/dist/src/lavish-tools.js +400 -0
- package/dist/src/ledger-repair-apply.js +538 -0
- package/dist/src/ledger-repair-plan.js +463 -0
- package/dist/src/loop.js +331 -161
- package/dist/src/m3-cohort.js +1577 -0
- package/dist/src/memory-capture.js +180 -32
- package/dist/src/memory-decay.js +30 -14
- package/dist/src/memory-lifecycle.js +1310 -0
- package/dist/src/memory-utility.js +27 -40
- package/dist/src/mock-llm.js +36 -61
- package/dist/src/model.js +81 -36
- package/dist/src/outcome-projection.js +746 -0
- package/dist/src/realtime-pricing.js +103 -0
- package/dist/src/recall/card.js +74 -0
- package/dist/src/recall/evaluator.js +140 -0
- package/dist/src/recall/integration.js +17 -0
- package/dist/src/recall/tick.js +166 -0
- package/dist/src/session-link/plan-it-action.js +48 -0
- package/dist/src/session-link/session-link.js +248 -0
- package/dist/src/session-zone-ledger.js +312 -0
- package/dist/src/session-zone-observation.js +354 -0
- package/dist/src/session-zone-wiring.js +658 -0
- package/dist/src/session-zones.js +510 -0
- package/dist/src/share/adapters.js +51 -0
- package/dist/src/share/share-consent.js +76 -0
- package/dist/src/share/share-deck.js +58 -0
- package/dist/src/share/share-token.js +146 -0
- package/dist/src/slot-spec.js +84 -106
- package/dist/src/state-ledger.js +696 -524
- package/dist/src/state-repair.js +299 -0
- package/dist/src/subagent-job-id-guard.js +58 -0
- package/dist/src/time-anchor.js +231 -33
- package/dist/src/tool-packet.js +20 -8
- package/dist/src/travel-slots.js +58 -35
- package/dist/src/travel-timeline.js +44 -63
- package/dist/src/turn-deadline.js +287 -0
- package/dist/src/turn-policy.js +118 -0
- package/dist/src/tz-resolver.js +143 -0
- package/dist/src/unified.js +600 -360
- package/dist/src/wish-pool.js +42 -12
- package/dist/src/write-gate.js +495 -0
- package/dist/src/z3-shared.js +284 -0
- package/extension/README.md +21 -0
- package/extension/README.zh-CN.md +21 -0
- package/extension/background.js +463 -0
- package/extension/content-bridge.js +270 -0
- package/extension/content-main.js +111 -0
- package/extension/manifest.json +60 -0
- package/package.json +407 -14
- package/schemas/booking.intent.schema.json +193 -0
- package/schemas/booking.surface.schema.json +2622 -0
- package/ts/capabilities/agent-reach-deep.ts +114 -0
- package/ts/capabilities/agent-reach.ts +198 -0
- package/ts/capabilities/anything.ts +282 -0
- package/ts/capabilities/artifacts.ts +428 -0
- package/ts/capabilities/fact-log.ts +54 -0
- package/ts/capabilities/flyai-config.ts +388 -0
- package/ts/capabilities/flyai.ts +1274 -0
- package/ts/capabilities/hbcli.ts +253 -71
- package/ts/capabilities/incident-log.ts +88 -59
- package/ts/capabilities/itinerary-artifact.ts +214 -0
- package/ts/capabilities/itinerary-deck-artifact.ts +218 -0
- package/ts/capabilities/itinerary-deck-export.ts +378 -0
- package/ts/capabilities/opensky.ts +121 -0
- package/ts/capabilities/session/action-cache.ts +148 -0
- package/ts/capabilities/session/adapters/ctrip-flight.ts +181 -0
- package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
- package/ts/capabilities/session/adapters/dida-portal.ts +338 -0
- package/ts/capabilities/session/adapters/meituan-local.ts +70 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +392 -0
- package/ts/capabilities/session/benchmark.ts +276 -0
- package/ts/capabilities/session/bridge-ledger.ts +333 -0
- package/ts/capabilities/session/extension-bridge.ts +794 -0
- package/ts/capabilities/session/extension-channel.ts +144 -0
- package/ts/capabilities/session/extension-distribution.ts +266 -0
- package/ts/capabilities/session/extract.ts +54 -0
- package/ts/capabilities/session/golden-score.ts +139 -0
- package/ts/capabilities/session/health-watch.ts +264 -0
- package/ts/capabilities/session/read-guard.ts +141 -0
- package/ts/capabilities/session/static-flight-golden.ts +209 -0
- package/ts/capabilities/session/transport.ts +146 -0
- package/ts/capabilities/session/wizard.ts +140 -0
- package/ts/capabilities/session-consent.ts +211 -0
- package/ts/capabilities/session-login.ts +208 -0
- package/ts/capabilities/session-search.ts +893 -0
- package/ts/capabilities/weather.ts +330 -0
- package/ts/dsh-runtime/vendor/README.md +68 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +405 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +404 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +1584 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/icon.svg +5 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +595 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +58 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +131 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/geo.js +232 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +120 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +40 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/staticmap-route.js +115 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +267 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +90 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +23 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/geo.d.ts +61 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/staticmap-route.d.ts +31 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +75 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +68 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +97 -0
- package/ts/package.json +293 -3
- package/ts/scripts/async-collect.ts +91 -0
- package/ts/scripts/map-tools-vendor-package-proof.ts +404 -0
- package/ts/scripts/skeleton-check.ts +1 -1
- package/ts/scripts/skeleton-integration-test.ts +23 -1
- package/ts/scripts/state-cli.ts +586 -0
- package/ts/scripts/turn-handoff-collect.ts +178 -0
- package/ts/src/artifact-gate.ts +991 -0
- package/ts/src/benchmark-agent-conformance.ts +619 -0
- package/ts/src/benchmark-environment-bridge.ts +538 -0
- package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
- package/ts/src/benchmark-tool-isolation.ts +166 -0
- package/ts/src/bookable-facts.ts +837 -0
- package/ts/src/booking-saga.ts +133 -0
- package/ts/src/booking-surface/availability-policy.ts +522 -0
- package/ts/src/booking-surface/booking-intent.ts +86 -0
- package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
- package/ts/src/booking-surface/canonical-schema.js +116 -0
- package/ts/src/booking-surface/contracts.ts +475 -0
- package/ts/src/booking-surface/dsh-planner.ts +1949 -0
- package/ts/src/booking-surface/dsh-plugin.js +378 -0
- package/ts/src/booking-surface/dsh-worker.ts +110 -0
- package/ts/src/booking-surface/error-codes.ts +241 -0
- package/ts/src/booking-surface/index.ts +9 -0
- package/ts/src/booking-surface/managed-dsh-cleanup-diagnostic.ts +151 -0
- package/ts/src/booking-surface/managed-dsh-run-port.ts +273 -0
- package/ts/src/booking-surface/profile.ts +42 -0
- package/ts/src/booking-surface/recovery-chain.ts +409 -0
- package/ts/src/booking-surface/runtime.ts +1793 -0
- package/ts/src/booking-surface/server.ts +733 -0
- package/ts/src/booking-surface/startup.ts +201 -0
- package/ts/src/booking-surface/validation.ts +217 -0
- package/ts/src/companions.ts +113 -0
- package/ts/src/contracts.ts +17 -3
- package/ts/src/dsh-llm.ts +152 -17
- package/ts/src/flyai-setup-tool.ts +77 -0
- package/ts/src/index.ts +2123 -200
- package/ts/src/itinerary-deck.ts +189 -0
- package/ts/src/itinerary-doc-shared.ts +741 -0
- package/ts/src/itinerary-html.ts +171 -0
- package/ts/src/loop.ts +234 -26
- package/ts/src/memory-capture.ts +283 -0
- package/ts/src/memory-decay.ts +55 -0
- package/ts/src/memory-utility.ts +70 -0
- package/ts/src/mock-llm.ts +4 -8
- package/ts/src/model.ts +88 -2
- package/ts/src/recall/card.ts +106 -0
- package/ts/src/recall/evaluator.ts +170 -0
- package/ts/src/recall/integration.ts +35 -0
- package/ts/src/recall/tick.ts +213 -0
- package/ts/src/session-link/plan-it-action.ts +78 -0
- package/ts/src/session-link/session-link.ts +275 -0
- package/ts/src/share/adapters.ts +93 -0
- package/ts/src/share/share-consent.ts +105 -0
- package/ts/src/share/share-deck.ts +85 -0
- package/ts/src/share/share-token.ts +166 -0
- package/ts/src/slot-spec.ts +172 -0
- package/ts/src/state-ledger.ts +1166 -0
- package/ts/src/subagent-job-id-guard.ts +72 -0
- package/ts/src/time-anchor.ts +338 -0
- package/ts/src/tool-packet.ts +32 -0
- package/ts/src/travel-slots.ts +219 -0
- package/ts/src/travel-timeline.ts +83 -0
- package/ts/src/turn-deadline.ts +366 -0
- package/ts/src/turn-policy.ts +154 -0
- package/ts/src/unified.ts +444 -105
- package/ts/src/wish-pool.ts +79 -0
- package/dist/scripts/session-attach-wait.js +0 -41
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 效应解译器 effect_interpreter.v1(issue #16 采纳,ADR-18)。
|
|
3
|
+
*
|
|
4
|
+
* 「效应描述 + 解译器」:业务/编排层对基础设施的依赖收敛为一个**纯数据的效应值**
|
|
5
|
+
* (effect 名 + params),不存在对渠道模块的直接 import——agent 不调用 Ctrip.Query(),
|
|
6
|
+
* 它描述 `SEARCH_FLIGHT`;把意图换算成真实副作用的是可替换的解译器:
|
|
7
|
+
*
|
|
8
|
+
* - 生产解译器 makeProductionInterpreter:查注册表 → 韧性横切(指数退避重试/
|
|
9
|
+
* 断路器,per-效应策略表)→ 渠道 handler(现能力层函数,零改写);
|
|
10
|
+
* - mock 解译器 makeMockInterpreter:夹具回放(CI/本地无网确定性);
|
|
11
|
+
* - 浏览器解译 = SESSION_* 效应(用户本人登录态的扩展桥车道,2026-08-29 起默认传输:
|
|
12
|
+
* MV3 GoTry Session Bridge + 本地桥长轮询,零系统弹窗;ReadGuard+授权闸不变,
|
|
13
|
+
* 非 CUA 视觉点击——本仓已按零 Python 依赖与 a11y/DOM 优先判死后者,
|
|
14
|
+
* docs/design/effect-interpreter.md §4)。
|
|
15
|
+
*
|
|
16
|
+
* 解译产物:`{ result, trace }`——result 是渠道自有 observation **原样透传**
|
|
17
|
+
* (ADR-13 平铺 envelope 在工具层不受扰);trace 是解译层横切证据
|
|
18
|
+
* {attempts, backoffMs, breaker, declined, evidence},拼进工具层 summary/evidence。
|
|
19
|
+
* unknown-effect / 断路拒绝时 result=null + declinedObservation() 给出平铺失败面。
|
|
20
|
+
*
|
|
21
|
+
* 多渠道比价口径:解译器**不做**渠道间自动路由/降级排序——OTA 工具面平铺是
|
|
22
|
+
* founder 判定(persona 19),比价发生在 agent 层(多工具并调+证据链逐源标注);
|
|
23
|
+
* 本层只让「每条通道」更可替换、可熔断、可 mock。
|
|
24
|
+
*/
|
|
25
|
+
import { CircuitBreaker, withRetry, } from './resilience.js';
|
|
26
|
+
import { flyaiSearch } from './flyai.js';
|
|
27
|
+
import { checkAvail, hotelRates, searchHotels } from './hbcli.js';
|
|
28
|
+
import { sessionFlightSearch, sessionHotelSearch, sessionTrainSearch, sessionDidaSearch } from './session-search.js';
|
|
29
|
+
import { geocodePlace, getClimate, getForecast } from './weather.js';
|
|
30
|
+
import { verifyFlight } from './opensky.js';
|
|
31
|
+
import { anythingSearch } from './anything.js';
|
|
32
|
+
import { continentListUrl, countryPageUrl, extractVisaSection, fetchVisaPolicyPage, listCountryPaths, policyFactsFromMfa } from './visa-policy.js';
|
|
33
|
+
import { readUrl, reach, reachStatus } from './agent-reach.js';
|
|
34
|
+
import { githubSearch, videoSubtitle } from './agent-reach-deep.js';
|
|
35
|
+
import { sessionLogin } from './session-login.js';
|
|
36
|
+
/** C 档中国领事服务网抓取编排:country 直抓 / continent 列表遍历(≤limit,默认 10;抓取纪律=礼貌间隔,不并发) */
|
|
37
|
+
async function fetchVisaPolicyEntries(p) {
|
|
38
|
+
const started = Date.now();
|
|
39
|
+
const fetchedAt = new Date().toISOString();
|
|
40
|
+
const countries = [];
|
|
41
|
+
const facts = [];
|
|
42
|
+
const targets = [];
|
|
43
|
+
if (p.country) {
|
|
44
|
+
targets.push({ continent: p.continent ?? 'yz_645708', country: p.country });
|
|
45
|
+
}
|
|
46
|
+
else if (p.continent) {
|
|
47
|
+
const listUrl = continentListUrl(p.continent);
|
|
48
|
+
if (!listUrl)
|
|
49
|
+
return { ok: false, via: 'visa-policy-error', evidence: `[visa-policy@${fetchedAt}] 非法洲路径`, latencyMs: 0, facts: [], countries: [], error: 'invalid_continent' };
|
|
50
|
+
const page = await fetchVisaPolicyPage(listUrl, p.timeoutMs);
|
|
51
|
+
if (!page.ok || !page.html)
|
|
52
|
+
return { ok: false, via: 'visa-policy-error', evidence: `[visa-policy@${fetchedAt}] 洲列表抓取失败: ${page.error ?? '?'}`, latencyMs: 0, facts: [], countries: [], error: page.error };
|
|
53
|
+
for (const c of listCountryPaths(page.html, p.continent).slice(0, Math.max(1, Math.min(10, p.limit ?? 10))))
|
|
54
|
+
targets.push({ continent: p.continent, country: c });
|
|
55
|
+
}
|
|
56
|
+
for (const t of targets) {
|
|
57
|
+
const url = countryPageUrl(t.continent, t.country);
|
|
58
|
+
if (!url)
|
|
59
|
+
continue;
|
|
60
|
+
const page = await fetchVisaPolicyPage(url, p.timeoutMs);
|
|
61
|
+
countries.push(t.country);
|
|
62
|
+
if (!page.ok || !page.html)
|
|
63
|
+
continue;
|
|
64
|
+
const section = extractVisaSection(page.html);
|
|
65
|
+
if (!section)
|
|
66
|
+
continue;
|
|
67
|
+
facts.push(...policyFactsFromMfa({ countryLabel: t.country, countryPath: t.country, section, fetchedAt }));
|
|
68
|
+
}
|
|
69
|
+
const evidence = facts.length > 0
|
|
70
|
+
? `[visa-policy:cs-mfa@${fetchedAt}] ${facts.length} 条政策事实(抓取 ${countries.length} 国)`
|
|
71
|
+
: `[visa-policy@${fetchedAt}] 0 条(页面缺「签证入境」章节或抓取失败;不落负事实)`;
|
|
72
|
+
return { ok: true, via: 'cs-mfa', evidence, latencyMs: Date.now() - started, facts, countries };
|
|
73
|
+
}
|
|
74
|
+
/** 默认渠道实现(生产解译器的 dispatch 目标;全部满足「永不抛错」能力层契约) */
|
|
75
|
+
const DEFAULT_HANDLERS = {
|
|
76
|
+
/** 飞猪官方只读通道(机/火/酒店;spawn CLI) */
|
|
77
|
+
FLYAI_SEARCH: (p) => flyaiSearch(p),
|
|
78
|
+
/** hotelbyte 桥(实时 hbcli → 静态包降级;spawn CLI) */
|
|
79
|
+
HBCLI_HOTEL_SEARCH: (p) => searchHotels({ destination: p.destination, checkIn: p.checkIn, checkOut: p.checkOut, adults: p.adults }, { hbcliBin: p.hbcliBin, timeoutMs: p.timeoutMs, fallbackPath: p.fallbackPath, signal: p.signal }),
|
|
80
|
+
/** hotelbyte 桥·房型报价(M0 预订链;spawn CLI;价格面无静态降级 fail-closed) */
|
|
81
|
+
HBCLI_HOTEL_RATES: (p) => hotelRates({
|
|
82
|
+
hotelId: p.hotelId, checkIn: p.checkIn, checkOut: p.checkOut, adults: p.adults,
|
|
83
|
+
countryCode: p.countryCode, nationalityCode: p.nationalityCode, residencyCode: p.residencyCode,
|
|
84
|
+
}, { hbcliBin: p.hbcliBin, timeoutMs: p.timeoutMs }),
|
|
85
|
+
/** hotelbyte 桥·下单前实时验价(M0 预订链;spawn CLI;同价格面红线,不可用即诚实失败) */
|
|
86
|
+
HBCLI_CHECK_AVAIL: (p) => checkAvail({ ratePkgId: p.ratePkgId }, { hbcliBin: p.hbcliBin, timeoutMs: p.timeoutMs }),
|
|
87
|
+
/** 用户本人登录态会话检索(浏览器解译通道:扩展桥默认 + cdp 显式后备;ReadGuard+节律闸) */
|
|
88
|
+
SESSION_FLIGHT_SEARCH: (p) => sessionFlightSearch(p),
|
|
89
|
+
/** 用户本人登录态会话酒店检索(2026-09-03 实装;同浏览器解译通道,风控红线同款) */
|
|
90
|
+
SESSION_HOTEL_SEARCH: (p) => sessionHotelSearch(p),
|
|
91
|
+
/** 12306 余票会话检索(2026-09-03 实装;公开查询面,无登录闸;同浏览器解译通道) */
|
|
92
|
+
SESSION_TRAIN_SEARCH: (p) => sessionTrainSearch(p),
|
|
93
|
+
/** Dida 供应商门户实时价会话检索(2026-09-09 实装;hotel-be portal integration 迁移线) */
|
|
94
|
+
SESSION_DIDA_SEARCH: (p) => sessionDidaSearch(p),
|
|
95
|
+
/** 地名 → 坐标(open-meteo → nominatim 双源,免费) */
|
|
96
|
+
WEATHER_GEOCODE: (p) => geocodePlace(p.name, { count: p.count, timeoutMs: p.timeoutMs }),
|
|
97
|
+
/** 预报(≤16 天,免费无 key) */
|
|
98
|
+
WEATHER_FORECAST: (p) => getForecast({ latitude: p.latitude, longitude: p.longitude }, { days: p.days, timeoutMs: p.timeoutMs }),
|
|
99
|
+
/** 历史气候(季节性基线,免费无 key) */
|
|
100
|
+
WEATHER_CLIMATE: (p) => getClimate({ latitude: p.latitude, longitude: p.longitude }, p.month, { timeoutMs: p.timeoutMs }),
|
|
101
|
+
/** OpenSky ADS-B 实时印证(免费匿名,~400 credits/天) */
|
|
102
|
+
OPENSKY_FLIGHT_VERIFY: (p) => verifyFlight(p),
|
|
103
|
+
/** hotel-be Anything 目的地/酒店候选(hbcli CLI;gotry_anything_search 同源,D-23 收编) */
|
|
104
|
+
ANYTHING_SEARCH: (p) => anythingSearch(p),
|
|
105
|
+
/** 网页读取兜底(r.jina.ai 免费公共源;gotry_web_search 同源,D-23 收编) */
|
|
106
|
+
WEB_READ: (p) => readUrl({ url: p.url, timeoutMs: p.timeoutMs }),
|
|
107
|
+
/** GitHub 仓库搜索(gh CLI;agent-reach-deep 执行面,gotry_github_search 同源,D-23 收编) */
|
|
108
|
+
GITHUB_SEARCH: (p) => githubSearch(p),
|
|
109
|
+
/** 视频字幕拉取(yt-dlp;agent-reach-deep 执行面,gotry_video_subtitle 同源,D-23 收编) */
|
|
110
|
+
VIDEO_SUBTITLE: (p) => videoSubtitle(p),
|
|
111
|
+
/** agent-reach 反射桥(status=上游 doctor;reach=渠道×方法透传,gotry_agent_reach 同源,D-23 收编) */
|
|
112
|
+
AGENT_REACH: (p) => (p.action === 'status' || (!p.action && !p.channel))
|
|
113
|
+
? reachStatus(p.timeoutMs)
|
|
114
|
+
: reach({ channel: p.channel ?? '', method: p.method ?? '', args: p.args, timeoutMs: p.timeoutMs }),
|
|
115
|
+
/** 账号会话登录引导(浏览器通道;票据名只读 0 值过手,gotry_session_login 同源,D-23 收编) */
|
|
116
|
+
SESSION_LOGIN: (p) => sessionLogin({ waitMs: typeof p.waitSeconds === 'number' ? p.waitSeconds * 1000 : undefined }),
|
|
117
|
+
/** 政策事实生产端 v1(issue #141,D-26):C 档中国领事服务网国家指南树(礼貌抓取:永不重试+断路器护站) */
|
|
118
|
+
VISA_POLICY_FETCH: (p) => fetchVisaPolicyEntries(p),
|
|
119
|
+
};
|
|
120
|
+
const defaultIsFailure = (r) => r?.ok === false;
|
|
121
|
+
const API_RETRY = { maxAttempts: 2, baseDelayMs: 400, maxDelayMs: 1_600 };
|
|
122
|
+
const API_BREAKER = { failureThreshold: 3, openMs: 30_000 };
|
|
123
|
+
/**
|
|
124
|
+
* HBCLI timeout 专用瞬时判定(2026-09-02 迪拜 session 实况:hotel-list 首调
|
|
125
|
+
* 30s 超时,LLM 立即手动重试即成功——上游冷启动建后端 session 可超 30s,而
|
|
126
|
+
* 「候选路径是切换不是重试」的契约只针对 ENOENT 类 spawn 失败,不覆盖超时)。
|
|
127
|
+
* 只有 timeout 类失败值得重试一次;ENOENT/退码类上游明确说「不」的失败永不。
|
|
128
|
+
*/
|
|
129
|
+
const hbcliTimeout = (r) => {
|
|
130
|
+
const msg = String((r ?? {}).error ?? '');
|
|
131
|
+
return msg.length > 0 && /timeout/i.test(msg);
|
|
132
|
+
};
|
|
133
|
+
const HBCLI_RETRY = { maxAttempts: 2, baseDelayMs: 300, maxDelayMs: 1_000 };
|
|
134
|
+
/**
|
|
135
|
+
* 渠道韧性策略表(权威面;docs/design/effect-interpreter.md §3 同表逐行有依据):
|
|
136
|
+
* - FLYAI:瞬时代码级错误重试 1 次;连续 3 次 error(含 Sentinel)熔断 60s 保护配额;
|
|
137
|
+
* 试用额度达限(429)归 needs-setup,永不重试;
|
|
138
|
+
* - HBCLI:仅 timeout 类失败重试 1 次(冷启动建后端 session 可超时,重试即恢复);
|
|
139
|
+
* ENOENT/退码类永不重试(上游契约「候选路径是切换不是重试」),熔断同上;
|
|
140
|
+
* RATES/CHECK_AVAIL 同族,且价格面无静态降级(fail-closed,不估算房价——
|
|
141
|
+
* 与 bookable-facts 证据分级同口径,live_inventory 才可进确认卡);
|
|
142
|
+
* - SESSION:永不重试、不熔断——风控/挑战是「上游说不」,重试即红线;
|
|
143
|
+
* 节律闸(≥30s)在渠道内(session-search §3.4),不在本层重复。
|
|
144
|
+
* - WEATHER/OPENSKY:免费源,瞬时网络抖动可重试,熔断防免费配额空转。
|
|
145
|
+
* - ANYTHING:同 HBCLI 族(timeout 类瞬时重试 1 次;ENOENT/退码类永不);
|
|
146
|
+
* - WEB_READ:r.jina.ai 免费公共源,瞬时抖动可重试,熔断防公共配额空转;
|
|
147
|
+
* - GITHUB/VIDEO(gh/yt-dlp):verdict=timeout 瞬时重试 1 次;not-installed=配置态不重试;
|
|
148
|
+
* - AGENT_REACH:反射桥透传(wrapper 不是 router)——永不重试不熔断,上游超时自管;
|
|
149
|
+
* - SESSION_LOGIN:同 SESSION 浏览器族(风控红线)。
|
|
150
|
+
* (D-23 收尾,issue #115:六渠道入表,没有策略表行就没有效应。)
|
|
151
|
+
*/
|
|
152
|
+
const SPECS = {
|
|
153
|
+
FLYAI_SEARCH: {
|
|
154
|
+
channel: 'cli',
|
|
155
|
+
retry: { maxAttempts: 2, baseDelayMs: 500, maxDelayMs: 2_000 },
|
|
156
|
+
breaker: { failureThreshold: 3, openMs: 60_000 },
|
|
157
|
+
isRetryable: (r) => {
|
|
158
|
+
const result = r;
|
|
159
|
+
// Only explicit transient classification permits retry; terminal failures stay final.
|
|
160
|
+
return result?.retryable === true;
|
|
161
|
+
},
|
|
162
|
+
isFailure: defaultIsFailure,
|
|
163
|
+
},
|
|
164
|
+
HBCLI_HOTEL_SEARCH: {
|
|
165
|
+
channel: 'cli',
|
|
166
|
+
retry: HBCLI_RETRY,
|
|
167
|
+
// spec 级 isRetryable 是解译器组装 policy 的权威源(FLYAI 同款):仅 timeout 类
|
|
168
|
+
// 瞬时(冷启动建后端 session);ENOENT/退码类永不(「切换不是重试」契约不变)
|
|
169
|
+
isRetryable: (r) => hbcliTimeout(r),
|
|
170
|
+
breaker: { failureThreshold: 3, openMs: 60_000 },
|
|
171
|
+
isFailure: r => r.via === 'hbcli-error',
|
|
172
|
+
},
|
|
173
|
+
HBCLI_HOTEL_RATES: {
|
|
174
|
+
channel: 'cli',
|
|
175
|
+
retry: HBCLI_RETRY,
|
|
176
|
+
isRetryable: (r) => hbcliTimeout(r),
|
|
177
|
+
breaker: { failureThreshold: 3, openMs: 60_000 },
|
|
178
|
+
isFailure: r => r.via === 'hbcli-error',
|
|
179
|
+
},
|
|
180
|
+
HBCLI_CHECK_AVAIL: {
|
|
181
|
+
channel: 'cli',
|
|
182
|
+
retry: HBCLI_RETRY,
|
|
183
|
+
isRetryable: (r) => hbcliTimeout(r),
|
|
184
|
+
breaker: { failureThreshold: 3, openMs: 60_000 },
|
|
185
|
+
isFailure: r => r.via === 'hbcli-error',
|
|
186
|
+
},
|
|
187
|
+
SESSION_FLIGHT_SEARCH: {
|
|
188
|
+
channel: 'browser',
|
|
189
|
+
retry: null,
|
|
190
|
+
breaker: null,
|
|
191
|
+
isFailure: defaultIsFailure,
|
|
192
|
+
},
|
|
193
|
+
SESSION_HOTEL_SEARCH: {
|
|
194
|
+
channel: 'browser',
|
|
195
|
+
retry: null,
|
|
196
|
+
breaker: null,
|
|
197
|
+
isFailure: defaultIsFailure,
|
|
198
|
+
},
|
|
199
|
+
SESSION_TRAIN_SEARCH: {
|
|
200
|
+
channel: 'browser',
|
|
201
|
+
retry: null,
|
|
202
|
+
breaker: null,
|
|
203
|
+
isFailure: defaultIsFailure,
|
|
204
|
+
},
|
|
205
|
+
SESSION_DIDA_SEARCH: {
|
|
206
|
+
channel: 'browser',
|
|
207
|
+
retry: null,
|
|
208
|
+
breaker: null,
|
|
209
|
+
isFailure: defaultIsFailure,
|
|
210
|
+
},
|
|
211
|
+
WEATHER_GEOCODE: { channel: 'api', retry: API_RETRY, breaker: API_BREAKER, isFailure: defaultIsFailure },
|
|
212
|
+
WEATHER_FORECAST: { channel: 'api', retry: API_RETRY, breaker: API_BREAKER, isFailure: defaultIsFailure },
|
|
213
|
+
WEATHER_CLIMATE: { channel: 'api', retry: API_RETRY, breaker: API_BREAKER, isFailure: defaultIsFailure },
|
|
214
|
+
OPENSKY_FLIGHT_VERIFY: {
|
|
215
|
+
channel: 'api',
|
|
216
|
+
retry: API_RETRY,
|
|
217
|
+
breaker: API_BREAKER,
|
|
218
|
+
isRetryable: (_r, e) => e != null || (_r ?? {}).verdict === 'unavailable',
|
|
219
|
+
isFailure: r => r.via === 'opensky-error',
|
|
220
|
+
},
|
|
221
|
+
// --- D-23 收尾(issue #115):六渠道入注册表——没有策略表行就没有效应 ---
|
|
222
|
+
// ANYTHING:同 HBCLI 族(hbcli CLI):仅 timeout 类瞬时(冷启动建后端 session)重试 1 次;
|
|
223
|
+
// ENOENT/退码类永不(「切换不是重试」契约);熔断防上游空转
|
|
224
|
+
ANYTHING_SEARCH: {
|
|
225
|
+
channel: 'cli',
|
|
226
|
+
retry: HBCLI_RETRY,
|
|
227
|
+
isRetryable: (r) => hbcliTimeout(r),
|
|
228
|
+
breaker: { failureThreshold: 3, openMs: 60_000 },
|
|
229
|
+
isFailure: r => r.via === 'hbcli-anything-error',
|
|
230
|
+
},
|
|
231
|
+
// WEB_READ:r.jina.ai 免费公共源(同 WEATHER/OPENSKY 族):瞬时网络抖动可重试,
|
|
232
|
+
// 熔断防公共配额空转;非法 URL 是用户输入错(isFailure 不含,不重试不熔断)
|
|
233
|
+
WEB_READ: {
|
|
234
|
+
channel: 'api',
|
|
235
|
+
retry: API_RETRY,
|
|
236
|
+
breaker: API_BREAKER,
|
|
237
|
+
isFailure: r => r.via === 'r.jina.ai-error',
|
|
238
|
+
},
|
|
239
|
+
// GITHUB/VIDEO(gh/yt-dlp CLI):verdict=timeout 瞬时重试 1 次(进程冷启动超时同 HBCLI
|
|
240
|
+
// 2026-09-02 实况);not-installed 是配置态(needs-setup 族,重试无意义),error 是上游说不;
|
|
241
|
+
// 两者都计入熔断(防坏环境空转)
|
|
242
|
+
GITHUB_SEARCH: {
|
|
243
|
+
channel: 'cli',
|
|
244
|
+
retry: HBCLI_RETRY,
|
|
245
|
+
isRetryable: (r) => r.verdict === 'timeout',
|
|
246
|
+
breaker: { failureThreshold: 3, openMs: 60_000 },
|
|
247
|
+
isFailure: r => { const v = r.verdict; return v === 'error' || v === 'timeout'; },
|
|
248
|
+
},
|
|
249
|
+
VIDEO_SUBTITLE: {
|
|
250
|
+
channel: 'cli',
|
|
251
|
+
retry: HBCLI_RETRY,
|
|
252
|
+
isRetryable: (r) => r.verdict === 'timeout',
|
|
253
|
+
breaker: { failureThreshold: 3, openMs: 60_000 },
|
|
254
|
+
isFailure: r => { const v = r.verdict; return v === 'error' || v === 'timeout'; },
|
|
255
|
+
},
|
|
256
|
+
// AGENT_REACH:反射桥透传(wrapper 不是 router,D-4a')——上游超时自管,重试会放大
|
|
257
|
+
// 上游配额消耗,永不重试不熔断;needs-setup/not-installed 是「上游说不」同 SESSION 族
|
|
258
|
+
AGENT_REACH: {
|
|
259
|
+
channel: 'cli',
|
|
260
|
+
retry: null,
|
|
261
|
+
breaker: null,
|
|
262
|
+
isFailure: r => r.verdict === 'error',
|
|
263
|
+
},
|
|
264
|
+
// SESSION_LOGIN:同 SESSION_* 浏览器族——风控/挑战红线,永不重试不熔断;
|
|
265
|
+
// pending 是「等用户」不是失败
|
|
266
|
+
SESSION_LOGIN: {
|
|
267
|
+
channel: 'browser',
|
|
268
|
+
retry: null,
|
|
269
|
+
breaker: null,
|
|
270
|
+
isFailure: defaultIsFailure,
|
|
271
|
+
},
|
|
272
|
+
// VISA_POLICY(issue #141,D-26 v1):C 档中国领事服务网——政府权威源礼貌抓取:
|
|
273
|
+
// 永不重试(抓取纪律)+ 断路器防 hammering 政府站点;页面缺章节=无结论不落负事实
|
|
274
|
+
VISA_POLICY_FETCH: {
|
|
275
|
+
channel: 'api',
|
|
276
|
+
retry: null,
|
|
277
|
+
breaker: { failureThreshold: 2, openMs: 300_000 },
|
|
278
|
+
isFailure: defaultIsFailure,
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
/** 断路/未注册时的平铺失败观察(ADR-13 ToolFailure 兼容形态,工具层可直接返回) */
|
|
282
|
+
export function declinedObservation(effect, trace) {
|
|
283
|
+
return {
|
|
284
|
+
ok: false,
|
|
285
|
+
verdict: 'error',
|
|
286
|
+
summary: trace.declined === 'aborted'
|
|
287
|
+
? `${effect} 检索已取消,未产生新的查询结果。`
|
|
288
|
+
: trace.declined === 'circuit-open'
|
|
289
|
+
? `${effect} 通道被断路器开启保护(连续失败达到阈值,冷却中)——不要立即重试,换其他工具或稍后再试;原因见 trace 证据链`
|
|
290
|
+
: `${effect} 未登记效应(effect_interpreter.v1 注册表外,生产解译器拒绝)`,
|
|
291
|
+
evidence: trace.evidence.join(';'),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
const defaultBreakers = new Map();
|
|
295
|
+
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
296
|
+
/** 测试隔离:清空产品单例断路器(断路状态是进程内瞬态,同 rate limiter 先例) */
|
|
297
|
+
export function __resetEffectBreakersForTest() {
|
|
298
|
+
defaultBreakers.clear();
|
|
299
|
+
}
|
|
300
|
+
export function makeProductionInterpreter(opts = {}) {
|
|
301
|
+
const breakers = opts.breakers ?? defaultBreakers;
|
|
302
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
303
|
+
return async (fx) => {
|
|
304
|
+
const ts = new Date().toISOString();
|
|
305
|
+
const name = fx.effect;
|
|
306
|
+
const spec = SPECS[name];
|
|
307
|
+
const handler = opts.handlers?.[fx.effect] ?? DEFAULT_HANDLERS[name];
|
|
308
|
+
if (!spec || !handler) {
|
|
309
|
+
return {
|
|
310
|
+
result: null,
|
|
311
|
+
trace: { effect: fx.effect, channel: 'api', attempts: 0, backoffMs: 0, breaker: 'off', declined: 'unknown-effect', evidence: [`[效应:${fx.effect}@${ts}] unknown effect`] },
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
const br = (() => {
|
|
315
|
+
if (!spec.breaker)
|
|
316
|
+
return null;
|
|
317
|
+
const existing = breakers.get(fx.effect);
|
|
318
|
+
if (existing)
|
|
319
|
+
return existing;
|
|
320
|
+
const created = new CircuitBreaker({ ...spec.breaker, ...(opts.now ? { now: opts.now } : {}) });
|
|
321
|
+
breakers.set(fx.effect, created);
|
|
322
|
+
return created;
|
|
323
|
+
})();
|
|
324
|
+
const gate = br?.canAttempt() ?? { allowed: true, state: 'closed', token: undefined };
|
|
325
|
+
if (!gate.allowed) {
|
|
326
|
+
const trace = { effect: fx.effect, channel: spec.channel, attempts: 0, backoffMs: 0, breaker: gate.state, declined: 'circuit-open', evidence: [`[效应:${fx.effect}@${ts}] breaker=${gate.state} 拒绝(冷却中,不重试)`] };
|
|
327
|
+
return { result: null, trace };
|
|
328
|
+
}
|
|
329
|
+
const policy = spec.retry
|
|
330
|
+
? { maxAttempts: spec.retry.maxAttempts, baseDelayMs: spec.retry.baseDelayMs, maxDelayMs: spec.retry.maxDelayMs, isRetryable: spec.isRetryable, sleep }
|
|
331
|
+
: { maxAttempts: 1, baseDelayMs: 0, maxDelayMs: 0, sleep };
|
|
332
|
+
// 宿主取消(若该效应 params 带 signal)贯穿 backoff 等待与下一次 dispatch
|
|
333
|
+
const hostSignal = fx.params?.signal;
|
|
334
|
+
if (hostSignal)
|
|
335
|
+
policy.signal = hostSignal;
|
|
336
|
+
const dispatch = handler;
|
|
337
|
+
const outcome = await withRetry(() => dispatch(fx.params), policy);
|
|
338
|
+
if (outcome.aborted) {
|
|
339
|
+
// 取消不计 failure(不增熔断计数),也不 onSuccess 清掉既有故障;
|
|
340
|
+
// half-open 探测取消只释放本次 probe 所有权。
|
|
341
|
+
br?.releaseProbe(gate.token);
|
|
342
|
+
const abortedTrace = {
|
|
343
|
+
effect: fx.effect, channel: spec.channel, attempts: outcome.attempts,
|
|
344
|
+
backoffMs: outcome.backoffMs, breaker: br?.state() ?? 'off', declined: 'aborted',
|
|
345
|
+
evidence: [`[效应:${fx.effect}@abort@${ts}] aborted; breaker state untouched`],
|
|
346
|
+
};
|
|
347
|
+
return { result: null, trace: abortedTrace };
|
|
348
|
+
}
|
|
349
|
+
const failed = outcome.error != null || spec.isFailure(outcome.result);
|
|
350
|
+
if (br)
|
|
351
|
+
failed ? br.onFailure(gate.token) : br.onSuccess(gate.token);
|
|
352
|
+
const breakerState = br?.state() ?? 'off';
|
|
353
|
+
const evidence = [`[效应:${fx.effect}@${ts}] attempts=${outcome.attempts} backoff=${outcome.backoffMs}ms breaker=${breakerState}`];
|
|
354
|
+
return {
|
|
355
|
+
result: outcome.error != null ? null : outcome.result,
|
|
356
|
+
trace: { effect: fx.effect, channel: spec.channel, attempts: outcome.attempts, backoffMs: outcome.backoffMs, breaker: breakerState, evidence },
|
|
357
|
+
};
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
/** 产品面解译器单例(断路器状态随进程存活;同 session-search 节律闸先例) */
|
|
361
|
+
export const productionInterpreter = makeProductionInterpreter();
|
|
362
|
+
/** 类型化入口:tool 层 `interpretEffect({ effect: 'FLYAI_SEARCH', params })` */
|
|
363
|
+
export async function interpretEffect(fx) {
|
|
364
|
+
return productionInterpreter(fx);
|
|
365
|
+
}
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
// mock 解译器:CI/本地无网确定性(夹具 = 渠道 observation 的录制形态)
|
|
368
|
+
// ---------------------------------------------------------------------------
|
|
369
|
+
/**
|
|
370
|
+
* mock 解译器(禁止网络,0 延迟,确定性):给同一效应返回夹具原样——
|
|
371
|
+
* 渠道 observation 的录制就长什么样回放什么样,证据链由夹具自带
|
|
372
|
+
* (录制期已带 [实时API:…]),trace 落 [效应:mock@ts] 夹具回放标注。
|
|
373
|
+
* CI / 离线单测 / 巡检「不联网跑全链」即此通道(与 mock-llm 同思想的第三个成员)。
|
|
374
|
+
*/
|
|
375
|
+
export function makeMockInterpreter(fixtures) {
|
|
376
|
+
return async (fx) => {
|
|
377
|
+
const ts = new Date().toISOString();
|
|
378
|
+
const registered = Object.prototype.hasOwnProperty.call(fixtures, fx.effect);
|
|
379
|
+
if (!registered) {
|
|
380
|
+
return {
|
|
381
|
+
result: null,
|
|
382
|
+
trace: { effect: fx.effect, channel: 'mock', attempts: 0, backoffMs: 0, breaker: 'off', declined: 'unknown-effect', evidence: [`[效应:mock@${ts}] ${fx.effect} 未登记夹具`] },
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
result: fixtures[fx.effect],
|
|
387
|
+
trace: { effect: fx.effect, channel: 'mock', attempts: 1, backoffMs: 0, breaker: 'off', evidence: [`[效应:mock@${ts}] ${fx.effect} 夹具回放(确定性,无网络)`] },
|
|
388
|
+
};
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
/** 按环境选择解译器('production' 默认;'mock' 供 CI/离线) */
|
|
392
|
+
export function selectInterpreter(env, opts = {}) {
|
|
393
|
+
return env === 'mock' ? makeMockInterpreter(opts.fixtures ?? {}) : makeProductionInterpreter(opts);
|
|
394
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inert external-event adapter contract for issue #432, parent #82.
|
|
3
|
+
*
|
|
4
|
+
* Scope: project a serialized W2A `w2a/0.1` core envelope into bounded,
|
|
5
|
+
* typed metadata. This module is pure and deterministic: no IO, no Date.now,
|
|
6
|
+
* no network, no process spawning, no runtime registration, and no state writes.
|
|
7
|
+
*
|
|
8
|
+
* Upstream reference: machinepulse-ai/world2agent
|
|
9
|
+
* 7e5fc4d441699993b8f1ef7d3b9776065b7a93e0
|
|
10
|
+
* `schema/0.1/schema.ts`, `schema/0.1/schema.json`, and
|
|
11
|
+
* `docs/signal-format.md`.
|
|
12
|
+
*/
|
|
13
|
+
export const SUPPORTED_SCHEMA_VERSION = 'w2a/0.1';
|
|
14
|
+
export const ABSOLUTE_MAX_BYTES = 64 * 1024;
|
|
15
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
16
|
+
const EVENT_TYPE_RE = /^[^.\s]+\.[^.\s]+\.[^.\s]+$/u;
|
|
17
|
+
const MAX_DATE_EPOCH_MS = 8.64e15;
|
|
18
|
+
export function ingestExternalEvent(raw, options = {}) {
|
|
19
|
+
const cfg = validateOptions(options);
|
|
20
|
+
if ('rejected' in cfg)
|
|
21
|
+
return reject(cfg.rejected.reason, cfg.rejected.detail);
|
|
22
|
+
if (cfg.enabled !== true) {
|
|
23
|
+
return reject('disabled-by-default', 'options.enabled must be explicitly true');
|
|
24
|
+
}
|
|
25
|
+
if (cfg.allowedSources.length === 0) {
|
|
26
|
+
return reject('empty-allowlist', 'allowedSources must contain at least one exact tuple');
|
|
27
|
+
}
|
|
28
|
+
if (typeof raw !== 'string') {
|
|
29
|
+
return reject('input-not-a-string', 'raw must be a serialized JSON string');
|
|
30
|
+
}
|
|
31
|
+
const inputBytes = Buffer.byteLength(raw, 'utf8');
|
|
32
|
+
if (inputBytes > cfg.maxBytes) {
|
|
33
|
+
return reject('input-too-large', 'serialized envelope exceeds maxBytes', inputBytes);
|
|
34
|
+
}
|
|
35
|
+
let envelope;
|
|
36
|
+
try {
|
|
37
|
+
envelope = JSON.parse(raw);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return reject('malformed-json', 'serialized envelope is not valid JSON', inputBytes);
|
|
41
|
+
}
|
|
42
|
+
if (!isRecord(envelope)) {
|
|
43
|
+
return reject('missing-required-field', 'envelope must be a JSON object', inputBytes);
|
|
44
|
+
}
|
|
45
|
+
if (envelope.schema_version !== SUPPORTED_SCHEMA_VERSION) {
|
|
46
|
+
return reject('unsupported-schema-version', 'schema_version must be w2a/0.1', inputBytes);
|
|
47
|
+
}
|
|
48
|
+
if (typeof envelope.signal_id !== 'string' || !UUID_RE.test(envelope.signal_id)) {
|
|
49
|
+
return reject('malformed-uuid', 'signal_id must be a UUID-shaped string', inputBytes);
|
|
50
|
+
}
|
|
51
|
+
if (!isValidEpochMs(envelope.emitted_at)) {
|
|
52
|
+
return reject('malformed-timestamp', 'emitted_at must be a non-negative integer epoch ms in Date range', inputBytes);
|
|
53
|
+
}
|
|
54
|
+
if (!isRecord(envelope.source)) {
|
|
55
|
+
return reject('missing-required-field', 'source must be a JSON object', inputBytes);
|
|
56
|
+
}
|
|
57
|
+
const source = envelope.source;
|
|
58
|
+
const sensorId = readNonEmptyString(source.sensor_id);
|
|
59
|
+
const packageName = readNonEmptyString(source.package);
|
|
60
|
+
const sensorVersion = readNonEmptyString(source.sensor_version);
|
|
61
|
+
const sourceType = readNonEmptyString(source.source_type);
|
|
62
|
+
const userIdentity = readNonEmptyString(source.user_identity);
|
|
63
|
+
if (sensorId === null || packageName === null || sensorVersion === null || sourceType === null || userIdentity === null) {
|
|
64
|
+
return reject('missing-required-field', 'source.{sensor_id,package,sensor_version,source_type,user_identity} must be non-empty strings', inputBytes);
|
|
65
|
+
}
|
|
66
|
+
if (!isRecord(envelope.event)) {
|
|
67
|
+
return reject('missing-required-field', 'event must be a JSON object', inputBytes);
|
|
68
|
+
}
|
|
69
|
+
const event = envelope.event;
|
|
70
|
+
const eventType = readNonEmptyString(event.type);
|
|
71
|
+
if (eventType === null || !EVENT_TYPE_RE.test(eventType)) {
|
|
72
|
+
return reject('malformed-event-type', 'event.type must be a domain.entity.action string with three non-empty dot-separated segments', inputBytes);
|
|
73
|
+
}
|
|
74
|
+
if (!isValidEpochMs(event.occurred_at)) {
|
|
75
|
+
return reject('malformed-timestamp', 'event.occurred_at must be a non-negative integer epoch ms in Date range', inputBytes);
|
|
76
|
+
}
|
|
77
|
+
if (typeof event.summary !== 'string' || event.summary.length < 20) {
|
|
78
|
+
return reject('malformed-summary', 'event.summary must be at least 20 characters and is never returned', inputBytes);
|
|
79
|
+
}
|
|
80
|
+
const tupleAllowed = cfg.allowedSources.some((tuple) => tuple.sensorId === sensorId &&
|
|
81
|
+
tuple.package === packageName &&
|
|
82
|
+
tuple.sensorVersion === sensorVersion &&
|
|
83
|
+
tuple.sourceType === sourceType);
|
|
84
|
+
if (!tupleAllowed) {
|
|
85
|
+
return reject('tuple-not-allowed', 'source tuple is not in caller allowlist', inputBytes);
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
ok: true,
|
|
89
|
+
kind: 'ingested',
|
|
90
|
+
inputBytes,
|
|
91
|
+
metadata: {
|
|
92
|
+
trust: 'untrusted',
|
|
93
|
+
schemaVersion: SUPPORTED_SCHEMA_VERSION,
|
|
94
|
+
signalId: envelope.signal_id,
|
|
95
|
+
emittedAt: envelope.emitted_at,
|
|
96
|
+
occurredAt: event.occurred_at,
|
|
97
|
+
sensorId,
|
|
98
|
+
sourceType,
|
|
99
|
+
package: packageName,
|
|
100
|
+
sensorVersion,
|
|
101
|
+
eventType,
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function reject(reason, detail, inputBytes = 0) {
|
|
106
|
+
return { ok: false, kind: 'rejected', reason, detail, inputBytes };
|
|
107
|
+
}
|
|
108
|
+
function validateOptions(options) {
|
|
109
|
+
if (!isRecord(options)) {
|
|
110
|
+
return { rejected: { reason: 'invalid-options', detail: 'options must be a plain object' } };
|
|
111
|
+
}
|
|
112
|
+
const enabled = options.enabled === true;
|
|
113
|
+
const maxBytes = options.maxBytes ?? ABSOLUTE_MAX_BYTES;
|
|
114
|
+
if (typeof maxBytes !== 'number' || !Number.isFinite(maxBytes) || !Number.isInteger(maxBytes) || maxBytes <= 0 || maxBytes > ABSOLUTE_MAX_BYTES) {
|
|
115
|
+
return { rejected: { reason: 'invalid-options', detail: `maxBytes must be an integer in [1, ${ABSOLUTE_MAX_BYTES}]` } };
|
|
116
|
+
}
|
|
117
|
+
const rawAllowedSources = options.allowedSources ?? [];
|
|
118
|
+
if (!Array.isArray(rawAllowedSources)) {
|
|
119
|
+
return { rejected: { reason: 'invalid-options', detail: 'allowedSources must be an array' } };
|
|
120
|
+
}
|
|
121
|
+
const allowedSources = [];
|
|
122
|
+
for (const tuple of rawAllowedSources) {
|
|
123
|
+
if (!isRecord(tuple)) {
|
|
124
|
+
return { rejected: { reason: 'malformed-options-tuple', detail: 'allowedSources entry must be an object' } };
|
|
125
|
+
}
|
|
126
|
+
const sensorId = readNonEmptyString(tuple.sensorId);
|
|
127
|
+
const packageName = readNonEmptyString(tuple.package);
|
|
128
|
+
const sensorVersion = readNonEmptyString(tuple.sensorVersion);
|
|
129
|
+
const sourceType = readNonEmptyString(tuple.sourceType);
|
|
130
|
+
if (sensorId === null || packageName === null || sensorVersion === null || sourceType === null) {
|
|
131
|
+
return { rejected: { reason: 'malformed-options-tuple', detail: 'tuple fields must be non-empty strings' } };
|
|
132
|
+
}
|
|
133
|
+
allowedSources.push({ sensorId, package: packageName, sensorVersion, sourceType });
|
|
134
|
+
}
|
|
135
|
+
return { enabled, allowedSources, maxBytes };
|
|
136
|
+
}
|
|
137
|
+
function readNonEmptyString(value) {
|
|
138
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
139
|
+
}
|
|
140
|
+
function isRecord(value) {
|
|
141
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
142
|
+
}
|
|
143
|
+
function isValidEpochMs(value) {
|
|
144
|
+
return typeof value === 'number' &&
|
|
145
|
+
Number.isFinite(value) &&
|
|
146
|
+
Number.isInteger(value) &&
|
|
147
|
+
value >= 0 &&
|
|
148
|
+
value <= MAX_DATE_EPOCH_MS;
|
|
149
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 可下单事实侧车持久化(issue #46,ADR-19):append-only JSONL 证据日志。
|
|
3
|
+
*
|
|
4
|
+
* 定位:这是**会话证据**而非用户状态——与 bridge-latency.jsonl / incidents.jsonl
|
|
5
|
+
* 同类(审计痕迹),不进账本投影。append-only 保历史(同 route+date 重查的旧快照
|
|
6
|
+
* 可回溯),读取侧经 dedupeFacts 取同 fact_id 最新。
|
|
7
|
+
*
|
|
8
|
+
* 路径:<stateRoot>/gotry-state/bookable-facts.jsonl
|
|
9
|
+
*/
|
|
10
|
+
import { appendFile, mkdir, readFile } from 'node:fs/promises';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { dedupeFacts } from '../src/bookable-facts.js';
|
|
13
|
+
export const FACT_LOG_NAME = 'bookable-facts.jsonl';
|
|
14
|
+
export function factLogPath(stateRoot) {
|
|
15
|
+
const root = stateRoot === '.' ? process.cwd() : stateRoot;
|
|
16
|
+
return join(root, 'gotry-state', FACT_LOG_NAME);
|
|
17
|
+
}
|
|
18
|
+
/** 追加事实(永不抛错:落盘失败不阻塞检索工具主路径,返回 false 由调用方记 incident) */
|
|
19
|
+
export async function appendFacts(stateRoot, facts) {
|
|
20
|
+
if (facts.length === 0)
|
|
21
|
+
return true;
|
|
22
|
+
try {
|
|
23
|
+
const p = factLogPath(stateRoot);
|
|
24
|
+
await mkdir(join(p, '..'), { recursive: true });
|
|
25
|
+
await appendFile(p, facts.map(f => JSON.stringify(f)).join('\n') + '\n', 'utf-8');
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** 读注册表(去重后视图;无文件 = 空注册表,不抛) */
|
|
33
|
+
export async function loadFactRegistry(stateRoot) {
|
|
34
|
+
try {
|
|
35
|
+
const raw = await readFile(factLogPath(stateRoot), 'utf-8');
|
|
36
|
+
const facts = [];
|
|
37
|
+
for (const line of raw.split('\n')) {
|
|
38
|
+
const t = line.trim();
|
|
39
|
+
if (!t)
|
|
40
|
+
continue;
|
|
41
|
+
try {
|
|
42
|
+
const f = JSON.parse(t);
|
|
43
|
+
if (f.schema === 'gotry_bookable_fact.v1' && f.fact_id)
|
|
44
|
+
facts.push(f);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// 单行损坏不拖垮整本注册表(与 incidents/latency 日志同纪律)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return dedupeFacts(facts);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
}
|