@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,296 @@
1
+ /**
2
+ * Issue #289 native request proof.
3
+ *
4
+ * Provenance: the earlier Claude Code candidate mounted dsh-llm-retry but
5
+ * never consumed LlmRuntime.stream(); its FixtureLlmAdapter.stream() was dead
6
+ * code. Claude's recent attempts returned 503 or were boundedly stopped. This
7
+ * replacement drives the installed AgentLoop host, so the installed
8
+ * dsh-llm-retry listener receives the real agent/request-error event and owns
9
+ * the retry decision. Every provider attempt still goes through
10
+ * LlmRuntime -> LlmAdapter.stream() -> a real 127.0.0.1 HTTP server.
11
+ *
12
+ * Pinned acceptance for this proof:
13
+ * 1. normal provider retry is finite and request-count bounded;
14
+ * 2. 429 is retried, timeout and server failure reach stable terminal
15
+ * errors without another request after the bound;
16
+ * 3. cancelling the real AgentLoop during retry backoff produces no started
17
+ * retry and no subsequent provider request;
18
+ * 4. durable retry events carry the UI-visible retry index and limit.
19
+ *
20
+ * No real provider, credentials, browser, supplier, booking write, or shared
21
+ * gotry-state is used. Run from ts/:
22
+ * npx tsx scripts/issue-289-model-retry-real-tests.ts
23
+ */
24
+ import assert from 'node:assert/strict';
25
+ import { createServer } from 'node:http';
26
+ import { Context } from '@deepseek-ai/cordis';
27
+ import AgentRegistry from '@deepseek-ai/dsh-agent';
28
+ import AgentLoop from '@deepseek-ai/dsh-agent-loop';
29
+ import { LlmAdapter, LlmError, LlmRuntime, createUserMessage, } from '@deepseek-ai/dsh-llm';
30
+ import { apply as applyLlmRetry } from '@deepseek-ai/dsh-llm-retry';
31
+ import SessionProjection from '@deepseek-ai/dsh-session-projection';
32
+ import SessionStore, { SessionId } from '@deepseek-ai/dsh-session';
33
+ import SystemPrompt from '@deepseek-ai/dsh-system-prompt';
34
+ import ToolRuntime from '@deepseek-ai/dsh-tools';
35
+ function statusFor(code) {
36
+ if (code === 'RATE_LIMIT')
37
+ return 429;
38
+ if (code === 'TIMEOUT')
39
+ return 408;
40
+ if (code === 'SERVER')
41
+ return 503;
42
+ return 400;
43
+ }
44
+ function startProviderFixture() {
45
+ let sequence = [{ kind: 'ok' }];
46
+ const records = [];
47
+ const server = createServer((req, res) => {
48
+ const chunks = [];
49
+ req.on('data', (chunk) => chunks.push(chunk));
50
+ req.on('end', () => {
51
+ const index = records.length;
52
+ records.push({ index, method: req.method ?? 'GET', url: req.url ?? '/', body: Buffer.concat(chunks).toString('utf8') });
53
+ const spec = sequence[Math.min(index, sequence.length - 1)] ?? { kind: 'ok' };
54
+ if (spec.kind === 'ok') {
55
+ res.writeHead(200, { 'content-type': 'text/event-stream; charset=utf-8' });
56
+ res.end('data: {"choices":[{"delta":{"content":"fixture-ok"}}]}\n\ndata: [DONE]\n\n');
57
+ return;
58
+ }
59
+ if (spec.code === 'TIMEOUT') {
60
+ // The adapter reads the body with its own timeout signal; this response
61
+ // intentionally never ends, so the installed runtime sees TIMEOUT.
62
+ res.writeHead(200, { 'content-type': 'text/event-stream; charset=utf-8' });
63
+ res.write(': keep-alive\n\n');
64
+ return;
65
+ }
66
+ const code = spec.code ?? 'SERVER';
67
+ const status = spec.status ?? statusFor(code);
68
+ res.writeHead(status, { 'content-type': 'application/json' });
69
+ res.end(JSON.stringify({ error: { code, message: `fixture ${code}`, status } }));
70
+ });
71
+ });
72
+ return new Promise((resolve, reject) => {
73
+ server.once('error', reject);
74
+ server.listen(0, '127.0.0.1', () => {
75
+ const address = server.address();
76
+ if (!address || typeof address !== 'object') {
77
+ reject(new Error('loopback fixture did not bind'));
78
+ return;
79
+ }
80
+ resolve({
81
+ url: `http://127.0.0.1:${address.port}/v1/chat/completions`,
82
+ records,
83
+ get requestCount() { return records.length; },
84
+ setSequence(next) { sequence = [...next]; },
85
+ close: () => new Promise((res, rej) => server.close(error => error ? rej(error) : res())),
86
+ });
87
+ });
88
+ });
89
+ }
90
+ class FixtureLlmAdapter extends LlmAdapter {
91
+ endpoint;
92
+ timeoutMs;
93
+ policy;
94
+ constructor(endpoint, timeoutMs, policy) {
95
+ super();
96
+ this.endpoint = endpoint;
97
+ this.timeoutMs = timeoutMs;
98
+ this.policy = policy;
99
+ }
100
+ providerInfo(provider) { return { id: provider, name: 'loopback fixture' }; }
101
+ providerRetryPolicy(_provider) { return this.policy; }
102
+ resolveModel(provider, model) { return Promise.resolve({ provider, id: model, name: model }); }
103
+ async *stream(options) {
104
+ const timeout = AbortSignal.timeout(this.timeoutMs);
105
+ const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
106
+ try {
107
+ const response = await fetch(this.endpoint, {
108
+ method: 'POST',
109
+ headers: { 'content-type': 'application/json', authorization: 'Bearer loopback-only' },
110
+ body: JSON.stringify({ provider: options.provider, model: options.model, messages: options.messages }),
111
+ signal,
112
+ });
113
+ const body = await response.text();
114
+ if (!response.ok) {
115
+ let error = {};
116
+ try {
117
+ error = JSON.parse(body).error ?? {};
118
+ }
119
+ catch { /* preserve the HTTP status */ }
120
+ throw new LlmError(error.message ?? `fixture HTTP ${response.status}`, error.code ?? 'SERVER', { status: error.status ?? response.status });
121
+ }
122
+ yield { type: 'text-delta', index: 0, text: body.includes('[DONE]') ? 'fixture-ok' : body };
123
+ yield { type: 'finish', reason: { kind: 'stop' } };
124
+ }
125
+ catch (error) {
126
+ if (options.signal?.aborted)
127
+ throw new LlmError('fixture request aborted', 'ABORTED', { cause: error });
128
+ if (timeout.aborted)
129
+ throw new LlmError('fixture request timed out', 'TIMEOUT', { cause: error, status: 408 });
130
+ if (error instanceof LlmError)
131
+ throw error;
132
+ throw new LlmError('fixture transport failure', 'TRANSPORT', { cause: error });
133
+ }
134
+ }
135
+ }
136
+ async function startWorld(sequence, policy, timeoutMs = 1000) {
137
+ const fixture = await startProviderFixture();
138
+ fixture.setSequence(sequence);
139
+ const ctx = new Context();
140
+ await ctx.plugin(LlmRuntime);
141
+ await ctx.plugin(SessionStore);
142
+ await ctx.plugin(SessionProjection);
143
+ await ctx.plugin(SystemPrompt, {});
144
+ await ctx.plugin(ToolRuntime, {});
145
+ await ctx.plugin(AgentRegistry);
146
+ const adapter = new FixtureLlmAdapter(fixture.url, timeoutMs, policy);
147
+ ctx.llm.registerAdapter(['fixture'], adapter);
148
+ // This is the installed upstream plugin's real listener and projection;
149
+ // AgentLoop below is the host that emits agent/request-error and consumes its
150
+ // retry action. No GoTry retry layer is added here.
151
+ applyLlmRetry(ctx, {}, { random: () => 0.5 });
152
+ await ctx.plugin(AgentLoop, { agents: [], maxParallelToolCalls: 1 });
153
+ const handle = await ctx.agentLoop.createAgent(ctx, {
154
+ sessionId: SessionId(`issue-289-loopback-${Date.now()}-${Math.random().toString(36).slice(2)}`),
155
+ agentOptions: { provider: 'fixture', model: 'fixture-model' },
156
+ });
157
+ return {
158
+ ctx,
159
+ adapter,
160
+ fixture,
161
+ agent: handle.agent,
162
+ handle,
163
+ policy,
164
+ async close() {
165
+ await handle.dispose();
166
+ await ctx.fiber.dispose();
167
+ await fixture.close();
168
+ },
169
+ };
170
+ }
171
+ function normalPolicy(overrides = {}) {
172
+ return {
173
+ mode: 'normal', maxRetries: 2,
174
+ retryableCodes: ['RATE_LIMIT', 'TIMEOUT', 'SERVER', 'TRANSPORT'],
175
+ initialDelayMs: 5, maxDelayMs: 30, jitterRatio: 0,
176
+ ...overrides,
177
+ };
178
+ }
179
+ async function waitUntil(predicate, timeoutMs = 500) {
180
+ const deadline = Date.now() + timeoutMs;
181
+ while (!predicate() && Date.now() < deadline)
182
+ await new Promise(resolve => setTimeout(resolve, 2));
183
+ assert.ok(predicate(), `condition did not settle within ${timeoutMs}ms`);
184
+ }
185
+ async function settleWithin(promise, timeoutMs = 2500) {
186
+ return Promise.race([
187
+ promise,
188
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`agent did not settle within ${timeoutMs}ms`)), timeoutMs)),
189
+ ]);
190
+ }
191
+ function eventsOf(world) {
192
+ return world.agent.session.snapshotEvents();
193
+ }
194
+ function terminalOf(world) {
195
+ const end = [...eventsOf(world)].reverse().find(event => event.type === 'turn/end');
196
+ assert.ok(end, 'AgentLoop must append a turn/end event');
197
+ const reason = end.data.reason.kind;
198
+ if (reason === 'completed')
199
+ return 'completed';
200
+ if (reason === 'aborted')
201
+ return 'cancelled';
202
+ if (reason === 'error')
203
+ return 'error';
204
+ throw new Error(`unexpected turn-end reason ${reason}`);
205
+ }
206
+ function retryEvents(world) {
207
+ return eventsOf(world).filter(event => event.type === 'llm/retry');
208
+ }
209
+ async function runAgentTurn(world) {
210
+ world.agent.followup(createUserMessage({
211
+ source: { kind: 'user' },
212
+ content: [{ type: 'text', text: 'loopback retry proof' }],
213
+ }));
214
+ await world.agent.whenIdle();
215
+ return terminalOf(world);
216
+ }
217
+ async function finite429Proof() {
218
+ const world = await startWorld([
219
+ { kind: 'failure', code: 'RATE_LIMIT', status: 429 },
220
+ { kind: 'failure', code: 'RATE_LIMIT', status: 429 },
221
+ { kind: 'ok' },
222
+ ], normalPolicy({ maxRetries: 2 }));
223
+ try {
224
+ const terminal = await runAgentTurn(world);
225
+ assert.equal(terminal, 'completed');
226
+ assert.equal(world.fixture.requestCount, 3, '429 then success uses one initial request plus two retry requests');
227
+ assert.equal(retryEvents(world).length, 2);
228
+ assert.equal(eventsOf(world).filter(event => event.type === 'llm/retry-started').length, 2);
229
+ const retry = retryEvents(world)[0].data;
230
+ assert.equal(retry.mode, 'normal');
231
+ assert.equal(retry.maxRetries, 2);
232
+ assert.equal(retry.retry, 1);
233
+ assert.equal(retry.provider, 'fixture');
234
+ assert.equal(retry.failure.code, 'RATE_LIMIT');
235
+ assert.equal(retry.failure.status, 429);
236
+ assert.equal(world.fixture.records[0].method, 'POST');
237
+ assert.ok(world.fixture.records.every(record => record.url === '/v1/chat/completions'));
238
+ console.log(`OK issue-289 host 429 requests=${world.fixture.requestCount} retries=2 terminal=${terminal}`);
239
+ }
240
+ finally {
241
+ await world.close();
242
+ }
243
+ }
244
+ async function timeoutTerminalProof() {
245
+ const world = await startWorld([{ kind: 'failure', code: 'TIMEOUT' }], normalPolicy({ maxRetries: 1 }), 200);
246
+ try {
247
+ const terminal = await runAgentTurn(world);
248
+ assert.equal(terminal, 'error');
249
+ assert.equal(world.fixture.requestCount, 2, 'timeout is retried once and then terminates');
250
+ assert.equal(retryEvents(world).length, 1);
251
+ const beforeStable = world.fixture.requestCount;
252
+ await new Promise(resolve => setTimeout(resolve, 40));
253
+ assert.equal(world.fixture.requestCount, beforeStable, 'terminal timeout remains stable with no new request');
254
+ console.log(`OK issue-289 host timeout requests=${world.fixture.requestCount} retries=1 terminal=${terminal} stable=true`);
255
+ }
256
+ finally {
257
+ await world.close();
258
+ }
259
+ }
260
+ async function serverTerminalProof() {
261
+ const world = await startWorld([{ kind: 'failure', code: 'SERVER', status: 503 }], normalPolicy({ maxRetries: 1 }));
262
+ try {
263
+ const terminal = await runAgentTurn(world);
264
+ assert.equal(terminal, 'error');
265
+ assert.equal(world.fixture.requestCount, 2, 'server failure is retried once and then terminates');
266
+ assert.equal(retryEvents(world).length, 1);
267
+ const beforeStable = world.fixture.requestCount;
268
+ await new Promise(resolve => setTimeout(resolve, 40));
269
+ assert.equal(world.fixture.requestCount, beforeStable, 'terminal server failure remains stable with no new request');
270
+ console.log(`OK issue-289 host server requests=${world.fixture.requestCount} retries=1 terminal=${terminal} stable=true`);
271
+ }
272
+ finally {
273
+ await world.close();
274
+ }
275
+ }
276
+ async function cancelBackoffProof() {
277
+ const world = await startWorld([{ kind: 'failure', code: 'RATE_LIMIT', status: 429 }], normalPolicy({ maxRetries: 5, initialDelayMs: 120, maxDelayMs: 120 }));
278
+ try {
279
+ const run = runAgentTurn(world);
280
+ await waitUntil(() => world.fixture.requestCount === 1 && retryEvents(world).length === 1);
281
+ world.agent.cancel({ kind: 'user' }, { keepInbox: true });
282
+ assert.equal(await settleWithin(run), 'cancelled');
283
+ await new Promise(resolve => setTimeout(resolve, 2000));
284
+ assert.equal(world.fixture.requestCount, 1, 'abort during backoff prevents every follow-up request');
285
+ assert.equal(eventsOf(world).filter(event => event.type === 'llm/retry-started').length, 0);
286
+ console.log(`OK issue-289 host cancel requests=${world.fixture.requestCount} started=0 terminal=cancelled`);
287
+ }
288
+ finally {
289
+ await world.close();
290
+ }
291
+ }
292
+ await finite429Proof();
293
+ await timeoutTerminalProof();
294
+ await serverTerminalProof();
295
+ await cancelBackoffProof();
296
+ console.log('ISSUE #289 MODEL-RETRY REAL FIXTURE: installed AgentLoop/LlmRuntime/adapter HTTP/dsh retry/AbortSignal proof passed');