@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
|
@@ -1,95 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anything 能力层测试(模拟 hbcli):
|
|
3
|
+
* 1. 真实候选:fake hbcli 按 hotel-be SearchItem wire 形状返 JSON — verdict hit + hits
|
|
4
|
+
* (i18n name zh 优先 / region.centerLat / hotel.latlngCoordinator / star / id 逐字段对齐)
|
|
5
|
+
* 2. 旗标回归:--json 在子命令前(cli.ts 全局旗标预扫描只认子命令前位置)+
|
|
6
|
+
* --content-type / parentDestinationId → --destination-id 映射
|
|
7
|
+
* 3. 空候选:fake hbcli 返 empty JSON — verdict miss
|
|
8
|
+
* 4. exit≠0:fake hbcli 返 stderr+退出 — verdict error + 不抛错
|
|
9
|
+
* 5. 旧 CLI「unknown command」:error 带升级指引(issue #195:裸 unknown command 读起来像工具坏了)
|
|
10
|
+
* 6. 超时:fake hbcli hang — AbortError → verdict error + 不抛错
|
|
11
|
+
* 7. 关键词为空:不调 hbcli — verdict error
|
|
12
|
+
* 8. 缺 hbcli(spawn 失败):用户面是人话(不留 `(exit null)`/ENOENT 进程噪音)
|
|
13
|
+
* 9. 离线开关 GOTRY_HBCLI_LIVE=0|false|off(issue #617):计数 fake hbcli **零次**被调起,
|
|
14
|
+
* 返回既有降级面(与缺 hbcli 同形:ok:false / hbcli-anything-error / verdict:error + 人话原因);
|
|
15
|
+
* 含 PATH 解析的真实调用面(runTurn 的 PoI 探针不传 hbcliBin)
|
|
16
|
+
* 10. 开关未设或任何其他取值:行为与此前完全一致——fake 被调起,hit/miss/error 三值语义不变
|
|
17
|
+
* 11. 既有入参的既有结果在开关关闭时逐字节不变:空 keyword / pre-aborted 先于离线判定
|
|
18
|
+
*
|
|
19
|
+
* 本套件独占 GOTRY_HBCLI_LIVE:开头清掉外部值(`GOTRY_HBCLI_LIVE=0 ./scripts/run-all-tests.sh` 也不得改变 fake 断言),
|
|
20
|
+
* 结束还原。
|
|
21
|
+
*
|
|
22
|
+
* 运行: cd ts && npx tsx scripts/anything-tests.ts
|
|
23
|
+
*/
|
|
1
24
|
import assert from 'node:assert/strict';
|
|
2
|
-
import { mkdtemp, rm, writeFile
|
|
25
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
26
|
import { tmpdir } from 'node:os';
|
|
4
27
|
import { join } from 'node:path';
|
|
5
28
|
import { anythingSearch } from '../capabilities/anything.js';
|
|
6
29
|
const tmp = await mkdtemp(join(tmpdir(), 'anything-test-'));
|
|
30
|
+
const savedLive = process.env.GOTRY_HBCLI_LIVE;
|
|
31
|
+
delete process.env.GOTRY_HBCLI_LIVE;
|
|
7
32
|
try {
|
|
8
33
|
async function fakeBin(name, behaviour) {
|
|
9
34
|
const p = join(tmp, name);
|
|
10
35
|
if (behaviour === 'ok') {
|
|
36
|
+
// hotel-be SearchItem 真实 wire 形状(2026-09-07 对齐源码):i18n name 对象、
|
|
37
|
+
// region.coordinates.centerLat/centerLng、hotel.latlngCoordinator.google、star、matchScore
|
|
11
38
|
await writeFile(p, `#!/bin/sh
|
|
12
39
|
cat <<'JSON'
|
|
13
40
|
{"candidates":[
|
|
14
|
-
{"type":"hotel","
|
|
15
|
-
{"type":"city","
|
|
16
|
-
{"type":"place","
|
|
41
|
+
{"type":"hotel","matchScore":180,"hotel":{"id":"h1","name":{"en":"Park Hyatt","zh":"柏悦酒店"},"star":5,"destinationId":"d1","latlngCoordinator":{"google":{"lat":22.31,"lng":114.16}}}},
|
|
42
|
+
{"type":"city","matchScore":250,"region":{"id":"d1","name":{"en":"Dali","zh":"大理市"},"countryCode":"CN","coordinates":{"centerLat":25.58,"centerLng":100.21}}},
|
|
43
|
+
{"type":"place","matchScore":90,"region":{"id":"r1","name":{"zh":"洱海"}},"place":{"latlngCoordinator":{"google":{"lat":25.74,"lng":100.25}}}}
|
|
17
44
|
]}
|
|
18
45
|
JSON
|
|
19
|
-
`, {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
46
|
+
`, { mode: 0o755 });
|
|
47
|
+
}
|
|
48
|
+
else if (behaviour === 'echo-args') {
|
|
49
|
+
// 旗标回归探针:把 "$@" 回显进候选 region.id(anythingSearch 原样透传为 destinationId)
|
|
50
|
+
await writeFile(p, `#!/bin/sh
|
|
51
|
+
printf '{"candidates":[{"type":"city","region":{"id":"%s"}}]}' "$*"
|
|
52
|
+
`, { mode: 0o755 });
|
|
53
|
+
}
|
|
54
|
+
else if (behaviour === 'empty') {
|
|
23
55
|
await writeFile(p, `#!/bin/sh
|
|
24
56
|
cat <<'JSON'
|
|
25
57
|
{"candidates":[]}
|
|
26
58
|
JSON
|
|
27
|
-
`, {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
} else if (behaviour === 'fail') {
|
|
59
|
+
`, { mode: 0o755 });
|
|
60
|
+
}
|
|
61
|
+
else if (behaviour === 'fail') {
|
|
31
62
|
await writeFile(p, `#!/bin/sh
|
|
32
63
|
echo 'hotelbe down' >&2
|
|
33
64
|
exit 1
|
|
34
|
-
`, {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
65
|
+
`, { mode: 0o755 });
|
|
66
|
+
}
|
|
67
|
+
else if (behaviour === 'unknown-command') {
|
|
68
|
+
// 旧版 CLI(< 2026-09 的 staicli)没有 search anything 子命令时的 commander 原话
|
|
69
|
+
await writeFile(p, `#!/bin/sh
|
|
70
|
+
echo "error: unknown command 'anything'" >&2
|
|
71
|
+
exit 1
|
|
72
|
+
`, { mode: 0o755 });
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// hang forever — test will timeout. Spawn on POSIX reacts to AbortSignal
|
|
76
|
+
// via SIGTERM; SIGKILL is the fallback at +500ms. Some shells (e.g. bash
|
|
77
|
+
// builtins shelled-out via Bun) cache the child pid briefly, so we give
|
|
78
|
+
// a generous upper bound in the assertion (note in code for future).
|
|
38
79
|
await writeFile(p, `#!/bin/sh
|
|
39
80
|
echo 'starting' >&2
|
|
40
81
|
while :; do sleep 5; done
|
|
41
|
-
`, {
|
|
42
|
-
mode: 0o755
|
|
43
|
-
});
|
|
82
|
+
`, { mode: 0o755 });
|
|
44
83
|
}
|
|
45
84
|
return p;
|
|
46
85
|
}
|
|
86
|
+
// 1. 真实候选(wire 形状逐字段)
|
|
47
87
|
const ok = await fakeBin('hbcli-ok', 'ok');
|
|
48
|
-
const r1 = await anythingSearch({
|
|
49
|
-
keyword: 'Park Hyatt',
|
|
50
|
-
hbcliBin: ok
|
|
51
|
-
});
|
|
88
|
+
const r1 = await anythingSearch({ keyword: '大理', hbcliBin: ok });
|
|
52
89
|
assert.equal(r1.verdict, 'hit');
|
|
53
90
|
assert.equal(r1.hits.length, 3);
|
|
54
91
|
assert.ok(r1.evidence.includes('hbcli-anything@'));
|
|
55
|
-
|
|
92
|
+
const [hotelHit, cityHit, placeHit] = r1.hits;
|
|
93
|
+
assert.equal(hotelHit.type, 'hotel');
|
|
94
|
+
assert.equal(hotelHit.name, '柏悦酒店', 'hotel i18n name 应 zh 优先');
|
|
95
|
+
assert.equal(hotelHit.hotelId, 'h1');
|
|
96
|
+
assert.equal(hotelHit.destinationId, 'd1', 'hotel 候选 destinationId 取所属目的地');
|
|
97
|
+
assert.equal(hotelHit.star, 5);
|
|
98
|
+
assert.equal(hotelHit.latitude, 22.31);
|
|
99
|
+
assert.equal(hotelHit.longitude, 114.16);
|
|
100
|
+
assert.equal(hotelHit.score, 180);
|
|
101
|
+
assert.equal(cityHit.type, 'city');
|
|
102
|
+
assert.equal(cityHit.name, '大理市', 'region i18n name 应 zh 优先');
|
|
103
|
+
assert.equal(cityHit.destinationId, 'd1');
|
|
104
|
+
assert.equal(cityHit.latitude, 25.58, 'city 坐标取 region.coordinates.centerLat');
|
|
105
|
+
assert.equal(cityHit.longitude, 100.21);
|
|
106
|
+
assert.equal(placeHit.type, 'place');
|
|
107
|
+
assert.equal(placeHit.name, '洱海');
|
|
108
|
+
assert.equal(placeHit.latitude, 25.74, 'place 坐标回退 place.latlngCoordinator');
|
|
109
|
+
console.log('1. 真实候选 wire 形状(hotel/city/place 逐字段)OK');
|
|
110
|
+
// 2. 旗标回归:--json 必须在子命令前(cli.ts 全局旗标预扫描只认子命令前位置);
|
|
111
|
+
// contentType → --content-type;parentDestinationId → --destination-id(上游 CLI 旗标名)
|
|
112
|
+
const echo = await fakeBin('hbcli-echo-args', 'echo-args');
|
|
113
|
+
const r2 = await anythingSearch({ keyword: '大理', contentType: 'city', parentDestinationId: 'd1', hbcliBin: echo });
|
|
114
|
+
assert.equal(r2.verdict, 'hit');
|
|
115
|
+
assert.equal(r2.hits[0].destinationId, '--json search anything 大理 --content-type city --destination-id d1', 'spawn 旗标形态(--json 前置/--destination-id 映射)应稳定,实际 ' + r2.hits[0].destinationId);
|
|
116
|
+
console.log('2. 旗标回归(--json 前置 + --destination-id 映射)OK');
|
|
117
|
+
// 3. 空候选
|
|
56
118
|
const empty = await fakeBin('hbcli-empty', 'empty');
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
assert.equal(r2.hits.length, 0);
|
|
63
|
-
console.log('2. nothing → miss OK');
|
|
119
|
+
const r3 = await anythingSearch({ keyword: 'nothing', hbcliBin: empty });
|
|
120
|
+
assert.equal(r3.verdict, 'miss');
|
|
121
|
+
assert.equal(r3.hits.length, 0);
|
|
122
|
+
console.log('3. nothing → miss OK');
|
|
123
|
+
// 4. exit≠0
|
|
64
124
|
const fail = await fakeBin('hbcli-fail', 'fail');
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const hang = await fakeBin('hbcli-hang', 'hang');
|
|
74
|
-
const r4 = await anythingSearch({
|
|
75
|
-
keyword: 'x',
|
|
76
|
-
hbcliBin: hang,
|
|
77
|
-
timeoutMs: 800
|
|
78
|
-
});
|
|
79
|
-
assert.equal(r4.verdict, 'error', 'timeout 应判 error');
|
|
80
|
-
console.log(`4. hang/timeout → error OK (latency=${r4.latencyMs}ms)`);
|
|
81
|
-
const r5 = await anythingSearch({
|
|
82
|
-
keyword: ' '
|
|
83
|
-
});
|
|
125
|
+
const r4 = await anythingSearch({ keyword: 'x', hbcliBin: fail });
|
|
126
|
+
assert.equal(r4.verdict, 'error');
|
|
127
|
+
assert.equal(r4.ok, false);
|
|
128
|
+
assert.match(r4.evidence, /error/);
|
|
129
|
+
console.log('4. fail → error (降级) OK');
|
|
130
|
+
// 5. 旧 CLI unknown command → error 带升级指引(issue #195)
|
|
131
|
+
const old = await fakeBin('hbcli-old', 'unknown-command');
|
|
132
|
+
const r5 = await anythingSearch({ keyword: 'x', hbcliBin: old });
|
|
84
133
|
assert.equal(r5.verdict, 'error');
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
});
|
|
134
|
+
assert.match(r5.error ?? '', /hbcli update|gotry setup/, 'error 应带升级指引,实际 ' + r5.error);
|
|
135
|
+
assert.match(r5.evidence, /unknown command/, 'evidence 保留上游原话(溯源)');
|
|
136
|
+
console.log(`5. 旧 CLI unknown command → 升级指引 OK(${r5.error})`);
|
|
137
|
+
// 6. 超时:fake hang 60s,我们给 800ms 超时,期望 verdict=error
|
|
138
|
+
// (latency 断言范围放宽,因为 sandbox node spawn 的 abort 行为不稳定)
|
|
139
|
+
const hang = await fakeBin('hbcli-hang', 'hang');
|
|
140
|
+
const r6 = await anythingSearch({ keyword: 'x', hbcliBin: hang, timeoutMs: 800 });
|
|
141
|
+
assert.equal(r6.verdict, 'error', 'timeout 应判 error');
|
|
142
|
+
console.log(`6. hang/timeout → error OK (latency=${r6.latencyMs}ms)`);
|
|
143
|
+
// 7. 空 keyword
|
|
144
|
+
const r7 = await anythingSearch({ keyword: ' ' });
|
|
145
|
+
assert.equal(r7.verdict, 'error');
|
|
146
|
+
console.log('7. empty keyword → error OK');
|
|
147
|
+
// 8. 缺 hbcli(spawn 失败):用户面必须是人话。这条消息会直接渲染进用户回复
|
|
148
|
+
// (loop.ts 的 D-7a PoI 探针),旧实现回落成 `(exit null)` 这类进程噪音
|
|
149
|
+
// (2026-09-22 端到端走查在「我订了酒店:The Title East Wing Rawai」上实测到)。
|
|
150
|
+
const r8 = await anythingSearch({ keyword: 'The Title East Wing Rawai', hbcliBin: join(tmp, 'definitely-missing-hbcli') });
|
|
151
|
+
assert.equal(r8.verdict, 'error', '缺 hbcli 应判 error 而非抛错');
|
|
152
|
+
assert.ok(r8.error && r8.error.length > 0, '必须给出原因');
|
|
153
|
+
assert.ok(!/(exit null|ENOENT|spawn )/i.test(r8.error), `用户面不得出现进程噪音,实得:${r8.error}`);
|
|
154
|
+
assert.ok(/hbcli/.test(r8.error), `原因须指明是 hbcli,实得:${r8.error}`);
|
|
155
|
+
assert.ok(r8.evidence.includes('[实时API:hbcli-anything@error@'), '证据链 tag 形态不变');
|
|
156
|
+
console.log(`8. 缺 hbcli → 人话降级 OK(${r8.error})`);
|
|
157
|
+
// ---- 9-11. 离线开关 GOTRY_HBCLI_LIVE(issue #617) -------------------------------------------
|
|
158
|
+
// 计数 fake:每被调起一次就往 calls.log 追加一行(只用 shell 内建,PATH 为空也能跑),再按 kind 回包。
|
|
159
|
+
const callsLog = join(tmp, 'calls.log');
|
|
160
|
+
const invocations = async () => {
|
|
161
|
+
try {
|
|
162
|
+
return (await readFile(callsLog, 'utf8')).split('\n').filter(Boolean).length;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
async function countedBin(name, kind, dir = tmp) {
|
|
169
|
+
const p = join(dir, name);
|
|
170
|
+
const reply = kind === 'hit'
|
|
171
|
+
? `printf '%s' '{"candidates":[{"type":"city","matchScore":9,"region":{"id":"d1","name":{"zh":"大理市"}}}]}'`
|
|
172
|
+
: kind === 'miss'
|
|
173
|
+
? `printf '%s' '{"candidates":[]}'`
|
|
174
|
+
: `printf 'hotelbe down\\n' >&2\nexit 1`;
|
|
175
|
+
await writeFile(p, `#!/bin/sh\nprintf 'call\\n' >> '${callsLog}'\n${reply}\n`, { mode: 0o755 });
|
|
176
|
+
return p;
|
|
177
|
+
}
|
|
178
|
+
async function withLive(value, body) {
|
|
179
|
+
const prev = process.env.GOTRY_HBCLI_LIVE;
|
|
180
|
+
if (value === undefined)
|
|
181
|
+
delete process.env.GOTRY_HBCLI_LIVE;
|
|
182
|
+
else
|
|
183
|
+
process.env.GOTRY_HBCLI_LIVE = value;
|
|
184
|
+
try {
|
|
185
|
+
return await body();
|
|
186
|
+
}
|
|
187
|
+
finally {
|
|
188
|
+
if (prev === undefined)
|
|
189
|
+
delete process.env.GOTRY_HBCLI_LIVE;
|
|
190
|
+
else
|
|
191
|
+
process.env.GOTRY_HBCLI_LIVE = prev;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const countedHit = await countedBin('hbcli-counted-hit', 'hit');
|
|
195
|
+
const countedMiss = await countedBin('hbcli-counted-miss', 'miss');
|
|
196
|
+
const countedFail = await countedBin('hbcli-counted-fail', 'fail');
|
|
197
|
+
// 9. 关闭:0/false/off(不分大小写、忽略首尾空白)→ 零 spawn + 既有降级面
|
|
198
|
+
for (const off of ['0', 'false', 'off', 'OFF', 'False', ' 0 ', ' off\n']) {
|
|
199
|
+
const before = await invocations();
|
|
200
|
+
const r9 = await withLive(off, () => anythingSearch({ keyword: '大理', hbcliBin: countedHit }));
|
|
201
|
+
assert.equal(await invocations(), before, `GOTRY_HBCLI_LIVE=${JSON.stringify(off)}:fake hbcli 必须被调起 0 次`);
|
|
202
|
+
assert.equal(r9.ok, false);
|
|
203
|
+
assert.equal(r9.via, 'hbcli-anything-error');
|
|
204
|
+
assert.equal(r9.verdict, 'error', '「没查」不得说成「查无」:走 error 降级面而非 miss');
|
|
205
|
+
assert.equal(r9.hits, undefined);
|
|
206
|
+
assert.deepEqual(Object.keys(r9).sort(), Object.keys(r8).sort(), '与缺 hbcli 降级同形(同一组字段)');
|
|
207
|
+
assert.match(r9.evidence, /^\[实时API:hbcli-anything@offline@[^\]]+\]/, `证据链应标 offline,实得:${r9.evidence}`);
|
|
208
|
+
assert.ok(r9.error && /hbcli/.test(r9.error) && /GOTRY_HBCLI_LIVE/.test(r9.error), `原因须说明 hbcli 通道被开关关闭,实得:${r9.error}`);
|
|
209
|
+
assert.ok(!/(exit null|ENOENT|spawn |timeout)/i.test(r9.error), `用户面不得出现进程噪音(也不得含 timeout:效应层只重试 timeout 类),实得:${r9.error}`);
|
|
210
|
+
}
|
|
211
|
+
// 9b. 真实调用面:不传 hbcliBin('hbcli' 由 PATH 解析,runTurn 的 PoI 探针即如此)。
|
|
212
|
+
// 未设开关的 positive control 证明 fake 确实可达——否则「零次」可能是空断言。
|
|
213
|
+
const pathBin = join(tmp, 'pathbin');
|
|
214
|
+
await mkdir(pathBin);
|
|
215
|
+
await countedBin('hbcli', 'hit', pathBin);
|
|
216
|
+
const savedPath = process.env.PATH;
|
|
217
|
+
const savedHome = process.env.HOME;
|
|
218
|
+
process.env.PATH = `${pathBin}:${savedPath ?? ''}`;
|
|
219
|
+
process.env.HOME = join(tmp, 'empty-home'); // 已知安装位回退落在空目录,碰不到开发机真实 hbcli
|
|
220
|
+
try {
|
|
221
|
+
const base = await invocations();
|
|
222
|
+
const control = await withLive(undefined, () => anythingSearch({ keyword: '大理' }));
|
|
223
|
+
assert.equal(control.verdict, 'hit', `positive control:未设开关时 PATH 上的 fake 应被调起,实得:${control.evidence}`);
|
|
224
|
+
assert.equal(await invocations(), base + 1, 'positive control 恰好调起一次');
|
|
225
|
+
const off = await withLive('0', () => anythingSearch({ keyword: '大理' }));
|
|
226
|
+
assert.equal(off.verdict, 'error');
|
|
227
|
+
assert.match(off.evidence, /@offline@/);
|
|
228
|
+
assert.equal(await invocations(), base + 1, 'GOTRY_HBCLI_LIVE=0:PATH 上的 hbcli 零次调起');
|
|
229
|
+
}
|
|
230
|
+
finally {
|
|
231
|
+
if (savedPath === undefined)
|
|
232
|
+
delete process.env.PATH;
|
|
233
|
+
else
|
|
234
|
+
process.env.PATH = savedPath;
|
|
235
|
+
if (savedHome === undefined)
|
|
236
|
+
delete process.env.HOME;
|
|
237
|
+
else
|
|
238
|
+
process.env.HOME = savedHome;
|
|
239
|
+
}
|
|
240
|
+
console.log('9. GOTRY_HBCLI_LIVE=0|false|off → 零 spawn + 既有降级面(显式 hbcliBin 与 PATH 解析两种调用面)OK');
|
|
241
|
+
// 10. 未设或任何其他取值 = 与此前完全一致:fake 被调起,hit/miss/error 三值语义不变
|
|
242
|
+
for (const on of [undefined, '1']) {
|
|
243
|
+
const before = await invocations();
|
|
244
|
+
const h = await withLive(on, () => anythingSearch({ keyword: '大理', hbcliBin: countedHit }));
|
|
245
|
+
const m = await withLive(on, () => anythingSearch({ keyword: 'nothing', hbcliBin: countedMiss }));
|
|
246
|
+
const f = await withLive(on, () => anythingSearch({ keyword: 'x', hbcliBin: countedFail }));
|
|
247
|
+
assert.equal(await invocations(), before + 3, `GOTRY_HBCLI_LIVE=${String(on)}:三只 fake 各被调起一次`);
|
|
248
|
+
assert.equal(h.verdict, 'hit');
|
|
249
|
+
assert.equal(h.hits[0].name, '大理市');
|
|
250
|
+
assert.match(h.evidence, /^\[实时API:hbcli-anything@\d{4}-/, 'hit 证据链形态不变');
|
|
251
|
+
assert.equal(m.verdict, 'miss');
|
|
252
|
+
assert.equal(m.hits.length, 0);
|
|
253
|
+
assert.equal(f.verdict, 'error');
|
|
254
|
+
assert.match(f.evidence, /^\[实时API:hbcli-anything@error@/, 'error 证据链形态不变(不是 offline)');
|
|
255
|
+
assert.equal(f.error, 'hbcli 报错:hotelbe down', '既有原因串逐字节不变');
|
|
256
|
+
}
|
|
257
|
+
// 非 0/false/off 的取值一律视为开启(与此前一致):空串/1/true/on/yes/no/disabled/00/任意字符串
|
|
258
|
+
for (const other of ['', 'true', 'on', 'yes', 'no', 'disabled', '00', 'maybe']) {
|
|
259
|
+
const before = await invocations();
|
|
260
|
+
const r = await withLive(other, () => anythingSearch({ keyword: '大理', hbcliBin: countedHit }));
|
|
261
|
+
assert.equal(r.verdict, 'hit', `GOTRY_HBCLI_LIVE=${JSON.stringify(other)} 不是关闭值,应保持 live,实得:${r.evidence}`);
|
|
262
|
+
assert.equal(await invocations(), before + 1);
|
|
263
|
+
}
|
|
264
|
+
console.log('10. 开关未设/其他取值 → 与此前一致(hit/miss/error 三值 + 既有原因串)OK');
|
|
265
|
+
// 11. 关闭时,既有入参的既有结果逐字节不变:空 keyword / pre-aborted 先于离线判定(两者本就零 spawn)
|
|
266
|
+
const before11 = await invocations();
|
|
267
|
+
const rEmpty = await withLive('0', () => anythingSearch({ keyword: ' ', hbcliBin: countedHit }));
|
|
268
|
+
assert.equal(rEmpty.error, 'keyword is required');
|
|
269
|
+
assert.match(rEmpty.evidence, /^\[实时API:hbcli-anything@error@[^\]]+\] keyword empty$/);
|
|
270
|
+
const preAborted = new AbortController();
|
|
271
|
+
preAborted.abort();
|
|
272
|
+
const rAbort = await withLive('0', () => anythingSearch({ keyword: '大理', hbcliBin: countedHit, signal: preAborted.signal }));
|
|
273
|
+
assert.equal(rAbort.error, 'aborted by host signal');
|
|
274
|
+
assert.match(rAbort.evidence, /^\[实时API:hbcli-anything@abort@[^\]]+\] pre-aborted, zero spawn$/);
|
|
275
|
+
assert.equal(await invocations(), before11, '两条既有降级路径均零 spawn');
|
|
276
|
+
console.log('11. 关闭态下空 keyword / pre-aborted 的既有结果不变 OK');
|
|
277
|
+
console.log('\nANYTHING TESTS: 11/11 OK(hbcli fake + wire 对齐 + 降级诚实 + #617 离线开关零 spawn)');
|
|
278
|
+
}
|
|
279
|
+
finally {
|
|
280
|
+
if (savedLive === undefined)
|
|
281
|
+
delete process.env.GOTRY_HBCLI_LIVE;
|
|
282
|
+
else
|
|
283
|
+
process.env.GOTRY_HBCLI_LIVE = savedLive;
|
|
284
|
+
await rm(tmp, { recursive: true, force: true });
|
|
92
285
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
//# sourceURL=/Users/bytedance/work/gotry/ts/scripts/anything-tests.ts
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public Client contract: package export, lazy-CJS loader shape, keyed DSH
|
|
3
|
+
* registrations, and defensive rendering from the runtime `block` payload.
|
|
4
|
+
* Browser execution is covered separately by dsh-artifact-web-e2e.ts.
|
|
5
|
+
*/
|
|
6
|
+
import assert from 'node:assert/strict';
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import vm from 'node:vm';
|
|
11
|
+
const ROOT = join(import.meta.dirname, '..', '..');
|
|
12
|
+
const source = readFileSync(join(ROOT, 'client/client.js'), 'utf8');
|
|
13
|
+
const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'));
|
|
14
|
+
assert.equal(pkg.exports?.['./client'], './client/client.js');
|
|
15
|
+
assert.equal(pkg.dsh?.client?.platform, 'web');
|
|
16
|
+
assert.ok(pkg.dsh?.client?.inject?.includes('@deepseek-ai/dsh-client-ui-tool'));
|
|
17
|
+
assert.ok(pkg.files?.includes('client/'));
|
|
18
|
+
assert.ok(source.includes('window.__ModuleLoader__.load'));
|
|
19
|
+
assert.ok(source.includes("tool.call.toolview"));
|
|
20
|
+
assert.ok(source.includes("gotry_artifacts_list"));
|
|
21
|
+
assert.ok(source.includes("gotry_artifacts_read"));
|
|
22
|
+
assert.ok(!source.includes('@deepseek-ai/dsh-client-ui-tool/lib/'));
|
|
23
|
+
const registrations = [];
|
|
24
|
+
const context = vm.createContext({
|
|
25
|
+
window: { __ModuleLoader__: { load: (registration) => registrations.push(registration) } },
|
|
26
|
+
console,
|
|
27
|
+
});
|
|
28
|
+
new vm.Script(source, { filename: 'client/client.js' }).runInContext(context);
|
|
29
|
+
assert.equal(registrations.length, 1);
|
|
30
|
+
assert.equal(registrations[0].id, '@danceiny/gotry');
|
|
31
|
+
function createElement(type, props, ...children) {
|
|
32
|
+
return { type, props: props ?? {}, children };
|
|
33
|
+
}
|
|
34
|
+
const fakeReact = {
|
|
35
|
+
createElement,
|
|
36
|
+
useState(initial) { return [initial, () => { }]; },
|
|
37
|
+
};
|
|
38
|
+
const clientExports = registrations[0].factory(name => {
|
|
39
|
+
assert.equal(name, 'react');
|
|
40
|
+
return fakeReact;
|
|
41
|
+
});
|
|
42
|
+
assert.deepEqual(Object.keys(clientExports), ['apply', 'inject']);
|
|
43
|
+
const slotRegistrations = [];
|
|
44
|
+
const registrationContext = {
|
|
45
|
+
inject(_deps, callback) { callback(registrationContext); },
|
|
46
|
+
slots: {
|
|
47
|
+
inject(_slot, callback) { callback(); },
|
|
48
|
+
register(definition, view) {
|
|
49
|
+
slotRegistrations.push({ key: definition.key, view });
|
|
50
|
+
return () => { };
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
clientExports.apply(registrationContext);
|
|
55
|
+
assert.deepEqual(slotRegistrations.map(item => item.key), ['gotry_artifacts_list', 'gotry_artifacts_read']);
|
|
56
|
+
assert.equal(slotRegistrations[0].view, slotRegistrations[1].view);
|
|
57
|
+
const view = slotRegistrations[0].view;
|
|
58
|
+
const listTree = view({
|
|
59
|
+
toolName: 'gotry_artifacts_list',
|
|
60
|
+
block: { kind: 'tool-result', isError: false, meta: { shape: 'paths', paths: ['/tmp/trip-2027.md'], total: 1, truncated: false }, content: [] },
|
|
61
|
+
});
|
|
62
|
+
assert.equal(listTree.props['data-gotry-artifact-card'], 'list');
|
|
63
|
+
assert.equal(listTree.props['data-gotry-artifact-state'], 'ok');
|
|
64
|
+
const malformedTree = view({ toolName: 'gotry_artifacts_read', block: { kind: 'tool-result', isError: false, meta: { shape: 'bad' }, content: [{ type: 'text', text: 'raw fallback' }] } });
|
|
65
|
+
assert.equal(malformedTree.props['data-gotry-artifact-card'], 'read');
|
|
66
|
+
assert.equal(malformedTree.props['data-gotry-artifact-state'], 'ok');
|
|
67
|
+
const requireRoot = createRequire(join(ROOT, 'package.json'));
|
|
68
|
+
assert.ok(requireRoot.resolve('./client/client.js'));
|
|
69
|
+
// issue #441: an HTML artifact is previewed as source text only. The client half
|
|
70
|
+
// must not gain a raw-HTML injection API or an embedded browsing context.
|
|
71
|
+
for (const banned of ['dangerouslySetInnerHTML', 'innerHTML', 'outerHTML', 'insertAdjacentHTML', 'srcdoc', 'iframe', 'document.write', 'eval(']) {
|
|
72
|
+
assert.ok(!source.includes(banned), `client bundle must not use ${banned}`);
|
|
73
|
+
}
|
|
74
|
+
const textNodes = [];
|
|
75
|
+
const elementTypes = [];
|
|
76
|
+
const forbiddenProps = [];
|
|
77
|
+
function walkTree(node) {
|
|
78
|
+
if (typeof node === 'string') {
|
|
79
|
+
textNodes.push(node);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (Array.isArray(node)) {
|
|
83
|
+
for (const child of node)
|
|
84
|
+
walkTree(child);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const item = node;
|
|
88
|
+
if (!item || typeof item !== 'object')
|
|
89
|
+
return;
|
|
90
|
+
elementTypes.push(item.type);
|
|
91
|
+
for (const key of ['dangerouslySetInnerHTML', 'innerHTML', 'outerHTML', 'srcdoc']) {
|
|
92
|
+
if (key in (item.props ?? {}))
|
|
93
|
+
forbiddenProps.push(key);
|
|
94
|
+
}
|
|
95
|
+
for (const child of item.children ?? [])
|
|
96
|
+
walkTree(child);
|
|
97
|
+
}
|
|
98
|
+
const hostileLines = [
|
|
99
|
+
'<script>globalThis.__gotry441Pwned = true</script>',
|
|
100
|
+
'<img src=x onerror="globalThis.__gotry441Pwned = true">',
|
|
101
|
+
'<iframe src="https://example.invalid/track"></iframe>',
|
|
102
|
+
'<a href="javascript:globalThis.__gotry441Pwned=true">点击</a>',
|
|
103
|
+
];
|
|
104
|
+
const hostileTree = view({
|
|
105
|
+
toolName: 'gotry_artifacts_read',
|
|
106
|
+
block: {
|
|
107
|
+
kind: 'tool-result',
|
|
108
|
+
isError: false,
|
|
109
|
+
meta: {
|
|
110
|
+
path: '/tmp/trip-2027.html', offset: 1, totalLines: hostileLines.length, lang: 'html',
|
|
111
|
+
source: 'cwd', version: 'a1b2c3d4e5f6',
|
|
112
|
+
lines: hostileLines.map((text, index) => ({ number: index + 1, text })),
|
|
113
|
+
},
|
|
114
|
+
content: [],
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
assert.equal(hostileTree.props['data-gotry-artifact-card'], 'read');
|
|
118
|
+
assert.equal(hostileTree.props['data-gotry-artifact-state'], 'ok');
|
|
119
|
+
walkTree(hostileTree);
|
|
120
|
+
assert.deepEqual(forbiddenProps, [], `预览树不得出现 raw HTML 注入 props,实际 ${forbiddenProps.join(',')}`);
|
|
121
|
+
assert.ok(!elementTypes.includes('iframe'), '预览树不得创建 iframe');
|
|
122
|
+
for (const line of hostileLines) {
|
|
123
|
+
assert.ok(textNodes.includes(line), `HTML 源码行必须以文本节点原样呈现:${line}`);
|
|
124
|
+
}
|
|
125
|
+
function collect(node, out = []) {
|
|
126
|
+
if (Array.isArray(node)) {
|
|
127
|
+
for (const child of node)
|
|
128
|
+
collect(child, out);
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
const item = node;
|
|
132
|
+
if (!item || typeof item !== 'object')
|
|
133
|
+
return out;
|
|
134
|
+
out.push({ type: item.type, props: item.props ?? {}, children: item.children ?? [] });
|
|
135
|
+
for (const child of item.children ?? [])
|
|
136
|
+
collect(child, out);
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
function textsOf(node) {
|
|
140
|
+
const out = [];
|
|
141
|
+
const visit = (value) => {
|
|
142
|
+
if (typeof value === 'string') {
|
|
143
|
+
out.push(value);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (Array.isArray(value)) {
|
|
147
|
+
for (const child of value)
|
|
148
|
+
visit(child);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const item = value;
|
|
152
|
+
if (!item || typeof item !== 'object')
|
|
153
|
+
return;
|
|
154
|
+
for (const child of item.children ?? [])
|
|
155
|
+
visit(child);
|
|
156
|
+
};
|
|
157
|
+
visit(node.children);
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
// HTML list entries are opened through the host's native HTML preview, which is a
|
|
161
|
+
// different action from reading source text: it must be labelled and explained.
|
|
162
|
+
const mixedTree = view({
|
|
163
|
+
toolName: 'gotry_artifacts_list',
|
|
164
|
+
block: {
|
|
165
|
+
kind: 'tool-result', isError: false,
|
|
166
|
+
meta: { shape: 'paths', paths: ['/tmp/trip-2027.md', '/tmp/trip-2027.html', '/tmp/Trip-2027.HTM'], total: 3, truncated: false },
|
|
167
|
+
content: [],
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
const buttons = collect(mixedTree).filter(node => node.type === 'button');
|
|
171
|
+
assert.equal(buttons.length, 3, `3 条路径应有 3 个按钮,实际 ${buttons.length}`);
|
|
172
|
+
const mdButton = buttons.find(node => node.props['data-gotry-artifact-path'] === '/tmp/trip-2027.md');
|
|
173
|
+
const htmlButtons = buttons.filter(node => String(node.props['data-gotry-artifact-path']).toLowerCase().endsWith('.htm') ||
|
|
174
|
+
String(node.props['data-gotry-artifact-path']).toLowerCase().endsWith('.html'));
|
|
175
|
+
assert.equal(htmlButtons.length, 2, `.html 与 .HTM 都应识别为 HTML 预览,实际 ${htmlButtons.length}`);
|
|
176
|
+
assert.equal(mdButton?.props['data-gotry-artifact-open'], 'artifact', 'markdown 打开动作保持原文');
|
|
177
|
+
assert.equal(mdButton?.props['aria-label'], 'Open artifact /tmp/trip-2027.md', 'markdown 可访问标签不变');
|
|
178
|
+
assert.equal(mdButton?.props.title, undefined, 'markdown 不新增提示(行为不变)');
|
|
179
|
+
assert.ok(!textsOf(mdButton).includes('HTML 预览'), 'markdown 条目不显示 HTML 预览徽标');
|
|
180
|
+
for (const button of htmlButtons) {
|
|
181
|
+
const path = String(button.props['data-gotry-artifact-path']);
|
|
182
|
+
assert.equal(button.props['data-gotry-artifact-open'], 'html-preview', `HTML 条目应标记为原生预览动作:${path}`);
|
|
183
|
+
assert.equal(button.props['aria-label'], `Open HTML preview ${path}`, `HTML 条目可访问标签应显式声明 HTML 预览:${path}`);
|
|
184
|
+
const title = String(button.props.title ?? '');
|
|
185
|
+
assert.ok(title.includes('脚本') && title.includes('可能'), `提示应说明脚本可能运行:${title}`);
|
|
186
|
+
assert.ok(!/sandbox|renderer|iframe|宿主|DSH|dsh/i.test(title), `提示不得使用基础设施术语:${title}`);
|
|
187
|
+
assert.ok(textsOf(button).includes('HTML 预览'), `HTML 条目应有可见的 HTML 预览指示:${path}`);
|
|
188
|
+
}
|
|
189
|
+
// Source read card: HTML is identified as source text, still plain text nodes.
|
|
190
|
+
const htmlSourceTree = view({
|
|
191
|
+
toolName: 'gotry_artifacts_read',
|
|
192
|
+
block: {
|
|
193
|
+
kind: 'tool-result', isError: false,
|
|
194
|
+
meta: {
|
|
195
|
+
path: '/tmp/trip-2027.html', offset: 1, totalLines: hostileLines.length, lang: 'html',
|
|
196
|
+
source: 'cwd', version: 'a1b2c3d4e5f6',
|
|
197
|
+
lines: hostileLines.map((text, index) => ({ number: index + 1, text })),
|
|
198
|
+
},
|
|
199
|
+
content: [],
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
assert.equal(htmlSourceTree.props['data-gotry-artifact-source'], 'html', 'HTML 读卡应显式标注源码来源');
|
|
203
|
+
assert.ok(textsOf(htmlSourceTree).includes('HTML 源码预览(只读文本)'), 'HTML 读卡标题应明确是源码文本');
|
|
204
|
+
const htmlSourceNodes = collect(htmlSourceTree);
|
|
205
|
+
assert.ok(!htmlSourceNodes.some(node => 'dangerouslySetInnerHTML' in node.props || 'innerHTML' in node.props), 'HTML 读卡不得注入原始 HTML');
|
|
206
|
+
assert.ok(htmlSourceNodes.every(node => node.type !== 'iframe'), 'HTML 读卡不得内嵌浏览上下文');
|
|
207
|
+
for (const line of hostileLines) {
|
|
208
|
+
assert.ok(textsOf({ children: [htmlSourceTree] }).includes(line), `HTML 读卡仍以文本节点逐字呈现:${line}`);
|
|
209
|
+
}
|
|
210
|
+
const mdSourceTree = view({
|
|
211
|
+
toolName: 'gotry_artifacts_read',
|
|
212
|
+
block: {
|
|
213
|
+
kind: 'tool-result', isError: false,
|
|
214
|
+
meta: { path: '/tmp/trip-2027.md', offset: 1, totalLines: 1, lang: 'markdown', source: 'cwd', version: 'f6e5d4c3b2a1', lines: [{ number: 1, text: '# 行程' }] },
|
|
215
|
+
content: [],
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
assert.equal(mdSourceTree.props['data-gotry-artifact-source'], undefined, 'markdown 读卡不得被标注为 HTML 源码');
|
|
219
|
+
assert.ok(textsOf(mdSourceTree).includes('产物预览'), 'markdown 读卡标题保持原样');
|
|
220
|
+
console.log('ARTIFACT CLIENT CONTRACT: package export + web manifest + lazy loader + 2 keyed views + malformed fallback + HTML source-as-text + explicit HTML preview action OK');
|