@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.
- package/README.md +102 -194
- package/README.zh-CN.md +115 -210
- package/bin/gotry-backend.js +53 -0
- package/bin/gotry-bootstrap.js +466 -35
- package/bin/gotry-inner.js +255 -36
- package/bin/gotry-process-liveness.d.ts +27 -0
- package/bin/gotry-process-liveness.js +97 -0
- package/bin/gotry-runtime-resolution.d.ts +1 -1
- package/bin/gotry-runtime-resolution.js +2 -2
- package/client/client.js +174 -0
- package/cordis.gotry-patch.yml +70 -93
- package/dist/capabilities/agent-reach-deep.js +45 -122
- package/dist/capabilities/agent-reach.js +63 -148
- package/dist/capabilities/anything.js +89 -75
- package/dist/capabilities/artifacts.js +123 -104
- package/dist/capabilities/channel-health.js +65 -45
- package/dist/capabilities/channel-registry.js +108 -87
- package/dist/capabilities/doctor.js +357 -235
- package/dist/capabilities/effect.js +223 -309
- package/dist/capabilities/fact-log.js +28 -14
- package/dist/capabilities/flyai.js +245 -156
- package/dist/capabilities/ground-transfer.js +509 -0
- package/dist/capabilities/hbcli.js +144 -148
- package/dist/capabilities/hotelbyte-transaction.js +194 -0
- package/dist/capabilities/incident-log.js +131 -61
- package/dist/capabilities/model-override.js +30 -10
- package/dist/capabilities/opensky.js +54 -43
- package/dist/capabilities/resilience.js +50 -32
- package/dist/capabilities/session/action-cache.js +50 -44
- package/dist/capabilities/session/adapters/ctrip-flight.js +119 -62
- package/dist/capabilities/session/adapters/ctrip-hotel.js +125 -110
- package/dist/capabilities/session/adapters/dida-portal.js +206 -0
- package/dist/capabilities/session/adapters/meituan-local.js +35 -54
- package/dist/capabilities/session/adapters/rail-12306.js +169 -112
- package/dist/capabilities/session/benchmark.js +85 -149
- package/dist/capabilities/session/extension-bridge.js +320 -259
- package/dist/capabilities/session/extension-channel.js +57 -76
- package/dist/capabilities/session/extension-distribution.js +115 -119
- package/dist/capabilities/session/extract.js +31 -21
- package/dist/capabilities/session/golden-score.js +55 -33
- package/dist/capabilities/session/health-watch.js +146 -154
- package/dist/capabilities/session/read-guard.js +55 -47
- package/dist/capabilities/session/static-flight-golden.js +49 -36
- package/dist/capabilities/session/transport.js +54 -61
- package/dist/capabilities/session/wizard.js +63 -93
- package/dist/capabilities/session-consent.js +143 -47
- package/dist/capabilities/session-login.js +95 -126
- package/dist/capabilities/session-search.js +391 -253
- package/dist/capabilities/sponsor-plugin.js +341 -0
- package/dist/capabilities/visa-policy.js +87 -73
- package/dist/capabilities/weather.js +163 -235
- package/dist/scripts/action-cache-tests.js +24 -59
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +91 -142
- package/dist/scripts/agent-planning-turn-deadline-tests.js +80 -176
- package/dist/scripts/agent-reach-deep-tests.js +30 -40
- package/dist/scripts/agent-reach-tests.js +26 -18
- package/dist/scripts/agent-reach-wrapper-tests.js +46 -50
- package/dist/scripts/anything-tests.js +58 -62
- package/dist/scripts/artifact-client-contract-tests.js +69 -0
- package/dist/scripts/artifacts-capability-tests.js +235 -0
- package/dist/scripts/async-collect.js +26 -15
- package/dist/scripts/benchmark-environment-bridge-e2e.js +287 -684
- package/dist/scripts/benchmark-environment-bridge-tests.js +818 -3123
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +66 -279
- package/dist/scripts/booking-copilot-availability-policy-tests.js +153 -1257
- package/dist/scripts/booking-copilot-bin-proof-tests.js +22 -45
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +64 -76
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +69 -199
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +393 -759
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +33 -75
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +60 -110
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +28 -66
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +72 -271
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +73 -275
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +784 -3398
- package/dist/scripts/booking-copilot-server-proof-tests.js +63 -168
- package/dist/scripts/booking-copilot-startup-proof-tests.js +38 -124
- 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 +61 -123
- package/dist/scripts/booking-surface-contract-proof-tests.js +152 -1167
- package/dist/scripts/bootstrap-tests.js +1383 -128
- package/dist/scripts/build-changelog.js +89 -130
- package/dist/scripts/build-metrics-report.js +135 -143
- package/dist/scripts/changelog-tests.js +35 -82
- package/dist/scripts/channel-probe-tests.js +44 -116
- package/dist/scripts/channel-probe.js +84 -167
- package/dist/scripts/channel-registry-tests.js +97 -185
- package/dist/scripts/companion-tests.js +25 -70
- package/dist/scripts/dida-cookie-probe.js +87 -0
- package/dist/scripts/dida-sw-probe.js +114 -0
- package/dist/scripts/diff-test.js +17 -12
- package/dist/scripts/doctor-tests.js +428 -120
- package/dist/scripts/dsh-artifact-e2e.js +150 -0
- package/dist/scripts/dsh-artifact-web-e2e.js +443 -0
- package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
- package/dist/scripts/dsh-runtime-closure-tests.js +110 -153
- package/dist/scripts/dsh-runtime-closure.js +75 -63
- package/dist/scripts/dsh-session-v3-migration-proof.js +106 -0
- package/dist/scripts/dsh-subprocess-local-proof.js +149 -0
- package/dist/scripts/dsh-target-closure-proof.js +178 -0
- package/dist/scripts/effect-tests.js +165 -552
- package/dist/scripts/engine-run.js +1 -3
- package/dist/scripts/engine-tests.js +16 -10
- package/dist/scripts/evaluation-cadence-tests.js +69 -242
- package/dist/scripts/evaluation-contract-tests.js +193 -499
- package/dist/scripts/extension-distribution-cli.js +19 -15
- package/dist/scripts/extension-distribution-tests.js +98 -211
- package/dist/scripts/extension-tests.js +366 -389
- package/dist/scripts/fact-gate-tests.js +725 -263
- 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-tests.js +325 -68
- package/dist/scripts/g5-guard-tests.js +131 -0
- package/dist/scripts/g5-guard.js +189 -0
- package/dist/scripts/gen-lunar-anchors.js +106 -0
- package/dist/scripts/gotry-backend-kernel-tests.js +79 -0
- package/dist/scripts/ground-transfer-tests.js +956 -0
- package/dist/scripts/hbcli-e2e-tests.js +71 -90
- 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 +41 -115
- package/dist/scripts/health-watch-cli.js +42 -28
- 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 +16 -9
- 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 +547 -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/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/ledger-repair-apply-tests.js +316 -0
- package/dist/scripts/ledger-repair-plan-tests.js +375 -0
- package/dist/scripts/ledger-tests.js +325 -340
- package/dist/scripts/ledger-workflow-crash.js +10 -20
- package/dist/scripts/map-tools-vendor-package-proof.js +206 -134
- 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 +474 -222
- package/dist/scripts/memory-value-tests.js +328 -0
- package/dist/scripts/metrics-report-tests.js +62 -199
- package/dist/scripts/model-override-e2e.js +66 -105
- package/dist/scripts/nightly-evidence-tests.js +64 -89
- package/dist/scripts/nightly-evidence.js +108 -111
- package/dist/scripts/nudge-digest.js +39 -28
- package/dist/scripts/onboarding-tests.js +63 -88
- package/dist/scripts/opensky-check.js +18 -13
- package/dist/scripts/opensky-tests.js +28 -28
- package/dist/scripts/persona-surface-guard-tests.js +37 -12
- package/dist/scripts/pnpm-dsh-closure-proof.js +2 -5
- package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
- package/dist/scripts/price-drift-tests.js +126 -227
- package/dist/scripts/price-drift-watch.js +209 -253
- package/dist/scripts/probe-poi-tests.js +52 -45
- package/dist/scripts/product-metrics.js +187 -273
- package/dist/scripts/publish-preverify.js +47 -41
- package/dist/scripts/realtime-pricing-tests.js +37 -76
- package/dist/scripts/replay-async.js +20 -18
- package/dist/scripts/replay-real.js +16 -18
- package/dist/scripts/replay.js +52 -82
- package/dist/scripts/session-attach-diagnose.js +16 -12
- package/dist/scripts/session-attach-poc.js +34 -37
- package/dist/scripts/session-benchmark.js +66 -147
- package/dist/scripts/session-dida-live-e2e.js +151 -0
- package/dist/scripts/session-extract-tests.js +14 -35
- package/dist/scripts/session-login.js +14 -13
- package/dist/scripts/session-tests.js +545 -583
- package/dist/scripts/sf-live-benchmark.js +92 -110
- package/dist/scripts/sf-live-cli-tests.js +2 -9
- package/dist/scripts/sf-soft-score-tests.js +19 -60
- package/dist/scripts/sf-summary-tests.js +149 -0
- package/dist/scripts/sf-summary.js +466 -84
- package/dist/scripts/skeleton-check.js +16 -14
- package/dist/scripts/skeleton-integration-test.js +11 -10
- package/dist/scripts/skills-contract-tests.js +51 -46
- package/dist/scripts/smoke-session-gate-tests.js +12 -20
- package/dist/scripts/smoke.js +541 -422
- package/dist/scripts/sponsor-reuse-tests.js +262 -0
- package/dist/scripts/state-cli-tests.js +226 -101
- package/dist/scripts/state-cli.js +549 -212
- package/dist/scripts/static-golden-tests.js +46 -180
- package/dist/scripts/time-eval-tests.js +210 -192
- package/dist/scripts/travel-timeline-tests.js +29 -93
- package/dist/scripts/turn-handoff-collect-tests.js +46 -93
- package/dist/scripts/turn-handoff-collect.js +71 -99
- package/dist/scripts/turn-policy-tests.js +28 -29
- package/dist/scripts/typed-contract-canary.js +77 -162
- package/dist/scripts/tz-resolver-sanity.js +50 -0
- package/dist/scripts/tz-resolver-tests.js +584 -0
- package/dist/scripts/unified-tests.js +23 -19
- package/dist/scripts/visa-policy-tests.js +31 -44
- package/dist/scripts/weather-tests.js +121 -543
- package/dist/scripts/wish-channel-gate-tests.js +29 -86
- 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 +18 -12
- package/dist/src/artifact-gate.js +652 -221
- package/dist/src/backend/kernel.js +91 -0
- package/dist/src/backend/modules/booking-copilot.js +55 -0
- package/dist/src/backend/modules/booking-executor.js +124 -0
- package/dist/src/backend/modules/session-search.js +194 -0
- package/dist/src/benchmark-agent-conformance.js +296 -262
- package/dist/src/benchmark-environment-bridge.js +298 -413
- package/dist/src/benchmark-headless-child-diagnostics.js +93 -100
- package/dist/src/benchmark-tool-isolation.js +94 -67
- package/dist/src/bookable-facts.js +299 -174
- package/dist/src/booking-saga.js +64 -120
- package/dist/src/booking-surface/availability-policy.js +443 -576
- package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
- package/dist/src/booking-surface/contracts.js +24 -80
- package/dist/src/booking-surface/dsh-planner.js +510 -281
- package/dist/src/booking-surface/dsh-plugin.js +17 -1
- package/dist/src/booking-surface/error-codes.js +82 -10
- package/dist/src/booking-surface/index.js +0 -3
- package/dist/src/booking-surface/profile.js +10 -54
- package/dist/src/booking-surface/recovery-chain.js +367 -0
- package/dist/src/booking-surface/runtime.js +765 -891
- package/dist/src/booking-surface/server.js +200 -251
- package/dist/src/booking-surface/startup.js +58 -54
- package/dist/src/booking-surface/validation.js +150 -218
- package/dist/src/bridge.js +20 -13
- package/dist/src/companions.js +48 -74
- package/dist/src/contracts.js +21 -43
- package/dist/src/dsh-llm.js +82 -120
- package/dist/src/engine.js +253 -239
- package/dist/src/evaluation-cadence.js +72 -209
- package/dist/src/evaluation-contracts.js +220 -843
- package/dist/src/flight-pack-adapter.js +54 -0
- package/dist/src/flight-pack-contract.js +2 -0
- package/dist/src/gotry-backend.js +70 -0
- package/dist/src/hotel-date-gate.js +118 -0
- package/dist/src/i18n.js +18 -7
- package/dist/src/index.js +1425 -1629
- package/dist/src/journey.js +132 -81
- package/dist/src/ledger-repair-apply.js +538 -0
- package/dist/src/ledger-repair-plan.js +463 -0
- package/dist/src/loop.js +257 -166
- package/dist/src/memory-capture.js +180 -32
- package/dist/src/memory-decay.js +30 -14
- package/dist/src/memory-lifecycle.js +1310 -0
- package/dist/src/memory-utility.js +27 -40
- package/dist/src/mock-llm.js +36 -61
- package/dist/src/model.js +81 -36
- package/dist/src/realtime-pricing.js +63 -72
- package/dist/src/slot-spec.js +84 -106
- package/dist/src/state-ledger.js +310 -471
- 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 +121 -127
- package/dist/src/turn-policy.js +61 -89
- package/dist/src/tz-resolver.js +143 -0
- package/dist/src/unified.js +504 -381
- package/dist/src/wish-pool.js +39 -14
- package/dist/src/write-gate.js +495 -0
- package/dist/src/z3-shared.js +273 -7
- package/extension/README.md +4 -3
- package/extension/background.js +125 -14
- package/extension/content-bridge.js +23 -1
- package/extension/content-main.js +15 -4
- package/extension/manifest.json +16 -8
- package/package.json +251 -220
- package/ts/capabilities/agent-reach.ts +4 -4
- package/ts/capabilities/anything.ts +1 -1
- package/ts/capabilities/artifacts.ts +46 -9
- package/ts/capabilities/flyai.ts +156 -37
- package/ts/capabilities/hbcli.ts +1 -1
- package/ts/capabilities/incident-log.ts +88 -59
- package/ts/capabilities/session/adapters/ctrip-flight.ts +99 -36
- package/ts/capabilities/session/adapters/dida-portal.ts +250 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +123 -24
- package/ts/capabilities/session/extension-bridge.ts +226 -101
- package/ts/capabilities/session/extension-channel.ts +22 -9
- package/ts/capabilities/session/wizard.ts +2 -2
- package/ts/capabilities/session-consent.ts +98 -14
- package/ts/capabilities/session-login.ts +1 -0
- package/ts/capabilities/session-search.ts +315 -31
- package/ts/dsh-runtime/vendor/README.md +8 -4
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +7 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +5 -3
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +6 -6
- package/ts/package.json +236 -16
- package/ts/scripts/map-tools-vendor-package-proof.ts +152 -31
- package/ts/scripts/state-cli.ts +418 -51
- package/ts/src/artifact-gate.ts +561 -15
- package/ts/src/benchmark-environment-bridge.ts +37 -20
- package/ts/src/bookable-facts.ts +183 -7
- package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
- package/ts/src/booking-surface/dsh-planner.ts +240 -32
- package/ts/src/booking-surface/dsh-plugin.js +17 -1
- package/ts/src/booking-surface/error-codes.ts +75 -0
- package/ts/src/booking-surface/recovery-chain.ts +409 -0
- package/ts/src/booking-surface/server.ts +84 -51
- package/ts/src/booking-surface/validation.ts +2 -0
- package/ts/src/contracts.ts +12 -2
- package/ts/src/dsh-llm.ts +4 -8
- package/ts/src/index.ts +609 -90
- package/ts/src/loop.ts +116 -7
- package/ts/src/memory-capture.ts +227 -3
- package/ts/src/mock-llm.ts +4 -8
- package/ts/src/model.ts +88 -2
- package/ts/src/state-ledger.ts +47 -29
- package/ts/src/subagent-job-id-guard.ts +72 -0
- package/ts/src/time-anchor.ts +217 -13
- 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');
|