@danceiny/gotry 0.0.1-rc.21 → 0.0.1-rc.24

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.
Files changed (327) hide show
  1. package/README.md +102 -194
  2. package/README.zh-CN.md +115 -210
  3. package/bin/gotry-backend.js +53 -0
  4. package/bin/gotry-bootstrap.js +466 -35
  5. package/bin/gotry-inner.js +255 -36
  6. package/bin/gotry-process-liveness.d.ts +27 -0
  7. package/bin/gotry-process-liveness.js +97 -0
  8. package/bin/gotry-runtime-resolution.d.ts +1 -1
  9. package/bin/gotry-runtime-resolution.js +2 -2
  10. package/client/client.js +174 -0
  11. package/cordis.gotry-patch.yml +70 -93
  12. package/dist/capabilities/agent-reach-deep.js +45 -122
  13. package/dist/capabilities/agent-reach.js +63 -148
  14. package/dist/capabilities/anything.js +89 -75
  15. package/dist/capabilities/artifacts.js +123 -104
  16. package/dist/capabilities/channel-health.js +65 -45
  17. package/dist/capabilities/channel-registry.js +108 -87
  18. package/dist/capabilities/doctor.js +357 -235
  19. package/dist/capabilities/effect.js +223 -309
  20. package/dist/capabilities/fact-log.js +28 -14
  21. package/dist/capabilities/flyai.js +245 -156
  22. package/dist/capabilities/ground-transfer.js +509 -0
  23. package/dist/capabilities/hbcli.js +144 -148
  24. package/dist/capabilities/hotelbyte-transaction.js +194 -0
  25. package/dist/capabilities/incident-log.js +131 -61
  26. package/dist/capabilities/model-override.js +30 -10
  27. package/dist/capabilities/opensky.js +54 -43
  28. package/dist/capabilities/resilience.js +50 -32
  29. package/dist/capabilities/session/action-cache.js +50 -44
  30. package/dist/capabilities/session/adapters/ctrip-flight.js +119 -62
  31. package/dist/capabilities/session/adapters/ctrip-hotel.js +125 -110
  32. package/dist/capabilities/session/adapters/dida-portal.js +206 -0
  33. package/dist/capabilities/session/adapters/meituan-local.js +35 -54
  34. package/dist/capabilities/session/adapters/rail-12306.js +169 -112
  35. package/dist/capabilities/session/benchmark.js +85 -149
  36. package/dist/capabilities/session/extension-bridge.js +320 -259
  37. package/dist/capabilities/session/extension-channel.js +57 -76
  38. package/dist/capabilities/session/extension-distribution.js +115 -119
  39. package/dist/capabilities/session/extract.js +31 -21
  40. package/dist/capabilities/session/golden-score.js +55 -33
  41. package/dist/capabilities/session/health-watch.js +146 -154
  42. package/dist/capabilities/session/read-guard.js +55 -47
  43. package/dist/capabilities/session/static-flight-golden.js +49 -36
  44. package/dist/capabilities/session/transport.js +54 -61
  45. package/dist/capabilities/session/wizard.js +63 -93
  46. package/dist/capabilities/session-consent.js +143 -47
  47. package/dist/capabilities/session-login.js +95 -126
  48. package/dist/capabilities/session-search.js +391 -253
  49. package/dist/capabilities/sponsor-plugin.js +341 -0
  50. package/dist/capabilities/visa-policy.js +87 -73
  51. package/dist/capabilities/weather.js +163 -235
  52. package/dist/scripts/action-cache-tests.js +24 -59
  53. package/dist/scripts/agent-planning-turn-deadline-e2e.js +91 -142
  54. package/dist/scripts/agent-planning-turn-deadline-tests.js +80 -176
  55. package/dist/scripts/agent-reach-deep-tests.js +30 -40
  56. package/dist/scripts/agent-reach-tests.js +26 -18
  57. package/dist/scripts/agent-reach-wrapper-tests.js +46 -50
  58. package/dist/scripts/anything-tests.js +58 -62
  59. package/dist/scripts/artifact-client-contract-tests.js +69 -0
  60. package/dist/scripts/artifacts-capability-tests.js +235 -0
  61. package/dist/scripts/async-collect.js +26 -15
  62. package/dist/scripts/benchmark-environment-bridge-e2e.js +287 -684
  63. package/dist/scripts/benchmark-environment-bridge-tests.js +818 -3123
  64. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +66 -279
  65. package/dist/scripts/booking-copilot-availability-policy-tests.js +153 -1257
  66. package/dist/scripts/booking-copilot-bin-proof-tests.js +22 -45
  67. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +64 -76
  68. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +69 -199
  69. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +393 -759
  70. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +33 -75
  71. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +60 -110
  72. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +28 -66
  73. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +72 -271
  74. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +73 -275
  75. package/dist/scripts/booking-copilot-runtime-proof-tests.js +784 -3398
  76. package/dist/scripts/booking-copilot-server-proof-tests.js +63 -168
  77. package/dist/scripts/booking-copilot-startup-proof-tests.js +38 -124
  78. package/dist/scripts/booking-executor-tests.js +80 -0
  79. package/dist/scripts/booking-recovery-chain-tests.js +182 -0
  80. package/dist/scripts/booking-saga-tests.js +61 -123
  81. package/dist/scripts/booking-surface-contract-proof-tests.js +152 -1167
  82. package/dist/scripts/bootstrap-tests.js +1383 -128
  83. package/dist/scripts/build-changelog.js +89 -130
  84. package/dist/scripts/build-metrics-report.js +135 -143
  85. package/dist/scripts/changelog-tests.js +35 -82
  86. package/dist/scripts/channel-probe-tests.js +44 -116
  87. package/dist/scripts/channel-probe.js +84 -167
  88. package/dist/scripts/channel-registry-tests.js +97 -185
  89. package/dist/scripts/companion-tests.js +25 -70
  90. package/dist/scripts/dida-cookie-probe.js +87 -0
  91. package/dist/scripts/dida-sw-probe.js +114 -0
  92. package/dist/scripts/diff-test.js +17 -12
  93. package/dist/scripts/doctor-tests.js +428 -120
  94. package/dist/scripts/dsh-artifact-e2e.js +150 -0
  95. package/dist/scripts/dsh-artifact-web-e2e.js +443 -0
  96. package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
  97. package/dist/scripts/dsh-runtime-closure-tests.js +110 -153
  98. package/dist/scripts/dsh-runtime-closure.js +75 -63
  99. package/dist/scripts/dsh-session-v3-migration-proof.js +106 -0
  100. package/dist/scripts/dsh-subprocess-local-proof.js +149 -0
  101. package/dist/scripts/dsh-target-closure-proof.js +178 -0
  102. package/dist/scripts/effect-tests.js +165 -552
  103. package/dist/scripts/engine-run.js +1 -3
  104. package/dist/scripts/engine-tests.js +16 -10
  105. package/dist/scripts/evaluation-cadence-tests.js +69 -242
  106. package/dist/scripts/evaluation-contract-tests.js +193 -499
  107. package/dist/scripts/extension-distribution-cli.js +19 -15
  108. package/dist/scripts/extension-distribution-tests.js +98 -211
  109. package/dist/scripts/extension-tests.js +366 -389
  110. package/dist/scripts/fact-gate-tests.js +725 -263
  111. package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
  112. package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
  113. package/dist/scripts/flight-malformed-tests.js +159 -0
  114. package/dist/scripts/flyai-tests.js +325 -68
  115. package/dist/scripts/g5-guard-tests.js +131 -0
  116. package/dist/scripts/g5-guard.js +189 -0
  117. package/dist/scripts/gen-lunar-anchors.js +106 -0
  118. package/dist/scripts/gotry-backend-kernel-tests.js +79 -0
  119. package/dist/scripts/ground-transfer-tests.js +956 -0
  120. package/dist/scripts/hbcli-e2e-tests.js +71 -90
  121. package/dist/scripts/hbcli-live-optin-tests.js +96 -0
  122. package/dist/scripts/hbcli-release-contract-tests.js +122 -0
  123. package/dist/scripts/hbcli-tests.js +41 -115
  124. package/dist/scripts/health-watch-cli.js +42 -28
  125. package/dist/scripts/hotel-date-gate-tests.js +496 -0
  126. package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
  127. package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
  128. package/dist/scripts/i18n-tests.js +16 -9
  129. package/dist/scripts/incident-tests.js +281 -87
  130. package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
  131. package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
  132. package/dist/scripts/issue-271-liveness-tests.js +547 -0
  133. package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
  134. package/dist/scripts/issue-289-model-retry-tests.js +441 -0
  135. package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
  136. package/dist/scripts/issue-338-home-city-e2e.js +577 -0
  137. package/dist/scripts/issue343-real-entry-e2e.js +160 -0
  138. package/dist/scripts/journey-tests.js +22 -27
  139. package/dist/scripts/kernel-manifest-evidence.js +109 -0
  140. package/dist/scripts/kernel-manifest-gate.js +126 -0
  141. package/dist/scripts/kernel-manifest-tests.js +212 -0
  142. package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
  143. package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
  144. package/dist/scripts/kernel-manifest.js +369 -0
  145. package/dist/scripts/ledger-repair-apply-tests.js +316 -0
  146. package/dist/scripts/ledger-repair-plan-tests.js +375 -0
  147. package/dist/scripts/ledger-tests.js +325 -340
  148. package/dist/scripts/ledger-workflow-crash.js +10 -20
  149. package/dist/scripts/map-tools-vendor-package-proof.js +206 -134
  150. package/dist/scripts/memory-capture-tests.js +242 -61
  151. package/dist/scripts/memory-decay-tests.js +18 -52
  152. package/dist/scripts/memory-lifecycle-tests.js +657 -0
  153. package/dist/scripts/memory-lifecycle.js +196 -0
  154. package/dist/scripts/memory-metrics.js +24 -12
  155. package/dist/scripts/memory-value-report.js +474 -222
  156. package/dist/scripts/memory-value-tests.js +328 -0
  157. package/dist/scripts/metrics-report-tests.js +62 -199
  158. package/dist/scripts/model-override-e2e.js +66 -105
  159. package/dist/scripts/nightly-evidence-tests.js +64 -89
  160. package/dist/scripts/nightly-evidence.js +108 -111
  161. package/dist/scripts/nudge-digest.js +39 -28
  162. package/dist/scripts/onboarding-tests.js +63 -88
  163. package/dist/scripts/opensky-check.js +18 -13
  164. package/dist/scripts/opensky-tests.js +28 -28
  165. package/dist/scripts/persona-surface-guard-tests.js +37 -12
  166. package/dist/scripts/pnpm-dsh-closure-proof.js +2 -5
  167. package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
  168. package/dist/scripts/price-drift-tests.js +126 -227
  169. package/dist/scripts/price-drift-watch.js +209 -253
  170. package/dist/scripts/probe-poi-tests.js +52 -45
  171. package/dist/scripts/product-metrics.js +187 -273
  172. package/dist/scripts/publish-preverify.js +47 -41
  173. package/dist/scripts/realtime-pricing-tests.js +37 -76
  174. package/dist/scripts/replay-async.js +20 -18
  175. package/dist/scripts/replay-real.js +16 -18
  176. package/dist/scripts/replay.js +52 -82
  177. package/dist/scripts/session-attach-diagnose.js +16 -12
  178. package/dist/scripts/session-attach-poc.js +34 -37
  179. package/dist/scripts/session-benchmark.js +66 -147
  180. package/dist/scripts/session-dida-live-e2e.js +151 -0
  181. package/dist/scripts/session-extract-tests.js +14 -35
  182. package/dist/scripts/session-login.js +14 -13
  183. package/dist/scripts/session-tests.js +545 -583
  184. package/dist/scripts/sf-live-benchmark.js +92 -110
  185. package/dist/scripts/sf-live-cli-tests.js +2 -9
  186. package/dist/scripts/sf-soft-score-tests.js +19 -60
  187. package/dist/scripts/sf-summary-tests.js +149 -0
  188. package/dist/scripts/sf-summary.js +466 -84
  189. package/dist/scripts/skeleton-check.js +16 -14
  190. package/dist/scripts/skeleton-integration-test.js +11 -10
  191. package/dist/scripts/skills-contract-tests.js +51 -46
  192. package/dist/scripts/smoke-session-gate-tests.js +12 -20
  193. package/dist/scripts/smoke.js +541 -422
  194. package/dist/scripts/sponsor-reuse-tests.js +262 -0
  195. package/dist/scripts/state-cli-tests.js +226 -101
  196. package/dist/scripts/state-cli.js +549 -212
  197. package/dist/scripts/static-golden-tests.js +46 -180
  198. package/dist/scripts/time-eval-tests.js +210 -192
  199. package/dist/scripts/travel-timeline-tests.js +29 -93
  200. package/dist/scripts/turn-handoff-collect-tests.js +46 -93
  201. package/dist/scripts/turn-handoff-collect.js +71 -99
  202. package/dist/scripts/turn-policy-tests.js +28 -29
  203. package/dist/scripts/typed-contract-canary.js +77 -162
  204. package/dist/scripts/tz-resolver-sanity.js +50 -0
  205. package/dist/scripts/tz-resolver-tests.js +584 -0
  206. package/dist/scripts/unified-tests.js +23 -19
  207. package/dist/scripts/visa-policy-tests.js +31 -44
  208. package/dist/scripts/weather-tests.js +121 -543
  209. package/dist/scripts/wish-channel-gate-tests.js +29 -86
  210. package/dist/scripts/write-gate-tests.js +522 -0
  211. package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
  212. package/dist/scripts/z3-lifecycle-tests.js +170 -0
  213. package/dist/scripts/z3-race-repeat-tests.js +39 -0
  214. package/dist/scripts/z3-race-tests.js +18 -12
  215. package/dist/src/artifact-gate.js +652 -221
  216. package/dist/src/backend/kernel.js +91 -0
  217. package/dist/src/backend/modules/booking-copilot.js +55 -0
  218. package/dist/src/backend/modules/booking-executor.js +124 -0
  219. package/dist/src/backend/modules/session-search.js +194 -0
  220. package/dist/src/benchmark-agent-conformance.js +296 -262
  221. package/dist/src/benchmark-environment-bridge.js +298 -413
  222. package/dist/src/benchmark-headless-child-diagnostics.js +93 -100
  223. package/dist/src/benchmark-tool-isolation.js +94 -67
  224. package/dist/src/bookable-facts.js +299 -174
  225. package/dist/src/booking-saga.js +64 -120
  226. package/dist/src/booking-surface/availability-policy.js +443 -576
  227. package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
  228. package/dist/src/booking-surface/contracts.js +24 -80
  229. package/dist/src/booking-surface/dsh-planner.js +510 -281
  230. package/dist/src/booking-surface/dsh-plugin.js +17 -1
  231. package/dist/src/booking-surface/error-codes.js +82 -10
  232. package/dist/src/booking-surface/index.js +0 -3
  233. package/dist/src/booking-surface/profile.js +10 -54
  234. package/dist/src/booking-surface/recovery-chain.js +367 -0
  235. package/dist/src/booking-surface/runtime.js +765 -891
  236. package/dist/src/booking-surface/server.js +200 -251
  237. package/dist/src/booking-surface/startup.js +58 -54
  238. package/dist/src/booking-surface/validation.js +150 -218
  239. package/dist/src/bridge.js +20 -13
  240. package/dist/src/companions.js +48 -74
  241. package/dist/src/contracts.js +21 -43
  242. package/dist/src/dsh-llm.js +82 -120
  243. package/dist/src/engine.js +253 -239
  244. package/dist/src/evaluation-cadence.js +72 -209
  245. package/dist/src/evaluation-contracts.js +220 -843
  246. package/dist/src/flight-pack-adapter.js +54 -0
  247. package/dist/src/flight-pack-contract.js +2 -0
  248. package/dist/src/gotry-backend.js +70 -0
  249. package/dist/src/hotel-date-gate.js +118 -0
  250. package/dist/src/i18n.js +18 -7
  251. package/dist/src/index.js +1425 -1629
  252. package/dist/src/journey.js +132 -81
  253. package/dist/src/ledger-repair-apply.js +538 -0
  254. package/dist/src/ledger-repair-plan.js +463 -0
  255. package/dist/src/loop.js +257 -166
  256. package/dist/src/memory-capture.js +180 -32
  257. package/dist/src/memory-decay.js +30 -14
  258. package/dist/src/memory-lifecycle.js +1310 -0
  259. package/dist/src/memory-utility.js +27 -40
  260. package/dist/src/mock-llm.js +36 -61
  261. package/dist/src/model.js +81 -36
  262. package/dist/src/realtime-pricing.js +63 -72
  263. package/dist/src/slot-spec.js +84 -106
  264. package/dist/src/state-ledger.js +310 -471
  265. package/dist/src/subagent-job-id-guard.js +58 -0
  266. package/dist/src/time-anchor.js +231 -33
  267. package/dist/src/tool-packet.js +20 -8
  268. package/dist/src/travel-slots.js +58 -35
  269. package/dist/src/travel-timeline.js +44 -63
  270. package/dist/src/turn-deadline.js +121 -127
  271. package/dist/src/turn-policy.js +61 -89
  272. package/dist/src/tz-resolver.js +143 -0
  273. package/dist/src/unified.js +504 -381
  274. package/dist/src/wish-pool.js +39 -14
  275. package/dist/src/write-gate.js +495 -0
  276. package/dist/src/z3-shared.js +273 -7
  277. package/extension/README.md +4 -3
  278. package/extension/background.js +125 -14
  279. package/extension/content-bridge.js +23 -1
  280. package/extension/content-main.js +15 -4
  281. package/extension/manifest.json +16 -8
  282. package/package.json +251 -220
  283. package/ts/capabilities/agent-reach.ts +4 -4
  284. package/ts/capabilities/anything.ts +1 -1
  285. package/ts/capabilities/artifacts.ts +46 -9
  286. package/ts/capabilities/flyai.ts +156 -37
  287. package/ts/capabilities/hbcli.ts +1 -1
  288. package/ts/capabilities/incident-log.ts +88 -59
  289. package/ts/capabilities/session/adapters/ctrip-flight.ts +99 -36
  290. package/ts/capabilities/session/adapters/dida-portal.ts +250 -0
  291. package/ts/capabilities/session/adapters/rail-12306.ts +123 -24
  292. package/ts/capabilities/session/extension-bridge.ts +226 -101
  293. package/ts/capabilities/session/extension-channel.ts +22 -9
  294. package/ts/capabilities/session/wizard.ts +2 -2
  295. package/ts/capabilities/session-consent.ts +98 -14
  296. package/ts/capabilities/session-login.ts +1 -0
  297. package/ts/capabilities/session-search.ts +315 -31
  298. package/ts/dsh-runtime/vendor/README.md +8 -4
  299. package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +7 -1
  300. package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +5 -3
  301. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +1 -1
  302. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +1 -1
  303. package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +6 -6
  304. package/ts/package.json +236 -16
  305. package/ts/scripts/map-tools-vendor-package-proof.ts +152 -31
  306. package/ts/scripts/state-cli.ts +418 -51
  307. package/ts/src/artifact-gate.ts +561 -15
  308. package/ts/src/benchmark-environment-bridge.ts +37 -20
  309. package/ts/src/bookable-facts.ts +183 -7
  310. package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
  311. package/ts/src/booking-surface/dsh-planner.ts +240 -32
  312. package/ts/src/booking-surface/dsh-plugin.js +17 -1
  313. package/ts/src/booking-surface/error-codes.ts +75 -0
  314. package/ts/src/booking-surface/recovery-chain.ts +409 -0
  315. package/ts/src/booking-surface/server.ts +84 -51
  316. package/ts/src/booking-surface/validation.ts +2 -0
  317. package/ts/src/contracts.ts +12 -2
  318. package/ts/src/dsh-llm.ts +4 -8
  319. package/ts/src/index.ts +609 -90
  320. package/ts/src/loop.ts +116 -7
  321. package/ts/src/memory-capture.ts +227 -3
  322. package/ts/src/mock-llm.ts +4 -8
  323. package/ts/src/model.ts +88 -2
  324. package/ts/src/state-ledger.ts +47 -29
  325. package/ts/src/subagent-job-id-guard.ts +72 -0
  326. package/ts/src/time-anchor.ts +217 -13
  327. package/ts/src/unified.ts +316 -54
