@danceiny/gotry 0.0.1-rc.9 → 0.2.0-rc.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -219
- package/README.zh-CN.md +222 -0
- package/bin/gotry-backend.js +53 -0
- package/bin/gotry-booking-copilot.js +53 -0
- package/bin/gotry-bootstrap.js +1744 -0
- package/bin/gotry-inner.js +733 -83
- package/bin/gotry-process-liveness.d.ts +27 -0
- package/bin/gotry-process-liveness.js +97 -0
- package/bin/gotry-runtime-resolution.d.ts +27 -0
- package/bin/gotry-runtime-resolution.js +49 -0
- package/bin/gotry.js +1 -1
- package/client/client.js +203 -0
- package/cordis.gotry-patch.yml +80 -56
- package/data/airline-airports.json +39 -0
- package/data/stations-12306-verify.json +524 -0
- package/dist/capabilities/agent-reach-deep.js +45 -122
- package/dist/capabilities/agent-reach.js +101 -146
- package/dist/capabilities/anything.js +135 -94
- package/dist/capabilities/artifacts.js +370 -0
- package/dist/capabilities/channel-health.js +131 -0
- package/dist/capabilities/channel-registry.js +256 -0
- package/dist/capabilities/doctor.js +546 -0
- package/dist/capabilities/effect.js +394 -0
- package/dist/capabilities/external-event.js +149 -0
- package/dist/capabilities/fact-log.js +55 -0
- package/dist/capabilities/flyai-config.js +337 -0
- package/dist/capabilities/flyai.js +947 -110
- package/dist/capabilities/fx-contract.js +293 -0
- package/dist/capabilities/geo-atlas.js +246 -0
- package/dist/capabilities/ground-transfer.js +509 -0
- package/dist/capabilities/hbcli.js +260 -123
- package/dist/capabilities/hotelbyte-transaction.js +194 -0
- package/dist/capabilities/incident-log.js +131 -61
- package/dist/capabilities/itinerary-artifact.js +190 -0
- package/dist/capabilities/itinerary-deck-artifact.js +194 -0
- package/dist/capabilities/itinerary-deck-export.js +339 -0
- package/dist/capabilities/lavish-local.js +924 -0
- package/dist/capabilities/model-override.js +38 -0
- package/dist/capabilities/opensky.js +54 -43
- package/dist/capabilities/resilience.js +191 -0
- package/dist/capabilities/session/action-cache.js +50 -44
- package/dist/capabilities/session/adapters/ctrip-flight.js +120 -62
- package/dist/capabilities/session/adapters/ctrip-hotel.js +222 -0
- package/dist/capabilities/session/adapters/dida-portal.js +274 -0
- package/dist/capabilities/session/adapters/meituan-local.js +35 -54
- package/dist/capabilities/session/adapters/rail-12306.js +342 -0
- package/dist/capabilities/session/benchmark.js +189 -0
- package/dist/capabilities/session/bridge-ledger.js +224 -0
- package/dist/capabilities/session/extension-bridge.js +636 -0
- package/dist/capabilities/session/extension-channel.js +109 -0
- package/dist/capabilities/session/extension-distribution.js +230 -0
- package/dist/capabilities/session/extract.js +31 -21
- package/dist/capabilities/session/golden-score.js +114 -0
- package/dist/capabilities/session/health-watch.js +215 -0
- package/dist/capabilities/session/read-guard.js +75 -24
- package/dist/capabilities/session/static-flight-golden.js +150 -0
- package/dist/capabilities/session/transport.js +97 -83
- package/dist/capabilities/session/wizard.js +117 -0
- package/dist/capabilities/session-consent.js +178 -0
- package/dist/capabilities/session-login.js +170 -0
- package/dist/capabilities/session-search.js +635 -44
- package/dist/capabilities/spawn-bounded.js +203 -0
- package/dist/capabilities/sponsor-plugin.js +341 -0
- package/dist/capabilities/visa-policy.js +140 -0
- package/dist/capabilities/weather.js +243 -138
- package/dist/data/session-golden-20.json +25 -0
- package/dist/data/sf-golden-manifest.json +103 -0
- package/dist/data/sf-static-routes.json +91 -0
- package/dist/scripts/action-cache-tests.js +24 -59
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +242 -0
- package/dist/scripts/agent-planning-turn-deadline-tests.js +185 -0
- package/dist/scripts/agent-reach-deep-tests.js +30 -40
- package/dist/scripts/agent-reach-error-tests.js +354 -0
- package/dist/scripts/agent-reach-tests.js +26 -18
- package/dist/scripts/agent-reach-wrapper-tests.js +50 -50
- package/dist/scripts/anything-tests.js +116 -60
- package/dist/scripts/artifact-client-contract-tests.js +220 -0
- package/dist/scripts/artifacts-capability-tests.js +727 -0
- package/dist/scripts/async-collect.js +50 -11
- package/dist/scripts/benchmark-environment-bridge-e2e.js +741 -0
- package/dist/scripts/benchmark-environment-bridge-tests.js +1452 -0
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +126 -0
- package/dist/scripts/booking-copilot-availability-policy-tests.js +265 -0
- package/dist/scripts/booking-copilot-bin-proof-tests.js +66 -0
- package/dist/scripts/booking-copilot-cleanup-proof-helper.js +76 -0
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +144 -0
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +424 -0
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +2839 -0
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +309 -0
- package/dist/scripts/booking-copilot-dsh-readiness-proof.js +238 -0
- package/dist/scripts/booking-copilot-dsh-warmer-proof.js +123 -0
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +156 -0
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +70 -0
- package/dist/scripts/booking-copilot-intent-proof-tests.js +422 -0
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +184 -0
- package/dist/scripts/booking-copilot-planner-live-probe.js +95 -0
- package/dist/scripts/booking-copilot-proof-runtime.js +42 -0
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +210 -0
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +2349 -0
- package/dist/scripts/booking-copilot-server-proof-tests.js +784 -0
- package/dist/scripts/booking-copilot-startup-proof-tests.js +94 -0
- package/dist/scripts/booking-executor-tests.js +80 -0
- package/dist/scripts/booking-recovery-chain-tests.js +182 -0
- package/dist/scripts/booking-saga-tests.js +125 -0
- package/dist/scripts/booking-surface-contract-proof-tests.js +212 -0
- package/dist/scripts/bootstrap-tests.js +1479 -0
- package/dist/scripts/bridge-events-tests.js +239 -0
- package/dist/scripts/bridge-ledger-tests.js +428 -0
- package/dist/scripts/build-changelog.js +238 -0
- package/dist/scripts/build-metrics-report.js +331 -0
- package/dist/scripts/changelog-tests.js +118 -0
- package/dist/scripts/channel-probe-tests.js +103 -0
- package/dist/scripts/channel-probe.js +169 -0
- package/dist/scripts/channel-registry-tests.js +208 -0
- package/dist/scripts/companion-tests.js +25 -70
- package/dist/scripts/dida-cookie-probe.js +87 -0
- package/dist/scripts/dida-runner-stop-tests.js +311 -0
- package/dist/scripts/dida-sw-probe.js +114 -0
- package/dist/scripts/diff-test.js +17 -12
- package/dist/scripts/doctor-tests.js +564 -0
- package/dist/scripts/dsh-artifact-e2e.js +279 -0
- package/dist/scripts/dsh-artifact-web-e2e.js +675 -0
- package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
- package/dist/scripts/dsh-runtime-closure-tests.js +182 -0
- package/dist/scripts/dsh-runtime-closure.js +162 -0
- package/dist/scripts/dsh-session-v3-migration-proof.js +109 -0
- package/dist/scripts/dsh-subprocess-local-proof.js +150 -0
- package/dist/scripts/dsh-target-closure-proof.js +178 -0
- package/dist/scripts/effect-tests.js +539 -0
- package/dist/scripts/engine-run.js +1 -3
- package/dist/scripts/engine-tests.js +16 -10
- package/dist/scripts/evaluation-cadence-tests.js +174 -0
- package/dist/scripts/evaluation-contract-tests.js +268 -0
- package/dist/scripts/extension-distribution-cli.js +39 -0
- package/dist/scripts/extension-distribution-tests.js +443 -0
- package/dist/scripts/extension-tests.js +988 -0
- package/dist/scripts/external-event-tests.js +350 -0
- package/dist/scripts/fact-gate-tests.js +869 -0
- package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
- package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
- package/dist/scripts/flight-malformed-tests.js +159 -0
- package/dist/scripts/flyai-setup-tests.js +228 -0
- package/dist/scripts/flyai-setup-tool-tests.js +54 -0
- package/dist/scripts/flyai-tests.js +692 -0
- package/dist/scripts/flyai-tool-registration-contract-tests.js +91 -0
- package/dist/scripts/fx-contract-tests.js +201 -0
- package/dist/scripts/g5-guard-tests.js +131 -0
- package/dist/scripts/g5-guard.js +189 -0
- package/dist/scripts/gen-lunar-anchors.js +106 -0
- package/dist/scripts/geo-atlas-tests.js +283 -0
- package/dist/scripts/gotry-backend-kernel-tests.js +206 -0
- package/dist/scripts/gotry-try-demo-tests.js +79 -0
- package/dist/scripts/gotry-try-demo.js +49 -0
- package/dist/scripts/ground-transfer-tests.js +956 -0
- package/dist/scripts/hbcli-e2e-tests.js +159 -0
- package/dist/scripts/hbcli-live-optin-tests.js +96 -0
- package/dist/scripts/hbcli-release-contract-tests.js +122 -0
- package/dist/scripts/hbcli-tests.js +121 -69
- package/dist/scripts/health-watch-cli.js +73 -0
- package/dist/scripts/hotel-date-gate-tests.js +496 -0
- package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
- package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
- package/dist/scripts/i18n-tests.js +41 -0
- package/dist/scripts/incident-tests.js +281 -87
- package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
- package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
- package/dist/scripts/issue-271-liveness-tests.js +569 -0
- package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
- package/dist/scripts/issue-289-model-retry-tests.js +441 -0
- package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
- package/dist/scripts/issue-338-home-city-e2e.js +577 -0
- package/dist/scripts/issue343-real-entry-e2e.js +160 -0
- package/dist/scripts/issue436-persisted-routing-e2e.js +247 -0
- package/dist/scripts/itinerary-artifact-tests.js +368 -0
- package/dist/scripts/itinerary-deck-artifact-tests.js +416 -0
- package/dist/scripts/itinerary-deck-export-tests.js +552 -0
- package/dist/scripts/itinerary-deck-tests.js +435 -0
- package/dist/scripts/itinerary-html-tests.js +426 -0
- package/dist/scripts/journey-tests.js +22 -27
- package/dist/scripts/kernel-manifest-evidence.js +109 -0
- package/dist/scripts/kernel-manifest-gate.js +126 -0
- package/dist/scripts/kernel-manifest-tests.js +212 -0
- package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
- package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
- package/dist/scripts/kernel-manifest.js +369 -0
- package/dist/scripts/lavish-local-tests.js +1226 -0
- package/dist/scripts/lavish-product-tools-tests.js +537 -0
- package/dist/scripts/ledger-repair-apply-tests.js +316 -0
- package/dist/scripts/ledger-repair-plan-tests.js +375 -0
- package/dist/scripts/ledger-tests.js +382 -246
- package/dist/scripts/ledger-workflow-crash.js +10 -20
- package/dist/scripts/managed-dsh-cleanup-diagnostic-proof.js +184 -0
- package/dist/scripts/managed-dsh-close-failure-proof.js +172 -0
- package/dist/scripts/managed-dsh-run-port-proof.js +153 -0
- package/dist/scripts/managed-dsh-terminal-proof.js +252 -0
- package/dist/scripts/map-tools-vendor-package-proof.js +365 -0
- package/dist/scripts/memory-capture-tests.js +242 -61
- package/dist/scripts/memory-decay-tests.js +18 -52
- package/dist/scripts/memory-lifecycle-tests.js +657 -0
- package/dist/scripts/memory-lifecycle.js +196 -0
- package/dist/scripts/memory-metrics.js +24 -12
- package/dist/scripts/memory-value-report.js +631 -0
- package/dist/scripts/memory-value-tests.js +328 -0
- package/dist/scripts/metrics-report-tests.js +151 -0
- package/dist/scripts/model-override-e2e.js +137 -0
- package/dist/scripts/nightly-evidence-tests.js +161 -0
- package/dist/scripts/nightly-evidence.js +286 -0
- package/dist/scripts/nudge-digest.js +39 -28
- package/dist/scripts/observability-tests.js +381 -0
- package/dist/scripts/onboarding-tests.js +161 -0
- package/dist/scripts/opensky-check.js +18 -13
- package/dist/scripts/opensky-tests.js +28 -28
- package/dist/scripts/persona-surface-guard-tests.js +49 -0
- package/dist/scripts/pnpm-dsh-closure-proof.js +17 -0
- package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
- package/dist/scripts/price-drift-tests.js +340 -0
- package/dist/scripts/price-drift-watch.js +447 -0
- package/dist/scripts/probe-poi-tests.js +53 -45
- package/dist/scripts/product-metrics.js +483 -0
- package/dist/scripts/publish-preverify.js +110 -0
- package/dist/scripts/realtime-pricing-tests.js +76 -0
- package/dist/scripts/recall-tests.js +513 -0
- package/dist/scripts/replay-async.js +20 -18
- package/dist/scripts/replay-real.js +18 -18
- package/dist/scripts/replay.js +52 -82
- package/dist/scripts/session-attach-diagnose.js +20 -20
- package/dist/scripts/session-attach-poc.js +34 -37
- package/dist/scripts/session-benchmark.js +180 -0
- package/dist/scripts/session-dida-live-e2e.js +199 -0
- package/dist/scripts/session-extract-tests.js +14 -35
- package/dist/scripts/session-link-tests.js +478 -0
- package/dist/scripts/session-login.js +27 -46
- package/dist/scripts/session-tests.js +796 -96
- package/dist/scripts/session-zones-tests.js +358 -0
- package/dist/scripts/sf-live-benchmark.js +367 -0
- package/dist/scripts/sf-live-challenge-stop-tests.js +453 -0
- package/dist/scripts/sf-live-cli-tests.js +92 -0
- package/dist/scripts/sf-live-options.js +46 -0
- package/dist/scripts/sf-manifest-tests.js +30 -0
- package/dist/scripts/sf-manifest.js +33 -0
- package/dist/scripts/sf-soft-score-tests.js +67 -0
- package/dist/scripts/sf-summary-tests.js +324 -0
- package/dist/scripts/sf-summary.js +525 -0
- package/dist/scripts/share-tests.js +432 -0
- package/dist/scripts/skeleton-check.js +16 -14
- package/dist/scripts/skeleton-integration-test.js +28 -11
- package/dist/scripts/skills-contract-tests.js +51 -46
- package/dist/scripts/smoke-session-gate-tests.js +21 -0
- package/dist/scripts/smoke.js +690 -246
- package/dist/scripts/spawn-bounded-abort-tests.js +241 -0
- package/dist/scripts/sponsor-reuse-tests.js +262 -0
- package/dist/scripts/state-cli-tests.js +226 -101
- package/dist/scripts/state-cli.js +550 -199
- package/dist/scripts/state-repair-cli.js +66 -0
- package/dist/scripts/state-repair-tests.js +140 -0
- package/dist/scripts/static-golden-tests.js +165 -0
- package/dist/scripts/time-eval-tests.js +210 -192
- package/dist/scripts/travel-timeline-tests.js +29 -93
- package/dist/scripts/turn-handoff-collect-tests.js +169 -0
- package/dist/scripts/turn-handoff-collect.js +161 -0
- package/dist/scripts/turn-policy-tests.js +55 -0
- package/dist/scripts/typed-contract-canary.js +176 -0
- package/dist/scripts/tz-resolver-sanity.js +50 -0
- package/dist/scripts/tz-resolver-tests.js +584 -0
- package/dist/scripts/unified-tests.js +23 -19
- package/dist/scripts/visa-policy-tests.js +62 -0
- package/dist/scripts/weather-tests.js +276 -41
- package/dist/scripts/wish-channel-gate-tests.js +54 -0
- package/dist/scripts/write-gate-tests.js +522 -0
- package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
- package/dist/scripts/z3-lifecycle-tests.js +170 -0
- package/dist/scripts/z3-race-repeat-tests.js +39 -0
- package/dist/scripts/z3-race-tests.js +41 -0
- package/dist/src/artifact-gate.js +829 -0
- package/dist/src/backend/kernel.js +91 -0
- package/dist/src/backend/modules/booking-copilot.js +92 -0
- package/dist/src/backend/modules/booking-executor.js +124 -0
- package/dist/src/backend/modules/session-search.js +551 -0
- package/dist/src/benchmark-agent-conformance.js +578 -0
- package/dist/src/benchmark-environment-bridge.js +515 -0
- package/dist/src/benchmark-headless-child-diagnostics.js +166 -0
- package/dist/src/benchmark-tool-isolation.js +151 -0
- package/dist/src/bookable-facts.js +533 -0
- package/dist/src/booking-saga.js +97 -0
- package/dist/src/booking-surface/availability-policy.js +697 -0
- package/dist/src/booking-surface/booking-intent.js +43 -0
- package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
- package/dist/src/booking-surface/canonical-schema.js +116 -0
- package/dist/src/booking-surface/contracts.js +75 -0
- package/dist/src/booking-surface/dsh-planner.js +1854 -0
- package/dist/src/booking-surface/dsh-plugin.js +378 -0
- package/dist/src/booking-surface/dsh-worker.js +98 -0
- package/dist/src/booking-surface/error-codes.js +231 -0
- package/dist/src/booking-surface/index.js +9 -0
- package/dist/src/booking-surface/managed-dsh-cleanup-diagnostic.js +97 -0
- package/dist/src/booking-surface/managed-dsh-run-port.js +244 -0
- package/dist/src/booking-surface/profile.js +24 -0
- package/dist/src/booking-surface/recovery-chain.js +367 -0
- package/dist/src/booking-surface/runtime.js +1968 -0
- package/dist/src/booking-surface/server.js +674 -0
- package/dist/src/booking-surface/startup.js +168 -0
- package/dist/src/booking-surface/validation.js +261 -0
- package/dist/src/bridge.js +20 -13
- package/dist/src/companions.js +48 -74
- package/dist/src/contracts.js +21 -43
- package/dist/src/dsh-llm.js +97 -111
- package/dist/src/engine.js +276 -193
- package/dist/src/evaluation-cadence.js +97 -0
- package/dist/src/evaluation-contracts.js +283 -0
- package/dist/src/flight-pack-adapter.js +54 -0
- package/dist/src/flight-pack-contract.js +2 -0
- package/dist/src/flyai-setup-tool.js +70 -0
- package/dist/src/gotry-backend.js +125 -0
- package/dist/src/hotel-date-gate.js +118 -0
- package/dist/src/i18n.js +88 -0
- package/dist/src/index.js +2227 -882
- package/dist/src/itinerary-deck.js +139 -0
- package/dist/src/itinerary-doc-shared.js +661 -0
- package/dist/src/itinerary-html.js +131 -0
- package/dist/src/journey.js +155 -108
- package/dist/src/lavish-tools.js +400 -0
- package/dist/src/ledger-repair-apply.js +538 -0
- package/dist/src/ledger-repair-plan.js +463 -0
- package/dist/src/loop.js +319 -160
- package/dist/src/memory-capture.js +180 -32
- package/dist/src/memory-decay.js +30 -14
- package/dist/src/memory-lifecycle.js +1310 -0
- package/dist/src/memory-utility.js +27 -40
- package/dist/src/mock-llm.js +36 -61
- package/dist/src/model.js +81 -36
- package/dist/src/realtime-pricing.js +103 -0
- package/dist/src/recall/card.js +74 -0
- package/dist/src/recall/evaluator.js +140 -0
- package/dist/src/recall/integration.js +17 -0
- package/dist/src/recall/tick.js +166 -0
- package/dist/src/session-link/plan-it-action.js +48 -0
- package/dist/src/session-link/session-link.js +248 -0
- package/dist/src/session-zones.js +507 -0
- package/dist/src/share/adapters.js +51 -0
- package/dist/src/share/share-consent.js +76 -0
- package/dist/src/share/share-deck.js +58 -0
- package/dist/src/share/share-token.js +146 -0
- package/dist/src/slot-spec.js +84 -106
- package/dist/src/state-ledger.js +435 -516
- package/dist/src/state-repair.js +299 -0
- package/dist/src/subagent-job-id-guard.js +58 -0
- package/dist/src/time-anchor.js +231 -33
- package/dist/src/tool-packet.js +20 -8
- package/dist/src/travel-slots.js +58 -35
- package/dist/src/travel-timeline.js +44 -63
- package/dist/src/turn-deadline.js +287 -0
- package/dist/src/turn-policy.js +118 -0
- package/dist/src/tz-resolver.js +143 -0
- package/dist/src/unified.js +535 -359
- package/dist/src/wish-pool.js +42 -12
- package/dist/src/write-gate.js +495 -0
- package/dist/src/z3-shared.js +284 -0
- package/extension/README.md +21 -0
- package/extension/README.zh-CN.md +21 -0
- package/extension/background.js +463 -0
- package/extension/content-bridge.js +270 -0
- package/extension/content-main.js +111 -0
- package/extension/manifest.json +60 -0
- package/package.json +407 -14
- package/schemas/booking.intent.schema.json +193 -0
- package/schemas/booking.surface.schema.json +2622 -0
- package/ts/capabilities/agent-reach-deep.ts +114 -0
- package/ts/capabilities/agent-reach.ts +198 -0
- package/ts/capabilities/anything.ts +255 -0
- package/ts/capabilities/artifacts.ts +428 -0
- package/ts/capabilities/fact-log.ts +54 -0
- package/ts/capabilities/flyai-config.ts +388 -0
- package/ts/capabilities/flyai.ts +1274 -0
- package/ts/capabilities/hbcli.ts +253 -71
- package/ts/capabilities/incident-log.ts +88 -59
- package/ts/capabilities/itinerary-artifact.ts +214 -0
- package/ts/capabilities/itinerary-deck-artifact.ts +218 -0
- package/ts/capabilities/itinerary-deck-export.ts +378 -0
- package/ts/capabilities/opensky.ts +121 -0
- package/ts/capabilities/session/action-cache.ts +148 -0
- package/ts/capabilities/session/adapters/ctrip-flight.ts +181 -0
- package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
- package/ts/capabilities/session/adapters/dida-portal.ts +338 -0
- package/ts/capabilities/session/adapters/meituan-local.ts +70 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +392 -0
- package/ts/capabilities/session/benchmark.ts +276 -0
- package/ts/capabilities/session/bridge-ledger.ts +333 -0
- package/ts/capabilities/session/extension-bridge.ts +794 -0
- package/ts/capabilities/session/extension-channel.ts +144 -0
- package/ts/capabilities/session/extension-distribution.ts +266 -0
- package/ts/capabilities/session/extract.ts +54 -0
- package/ts/capabilities/session/golden-score.ts +139 -0
- package/ts/capabilities/session/health-watch.ts +264 -0
- package/ts/capabilities/session/read-guard.ts +141 -0
- package/ts/capabilities/session/static-flight-golden.ts +209 -0
- package/ts/capabilities/session/transport.ts +146 -0
- package/ts/capabilities/session/wizard.ts +140 -0
- package/ts/capabilities/session-consent.ts +211 -0
- package/ts/capabilities/session-login.ts +208 -0
- package/ts/capabilities/session-search.ts +893 -0
- package/ts/capabilities/weather.ts +330 -0
- package/ts/dsh-runtime/vendor/README.md +68 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +405 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +404 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +1584 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/icon.svg +5 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +595 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +58 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +131 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/geo.js +232 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +120 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +40 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/staticmap-route.js +115 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +267 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +90 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +23 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/geo.d.ts +61 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/staticmap-route.d.ts +31 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +75 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +68 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +97 -0
- package/ts/package.json +293 -3
- package/ts/scripts/async-collect.ts +91 -0
- package/ts/scripts/map-tools-vendor-package-proof.ts +404 -0
- package/ts/scripts/skeleton-check.ts +1 -1
- package/ts/scripts/skeleton-integration-test.ts +23 -1
- package/ts/scripts/state-cli.ts +568 -0
- package/ts/scripts/turn-handoff-collect.ts +178 -0
- package/ts/src/artifact-gate.ts +991 -0
- package/ts/src/benchmark-agent-conformance.ts +619 -0
- package/ts/src/benchmark-environment-bridge.ts +538 -0
- package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
- package/ts/src/benchmark-tool-isolation.ts +166 -0
- package/ts/src/bookable-facts.ts +837 -0
- package/ts/src/booking-saga.ts +133 -0
- package/ts/src/booking-surface/availability-policy.ts +522 -0
- package/ts/src/booking-surface/booking-intent.ts +86 -0
- package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
- package/ts/src/booking-surface/canonical-schema.js +116 -0
- package/ts/src/booking-surface/contracts.ts +475 -0
- package/ts/src/booking-surface/dsh-planner.ts +1949 -0
- package/ts/src/booking-surface/dsh-plugin.js +378 -0
- package/ts/src/booking-surface/dsh-worker.ts +110 -0
- package/ts/src/booking-surface/error-codes.ts +241 -0
- package/ts/src/booking-surface/index.ts +9 -0
- package/ts/src/booking-surface/managed-dsh-cleanup-diagnostic.ts +151 -0
- package/ts/src/booking-surface/managed-dsh-run-port.ts +273 -0
- package/ts/src/booking-surface/profile.ts +42 -0
- package/ts/src/booking-surface/recovery-chain.ts +409 -0
- package/ts/src/booking-surface/runtime.ts +1793 -0
- package/ts/src/booking-surface/server.ts +733 -0
- package/ts/src/booking-surface/startup.ts +201 -0
- package/ts/src/booking-surface/validation.ts +217 -0
- package/ts/src/companions.ts +113 -0
- package/ts/src/contracts.ts +13 -3
- package/ts/src/dsh-llm.ts +44 -11
- package/ts/src/flyai-setup-tool.ts +77 -0
- package/ts/src/index.ts +1948 -198
- package/ts/src/itinerary-deck.ts +189 -0
- package/ts/src/itinerary-doc-shared.ts +741 -0
- package/ts/src/itinerary-html.ts +171 -0
- package/ts/src/loop.ts +223 -25
- package/ts/src/memory-capture.ts +283 -0
- package/ts/src/memory-decay.ts +55 -0
- package/ts/src/memory-utility.ts +70 -0
- package/ts/src/mock-llm.ts +4 -8
- package/ts/src/model.ts +88 -2
- package/ts/src/recall/card.ts +106 -0
- package/ts/src/recall/evaluator.ts +170 -0
- package/ts/src/recall/integration.ts +35 -0
- package/ts/src/recall/tick.ts +213 -0
- package/ts/src/session-link/plan-it-action.ts +78 -0
- package/ts/src/session-link/session-link.ts +275 -0
- package/ts/src/share/adapters.ts +93 -0
- package/ts/src/share/share-consent.ts +105 -0
- package/ts/src/share/share-deck.ts +85 -0
- package/ts/src/share/share-token.ts +166 -0
- package/ts/src/slot-spec.ts +172 -0
- package/ts/src/state-ledger.ts +912 -0
- package/ts/src/subagent-job-id-guard.ts +72 -0
- package/ts/src/time-anchor.ts +338 -0
- package/ts/src/tool-packet.ts +32 -0
- package/ts/src/travel-slots.ts +219 -0
- package/ts/src/travel-timeline.ts +83 -0
- package/ts/src/turn-deadline.ts +366 -0
- package/ts/src/turn-policy.ts +154 -0
- package/ts/src/unified.ts +354 -88
- package/ts/src/wish-pool.ts +79 -0
- package/dist/scripts/session-attach-wait.js +0 -41
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通道探针 tick(外部事件接缝落地第 1 段,docs/design/external-event-seam.md §6①;
|
|
3
|
+
* issue #82 兼容方向)——外部事件作为健康面第二个生产者的本地形态。
|
|
4
|
+
*
|
|
5
|
+
* 设计边界(设计文档 §4,边界即信任):
|
|
6
|
+
* - 只读探测:每通道一个无副作用最小调用(whoami/免费公开 GET),不检索、不下单;
|
|
7
|
+
* - 本地生产者免鉴权(§3.2 本地可信):异常写 `down` 事件进
|
|
8
|
+
* <stateRoot>/gotry-state/channel-health.jsonl,routingAdvice/doctor 零改动受益;
|
|
9
|
+
* - 恢复走事件:探测通过且该通道此前处于 down → 写 'ok' 恢复事件
|
|
10
|
+
* (readLatestChannelEvents latest-wins 超越 down);此前健康则零写放大;
|
|
11
|
+
* - session:* 通道(需用户 Chrome 会话)无头不可探,显式 skip 不误报;
|
|
12
|
+
* - flyai 默认不探(匿名共享额度,探针烧额度弊大于利)——`--only flyai` 显式选入;
|
|
13
|
+
* - 不做常驻监听:tick 由 loopx/cron 驱动,一次运行一轮探测后退出。
|
|
14
|
+
*
|
|
15
|
+
* 用法(loopx/cron 驱动):
|
|
16
|
+
* npx tsx ts/scripts/channel-probe.ts --state-root <root> # 探测+落账
|
|
17
|
+
* npx tsx ts/scripts/channel-probe.ts --dry-run # 只打印不落账
|
|
18
|
+
* npx tsx ts/scripts/channel-probe.ts --only hbcli-hotel,open-meteo # 选通道
|
|
19
|
+
* npx tsx ts/scripts/channel-probe.ts --list # 列探测面
|
|
20
|
+
*
|
|
21
|
+
* @module scripts/channel-probe
|
|
22
|
+
*/
|
|
23
|
+
import { recordChannelEvent, readLatestChannelEvents } from '../capabilities/channel-health.js';
|
|
24
|
+
function timeout(ms) {
|
|
25
|
+
return AbortSignal.timeout(ms);
|
|
26
|
+
}
|
|
27
|
+
async function fetchOk(url, what, ms = 8000) {
|
|
28
|
+
const t0 = Date.now();
|
|
29
|
+
try {
|
|
30
|
+
const res = await fetch(url, { signal: timeout(ms), headers: { Accept: 'application/json' } });
|
|
31
|
+
if (res.ok)
|
|
32
|
+
return { ok: true, ms: Date.now() - t0 };
|
|
33
|
+
return { ok: false, reason: `${what}-http-${res.status}`, ms: Date.now() - t0 };
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
const msg = err instanceof Error ? err.name : String(err);
|
|
37
|
+
return { ok: false, reason: msg === 'TimeoutError' ? 'timeout' : 'unreachable', ms: Date.now() - t0 };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function spawnOk(cmd, args, what, ms = 10000) {
|
|
41
|
+
return async () => {
|
|
42
|
+
const t0 = Date.now();
|
|
43
|
+
try {
|
|
44
|
+
const { spawn } = await import('node:child_process');
|
|
45
|
+
const child = spawn(cmd, args, { stdio: ['ignore', 'ignore', 'ignore'] });
|
|
46
|
+
const code = await new Promise((resolve, reject) => {
|
|
47
|
+
const timer = setTimeout(() => { child.kill('SIGKILL'); reject(new Error('timeout')); }, ms);
|
|
48
|
+
child.on('exit', (c) => { clearTimeout(timer); resolve(c ?? 1); });
|
|
49
|
+
child.on('error', (e) => { clearTimeout(timer); reject(e); });
|
|
50
|
+
});
|
|
51
|
+
return code === 0 ? { ok: true, ms: Date.now() - t0 } : { ok: false, reason: `${what}-exit-${code}`, ms: Date.now() - t0 };
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
55
|
+
return { ok: false, reason: msg === 'timeout' ? 'timeout' : 'spawn-failed', ms: Date.now() - t0 };
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** 默认探测面(设计文档 §6①「关键通道」的无头子集;session 系与 flyai 显式 skip) */
|
|
60
|
+
export function defaultProbes() {
|
|
61
|
+
return [
|
|
62
|
+
{
|
|
63
|
+
channel: 'hbcli-hotel',
|
|
64
|
+
probeable: true,
|
|
65
|
+
run: spawnOk('hbcli', ['auth', 'whoami'], 'auth-denied'),
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
channel: 'open-meteo',
|
|
69
|
+
probeable: true,
|
|
70
|
+
run: () => fetchOk('https://api.open-meteo.com/v1/forecast?latitude=25.2048&longitude=55.2708¤t=temperature_2m', 'weather'),
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
channel: 'opensky',
|
|
74
|
+
probeable: true,
|
|
75
|
+
run: () => fetchOk('https://opensky-network.org/api/states/all?lamin=24.0&lomin=53.0&lamax=26.5&lomax=56.5', 'flights', 12000),
|
|
76
|
+
},
|
|
77
|
+
{ channel: 'flyai', probeable: false, skipReason: 'anonymous-shared-quota——探针烧额度弊大于利;--only flyai 显式选入', run: async () => ({ ok: true, ms: 0 }) },
|
|
78
|
+
{ channel: 'session:ctrip-flight', probeable: false, skipReason: '需用户 Chrome 会话,无头不可探', run: async () => ({ ok: true, ms: 0 }) },
|
|
79
|
+
{ channel: 'session:ctrip-hotel', probeable: false, skipReason: '需用户 Chrome 会话,无头不可探', run: async () => ({ ok: true, ms: 0 }) },
|
|
80
|
+
{ channel: 'session:12306-train', probeable: false, skipReason: '需用户 Chrome 会话,无头不可探', run: async () => ({ ok: true, ms: 0 }) },
|
|
81
|
+
{ channel: 'anything-geo', probeable: false, skipReason: '静态包通道,无远端可探', run: async () => ({ ok: true, ms: 0 }) },
|
|
82
|
+
{ channel: 'static-hotel', probeable: false, skipReason: '静态包通道,无远端可探', run: async () => ({ ok: true, ms: 0 }) },
|
|
83
|
+
{ channel: 'web-read', probeable: false, skipReason: '依赖 agent-reach 装配态,探测意义低;--only 显式选入可探', run: async () => ({ ok: true, ms: 0 }) },
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 探测结果 → 事件序列(纯函数,测试锚点):
|
|
88
|
+
* - fail → down 事件(永远落,哪怕已 down——重申事实,消费方幂等);
|
|
89
|
+
* - ok 且此前处于 down → 'ok' 恢复事件(latest-wins 超越);
|
|
90
|
+
* - ok 且此前健康 → 不落(零写放大);
|
|
91
|
+
* - skip(probeable=false)→ 不产生事件。
|
|
92
|
+
*/
|
|
93
|
+
export function evaluateProbeResults(results, priorDown, now = new Date()) {
|
|
94
|
+
const events = [];
|
|
95
|
+
for (const { probe, result } of results) {
|
|
96
|
+
if (!probe.probeable)
|
|
97
|
+
continue;
|
|
98
|
+
if (result.ok) {
|
|
99
|
+
if (priorDown.has(probe.channel)) {
|
|
100
|
+
events.push({ channel: probe.channel, state: 'ok', at: now.toISOString() });
|
|
101
|
+
}
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
events.push({ channel: probe.channel, state: 'down', reason: result.reason, at: now.toISOString() });
|
|
105
|
+
}
|
|
106
|
+
return events;
|
|
107
|
+
}
|
|
108
|
+
export function parseArgs(argv) {
|
|
109
|
+
let stateRoot;
|
|
110
|
+
let only;
|
|
111
|
+
let dryRun = false;
|
|
112
|
+
let list = false;
|
|
113
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
114
|
+
const a = argv[i];
|
|
115
|
+
if (a === '--state-root')
|
|
116
|
+
stateRoot = argv[++i];
|
|
117
|
+
else if (a === '--only')
|
|
118
|
+
only = (argv[++i] ?? '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
119
|
+
else if (a === '--dry-run')
|
|
120
|
+
dryRun = true;
|
|
121
|
+
else if (a === '--list')
|
|
122
|
+
list = true;
|
|
123
|
+
}
|
|
124
|
+
return { stateRoot, only, dryRun, list };
|
|
125
|
+
}
|
|
126
|
+
export async function main(argv) {
|
|
127
|
+
const args = parseArgs(argv);
|
|
128
|
+
const all = defaultProbes();
|
|
129
|
+
if (args.list) {
|
|
130
|
+
for (const p of all) {
|
|
131
|
+
console.log(`${p.probeable ? 'probe' : 'skip '} ${p.channel}${p.skipReason ? ` # ${p.skipReason}` : ''}`);
|
|
132
|
+
}
|
|
133
|
+
return 0;
|
|
134
|
+
}
|
|
135
|
+
const selected = args.only?.length ? all.filter((p) => args.only.includes(p.channel)) : all.filter((p) => p.probeable);
|
|
136
|
+
if (selected.length === 0) {
|
|
137
|
+
console.error('[channel-probe] --only 未匹配任何通道;--list 查看探测面');
|
|
138
|
+
return 2;
|
|
139
|
+
}
|
|
140
|
+
const results = [];
|
|
141
|
+
for (const probe of selected) {
|
|
142
|
+
const result = await probe.run();
|
|
143
|
+
results.push({ probe, result });
|
|
144
|
+
console.log(`[channel-probe] ${result.ok ? 'ok ' : 'FAIL'} ${probe.channel} (${result.ms}ms)${result.ok ? '' : ` reason=${result.reason}`}`);
|
|
145
|
+
}
|
|
146
|
+
const failures = results.filter((r) => !r.result.ok).length;
|
|
147
|
+
if (args.dryRun || !args.stateRoot) {
|
|
148
|
+
if (!args.stateRoot)
|
|
149
|
+
console.log('[channel-probe] 未给 --state-root:只探测不落账(等价 --dry-run)');
|
|
150
|
+
return failures > 0 ? 1 : 0;
|
|
151
|
+
}
|
|
152
|
+
let priorDown = new Set();
|
|
153
|
+
try {
|
|
154
|
+
const latest = await readLatestChannelEvents(args.stateRoot);
|
|
155
|
+
priorDown = new Set([...latest.entries()].filter(([, ev]) => ev.state === 'down' || ev.state === 'cooldown').map(([ch]) => ch));
|
|
156
|
+
}
|
|
157
|
+
catch { /* 读不到按全健康处理 */ }
|
|
158
|
+
for (const ev of evaluateProbeResults(results, priorDown)) {
|
|
159
|
+
await recordChannelEvent(args.stateRoot, ev);
|
|
160
|
+
console.log(`[channel-probe] 记录 ${ev.state} ${ev.channel}${ev.reason ? `(${ev.reason})` : ''}`);
|
|
161
|
+
}
|
|
162
|
+
return failures > 0 ? 1 : 0;
|
|
163
|
+
}
|
|
164
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
165
|
+
main(process.argv.slice(2)).then((code) => process.exit(code)).catch((err) => {
|
|
166
|
+
console.error('[channel-probe] fatal:', err instanceof Error ? err.message : err);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通道注册表 + 通道健康面测试(issue #106/#107/#108 编排设计落地,
|
|
3
|
+
* docs/design/tool-orchestration-design.md §2/§3;全离线,隔离 tmp stateRoot):
|
|
4
|
+
* 1. 注册表封闭性:id 唯一/quotaClass·tier·intent 闭集/静态包不可路由
|
|
5
|
+
* 2. 意图顺位:证据级降序(官方API > 会话 > 网页兜底),同 tier 按效率
|
|
6
|
+
* 3. routingAdvice:down 通道被排除/发起通道被排除/limit 截断;hit 即恢复;
|
|
7
|
+
* Dida down 时推进到下一个健康酒店通道
|
|
8
|
+
* 4. noteChannelVerdict 映射:needs-setup→down/hit→清除/miss·error→不动/cooldown 过期
|
|
9
|
+
* 5. persona 路由卡:确定性渲染,含改道规则与意图顺位行
|
|
10
|
+
* 6. JSONL 持久面:record/readLatest 往返 + 坏行容忍 + limitDays 过滤
|
|
11
|
+
* 7. doctor 集成:flyai 最近达限时间可见;calendar 三态(默认不挂载/opt-in 未配置/opt-in 已配置);
|
|
12
|
+
* patch 宿主插件两态照亮(map-tools/ask-user,issue #113 L1 残量)
|
|
13
|
+
* 8. apply 接线:channel_routing_card 变量已注册;参数级拒绝不带 routing 字段;
|
|
14
|
+
* 工具描述首行生成(检索工具前置意图顺位卡,非检索工具原样,#113)
|
|
15
|
+
* 9. 持久健康面投影(#436):persistedDownChannels 纯投影(有效 down 才排除;
|
|
16
|
+
* ok/cooldown/缺时间戳/坏时间戳/未来时间戳一律不排除)+ routingAdvice
|
|
17
|
+
* 与会话态的并集语义(会话失败永不被持久面解除)
|
|
18
|
+
*
|
|
19
|
+
* 运行: cd ts && npx tsx scripts/channel-registry-tests.ts
|
|
20
|
+
*/
|
|
21
|
+
import assert from 'node:assert/strict';
|
|
22
|
+
import { mkdtemp, mkdir, writeFile, rm, readFile } from 'node:fs/promises';
|
|
23
|
+
import { tmpdir } from 'node:os';
|
|
24
|
+
import { join } from 'node:path';
|
|
25
|
+
import { CHANNELS, channelsForIntent, routingAdvice, renderRoutingCard, toolRoutingHeadline, INTENT_LABELS, persistedDownChannels, } from '../capabilities/channel-registry.js';
|
|
26
|
+
import { noteChannelVerdict, channelState, clearChannel, resetChannelHealth, recordChannelEvent, readLatestChannelEvents, } from '../capabilities/channel-health.js';
|
|
27
|
+
import { runDoctorChecks } from '../capabilities/doctor.js';
|
|
28
|
+
import { apply } from '../src/index.js';
|
|
29
|
+
const NOW = 1_700_000_000_000;
|
|
30
|
+
// 1. 注册表封闭性
|
|
31
|
+
const QUOTA_CLASSES = ['user-session', 'user-key', 'anonymous-trial', 'free-public', 'static'];
|
|
32
|
+
const TIERS = ['realtime-api', 'session', 'best-effort', 'static'];
|
|
33
|
+
const INTENTS = Object.keys(INTENT_LABELS);
|
|
34
|
+
const ids = new Set();
|
|
35
|
+
for (const c of CHANNELS) {
|
|
36
|
+
assert.ok(!ids.has(c.id), `通道 id 重复:${c.id}`);
|
|
37
|
+
ids.add(c.id);
|
|
38
|
+
assert.ok(QUOTA_CLASSES.includes(c.quotaClass), `${c.id} quotaClass 越界`);
|
|
39
|
+
assert.ok(TIERS.includes(c.tier), `${c.id} tier 越界`);
|
|
40
|
+
assert.ok(c.intents.length > 0 && c.intents.every(i => INTENTS.includes(i)), `${c.id} intents 越界`);
|
|
41
|
+
assert.ok(c.tool.startsWith('gotry_'), `${c.id} 工具名形态`);
|
|
42
|
+
if (c.quotaClass === 'static')
|
|
43
|
+
assert.equal(c.routable, false, '静态包不进模型可选路由');
|
|
44
|
+
}
|
|
45
|
+
assert.ok(CHANNELS.length >= 9, '注册表覆盖面(≥9 通道)');
|
|
46
|
+
console.log('1. 注册表封闭性 OK');
|
|
47
|
+
// 2. 意图顺位(证据级降序)
|
|
48
|
+
resetChannelHealth();
|
|
49
|
+
assert.deepEqual(channelsForIntent('search-flight').map(c => c.id), ['flyai', 'session:ctrip-flight', 'web-read'], '机票顺位');
|
|
50
|
+
assert.deepEqual(channelsForIntent('search-train').map(c => c.id), ['flyai', 'session:12306-train', 'web-read'], '火车顺位');
|
|
51
|
+
assert.deepEqual(channelsForIntent('search-hotel').map(c => c.id), ['flyai', 'hbcli-hotel', 'session:ctrip-hotel', 'session:dida-portal', 'web-read'], '酒店完整顺位(Dida 登记后仍钉全序)');
|
|
52
|
+
assert.deepEqual(channelsForIntent('weather').map(c => c.id), ['open-meteo'], '单通道意图');
|
|
53
|
+
console.log('2. 意图顺位(证据级 > 效率)OK');
|
|
54
|
+
// 3. routingAdvice:健康面驱动
|
|
55
|
+
const base = routingAdvice('search-flight');
|
|
56
|
+
assert.equal(base.alternatives[0].channel, 'flyai', '健康时 flyai 首荐(零 setup 摩擦)');
|
|
57
|
+
assert.ok(base.alternatives[0].why.length > 4 && (base.alternatives[0].setup?.length ?? 0) > 0, 'why/setup 齐备');
|
|
58
|
+
// #559 B 步:flyai setup 行必须显式提到 Trial limit reached 后果,给匿名试用用户
|
|
59
|
+
// 一眼看见「达限后会改道 gotry_session_search」(避免装好的人替没装的人以为整盘不可用)
|
|
60
|
+
assert.match(base.alternatives[0].setup ?? '', /Trial limit reached/, 'flyai setup 行必显 Trial limit reached');
|
|
61
|
+
assert.match(base.alternatives[0].setup ?? '', /gotry_session_search/, 'flyai setup 行必显 fallback 路径');
|
|
62
|
+
noteChannelVerdict('flyai', 'needs-setup', { now: NOW });
|
|
63
|
+
const afterDown = routingAdvice('search-flight', { now: NOW });
|
|
64
|
+
assert.ok(!afterDown.alternatives.some(a => a.channel === 'flyai'), 'flyai 达限后从建议表排除(#108 病灶)');
|
|
65
|
+
assert.equal(afterDown.alternatives[0].channel, 'session:ctrip-flight', 'session 升首荐(优先级=健康面投影)');
|
|
66
|
+
const excl = routingAdvice('search-flight', { excludeChannel: 'session:ctrip-flight', now: NOW });
|
|
67
|
+
assert.ok(!excl.alternatives.some(a => a.channel === 'session:ctrip-flight'), '发起通道被排除');
|
|
68
|
+
assert.equal(routingAdvice('search-flight', { limit: 1, now: NOW }).alternatives.length, 1, 'limit 截断');
|
|
69
|
+
noteChannelVerdict('flyai', 'hit', { now: NOW });
|
|
70
|
+
assert.equal(routingAdvice('search-flight', { now: NOW }).alternatives[0].channel, 'flyai', 'hit 即恢复(#107:补 key 不被陈旧状态锁死)');
|
|
71
|
+
// Dida 已是酒店 session 通道的一员:#297 后先让更高顺位通道不可用,证明 Dida
|
|
72
|
+
// 成为当前首选;再把 Dida 标 down,必须推进到注册表中的后继 web-read。
|
|
73
|
+
resetChannelHealth();
|
|
74
|
+
for (const channel of ['flyai', 'hbcli-hotel', 'session:ctrip-hotel']) {
|
|
75
|
+
noteChannelVerdict(channel, 'needs-setup', { now: NOW });
|
|
76
|
+
}
|
|
77
|
+
assert.equal(routingAdvice('search-hotel', { now: NOW }).alternatives[0].channel, 'session:dida-portal', '前三个酒店通道 down 后 Dida 升为首荐');
|
|
78
|
+
noteChannelVerdict('session:dida-portal', 'needs-login', { now: NOW });
|
|
79
|
+
assert.equal(routingAdvice('search-hotel', { now: NOW }).alternatives[0].channel, 'web-read', 'Dida down 后推进到后继健康酒店通道');
|
|
80
|
+
console.log('3. routingAdvice(down 排除/发起排除/limit/hit 恢复 + Dida down→后继)OK');
|
|
81
|
+
// 4. noteChannelVerdict 映射闭集
|
|
82
|
+
resetChannelHealth();
|
|
83
|
+
assert.ok(noteChannelVerdict('x', 'needs-setup', { now: NOW }), 'needs-setup → down 事件');
|
|
84
|
+
assert.equal(channelState('x', NOW)?.state, 'down');
|
|
85
|
+
assert.equal(noteChannelVerdict('x', 'challenged', { now: NOW })?.reason, 'challenged', 'challenged → down');
|
|
86
|
+
assert.equal(noteChannelVerdict('x', 'needs-login', { now: NOW })?.reason, 'needs-login', 'needs-login → down');
|
|
87
|
+
assert.equal(noteChannelVerdict('x', 'needs-extension', { now: NOW })?.reason, 'needs-extension', 'needs-extension → down');
|
|
88
|
+
assert.equal(noteChannelVerdict('x', 'miss', { now: NOW }), undefined, '业务 miss ≠ 通道故障');
|
|
89
|
+
assert.equal(noteChannelVerdict('x', 'error', { now: NOW }), undefined, '业务 error ≠ 通道故障');
|
|
90
|
+
clearChannel('x');
|
|
91
|
+
assert.equal(channelState('x', NOW), undefined, '清除后无状态');
|
|
92
|
+
const cd = noteChannelVerdict('y', 'cooldown', { now: NOW, cooldownMs: 30_000 });
|
|
93
|
+
assert.equal(cd?.state, 'cooldown', 'cooldown → cooldown 事件');
|
|
94
|
+
assert.equal(channelState('y', NOW + 29_999)?.state, 'cooldown', '冷却未满仍在');
|
|
95
|
+
assert.equal(channelState('y', NOW + 30_001), undefined, '冷却满惰性过期');
|
|
96
|
+
console.log('4. noteChannelVerdict 映射闭集 OK');
|
|
97
|
+
// 5. persona 路由卡
|
|
98
|
+
const card1 = renderRoutingCard(new Date(NOW));
|
|
99
|
+
const card2 = renderRoutingCard(new Date(NOW));
|
|
100
|
+
assert.equal(card1, card2, '同刻渲染确定');
|
|
101
|
+
assert.match(card1, /检索通道顺位/, '卡头');
|
|
102
|
+
assert.match(card1, /verdict≠hit/, '改道规则在卡内');
|
|
103
|
+
assert.match(card1, /- 机票: .+gotry_flyai_search.+→.+gotry_session_search/, '机票顺位行');
|
|
104
|
+
assert.match(card1, /- 火车: .+12306 公开查询面.+kind=train/, '火车行带 kind 消歧');
|
|
105
|
+
assert.match(card1, /匿名试用=首次体验导流/, '额度口径脚注(D-7)');
|
|
106
|
+
assert.ok(!card1.includes('- 天气'), '单通道意图不上卡');
|
|
107
|
+
console.log('5. persona 路由卡 OK');
|
|
108
|
+
// 6. JSONL 持久面往返(事件时间用真实时钟——limitDays 窗口以读方时钟为基准)
|
|
109
|
+
const stateRoot = await mkdtemp(join(tmpdir(), 'gotry-channel-test-'));
|
|
110
|
+
const realNow = Date.now();
|
|
111
|
+
await recordChannelEvent(stateRoot, { channel: 'flyai', state: 'down', reason: 'needs-setup', at: new Date(realNow - 86_400_000).toISOString() });
|
|
112
|
+
await recordChannelEvent(stateRoot, { channel: 'flyai', state: 'down', reason: 'needs-setup', at: new Date(realNow).toISOString() });
|
|
113
|
+
await recordChannelEvent(stateRoot, { channel: 'session:ctrip-flight', state: 'cooldown', at: new Date(realNow).toISOString() });
|
|
114
|
+
const latest = await readLatestChannelEvents(stateRoot);
|
|
115
|
+
assert.equal(latest.get('flyai')?.at, new Date(realNow).toISOString(), '每通道取最新');
|
|
116
|
+
assert.equal(latest.get('session:ctrip-flight')?.state, 'cooldown');
|
|
117
|
+
await recordChannelEvent(stateRoot, { channel: 'legacy', state: 'down', at: new Date(realNow - 40 * 86_400_000).toISOString() });
|
|
118
|
+
assert.ok(!((await readLatestChannelEvents(stateRoot)).has('legacy')), '40 天前事件被 limitDays 过滤');
|
|
119
|
+
const raw = `${stateRoot}/gotry-state/channel-health.jsonl`;
|
|
120
|
+
await writeFile(raw, `${await readFile(raw, 'utf-8')}not-json\n{"half":true}\n`, 'utf-8');
|
|
121
|
+
assert.equal((await readLatestChannelEvents(stateRoot)).size, 2, '坏行容忍');
|
|
122
|
+
console.log('6. JSONL 持久面 OK');
|
|
123
|
+
// 7. doctor 集成:flyai 配额可见 + calendar 三态(setup 状态面,非 env)
|
|
124
|
+
const emptyRepo = join(stateRoot, 'repo');
|
|
125
|
+
const emptyHome = join(stateRoot, 'home');
|
|
126
|
+
await mkdir(emptyRepo, { recursive: true });
|
|
127
|
+
await mkdir(join(emptyHome, '.gotry'), { recursive: true });
|
|
128
|
+
const calStatePath = join(emptyHome, '.gotry', 'calendar.json');
|
|
129
|
+
const d1 = await runDoctorChecks({ repoRoot: emptyRepo, homeDir: emptyHome, env: {}, stateRoot });
|
|
130
|
+
assert.match(d1.items.find(i => i.id === 'flyai').detail, /最近一次试用达限/, '最近达限时间进 doctor(#107 可见性)');
|
|
131
|
+
assert.equal(d1.items.find(i => i.id === 'calendar').status, 'ok', 'calendar 默认不挂载=ok(D-9)');
|
|
132
|
+
assert.match(d1.items.find(i => i.id === 'calendar').detail, /默认未挂载/, '默认态人话');
|
|
133
|
+
await writeFile(calStatePath, JSON.stringify({ enabled: true }), 'utf-8');
|
|
134
|
+
const d2 = await runDoctorChecks({ repoRoot: emptyRepo, homeDir: emptyHome, env: {} });
|
|
135
|
+
assert.equal(d2.items.find(i => i.id === 'calendar').status, 'degraded', 'setup 开启但未配置 username=degraded');
|
|
136
|
+
assert.match(d2.items.find(i => i.id === 'calendar').fix ?? '', /npx @danceiny\/gotry setup calendar/, 'fix 指向 setup 状态面');
|
|
137
|
+
const profileDir = join(emptyHome, '.dsh/profiles/web');
|
|
138
|
+
await mkdir(profileDir, { recursive: true });
|
|
139
|
+
await writeFile(join(profileDir, 'cordis.patch.yml'), '- id: dsh-calendar\n config:\n username: someone\n', 'utf-8');
|
|
140
|
+
const d3 = await runDoctorChecks({ repoRoot: emptyRepo, homeDir: emptyHome, env: {} });
|
|
141
|
+
assert.equal(d3.items.find(i => i.id === 'calendar').status, 'ok', 'setup 开启且已配置=ok');
|
|
142
|
+
const d4 = await runDoctorChecks({ repoRoot: emptyRepo, homeDir: emptyHome, env: {} });
|
|
143
|
+
assert.ok(!/最近一次试用达限/.test(d4.items.find(i => i.id === 'flyai').detail), '无 stateRoot 不读持久面(向后兼容)');
|
|
144
|
+
// patch 宿主插件两态照亮(issue #113):隔离环境两者皆 missing——静默剔除面进 doctor 可见
|
|
145
|
+
const mapItem = d4.items.find(i => i.id === 'map-tools');
|
|
146
|
+
const askItem = d4.items.find(i => i.id === 'ask-user');
|
|
147
|
+
assert.equal(mapItem.status, 'missing', '隔离环境 map-tools=missing(静默剔除面可见)');
|
|
148
|
+
assert.match(mapItem.detail, /静默剔除/, 'map-tools detail 说明静默剔除语义');
|
|
149
|
+
assert.equal(askItem.status, 'missing', '隔离环境 ask-user=missing');
|
|
150
|
+
assert.match(askItem.detail, /散文追问/, 'ask-user detail 说明退化面');
|
|
151
|
+
console.log('7. doctor 集成(flyai 配额可见 + calendar setup 状态面三态 + 宿主插件两态)OK');
|
|
152
|
+
// 8. apply 接线:变量注册 + 参数级拒绝不带 routing
|
|
153
|
+
resetChannelHealth();
|
|
154
|
+
const variables = {};
|
|
155
|
+
const registered = [];
|
|
156
|
+
const ctx = {
|
|
157
|
+
tools: { register: (t) => registered.push(t) },
|
|
158
|
+
systemPrompt: { variable: (name, provider) => { variables[name] = provider; } },
|
|
159
|
+
on: () => () => { },
|
|
160
|
+
};
|
|
161
|
+
apply(ctx, { stateRoot, timeoutMs: 30_000, hbcliBin: 'hbcli-not-on-path', sessionAccess: 'ask' });
|
|
162
|
+
assert.equal(typeof variables.channel_routing_card, 'function', 'channel_routing_card 变量已注册');
|
|
163
|
+
assert.match(variables.channel_routing_card(), /检索通道顺位/, '卡片可渲染');
|
|
164
|
+
const pastDate = await registered.find(t => t.name === 'gotry_flyai_search').execute({ kind: 'flight', from: '深圳', to: '普吉', date: '2026-01-01' }, null);
|
|
165
|
+
assert.ok(!('routing' in pastDate), '参数级预校验拒绝不带 routing(未发起检索,无改道语义)');
|
|
166
|
+
// D-30 迁移锁(issue #112):typed 平铺契约下 legacy blob 包裹在宿主权校验即拒绝,
|
|
167
|
+
// 拒绝形状保持 ADR-13 ToolFailure(guardToolExecute 兜底:ok:false + evidence)且不带 routing
|
|
168
|
+
const legacyBlob = await registered.find(t => t.name === 'gotry_flyai_search').execute({ query: { kind: 'flight', from: '深圳', to: '普吉', date: '2026-01-01' } }, null);
|
|
169
|
+
assert.ok(legacyBlob.ok === false && !('routing' in legacyBlob) && !!legacyBlob.evidence, 'legacy blob → 结构化拒绝(ToolFailure,无 routing)');
|
|
170
|
+
// 工具描述首行生成(issue #113):检索工具前置注册表生成的意图顺位卡;非检索工具原样
|
|
171
|
+
const flyaiTool = registered.find(t => t.name === 'gotry_flyai_search');
|
|
172
|
+
const doctorToolDesc = registered.find(t => t.name === 'gotry_doctor');
|
|
173
|
+
assert.ok(flyaiTool, 'flyai 工具已注册');
|
|
174
|
+
assert.match(toolRoutingHeadline('gotry_flyai_search'), /机票: 飞猪官方 API\(gotry_flyai_search\) → 携程·用户登录态/, 'flyai 首行卡:机票顺位 flyai 在前(证据级)');
|
|
175
|
+
assert.match(toolRoutingHeadline('gotry_session_search'), /火车: .*12306 公开查询面\(gotry_session_search kind=train\)/, 'session 首行卡带 kind 消歧');
|
|
176
|
+
assert.equal(toolRoutingHeadline('gotry_doctor'), '', '非检索工具无首行卡(原样描述)');
|
|
177
|
+
assert.ok(flyaiTool.description?.startsWith('本工具服务的检索意图与通道顺位'), 'flyai 描述已前置首行卡(接线生效)');
|
|
178
|
+
assert.ok(!doctorToolDesc.description?.startsWith('本工具服务的检索意图'), 'doctor 描述无首行卡(接线不误伤)');
|
|
179
|
+
console.log('8. apply 接线 + 描述首行生成 OK');
|
|
180
|
+
// 9. 持久健康面投影 + 并集语义(issue #436)
|
|
181
|
+
resetChannelHealth();
|
|
182
|
+
const latestEvents = new Map([
|
|
183
|
+
['down-valid', { state: 'down', at: new Date(NOW - 60_000).toISOString() }],
|
|
184
|
+
['down-now', { state: 'down', at: new Date(NOW).toISOString() }],
|
|
185
|
+
['ok-latest', { state: 'ok', at: new Date(NOW).toISOString() }],
|
|
186
|
+
['cooldown-latest', { state: 'cooldown', at: new Date(NOW).toISOString() }],
|
|
187
|
+
['down-no-at', { state: 'down' }],
|
|
188
|
+
['down-bad-at', { state: 'down', at: 'not-a-date' }],
|
|
189
|
+
['down-future', { state: 'down', at: new Date(NOW + 86_400_000).toISOString() }],
|
|
190
|
+
]);
|
|
191
|
+
assert.deepEqual([...persistedDownChannels(latestEvents, NOW)].sort(), ['down-now', 'down-valid'], '投影:只有带有效时间戳且不在未来的 down 才排除(cooldown 是节律、ok 是恢复)');
|
|
192
|
+
assert.ok(persistedDownChannels(new Map(), NOW).size === 0, '空事件表=零排除');
|
|
193
|
+
// 并集语义:持久集合只加排除,不能解除会话态;顺位表本身不变
|
|
194
|
+
const noPersisted = routingAdvice('search-flight', { now: NOW });
|
|
195
|
+
assert.ok(noPersisted.alternatives.some(a => a.channel === 'session:ctrip-flight'), '无持久集合=原会话态口径');
|
|
196
|
+
const persistedExcl = routingAdvice('search-flight', { now: NOW, persistedDownChannels: new Set(['session:ctrip-flight']) });
|
|
197
|
+
assert.ok(!persistedExcl.alternatives.some(a => a.channel === 'session:ctrip-flight'), '持久 down 通道被排除');
|
|
198
|
+
assert.equal(persistedExcl.alternatives[0].channel, 'flyai', '顺位不变,只过滤');
|
|
199
|
+
const persistedLimit = routingAdvice('search-hotel', { now: NOW, persistedDownChannels: new Set(['flyai', 'hbcli-hotel']), limit: 1 });
|
|
200
|
+
assert.deepEqual(persistedLimit.alternatives.map(a => a.channel), ['session:ctrip-hotel'], '持久排除叠加后 limit 仍截断');
|
|
201
|
+
noteChannelVerdict('session:ctrip-flight', 'challenged', { now: NOW });
|
|
202
|
+
assert.ok(!routingAdvice('search-flight', { now: NOW, persistedDownChannels: new Set() }).alternatives.some(a => a.channel === 'session:ctrip-flight'), '会话 down 在空持久集合(含持久 ok 恢复)下仍排除——持久面不解锁会话失败');
|
|
203
|
+
assert.equal(channelState('session:ctrip-flight', NOW)?.state, 'down', '会话态未被持久面改写');
|
|
204
|
+
clearChannel('session:ctrip-flight');
|
|
205
|
+
assert.ok(routingAdvice('search-flight', { now: NOW, persistedDownChannels: new Set() }).alternatives.some(a => a.channel === 'session:ctrip-flight'), '会话清除 + 持久面无 down → 通道恢复(恢复只对没有会话 down 的通道生效)');
|
|
206
|
+
console.log('9. 持久健康面投影 + 并集语义(#436)OK');
|
|
207
|
+
await rm(stateRoot, { recursive: true, force: true });
|
|
208
|
+
console.log('channel-registry-tests: 全部通过');
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 同行人档案单测(memory-design P2,companion_profile.v1):
|
|
3
|
+
* 负面清单守卫(证件/电话零入库)/upsert 合并(追加不删史/幂等)/brief 摘要形状。
|
|
4
|
+
* 运行:cd ts && npx tsx scripts/companion-tests.ts
|
|
5
|
+
*/
|
|
1
6
|
import assert from 'node:assert/strict';
|
|
2
7
|
import { companionId, sensitiveViolation, upsertCompanion } from '../src/companions.js';
|
|
3
8
|
let n = 0;
|
|
@@ -5,108 +10,58 @@ function pass(name, body) {
|
|
|
5
10
|
body();
|
|
6
11
|
console.log(` ${++n}. ${name} OK`);
|
|
7
12
|
}
|
|
8
|
-
pass('负面清单守卫:证件号/手机号拒收入库(红线 6 工程形态)', ()=>{
|
|
13
|
+
pass('负面清单守卫:证件号/手机号拒收入库(红线 6 工程形态)', () => {
|
|
9
14
|
assert.ok(sensitiveViolation('护照号 E12345678')?.includes('负面清单'), '证件拒');
|
|
10
15
|
assert.ok(sensitiveViolation('我电话 13812345678')?.includes('负面清单'), '手机号拒');
|
|
11
16
|
assert.equal(sensitiveViolation('爸爸65,轻度高血压,走不动山路'), null, '行为约束正常');
|
|
12
17
|
const r = upsertCompanion([], {
|
|
13
18
|
label: '爸爸',
|
|
14
|
-
constraints: {
|
|
15
|
-
|
|
16
|
-
'轻度高血压'
|
|
17
|
-
]
|
|
18
|
-
},
|
|
19
|
-
evidence: '身份证号 110101199001011234,轻度高血压'
|
|
19
|
+
constraints: { health: ['轻度高血压'] },
|
|
20
|
+
evidence: '身份证号 110101199001011234,轻度高血压',
|
|
20
21
|
});
|
|
21
22
|
assert.equal(r.appended, false);
|
|
22
23
|
assert.ok(r.reason?.includes('负面清单'));
|
|
23
24
|
});
|
|
24
|
-
pass('新建:约束+证据落库,companion_id 语义派生', ()=>{
|
|
25
|
+
pass('新建:约束+证据落库,companion_id 语义派生', () => {
|
|
25
26
|
const r = upsertCompanion([], {
|
|
26
27
|
label: '爸 爸',
|
|
27
|
-
constraints: {
|
|
28
|
-
|
|
29
|
-
'轻度高血压'
|
|
30
|
-
],
|
|
31
|
-
mobility: '步行≤4h'
|
|
32
|
-
},
|
|
33
|
-
evidence: '爸爸65有轻度高血压,别太累'
|
|
28
|
+
constraints: { health: ['轻度高血压'], mobility: '步行≤4h' },
|
|
29
|
+
evidence: '爸爸65有轻度高血压,别太累',
|
|
34
30
|
});
|
|
35
31
|
assert.equal(r.appended, true);
|
|
36
32
|
assert.equal(r.companionId, '爸爸');
|
|
37
33
|
const p = r.profiles[0];
|
|
38
|
-
assert.deepEqual(p.constraints.health, [
|
|
39
|
-
|
|
40
|
-
]);
|
|
41
|
-
assert.deepEqual(p.evidence, [
|
|
42
|
-
'爸爸65有轻度高血压,别太累'
|
|
43
|
-
]);
|
|
34
|
+
assert.deepEqual(p.constraints.health, ['轻度高血压']);
|
|
35
|
+
assert.deepEqual(p.evidence, ['爸爸65有轻度高血压,别太累']);
|
|
44
36
|
});
|
|
45
|
-
pass('upsert 合并:数组追加不删史/覆盖取新/幂等', ()=>{
|
|
37
|
+
pass('upsert 合并:数组追加不删史/覆盖取新/幂等', () => {
|
|
46
38
|
let ps = [];
|
|
47
39
|
ps = upsertCompanion(ps, {
|
|
48
40
|
label: '爸爸',
|
|
49
|
-
constraints: {
|
|
50
|
-
|
|
51
|
-
'轻度高血压'
|
|
52
|
-
],
|
|
53
|
-
mobility: '步行≤4h'
|
|
54
|
-
},
|
|
55
|
-
evidence: '原话一'
|
|
41
|
+
constraints: { health: ['轻度高血压'], mobility: '步行≤4h' },
|
|
42
|
+
evidence: '原话一',
|
|
56
43
|
}).profiles;
|
|
57
44
|
ps = upsertCompanion(ps, {
|
|
58
45
|
label: '爸爸',
|
|
59
|
-
constraints: {
|
|
60
|
-
|
|
61
|
-
'晕车'
|
|
62
|
-
],
|
|
63
|
-
mobility: '步行≤3h'
|
|
64
|
-
},
|
|
65
|
-
evidence: '原话二:晕车'
|
|
46
|
+
constraints: { health: ['晕车'], mobility: '步行≤3h' },
|
|
47
|
+
evidence: '原话二:晕车',
|
|
66
48
|
}).profiles;
|
|
67
|
-
const p = ps.find(
|
|
68
|
-
assert.deepEqual(p.constraints.health, [
|
|
69
|
-
'轻度高血压',
|
|
70
|
-
'晕车'
|
|
71
|
-
], '数组追加不删史');
|
|
49
|
+
const p = ps.find(x => x.companion_id === '爸爸');
|
|
50
|
+
assert.deepEqual(p.constraints.health, ['轻度高血压', '晕车'], '数组追加不删史');
|
|
72
51
|
assert.equal(p.constraints.mobility, '步行≤3h', '标量取新值');
|
|
73
52
|
assert.equal(p.evidence.length, 2, '证据两条');
|
|
74
53
|
const r3 = upsertCompanion(ps, {
|
|
75
54
|
label: '爸爸',
|
|
76
|
-
constraints: {
|
|
77
|
-
|
|
78
|
-
'晕车'
|
|
79
|
-
],
|
|
80
|
-
mobility: '步行≤3h'
|
|
81
|
-
},
|
|
82
|
-
evidence: '原话二:晕车'
|
|
55
|
+
constraints: { health: ['晕车'], mobility: '步行≤3h' },
|
|
56
|
+
evidence: '原话二:晕车',
|
|
83
57
|
});
|
|
84
58
|
assert.equal(r3.appended, false, '全量相同幂等 no-op');
|
|
85
59
|
});
|
|
86
|
-
pass('多人并存与 id 稳定性', ()=>{
|
|
60
|
+
pass('多人并存与 id 稳定性', () => {
|
|
87
61
|
let ps = [];
|
|
88
|
-
ps = upsertCompanion(ps, {
|
|
89
|
-
|
|
90
|
-
constraints: {
|
|
91
|
-
health: [
|
|
92
|
-
'高血压'
|
|
93
|
-
]
|
|
94
|
-
},
|
|
95
|
-
evidence: 'a'
|
|
96
|
-
}).profiles;
|
|
97
|
-
ps = upsertCompanion(ps, {
|
|
98
|
-
label: '女朋友',
|
|
99
|
-
constraints: {
|
|
100
|
-
prefs: [
|
|
101
|
-
'怕吵'
|
|
102
|
-
]
|
|
103
|
-
},
|
|
104
|
-
evidence: 'b'
|
|
105
|
-
}).profiles;
|
|
62
|
+
ps = upsertCompanion(ps, { label: '爸爸', constraints: { health: ['高血压'] }, evidence: 'a' }).profiles;
|
|
63
|
+
ps = upsertCompanion(ps, { label: '女朋友', constraints: { prefs: ['怕吵'] }, evidence: 'b' }).profiles;
|
|
106
64
|
assert.equal(ps.length, 2);
|
|
107
65
|
assert.equal(companionId('女 朋友'), '女朋友');
|
|
108
66
|
});
|
|
109
67
|
console.log(`\nCOMPANION TESTS: ${n}/4 OK(memory-design P2 守门面)`);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
//# sourceURL=/Users/bytedance/work/gotry/ts/scripts/companion-tests.ts
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dida 会话 cookie 形态探针(只打印 name/domain,永不打印值——红线)。
|
|
3
|
+
* 复用 live E2E 的启动与登录流程,登录后枚举 dida 相关 cookie 的名字与域。
|
|
4
|
+
*/
|
|
5
|
+
import { spawn } from 'node:child_process';
|
|
6
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { chromium } from 'playwright-core';
|
|
10
|
+
const EXT_DIR = new URL('../../extension/', import.meta.url).pathname;
|
|
11
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
12
|
+
async function main() {
|
|
13
|
+
const user = process.env.DIDA_USERNAME ?? '';
|
|
14
|
+
const pass = process.env.DIDA_PASSWORD ?? '';
|
|
15
|
+
if (!user || !pass) {
|
|
16
|
+
console.error('DIDA_USERNAME/DIDA_PASSWORD 必填');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const profileDir = mkdtempSync(join(tmpdir(), 'gotry-dida-probe-'));
|
|
20
|
+
const chrome = spawn('/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', [
|
|
21
|
+
'--remote-debugging-port=9234', `--user-data-dir=${profileDir}`, '--no-first-run',
|
|
22
|
+
`--disable-extensions-except=${EXT_DIR}`, `--load-extension=${EXT_DIR}`, 'about:blank',
|
|
23
|
+
], { stdio: 'ignore' });
|
|
24
|
+
const cleanup = () => { try {
|
|
25
|
+
chrome.kill();
|
|
26
|
+
}
|
|
27
|
+
catch { } try {
|
|
28
|
+
rmSync(profileDir, { recursive: true, force: true });
|
|
29
|
+
}
|
|
30
|
+
catch { } };
|
|
31
|
+
process.on('exit', cleanup);
|
|
32
|
+
await sleep(10_000);
|
|
33
|
+
const browser = await chromium.connectOverCDP('http://127.0.0.1:9234');
|
|
34
|
+
const context = browser.contexts()[0];
|
|
35
|
+
const page = await context.newPage();
|
|
36
|
+
await page.goto('https://portal.dida.com/login', { waitUntil: 'networkidle', timeout: 45_000 });
|
|
37
|
+
await page.evaluate(`(() => {
|
|
38
|
+
document.querySelectorAll('#onetrust-consent-sdk, .nd-cookie-consent, .cdk-overlay-backdrop, .cdk-overlay-container').forEach(el => el.remove())
|
|
39
|
+
})()`).catch(() => { });
|
|
40
|
+
await page.waitForSelector('input[name="username"]', { timeout: 20_000 });
|
|
41
|
+
await page.evaluate(`(() => {
|
|
42
|
+
const u = document.querySelector('input[name="username"]')
|
|
43
|
+
const p = document.querySelector('input[name="password"]')
|
|
44
|
+
const setVal = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set
|
|
45
|
+
setVal.call(u, ${JSON.stringify(user)}); u.dispatchEvent(new Event('input', { bubbles: true }))
|
|
46
|
+
setVal.call(p, ${JSON.stringify(pass)}); p.dispatchEvent(new Event('input', { bubbles: true }))
|
|
47
|
+
document.querySelectorAll('.ant-checkbox-wrapper, label[class*="checkbox"]').forEach(w => w.click())
|
|
48
|
+
})()`);
|
|
49
|
+
await sleep(500);
|
|
50
|
+
await page.evaluate(`(() => {
|
|
51
|
+
Array.from(document.querySelectorAll('button')).find(b => b.textContent.trim().toLowerCase() === 'login')?.click()
|
|
52
|
+
})()`);
|
|
53
|
+
try {
|
|
54
|
+
await page.waitForURL('**/hotel/**', { timeout: 60_000 });
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
console.log('⚠️ 未跳转——等待人工完成验证码 60s');
|
|
58
|
+
try {
|
|
59
|
+
await page.waitForURL('**/hotel/**', { timeout: 60_000 });
|
|
60
|
+
}
|
|
61
|
+
catch { }
|
|
62
|
+
}
|
|
63
|
+
await sleep(4_000);
|
|
64
|
+
console.log('final url:', page.url());
|
|
65
|
+
// 全上下文 cookie(只 name+domain+httpOnly/partitioned 元数据;值不落任何输出)
|
|
66
|
+
const cookies = await context.cookies();
|
|
67
|
+
const dida = cookies.filter((c) => c.domain.includes('dida'));
|
|
68
|
+
console.log(`全上下文 cookie ${cookies.length} 条;dida 相关 ${dida.length} 条:`);
|
|
69
|
+
for (const c of dida) {
|
|
70
|
+
console.log(` - ${c.name} domain=${c.domain} httpOnly=${c.httpOnly} partitioned=${c.partitioned ?? false} expires=${c.expires > 0 ? new Date(c.expires * 1000).toISOString() : 'session'}`);
|
|
71
|
+
}
|
|
72
|
+
// 页面内非 HttpOnly 视角
|
|
73
|
+
const jsNames = await page.evaluate(`(() => document.cookie.split(';').map(s => s.trim().split('=')[0]).filter(Boolean))()`);
|
|
74
|
+
console.log('document.cookie 可见名(非 HttpOnly):', JSON.stringify(jsNames));
|
|
75
|
+
// 认证态校验
|
|
76
|
+
const authed = await page.evaluate(`(async () => {
|
|
77
|
+
try {
|
|
78
|
+
const r = await fetch('https://portal-webapi.dida.com/ApplicationContextApi/GetContext?init=true', { credentials: 'include' })
|
|
79
|
+
const j = await r.json()
|
|
80
|
+
return { success: j?.Success, code: j?.MessageCode, authed: j?.Data?.userIdentityInfo?.isAuthenticated }
|
|
81
|
+
} catch (e) { return { err: String(e) } }
|
|
82
|
+
})()`);
|
|
83
|
+
console.log('GetContext:', JSON.stringify(authed));
|
|
84
|
+
await browser.close();
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
main().catch((e) => { console.error('Fatal:', e); process.exit(1); });
|