@danceiny/gotry 0.0.1-rc.9 → 0.2.0-rc.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -219
- package/README.zh-CN.md +222 -0
- package/bin/gotry-backend.js +53 -0
- package/bin/gotry-booking-copilot.js +53 -0
- package/bin/gotry-bootstrap.js +1788 -0
- package/bin/gotry-inner.js +733 -83
- package/bin/gotry-process-liveness.d.ts +27 -0
- package/bin/gotry-process-liveness.js +97 -0
- package/bin/gotry-runtime-resolution.d.ts +27 -0
- package/bin/gotry-runtime-resolution.js +49 -0
- package/bin/gotry.js +1 -1
- package/client/client.js +203 -0
- package/cordis.gotry-patch.yml +80 -56
- package/data/airline-airports.json +39 -0
- package/data/stations-12306-verify.json +524 -0
- package/dist/capabilities/agent-reach-deep.js +45 -122
- package/dist/capabilities/agent-reach.js +101 -146
- package/dist/capabilities/anything.js +161 -94
- package/dist/capabilities/artifacts.js +370 -0
- package/dist/capabilities/channel-health.js +131 -0
- package/dist/capabilities/channel-registry.js +256 -0
- package/dist/capabilities/doctor.js +553 -0
- package/dist/capabilities/effect.js +394 -0
- package/dist/capabilities/external-event.js +149 -0
- package/dist/capabilities/fact-log.js +55 -0
- package/dist/capabilities/flyai-config.js +337 -0
- package/dist/capabilities/flyai.js +947 -110
- package/dist/capabilities/fx-contract.js +293 -0
- package/dist/capabilities/geo-atlas.js +246 -0
- package/dist/capabilities/ground-transfer.js +676 -0
- package/dist/capabilities/hbcli.js +260 -123
- package/dist/capabilities/hotelbyte-transaction.js +194 -0
- package/dist/capabilities/incident-log.js +131 -61
- package/dist/capabilities/itinerary-artifact.js +190 -0
- package/dist/capabilities/itinerary-deck-artifact.js +194 -0
- package/dist/capabilities/itinerary-deck-export.js +339 -0
- package/dist/capabilities/lavish-local.js +924 -0
- package/dist/capabilities/model-override.js +38 -0
- package/dist/capabilities/opensky.js +54 -43
- package/dist/capabilities/provider-detail-sanitize.js +64 -0
- package/dist/capabilities/resilience.js +191 -0
- package/dist/capabilities/route-provider-conformance.js +341 -0
- package/dist/capabilities/session/action-cache.js +50 -44
- package/dist/capabilities/session/adapters/ctrip-flight.js +120 -62
- package/dist/capabilities/session/adapters/ctrip-hotel.js +222 -0
- package/dist/capabilities/session/adapters/dida-portal.js +274 -0
- package/dist/capabilities/session/adapters/meituan-local.js +35 -54
- package/dist/capabilities/session/adapters/rail-12306.js +342 -0
- package/dist/capabilities/session/benchmark.js +189 -0
- package/dist/capabilities/session/bridge-ledger.js +224 -0
- package/dist/capabilities/session/extension-bridge.js +636 -0
- package/dist/capabilities/session/extension-channel.js +109 -0
- package/dist/capabilities/session/extension-distribution.js +230 -0
- package/dist/capabilities/session/extract.js +31 -21
- package/dist/capabilities/session/golden-score.js +114 -0
- package/dist/capabilities/session/health-watch.js +215 -0
- package/dist/capabilities/session/read-guard.js +75 -24
- package/dist/capabilities/session/static-flight-golden.js +150 -0
- package/dist/capabilities/session/transport.js +97 -83
- package/dist/capabilities/session/wizard.js +117 -0
- package/dist/capabilities/session-consent.js +178 -0
- package/dist/capabilities/session-login.js +170 -0
- package/dist/capabilities/session-search.js +635 -44
- package/dist/capabilities/spawn-bounded.js +203 -0
- package/dist/capabilities/sponsor-plugin.js +341 -0
- package/dist/capabilities/visa-policy.js +140 -0
- package/dist/capabilities/weather.js +243 -138
- package/dist/data/session-golden-20.json +25 -0
- package/dist/data/sf-golden-manifest.json +103 -0
- package/dist/data/sf-static-routes.json +91 -0
- package/dist/scripts/action-cache-tests.js +24 -59
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +242 -0
- package/dist/scripts/agent-planning-turn-deadline-tests.js +185 -0
- package/dist/scripts/agent-reach-deep-tests.js +30 -40
- package/dist/scripts/agent-reach-error-tests.js +354 -0
- package/dist/scripts/agent-reach-tests.js +26 -18
- package/dist/scripts/agent-reach-wrapper-tests.js +50 -50
- package/dist/scripts/anything-path-measure-tests.js +133 -0
- package/dist/scripts/anything-path-measure.js +150 -0
- package/dist/scripts/anything-tests.js +250 -60
- package/dist/scripts/artifact-client-contract-tests.js +220 -0
- package/dist/scripts/artifacts-capability-tests.js +727 -0
- package/dist/scripts/async-collect.js +50 -11
- package/dist/scripts/benchmark-environment-bridge-e2e.js +741 -0
- package/dist/scripts/benchmark-environment-bridge-tests.js +1452 -0
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +126 -0
- package/dist/scripts/booking-copilot-availability-policy-tests.js +265 -0
- package/dist/scripts/booking-copilot-bin-proof-tests.js +66 -0
- package/dist/scripts/booking-copilot-cleanup-proof-helper.js +76 -0
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +144 -0
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +424 -0
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +2839 -0
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +309 -0
- package/dist/scripts/booking-copilot-dsh-readiness-proof.js +238 -0
- package/dist/scripts/booking-copilot-dsh-warmer-proof.js +126 -0
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +156 -0
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +70 -0
- package/dist/scripts/booking-copilot-intent-proof-tests.js +422 -0
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +184 -0
- package/dist/scripts/booking-copilot-planner-live-probe.js +95 -0
- package/dist/scripts/booking-copilot-proof-runtime.js +42 -0
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +210 -0
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +2349 -0
- package/dist/scripts/booking-copilot-server-proof-tests.js +784 -0
- package/dist/scripts/booking-copilot-startup-proof-tests.js +94 -0
- package/dist/scripts/booking-executor-tests.js +80 -0
- package/dist/scripts/booking-recovery-chain-tests.js +182 -0
- package/dist/scripts/booking-saga-tests.js +125 -0
- package/dist/scripts/booking-surface-contract-proof-tests.js +212 -0
- package/dist/scripts/bootstrap-tests.js +1549 -0
- package/dist/scripts/bridge-events-tests.js +239 -0
- package/dist/scripts/bridge-ledger-tests.js +428 -0
- package/dist/scripts/build-changelog.js +238 -0
- package/dist/scripts/build-metrics-report.js +331 -0
- package/dist/scripts/changelog-tests.js +118 -0
- package/dist/scripts/channel-probe-tests.js +103 -0
- package/dist/scripts/channel-probe.js +169 -0
- package/dist/scripts/channel-registry-tests.js +208 -0
- package/dist/scripts/city-scenario-tier-tests.js +234 -0
- package/dist/scripts/companion-tests.js +25 -70
- package/dist/scripts/dida-cookie-probe.js +87 -0
- package/dist/scripts/dida-runner-stop-tests.js +311 -0
- package/dist/scripts/dida-sw-probe.js +114 -0
- package/dist/scripts/diff-test.js +17 -12
- package/dist/scripts/doctor-tests.js +600 -0
- package/dist/scripts/drill-multiuser-ledger-tests.js +1076 -0
- package/dist/scripts/drill-sdk-descendant-cleanup-tests.js +606 -0
- package/dist/scripts/drill-w2a-sensor-tests.js +788 -0
- package/dist/scripts/dsh-artifact-e2e.js +279 -0
- package/dist/scripts/dsh-artifact-web-e2e.js +675 -0
- package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
- package/dist/scripts/dsh-llm-tests.js +437 -0
- package/dist/scripts/dsh-runtime-closure-tests.js +182 -0
- package/dist/scripts/dsh-runtime-closure.js +162 -0
- package/dist/scripts/dsh-session-v3-migration-proof.js +109 -0
- package/dist/scripts/dsh-subprocess-local-proof.js +150 -0
- package/dist/scripts/dsh-target-closure-proof.js +178 -0
- package/dist/scripts/effect-tests.js +539 -0
- package/dist/scripts/engine-run.js +1 -3
- package/dist/scripts/engine-tests.js +16 -10
- package/dist/scripts/evaluation-cadence-tests.js +174 -0
- package/dist/scripts/evaluation-contract-tests.js +268 -0
- package/dist/scripts/extension-distribution-cli.js +39 -0
- package/dist/scripts/extension-distribution-tests.js +443 -0
- package/dist/scripts/extension-tests.js +988 -0
- package/dist/scripts/external-event-tests.js +350 -0
- package/dist/scripts/fact-gate-tests.js +869 -0
- package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
- package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
- package/dist/scripts/flight-malformed-tests.js +159 -0
- package/dist/scripts/flyai-setup-tests.js +228 -0
- package/dist/scripts/flyai-setup-tool-tests.js +54 -0
- package/dist/scripts/flyai-tests.js +692 -0
- package/dist/scripts/flyai-tool-registration-contract-tests.js +91 -0
- package/dist/scripts/fx-contract-tests.js +201 -0
- package/dist/scripts/g5-guard-tests.js +150 -0
- package/dist/scripts/g5-guard.js +193 -0
- package/dist/scripts/gen-lunar-anchors.js +106 -0
- package/dist/scripts/geo-atlas-tests.js +283 -0
- package/dist/scripts/gotry-backend-kernel-tests.js +206 -0
- package/dist/scripts/gotry-try-demo-tests.js +79 -0
- package/dist/scripts/gotry-try-demo.js +49 -0
- package/dist/scripts/ground-transfer-tests.js +956 -0
- package/dist/scripts/hbcli-e2e-tests.js +159 -0
- package/dist/scripts/hbcli-live-optin-tests.js +96 -0
- package/dist/scripts/hbcli-release-contract-tests.js +122 -0
- package/dist/scripts/hbcli-tests.js +121 -69
- package/dist/scripts/health-watch-cli.js +73 -0
- package/dist/scripts/hotel-date-gate-tests.js +496 -0
- package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
- package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
- package/dist/scripts/i18n-tests.js +41 -0
- package/dist/scripts/incident-tests.js +281 -87
- package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
- package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
- package/dist/scripts/issue-271-liveness-tests.js +569 -0
- package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
- package/dist/scripts/issue-289-model-retry-tests.js +441 -0
- package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
- package/dist/scripts/issue-338-home-city-e2e.js +577 -0
- package/dist/scripts/issue343-real-entry-e2e.js +160 -0
- package/dist/scripts/issue436-persisted-routing-e2e.js +247 -0
- package/dist/scripts/itinerary-artifact-tests.js +368 -0
- package/dist/scripts/itinerary-deck-artifact-tests.js +416 -0
- package/dist/scripts/itinerary-deck-export-tests.js +552 -0
- package/dist/scripts/itinerary-deck-tests.js +435 -0
- package/dist/scripts/itinerary-html-tests.js +426 -0
- package/dist/scripts/journey-tests.js +22 -27
- package/dist/scripts/kernel-manifest-evidence.js +109 -0
- package/dist/scripts/kernel-manifest-gate.js +126 -0
- package/dist/scripts/kernel-manifest-tests.js +212 -0
- package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
- package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
- package/dist/scripts/kernel-manifest.js +369 -0
- package/dist/scripts/lavish-local-tests.js +1226 -0
- package/dist/scripts/lavish-product-tools-tests.js +537 -0
- package/dist/scripts/ledger-repair-apply-tests.js +316 -0
- package/dist/scripts/ledger-repair-plan-tests.js +375 -0
- package/dist/scripts/ledger-tests.js +382 -246
- package/dist/scripts/ledger-workflow-crash.js +10 -20
- package/dist/scripts/m3-cohort-tests.js +714 -0
- package/dist/scripts/m3-cohort.js +310 -0
- package/dist/scripts/managed-dsh-cleanup-diagnostic-proof.js +184 -0
- package/dist/scripts/managed-dsh-close-failure-proof.js +172 -0
- package/dist/scripts/managed-dsh-run-port-proof.js +153 -0
- package/dist/scripts/managed-dsh-terminal-proof.js +252 -0
- package/dist/scripts/map-tools-vendor-package-proof.js +365 -0
- package/dist/scripts/memory-capture-tests.js +242 -61
- package/dist/scripts/memory-decay-tests.js +18 -52
- package/dist/scripts/memory-lifecycle-tests.js +657 -0
- package/dist/scripts/memory-lifecycle.js +196 -0
- package/dist/scripts/memory-metrics.js +24 -12
- package/dist/scripts/memory-value-report.js +631 -0
- package/dist/scripts/memory-value-tests.js +328 -0
- package/dist/scripts/metrics-report-tests.js +151 -0
- package/dist/scripts/model-override-e2e.js +137 -0
- package/dist/scripts/nightly-evidence-tests.js +203 -0
- package/dist/scripts/nightly-evidence.js +303 -0
- package/dist/scripts/nudge-digest.js +39 -28
- package/dist/scripts/observability-tests.js +381 -0
- package/dist/scripts/onboarding-tests.js +161 -0
- package/dist/scripts/opensky-check.js +18 -13
- package/dist/scripts/opensky-tests.js +28 -28
- package/dist/scripts/outcome-projection-tests.js +427 -0
- package/dist/scripts/persona-sim-tests.js +603 -0
- package/dist/scripts/persona-sim.js +1081 -0
- package/dist/scripts/persona-surface-guard-tests.js +49 -0
- package/dist/scripts/pnpm-dsh-closure-proof.js +17 -0
- package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
- package/dist/scripts/price-drift-tests.js +340 -0
- package/dist/scripts/price-drift-watch.js +447 -0
- package/dist/scripts/probe-poi-tests.js +53 -45
- package/dist/scripts/product-metrics.js +483 -0
- package/dist/scripts/publish-preverify.js +110 -0
- package/dist/scripts/realtime-pricing-tests.js +76 -0
- package/dist/scripts/recall-tests.js +513 -0
- package/dist/scripts/replay-async.js +22 -18
- package/dist/scripts/replay-real.js +18 -18
- package/dist/scripts/replay.js +54 -82
- package/dist/scripts/route-provider-conformance-tests.js +391 -0
- package/dist/scripts/session-attach-diagnose.js +20 -20
- package/dist/scripts/session-attach-poc.js +34 -37
- package/dist/scripts/session-benchmark.js +180 -0
- package/dist/scripts/session-dida-live-e2e.js +199 -0
- package/dist/scripts/session-extract-tests.js +14 -35
- package/dist/scripts/session-link-tests.js +478 -0
- package/dist/scripts/session-login.js +27 -46
- package/dist/scripts/session-tests.js +796 -96
- package/dist/scripts/session-zone-crash.js +49 -0
- package/dist/scripts/session-zone-ledger-tests.js +432 -0
- package/dist/scripts/session-zone-observation-tests.js +492 -0
- package/dist/scripts/session-zone-observe.js +135 -0
- package/dist/scripts/session-zone-wiring-tests.js +566 -0
- package/dist/scripts/session-zones-tests.js +374 -0
- package/dist/scripts/sf-live-benchmark.js +367 -0
- package/dist/scripts/sf-live-challenge-stop-tests.js +453 -0
- package/dist/scripts/sf-live-cli-tests.js +92 -0
- package/dist/scripts/sf-live-options.js +46 -0
- package/dist/scripts/sf-manifest-tests.js +30 -0
- package/dist/scripts/sf-manifest.js +33 -0
- package/dist/scripts/sf-soft-score-tests.js +67 -0
- package/dist/scripts/sf-summary-tests.js +324 -0
- package/dist/scripts/sf-summary.js +525 -0
- package/dist/scripts/share-tests.js +432 -0
- package/dist/scripts/skeleton-check.js +16 -14
- package/dist/scripts/skeleton-integration-test.js +28 -11
- package/dist/scripts/skills-contract-tests.js +51 -46
- package/dist/scripts/smoke-session-gate-tests.js +21 -0
- package/dist/scripts/smoke.js +690 -246
- package/dist/scripts/spawn-bounded-abort-tests.js +244 -0
- package/dist/scripts/sponsor-reuse-tests.js +262 -0
- package/dist/scripts/state-cli-tests.js +226 -101
- package/dist/scripts/state-cli.js +565 -198
- package/dist/scripts/state-repair-cli.js +66 -0
- package/dist/scripts/state-repair-tests.js +140 -0
- package/dist/scripts/static-golden-tests.js +165 -0
- package/dist/scripts/time-eval-tests.js +212 -192
- package/dist/scripts/travel-timeline-tests.js +29 -93
- package/dist/scripts/turn-handoff-collect-tests.js +169 -0
- package/dist/scripts/turn-handoff-collect.js +161 -0
- package/dist/scripts/turn-policy-tests.js +55 -0
- package/dist/scripts/typed-contract-canary.js +176 -0
- package/dist/scripts/tz-resolver-sanity.js +50 -0
- package/dist/scripts/tz-resolver-tests.js +584 -0
- package/dist/scripts/unified-tests.js +60 -20
- package/dist/scripts/visa-policy-tests.js +62 -0
- package/dist/scripts/weather-tests.js +276 -41
- package/dist/scripts/wish-channel-gate-tests.js +54 -0
- package/dist/scripts/write-gate-tests.js +522 -0
- package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
- package/dist/scripts/z3-lifecycle-tests.js +170 -0
- package/dist/scripts/z3-race-repeat-tests.js +39 -0
- package/dist/scripts/z3-race-tests.js +46 -0
- package/dist/src/artifact-gate.js +829 -0
- package/dist/src/backend/kernel.js +91 -0
- package/dist/src/backend/modules/booking-copilot.js +92 -0
- package/dist/src/backend/modules/booking-executor.js +124 -0
- package/dist/src/backend/modules/session-search.js +551 -0
- package/dist/src/benchmark-agent-conformance.js +578 -0
- package/dist/src/benchmark-environment-bridge.js +515 -0
- package/dist/src/benchmark-headless-child-diagnostics.js +166 -0
- package/dist/src/benchmark-tool-isolation.js +151 -0
- package/dist/src/bookable-facts.js +533 -0
- package/dist/src/booking-saga.js +97 -0
- package/dist/src/booking-surface/availability-policy.js +697 -0
- package/dist/src/booking-surface/booking-intent.js +43 -0
- package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
- package/dist/src/booking-surface/canonical-schema.js +116 -0
- package/dist/src/booking-surface/contracts.js +75 -0
- package/dist/src/booking-surface/dsh-planner.js +1854 -0
- package/dist/src/booking-surface/dsh-plugin.js +378 -0
- package/dist/src/booking-surface/dsh-worker.js +98 -0
- package/dist/src/booking-surface/error-codes.js +231 -0
- package/dist/src/booking-surface/index.js +9 -0
- package/dist/src/booking-surface/managed-dsh-cleanup-diagnostic.js +97 -0
- package/dist/src/booking-surface/managed-dsh-run-port.js +244 -0
- package/dist/src/booking-surface/profile.js +24 -0
- package/dist/src/booking-surface/recovery-chain.js +367 -0
- package/dist/src/booking-surface/runtime.js +1968 -0
- package/dist/src/booking-surface/server.js +674 -0
- package/dist/src/booking-surface/startup.js +168 -0
- package/dist/src/booking-surface/validation.js +261 -0
- package/dist/src/bridge.js +20 -13
- package/dist/src/city-scenario-tier.js +378 -0
- package/dist/src/companions.js +48 -74
- package/dist/src/contracts.js +21 -43
- package/dist/src/dsh-llm.js +171 -115
- package/dist/src/engine.js +276 -193
- package/dist/src/evaluation-cadence.js +97 -0
- package/dist/src/evaluation-contracts.js +283 -0
- package/dist/src/flight-pack-adapter.js +54 -0
- package/dist/src/flight-pack-contract.js +2 -0
- package/dist/src/flyai-setup-tool.js +70 -0
- package/dist/src/gotry-backend.js +125 -0
- package/dist/src/hotel-date-gate.js +118 -0
- package/dist/src/i18n.js +88 -0
- package/dist/src/index.js +2385 -881
- package/dist/src/itinerary-deck.js +139 -0
- package/dist/src/itinerary-doc-shared.js +661 -0
- package/dist/src/itinerary-html.js +131 -0
- package/dist/src/journey.js +155 -108
- package/dist/src/lavish-tools.js +400 -0
- package/dist/src/ledger-repair-apply.js +538 -0
- package/dist/src/ledger-repair-plan.js +463 -0
- package/dist/src/loop.js +331 -161
- package/dist/src/m3-cohort.js +1577 -0
- package/dist/src/memory-capture.js +180 -32
- package/dist/src/memory-decay.js +30 -14
- package/dist/src/memory-lifecycle.js +1310 -0
- package/dist/src/memory-utility.js +27 -40
- package/dist/src/mock-llm.js +36 -61
- package/dist/src/model.js +81 -36
- package/dist/src/outcome-projection.js +746 -0
- package/dist/src/realtime-pricing.js +103 -0
- package/dist/src/recall/card.js +74 -0
- package/dist/src/recall/evaluator.js +140 -0
- package/dist/src/recall/integration.js +17 -0
- package/dist/src/recall/tick.js +166 -0
- package/dist/src/session-link/plan-it-action.js +48 -0
- package/dist/src/session-link/session-link.js +248 -0
- package/dist/src/session-zone-ledger.js +312 -0
- package/dist/src/session-zone-observation.js +354 -0
- package/dist/src/session-zone-wiring.js +658 -0
- package/dist/src/session-zones.js +510 -0
- package/dist/src/share/adapters.js +51 -0
- package/dist/src/share/share-consent.js +76 -0
- package/dist/src/share/share-deck.js +58 -0
- package/dist/src/share/share-token.js +146 -0
- package/dist/src/slot-spec.js +84 -106
- package/dist/src/state-ledger.js +696 -524
- package/dist/src/state-repair.js +299 -0
- package/dist/src/subagent-job-id-guard.js +58 -0
- package/dist/src/time-anchor.js +231 -33
- package/dist/src/tool-packet.js +20 -8
- package/dist/src/travel-slots.js +58 -35
- package/dist/src/travel-timeline.js +44 -63
- package/dist/src/turn-deadline.js +287 -0
- package/dist/src/turn-policy.js +118 -0
- package/dist/src/tz-resolver.js +143 -0
- package/dist/src/unified.js +600 -360
- package/dist/src/wish-pool.js +42 -12
- package/dist/src/write-gate.js +495 -0
- package/dist/src/z3-shared.js +284 -0
- package/extension/README.md +21 -0
- package/extension/README.zh-CN.md +21 -0
- package/extension/background.js +463 -0
- package/extension/content-bridge.js +270 -0
- package/extension/content-main.js +111 -0
- package/extension/manifest.json +60 -0
- package/package.json +407 -14
- package/schemas/booking.intent.schema.json +193 -0
- package/schemas/booking.surface.schema.json +2622 -0
- package/ts/capabilities/agent-reach-deep.ts +114 -0
- package/ts/capabilities/agent-reach.ts +198 -0
- package/ts/capabilities/anything.ts +282 -0
- package/ts/capabilities/artifacts.ts +428 -0
- package/ts/capabilities/fact-log.ts +54 -0
- package/ts/capabilities/flyai-config.ts +388 -0
- package/ts/capabilities/flyai.ts +1274 -0
- package/ts/capabilities/hbcli.ts +253 -71
- package/ts/capabilities/incident-log.ts +88 -59
- package/ts/capabilities/itinerary-artifact.ts +214 -0
- package/ts/capabilities/itinerary-deck-artifact.ts +218 -0
- package/ts/capabilities/itinerary-deck-export.ts +378 -0
- package/ts/capabilities/opensky.ts +121 -0
- package/ts/capabilities/session/action-cache.ts +148 -0
- package/ts/capabilities/session/adapters/ctrip-flight.ts +181 -0
- package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
- package/ts/capabilities/session/adapters/dida-portal.ts +338 -0
- package/ts/capabilities/session/adapters/meituan-local.ts +70 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +392 -0
- package/ts/capabilities/session/benchmark.ts +276 -0
- package/ts/capabilities/session/bridge-ledger.ts +333 -0
- package/ts/capabilities/session/extension-bridge.ts +794 -0
- package/ts/capabilities/session/extension-channel.ts +144 -0
- package/ts/capabilities/session/extension-distribution.ts +266 -0
- package/ts/capabilities/session/extract.ts +54 -0
- package/ts/capabilities/session/golden-score.ts +139 -0
- package/ts/capabilities/session/health-watch.ts +264 -0
- package/ts/capabilities/session/read-guard.ts +141 -0
- package/ts/capabilities/session/static-flight-golden.ts +209 -0
- package/ts/capabilities/session/transport.ts +146 -0
- package/ts/capabilities/session/wizard.ts +140 -0
- package/ts/capabilities/session-consent.ts +211 -0
- package/ts/capabilities/session-login.ts +208 -0
- package/ts/capabilities/session-search.ts +893 -0
- package/ts/capabilities/weather.ts +330 -0
- package/ts/dsh-runtime/vendor/README.md +68 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +405 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +404 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +1584 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/icon.svg +5 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +595 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +58 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +131 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/geo.js +232 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +120 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +40 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/staticmap-route.js +115 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +267 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +90 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +23 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/geo.d.ts +61 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/staticmap-route.d.ts +31 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +75 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +68 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +97 -0
- package/ts/package.json +293 -3
- package/ts/scripts/async-collect.ts +91 -0
- package/ts/scripts/map-tools-vendor-package-proof.ts +404 -0
- package/ts/scripts/skeleton-check.ts +1 -1
- package/ts/scripts/skeleton-integration-test.ts +23 -1
- package/ts/scripts/state-cli.ts +586 -0
- package/ts/scripts/turn-handoff-collect.ts +178 -0
- package/ts/src/artifact-gate.ts +991 -0
- package/ts/src/benchmark-agent-conformance.ts +619 -0
- package/ts/src/benchmark-environment-bridge.ts +538 -0
- package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
- package/ts/src/benchmark-tool-isolation.ts +166 -0
- package/ts/src/bookable-facts.ts +837 -0
- package/ts/src/booking-saga.ts +133 -0
- package/ts/src/booking-surface/availability-policy.ts +522 -0
- package/ts/src/booking-surface/booking-intent.ts +86 -0
- package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
- package/ts/src/booking-surface/canonical-schema.js +116 -0
- package/ts/src/booking-surface/contracts.ts +475 -0
- package/ts/src/booking-surface/dsh-planner.ts +1949 -0
- package/ts/src/booking-surface/dsh-plugin.js +378 -0
- package/ts/src/booking-surface/dsh-worker.ts +110 -0
- package/ts/src/booking-surface/error-codes.ts +241 -0
- package/ts/src/booking-surface/index.ts +9 -0
- package/ts/src/booking-surface/managed-dsh-cleanup-diagnostic.ts +151 -0
- package/ts/src/booking-surface/managed-dsh-run-port.ts +273 -0
- package/ts/src/booking-surface/profile.ts +42 -0
- package/ts/src/booking-surface/recovery-chain.ts +409 -0
- package/ts/src/booking-surface/runtime.ts +1793 -0
- package/ts/src/booking-surface/server.ts +733 -0
- package/ts/src/booking-surface/startup.ts +201 -0
- package/ts/src/booking-surface/validation.ts +217 -0
- package/ts/src/companions.ts +113 -0
- package/ts/src/contracts.ts +17 -3
- package/ts/src/dsh-llm.ts +152 -17
- package/ts/src/flyai-setup-tool.ts +77 -0
- package/ts/src/index.ts +2123 -200
- package/ts/src/itinerary-deck.ts +189 -0
- package/ts/src/itinerary-doc-shared.ts +741 -0
- package/ts/src/itinerary-html.ts +171 -0
- package/ts/src/loop.ts +234 -26
- package/ts/src/memory-capture.ts +283 -0
- package/ts/src/memory-decay.ts +55 -0
- package/ts/src/memory-utility.ts +70 -0
- package/ts/src/mock-llm.ts +4 -8
- package/ts/src/model.ts +88 -2
- package/ts/src/recall/card.ts +106 -0
- package/ts/src/recall/evaluator.ts +170 -0
- package/ts/src/recall/integration.ts +35 -0
- package/ts/src/recall/tick.ts +213 -0
- package/ts/src/session-link/plan-it-action.ts +78 -0
- package/ts/src/session-link/session-link.ts +275 -0
- package/ts/src/share/adapters.ts +93 -0
- package/ts/src/share/share-consent.ts +105 -0
- package/ts/src/share/share-deck.ts +85 -0
- package/ts/src/share/share-token.ts +166 -0
- package/ts/src/slot-spec.ts +172 -0
- package/ts/src/state-ledger.ts +1166 -0
- package/ts/src/subagent-job-id-guard.ts +72 -0
- package/ts/src/time-anchor.ts +338 -0
- package/ts/src/tool-packet.ts +32 -0
- package/ts/src/travel-slots.ts +219 -0
- package/ts/src/travel-timeline.ts +83 -0
- package/ts/src/turn-deadline.ts +366 -0
- package/ts/src/turn-policy.ts +154 -0
- package/ts/src/unified.ts +444 -105
- package/ts/src/wish-pool.ts +79 -0
- package/dist/scripts/session-attach-wait.js +0 -41
package/bin/gotry-inner.js
CHANGED
|
@@ -6,31 +6,42 @@
|
|
|
6
6
|
* gotry help # help
|
|
7
7
|
*
|
|
8
8
|
* 工作流程:
|
|
9
|
-
* 1. 解析 argv + .env(provider-neutral → DEEPSEEK_API_KEY)
|
|
9
|
+
* 1. 解析 argv + .env(provider-neutral → DEEPSEEK_API_KEY/DEEPSEEK_BASE_URL)
|
|
10
10
|
* 2. 定位 dsh runtime:
|
|
11
|
-
* a. repo checkout →
|
|
12
|
-
* b.
|
|
13
|
-
*
|
|
11
|
+
* a. repo checkout / npm 安装 → root package 解析锁定的 @deepseek-ai/dsh
|
|
12
|
+
* b. legacy vendored fallback 已按 D-27 移除(issue #120)——root 缺失即
|
|
13
|
+
* fail-closed 报错指重装,不再有「解析成功却跑不起来」的玄学形态
|
|
14
14
|
* 3. 运行时生成 patch(os.tmpdir):把 gotry-tools 插件路径重写为按本包
|
|
15
15
|
* 位置解析的绝对路径 —— 仓内 cordis.gotry-patch.yml 里的 name 行只是
|
|
16
16
|
* 占位(本机绝对路径),随 tarball 分发后对其他机器必错。
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { spawn } from 'node:child_process'
|
|
20
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
|
|
21
|
-
import { fileURLToPath } from 'node:url'
|
|
19
|
+
import { spawn, spawnSync } from 'node:child_process'
|
|
20
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
21
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
22
22
|
import { dirname, join } from 'node:path'
|
|
23
|
-
import { tmpdir } from 'node:os'
|
|
23
|
+
import { homedir, tmpdir } from 'node:os'
|
|
24
24
|
import { createRequire } from 'node:module'
|
|
25
|
+
import {
|
|
26
|
+
benchmarkRuntimeSupported,
|
|
27
|
+
resolveDshPackage,
|
|
28
|
+
selectDshCwd,
|
|
29
|
+
selectDshRuntime,
|
|
30
|
+
supportsNodeVersion,
|
|
31
|
+
} from './gotry-runtime-resolution.js'
|
|
32
|
+
import { onboardingSkipReason, orchestrateWebLaunch } from './gotry-bootstrap.js'
|
|
33
|
+
import { spawnOwnedChild, terminateOwnedChild } from './gotry-process-liveness.js'
|
|
25
34
|
|
|
26
35
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
27
36
|
const repoRoot = join(here, '..')
|
|
37
|
+
const sourceCheckoutMode = existsSync(join(repoRoot, '.git'))
|
|
28
38
|
const require_ = createRequire(import.meta.url)
|
|
39
|
+
const rootRequire = createRequire(join(repoRoot, 'package.json'))
|
|
29
40
|
|
|
30
41
|
// --- 环境 .env 加载(provider-neutral) ---
|
|
31
42
|
// npm 安装模式优先读用户当前目录的 .env(包目录内不该有凭证);repo 检出读仓根。
|
|
32
|
-
const
|
|
33
|
-
const envCandidates =
|
|
43
|
+
const sourceDshEarly = resolveDshPackage(rootRequire)
|
|
44
|
+
const envCandidates = sourceCheckoutMode || sourceDshEarly
|
|
34
45
|
? [join(repoRoot, '.env')]
|
|
35
46
|
: [join(process.cwd(), '.env'), join(repoRoot, '.env')]
|
|
36
47
|
const envLines = []
|
|
@@ -46,6 +57,22 @@ for (const line of envLines) {
|
|
|
46
57
|
if (process.env.LLM_API_KEY && !process.env.DEEPSEEK_API_KEY) {
|
|
47
58
|
process.env.DEEPSEEK_API_KEY = process.env.LLM_API_KEY
|
|
48
59
|
}
|
|
60
|
+
// base 同点映射(issue #48):dsh 侧 llm-deepseek 读 DEEPSEEK_BASE_URL 并拼
|
|
61
|
+
// `${base}/chat/completions`(与 ts/src/dsh-llm.ts 同语义,自定义端点一般含 /v1);
|
|
62
|
+
// 只映射 key 不映射 base 时,OpenAI 兼容 key 会被发往 DeepSeek 官方端点必然 401。
|
|
63
|
+
if (process.env.LLM_BASE_URL && !process.env.DEEPSEEK_BASE_URL) {
|
|
64
|
+
process.env.DEEPSEEK_BASE_URL = process.env.LLM_BASE_URL
|
|
65
|
+
}
|
|
66
|
+
// model 映射(issue #77):LLM_MODEL 显式存在时让它真正驱动 dsh 会话面——
|
|
67
|
+
// ① 这里映射为 GOTRY_LLM_MODEL 传给 gotry-tools 插件,在 agent/request 瀑布做
|
|
68
|
+
// 内存覆盖(dsh settings 用户层 ~/.dsh 压过 composition 层,单靠 patch 不够;
|
|
69
|
+
// 覆盖零持久化,进程退即散,不改写用户设置);
|
|
70
|
+
// ② 下方运行时 patch 追加两条 by-id 覆盖:agent-default-model 默认模型 +
|
|
71
|
+
// llm-deepseek 目录条目(web UI 模型页可见、元数据可记账)。
|
|
72
|
+
// 不设 LLM_MODEL 则两条都不动——默认路径(组合配置或用户 web 设置)面不变。
|
|
73
|
+
if (process.env.LLM_MODEL && !process.env.GOTRY_LLM_MODEL) {
|
|
74
|
+
process.env.GOTRY_LLM_MODEL = process.env.LLM_MODEL
|
|
75
|
+
}
|
|
49
76
|
|
|
50
77
|
// --- argv 解析 ---
|
|
51
78
|
const args = process.argv.slice(2)
|
|
@@ -56,53 +83,88 @@ if (help) {
|
|
|
56
83
|
|
|
57
84
|
Usage:
|
|
58
85
|
gotry web # dsh Web UI on http://127.0.0.1:3080
|
|
86
|
+
# (interactive TTY) 启动前若发现可自动安装的可选能力,问一次是否配置;
|
|
87
|
+
# 确认复用 doctor --fix 幂等安装,拒绝立即继续。CI/非 TTY/全健康不弹问。
|
|
88
|
+
# 跳过该问: --no-onboarding 或 GOTRY_ONBOARDING_SKIP=1
|
|
89
|
+
gotry setup # 扩展就位检查/指引(商店一键装)
|
|
90
|
+
gotry setup calendar # 可选日历(CalDAV 工作窗口)挂载开关:默认关;--off 关闭;--status 查看
|
|
91
|
+
gotry doctor # 可选依赖体检:扩展/agent-reach/hbcli/flyai/sidebar 状态 + 补装指引
|
|
92
|
+
gotry doctor --fix # 体检 + 按报告补装(hbcli 官方脚本 / agent-reach pip / sidebar 插件)
|
|
59
93
|
gotry "一段完整任务..." # headless 一问一答
|
|
60
94
|
gotry help # this help
|
|
61
|
-
|
|
62
|
-
Prerequisites:
|
|
63
|
-
• Node 22+
|
|
64
|
-
• \`.env\` 里 LLM_API_KEY (DeepSeek / OpenAI 兼容均可)
|
|
95
|
+
gotry --version # 版本号(bug 报告必填项)
|
|
65
96
|
|
|
66
97
|
Detail: https://github.com/Danceiny/gotry — README
|
|
67
98
|
`)
|
|
68
99
|
process.exit(0)
|
|
69
100
|
}
|
|
70
101
|
|
|
71
|
-
//
|
|
72
|
-
|
|
102
|
+
// repoRoot 在 npm 安装与源码检出两种形态下都是包根,version 统一从这里取
|
|
103
|
+
if (args[0] === '-v' || args[0] === '--version') {
|
|
104
|
+
console.log(JSON.parse(readFileSync(join(repoRoot, 'package.json'), 'utf-8')).version)
|
|
105
|
+
process.exit(0)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// mode 决定路径: 'web'/'setup'/'doctor'/'help' 是字面命令;否则第一段 args[0] 是任务本身的一部分
|
|
109
|
+
const literal = new Set(['web', 'setup', 'doctor', 'help', '-h', '--help'])
|
|
73
110
|
const isLiteral = literal.has(args[0])
|
|
74
111
|
const mode = isLiteral ? args[0] : 'headless'
|
|
75
112
|
const rest = isLiteral ? args.slice(1) : args
|
|
76
113
|
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (existsSync(vendoredDsh)) {
|
|
82
|
-
dshBin = vendoredDsh
|
|
83
|
-
dshCwd = join(repoRoot, 'ts/dsh-runtime')
|
|
84
|
-
} else {
|
|
85
|
-
try {
|
|
86
|
-
dshBin = require_.resolve('@deepseek-ai/dsh/lib/bin.js')
|
|
87
|
-
dshCwd = process.cwd() // npm 安装:gotry-state 落在用户调用目录
|
|
88
|
-
} catch {
|
|
89
|
-
dshBin = ''
|
|
90
|
-
}
|
|
114
|
+
// setup/doctor:外部依赖自举与体检,不需要 dsh runtime 与 LLM key,同步分发后即退
|
|
115
|
+
if (mode === 'setup' || mode === 'doctor') {
|
|
116
|
+
const r = spawnSync(process.execPath, [join(here, 'gotry-bootstrap.js'), mode, ...rest], { stdio: 'inherit' })
|
|
117
|
+
process.exit(r.status ?? (r.error ? 1 : 0))
|
|
91
118
|
}
|
|
92
119
|
|
|
120
|
+
const benchmarkEnvironmentConfig = process.env.GOTRY_BENCHMARK_ENV_CONFIG
|
|
121
|
+
if (!supportsNodeVersion(process.versions.node)) {
|
|
122
|
+
console.error('[gotry] Node.js 22.15 or newer is required')
|
|
123
|
+
process.exit(1)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// --- dsh runtime 定位(D-27 清偿,issue #120):仅 root manifest/依赖解析;legacy vendored 回退已移除 ---
|
|
127
|
+
const installedPackageMode = !sourceCheckoutMode
|
|
128
|
+
const selectedDsh = selectDshRuntime({
|
|
129
|
+
repoRoot,
|
|
130
|
+
rootResolver: rootRequire,
|
|
131
|
+
benchmark: Boolean(benchmarkEnvironmentConfig),
|
|
132
|
+
})
|
|
133
|
+
let dshBin = selectedDsh?.bin ?? ''
|
|
134
|
+
const dshSource = selectedDsh?.source ?? ''
|
|
135
|
+
const dshCwd = selectedDsh
|
|
136
|
+
? selectDshCwd({
|
|
137
|
+
repoRoot,
|
|
138
|
+
invocationCwd: process.cwd(),
|
|
139
|
+
sourceCheckoutMode,
|
|
140
|
+
benchmark: Boolean(benchmarkEnvironmentConfig),
|
|
141
|
+
})
|
|
142
|
+
: ''
|
|
143
|
+
|
|
93
144
|
if (!dshBin) {
|
|
94
|
-
|
|
95
|
-
|
|
145
|
+
if (benchmarkEnvironmentConfig) {
|
|
146
|
+
console.error('[gotry] benchmark dsh runtime unavailable')
|
|
147
|
+
process.exit(1)
|
|
148
|
+
}
|
|
149
|
+
console.error('[gotry] 找不到 dsh runtime(依赖里没有 @deepseek-ai/dsh;legacy vendored 回退已按 D-27 移除)。')
|
|
150
|
+
console.error('repo checkout: npm ci && npm --prefix ts ci && node scripts/build-dist.mjs;npm 安装: npm install(检查 node_modules)。')
|
|
151
|
+
process.exit(1)
|
|
152
|
+
}
|
|
153
|
+
if (benchmarkEnvironmentConfig && !benchmarkRuntimeSupported(selectedDsh)) {
|
|
154
|
+
console.error('[gotry] benchmark dsh runtime unavailable')
|
|
96
155
|
process.exit(1)
|
|
97
156
|
}
|
|
98
157
|
|
|
99
158
|
// --- 运行时 patch:插件路径按本包位置重写为绝对路径 ---
|
|
100
|
-
//
|
|
101
|
-
// repo
|
|
159
|
+
// 安装包始终指向 dist/ 纯 JS(Node 拒绝 strip node_modules 下的 .ts)。
|
|
160
|
+
// repo 检出仅在显式 tsx loader 可用时走 .ts;否则
|
|
161
|
+
// 使用当前 worktree 已构建的 dist,避免 Node strip-only 拒绝参数属性语法。
|
|
102
162
|
const distEntry = join(repoRoot, 'dist/src/index.js')
|
|
103
163
|
const tsEntry = join(repoRoot, 'ts/src/index.ts')
|
|
104
|
-
const
|
|
105
|
-
const
|
|
164
|
+
const tsxLoaderActive = /(?:^|\s)--(?:import|loader)(?:=|\s)(?:"[^"]*tsx[^"]*"|'[^']*tsx[^']*'|\S*tsx\S*)/.test(process.env.NODE_OPTIONS ?? '')
|
|
165
|
+
const sourceTypeScriptMode = !installedPackageMode && tsxLoaderActive
|
|
166
|
+
const pluginEntry = !sourceTypeScriptMode && existsSync(distEntry) ? distEntry : tsEntry
|
|
167
|
+
const distModuleMode = pluginEntry === distEntry
|
|
106
168
|
const staticPatch = join(repoRoot, 'cordis.gotry-patch.yml')
|
|
107
169
|
if (!existsSync(staticPatch)) {
|
|
108
170
|
console.error(`[gotry] 找不到 patch 配置: ${staticPatch}`)
|
|
@@ -112,16 +174,242 @@ if (!existsSync(pluginEntry)) {
|
|
|
112
174
|
console.error(`[gotry] 找不到插件入口: ${pluginEntry}(包不完整?)`)
|
|
113
175
|
process.exit(1)
|
|
114
176
|
}
|
|
115
|
-
|
|
116
|
-
//
|
|
177
|
+
|
|
178
|
+
// Benchmark mode projects the patch before any optional host plugin is
|
|
179
|
+
// resolved/imported. Non-GoTry (including unknown/future) insert entries are
|
|
180
|
+
// safely discarded; a missing/duplicate gotry-tools entry fails closed.
|
|
181
|
+
function projectBenchmarkPatch(raw, entryPath, configPath) {
|
|
182
|
+
const lines = raw.split('\n')
|
|
183
|
+
const rootInsertKey = /(?:^|[,{}]\s*)(?:insert|'insert'|"insert")\s*:/
|
|
184
|
+
const rootInsertLines = lines
|
|
185
|
+
.map((line, index) => {
|
|
186
|
+
const rootItem = line.match(/^-\s+(.*)$/)?.[1]?.trim()
|
|
187
|
+
const directProperty = line.match(/^ {2}(.*)$/)?.[1]?.trim()
|
|
188
|
+
return rootInsertKey.test(rootItem ?? '') || /^(?:insert|'insert'|"insert")\s*:/.test(directProperty ?? '') ? index : -1
|
|
189
|
+
})
|
|
190
|
+
.filter(index => index >= 0)
|
|
191
|
+
const canonicalInsertStarts = lines
|
|
192
|
+
.map((line, index) => /^- insert:\s*(?:#.*)?$/.test(line) ? index : -1)
|
|
193
|
+
.filter(index => index >= 0)
|
|
194
|
+
if (rootInsertLines.length !== 1 || canonicalInsertStarts.length !== 1 || rootInsertLines[0] !== canonicalInsertStarts[0]) {
|
|
195
|
+
throw new Error('benchmark insert block violation')
|
|
196
|
+
}
|
|
197
|
+
const start = canonicalInsertStarts[0]
|
|
198
|
+
let end = start + 1
|
|
199
|
+
while (end < lines.length) {
|
|
200
|
+
const line = lines[end]
|
|
201
|
+
if (line.trim() !== '' && !/^\s/.test(line) && !line.trim().startsWith('#')) break
|
|
202
|
+
end += 1
|
|
203
|
+
}
|
|
204
|
+
const body = lines.slice(start + 1, end)
|
|
205
|
+
const firstItem = body.find(line => line.trim() !== '' && !line.trim().startsWith('#'))
|
|
206
|
+
const firstItemMatch = firstItem?.match(/^(\s*)-\s+/)
|
|
207
|
+
if (!firstItemMatch) throw new Error('invalid insert sequence')
|
|
208
|
+
const itemIndent = firstItemMatch[1].length
|
|
209
|
+
const itemStarts = []
|
|
210
|
+
for (let index = 0; index < body.length; index += 1) {
|
|
211
|
+
const line = body[index]
|
|
212
|
+
if (line.trim() === '' || line.trim().startsWith('#')) continue
|
|
213
|
+
const indent = line.match(/^\s*/)?.[0].length ?? 0
|
|
214
|
+
if (indent < itemIndent || (indent === itemIndent && !/^\s*-\s+/.test(line))) {
|
|
215
|
+
throw new Error('invalid insert sequence')
|
|
216
|
+
}
|
|
217
|
+
if (indent === itemIndent) itemStarts.push(index)
|
|
218
|
+
}
|
|
219
|
+
if (itemStarts.length === 0) throw new Error('empty insert sequence')
|
|
220
|
+
|
|
221
|
+
const parseId = value => {
|
|
222
|
+
const quoted = value.trim().match(/^(['"])([A-Za-z0-9._-]+)\1(?:\s+#.*)?$/)
|
|
223
|
+
if (quoted) return quoted[2]
|
|
224
|
+
return value.trim().match(/^([A-Za-z0-9._-]+)(?:\s+#.*)?$/)?.[1] ?? null
|
|
225
|
+
}
|
|
226
|
+
const itemId = item => {
|
|
227
|
+
const header = item[0].slice(itemIndent + 2).trim()
|
|
228
|
+
const canonical = header.match(/^id:\s*(.+)$/)
|
|
229
|
+
if (canonical) return parseId(canonical[1])
|
|
230
|
+
if (header.startsWith('{')) {
|
|
231
|
+
const inline = header.match(/(?:^\{\s*|,\s*)id\s*:\s*(['"]?)([A-Za-z0-9._-]+)\1(?=\s*[,}])/)
|
|
232
|
+
if (inline) return inline[2]
|
|
233
|
+
}
|
|
234
|
+
const propertyIndent = ' '.repeat(itemIndent + 2)
|
|
235
|
+
const directIds = item.slice(1)
|
|
236
|
+
.map(line => line.startsWith(`${propertyIndent}id:`) ? parseId(line.slice(propertyIndent.length + 3)) : null)
|
|
237
|
+
.filter(Boolean)
|
|
238
|
+
if (directIds.length > 1) throw new Error('duplicate insert id')
|
|
239
|
+
return directIds[0] ?? null
|
|
240
|
+
}
|
|
241
|
+
const items = itemStarts.map((itemStart, index) => body.slice(itemStart, itemStarts[index + 1] ?? body.length))
|
|
242
|
+
const gotryItems = items.filter(item => itemId(item) === 'gotry-tools')
|
|
243
|
+
if (gotryItems.length !== 1) throw new Error('benchmark insert allowlist violation')
|
|
244
|
+
let gotry = gotryItems[0].join('\n')
|
|
245
|
+
gotry = rewriteBenchmarkPluginEntry(gotry, entryPath)
|
|
246
|
+
gotry = injectBenchmarkEnvironmentConfig(gotry, configPath)
|
|
247
|
+
const projected = [...lines.slice(0, start + 1), ...gotry.split('\n'), ...lines.slice(end)]
|
|
248
|
+
return projectBenchmarkSystemPrompt(projected).join('\n')
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// The normal persona contains dynamic variables and product-only tool
|
|
252
|
+
// instructions. They are not meaningful in the minimal benchmark kernel and
|
|
253
|
+
// can make dsh fail while assembling the first request. Require the exact
|
|
254
|
+
// source shape before replacing it; malformed or ambiguous prompt config must
|
|
255
|
+
// fail closed rather than being partially interpreted.
|
|
256
|
+
// Target dsh-system-prompt Config exposes only personaPrefix / personaSuffix;
|
|
257
|
+
// the legacy `persona:` key does not project through. Validate against the
|
|
258
|
+
// current personaPrefix key.
|
|
259
|
+
function projectBenchmarkSystemPrompt(lines) {
|
|
260
|
+
const rootStarts = lines.map((line, index) => /^-\s+/.test(line) ? index : -1).filter(index => index >= 0)
|
|
261
|
+
const rootItems = rootStarts.map((itemStart, index) => lines.slice(itemStart, rootStarts[index + 1] ?? lines.length))
|
|
262
|
+
if (rootItems.length !== 2) throw new Error('benchmark system-prompt root shape violation')
|
|
263
|
+
const insertItems = rootItems.filter(item => /^- insert:\s*(?:#.*)?$/.test(item[0]))
|
|
264
|
+
const systemItems = rootItems.filter(item => /^- id:\s*system-prompt\s*(?:#.*)?$/.test(item[0]))
|
|
265
|
+
if (insertItems.length !== 1 || systemItems.length !== 1) throw new Error('benchmark system-prompt canonical shape violation')
|
|
266
|
+
const start = rootStarts[rootItems.indexOf(systemItems[0])]
|
|
267
|
+
let end = start + 1
|
|
268
|
+
while (end < lines.length && (lines[end].trim() === '' || /^\s/.test(lines[end]) || lines[end].trim().startsWith('#'))) end += 1
|
|
269
|
+
const item = lines.slice(start, end)
|
|
270
|
+
const configIndexes = item.map((line, index) => /^ {2}config:\s*$/.test(line) ? index : -1).filter(index => index >= 0)
|
|
271
|
+
const personaIndexes = item.map((line, index) => /^ {4}personaPrefix:\s*>-\s*$/.test(line) ? index : -1).filter(index => index >= 0)
|
|
272
|
+
// Reject any leftover legacy `persona:` field under config: — it would be
|
|
273
|
+
// silently dropped by the target Config and leave the kernel persona-less.
|
|
274
|
+
// The minimal kernel must project through personaPrefix only.
|
|
275
|
+
const legacyPersonaIndexes = item.map((line, index) => /^ {4}persona:\s*>-\s*$/.test(line) ? index : -1).filter(index => index >= 0)
|
|
276
|
+
if (legacyPersonaIndexes.length !== 0) throw new Error('benchmark system-prompt legacy persona key not allowed under target 0.2.0-rc.2')
|
|
277
|
+
if (configIndexes.length !== 1 || personaIndexes.length !== 1 || personaIndexes[0] <= configIndexes[0]) {
|
|
278
|
+
throw new Error('benchmark system-prompt config violation')
|
|
279
|
+
}
|
|
280
|
+
const persona = personaIndexes[0]
|
|
281
|
+
const blockEnd = item.slice(persona + 1).findIndex(line => line.trim() !== '' && !/^\s{6,}/.test(line))
|
|
282
|
+
const contentEnd = blockEnd < 0 ? item.length : persona + 1 + blockEnd
|
|
283
|
+
if (contentEnd === persona + 1 || item.slice(persona + 1, contentEnd).some(line => line.trim() !== '' && !/^\s{6,}/.test(line))) {
|
|
284
|
+
throw new Error('benchmark system-prompt personaPrefix block violation')
|
|
285
|
+
}
|
|
286
|
+
const before = item.slice(1, configIndexes[0]).filter(line => line.trim() !== '' && !line.trim().startsWith('#'))
|
|
287
|
+
const between = item.slice(configIndexes[0] + 1, persona).filter(line => line.trim() !== '' && !line.trim().startsWith('#'))
|
|
288
|
+
const after = item.slice(contentEnd).filter(line => line.trim() !== '' && !line.trim().startsWith('#'))
|
|
289
|
+
if (before.length !== 0 || between.length !== 0 || after.length !== 0) throw new Error('benchmark system-prompt shape violation')
|
|
290
|
+
const replacement = [
|
|
291
|
+
'- id: system-prompt',
|
|
292
|
+
' config:',
|
|
293
|
+
' personaPrefix: >-',
|
|
294
|
+
' You are GoTry, a task-agnostic travel planning assistant.',
|
|
295
|
+
' Use only the current conversation and tools available in this benchmark session.',
|
|
296
|
+
]
|
|
297
|
+
return [...lines.slice(0, start), ...replacement, ...lines.slice(end)]
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function rewriteBenchmarkPluginEntry(raw, entryPath) {
|
|
301
|
+
const itemIndent = raw.match(/^(\s*)-\s+/)?.[1].length
|
|
302
|
+
if (itemIndent === undefined) throw new Error('benchmark plugin item unavailable')
|
|
303
|
+
const propertyIndent = ' '.repeat(itemIndent + 2)
|
|
304
|
+
const anchorPattern = new RegExp(`^(${propertyIndent}name:\\s*)'[^'\\r\\n]*ts\\/src\\/index\\.ts'(\\s*(?:#.*)?)$`, 'gm')
|
|
305
|
+
const anchors = [...raw.matchAll(anchorPattern)]
|
|
306
|
+
if (anchors.length !== 1 || anchors[0].index === undefined) throw new Error('benchmark plugin anchor unavailable')
|
|
307
|
+
const anchor = anchors[0]
|
|
308
|
+
const escapedEntry = entryPath.replace(/'/g, "''")
|
|
309
|
+
const replacement = `${anchor[1]}'${escapedEntry}'${anchor[2]}`
|
|
310
|
+
const rewritten = `${raw.slice(0, anchor.index)}${replacement}${raw.slice(anchor.index + anchor[0].length)}`
|
|
311
|
+
if (!rewritten.includes(replacement)) throw new Error('benchmark plugin rewrite unavailable')
|
|
312
|
+
return rewritten
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function injectBenchmarkEnvironmentConfig(raw, configPath) {
|
|
316
|
+
const itemIndent = raw.match(/^(\s*)-\s+/)?.[1].length
|
|
317
|
+
if (itemIndent === undefined) throw new Error('benchmark config item unavailable')
|
|
318
|
+
const propertyIndent = ' '.repeat(itemIndent + 2)
|
|
319
|
+
const configIndent = ' '.repeat(itemIndent + 4)
|
|
320
|
+
const configAnchors = raw.match(new RegExp(`^${propertyIndent}config:\\s*(?:#.*)?$`, 'gm')) ?? []
|
|
321
|
+
if (configAnchors.length !== 1) throw new Error('benchmark config block unavailable')
|
|
322
|
+
if (new RegExp(`^${propertyIndent}(?:inject|'inject'|"inject")\\s*:`, 'm').test(raw)) {
|
|
323
|
+
throw new Error('benchmark service injection already declared')
|
|
324
|
+
}
|
|
325
|
+
if ((raw.match(new RegExp(`^${configIndent}benchmarkEnvironmentConfigPath\\s*:`, 'gm')) ?? []).length !== 0) {
|
|
326
|
+
throw new Error('duplicate benchmark config path')
|
|
327
|
+
}
|
|
328
|
+
const anchorPattern = new RegExp(`^(${configIndent})hbcliBin:\\s*'[^'\\r\\n]*'\\s*(?:#.*)?$`, 'gm')
|
|
329
|
+
const anchors = [...raw.matchAll(anchorPattern)]
|
|
330
|
+
if (anchors.length !== 1 || anchors[0].index === undefined) throw new Error('benchmark config anchor unavailable')
|
|
331
|
+
const anchor = anchors[0]
|
|
332
|
+
const escapedPath = configPath.replace(/'/g, "''")
|
|
333
|
+
const insertAt = anchor.index + anchor[0].length
|
|
334
|
+
const injected = `${raw.slice(0, insertAt)}\n${anchor[1]}benchmarkEnvironmentConfigPath: '${escapedPath}'${raw.slice(insertAt)}`
|
|
335
|
+
if ((injected.match(new RegExp(`^${configIndent}benchmarkEnvironmentConfigPath\\s*:`, 'gm')) ?? []).length !== 1) {
|
|
336
|
+
throw new Error('benchmark config injection unavailable')
|
|
337
|
+
}
|
|
338
|
+
// Only benchmark opt-in requires this service. The loader merges this entry
|
|
339
|
+
// dependency with the plugin's tools/systemPrompt dependencies before apply.
|
|
340
|
+
// A one-time ctx.get() without this edge races the parallel profile loader.
|
|
341
|
+
return injected.replace(new RegExp(`^${propertyIndent}config:`, 'm'), `${propertyIndent}inject: [subprocess]\n${propertyIndent}config:`)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
let benchmarkTerminalConfig = null
|
|
345
|
+
let benchmarkPatchProjection = null
|
|
346
|
+
let benchmarkChildDiagnostics = null
|
|
347
|
+
if (benchmarkEnvironmentConfig !== undefined && benchmarkEnvironmentConfig !== '') {
|
|
348
|
+
let validBenchmarkEnvironmentConfig = false
|
|
349
|
+
try {
|
|
350
|
+
const bridgeModule = distModuleMode && existsSync(join(repoRoot, 'dist/src/benchmark-environment-bridge.js'))
|
|
351
|
+
? join(repoRoot, 'dist/src/benchmark-environment-bridge.js')
|
|
352
|
+
: join(repoRoot, 'ts/src/benchmark-environment-bridge.ts')
|
|
353
|
+
const { loadBenchmarkEnvironmentConfig } = await import(pathToFileURL(bridgeModule).href)
|
|
354
|
+
const loadedConfig = benchmarkEnvironmentConfig.length > 0
|
|
355
|
+
&& benchmarkEnvironmentConfig.length <= 4096
|
|
356
|
+
&& !benchmarkEnvironmentConfig.includes('\0')
|
|
357
|
+
&& !benchmarkEnvironmentConfig.includes('\r')
|
|
358
|
+
&& !benchmarkEnvironmentConfig.includes('\n')
|
|
359
|
+
? loadBenchmarkEnvironmentConfig(benchmarkEnvironmentConfig)
|
|
360
|
+
: null
|
|
361
|
+
validBenchmarkEnvironmentConfig = loadedConfig !== null
|
|
362
|
+
benchmarkTerminalConfig = loadedConfig?.terminal_output ?? null
|
|
363
|
+
if (validBenchmarkEnvironmentConfig) {
|
|
364
|
+
benchmarkPatchProjection = projectBenchmarkPatch(readFileSync(staticPatch, 'utf-8'), pluginEntry, benchmarkEnvironmentConfig)
|
|
365
|
+
}
|
|
366
|
+
} catch { validBenchmarkEnvironmentConfig = false }
|
|
367
|
+
if (!validBenchmarkEnvironmentConfig) {
|
|
368
|
+
console.error('[gotry] benchmark environment configuration unavailable')
|
|
369
|
+
process.exit(1)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (benchmarkEnvironmentConfig && mode !== 'headless') {
|
|
373
|
+
console.error('[gotry] benchmark environment requires headless mode')
|
|
374
|
+
process.exit(1)
|
|
375
|
+
}
|
|
376
|
+
if (benchmarkEnvironmentConfig) {
|
|
377
|
+
try {
|
|
378
|
+
const diagnosticModule = distModuleMode && existsSync(join(repoRoot, 'dist/src/benchmark-headless-child-diagnostics.js'))
|
|
379
|
+
? join(repoRoot, 'dist/src/benchmark-headless-child-diagnostics.js')
|
|
380
|
+
: join(repoRoot, 'ts/src/benchmark-headless-child-diagnostics.ts')
|
|
381
|
+
benchmarkChildDiagnostics = await import(pathToFileURL(diagnosticModule).href)
|
|
382
|
+
} catch {
|
|
383
|
+
console.error('[gotry] benchmark environment configuration unavailable')
|
|
384
|
+
process.exit(1)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// dsh-map-tools 宿主插件(地图/路线/POI,零 key 走 OSM/OSRM):tarball 随包 vendor,
|
|
388
|
+
// repo 工作副本走 runtime workspace 链接/依赖解析;都找不到就整块剔除 patch 条目
|
|
389
|
+
// (缺地图不挡旅行规划)。保持 vendored 形态而非 npm 依赖:历史上游 peerDependencies
|
|
390
|
+
// (dsh-settings/dsh-tools >=0.1.2-rc.1)与锁定的 0.1.2-alpha.3 家族在 npm 严格 peer
|
|
391
|
+
// 解析下 ERESOLVE;当前锁定 0.2.0-rc.2 家族;vendored 0.7.3 保留上游开区间
|
|
392
|
+
// peers(>=0.1.2-rc.1),npm 解析与 0.2.0 起的运行时 peer 兼容闸均直接通过,继续以
|
|
393
|
+
// vendored 形态随 tarball 分发并复用同一份七工具接线。
|
|
117
394
|
let mapEntry = ''
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
395
|
+
if (!benchmarkEnvironmentConfig) {
|
|
396
|
+
const vendoredMap = join(repoRoot, 'ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js')
|
|
397
|
+
// (随 tarball 分发;source 布局同一份文件)
|
|
398
|
+
const runtimeMap = join(repoRoot, 'ts/dsh-runtime/node_modules/dsh-map-tools/lib/index.js')
|
|
399
|
+
// (ts/dsh-runtime pnpm workspace vendor/* 链接形态)
|
|
400
|
+
if (existsSync(vendoredMap)) {
|
|
401
|
+
mapEntry = vendoredMap
|
|
402
|
+
} else if (existsSync(runtimeMap)) {
|
|
403
|
+
mapEntry = runtimeMap
|
|
404
|
+
} else {
|
|
405
|
+
// npm 布局:子路径可能被 exports 挡(resolve 抛错不能留下旧值),裸包名返回真实入口
|
|
406
|
+
try { mapEntry = require_.resolve('dsh-map-tools/lib/index.js') } catch { mapEntry = '' }
|
|
407
|
+
if (!mapEntry) { try { mapEntry = require_.resolve('dsh-map-tools') } catch { mapEntry = '' } }
|
|
408
|
+
if (!mapEntry) {
|
|
409
|
+
// ts/node_modules 回退(source 模式:dsh-map-tools 安装在 ts/package.json)
|
|
410
|
+
try { mapEntry = require_.resolve(join(repoRoot, 'ts/node_modules/dsh-map-tools')) } catch { mapEntry = '' }
|
|
411
|
+
}
|
|
412
|
+
}
|
|
125
413
|
}
|
|
126
414
|
// 结构化澄清卡(T2):ask_user_question 工具 + user-questions 服务,从 dsh 包
|
|
127
415
|
// 上下文解析(pnpm 嵌套布局下只有 dsh 自己看得见这些依赖);失败整块剔除
|
|
@@ -131,7 +419,7 @@ if (existsSync(vendoredMap)) {
|
|
|
131
419
|
const stdioAsk = mode === 'headless' && (process.stdin.isTTY || process.env.GOTRY_ASK_STDIO === '1')
|
|
132
420
|
? join(here, 'gotry-stdio-ask.js') : ''
|
|
133
421
|
let askUserInsert = ''
|
|
134
|
-
if (mode === 'web' || stdioAsk) try {
|
|
422
|
+
if (!benchmarkEnvironmentConfig && (mode === 'web' || stdioAsk)) try {
|
|
135
423
|
const reqFromDsh = createRequire(dshBin)
|
|
136
424
|
// 裸包名解析(这些包的 exports 不暴露 ./lib/index.js 子路径,但裸名返回真实入口)
|
|
137
425
|
// userQuestions 服务默认树已注册(重复插入会崩),只插工具消费者
|
|
@@ -140,75 +428,437 @@ if (mode === 'web' || stdioAsk) try {
|
|
|
140
428
|
+ ` - id: dsh-tool-ask-user\n name: '${at}'`
|
|
141
429
|
} catch { /* 缺件不挡启动 */ }
|
|
142
430
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
431
|
+
const unboundPatch = benchmarkPatchProjection ?? readFileSync(staticPatch, 'utf-8')
|
|
432
|
+
let patchRaw = benchmarkPatchProjection ?? unboundPatch.replace(/(name:\s*)'[^']*ts\/src\/index\.ts'/, `$1'${pluginEntry}'`)
|
|
433
|
+
patchRaw = patchRaw.replace(/^\s*# \{ask-user-insert\}.*\n(\s*# .*\n)?/m, askUserInsert ? askUserInsert + '\n' : '')
|
|
434
|
+
|
|
435
|
+
// Owner-local benchmark environment opt-in. Keep the path out of tool input
|
|
436
|
+
// and logs; it is only injected into the local plugin config as YAML data.
|
|
146
437
|
if (mapEntry) {
|
|
147
438
|
patchRaw = patchRaw.replace(/(name:\s*)'placeholder\/dsh-map-tools'/, `$1'${mapEntry}'`)
|
|
148
439
|
} else {
|
|
149
440
|
patchRaw = patchRaw.replace(/\n\s*- id: dsh-map-tools\n\s*name: 'placeholder\/dsh-map-tools'\n/, '\n')
|
|
150
441
|
}
|
|
151
442
|
|
|
152
|
-
// dsh-calendar 宿主插件(CalDAV
|
|
443
|
+
// dsh-calendar 宿主插件(CalDAV 工作窗口读取)——D-9 拍板(issue #106):默认不挂载。
|
|
444
|
+
// 未配置的日历工具是纯负资产(会话中段才撞「未配置 username」报错),gotry 对它的
|
|
445
|
+
// 唯一诉求(工作窗口)由访谈首轮覆盖。挂载与否由 **setup 状态面**决定(founder
|
|
446
|
+
// 2026-09-03 纠偏:禁止环境变量控制产品行为;可选依赖进 setup 状态管理,与扩展
|
|
447
|
+
// manifest 同居 ~/.gotry)——`npx @danceiny/gotry setup calendar` 写 ~/.gotry/calendar.json,
|
|
448
|
+
// `--off` 删除恢复默认;doctor 报告三态。
|
|
449
|
+
let calEnabled = false
|
|
450
|
+
try {
|
|
451
|
+
calEnabled = JSON.parse(readFileSync(join(homedir(), '.gotry', 'calendar.json'), 'utf-8'))?.enabled === true
|
|
452
|
+
} catch { /* 缺文件/坏文件 = 默认不挂载 */ }
|
|
153
453
|
let calEntry = ''
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
454
|
+
if (!benchmarkEnvironmentConfig) {
|
|
455
|
+
const vendoredCal = join(repoRoot, 'ts/dsh-runtime/node_modules/dsh-calendar/lib/index.js')
|
|
456
|
+
if (existsSync(vendoredCal)) {
|
|
457
|
+
calEntry = vendoredCal
|
|
458
|
+
} else {
|
|
459
|
+
try { calEntry = require_.resolve('dsh-calendar/lib/index.js') } catch { calEntry = '' }
|
|
460
|
+
if (!calEntry) { try { calEntry = require_.resolve('dsh-calendar') } catch { calEntry = '' } }
|
|
461
|
+
}
|
|
160
462
|
}
|
|
161
|
-
if (calEntry) {
|
|
463
|
+
if (calEntry && calEnabled) {
|
|
162
464
|
patchRaw = patchRaw.replace(/(name:\s*)'placeholder\/dsh-calendar'/, `$1'${calEntry}'`)
|
|
163
465
|
} else {
|
|
164
466
|
patchRaw = patchRaw.replace(/\n\s*- id: dsh-calendar\n\s*name: 'placeholder\/dsh-calendar'\n/, '\n')
|
|
165
467
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
468
|
+
|
|
469
|
+
// LLM_MODEL 指定模型(issue #77)②:composition 层 by-id 覆盖。cordis patch 语义
|
|
470
|
+
// (dsh-app-boot applyEntryPatches):非 insert 的 {id, ...overrides} 对目标条目做浅层
|
|
471
|
+
// 键赋值,config 整体替换;id 不存在只 warn+skip 不崩(上游改名时优雅退化)。
|
|
472
|
+
// 两条目标在 headless/web profile 均有(dsh-base 声明 llm-deepseek、两 profile 均声明
|
|
473
|
+
// agent-default-model)。llm-deepseek 目录整体替换为单条目:显式指定模型的场景多为
|
|
474
|
+
// 中转/兼容端点,默认 v4-* 目录对其是误导;不硬编码上游 DEFAULT_MODELS 防漂移。
|
|
475
|
+
let llmMaxTokens
|
|
476
|
+
if (process.env.GOTRY_LLM_MODEL) {
|
|
477
|
+
const rawMax = process.env.LLM_MAX_TOKENS
|
|
478
|
+
if (rawMax !== undefined && rawMax !== '') {
|
|
479
|
+
llmMaxTokens = Number.parseInt(rawMax, 10)
|
|
480
|
+
if (!Number.isSafeInteger(llmMaxTokens) || llmMaxTokens <= 0) {
|
|
481
|
+
throw new Error(`invalid LLM_MAX_TOKENS: ${JSON.stringify(rawMax)} (expected a positive safe integer)`)
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
const modelYaml = `'${process.env.GOTRY_LLM_MODEL.replace(/'/g, "''")}'`
|
|
485
|
+
// LLM_MAX_TOKENS(Round 9,#100/#102):中转/兼容端点模型常带低于 dsh 未知模型
|
|
486
|
+
// 默认 256K 预算的输出上限(MiniMax 2013 实测);目录条目 maxTokens 元数据是
|
|
487
|
+
// dsh-llm-deepseek defaultMaxTokens 的官方通道(web UI 模型同源)。
|
|
488
|
+
const maxTokensYaml = llmMaxTokens === undefined ? '' : `\n maxTokens: ${llmMaxTokens}`
|
|
489
|
+
patchRaw += `\n# LLM_MODEL 指定模型(issue #77):dsh 会话面默认模型 + 模型目录\n- id: agent-default-model\n config:\n provider: deepseek-official\n model: ${modelYaml}\n- id: llm-deepseek\n config:\n models:\n - id: ${modelYaml}\n name: ${modelYaml}${maxTokensYaml}\n`
|
|
490
|
+
}
|
|
491
|
+
// Keep the patch private and short-lived. mkdtempSync creates a 0700 directory;
|
|
492
|
+
// wx prevents accidental reuse/races if a process is started concurrently.
|
|
493
|
+
const patchDir = mkdtempSync(join(tmpdir(), 'gotry-cordis-'))
|
|
494
|
+
const patchPath = join(patchDir, 'patch.yml')
|
|
495
|
+
writeFileSync(patchPath, patchRaw, { mode: 0o600, flag: 'wx' })
|
|
496
|
+
let patchCleaned = false
|
|
497
|
+
const cleanupPatch = () => {
|
|
498
|
+
if (patchCleaned) return
|
|
499
|
+
patchCleaned = true
|
|
500
|
+
rmSync(patchDir, { recursive: true, force: true })
|
|
501
|
+
}
|
|
502
|
+
process.once('exit', cleanupPatch)
|
|
503
|
+
// 信号转发(issue #267 P1):spawnSync 阻塞 JS,信号处理器无法服务、finally 也跑不到。
|
|
504
|
+
// 故 web-onboarding 子调用改 awaited spawn。SIGINT/SIGTERM 转发在 onboarding **之前**注册一次,
|
|
505
|
+
// 并贯穿后续 dsh web 子进程的整个生命周期——服务管理器只向父进程发 SIGTERM 时,无论当前活跃的是
|
|
506
|
+
// onboarding 子进程组还是 dsh 子,都收到同一信号。收到信号时:终止当前活跃子进程/进程组,
|
|
507
|
+
// 等待 bounded close/reap,清 patch + 私有 result 目录(均幂等),移除本监听器,再向自己重发信号恢复原生终止语义。
|
|
508
|
+
// signalHandling 护栏防递归/重复处理(重发信号不再二次进入本回调)。setup/doctor 的 spawnSync 不在此窗口,保持不变。
|
|
509
|
+
let child = null // dsh web 子进程(launchWeb 后)
|
|
510
|
+
let childGroupPid = null // POSIX dsh child process-group leader
|
|
511
|
+
let onboardingChild = null // onboarding 子进程(运行中时);POSIX 下独立进程组
|
|
512
|
+
let onboardingChildGroupPid = null
|
|
513
|
+
let onboardingResultDir = null // 私有结果目录;信号/正常/错误路径都清
|
|
514
|
+
// outer grace 必须大于 bootstrap installer cleanup budget(2s TERM + 1s SIGKILL wait)。
|
|
515
|
+
// 否则 stubborn installer 忽略 TERM 时,inner 可能先杀 bootstrap,导致 bootstrap 来不及 SIGKILL
|
|
516
|
+
// 它自己 detached 出去的 installer 进程组。
|
|
517
|
+
const ONBOARDING_TERM_GRACE_MS = 5_000
|
|
518
|
+
const ONBOARDING_KILL_WAIT_MS = 1_000
|
|
519
|
+
const cleanupOnboardingResult = () => {
|
|
520
|
+
if (!onboardingResultDir) return
|
|
521
|
+
const d = onboardingResultDir
|
|
522
|
+
onboardingResultDir = null
|
|
523
|
+
try { rmSync(d, { recursive: true, force: true }) } catch { /* ignore */ }
|
|
524
|
+
}
|
|
525
|
+
const onboardingChildTimeoutMs = (() => {
|
|
526
|
+
const raw = process.env.GOTRY_ONBOARDING_CHILD_TIMEOUT_MS
|
|
527
|
+
if (raw === undefined || raw === '') return 300_000
|
|
528
|
+
if (!/^[1-9]\d*$/.test(raw)) return 300_000
|
|
529
|
+
const n = Number(raw)
|
|
530
|
+
return Number.isSafeInteger(n) && n > 0 ? n : 300_000
|
|
531
|
+
})()
|
|
532
|
+
let signalHandling = false
|
|
533
|
+
const signalListeners = new Map()
|
|
534
|
+
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
|
535
|
+
const signalChild = (proc, sig, groupPid = null) => {
|
|
536
|
+
if (!proc) return
|
|
537
|
+
if (process.platform !== 'win32' && groupPid) {
|
|
538
|
+
try { process.kill(-groupPid, sig); return } catch { /* fallback */ }
|
|
539
|
+
}
|
|
540
|
+
try { proc.kill(sig) } catch { /* ignore */ }
|
|
541
|
+
}
|
|
542
|
+
const terminateChildBounded = async (proc, sig, groupPid = null, graceMs = ONBOARDING_TERM_GRACE_MS, killMs = ONBOARDING_KILL_WAIT_MS) => {
|
|
543
|
+
await terminateOwnedChild({ child: proc, groupPid, signal: sig, termGraceMs: graceMs, killWaitMs: killMs })
|
|
544
|
+
}
|
|
545
|
+
const registerSignalForwarding = () => {
|
|
546
|
+
if (signalListeners.size) return
|
|
547
|
+
for (const sig of ['SIGINT', 'SIGTERM']) {
|
|
548
|
+
const listener = () => {
|
|
549
|
+
if (signalHandling) return // 防递归:重发信号不再二次进入
|
|
550
|
+
signalHandling = true
|
|
551
|
+
void (async () => {
|
|
552
|
+
const active = onboardingChild || child
|
|
553
|
+
const groupPid = onboardingChild ? onboardingChildGroupPid : childGroupPid
|
|
554
|
+
cleanupPatch()
|
|
555
|
+
cleanupOnboardingResult()
|
|
556
|
+
await terminateChildBounded(active, sig, groupPid)
|
|
557
|
+
for (const [s, l] of signalListeners) process.off(s, l)
|
|
558
|
+
signalListeners.clear()
|
|
559
|
+
process.kill(process.pid, sig) // 移除监听器后重发 → 原生终止语义
|
|
560
|
+
await sleep(2_000)
|
|
561
|
+
process.exit(128 + (sig === 'SIGINT' ? 2 : 15))
|
|
562
|
+
})()
|
|
563
|
+
}
|
|
564
|
+
process.on(sig, listener)
|
|
565
|
+
signalListeners.set(sig, listener)
|
|
566
|
+
}
|
|
173
567
|
}
|
|
174
568
|
|
|
175
|
-
|
|
176
|
-
|
|
569
|
+
const unregisterSignalForwarding = () => {
|
|
570
|
+
for (const [s, l] of signalListeners) process.off(s, l)
|
|
571
|
+
signalListeners.clear()
|
|
572
|
+
}
|
|
573
|
+
// LLM key 由 dsh 宿主管理(凭证是用户资产,UI 在 dsh 里;gotry 不拦截启动期,
|
|
574
|
+
// 不在用户面前展示任何 key 配置引导),此处直接放手 spawn dsh。
|
|
575
|
+
const webPortArgs = (() => {
|
|
576
|
+
if (mode !== 'web') return []
|
|
577
|
+
const portIndex = rest.indexOf('--port')
|
|
578
|
+
const port = portIndex >= 0 ? rest[portIndex + 1] : undefined
|
|
579
|
+
return port !== undefined && /^(?:0|[1-9]\d*)$/.test(port) ? ['--port', port] : []
|
|
580
|
+
})()
|
|
177
581
|
const binJs = mode === 'web'
|
|
178
|
-
? ['web', '--patch', patchPath, ...(process.argv.includes('--no-open') ? ['--no-open'] : [])]
|
|
582
|
+
? ['web', '--patch', patchPath, ...(process.argv.includes('--no-open') ? ['--no-open'] : []), ...webPortArgs]
|
|
179
583
|
: ['--profile', 'headless', '--patch', patchPath, ...rest]
|
|
180
584
|
|
|
181
|
-
const
|
|
182
|
-
|
|
585
|
+
const childEnv = { ...process.env }
|
|
586
|
+
// The benchmark config is parent-only metadata. Never make its path visible to
|
|
587
|
+
// dsh or any plugin loaded by the child.
|
|
588
|
+
delete childEnv.GOTRY_BENCHMARK_ENV_CONFIG
|
|
589
|
+
if (benchmarkEnvironmentConfig) {
|
|
590
|
+
for (const key of [
|
|
591
|
+
'GOTRY_BENCHMARK_BRIDGE_PARENT_SECRET',
|
|
592
|
+
'DATABASE_URL',
|
|
593
|
+
'SSH_AUTH_SOCK',
|
|
594
|
+
'AWS_PROFILE',
|
|
595
|
+
'HTTPS_PROXY',
|
|
596
|
+
]) delete childEnv[key]
|
|
597
|
+
}
|
|
598
|
+
const benchmarkStdout = benchmarkEnvironmentConfig ? [] : null
|
|
599
|
+
if (benchmarkEnvironmentConfig) childEnv.GOTRY_BENCHMARK_DIAGNOSTIC_FD = '3'
|
|
600
|
+
const childStdio = mode === 'web'
|
|
601
|
+
? 'inherit'
|
|
602
|
+
: benchmarkStdout
|
|
603
|
+
? ['pipe', 'pipe', 'pipe', 'pipe']
|
|
604
|
+
: process.stdin.isTTY
|
|
605
|
+
? 'inherit'
|
|
606
|
+
: ['pipe', 'inherit', 'inherit']
|
|
607
|
+
let benchmarkCapturedBytes = 0
|
|
608
|
+
let benchmarkDiagnosticBuffer = Buffer.alloc(0)
|
|
609
|
+
let benchmarkOutputTruncated = false
|
|
610
|
+
// issue #258:`gotry web` 启动前的交互式 onboarding——每次符合条件的启动评估一次、至多问一次,
|
|
611
|
+
// 无跨启动持久确认(每次 web 启动独立判定;不写「已问过」标记)。仅交互式 TTY + 有可自动安装
|
|
612
|
+
// 缺项时问一次;y 复用 doctor --fix 幂等安装器,不建第二套;n 立即继续 web)。
|
|
613
|
+
// CI/benchmark/非 TTY/全健康/GOTRY_SETUP_SKIP=1/GOTRY_ONBOARDING_SKIP=1/--no-onboarding
|
|
614
|
+
// 均零 prompt 零安装,仍启 web;永不 postinstall 或后台任务里安装。无 auto 但有需用户操作/
|
|
615
|
+
// 不可用缺项时(如 win32 平台不支持自动安装),onboarding 渲染分类计划与原因、不 prompt 不安装,
|
|
616
|
+
// 标记 reported;orchestrateWebLaunch 据此抑制重复的 detached doctor 摘要(design §3.1③,#114)。
|
|
617
|
+
// 编排走 orchestrateWebLaunch(显式注入依赖的 E2E 边界缝,bootstrap-tests 跨边界单测同款):
|
|
618
|
+
// bootstrap 以 `onboarding --result-file=…` 异步 inherit 跑(awaited spawn,POSIX 下独立进程组——
|
|
619
|
+
// 使 JS 能服务 SIGINT/SIGTERM 并在信号路径清 patch+result 目录、reap installer 子树);result JSON 告知是否 prompt /
|
|
620
|
+
// reported,用以抑制重复的启动摘要行。yes/no/失败/跳过/已 reported 均继续到 launchWeb(spawn dsh web)。
|
|
621
|
+
const onboardingSkip = onboardingSkipReason({
|
|
622
|
+
mode,
|
|
623
|
+
benchmark: Boolean(benchmarkEnvironmentConfig),
|
|
183
624
|
env: process.env,
|
|
184
|
-
|
|
625
|
+
argv: process.argv,
|
|
626
|
+
isTTY: process.stdin.isTTY,
|
|
185
627
|
})
|
|
628
|
+
// 一次性注册信号转发,覆盖 onboarding + dsh child 全生命周期(dsh close/error 时注销)。
|
|
629
|
+
registerSignalForwarding()
|
|
630
|
+
await orchestrateWebLaunch({
|
|
631
|
+
onboardingSkip,
|
|
632
|
+
runOnboarding: async () => {
|
|
633
|
+
// result 通道硬化:不在 os.tmpdir 下放可预测文件名——mkdtempSync 建 0700 私有目录,
|
|
634
|
+
// result.json 放其内;bootstrap 以 mode 0600 + flag wx 写(存在路径/符号链接永不覆盖,
|
|
635
|
+
// 防 symlink 互换攻击)。子调用改 awaited spawn(非 spawnSync)使 JS 能服务信号;
|
|
636
|
+
// 信号/正常/错误路径都清目录(幂等),详见上方 registerSignalForwarding/cleanupOnboardingResult。
|
|
637
|
+
let resultDir = null
|
|
638
|
+
try {
|
|
639
|
+
resultDir = mkdtempSync(join(tmpdir(), 'gotry-onboarding-'))
|
|
640
|
+
onboardingResultDir = resultDir
|
|
641
|
+
const resultFile = join(resultDir, 'result.json')
|
|
642
|
+
// 信号转发在 onboarding 之前注册一次,贯穿后续 dsh web 整个生命周期(见上方 registerSignalForwarding);
|
|
643
|
+
// onboarding 正常结束后不注销——服务管理器只向父进程发信号时,当前活跃子进程(onboarding 或 dsh)均收到。
|
|
644
|
+
registerSignalForwarding()
|
|
645
|
+
const detachedOnboarding = process.platform !== 'win32'
|
|
646
|
+
const oc = spawn(process.execPath, [join(here, 'gotry-bootstrap.js'), 'onboarding', `--result-file=${resultFile}`], {
|
|
647
|
+
stdio: 'inherit',
|
|
648
|
+
detached: detachedOnboarding,
|
|
649
|
+
})
|
|
650
|
+
onboardingChild = oc
|
|
651
|
+
onboardingChildGroupPid = detachedOnboarding ? oc.pid : null
|
|
652
|
+
// onboarding 永不挡 web:保留 spawnSync 原有的 300s 上限(可经 GOTRY_ONBOARDING_CHILD_TIMEOUT_MS 注入以供
|
|
653
|
+
// 确定性测试,生产默认 300_000)。超时即 SIGTERM(2s 后 SIGKILL 兜底),settle 恰好一次于 close/error/timeout;
|
|
654
|
+
// 之后继续 web(prompted:false),不留进程/目录(finally 清 result,信号路径清 patch+result)。
|
|
655
|
+
await new Promise((resolve) => {
|
|
656
|
+
let done = false
|
|
657
|
+
let killTimer = null
|
|
658
|
+
const timer = setTimeout(() => {
|
|
659
|
+
process.stderr.write(`[gotry] onboarding child timeout after ${onboardingChildTimeoutMs}ms; continuing to web\n`)
|
|
660
|
+
signalChild(oc, 'SIGTERM', onboardingChildGroupPid)
|
|
661
|
+
killTimer = setTimeout(() => { if (!done) signalChild(oc, 'SIGKILL', onboardingChildGroupPid) }, ONBOARDING_TERM_GRACE_MS)
|
|
662
|
+
}, onboardingChildTimeoutMs)
|
|
663
|
+
const finish = () => { if (done) return; done = true; clearTimeout(timer); if (killTimer) clearTimeout(killTimer); resolve() }
|
|
664
|
+
oc.once('close', finish)
|
|
665
|
+
oc.once('error', (error) => {
|
|
666
|
+
process.stderr.write(`[gotry] onboarding child spawn failed: ${error.code || error.name || 'error'}; continuing to web\n`)
|
|
667
|
+
finish()
|
|
668
|
+
})
|
|
669
|
+
})
|
|
670
|
+
if (signalHandling) await new Promise(() => {})
|
|
671
|
+
onboardingChild = null
|
|
672
|
+
onboardingChildGroupPid = null
|
|
673
|
+
if (existsSync(resultFile)) {
|
|
674
|
+
try { return JSON.parse(readFileSync(resultFile, 'utf-8')) }
|
|
675
|
+
catch { return { prompted: false } }
|
|
676
|
+
}
|
|
677
|
+
return { prompted: false }
|
|
678
|
+
} catch {
|
|
679
|
+
process.stderr.write('[gotry] onboarding setup failed; continuing to web\n')
|
|
680
|
+
return { prompted: false }
|
|
681
|
+
} finally {
|
|
682
|
+
onboardingChild = null
|
|
683
|
+
onboardingChildGroupPid = null
|
|
684
|
+
if (resultDir) { try { rmSync(resultDir, { recursive: true, force: true }) } catch { /* ignore */ } }
|
|
685
|
+
onboardingResultDir = null
|
|
686
|
+
}
|
|
687
|
+
},
|
|
688
|
+
// 启动一次性 doctor 摘要(issue #114,design §3.1③):分离子进程后台跑只读体检,
|
|
689
|
+
// 有待处理项打一行 stderr(stderr 继承,不污染 stdout;benchmark 面保持零杂音)。
|
|
690
|
+
// onboarding 已 prompt 时 orchestrateWebLaunch 已抑制本回调(不重复)。
|
|
691
|
+
runSummary: () => {
|
|
692
|
+
if (benchmarkEnvironmentConfig) return
|
|
693
|
+
try {
|
|
694
|
+
spawn(process.execPath, [join(here, 'gotry-bootstrap.js'), 'doctor', '--summary'], {
|
|
695
|
+
detached: true,
|
|
696
|
+
stdio: ['ignore', 'ignore', 'inherit'],
|
|
697
|
+
}).unref()
|
|
698
|
+
} catch { /* 体检不可用不挡启动 */ }
|
|
699
|
+
},
|
|
700
|
+
// dsh web 启动边界:launchWeb 即 inner 跨过 onboarding 后到达的 web 启动标记。
|
|
701
|
+
launchWeb: async () => {
|
|
702
|
+
const owned = spawnOwnedChild(process.execPath, [dshBin, ...binJs], {
|
|
703
|
+
stdio: childStdio,
|
|
704
|
+
env: childEnv,
|
|
705
|
+
cwd: dshCwd,
|
|
706
|
+
})
|
|
707
|
+
child = owned.child
|
|
708
|
+
childGroupPid = owned.groupPid
|
|
709
|
+
},
|
|
710
|
+
})
|
|
711
|
+
if (benchmarkStdout) {
|
|
712
|
+
const maxBytes = Math.max(1, Number(benchmarkTerminalConfig?.max_bytes ?? 1))
|
|
713
|
+
child.stdout?.on('data', chunk => {
|
|
714
|
+
if (benchmarkCapturedBytes + Buffer.byteLength(chunk) > maxBytes) benchmarkOutputTruncated = true
|
|
715
|
+
if (benchmarkCapturedBytes < maxBytes) {
|
|
716
|
+
const bytes = Buffer.from(chunk)
|
|
717
|
+
benchmarkStdout.push(bytes.subarray(0, Math.max(0, maxBytes - benchmarkCapturedBytes)))
|
|
718
|
+
benchmarkCapturedBytes += Math.min(bytes.length, maxBytes - benchmarkCapturedBytes)
|
|
719
|
+
}
|
|
720
|
+
})
|
|
721
|
+
child.stderr?.resume()
|
|
722
|
+
child.stdio?.[3]?.on('data', chunk => {
|
|
723
|
+
benchmarkDiagnosticBuffer = benchmarkChildDiagnostics.appendBoundedChildDiagnostic(
|
|
724
|
+
benchmarkDiagnosticBuffer,
|
|
725
|
+
Buffer.from(chunk),
|
|
726
|
+
)
|
|
727
|
+
})
|
|
728
|
+
}
|
|
186
729
|
if (process.env.GOTRY_DEBUG) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
730
|
+
if (benchmarkEnvironmentConfig) {
|
|
731
|
+
console.error('[gotry-debug] benchmark headless child configured(details redacted)')
|
|
732
|
+
} else {
|
|
733
|
+
console.error('[gotry-debug] exec:', process.execPath, dshBin)
|
|
734
|
+
console.error('[gotry-debug] argv:', binJs)
|
|
735
|
+
console.error('[gotry-debug] cwd:', dshCwd)
|
|
736
|
+
console.error('[gotry-debug] patch exists:', existsSync(patchPath), patchPath)
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
let benchmarkFailureReported = false
|
|
740
|
+
const reportBenchmarkFailure = reason => {
|
|
741
|
+
if (benchmarkFailureReported) return
|
|
742
|
+
benchmarkFailureReported = true
|
|
743
|
+
process.exitCode = 1
|
|
744
|
+
process.stderr.write(`[gotry] benchmark terminal output unavailable (${reason})\n`)
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
let childLifecycleHandled = false
|
|
748
|
+
let childCleanupPromise = null
|
|
749
|
+
// `close` may be delayed by a descendant which inherited dsh's stdio pipe.
|
|
750
|
+
// Always start bounded TERM→KILL cleanup from `exit` so the owned group is
|
|
751
|
+
// reaped before any close/parser wait. The benchmark zero-success close path
|
|
752
|
+
// still parses the already-captured stdout buffer unchanged; cleanup runs
|
|
753
|
+
// fire-and-forget in the background and stays bounded by ONBOARDING bounds.
|
|
754
|
+
const startChildCleanup = () => {
|
|
755
|
+
if (childCleanupPromise || !child) return childCleanupPromise
|
|
756
|
+
childCleanupPromise = terminateOwnedChild({
|
|
757
|
+
child,
|
|
758
|
+
groupPid: childGroupPid,
|
|
759
|
+
signal: 'SIGTERM',
|
|
760
|
+
termGraceMs: ONBOARDING_TERM_GRACE_MS,
|
|
761
|
+
killWaitMs: ONBOARDING_KILL_WAIT_MS,
|
|
762
|
+
})
|
|
763
|
+
return childCleanupPromise
|
|
191
764
|
}
|
|
192
765
|
child.on('exit', (code, signal) => {
|
|
766
|
+
void startChildCleanup()
|
|
767
|
+
})
|
|
768
|
+
child.on('close', async (code, signal) => {
|
|
769
|
+
if (childLifecycleHandled) return
|
|
770
|
+
childLifecycleHandled = true
|
|
771
|
+
cleanupPatch()
|
|
772
|
+
if (signalHandling) return
|
|
773
|
+
unregisterSignalForwarding()
|
|
774
|
+
// Non-benchmark web/headless children are owned through both normal and
|
|
775
|
+
// anomalous exits. The promise was normally started by `exit`; retaining
|
|
776
|
+
// this close fallback covers runtimes that report close without exit.
|
|
777
|
+
if (!benchmarkStdout || code !== 0 || signal) await startChildCleanup()
|
|
778
|
+
if (benchmarkStdout) {
|
|
779
|
+
const captured = Buffer.concat(benchmarkStdout).toString('utf8')
|
|
780
|
+
if (code !== 0 || signal || benchmarkOutputTruncated) {
|
|
781
|
+
const reason = benchmarkChildDiagnostics.classifyBenchmarkChildFailure({
|
|
782
|
+
code,
|
|
783
|
+
signal,
|
|
784
|
+
diagnostic: benchmarkDiagnosticBuffer.toString('utf8'),
|
|
785
|
+
outputTruncated: benchmarkOutputTruncated,
|
|
786
|
+
})
|
|
787
|
+
reportBenchmarkFailure(reason)
|
|
788
|
+
return
|
|
789
|
+
}
|
|
790
|
+
const parserModule = distModuleMode && existsSync(join(repoRoot, 'dist/src/benchmark-agent-conformance.js'))
|
|
791
|
+
? join(repoRoot, 'dist/src/benchmark-agent-conformance.js')
|
|
792
|
+
: join(repoRoot, 'ts/src/benchmark-agent-conformance.ts')
|
|
793
|
+
import(pathToFileURL(parserModule).href).then(async ({ parseBenchmarkTerminal }) => {
|
|
794
|
+
const parsed = parseBenchmarkTerminal(captured, benchmarkTerminalConfig)
|
|
795
|
+
if (!parsed || parsed.ok !== true) {
|
|
796
|
+
reportBenchmarkFailure('child_terminal_invalid')
|
|
797
|
+
return
|
|
798
|
+
}
|
|
799
|
+
await new Promise((resolve, reject) => {
|
|
800
|
+
const onError = error => {
|
|
801
|
+
process.stdout.off('error', onError)
|
|
802
|
+
reject(error)
|
|
803
|
+
}
|
|
804
|
+
process.stdout.once('error', onError)
|
|
805
|
+
process.stdout.write(captured, () => {
|
|
806
|
+
process.stdout.off('error', onError)
|
|
807
|
+
resolve()
|
|
808
|
+
})
|
|
809
|
+
})
|
|
810
|
+
process.exitCode = 0
|
|
811
|
+
}).catch(() => {
|
|
812
|
+
reportBenchmarkFailure('child_lifecycle_failure')
|
|
813
|
+
})
|
|
814
|
+
return
|
|
815
|
+
}
|
|
193
816
|
if (code !== 0 || signal) {
|
|
194
817
|
// D-NEW 护栏:dsh 异常退出也写一条 incident,留现场而非沉默
|
|
195
|
-
//
|
|
196
|
-
const incidentModule =
|
|
197
|
-
? '
|
|
198
|
-
: '
|
|
199
|
-
|
|
818
|
+
// dist 模式 import JS(node_modules 下的 .ts 会被 Node 拒 strip)
|
|
819
|
+
const incidentModule = distModuleMode && existsSync(join(repoRoot, 'dist/capabilities/incident-log.js'))
|
|
820
|
+
? join(repoRoot, 'dist/capabilities/incident-log.js')
|
|
821
|
+
: join(repoRoot, 'ts/capabilities/incident-log.ts')
|
|
822
|
+
try {
|
|
823
|
+
const { recordIncident } = await import(pathToFileURL(incidentModule).href)
|
|
200
824
|
recordIncident({
|
|
201
825
|
ts: new Date().toISOString(),
|
|
202
826
|
kind: 'plugin_error',
|
|
203
827
|
message: `gotry spawn exit: ${mode} code=${code} signal=${signal}`,
|
|
204
828
|
source: 'gotry-cli',
|
|
205
|
-
}, repoRoot)
|
|
206
|
-
}
|
|
829
|
+
}, repoRoot)
|
|
830
|
+
} catch { /* diagnostics remain best-effort; fatal exit is preserved */ }
|
|
831
|
+
}
|
|
832
|
+
if (signal) {
|
|
833
|
+
try { process.kill(process.pid, signal) } catch { process.exit(1) }
|
|
834
|
+
return
|
|
207
835
|
}
|
|
208
836
|
process.exit(code ?? 1)
|
|
209
837
|
})
|
|
210
|
-
child.on('error', (e) => {
|
|
838
|
+
child.on('error', async (e) => {
|
|
839
|
+
if (childLifecycleHandled) return
|
|
840
|
+
childLifecycleHandled = true
|
|
841
|
+
cleanupPatch()
|
|
842
|
+
if (signalHandling) return
|
|
843
|
+
unregisterSignalForwarding()
|
|
844
|
+
await startChildCleanup()
|
|
845
|
+
try {
|
|
846
|
+
const incidentModule = distModuleMode && existsSync(join(repoRoot, 'dist/capabilities/incident-log.js'))
|
|
847
|
+
? join(repoRoot, 'dist/capabilities/incident-log.js')
|
|
848
|
+
: join(repoRoot, 'ts/capabilities/incident-log.ts')
|
|
849
|
+
const { recordIncident } = await import(pathToFileURL(incidentModule).href)
|
|
850
|
+
recordIncident({
|
|
851
|
+
ts: new Date().toISOString(),
|
|
852
|
+
kind: 'plugin_error',
|
|
853
|
+
message: `gotry spawn error: ${e instanceof Error ? e.message : String(e)}`,
|
|
854
|
+
source: 'gotry-cli',
|
|
855
|
+
}, repoRoot)
|
|
856
|
+
} catch { /* diagnostics remain best-effort; fatal exit is preserved */ }
|
|
857
|
+
if (benchmarkEnvironmentConfig) {
|
|
858
|
+
reportBenchmarkFailure('child_spawn_failure')
|
|
859
|
+
return
|
|
860
|
+
}
|
|
211
861
|
console.error(`[gotry] dsh 启动失败: ${e.message}`)
|
|
212
|
-
console.error('尝试: node -v 看 Node 版本(需 22+),或查看 ts/dsh-runtime/node_modules/。')
|
|
862
|
+
console.error('尝试: node -v 看 Node 版本(需 22.15+),或查看 ts/dsh-runtime/node_modules/。')
|
|
213
863
|
process.exit(1)
|
|
214
864
|
})
|