@@ -0,0 +1,509 @@
1
+ /**
2
+ * Bounded ground-transfer seam for explicit airport/station coordinates.
3
+ *
4
+ * This capability owns only the boundary between a public
5
+ * `map_driving_route` result and the deterministic candidate input. It never
6
+ * calls a map client directly, never writes shared state, and never supplies
7
+ * a price: the selected candidate transfer keeps its static `priceCny`.
8
+ *
9
+ * Each `ground_transfer` request resolves two deterministic directions —
10
+ * outbound (origin → destination) and return (destination → origin) — and
11
+ * binds the resulting route minutes to the candidate transfer as
12
+ * `minutesOut` / `minutesRet` overrides consumed by the solver / arithmetic
13
+ * layer. Cache, freshness, and fallback are isolated per direction so a
14
+ * miss / error / stale / mismatch in one direction never borrows the
15
+ * dynamic value of the other.
16
+ */
17
+ import { ToolCallId } from '@deepseek-ai/dsh-llm';
18
+ export const GROUND_TRANSFER_MODE = 'driving';
19
+ export const GROUND_TRANSFER_POSITION = 'destination';
20
+ export const GROUND_TRANSFER_STATIC_MODE = 'taxi';
21
+ export const GROUND_TRANSFER_STATIC_MODE_MISMATCH = 'ground_transfer_static_mode_mismatch';
22
+ export const GROUND_TRANSFER_STATIC_PRICE_EVIDENCE = '[静态包:估算]';
23
+ export const GROUND_TRANSFER_DEFAULT_MAX_AGE_S = 15 * 60;
24
+ const GROUND_TRANSFER_CACHE_LIMIT = 32;
25
+ function asRecord(value) {
26
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
27
+ ? value
28
+ : null;
29
+ }
30
+ function coordinate(value) {
31
+ const record = asRecord(value);
32
+ const longitude = record?.['longitude'];
33
+ const latitude = record?.['latitude'];
34
+ if (typeof longitude !== 'number' || !Number.isFinite(longitude) || longitude < -180 || longitude > 180
35
+ || typeof latitude !== 'number' || !Number.isFinite(latitude) || latitude < -90 || latitude > 90)
36
+ return null;
37
+ return { longitude, latitude };
38
+ }
39
+ function coordinatePair(value) {
40
+ const record = asRecord(value);
41
+ if (!record)
42
+ return null;
43
+ const origin = coordinate(record['origin']);
44
+ const destination = coordinate(record['destination']);
45
+ if (origin === null || destination === null)
46
+ return null;
47
+ return { origin, destination };
48
+ }
49
+ function coordinateText(value) {
50
+ return `${String(value.longitude)},${String(value.latitude)}`;
51
+ }
52
+ function cacheKey(direction) {
53
+ return JSON.stringify({
54
+ mode: GROUND_TRANSFER_MODE,
55
+ direction: direction.direction,
56
+ origin: direction.origin,
57
+ destination: direction.destination,
58
+ });
59
+ }
60
+ function nowMs(clock) {
61
+ const value = clock().getTime();
62
+ if (!Number.isFinite(value))
63
+ throw new Error('ground-transfer clock returned an invalid date');
64
+ return value;
65
+ }
66
+ function asOf(clock) {
67
+ return new Date(nowMs(clock)).toISOString();
68
+ }
69
+ function safeErrorMessage(value) {
70
+ const message = value instanceof Error ? value.message : String(value);
71
+ return message.replace(/[\r\n]+/g, ' ').trim().slice(0, 400) || 'unknown provider error';
72
+ }
73
+ function staticCache(status = 'bypass') {
74
+ return { status, ageS: null, asOf: null };
75
+ }
76
+ function notAppliedBase(parts) {
77
+ return {
78
+ requestedMode: parts.requestedMode,
79
+ requestedPosition: parts.requestedPosition,
80
+ provenance: 'none',
81
+ asOf: null,
82
+ freshness: 'not_attempted',
83
+ cache: staticCache('bypass'),
84
+ evidenceClass: 'none',
85
+ };
86
+ }
87
+ function staticDirectionResolution(direction, staticMinutes, reason) {
88
+ return {
89
+ applied: false,
90
+ direction,
91
+ staticMinutes,
92
+ provenance: 'static-transfer-pack',
93
+ asOf: null,
94
+ freshness: 'fallback',
95
+ cache: staticCache('bypass'),
96
+ evidenceClass: 'static_transfer_estimate',
97
+ fallbackReason: reason,
98
+ };
99
+ }
100
+ function parseRequest(value) {
101
+ const record = asRecord(value);
102
+ if (!record) {
103
+ return {
104
+ candidateId: null,
105
+ transferIndex: null,
106
+ requestedMode: null,
107
+ requestedPosition: null,
108
+ outbound: null,
109
+ ret: null,
110
+ invalidReason: 'ground_transfer_request_invalid:expected an object',
111
+ };
112
+ }
113
+ const candidateId = typeof record['candidate_id'] === 'string' && record['candidate_id'].trim()
114
+ ? record['candidate_id']
115
+ : null;
116
+ const transferIndex = Number.isInteger(record['transfer_index']) && Number(record['transfer_index']) >= 0
117
+ ? Number(record['transfer_index'])
118
+ : null;
119
+ const requestedMode = typeof record['mode'] === 'string' && record['mode'].trim()
120
+ ? record['mode']
121
+ : null;
122
+ const requestedPosition = typeof record['position'] === 'string' && record['position'].trim()
123
+ ? record['position']
124
+ : null;
125
+ let outbound = null;
126
+ let ret = null;
127
+ if (record['outbound'] !== undefined || record['return'] !== undefined) {
128
+ outbound = coordinatePair(record['outbound']);
129
+ ret = coordinatePair(record['return']);
130
+ }
131
+ else {
132
+ // Legacy single-pair shape: outbound = origin/destination; return = the
133
+ // swapped pair. Keeps pre-#364 payloads behavior-compatible.
134
+ const legacyOrigin = coordinate(record['origin']);
135
+ const legacyDestination = coordinate(record['destination']);
136
+ if (legacyOrigin && legacyDestination) {
137
+ outbound = { origin: legacyOrigin, destination: legacyDestination };
138
+ ret = { origin: legacyDestination, destination: legacyOrigin };
139
+ }
140
+ }
141
+ let invalidReason;
142
+ if (candidateId === null)
143
+ invalidReason = 'ground_transfer_binding_invalid:candidate_id is required';
144
+ else if (transferIndex === null)
145
+ invalidReason = 'ground_transfer_binding_invalid:transfer_index must be a non-negative integer';
146
+ else if (requestedPosition !== GROUND_TRANSFER_POSITION)
147
+ invalidReason = `unsupported_ground_transfer_position:${requestedPosition ?? 'unknown'}`;
148
+ else if (requestedMode !== GROUND_TRANSFER_MODE)
149
+ invalidReason = `unsupported_ground_transfer_mode:${requestedMode ?? 'unknown'}`;
150
+ else if (outbound === null || ret === null)
151
+ invalidReason = 'invalid_ground_transfer_coordinates:longitude/latitude must be finite and in range';
152
+ return {
153
+ candidateId,
154
+ transferIndex,
155
+ requestedMode,
156
+ requestedPosition,
157
+ outbound,
158
+ ret,
159
+ invalidReason,
160
+ };
161
+ }
162
+ function completeRequest(parts) {
163
+ if (parts.invalidReason || parts.candidateId === null || parts.transferIndex === null
164
+ || parts.requestedMode !== GROUND_TRANSFER_MODE || parts.requestedPosition !== GROUND_TRANSFER_POSITION
165
+ || parts.outbound === null || parts.ret === null)
166
+ return null;
167
+ return {
168
+ candidateId: parts.candidateId,
169
+ transferIndex: parts.transferIndex,
170
+ mode: GROUND_TRANSFER_MODE,
171
+ position: GROUND_TRANSFER_POSITION,
172
+ outbound: { direction: 'outbound', origin: parts.outbound.origin, destination: parts.outbound.destination },
173
+ ret: { direction: 'return', origin: parts.ret.origin, destination: parts.ret.destination },
174
+ };
175
+ }
176
+ function parseRouteResult(value) {
177
+ const record = asRecord(value);
178
+ if (!record)
179
+ throw new Error('map_driving_route returned an invalid public result');
180
+ const provider = record?.['provider'];
181
+ const distanceM = record?.['distanceM'];
182
+ const durationS = record?.['durationS'];
183
+ if (typeof provider !== 'string' || provider.trim() === ''
184
+ || typeof distanceM !== 'number' || !Number.isFinite(distanceM) || distanceM < 0
185
+ || typeof durationS !== 'number' || !Number.isFinite(durationS) || durationS < 0
186
+ // A positive distance cannot be covered in zero time: such a response is
187
+ // a self-contradictory route fact, not a credible provider hit. It falls
188
+ // into the explicit provider-miss fallback instead of silently binding
189
+ // zero minutes into the solver.
190
+ || (distanceM > 0 && durationS === 0))
191
+ throw new Error('map_driving_route returned an invalid public result');
192
+ return {
193
+ provider,
194
+ distanceM,
195
+ durationS,
196
+ ...(typeof record['polyline'] === 'string' ? { polyline: record['polyline'] } : {}),
197
+ ...(Array.isArray(record['steps']) ? { steps: record['steps'] } : {}),
198
+ };
199
+ }
200
+ function buildRouteFact(direction, route, clock, cache) {
201
+ return {
202
+ direction: direction.direction,
203
+ origin: direction.origin,
204
+ destination: direction.destination,
205
+ mode: GROUND_TRANSFER_MODE,
206
+ provider: route.provider,
207
+ distanceM: route.distanceM,
208
+ durationS: route.durationS,
209
+ durationMin: Math.round(route.durationS / 60),
210
+ asOf: asOf(clock),
211
+ provenance: 'map_driving_route',
212
+ freshness: cache.status === 'hit' ? 'cache_hit' : 'fresh',
213
+ cache,
214
+ evidenceClass: 'public_map_route_estimate',
215
+ trafficStatus: 'not-live-route-estimate',
216
+ };
217
+ }
218
+ function selectionFor(candidates, parts) {
219
+ if (parts.candidateId === null || parts.transferIndex === null)
220
+ return null;
221
+ const matching = candidates
222
+ .map((candidate, index) => ({ candidate, index }))
223
+ .filter(({ candidate }) => candidate.id === parts.candidateId);
224
+ if (matching.length !== 1)
225
+ return null;
226
+ const match = matching[0];
227
+ const transfer = match.candidate.destTransfers[parts.transferIndex];
228
+ if (!transfer || typeof transfer.mode !== 'string' || !Number.isFinite(transfer.minutes) || transfer.minutes < 0 || !Number.isFinite(transfer.priceCny) || transfer.priceCny < 0)
229
+ return null;
230
+ return {
231
+ candidateIndex: match.index,
232
+ selection: {
233
+ candidateId: parts.candidateId,
234
+ transferIndex: parts.transferIndex,
235
+ staticMode: transfer.mode,
236
+ staticMinutes: transfer.minutes,
237
+ staticPriceCny: transfer.priceCny,
238
+ },
239
+ };
240
+ }
241
+ async function resolveDirection(direction, staticMinutes, provider, clock, cache, maxEntries, context) {
242
+ const key = cacheKey(direction);
243
+ const currentMs = nowMs(clock);
244
+ const cached = cache.get(key);
245
+ if (cached) {
246
+ const ageS = Math.max(0, currentMs - cached.storedAtMs) / 1000;
247
+ if (ageS <= GROUND_TRANSFER_DEFAULT_MAX_AGE_S) {
248
+ const hitCache = { status: 'hit', ageS, asOf: cached.fact.asOf };
249
+ const hitFact = { ...cached.fact, freshness: 'cache_hit', cache: hitCache };
250
+ return {
251
+ applied: true,
252
+ direction: direction.direction,
253
+ minutes: hitFact.durationMin,
254
+ staticMinutes,
255
+ provenance: 'map_driving_route',
256
+ asOf: hitFact.asOf,
257
+ freshness: 'cache_hit',
258
+ cache: hitCache,
259
+ evidenceClass: 'public_map_route_estimate',
260
+ routeFact: hitFact,
261
+ };
262
+ }
263
+ }
264
+ const requestForProvider = {
265
+ origin: coordinateText(direction.origin),
266
+ destination: coordinateText(direction.destination),
267
+ mode: GROUND_TRANSFER_MODE,
268
+ };
269
+ try {
270
+ const raw = await provider(requestForProvider, {
271
+ signal: context.signal ?? new AbortController().signal,
272
+ execution: context.execution,
273
+ });
274
+ const route = parseRouteResult(raw);
275
+ const cacheInfo = {
276
+ status: cached ? 'requery' : 'miss',
277
+ ageS: 0,
278
+ asOf: null,
279
+ };
280
+ const fact = buildRouteFact(direction, route, clock, cacheInfo);
281
+ cacheInfo.asOf = fact.asOf;
282
+ const entry = { fact, storedAtMs: currentMs };
283
+ if (cache.size >= maxEntries && !cache.has(key)) {
284
+ const oldest = cache.keys().next().value;
285
+ if (typeof oldest === 'string')
286
+ cache.delete(oldest);
287
+ }
288
+ cache.set(key, entry);
289
+ return {
290
+ applied: true,
291
+ direction: direction.direction,
292
+ minutes: fact.durationMin,
293
+ staticMinutes,
294
+ provenance: 'map_driving_route',
295
+ asOf: fact.asOf,
296
+ freshness: 'fresh',
297
+ cache: cacheInfo,
298
+ evidenceClass: 'public_map_route_estimate',
299
+ routeFact: fact,
300
+ };
301
+ }
302
+ catch (error) {
303
+ const detail = safeErrorMessage(error);
304
+ return {
305
+ applied: false,
306
+ direction: direction.direction,
307
+ staticMinutes,
308
+ provenance: 'static-transfer-pack',
309
+ asOf: null,
310
+ freshness: 'fallback',
311
+ cache: staticCache(cached ? 'stale' : 'bypass'),
312
+ evidenceClass: 'static_transfer_estimate',
313
+ fallbackReason: cached
314
+ ? `stale_route_requery_error:${direction.direction}:${detail}`
315
+ : `map_driving_route_provider_error:${direction.direction}:${detail}`,
316
+ };
317
+ }
318
+ }
319
+ export function createGroundTransferResolver(options) {
320
+ const clock = options.clock ?? (() => new Date());
321
+ const maxEntries = Number.isInteger(options.maxEntries) && Number(options.maxEntries) > 0
322
+ ? Number(options.maxEntries)
323
+ : GROUND_TRANSFER_CACHE_LIMIT;
324
+ const cache = new Map();
325
+ return {
326
+ async resolve(request, selection, context = {}) {
327
+ const [outbound, ret] = await Promise.all([
328
+ resolveDirection(request.outbound, selection.staticMinutes, options.provider, clock, cache, maxEntries, context),
329
+ resolveDirection(request.ret, selection.staticMinutes, options.provider, clock, cache, maxEntries, context),
330
+ ]);
331
+ const applied = outbound.applied || ret.applied;
332
+ // Aggregate fields are a summary only: per-direction objects above are
333
+ // the authoritative per-direction record. A full cache hit keeps the
334
+ // legacy aggregate semantics ('cache_hit'), a re-query keeps 'requery'.
335
+ const aggregateProvenance = applied ? 'map_driving_route' : 'static-transfer-pack';
336
+ const aggregateEvidenceClass = applied ? 'public_map_route_estimate' : 'static_transfer_estimate';
337
+ const aggregateFreshness = !applied
338
+ ? 'fallback'
339
+ : outbound.freshness === 'fresh' || ret.freshness === 'fresh'
340
+ ? 'fresh'
341
+ : 'cache_hit';
342
+ const statuses = [outbound.cache.status, ret.cache.status];
343
+ const aggregateCache = !applied
344
+ ? staticCache('bypass')
345
+ : {
346
+ status: statuses.includes('hit') ? 'hit' : statuses.includes('requery') ? 'requery' : 'miss',
347
+ ageS: 0,
348
+ asOf: null,
349
+ };
350
+ const aggregateAsOf = applied ? (outbound.asOf ?? ret.asOf) : null;
351
+ const resolution = {
352
+ applied,
353
+ candidateId: selection.candidateId,
354
+ transferIndex: selection.transferIndex,
355
+ requestedMode: request.mode,
356
+ requestedPosition: request.position,
357
+ staticMode: selection.staticMode,
358
+ staticMinutes: selection.staticMinutes,
359
+ priceCny: selection.staticPriceCny,
360
+ priceEvidence: GROUND_TRANSFER_STATIC_PRICE_EVIDENCE,
361
+ provenance: aggregateProvenance,
362
+ asOf: aggregateAsOf,
363
+ freshness: aggregateFreshness,
364
+ cache: aggregateCache,
365
+ evidenceClass: aggregateEvidenceClass,
366
+ outbound,
367
+ return: ret,
368
+ };
369
+ if (outbound.applied)
370
+ resolution.outboundMinutes = outbound.minutes;
371
+ if (ret.applied)
372
+ resolution.returnMinutes = ret.minutes;
373
+ if (!applied) {
374
+ resolution.fallbackReason = `both_directions_failed:${outbound.fallbackReason ?? 'unknown'};${ret.fallbackReason ?? 'unknown'}`;
375
+ }
376
+ return resolution;
377
+ },
378
+ };
379
+ }
380
+ /**
381
+ * Delegate through the registered public map tool. The dsh-tools types expose
382
+ * nested execution explicitly, so the current root/agent/signal are carried
383
+ * into the sub-dispatch instead of calling a private map client.
384
+ */
385
+ export function createPublicMapDrivingRouteProvider(ctx) {
386
+ return async (request, context) => {
387
+ const execution = context.execution;
388
+ if (!execution)
389
+ throw new Error('GROUND_TRANSFER_NESTED_EXECUTION_UNSUPPORTED: current tool execution context is unavailable');
390
+ const tools = ctx.tools;
391
+ if (typeof tools?.execute !== 'function' || typeof tools?.get !== 'function') {
392
+ throw new Error('GROUND_TRANSFER_NESTED_EXECUTION_UNSUPPORTED: host must expose dsh-tools ToolRuntime.execute/get');
393
+ }
394
+ if (!tools.get('map_driving_route', execution.agent)) {
395
+ throw new Error('GROUND_TRANSFER_PUBLIC_MAP_TOOL_UNAVAILABLE: map_driving_route is not registered in the current tool scope');
396
+ }
397
+ const nested = {
398
+ callId: ToolCallId(`${String(execution.callId)}:ground-transfer`),
399
+ rootCallId: execution.rootCallId,
400
+ name: 'map_driving_route',
401
+ arguments: { origin: request.origin, destination: request.destination },
402
+ ...(execution.agent !== undefined ? { agent: execution.agent } : {}),
403
+ parent: execution.token,
404
+ signal: execution.signal,
405
+ };
406
+ const result = await tools.execute(nested);
407
+ if (result.isError)
408
+ throw new Error(`map_driving_route failed: ${result.error.message}`);
409
+ return parseRouteResult(result.value);
410
+ };
411
+ }
412
+ /** Resolve and patch exactly one named transfer position before solver entry. */
413
+ export async function resolveGroundTransferPayload(rawRequest, candidates, resolver, context) {
414
+ const parts = parseRequest(rawRequest);
415
+ const bound = selectionFor(candidates, parts);
416
+ if (!bound) {
417
+ const base = notAppliedBase(parts);
418
+ return {
419
+ candidates: [...candidates],
420
+ resolution: {
421
+ ...base,
422
+ applied: false,
423
+ ...(parts.candidateId !== null ? { candidateId: parts.candidateId } : {}),
424
+ ...(parts.transferIndex !== null ? { transferIndex: parts.transferIndex } : {}),
425
+ outbound: staticDirectionResolution('outbound', Number.NaN, 'ground_transfer_binding_mismatch'),
426
+ return: staticDirectionResolution('return', Number.NaN, 'ground_transfer_binding_mismatch'),
427
+ fallbackReason: 'ground_transfer_binding_mismatch: candidate_id and transfer_index must identify exactly one valid static transfer',
428
+ },
429
+ };
430
+ }
431
+ const complete = completeRequest(parts);
432
+ if (!complete) {
433
+ return {
434
+ candidates: [...candidates],
435
+ resolution: {
436
+ applied: false,
437
+ candidateId: bound.selection.candidateId,
438
+ transferIndex: bound.selection.transferIndex,
439
+ requestedMode: parts.requestedMode,
440
+ requestedPosition: parts.requestedPosition,
441
+ staticMode: bound.selection.staticMode,
442
+ staticMinutes: bound.selection.staticMinutes,
443
+ priceCny: bound.selection.staticPriceCny,
444
+ priceEvidence: GROUND_TRANSFER_STATIC_PRICE_EVIDENCE,
445
+ provenance: 'static-transfer-pack',
446
+ asOf: null,
447
+ freshness: 'fallback',
448
+ cache: staticCache('bypass'),
449
+ evidenceClass: 'static_transfer_estimate',
450
+ outbound: staticDirectionResolution('outbound', bound.selection.staticMinutes, parts.invalidReason ?? 'ground_transfer_request_invalid'),
451
+ return: staticDirectionResolution('return', bound.selection.staticMinutes, parts.invalidReason ?? 'ground_transfer_request_invalid'),
452
+ fallbackReason: `${parts.invalidReason ?? 'ground_transfer_request_invalid'}; static transfer minutes and price preserved`,
453
+ },
454
+ };
455
+ }
456
+ if (bound.selection.staticMode !== GROUND_TRANSFER_STATIC_MODE) {
457
+ const reason = `${GROUND_TRANSFER_STATIC_MODE_MISMATCH}: mode=${bound.selection.staticMode}; only taxi accepts a driving route; static transfer preserved`;
458
+ return {
459
+ candidates: [...candidates],
460
+ resolution: {
461
+ applied: false,
462
+ candidateId: bound.selection.candidateId,
463
+ transferIndex: bound.selection.transferIndex,
464
+ requestedMode: parts.requestedMode,
465
+ requestedPosition: parts.requestedPosition,
466
+ staticMode: bound.selection.staticMode,
467
+ staticMinutes: bound.selection.staticMinutes,
468
+ priceCny: bound.selection.staticPriceCny,
469
+ priceEvidence: GROUND_TRANSFER_STATIC_PRICE_EVIDENCE,
470
+ provenance: 'static-transfer-pack',
471
+ asOf: null,
472
+ freshness: 'not_attempted',
473
+ cache: staticCache('bypass'),
474
+ evidenceClass: 'static_transfer_estimate',
475
+ outbound: staticDirectionResolution('outbound', bound.selection.staticMinutes, reason),
476
+ return: staticDirectionResolution('return', bound.selection.staticMinutes, reason),
477
+ fallbackReason: reason,
478
+ },
479
+ };
480
+ }
481
+ const resolution = await resolver.resolve(complete, bound.selection, context);
482
+ if (!resolution.applied) {
483
+ return { candidates: [...candidates], resolution };
484
+ }
485
+ const patched = candidates.map((candidate, candidateIndex) => {
486
+ if (candidateIndex !== bound.candidateIndex)
487
+ return candidate;
488
+ const destTransfers = candidate.destTransfers.map((transfer, transferIndex) => {
489
+ if (transferIndex !== bound.selection.transferIndex)
490
+ return transfer;
491
+ const next = { ...transfer };
492
+ if (resolution.outboundMinutes !== undefined)
493
+ next['minutesOut'] = resolution.outboundMinutes;
494
+ if (resolution.returnMinutes !== undefined)
495
+ next['minutesRet'] = resolution.returnMinutes;
496
+ return next;
497
+ });
498
+ return { ...candidate, destTransfers };
499
+ });
500
+ return { candidates: patched, resolution };
501
+ }
502
+ export function exposeGroundTransferEvidence(result, resolution) {
503
+ const verdicts = Array.isArray(result['verdicts'])
504
+ ? result['verdicts'].map(verdict => resolution.candidateId !== undefined && verdict['candidate_id'] === resolution.candidateId
505
+ ? { ...verdict, transfer_evidence: resolution }
506
+ : verdict)
507
+ : result['verdicts'];
508
+ return { ...result, verdicts, ground_transfer: resolution };
509
+ }