@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
package/dist/scripts/smoke.js
CHANGED
|
@@ -1,328 +1,772 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 冒烟:不启动完整 dsh 运行时,验证插件注册 + execute + 纯 TS 求解器及隔离能力夹具。
|
|
3
|
+
* 运行(在 ts/ 下):npx tsx scripts/smoke.ts
|
|
4
|
+
*/
|
|
1
5
|
import { readFile } from 'node:fs/promises';
|
|
2
6
|
import { join } from 'node:path';
|
|
3
7
|
import { apply } from '../src/index.js';
|
|
8
|
+
import { renderFlightFact } from '../src/bookable-facts.js';
|
|
9
|
+
import { installModelOverride } from '../capabilities/model-override.js';
|
|
10
|
+
import { offlineSessionFlightResult, sessionLiveEnabled } from '../capabilities/session-search.js';
|
|
11
|
+
import { interpretEffect } from '../capabilities/effect.js';
|
|
12
|
+
// 日期槽位动态化:live 检索(flyai/session)的入参日期必须在未来——硬编码日期
|
|
13
|
+
// 会在次日翻成 past date 被 date-gate 拒绝(2026-10-02 实爆:2026-10-01 变过去)。
|
|
14
|
+
const futureDate = (offsetDays) => new Date(Date.now() + offsetDays * 86_400_000).toISOString().slice(0, 10);
|
|
15
|
+
const SMOKE_FLIGHT_DATE = futureDate(7);
|
|
16
|
+
const SMOKE_HOTEL_CHECKIN = futureDate(10);
|
|
17
|
+
const SMOKE_HOTEL_CHECKOUT = futureDate(12);
|
|
4
18
|
async function main() {
|
|
19
|
+
// 巡检状态纪律:smoke 的探针数据(wish/profile/sidecar)写进独立临时 stateRoot,
|
|
20
|
+
// 不与真实用户状态(gotry-state)混居;结束即删
|
|
5
21
|
const { mkdtempSync, rmSync } = await import('node:fs');
|
|
6
22
|
const { tmpdir } = await import('node:os');
|
|
7
23
|
const smokeRoot = mkdtempSync(join(tmpdir(), 'gotry-smoke-'));
|
|
8
24
|
const registered = [];
|
|
9
25
|
const variables = {};
|
|
26
|
+
const approvalReasons = [];
|
|
27
|
+
const selectedEffects = [];
|
|
28
|
+
const effectSummaries = [];
|
|
29
|
+
let issueNow = new Date('2026-09-10T12:00:00.000Z');
|
|
30
|
+
const preExecutes = [];
|
|
10
31
|
const ctx = {
|
|
11
|
-
tools: {
|
|
12
|
-
|
|
32
|
+
tools: { register: (t) => registered.push(t) },
|
|
33
|
+
systemPrompt: { variable: (name, provider) => { variables[name] = provider; } },
|
|
34
|
+
get: (name) => name === 'approval' ? {
|
|
35
|
+
request: async (request) => {
|
|
36
|
+
approvalReasons.push(String(request.reason ?? ''));
|
|
37
|
+
return 'allowed-once';
|
|
38
|
+
},
|
|
39
|
+
} : undefined,
|
|
40
|
+
on: (event, fn) => {
|
|
41
|
+
if (event === 'tools/pre-execute')
|
|
42
|
+
preExecutes.push(fn);
|
|
43
|
+
return () => { };
|
|
13
44
|
},
|
|
14
|
-
systemPrompt: {
|
|
15
|
-
variable: (name, provider)=>{
|
|
16
|
-
variables[name] = provider;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
45
|
};
|
|
20
|
-
|
|
46
|
+
// 配置对象持引用:授权闸总闸(sessionAccess)在 §13 里运行时切闸验证
|
|
47
|
+
const cfg = {
|
|
21
48
|
stateRoot: smokeRoot,
|
|
22
49
|
timeoutMs: 30_000,
|
|
23
|
-
hbcliBin: 'hbcli-not-on-path'
|
|
50
|
+
hbcliBin: 'hbcli-not-on-path', // 强制走降级路径的确定性验证
|
|
51
|
+
sessionAccess: 'ask',
|
|
52
|
+
};
|
|
53
|
+
const fixtureEffect = async (fx) => {
|
|
54
|
+
if (fx.effect === 'SESSION_DIDA_SEARCH') {
|
|
55
|
+
selectedEffects.push(fx.effect);
|
|
56
|
+
return {
|
|
57
|
+
result: { ok: true, via: 'session-dida-portal', evidence: '[fixture:session-dida]', latencyMs: 0, verdict: 'hit', rates: [] },
|
|
58
|
+
trace: { effect: fx.effect, channel: 'fixture', attempts: 1, backoffMs: 0, breaker: 'off', evidence: ['[fixture:session-effect]'] },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (fx.effect === 'FLYAI_SEARCH') {
|
|
62
|
+
selectedEffects.push(fx.effect);
|
|
63
|
+
if (fx.params?.kind !== 'train') {
|
|
64
|
+
return {
|
|
65
|
+
result: {
|
|
66
|
+
ok: false,
|
|
67
|
+
via: 'flyai-error',
|
|
68
|
+
evidence: '[实时API:flyai@error@fixture-offline]',
|
|
69
|
+
latencyMs: 0,
|
|
70
|
+
verdict: 'error',
|
|
71
|
+
kind: fx.params?.kind === 'hotel' ? 'hotel' : 'flight',
|
|
72
|
+
error: 'offline smoke fixture; real FlyAI provider not invoked',
|
|
73
|
+
},
|
|
74
|
+
trace: { effect: fx.effect, channel: 'fixture', attempts: 1, backoffMs: 0, breaker: 'off', evidence: ['[fixture:flyai-offline]'] },
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
result: {
|
|
79
|
+
ok: true,
|
|
80
|
+
via: 'flyai',
|
|
81
|
+
evidence: '[fixture:flyai-train]',
|
|
82
|
+
latencyMs: 0,
|
|
83
|
+
verdict: 'hit',
|
|
84
|
+
kind: 'train',
|
|
85
|
+
options: [{ no: 'G1375', name: '高铁', depDateTime: '2027-11-11T07:35:00+08:00', arrDateTime: '2027-11-11T15:27:00+08:00', depStation: '上海', arrStation: '昆明', durationMin: 472, price: 0 }],
|
|
86
|
+
},
|
|
87
|
+
trace: { effect: fx.effect, channel: 'fixture', attempts: 1, backoffMs: 0, breaker: 'off', evidence: ['[fixture:flyai-train]'] },
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (fx.effect === 'SESSION_TRAIN_SEARCH') {
|
|
91
|
+
selectedEffects.push(fx.effect);
|
|
92
|
+
const date = fx.params?.date ?? '';
|
|
93
|
+
const from = fx.params?.from ?? '';
|
|
94
|
+
const to = fx.params?.to ?? '';
|
|
95
|
+
if (from !== '上海' || to !== '昆明') {
|
|
96
|
+
const reason = '城市不在 12306 码表;请带 fromStationTelecode/toStationTelecode 重试';
|
|
97
|
+
return {
|
|
98
|
+
result: { ok: false, via: 'session-train-12306-error', evidence: '[fixture:session-train-unresolved]', latencyMs: 0, verdict: 'error', outcome: { kind: 'transport-runtime-error', reason }, error: reason },
|
|
99
|
+
trace: { effect: fx.effect, channel: 'fixture', attempts: 1, backoffMs: 0, breaker: 'off', evidence: ['[fixture:session-train-unresolved]'] },
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const response = { url: `https://kyfw.12306.cn/otn/leftTicket/queryG?leftTicketDTO.train_date=${date}&leftTicketDTO.from_station=SHH&leftTicketDTO.to_station=KMM`, fromStationTelecode: 'SHH', toStationTelecode: 'KMM', date };
|
|
103
|
+
const common = { requested: { from, to, date }, request: { fromStationTelecode: 'SHH', toStationTelecode: 'KMM', date }, response, batchId: `fixture-${date}`, queryId: `session:12306-train:fixture-${date}`, fetchedAt: '2026-09-10T11:59:00.000Z' };
|
|
104
|
+
const collection = date === '2027-11-15'
|
|
105
|
+
? { ...common, requested: { from, to, date: '2027-11-14' } }
|
|
106
|
+
: common;
|
|
107
|
+
if (date === '2027-11-13') {
|
|
108
|
+
return {
|
|
109
|
+
result: { ok: false, via: 'session-train-12306-error', evidence: '[fixture:session-train-malformed]', latencyMs: 0, verdict: 'error', outcome: { kind: 'malformed', reason: 'fixture malformed root' }, error: 'fixture malformed root' },
|
|
110
|
+
trace: { effect: fx.effect, channel: 'fixture', attempts: 1, backoffMs: 0, breaker: 'off', evidence: ['[fixture:session-train-malformed]'] },
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (date === '2027-11-14') {
|
|
114
|
+
return {
|
|
115
|
+
result: { ok: false, via: 'session-train-12306-error', evidence: '[fixture:session-train-transport]', latencyMs: 0, verdict: 'error', outcome: { kind: 'transport-runtime-error', reason: 'fixture transport error' }, error: 'fixture transport error' },
|
|
116
|
+
trace: { effect: fx.effect, channel: 'fixture', attempts: 1, backoffMs: 0, breaker: 'off', evidence: ['[fixture:session-train-transport]'] },
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (date === '2027-11-12') {
|
|
120
|
+
const outcome = { kind: 'recognized-empty', trains: [] };
|
|
121
|
+
return {
|
|
122
|
+
result: { ok: true, via: 'session-train-12306', evidence: '[fixture:session-train-empty]', latencyMs: 0, verdict: 'miss', outcome, collection: common, trains: [] },
|
|
123
|
+
trace: { effect: fx.effect, channel: 'fixture', attempts: 1, backoffMs: 0, breaker: 'off', evidence: ['[fixture:session-train-empty]'] },
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const train = { trainCode: 'G1375', fromStation: '上海', toStation: '昆明', depTime: '07:35', arrTime: '15:27', durationMin: 472, startTrainDate: date === '2027-11-11' ? '2027-11-10' : date, canWebBuy: 'Y', seats: { '二等座': '有' }, jumpUrl: 'fixture://12306' };
|
|
127
|
+
const outcome = { kind: 'recognized-nonempty', trains: [train] };
|
|
128
|
+
return {
|
|
129
|
+
result: { ok: true, via: 'session-train-12306', evidence: '[fixture:session-train-hit]', latencyMs: 0, verdict: 'hit', outcome, collection, trains: [train] },
|
|
130
|
+
trace: { effect: fx.effect, channel: 'fixture', attempts: 1, backoffMs: 0, breaker: 'off', evidence: ['[fixture:session-train-hit]'] },
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return interpretEffect(fx);
|
|
134
|
+
};
|
|
135
|
+
apply(ctx, cfg, {
|
|
136
|
+
effect: fixtureEffect,
|
|
137
|
+
clock: () => issueNow,
|
|
24
138
|
});
|
|
25
|
-
console.log(`registered tools: ${registered.map(
|
|
26
|
-
const byName = (n)=>{
|
|
27
|
-
const t = registered.find(
|
|
28
|
-
if (!t)
|
|
139
|
+
console.log(`registered tools: ${registered.map(t => t.name).join(', ')}`);
|
|
140
|
+
const byName = (n) => {
|
|
141
|
+
const t = registered.find(t => t.name === n);
|
|
142
|
+
if (!t)
|
|
143
|
+
throw new Error(`tool ${n} not registered`);
|
|
29
144
|
return t;
|
|
30
145
|
};
|
|
146
|
+
// 1) 动机画像:evidence 缺失必须被拒绝(P0 反幻觉红线;D-30 后由宿主权 schema 闸——
|
|
147
|
+
// evidence required 进嵌套 schema,拒绝更前置,ToolFailure 形状不变)
|
|
31
148
|
const motivation = byName('gotry_motivation_save');
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
weights: {
|
|
36
|
-
escape_rest: 0.7
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}, null);
|
|
40
|
-
throw new Error('FAIL: profile without evidence should have been rejected');
|
|
41
|
-
} catch (e) {
|
|
42
|
-
console.log(`motivation without evidence rejected: ${e.message.slice(0, 60)}...`);
|
|
149
|
+
const rejectedMotivation = await motivation.execute({ profile: { weights: { escape_rest: 0.7 } } }, null);
|
|
150
|
+
if (rejectedMotivation.ok !== false || !/evidence/i.test(rejectedMotivation.summary ?? '')) {
|
|
151
|
+
throw new Error(`FAIL: profile without evidence should have been rejected, actual ${JSON.stringify(rejectedMotivation).slice(0, 160)}`);
|
|
43
152
|
}
|
|
153
|
+
console.log(`motivation without evidence rejected: ${(rejectedMotivation.summary ?? '').slice(0, 80)}...`);
|
|
44
154
|
const saved = await motivation.execute({
|
|
45
155
|
profile: {
|
|
46
|
-
weights: {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
evidence: [
|
|
51
|
-
'用户原话:想去湖边什么都不干(洱海照片)'
|
|
52
|
-
],
|
|
53
|
-
hard: {
|
|
54
|
-
wake_not_before: '06:30',
|
|
55
|
-
min_arrival_energy_pct: 40
|
|
56
|
-
}
|
|
57
|
-
}
|
|
156
|
+
weights: { escape_rest: 0.7, curiosity: 0.3 },
|
|
157
|
+
evidence: ['用户原话:想去湖边什么都不干(洱海照片)'],
|
|
158
|
+
hard: { wake_not_before: '06:30', min_arrival_energy_pct: 40 },
|
|
159
|
+
},
|
|
58
160
|
}, null);
|
|
59
161
|
console.log(`motivation saved -> ${JSON.stringify(saved).slice(0, 80)}...`);
|
|
162
|
+
// 2) 可行性引擎:洱海金标准用例,经注册工具跑纯 TS 求解路径
|
|
60
163
|
const payload = JSON.parse(await readFile(join('..', 'data', 'golden_erhai.json'), 'utf-8'));
|
|
61
164
|
const feasibility = byName('gotry_feasibility_check');
|
|
62
|
-
const result = await feasibility.execute({
|
|
63
|
-
payload
|
|
64
|
-
}, null);
|
|
165
|
+
const result = await feasibility.execute({ payload }, null);
|
|
65
166
|
console.log(`\nfeasibility: recommended=${result.recommended}, via=${result.via}, latency=${result.latency_ms}ms\n`);
|
|
66
167
|
console.log(result.answer_md);
|
|
168
|
+
// Issue #2:实际注册工具 execute → parseCandidate → segmentsFromCandidate
|
|
169
|
+
// → applyPlanningWindow → solve。日期下界来自 apply 注入的宿主时钟,不来自 payload。
|
|
170
|
+
const datedCandidates = payload.candidates.slice(0, 3).map((candidate, i) => ({
|
|
171
|
+
...candidate,
|
|
172
|
+
date: i === 0 ? '2026-09-01' : i === 1 ? '2026-10-01' : '2027-01-01',
|
|
173
|
+
}));
|
|
174
|
+
const futurePayload = {
|
|
175
|
+
...payload,
|
|
176
|
+
candidates: datedCandidates,
|
|
177
|
+
planning: { intent: 'future', requested_year: 2026 },
|
|
178
|
+
};
|
|
179
|
+
const noPlanningResult = await feasibility.execute({
|
|
180
|
+
payload: { ...payload, candidates: datedCandidates.slice(0, 2) },
|
|
181
|
+
}, null);
|
|
182
|
+
const futureResult = await feasibility.execute({ payload: futurePayload }, null);
|
|
183
|
+
const pastId = String(datedCandidates[0]?.['id']);
|
|
184
|
+
const validId = String(datedCandidates[1]?.['id']);
|
|
185
|
+
const nextYearId = String(datedCandidates[2]?.['id']);
|
|
186
|
+
if (noPlanningResult.ok !== true || noPlanningResult.recommended !== validId
|
|
187
|
+
|| noPlanningResult.verdicts?.some(v => v['candidate_id'] === pastId || v['candidate_id'] === nextYearId)) {
|
|
188
|
+
throw new Error(`FAIL: registered no-planning path accepted a past/cross-year candidate: ${JSON.stringify(noPlanningResult).slice(0, 240)}`);
|
|
189
|
+
}
|
|
190
|
+
const allPastResult = await feasibility.execute({
|
|
191
|
+
payload: { ...payload, candidates: datedCandidates.map(candidate => ({ ...candidate, date: '2026-09-01' })) },
|
|
192
|
+
}, null);
|
|
193
|
+
if (allPastResult.ok !== false || allPastResult.code !== 'planning_window_rejected'
|
|
194
|
+
|| allPastResult.evidence !== undefined || !/2026-09-10/.test(allPastResult.summary ?? '')) {
|
|
195
|
+
throw new Error(`FAIL: no-planning all-past dates should be structured validation rejection: ${JSON.stringify(allPastResult).slice(0, 240)}`);
|
|
196
|
+
}
|
|
197
|
+
const incidentsPath = join(smokeRoot, 'gotry-state', 'incidents.jsonl');
|
|
198
|
+
const incidentBytesBeforeDateValidation = await readFile(incidentsPath, 'utf-8').catch(() => '');
|
|
199
|
+
if (futureResult.ok !== true || futureResult.recommended !== validId
|
|
200
|
+
|| futureResult.verdicts?.some(v => v['candidate_id'] === pastId || v['candidate_id'] === nextYearId)) {
|
|
201
|
+
throw new Error(`FAIL: registered Issue #2 path accepted a past candidate: ${JSON.stringify(futureResult).slice(0, 240)}`);
|
|
202
|
+
}
|
|
203
|
+
const expiredResult = await feasibility.execute({
|
|
204
|
+
payload: { ...futurePayload, planning: { intent: 'future', requested_year: 2025 } },
|
|
205
|
+
}, null);
|
|
206
|
+
if (expiredResult.ok !== false || expiredResult.code !== 'planning_window_rejected'
|
|
207
|
+
|| expiredResult.evidence !== undefined || !/2025 年已结束/.test(JSON.stringify(expiredResult))) {
|
|
208
|
+
throw new Error(`FAIL: registered Issue #2 path did not reject expired year: ${JSON.stringify(expiredResult).slice(0, 240)}`);
|
|
209
|
+
}
|
|
210
|
+
const historicalResult = await feasibility.execute({
|
|
211
|
+
payload: { ...payload, candidates: datedCandidates, planning: { intent: 'historical', requested_year: 2026 } },
|
|
212
|
+
}, null);
|
|
213
|
+
const historicalIds = new Set((historicalResult.verdicts ?? []).map(v => String(v['candidate_id'])));
|
|
214
|
+
if (historicalResult.ok !== true || !historicalIds.has(pastId) || !historicalIds.has(validId) || !historicalIds.has(nextYearId)) {
|
|
215
|
+
throw new Error(`FAIL: explicit historical mode should preserve dated candidates: ${JSON.stringify(historicalResult).slice(0, 240)}`);
|
|
216
|
+
}
|
|
217
|
+
issueNow = new Date('2026-10-02T12:00:00.000Z');
|
|
218
|
+
const advancedClockResult = await feasibility.execute({
|
|
219
|
+
payload: { ...payload, candidates: [datedCandidates[1]] },
|
|
220
|
+
}, null);
|
|
221
|
+
if (advancedClockResult.ok !== false || advancedClockResult.code !== 'planning_window_rejected'
|
|
222
|
+
|| advancedClockResult.evidence !== undefined || !/2026-10-02/.test(advancedClockResult.summary ?? '')) {
|
|
223
|
+
throw new Error(`FAIL: advanced host clock should reject now-expired dated candidate: ${JSON.stringify(advancedClockResult).slice(0, 240)}`);
|
|
224
|
+
}
|
|
225
|
+
const incidentBytesAfterDateValidation = await readFile(incidentsPath, 'utf-8').catch(() => '');
|
|
226
|
+
if (incidentBytesAfterDateValidation !== incidentBytesBeforeDateValidation) {
|
|
227
|
+
throw new Error('FAIL: expected date rejections entered incident path');
|
|
228
|
+
}
|
|
229
|
+
issueNow = new Date('2026-09-10T12:00:00.000Z');
|
|
230
|
+
console.log(`Issue #2 registered execute path: dateless compatible, no-planning past/mixed guarded, valid=${validId} recommended, historical explicit, expired-year and advanced-clock rejection structured`);
|
|
231
|
+
// 3) wish pool:把不可行的憧憬连同成行条件放入「下一次出发」
|
|
67
232
|
const wish = byName('gotry_wish_pool_add');
|
|
68
233
|
const added = await wish.execute({
|
|
69
234
|
entry: {
|
|
70
235
|
name: '大理·洱海',
|
|
71
236
|
reason: '2 天窗口装不下(冲突:duration)',
|
|
72
|
-
conditions: {
|
|
73
|
-
|
|
74
|
-
budget_cny: 4950,
|
|
75
|
-
best_months: [
|
|
76
|
-
3,
|
|
77
|
-
4,
|
|
78
|
-
5,
|
|
79
|
-
9,
|
|
80
|
-
10,
|
|
81
|
-
11
|
|
82
|
-
]
|
|
83
|
-
}
|
|
84
|
-
}
|
|
237
|
+
conditions: { days: 5, budget_cny: 4950, best_months: [3, 4, 5, 9, 10, 11] },
|
|
238
|
+
},
|
|
85
239
|
}, null);
|
|
86
240
|
console.log(`\nwish pool -> ${JSON.stringify(added)}`);
|
|
87
|
-
if (result.recommended !== 'qiandao')
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
241
|
+
if (result.recommended !== 'qiandao')
|
|
242
|
+
throw new Error('FAIL: expected qiandao recommended');
|
|
243
|
+
// 4) D-4 结果卡:presentResult 把逐候选判定 + 全成本 vs 预算渲染成紧凑行(非裸 JSON)
|
|
244
|
+
if (typeof feasibility.presentResult !== 'function')
|
|
245
|
+
throw new Error('FAIL: feasibility 缺 presentResult');
|
|
246
|
+
const view = feasibility.presentResult({ payload }, result);
|
|
247
|
+
if (!view?.title?.includes('qiandao'))
|
|
248
|
+
throw new Error(`FAIL: 结果卡标题缺推荐,实际 ${view?.title}`);
|
|
93
249
|
const body = view?.content?.[0]?.text ?? '';
|
|
94
|
-
if (!body.includes('✅') || !body.includes('¥'))
|
|
250
|
+
if (!body.includes('✅') || !body.includes('¥'))
|
|
251
|
+
throw new Error('FAIL: 结果卡缺判定行/成本行');
|
|
95
252
|
console.log(`\nresult card: ${view?.title}\n${body.split('\n').slice(0, 5).join('\n')}`);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
'
|
|
99
|
-
|
|
100
|
-
'gotry_anything_search',
|
|
101
|
-
'gotry_agent_reach'
|
|
102
|
-
]){
|
|
103
|
-
if (typeof byName(n).presentResult !== 'function') throw new Error(`FAIL: ${n} 缺 presentResult`);
|
|
253
|
+
// 5) D-4 结果卡面:高流量 5 工具均带 presentResult;agent_reach 纯函数实拍
|
|
254
|
+
for (const n of ['gotry_feasibility_check', 'gotry_hotel_search', 'gotry_weather_check', 'gotry_anything_search', 'gotry_agent_reach']) {
|
|
255
|
+
if (typeof byName(n).presentResult !== 'function')
|
|
256
|
+
throw new Error(`FAIL: ${n} 缺 presentResult`);
|
|
104
257
|
}
|
|
105
258
|
const ar = byName('gotry_agent_reach');
|
|
106
|
-
const arView = ar.presentResult({
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
channel: 'v2ex',
|
|
110
|
-
method: 'get_hot_topics'
|
|
111
|
-
}
|
|
112
|
-
}, {
|
|
113
|
-
verdict: 'found',
|
|
114
|
-
summary: '10 topics'
|
|
115
|
-
});
|
|
116
|
-
if (!arView?.title?.includes('✅') || !arView.title.includes('v2ex.get_hot_topics')) throw new Error(`FAIL: agent_reach 结果卡,实际 ${arView?.title}`);
|
|
259
|
+
const arView = ar.presentResult({ action: 'reach', channel: 'v2ex', method: 'get_hot_topics' }, { verdict: 'found', summary: '10 topics' });
|
|
260
|
+
if (!arView?.title?.includes('✅') || !arView.title.includes('v2ex.get_hot_topics'))
|
|
261
|
+
throw new Error(`FAIL: agent_reach 结果卡,实际 ${arView?.title}`);
|
|
117
262
|
console.log(`result cards on 5 tools; agent_reach card: ${arView.title}`);
|
|
263
|
+
// 6) D-30 typed 契约后(#12/#13 形态兼容的演进终态):字符串 blob 形态被宿主权结构化拒绝
|
|
264
|
+
// (url required 进 schema);平铺 url 进 execute 走正常抓取/降级,不再有「裸形态落 url」
|
|
118
265
|
const ws = byName('gotry_web_search');
|
|
119
|
-
const r1 = await ws.execute({
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const r2 = await ws.execute({
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
],
|
|
130
|
-
[
|
|
131
|
-
'bare-obj',
|
|
132
|
-
r2
|
|
133
|
-
]
|
|
134
|
-
]){
|
|
135
|
-
if (r.summary === 'url 必填') throw new Error(`FAIL: ${name} 形态未被 unwrapQuery 接住`);
|
|
136
|
-
}
|
|
137
|
-
console.log('unwrapQuery: string + bare-object shapes both accepted');
|
|
266
|
+
const r1 = await ws.execute({ query: 'not-a-url' }, null);
|
|
267
|
+
if (!(r1.ok === false && !!r1.evidence))
|
|
268
|
+
throw new Error(`FAIL: 字符串 blob 形态应被宿主权结构化拒绝(ToolFailure),实际 ${JSON.stringify(r1).slice(0, 160)}`);
|
|
269
|
+
const r2 = await ws.execute({ url: 'not-a-url' }, null);
|
|
270
|
+
if (r2.summary === 'url 必填')
|
|
271
|
+
throw new Error(`FAIL: 平铺 url 应进 execute 走降级,而非参数闸,实际 ${r2.summary}`);
|
|
272
|
+
console.log('D-30 typed 契约:web_search 字符串 blob → 宿主权拒绝;平铺 url → 正常降级');
|
|
273
|
+
// 7) 骨架输出 schema 放宽回归:execute 正常返回三字段;guard 错误兜底(ok/summary)
|
|
274
|
+
// 不再被 strict schema 拒(dsh 参数层拒错→guard 兜底→旧 strict schema 校验炸,
|
|
275
|
+
// 真实错误信息被掩盖——patrol 实测抓到)
|
|
138
276
|
const skTool = byName('gotry_skeleton_check');
|
|
139
|
-
const skFlat = await skTool.execute({
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}, null);
|
|
143
|
-
if (skFlat.connected !== true) throw new Error(`FAIL: 骨架平铺调用应 connected=true,实际 ${JSON.stringify(skFlat).slice(0, 120)}`);
|
|
144
|
-
const skBad = await skTool.execute({
|
|
145
|
-
from: '',
|
|
146
|
-
to: ''
|
|
147
|
-
}, null);
|
|
277
|
+
const skFlat = await skTool.execute({ from: 'HKG', to: 'BKK' }, null);
|
|
278
|
+
if (skFlat.connected !== true)
|
|
279
|
+
throw new Error(`FAIL: 骨架平铺调用应 connected=true,实际 ${JSON.stringify(skFlat).slice(0, 120)}`);
|
|
280
|
+
const skBad = await skTool.execute({ from: '', to: '' }, null);
|
|
148
281
|
if (typeof skBad?.summary === 'string' && skBad.connected === undefined) {
|
|
282
|
+
// guard 兜底路径:宽松 schema 下字段可存在即可,不抛
|
|
149
283
|
console.log('skeleton guard-fallback shape survives (loose schema)');
|
|
150
284
|
}
|
|
151
285
|
console.log(`skeleton flat-args: connected=${skFlat.connected}`);
|
|
286
|
+
// 8) issue #283 闸:D-10 切片 B 接入点(逐字自然表达代码层换算;闸后保持 slot-resolved
|
|
287
|
+
// note 回显语义不变;闸失败路径走 input_required 不再无日期降级)
|
|
152
288
|
{
|
|
153
289
|
const hotel = byName('gotry_hotel_search');
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
checkIn: '2026-9-4',
|
|
158
|
-
checkOut: '2026-09-06'
|
|
159
|
-
}
|
|
160
|
-
}, null);
|
|
161
|
-
if (!resolved.date_notes?.some((n)=>n.includes('2026-9-4 → 2026-09-04'))) {
|
|
290
|
+
// 8a) 非规整 ISO「2026-9-4」由 slot-spec 归一为「2026-09-04」,进 hbcli 的 argv 与 date_notes 回显
|
|
291
|
+
const resolved = await hotel.execute({ destination: '大理', checkIn: '2026-9-4', checkOut: '2026-09-06' }, null);
|
|
292
|
+
if (!resolved.date_notes?.some(n => n.includes('2026-9-4 → 2026-09-04'))) {
|
|
162
293
|
throw new Error(`FAIL: 非规整 ISO 应产生 slot-resolved note,实际 ${JSON.stringify(resolved.date_notes)}`);
|
|
163
294
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
}, null);
|
|
170
|
-
if (!unresolved.date_notes?.some((n)=>n.includes('日期未解析:近期'))) {
|
|
171
|
-
throw new Error(`FAIL: 词表外表达应产生「日期未解析」note(不猜),实际 ${JSON.stringify(unresolved.date_notes)}`);
|
|
295
|
+
// 8b) 词表外表达(只给单侧 checkIn)→ 闸拒(input_required);issue #283 起,unresolved
|
|
296
|
+
// 不再走无日期降级——退化为「当前窗口价」是用户语义丢失的红线
|
|
297
|
+
const unresolved = await hotel.execute({ destination: '大理', checkIn: '近期' }, null);
|
|
298
|
+
if (unresolved.ok !== false || unresolved.verdict !== 'input_required' || unresolved.reason !== 'check_out_missing') {
|
|
299
|
+
throw new Error(`FAIL: 词表外/单侧日期闸应拒,实际 ${JSON.stringify(unresolved).slice(0, 200)}`);
|
|
172
300
|
}
|
|
173
|
-
|
|
301
|
+
if (!unresolved.missing?.includes('checkOut'))
|
|
302
|
+
throw new Error(`FAIL: 闸失败应指出缺 checkOut,实际 ${JSON.stringify(unresolved.missing)}`);
|
|
303
|
+
console.log('hotel date slots: verbatim resolved in code layer; unresolved/single-side rejected by date-gate (issue #283)');
|
|
174
304
|
}
|
|
305
|
+
// 9) RFC S1 observation envelope:成功路径平铺 ok:true;guard 兜底即失败分支同形
|
|
175
306
|
{
|
|
176
307
|
const wish = byName('gotry_wish_pool_add');
|
|
177
|
-
const w = await wish.execute({
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}, null);
|
|
185
|
-
if (w.ok !== true) throw new Error(`FAIL: wish 添加应 ok:true,实际 ${JSON.stringify(w)}`);
|
|
186
|
-
if (result.ok !== true) throw new Error(`FAIL: feasibility 应 ok:true,实际 ${String(result.ok)}`);
|
|
308
|
+
const w = await wish.execute({ entry: { name: 'envelope-probe', conditions: { days: 3 } } }, null);
|
|
309
|
+
if (w.ok !== true)
|
|
310
|
+
throw new Error(`FAIL: wish 添加应 ok:true,实际 ${JSON.stringify(w)}`);
|
|
311
|
+
if (result.ok !== true)
|
|
312
|
+
throw new Error(`FAIL: feasibility 应 ok:true,实际 ${String(result.ok)}`);
|
|
187
313
|
console.log('observation envelope: success payloads carry flat ok:true (guard fallback = failure branch)');
|
|
188
314
|
}
|
|
315
|
+
// 10) RFC S2+S3:wish pool sidecar——0..1 召回/muted 不召回/无命中不硬推/owner 确认归因
|
|
189
316
|
{
|
|
190
317
|
const add = byName('gotry_wish_pool_add');
|
|
191
318
|
const list = byName('gotry_wish_pool_list');
|
|
192
|
-
const a = await add.execute({
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
days: 6,
|
|
212
|
-
budgetCny: 6000,
|
|
213
|
-
month: 4
|
|
214
|
-
}
|
|
215
|
-
}, null);
|
|
216
|
-
if (!recall.suggestion?.wish_id) throw new Error(`FAIL: 条件命中应召回恰好 1 条(0..1),实际 ${JSON.stringify(recall.suggestion)}`);
|
|
217
|
-
if ((recall.suggestion.match_score ?? 0) < 3) throw new Error(`FAIL: 召回应按条件匹配评分,实际 ${JSON.stringify(recall.suggestion)}`);
|
|
218
|
-
const confirm = await list.execute({
|
|
219
|
-
query: {
|
|
220
|
-
action: 'confirm-outcome',
|
|
221
|
-
wishId: a.wish_id,
|
|
222
|
-
attribution: 'helpful',
|
|
223
|
-
detail: '用户明说:这条建议成了'
|
|
224
|
-
}
|
|
225
|
-
}, null);
|
|
226
|
-
if (!confirm.ok || confirm.status !== 'helpful') throw new Error(`FAIL: owner 确认归因应落盘,实际 ${JSON.stringify(confirm)}`);
|
|
227
|
-
const noAttr = await list.execute({
|
|
228
|
-
query: {
|
|
229
|
-
action: 'confirm-outcome',
|
|
230
|
-
wishId: a.wish_id
|
|
231
|
-
}
|
|
232
|
-
}, null);
|
|
233
|
-
if (noAttr.ok !== false) throw new Error('FAIL: 无归因的 confirm 应拒绝(归因不许缺省)');
|
|
234
|
-
await add.execute({
|
|
235
|
-
entry: {
|
|
236
|
-
name: 'sidecar-probe-洱海',
|
|
237
|
-
conditions: {
|
|
238
|
-
days: 5,
|
|
239
|
-
budget_cny: 4950,
|
|
240
|
-
best_months: [
|
|
241
|
-
3,
|
|
242
|
-
4,
|
|
243
|
-
5
|
|
244
|
-
]
|
|
245
|
-
},
|
|
246
|
-
muted: true
|
|
247
|
-
}
|
|
248
|
-
}, null);
|
|
249
|
-
const recall2 = await list.execute({
|
|
250
|
-
query: {
|
|
251
|
-
action: 'recall',
|
|
252
|
-
days: 6,
|
|
253
|
-
budgetCny: 6000,
|
|
254
|
-
month: 4
|
|
255
|
-
}
|
|
256
|
-
}, null);
|
|
257
|
-
if (recall2.suggestion?.wish_id === a.wish_id) throw new Error('FAIL: muted wish 不得召回');
|
|
319
|
+
const a = await add.execute({ entry: { name: 'sidecar-probe-洱海', reason: '5 天起', conditions: { days: 5, budget_cny: 4950, best_months: [3, 4, 5] } } }, null);
|
|
320
|
+
if (!a.wish_id)
|
|
321
|
+
throw new Error('FAIL: wish add 应返回稳定 wish_id');
|
|
322
|
+
const recall = await list.execute({ query: { action: 'recall', days: 6, budgetCny: 6000, month: 4 } }, null);
|
|
323
|
+
if (!recall.suggestion?.wish_id)
|
|
324
|
+
throw new Error(`FAIL: 条件命中应召回恰好 1 条(0..1),实际 ${JSON.stringify(recall.suggestion)}`);
|
|
325
|
+
if ((recall.suggestion.match_score ?? 0) < 3)
|
|
326
|
+
throw new Error(`FAIL: 召回应按条件匹配评分,实际 ${JSON.stringify(recall.suggestion)}`);
|
|
327
|
+
const confirm = await list.execute({ query: { action: 'confirm-outcome', wishId: a.wish_id, attribution: 'helpful', detail: '用户明说:这条建议成了' } }, null);
|
|
328
|
+
if (!confirm.ok || confirm.status !== 'helpful')
|
|
329
|
+
throw new Error(`FAIL: owner 确认归因应落盘,实际 ${JSON.stringify(confirm)}`);
|
|
330
|
+
const noAttr = await list.execute({ query: { action: 'confirm-outcome', wishId: a.wish_id } }, null);
|
|
331
|
+
if (noAttr.ok !== false)
|
|
332
|
+
throw new Error('FAIL: 无归因的 confirm 应拒绝(归因不许缺省)');
|
|
333
|
+
// muted:置休眠后 0..1 召回不再出现(同名更新 muted:true)
|
|
334
|
+
await add.execute({ entry: { name: 'sidecar-probe-洱海', conditions: { days: 5, budget_cny: 4950, best_months: [3, 4, 5] }, muted: true } }, null);
|
|
335
|
+
const recall2 = await list.execute({ query: { action: 'recall', days: 6, budgetCny: 6000, month: 4 } }, null);
|
|
336
|
+
if (recall2.suggestion?.wish_id === a.wish_id)
|
|
337
|
+
throw new Error('FAIL: muted wish 不得召回');
|
|
258
338
|
console.log('wish sidecar: recall 0..1 + muted excluded + owner-confirmed attribution only');
|
|
259
339
|
}
|
|
340
|
+
// 11) M4 T1 读回:记忆 brief——save 前空态/回访态含画像字段(与用户当轮冲突以用户为准)
|
|
260
341
|
{
|
|
261
342
|
const brief = variables['motivation_brief'];
|
|
262
|
-
if (typeof brief !== 'function')
|
|
343
|
+
if (typeof brief !== 'function')
|
|
344
|
+
throw new Error('FAIL: motivation_brief 变量未注册');
|
|
263
345
|
const saved = await byName('gotry_motivation_save').execute({
|
|
264
|
-
profile: {
|
|
265
|
-
weights: {
|
|
266
|
-
brief_probe: 0.6
|
|
267
|
-
},
|
|
268
|
-
evidence: [
|
|
269
|
-
'用户原话:smoke 读回探针'
|
|
270
|
-
],
|
|
271
|
-
hard: {
|
|
272
|
-
wake_not_before: '07:15'
|
|
273
|
-
}
|
|
274
|
-
}
|
|
346
|
+
profile: { weights: { brief_probe: 0.6 }, evidence: ['用户原话:smoke 读回探针'], hard: { wake_not_before: '07:15' } },
|
|
275
347
|
}, null);
|
|
276
|
-
if (saved.ok !== true)
|
|
348
|
+
if (saved.ok !== true)
|
|
349
|
+
throw new Error('FAIL: 读回探针画像应保存成功');
|
|
277
350
|
const rendered = brief();
|
|
278
351
|
if (!rendered.includes('brief_probe=') || !rendered.includes('wake_not_before=07:15') || !/证据 [1-9]\d* 条/.test(rendered)) {
|
|
279
352
|
throw new Error(`FAIL: 回访 brief 应含画像字段,实际:${rendered.slice(0, 200)}`);
|
|
280
353
|
}
|
|
281
354
|
console.log('memory read-back: motivation_brief renders profile for returning sessions (empty = first visit)');
|
|
282
355
|
}
|
|
356
|
+
// 12) 会话数据面工具(P3 切片1):官方通道 live + 会话工具 needs-login 合同(隔离 profile,零导航)
|
|
283
357
|
{
|
|
284
|
-
const fa = await byName('gotry_flyai_search').execute({
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
358
|
+
const fa = await byName('gotry_flyai_search').execute({ kind: 'flight', from: '上海', to: '丽江', date: SMOKE_FLIGHT_DATE }, null);
|
|
359
|
+
const faBlocked = fa.verdict === 'error' && /sentinel|block|trial limit/i.test(fa.error ?? '');
|
|
360
|
+
// 端点不可达/超时(出口 IP 被拒或网络抖动)→ 工具以带证据链的 error 终态优雅降级,同样合法
|
|
361
|
+
const faErrTerminal = fa.ok === false && fa.verdict === 'error' && /^flyai-error$/.test(String(fa.via ?? '')) && /\[实时API:flyai@error@/.test(String(fa.evidence ?? ''));
|
|
362
|
+
// 试用额度达限(2026-09-02 迪拜 session):共享配额耗尽是配置态不是代码故障——
|
|
363
|
+
// 归 needs-setup 且 setup 带配 key 指引;断言的是指引合同,不是上游配额可用性
|
|
364
|
+
const faNeedsSetup = fa.ok === false && fa.verdict === 'needs-setup';
|
|
365
|
+
if (faNeedsSetup) {
|
|
366
|
+
if (!/FLYAI_API_KEY/.test(String(fa.setup ?? '')) || !/勿重试|不要重试|请勿重试/.test(String(fa.setup ?? ''))) {
|
|
367
|
+
throw new Error(`FAIL: flyai needs-setup 应带配 key 指引与勿重试明示,实际 setup:${String(fa.setup ?? '').slice(0, 200)}`);
|
|
290
368
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (faBlocked) {
|
|
294
|
-
console.log(' WARN - flyai Sentinel
|
|
295
|
-
}
|
|
369
|
+
console.log(' WARN - flyai 匿名试用额度达限(needs-setup,配置态),setup 指引合同通过(hit 断言跳过)');
|
|
370
|
+
}
|
|
371
|
+
else if (faBlocked) {
|
|
372
|
+
console.log(' WARN - flyai 上游限流中(Sentinel/trial-limit 429),降级合同通过(hit 断言跳过)');
|
|
373
|
+
}
|
|
374
|
+
else if (faErrTerminal) {
|
|
375
|
+
console.log(' WARN - flyai 端点不可达(超时/降级),证据链合同通过(hit 断言跳过)');
|
|
376
|
+
}
|
|
377
|
+
else if (fa.ok !== true || fa.verdict !== 'hit' || (fa.options?.length ?? 0) < 1 || !/\[实时API:flyai@/.test(fa.evidence ?? '')) {
|
|
296
378
|
throw new Error(`FAIL: flyai 工具应 live hit,实际:${JSON.stringify(fa).slice(0, 200)}`);
|
|
297
379
|
}
|
|
380
|
+
// smoke 不得 attach 或读取用户日常 Chrome;显式选择 cdp 诊断车道并把发现目录
|
|
381
|
+
// 指向隔离空目录,确定性验证 needs-attach/no-spend 合同。真实 attach 只走 #21 人在场验收。
|
|
298
382
|
const prof = mkdtempSync(join(smokeRoot, 'sess-'));
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
383
|
+
// 过去日期预校验(issue #24):代码层直接拒绝并指明修正方向,不发上游查询、不产生误导性 miss
|
|
384
|
+
const faPast = await byName('gotry_flyai_search').execute({ kind: 'flight', from: '深圳', to: '普吉', date: '2026-01-01' }, null);
|
|
385
|
+
if (!(faPast.ok === false && /已是过去/.test(String(faPast.summary ?? '')))) {
|
|
386
|
+
throw new Error(`FAIL: flyai 过去日期应代码层预校验拒绝,实际:${JSON.stringify(faPast).slice(0, 200)}`);
|
|
387
|
+
}
|
|
388
|
+
let ss;
|
|
389
|
+
if (!sessionLiveEnabled()) {
|
|
390
|
+
ss = offlineSessionFlightResult();
|
|
391
|
+
console.log(' OFFLINE - GOTRY_SESSION_LIVE!=1: session transport was not invoked');
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
const previousChromeUserDataDir = process.env.CHROME_USER_DATA_DIR;
|
|
395
|
+
process.env.CHROME_USER_DATA_DIR = prof;
|
|
396
|
+
try {
|
|
397
|
+
ss = await byName('gotry_session_search').execute({ from: '上海', to: '丽江', date: SMOKE_FLIGHT_DATE }, null);
|
|
304
398
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
399
|
+
finally {
|
|
400
|
+
if (previousChromeUserDataDir === undefined)
|
|
401
|
+
delete process.env.CHROME_USER_DATA_DIR;
|
|
402
|
+
else
|
|
403
|
+
process.env.CHROME_USER_DATA_DIR = previousChromeUserDataDir;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
rmSync(prof, { recursive: true, force: true });
|
|
407
|
+
// puppeteer-core 可用时应为 needs-attach;缺依赖环境仍以带证据链 error 优雅降级。
|
|
408
|
+
const ssErrTerminal = ss.ok === false && /^session-[a-z0-9-]+-error$/.test(String(ss.via ?? '')) && !!ss.evidence;
|
|
409
|
+
if (!(ss.verdict === 'needs-login' || ss.verdict === 'needs-attach' || ss.verdict === 'hit' || ss.verdict === 'cooldown' || ss.verdict === 'challenged') && !ssErrTerminal) {
|
|
410
|
+
throw new Error(`FAIL: session 工具终态应属 {needs-attach,needs-login,hit,cooldown,challenged} 或带证据的 error 终态,实际:${JSON.stringify(ss).slice(0, 200)}`);
|
|
411
|
+
}
|
|
412
|
+
const faOutcome = faBlocked
|
|
413
|
+
? 'sentinel-限流降级'
|
|
414
|
+
: faErrTerminal
|
|
415
|
+
? '端点不可达降级'
|
|
416
|
+
: `live hit(${fa.options?.length ?? 0} 条)`;
|
|
417
|
+
console.log(`session-face tools: flyai ${faOutcome}; session cdp 隔离门禁=needs-attach`);
|
|
418
|
+
// 酒店平铺接入(2026-08-29):同一 flyai 工具 kind=hotel——live 三合法终态(试用达限 needs-setup / 限流·端点降级 or hit)
|
|
419
|
+
const fh = await byName('gotry_flyai_search').execute({ kind: 'hotel', to: '大理', checkIn: SMOKE_HOTEL_CHECKIN, checkOut: SMOKE_HOTEL_CHECKOUT }, null);
|
|
420
|
+
const fhBlocked = fh.verdict === 'error' && /sentinel|block|trial limit/i.test(fh.error ?? '');
|
|
421
|
+
const fhErrTerminal = fh.ok === false && fh.verdict === 'error' && /^flyai-error$/.test(String(fh.via ?? '')) && /\[实时API:flyai@error@/.test(String(fh.evidence ?? ''));
|
|
422
|
+
const fhNeedsSetup = fh.ok === false && fh.verdict === 'needs-setup';
|
|
423
|
+
if (fhNeedsSetup) {
|
|
424
|
+
if (!/FLYAI_API_KEY/.test(String(fh.setup ?? ''))) {
|
|
425
|
+
throw new Error(`FAIL: flyai hotel needs-setup 应带配 key 指引,实际 setup:${String(fh.setup ?? '').slice(0, 200)}`);
|
|
426
|
+
}
|
|
427
|
+
console.log(' WARN - flyai hotel 试用额度达限(needs-setup,配置态),setup 指引合同通过(hit 断言跳过)');
|
|
428
|
+
}
|
|
429
|
+
else if (fhBlocked || fhErrTerminal) {
|
|
430
|
+
console.log(' WARN - flyai hotel 限流/端点降级,证据链合同通过(hit 断言跳过)');
|
|
431
|
+
}
|
|
432
|
+
else if (fh.ok !== true || fh.verdict !== 'hit' || (fh.hotels?.length ?? 0) < 1 || !/\[实时API:flyai@/.test(fh.evidence ?? '')) {
|
|
433
|
+
throw new Error(`FAIL: flyai hotel 应 live hit,实际:${JSON.stringify(fh).slice(0, 220)}`);
|
|
434
|
+
}
|
|
435
|
+
const fhDest = await byName('gotry_flyai_search').execute({ kind: 'hotel' }, null);
|
|
436
|
+
if (fhDest.ok !== false)
|
|
437
|
+
throw new Error('FAIL: hotel 缺目的地应参数闸拒绝');
|
|
438
|
+
// D-30 迁移锁:legacy blob 形态({query:{...}} 包裹)在宿主权校验即被结构化拒绝——
|
|
439
|
+
// defineTool validateArgs 抛 ToolArgsError,guardToolExecute 兜成 ADR-13 ToolFailure(ok:false+evidence)
|
|
440
|
+
const faLegacy = await byName('gotry_flyai_search').execute({ query: { kind: 'flight', from: '上海', to: '丽江', date: SMOKE_FLIGHT_DATE } }, null);
|
|
441
|
+
if (!(faLegacy.ok === false && !!faLegacy.evidence && /内部错误|参数|schema|violat/i.test(String(faLegacy.summary ?? '')))) {
|
|
442
|
+
throw new Error(`FAIL: flyai legacy blob 形态应被 typed 契约结构化拒绝(ADR-13 ToolFailure),实际:${JSON.stringify(faLegacy).slice(0, 200)}`);
|
|
443
|
+
}
|
|
444
|
+
const faBadEnum = await byName('gotry_flyai_search').execute({ kind: 'plane' }, null);
|
|
445
|
+
if (!(faBadEnum.ok === false && !!faBadEnum.evidence)) {
|
|
446
|
+
throw new Error(`FAIL: flyai kind 枚举外值应被宿主权校验拒绝,实际:${JSON.stringify(faBadEnum).slice(0, 200)}`);
|
|
447
|
+
}
|
|
448
|
+
console.log(' D-30 typed 契约:legacy blob/枚举外值 → 宿主权结构化拒绝(ToolFailure 形状保持)');
|
|
449
|
+
// 会话酒店路由(2026-09-03 实装):未收录城市在 transport 前短路 → error + cityId 指引(offline 零桥零浏览器)
|
|
450
|
+
const sh = await byName('gotry_session_search').execute({ kind: 'hotel', to: '不在码表的城市' }, null);
|
|
451
|
+
if (!(sh.ok === false && /city=/.test(String(sh.summary ?? '')))) {
|
|
452
|
+
throw new Error(`FAIL: 会话酒店未收录城市应带 cityId 指引,实际:${JSON.stringify(sh).slice(0, 200)}`);
|
|
453
|
+
}
|
|
454
|
+
console.log(' hotel kind 路由:未收录城市 → error + cityId 指引(transport 前短路)');
|
|
455
|
+
// 会话火车路由(2026-09-03 实装):未收录电报码在 transport 前短路 → error + 电报码发现指引(offline 零桥零浏览器)
|
|
456
|
+
const st = await byName('gotry_session_search').execute({ kind: 'train', from: '不在码表', to: '也不在', date: '2026-12-01' }, null);
|
|
457
|
+
if (!(st.ok === false && /fromStationTelecode/.test(String(st.summary ?? '')))) {
|
|
458
|
+
throw new Error(`FAIL: 会话火车未收录电报码应带发现指引,实际:${JSON.stringify(st).slice(0, 200)}`);
|
|
459
|
+
}
|
|
460
|
+
console.log(' train kind 路由:未收录电报码 → error + 电报码指引(transport 前短路)');
|
|
461
|
+
// D-30 第二刀迁移锁:session 工具全字段可选(kind 缺省=flight),根 schema 开放无法宿主权拒 blob
|
|
462
|
+
// → interpretArgs 容忍层接住(设计 §4③):blob 归一后照常走业务闸——用 kind=hotel 缺 to 的
|
|
463
|
+
// 参数闸短路出确定性结构化报错(零 transport 零节律闸),证明容忍层归一语义不漂
|
|
464
|
+
const ssLegacy = await byName('gotry_session_search').execute({ query: { kind: 'hotel' } }, null);
|
|
465
|
+
if (!(ssLegacy.ok === false && !('routing' in ssLegacy) && /需要 to/.test(String(ssLegacy.summary ?? '')))) {
|
|
466
|
+
throw new Error(`FAIL: session legacy blob 应经容忍层归一走业务闸(结构化报错,无 routing),实际:${JSON.stringify(ssLegacy).slice(0, 200)}`);
|
|
467
|
+
}
|
|
468
|
+
console.log(' D-30 typed 契约:session legacy blob → 容忍层归一 + 结构化业务报错(全字段可选无法宿主权拒)');
|
|
469
|
+
const fhPast = await byName('gotry_flyai_search').execute({ kind: 'hotel', to: '大理', checkIn: '2026-01-01', checkOut: '2026-01-03' }, null);
|
|
470
|
+
if (!(fhPast.ok === false && /不是未来合法区间/.test(String(fhPast.summary ?? '')))) {
|
|
471
|
+
throw new Error(`FAIL: 酒店过去入住日应代码层预校验拒绝,实际:${JSON.stringify(fhPast).slice(0, 200)}`);
|
|
472
|
+
}
|
|
473
|
+
const fhOutcome = fhNeedsSetup
|
|
474
|
+
? '试用达限(needs-setup)'
|
|
475
|
+
: fhBlocked
|
|
476
|
+
? 'sentinel-限流降级'
|
|
477
|
+
: fhErrTerminal
|
|
478
|
+
? '端点不可达降级'
|
|
479
|
+
: `${fh.hotels?.length ?? 0} 家`;
|
|
480
|
+
console.log(` hotel channel: ${fhOutcome}; 参数闸/过去日闸生效`);
|
|
481
|
+
}
|
|
482
|
+
// 13) 账号会话授权闸(v2,RFC 支柱④进代码):每会话每站点首次弹卡、会话内记住;
|
|
483
|
+
// 总闸 off → fail-closed deny(随时可关);其他工具原样放行。授权完整语义(批准记忆/
|
|
484
|
+
// 拒绝吊销/allow)由 session-tests §I 纯函数覆盖;此处验证闸确实挂上了注册表。
|
|
485
|
+
{
|
|
486
|
+
const gate = preExecutes.at(-1);
|
|
487
|
+
if (!gate)
|
|
488
|
+
throw new Error('FAIL: tools/pre-execute 授权闸未注册');
|
|
489
|
+
const next = async () => ({ kind: 'allow' });
|
|
490
|
+
const ask = await gate({ name: 'gotry_session_search', arguments: { from: '上海' } }, next);
|
|
491
|
+
if (ask.kind !== 'ask' || !/只读检索/.test(String(ask.reason ?? ''))) {
|
|
492
|
+
throw new Error(`FAIL: 会话工具无审批通道时应交 ask(运行时原生结算),实际:${JSON.stringify(ask)}`);
|
|
493
|
+
}
|
|
494
|
+
if (!/携程机票/.test(String(ask.reason ?? ''))) {
|
|
495
|
+
throw new Error(`FAIL: 缺省 kind 必须选择 flight 站点,实际:${JSON.stringify(ask)}`);
|
|
496
|
+
}
|
|
497
|
+
const conflictArgs = { kind: 'flight', query: { kind: 'dida' } };
|
|
498
|
+
const conflict = await gate({
|
|
499
|
+
name: 'gotry_session_search',
|
|
500
|
+
callId: 'conflict-site',
|
|
501
|
+
arguments: conflictArgs,
|
|
502
|
+
}, next);
|
|
503
|
+
if (conflict.kind !== 'ask' || !/Dida 供应商门户/.test(String(conflict.reason ?? ''))) {
|
|
504
|
+
throw new Error(`FAIL: actual registered pre-execute hook must follow query-first selected site,实际:${JSON.stringify(conflict)}`);
|
|
505
|
+
}
|
|
506
|
+
const effectAgent = {};
|
|
507
|
+
const sessionTool = byName('gotry_session_search');
|
|
508
|
+
const pipeline = await gate({
|
|
509
|
+
name: 'gotry_session_search', agent: effectAgent, callId: 'conflict-effect', arguments: conflictArgs,
|
|
510
|
+
}, async () => {
|
|
511
|
+
const result = await sessionTool.execute(conflictArgs, null);
|
|
512
|
+
effectSummaries.push(result && typeof result === 'object' && 'summary' in result ? String(result.summary ?? '') : '');
|
|
513
|
+
return { kind: 'allow' };
|
|
309
514
|
});
|
|
310
|
-
|
|
311
|
-
|
|
515
|
+
const didaEffects = selectedEffects.filter(effect => effect === 'SESSION_DIDA_SEARCH');
|
|
516
|
+
if (pipeline.kind !== 'allow' || approvalReasons.length !== 1 || didaEffects.length !== 1 || didaEffects[0] !== 'SESSION_DIDA_SEARCH' || !effectSummaries[0]?.startsWith('Dida 门户实时价')) {
|
|
517
|
+
throw new Error(`FAIL: pre-execute→approval→selected effect fixture 未锁定 Dida,实际:${JSON.stringify({ pipeline, approvalReasons, selectedEffects, effectSummaries })}`);
|
|
312
518
|
}
|
|
313
|
-
console.log(`
|
|
519
|
+
console.log(`consent pipeline fixture: query-first conflict → approval=${approvalReasons.length}, effect=${selectedEffects[0]}, summary=${effectSummaries[0]}`);
|
|
520
|
+
const pass = await gate({ name: 'gotry_anything_search' }, next);
|
|
521
|
+
if (pass.kind !== 'allow')
|
|
522
|
+
throw new Error(`FAIL: 非会话工具应原样放行,实际:${JSON.stringify(pass)}`);
|
|
523
|
+
cfg.sessionAccess = 'off';
|
|
524
|
+
const deny = await gate({ name: 'gotry_session_search', arguments: { kind: 'flight' } }, next);
|
|
525
|
+
if (deny.kind !== 'deny' || !/sessionAccess=off/.test(String(deny.reason ?? ''))) {
|
|
526
|
+
throw new Error(`FAIL: sessionAccess=off 应 fail-closed deny,实际:${JSON.stringify(deny)}`);
|
|
527
|
+
}
|
|
528
|
+
const trainDeny = await gate({ name: 'gotry_session_search', arguments: { kind: 'train' } }, next);
|
|
529
|
+
if (trainDeny.kind !== 'deny' || !/sessionAccess=off/.test(String(trainDeny.reason ?? ''))) {
|
|
530
|
+
throw new Error(`FAIL: sessionAccess=off 不得绕过 train,实际:${JSON.stringify(trainDeny)}`);
|
|
531
|
+
}
|
|
532
|
+
cfg.sessionAccess = 'ask';
|
|
533
|
+
console.log('consent gate: session tool → approval-card ask(每会话一次/拒绝即会话内吊销); off → fail-closed deny; other tools pass through');
|
|
314
534
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
535
|
+
// 14) 登录引导产品工具(gotry_session_login,第 18 工具):注册 + 「登录在外部网站完成、
|
|
536
|
+
// gotry 不经手任何凭证」红线钉在描述里;不真调 execute(测试纪律:不开用户浏览器/不 attach)
|
|
537
|
+
{
|
|
538
|
+
const lo = byName('gotry_session_login');
|
|
539
|
+
if (typeof lo.presentResult !== 'function')
|
|
540
|
+
throw new Error('FAIL: gotry_session_login 缺 presentResult');
|
|
541
|
+
const desc = String(lo.description ?? '');
|
|
542
|
+
if (!/NEVER collects, stores, or transmits credentials/.test(desc)) {
|
|
543
|
+
throw new Error(`FAIL: 登录工具描述缺「不经手凭证」语义红线,实际 ${desc.slice(0, 120)}`);
|
|
544
|
+
}
|
|
545
|
+
console.log('login tool: registered; 登录=外部网站+用户自己的浏览器,gotry 只读票据名(0 值过手)语义钉死');
|
|
546
|
+
}
|
|
547
|
+
// 15) 产物面(issue #25):账本工单 + cwd md 可发现;read 行号窗口;dsh read 卡字段;越界/类型护栏
|
|
548
|
+
{
|
|
549
|
+
const { writeFileSync } = await import('node:fs');
|
|
550
|
+
const { listArtifacts, readArtifact } = await import('../capabilities/artifacts.js');
|
|
551
|
+
const { ensureLedger } = await import('../src/state-ledger.js');
|
|
552
|
+
const cwdDir = mkdtempSync(join(tmpdir(), 'gotry-artifacts-cwd-'));
|
|
553
|
+
const twelve = Array.from({ length: 12 }, (_, i) => `第 ${i + 1} 行`).join('\n');
|
|
554
|
+
writeFileSync(join(cwdDir, 'trip-2027-probe.md'), `# 行程·产物探针\n${twelve}\n`);
|
|
555
|
+
// 账本权威路径:种一个已交付工单(隔离 stateRoot,不碰真实 gotry-state)
|
|
556
|
+
const ledger = ensureLedger(smokeRoot);
|
|
557
|
+
ledger.createWorkflowRun({ id: 'art-probe-1', goal: '行程·产物探针(账本)', ticket: { objective: 'probe' }, state: {} });
|
|
558
|
+
ledger.settleWorkflowRun('art-probe-1', '# 交付·账本权威\nD1 大理\nD2 洱海');
|
|
559
|
+
const listTool = byName('gotry_artifacts_list');
|
|
560
|
+
const artifactExec = { agent: { session: { header: { cwd: cwdDir } } } };
|
|
561
|
+
const ledList = await listTool.execute({}, artifactExec);
|
|
562
|
+
const run = ledList.artifacts?.find(a => a.id === 'art-probe-1');
|
|
563
|
+
if (!ledList.ok || !run || run.source !== 'async-run' || run.status !== 'settled') {
|
|
564
|
+
throw new Error(`FAIL: artifacts list 应发现账本已交付工单,实际:${JSON.stringify(ledList).slice(0, 200)}`);
|
|
565
|
+
}
|
|
566
|
+
const direct = await listArtifacts({ stateRoot: smokeRoot, cwd: cwdDir });
|
|
567
|
+
if (!direct.artifacts.some(a => a.source === 'cwd-file' && a.id === 'trip-2027-probe.md')) {
|
|
568
|
+
throw new Error(`FAIL: artifacts list 应发现 cwd 顶层 md,实际:${JSON.stringify(direct.artifacts.map(a => a.id))}`);
|
|
569
|
+
}
|
|
570
|
+
const readTool = byName('gotry_artifacts_read');
|
|
571
|
+
const r1 = await readTool.execute({ path: 'art-probe-1' }, artifactExec);
|
|
572
|
+
if (!r1.ok || r1.lines?.[0]?.number !== 1 || !r1.lines?.[0]?.text.includes('交付·账本权威') || r1.lang !== 'markdown') {
|
|
573
|
+
throw new Error(`FAIL: 裸工单 id 应从账本读出行号视图,实际:${JSON.stringify(r1).slice(0, 200)}`);
|
|
574
|
+
}
|
|
575
|
+
const readOutput = readTool.output;
|
|
576
|
+
const view = readTool.presentResult?.({ path: 'art-probe-1' }, {
|
|
577
|
+
isError: false,
|
|
578
|
+
content: readOutput?.render?.({ path: 'art-probe-1' }, r1) ?? [],
|
|
579
|
+
meta: readOutput?.presentationMeta?.({ path: 'art-probe-1' }, r1),
|
|
580
|
+
});
|
|
581
|
+
if (view?.card !== 'read' || view.path !== r1.path || view.offset !== 1 || view.lines?.length !== r1.lines?.length || view.totalLines !== r1.totalLines || view.lang !== 'markdown') {
|
|
582
|
+
throw new Error(`FAIL: read 卡字段不齐,实际:${JSON.stringify(view).slice(0, 200)}`);
|
|
583
|
+
}
|
|
584
|
+
const r2 = await readArtifact({ stateRoot: smokeRoot, cwd: cwdDir, path: 'trip-2027-probe.md', offset: 10, limit: 5 });
|
|
585
|
+
if (!r2.ok || r2.lines[0].number !== 10 || r2.windowed !== false) {
|
|
586
|
+
throw new Error(`FAIL: 行窗口 12 行文件 offset=10 应从行号 10 起,实际:${JSON.stringify(r2.ok ? { o: r2.offset, first: r2.lines[0]?.number } : r2)}`);
|
|
587
|
+
}
|
|
588
|
+
if (r2.lines.length !== 5)
|
|
589
|
+
throw new Error(`FAIL: 14 行文件 offset=10/limit=5 窗口应恰 5 行(10-14),实际 ${r2.lines.length}`);
|
|
590
|
+
const badPath = await readArtifact({ stateRoot: smokeRoot, cwd: cwdDir, path: '../../../../../etc/passwd' });
|
|
591
|
+
if (badPath.ok)
|
|
592
|
+
throw new Error('FAIL: 越界路径必须被拒');
|
|
593
|
+
const badExt = await readArtifact({ stateRoot: smokeRoot, cwd: cwdDir, path: 'gotry-state/gotry-state.db' });
|
|
594
|
+
if (badExt.ok)
|
|
595
|
+
throw new Error('FAIL: 白名单外扩展名(.db)必须被拒');
|
|
596
|
+
// 15b) dsh Host presenter 契约(issue #285):
|
|
597
|
+
// - list 的 presentResult 是 SearchPathsResultView(card:'search' shape:'paths');
|
|
598
|
+
// DSH Web 的 public Client adapter separately consumes the runtime block;
|
|
599
|
+
// - read 的 presentCall 加 locations:[{path,line:1}],editor 视图在 call 阶段就 follow-along;
|
|
600
|
+
// - read 的 presentResult 是 ReadResultView(path/offset/lines/totalLines/lang 齐全),
|
|
601
|
+
// Host content[0] 是 source 身份行(显示「从哪个产物读」,避免把旧摘要当新内容)。
|
|
602
|
+
const listPayload = await listTool.execute({ limit: 5 }, artifactExec);
|
|
603
|
+
const listOutput = listTool.output;
|
|
604
|
+
const listView = listTool.presentResult?.({}, {
|
|
605
|
+
isError: false,
|
|
606
|
+
content: listOutput?.render?.({}, listPayload) ?? [],
|
|
607
|
+
meta: listOutput?.presentationMeta?.({}, listPayload),
|
|
608
|
+
});
|
|
609
|
+
if (listView?.card !== 'search' || listView?.shape !== 'paths') {
|
|
610
|
+
throw new Error(`FAIL: list 卡片应为 card='search' shape='paths',实际:${JSON.stringify({ card: listView?.card, shape: listView?.shape }).slice(0, 200)}`);
|
|
611
|
+
}
|
|
612
|
+
if (!Array.isArray(listView.paths) || listView.paths.length !== (listPayload.artifacts?.length ?? 0)) {
|
|
613
|
+
throw new Error(`FAIL: list paths 长度应等于 artifacts 长度,实际 ${listView.paths?.length} vs ${listPayload.artifacts?.length}`);
|
|
614
|
+
}
|
|
615
|
+
if (listView.truncated !== Boolean(listPayload.truncated) || listView.total !== (listPayload.total ?? 0)) {
|
|
616
|
+
throw new Error(`FAIL: list truncated/total 应与 execute 返回一致,实际 truncated=${listView.truncated} total=${listView.total} vs ${listPayload.truncated}/${listPayload.total}`);
|
|
617
|
+
}
|
|
618
|
+
const readCall = readTool.presentCall?.({ path: 'trip-2027-probe.md' });
|
|
619
|
+
if (readCall?.kind !== 'read' || !Array.isArray(readCall.locations) || readCall.locations[0]?.path !== 'trip-2027-probe.md') {
|
|
620
|
+
throw new Error(`FAIL: read presentCall 应 kind='read' + locations[0].path='trip-2027-probe.md',实际:${JSON.stringify(readCall).slice(0, 200)}`);
|
|
621
|
+
}
|
|
622
|
+
// 复用 r1(账本已交付工单,绝对 path),断言 read 卡片字段 + identity 源行
|
|
623
|
+
const readView = readTool.presentResult?.({ path: 'art-probe-1' }, {
|
|
624
|
+
isError: false,
|
|
625
|
+
content: readOutput?.render?.({ path: 'art-probe-1' }, r1) ?? [],
|
|
626
|
+
meta: readOutput?.presentationMeta?.({ path: 'art-probe-1' }, r1),
|
|
627
|
+
});
|
|
628
|
+
if (readView?.card !== 'read' || readView.path !== r1.path || readView.totalLines !== r1.totalLines || readView.lang !== 'markdown') {
|
|
629
|
+
throw new Error(`FAIL: read 卡片字段不齐,实际:${JSON.stringify(readView).slice(0, 200)}`);
|
|
630
|
+
}
|
|
631
|
+
const identityText = readView.content?.[0]?.text ?? '';
|
|
632
|
+
if (!identityText.includes('source:') || !identityText.includes(r1.path ?? '')) {
|
|
633
|
+
throw new Error(`FAIL: read fallback content[0] 应含 source 身份行 + 完整 path,实际:${identityText.slice(0, 200)}`);
|
|
634
|
+
}
|
|
635
|
+
rmSync(cwdDir, { recursive: true, force: true });
|
|
636
|
+
console.log(`artifacts: ledger run + cwd md discovered; read window(${r2.ok ? r2.lines.length : '?'} lines @10) + read card; list→SearchPathsResultView + read locations + source identity; path/ext guardrails hold`);
|
|
637
|
+
}
|
|
638
|
+
// 16) 产物事实闸(issue #46,第 21 工具):事实落账(hit 正事实 + miss 负事实,隔离 stateRoot)
|
|
639
|
+
// → 闸内回溯:已验证措辞 pass;exact-date miss 的 route+date 被航班号填充 = blocked
|
|
640
|
+
{
|
|
641
|
+
const { appendFacts, loadFactRegistry } = await import('../capabilities/fact-log.js');
|
|
642
|
+
const { factsFromFlyai } = await import('../src/bookable-facts.js');
|
|
643
|
+
// 与生产同路径:事实落账即归一到 IATA 空间(index.ts 传 city_alias;闸侧 claim 也归一,两边才可比)
|
|
644
|
+
const avMap = JSON.parse(await readFile(join(import.meta.dirname, '..', '..', 'data', 'airline-airports.json'), 'utf-8'));
|
|
645
|
+
const fetchedAt = '2026-08-29T00:00:00.000Z';
|
|
646
|
+
await appendFacts(smokeRoot, [
|
|
647
|
+
...factsFromFlyai({ kind: 'flight', origin: '香港', destination: '普吉', date: '2027-07-18' }, { verdict: 'hit', options: [{ no: 'CX771', name: '国泰航空', depDateTime: '2027-07-18T08:05:00+08:00', arrDateTime: '2027-07-18T10:35:00+07:00', price: 1234 }], evidence: '' }, fetchedAt, avMap.city_alias),
|
|
648
|
+
...factsFromFlyai({ kind: 'flight', origin: '深圳', destination: '普吉', date: '2027-07-16' }, { verdict: 'miss', options: [], evidence: '' }, fetchedAt, avMap.city_alias),
|
|
649
|
+
]);
|
|
650
|
+
const registry = await loadFactRegistry(smokeRoot);
|
|
651
|
+
// §12 的 flyai live 检索也会落账(接线正确性的副产品)——按本次探针的 query_id 过滤
|
|
652
|
+
const probeFacts = registry.filter((f) => f.kind !== 'policy' && (f.query_id.endsWith(':2027-07-16') || f.query_id.endsWith(':2027-07-18')));
|
|
653
|
+
if (probeFacts.length !== 2 || !probeFacts.some(f => f.bookability === 'unavailable_exact_date') || !probeFacts.some(f => f.bookability === 'bookable_exact_date')) {
|
|
654
|
+
throw new Error(`FAIL: 事实侧车应落 1 正 1 负两条,实际:${JSON.stringify(probeFacts.map(f => f.bookability))}`);
|
|
655
|
+
}
|
|
656
|
+
const gate = byName('gotry_fact_gate');
|
|
657
|
+
const passMd = ['# 行程片段', '## D3 7.18 香港 → 普吉', '- 国泰航空 CX771 08:05→10:35(已按 exact-date 检索记录)'].join('\n');
|
|
658
|
+
const pass = await gate.execute({ markdown: passMd, tripYear: 2027 }, null);
|
|
659
|
+
if (pass.verdict !== 'pass' || pass.presentation !== 'verified_itinerary_allowed') {
|
|
660
|
+
throw new Error(`FAIL: 可回溯产物应 pass,实际:${JSON.stringify(pass).slice(0, 300)}`);
|
|
661
|
+
}
|
|
662
|
+
const badMd = ['# 行程片段', '## D1 7.16 深圳 → 普吉', '- 香港快运 UO784 10:05→12:40 直飞 ✓'].join('\n');
|
|
663
|
+
const blocked = await gate.execute({ markdown: badMd, tripYear: 2027 }, null);
|
|
664
|
+
if (blocked.verdict !== 'blocked' || !blocked.violations?.some(v => v.kind === 'not_in_source')) {
|
|
665
|
+
throw new Error(`FAIL: exact-date miss 被 UO784 填充必须 blocked(not_in_source),实际:${JSON.stringify(blocked).slice(0, 300)}`);
|
|
666
|
+
}
|
|
667
|
+
const view = gate.presentResult?.({}, blocked);
|
|
668
|
+
if (!view?.title?.includes('blocked'))
|
|
669
|
+
throw new Error(`FAIL: blocked 呈现卡标题应含 blocked,实际:${view?.title}`);
|
|
670
|
+
console.log(`fact gate: registry 1+1(hit/miss);verified 措辞 pass;miss 填充 UO784 blocked(not_in_source) + 呈现卡`);
|
|
671
|
+
// 16b) #299 FlyAI train 的真实注册工具 seam:
|
|
672
|
+
// gotry_flyai_search execute → factsFromFlyai → appendFacts → loadFactRegistry
|
|
673
|
+
// → canonical renderer(anchor) → gotry_fact_gate execute;不触碰真实 provider。
|
|
674
|
+
const flyaiTrain = byName('gotry_flyai_search');
|
|
675
|
+
const searchedTrain = await flyaiTrain.execute({ kind: 'train', from: '上海', to: '昆明', date: '2027-11-11' }, null);
|
|
676
|
+
if (searchedTrain.kind !== 'train' || searchedTrain.verdict !== 'hit') {
|
|
677
|
+
throw new Error(`FAIL: FlyAI train fixture 应经注册工具返回 hit,实际:${JSON.stringify(searchedTrain).slice(0, 300)}`);
|
|
678
|
+
}
|
|
679
|
+
const trainRegistry = (await loadFactRegistry(smokeRoot)).filter((f) => f.kind === 'train' && f.query_id === 'flyai:train:上海-昆明:2027-11-11');
|
|
680
|
+
if (trainRegistry.length !== 1 || trainRegistry[0].bookability !== 'bookable_exact_date' || trainRegistry[0].source !== 'flyai') {
|
|
681
|
+
throw new Error(`FAIL: FlyAI train fixture 应经真实工具 seam 落 typed fact,实际:${JSON.stringify(trainRegistry).slice(0, 500)}`);
|
|
682
|
+
}
|
|
683
|
+
const trainMarkdown = `# 车次片段\n## D1 11.11 上海 → 昆明\n${renderFlightFact(trainRegistry[0])}`;
|
|
684
|
+
if (!trainMarkdown.includes(`<!-- fact:${trainRegistry[0].fact_id} -->`)
|
|
685
|
+
|| !trainMarkdown.includes('车次')
|
|
686
|
+
|| trainMarkdown.includes('直飞')
|
|
687
|
+
|| trainMarkdown.includes('¥')
|
|
688
|
+
|| trainMarkdown.includes('价待询')) {
|
|
689
|
+
throw new Error(`FAIL: train canonical renderer 应只携带车次语义与 typed anchor,不得直飞/价格:${trainMarkdown}`);
|
|
690
|
+
}
|
|
691
|
+
const trainGate = await gate.execute({ markdown: trainMarkdown, tripYear: 2027 }, null);
|
|
692
|
+
if (trainGate.verdict !== 'pass' || (trainGate.violations?.length ?? 0) !== 0) {
|
|
693
|
+
throw new Error(`FAIL: FlyAI train canonical anchor 经 gotry_fact_gate 应 pass,实际:${JSON.stringify(trainGate).slice(0, 500)}`);
|
|
694
|
+
}
|
|
695
|
+
console.log('fact gate: registered FlyAI train → typed log → canonical anchor → gotry_fact_gate pass');
|
|
696
|
+
// 16c) #355 registered gotry_session_search seam: typed outcome → isolated
|
|
697
|
+
// bookable-facts bytes → existing canonical rail anchor → fact gate.
|
|
698
|
+
const sessionTrain = byName('gotry_session_search');
|
|
699
|
+
const trainHit = await sessionTrain.execute({ kind: 'train', from: '上海', to: '昆明', date: '2027-11-11' }, null);
|
|
700
|
+
const trainEmpty = await sessionTrain.execute({ kind: 'train', from: '上海', to: '昆明', date: '2027-11-12' }, null);
|
|
701
|
+
const trainMalformed = await sessionTrain.execute({ kind: 'train', from: '上海', to: '昆明', date: '2027-11-13' }, null);
|
|
702
|
+
const trainTransport = await sessionTrain.execute({ kind: 'train', from: '上海', to: '昆明', date: '2027-11-14' }, null);
|
|
703
|
+
const trainTampered = await sessionTrain.execute({ kind: 'train', from: '上海', to: '昆明', date: '2027-11-15' }, null);
|
|
704
|
+
if (trainHit.ok !== true || trainHit.verdict !== 'hit' || trainHit.outcome?.kind !== 'recognized-nonempty' || !trainHit.collection?.queryId || (trainHit.trains?.length ?? 0) !== 1) {
|
|
705
|
+
throw new Error(`FAIL: registered session train hit typed result,实际:${JSON.stringify(trainHit).slice(0, 600)}`);
|
|
706
|
+
}
|
|
707
|
+
if (trainEmpty.ok !== true || trainEmpty.verdict !== 'miss' || trainEmpty.outcome?.kind !== 'recognized-empty') {
|
|
708
|
+
throw new Error(`FAIL: registered session train genuine empty,实际:${JSON.stringify(trainEmpty).slice(0, 400)}`);
|
|
709
|
+
}
|
|
710
|
+
if (trainMalformed.ok !== false || trainMalformed.verdict !== 'error' || trainMalformed.outcome?.kind !== 'malformed') {
|
|
711
|
+
throw new Error(`FAIL: registered session train malformed,实际:${JSON.stringify(trainMalformed).slice(0, 400)}`);
|
|
712
|
+
}
|
|
713
|
+
if (trainTransport.ok !== false || trainTransport.verdict !== 'error' || trainTransport.outcome?.kind !== 'transport-runtime-error') {
|
|
714
|
+
throw new Error(`FAIL: registered session train transport error,实际:${JSON.stringify(trainTransport).slice(0, 400)}`);
|
|
715
|
+
}
|
|
716
|
+
if (trainTampered.ok !== true || trainTampered.verdict !== 'hit' || trainTampered.outcome?.kind !== 'recognized-nonempty'
|
|
717
|
+
|| trainTampered.collection?.requested?.date !== '2027-11-14' || (trainTampered.trains?.length ?? 0) !== 1) {
|
|
718
|
+
throw new Error(`FAIL: tampered collection fixture should remain hit-shaped but carry mismatched binding,实际:${JSON.stringify(trainTampered).slice(0, 600)}`);
|
|
719
|
+
}
|
|
720
|
+
const sessionFactBytes = await readFile(join(smokeRoot, 'gotry-state', 'bookable-facts.jsonl'), 'utf8');
|
|
721
|
+
const sessionFacts = sessionFactBytes.split('\n').filter(Boolean).map(line => JSON.parse(line));
|
|
722
|
+
const sessionTrainFacts = sessionFacts.filter(f => f.source === 'session:12306-train');
|
|
723
|
+
if (sessionTrainFacts.length !== 2
|
|
724
|
+
|| !sessionTrainFacts.some(f => f.query_id === trainHit.collection.queryId && f.bookability === 'bookable_exact_date' && f.kind === 'train')
|
|
725
|
+
|| !sessionTrainFacts.some(f => f.query_id === 'session:12306-train:fixture-2027-11-12' && f.bookability === 'unavailable_exact_date')
|
|
726
|
+
|| sessionFacts.some(f => /2027-11-13|2027-11-14|2027-11-15/.test(f.query_id))) {
|
|
727
|
+
throw new Error(`FAIL: session train isolated fact bytes 应仅含 hit+empty,实际:${JSON.stringify(sessionTrainFacts).slice(0, 900)}`);
|
|
728
|
+
}
|
|
729
|
+
const sessionHitFact = sessionTrainFacts.find(f => f.query_id === trainHit.collection.queryId);
|
|
730
|
+
const sessionMarkdown = `# 车次片段\n## 11.11 上海 → 昆明\n${renderFlightFact(sessionHitFact)}`;
|
|
731
|
+
if (!sessionMarkdown.includes(`<!-- fact:${sessionHitFact.fact_id} -->`) || !sessionMarkdown.includes('车次') || sessionMarkdown.includes('直飞') || sessionMarkdown.includes('¥') || sessionMarkdown.includes('价待询')) {
|
|
732
|
+
throw new Error(`FAIL: session train canonical anchored line,实际:${sessionMarkdown}`);
|
|
733
|
+
}
|
|
734
|
+
const sessionGate = await gate.execute({ markdown: sessionMarkdown, tripYear: 2027 }, null);
|
|
735
|
+
if (sessionGate.verdict !== 'pass' || (sessionGate.violations?.length ?? 0) !== 0) {
|
|
736
|
+
throw new Error(`FAIL: session train anchor 经 gotry_fact_gate 应 pass,实际:${JSON.stringify(sessionGate).slice(0, 700)}`);
|
|
737
|
+
}
|
|
738
|
+
console.log(`fact gate: registered session train previous-origin-date hit/miss/malformed/transport/tampered-binding → ${sessionTrainFacts.length} isolated facts → anchored canonical line → pass`);
|
|
739
|
+
}
|
|
740
|
+
// 17) LLM_MODEL 会话面覆盖(issue #77):未设 GOTRY_LLM_MODEL 不挂监听(默认路径
|
|
741
|
+
// 面不变);设了则 agent/request 瀑布 post-next 覆盖 provider/model 并清掉继承
|
|
742
|
+
// effort——用户层(~/.dsh)选择被显式 .env 意图压过,内存态零持久化
|
|
743
|
+
{
|
|
744
|
+
// 17a 未设环境变量 → 不挂监听(默认面不变)
|
|
745
|
+
const none = [];
|
|
746
|
+
const ctxNone = { on: (_e, fn) => { none.push(fn); } };
|
|
747
|
+
if (installModelOverride(ctxNone, {}) !== false || none.length !== 0) {
|
|
748
|
+
throw new Error('FAIL: 未设 GOTRY_LLM_MODEL 时不得挂 agent/request 覆盖');
|
|
749
|
+
}
|
|
750
|
+
// 17b 无事件总线的极简宿主 → false 而非抛错
|
|
751
|
+
if (installModelOverride({}, { GOTRY_LLM_MODEL: 'MiniMax-M2' }) !== false) {
|
|
752
|
+
throw new Error('FAIL: 无事件总线宿主应返回 false 而非抛错');
|
|
753
|
+
}
|
|
754
|
+
// 17c 设定 → 覆盖生效且清继承 effort(其余请求字段原样保留)
|
|
755
|
+
const listeners = [];
|
|
756
|
+
const ctxOn = { on: (_e, fn) => { listeners.push(fn); } };
|
|
757
|
+
if (installModelOverride(ctxOn, { GOTRY_LLM_MODEL: 'MiniMax-M2' }) !== true || listeners.length !== 1) {
|
|
758
|
+
throw new Error('FAIL: 设定 GOTRY_LLM_MODEL 后应挂恰好一条 agent/request 覆盖');
|
|
759
|
+
}
|
|
760
|
+
const inner = async () => ({ provider: 'deepseek-official', model: 'glm-5.3-flash', reasoningEffort: 'high', maxTokens: 4096 });
|
|
761
|
+
const overridden = await listeners[0](null, inner);
|
|
762
|
+
if (overridden.model !== 'MiniMax-M2' || overridden.provider !== 'deepseek-official'
|
|
763
|
+
|| 'reasoningEffort' in overridden || overridden.maxTokens !== 4096) {
|
|
764
|
+
throw new Error(`FAIL: 覆盖结果不符预期:${JSON.stringify(overridden)}`);
|
|
765
|
+
}
|
|
766
|
+
console.log('model override: 未设不挂/无总线不抛/设定后压过用户层选择(glm-5.3-flash→MiniMax-M2)且清继承 effort');
|
|
767
|
+
}
|
|
768
|
+
rmSync(smokeRoot, { recursive: true, force: true });
|
|
319
769
|
console.log(`smoke state cleaned: ${smokeRoot}`);
|
|
320
770
|
console.log('\nSMOKE OK');
|
|
321
771
|
}
|
|
322
|
-
main().catch((e)=>{
|
|
323
|
-
console.error(e);
|
|
324
|
-
process.exit(1);
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
//# sourceURL=/Users/bytedance/work/gotry/ts/scripts/smoke.ts
|
|
772
|
+
main().catch((e) => { console.error(e); process.exit(1); });
|