@esankhan3/anvil-agent-core 0.2.0 → 0.4.0

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 (175) hide show
  1. package/README.md +1 -1
  2. package/dist/__tests__/fetch-pool.integration.test.d.ts +15 -0
  3. package/dist/__tests__/fetch-pool.integration.test.d.ts.map +1 -0
  4. package/dist/__tests__/fetch-pool.integration.test.js +109 -0
  5. package/dist/__tests__/fetch-pool.integration.test.js.map +1 -0
  6. package/dist/__tests__/fetch-pool.test.d.ts +15 -0
  7. package/dist/__tests__/fetch-pool.test.d.ts.map +1 -0
  8. package/dist/__tests__/fetch-pool.test.js +115 -0
  9. package/dist/__tests__/fetch-pool.test.js.map +1 -0
  10. package/dist/__tests__/gemini-adapter-recording.test.d.ts +11 -0
  11. package/dist/__tests__/gemini-adapter-recording.test.d.ts.map +1 -0
  12. package/dist/__tests__/gemini-adapter-recording.test.js +108 -0
  13. package/dist/__tests__/gemini-adapter-recording.test.js.map +1 -0
  14. package/dist/__tests__/legacy-adapter-language-model.test.d.ts +11 -0
  15. package/dist/__tests__/legacy-adapter-language-model.test.d.ts.map +1 -0
  16. package/dist/__tests__/legacy-adapter-language-model.test.js +102 -0
  17. package/dist/__tests__/legacy-adapter-language-model.test.js.map +1 -0
  18. package/dist/__tests__/ollama-adapter-recording.test.d.ts +15 -0
  19. package/dist/__tests__/ollama-adapter-recording.test.d.ts.map +1 -0
  20. package/dist/__tests__/ollama-adapter-recording.test.js +128 -0
  21. package/dist/__tests__/ollama-adapter-recording.test.js.map +1 -0
  22. package/dist/__tests__/openrouter-prefill.integration.test.d.ts +19 -0
  23. package/dist/__tests__/openrouter-prefill.integration.test.d.ts.map +1 -0
  24. package/dist/__tests__/openrouter-prefill.integration.test.js +410 -0
  25. package/dist/__tests__/openrouter-prefill.integration.test.js.map +1 -0
  26. package/dist/__tests__/prefill-truncate.test.d.ts +8 -0
  27. package/dist/__tests__/prefill-truncate.test.d.ts.map +1 -0
  28. package/dist/__tests__/prefill-truncate.test.js +75 -0
  29. package/dist/__tests__/prefill-truncate.test.js.map +1 -0
  30. package/dist/__tests__/router-circuit-breaker.test.js +1 -1
  31. package/dist/__tests__/router-circuit-breaker.test.js.map +1 -1
  32. package/dist/__tests__/router-classify-unified.test.d.ts +12 -0
  33. package/dist/__tests__/router-classify-unified.test.d.ts.map +1 -0
  34. package/dist/__tests__/router-classify-unified.test.js +108 -0
  35. package/dist/__tests__/router-classify-unified.test.js.map +1 -0
  36. package/dist/__tests__/router-fallback.test.js +1 -1
  37. package/dist/__tests__/router-fallback.test.js.map +1 -1
  38. package/dist/__tests__/router-model-registry.test.js +24 -0
  39. package/dist/__tests__/router-model-registry.test.js.map +1 -1
  40. package/dist/__tests__/router-retry.test.js +3 -5
  41. package/dist/__tests__/router-retry.test.js.map +1 -1
  42. package/dist/__tests__/router-runagent.test.d.ts +14 -0
  43. package/dist/__tests__/router-runagent.test.d.ts.map +1 -0
  44. package/dist/__tests__/router-runagent.test.js +95 -0
  45. package/dist/__tests__/router-runagent.test.js.map +1 -0
  46. package/dist/__tests__/router-scaffold.test.js +2 -1
  47. package/dist/__tests__/router-scaffold.test.js.map +1 -1
  48. package/dist/__tests__/router-spend-ledger.test.js +1 -1
  49. package/dist/__tests__/router-spend-ledger.test.js.map +1 -1
  50. package/dist/__tests__/router-stream-fallback.test.d.ts +13 -0
  51. package/dist/__tests__/router-stream-fallback.test.d.ts.map +1 -0
  52. package/dist/__tests__/router-stream-fallback.test.js +129 -0
  53. package/dist/__tests__/router-stream-fallback.test.js.map +1 -0
  54. package/dist/__tests__/router-telemetry.test.js +3 -1
  55. package/dist/__tests__/router-telemetry.test.js.map +1 -1
  56. package/dist/__tests__/util/mock-upstream.d.ts +77 -0
  57. package/dist/__tests__/util/mock-upstream.d.ts.map +1 -0
  58. package/dist/__tests__/util/mock-upstream.js +219 -0
  59. package/dist/__tests__/util/mock-upstream.js.map +1 -0
  60. package/dist/adk-adapter.d.ts.map +1 -1
  61. package/dist/adk-adapter.js +51 -0
  62. package/dist/adk-adapter.js.map +1 -1
  63. package/dist/adk-anthropic-llm.d.ts.map +1 -1
  64. package/dist/adk-anthropic-llm.js +23 -10
  65. package/dist/adk-anthropic-llm.js.map +1 -1
  66. package/dist/agent/session/adapter.d.ts +25 -0
  67. package/dist/agent/session/adapter.d.ts.map +1 -1
  68. package/dist/agent/session/adapter.js +4 -0
  69. package/dist/agent/session/adapter.js.map +1 -1
  70. package/dist/agent/session/index.d.ts +1 -0
  71. package/dist/agent/session/index.d.ts.map +1 -1
  72. package/dist/agent/session/index.js +1 -0
  73. package/dist/agent/session/index.js.map +1 -1
  74. package/dist/agent/session/language-model-bridge.d.ts.map +1 -1
  75. package/dist/agent/session/language-model-bridge.js +30 -2
  76. package/dist/agent/session/language-model-bridge.js.map +1 -1
  77. package/dist/agent/session/legacy-adapter-language-model.d.ts +27 -0
  78. package/dist/agent/session/legacy-adapter-language-model.d.ts.map +1 -0
  79. package/dist/agent/session/legacy-adapter-language-model.js +221 -0
  80. package/dist/agent/session/legacy-adapter-language-model.js.map +1 -0
  81. package/dist/agent/session/types.d.ts +32 -0
  82. package/dist/agent/session/types.d.ts.map +1 -1
  83. package/dist/agent/session/types.js.map +1 -1
  84. package/dist/claude-adapter.d.ts.map +1 -1
  85. package/dist/claude-adapter.js +27 -0
  86. package/dist/claude-adapter.js.map +1 -1
  87. package/dist/fetch-pool.d.ts +47 -0
  88. package/dist/fetch-pool.d.ts.map +1 -0
  89. package/dist/fetch-pool.js +132 -0
  90. package/dist/fetch-pool.js.map +1 -0
  91. package/dist/gemini-adapter.d.ts.map +1 -1
  92. package/dist/gemini-adapter.js +65 -6
  93. package/dist/gemini-adapter.js.map +1 -1
  94. package/dist/index.d.ts +11 -0
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +6 -0
  97. package/dist/index.js.map +1 -1
  98. package/dist/ollama-adapter.d.ts.map +1 -1
  99. package/dist/ollama-adapter.js +145 -15
  100. package/dist/ollama-adapter.js.map +1 -1
  101. package/dist/openrouter-adapter.d.ts.map +1 -1
  102. package/dist/openrouter-adapter.js +296 -15
  103. package/dist/openrouter-adapter.js.map +1 -1
  104. package/dist/prefill/strip.d.ts +49 -0
  105. package/dist/prefill/strip.d.ts.map +1 -0
  106. package/dist/prefill/strip.js +93 -0
  107. package/dist/prefill/strip.js.map +1 -0
  108. package/dist/prefill/translate.d.ts +55 -0
  109. package/dist/prefill/translate.d.ts.map +1 -0
  110. package/dist/prefill/translate.js +162 -0
  111. package/dist/prefill/translate.js.map +1 -0
  112. package/dist/prefill/truncate.d.ts +62 -0
  113. package/dist/prefill/truncate.d.ts.map +1 -0
  114. package/dist/prefill/truncate.js +77 -0
  115. package/dist/prefill/truncate.js.map +1 -0
  116. package/dist/provider-liveness.d.ts.map +1 -1
  117. package/dist/provider-liveness.js +17 -6
  118. package/dist/provider-liveness.js.map +1 -1
  119. package/dist/router/errors.d.ts +24 -7
  120. package/dist/router/errors.d.ts.map +1 -1
  121. package/dist/router/errors.js +71 -14
  122. package/dist/router/errors.js.map +1 -1
  123. package/dist/router/index.d.ts +3 -2
  124. package/dist/router/index.d.ts.map +1 -1
  125. package/dist/router/index.js +2 -1
  126. package/dist/router/index.js.map +1 -1
  127. package/dist/router/local-executor.d.ts.map +1 -1
  128. package/dist/router/local-executor.js +19 -6
  129. package/dist/router/local-executor.js.map +1 -1
  130. package/dist/router/model-registry.d.ts +22 -0
  131. package/dist/router/model-registry.d.ts.map +1 -1
  132. package/dist/router/model-registry.js +64 -1
  133. package/dist/router/model-registry.js.map +1 -1
  134. package/dist/router/provider-registry-resolver.d.ts +25 -0
  135. package/dist/router/provider-registry-resolver.d.ts.map +1 -0
  136. package/dist/router/provider-registry-resolver.js +84 -0
  137. package/dist/router/provider-registry-resolver.js.map +1 -0
  138. package/dist/router/retry.d.ts +3 -0
  139. package/dist/router/retry.d.ts.map +1 -1
  140. package/dist/router/retry.js +4 -1
  141. package/dist/router/retry.js.map +1 -1
  142. package/dist/router/router.d.ts +79 -1
  143. package/dist/router/router.d.ts.map +1 -1
  144. package/dist/router/router.js +256 -2
  145. package/dist/router/router.js.map +1 -1
  146. package/dist/router/types.d.ts +1 -1
  147. package/dist/router/types.d.ts.map +1 -1
  148. package/dist/router/types.js +1 -0
  149. package/dist/router/types.js.map +1 -1
  150. package/dist/single-shot.d.ts.map +1 -1
  151. package/dist/single-shot.js +26 -1
  152. package/dist/single-shot.js.map +1 -1
  153. package/dist/tools/builtin.d.ts +19 -0
  154. package/dist/tools/builtin.d.ts.map +1 -1
  155. package/dist/tools/builtin.js +51 -0
  156. package/dist/tools/builtin.js.map +1 -1
  157. package/dist/turn-recorder/__tests__/turn-recorder.test.d.ts +20 -0
  158. package/dist/turn-recorder/__tests__/turn-recorder.test.d.ts.map +1 -0
  159. package/dist/turn-recorder/__tests__/turn-recorder.test.js +282 -0
  160. package/dist/turn-recorder/__tests__/turn-recorder.test.js.map +1 -0
  161. package/dist/turn-recorder/hash.d.ts +10 -0
  162. package/dist/turn-recorder/hash.d.ts.map +1 -0
  163. package/dist/turn-recorder/hash.js +35 -0
  164. package/dist/turn-recorder/hash.js.map +1 -0
  165. package/dist/turn-recorder/index.d.ts +119 -0
  166. package/dist/turn-recorder/index.d.ts.map +1 -0
  167. package/dist/turn-recorder/index.js +326 -0
  168. package/dist/turn-recorder/index.js.map +1 -0
  169. package/dist/turn-recorder/types.d.ts +227 -0
  170. package/dist/turn-recorder/types.d.ts.map +1 -0
  171. package/dist/turn-recorder/types.js +20 -0
  172. package/dist/turn-recorder/types.js.map +1 -0
  173. package/dist/types.d.ts +35 -2
  174. package/dist/types.d.ts.map +1 -1
  175. package/package.json +4 -3
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Per-provider fetch dispatcher pool.
3
+ *
4
+ * Each upstream provider gets its own bounded-keep-alive `undici.Agent`.
5
+ * Adapters pass `dispatcher: getFetchPool(provider)` to every fetch.
6
+ * On a network-layer failure (TypeError: fetch failed / ECONNRESET / EPIPE),
7
+ * adapters call `recycleFetchPoolOnFailure(provider, err)` so the next
8
+ * fetch to the same provider lands on a fresh socket pool.
9
+ *
10
+ * Why: Node's default global undici dispatcher can retain zombie sockets
11
+ * after OS sleep/wake or VPN flips. One poisoned global pool burns every
12
+ * provider at once. Per-provider pools + heal-on-failure isolate the blast
13
+ * radius and self-recover without process restart.
14
+ */
15
+ import { Agent } from 'undici';
16
+ const POOL_OPTS = {
17
+ keepAliveTimeout: 4_000,
18
+ keepAliveMaxTimeout: 10_000,
19
+ connections: 32,
20
+ pipelining: 1,
21
+ // No dispatcher-level I/O timeouts — every caller already owns its
22
+ // own per-call AbortSignal (run()'s AbortController for agentic
23
+ // adapters; AbortSignal.timeout(...) for short probes). The previous
24
+ // 30s headers / 60s body ceilings were appropriate for short REST
25
+ // calls but hostile to LLM streaming: premium reasoning models can
26
+ // take >30s to first byte and pause >60s between SSE chunks, both
27
+ // of which manifest as `TypeError: fetch failed` that the chain
28
+ // walker can't distinguish from a real network failure.
29
+ // 0 = no timeout; the per-call signal is the single source of truth.
30
+ bodyTimeout: 0,
31
+ headersTimeout: 0,
32
+ };
33
+ const pools = new Map();
34
+ function makeEntry() {
35
+ return {
36
+ agent: new Agent(POOL_OPTS),
37
+ createdAt: new Date().toISOString(),
38
+ recycleCount: 0,
39
+ lastRecycleAt: null,
40
+ lastRecycleReason: null,
41
+ recycling: null,
42
+ };
43
+ }
44
+ /**
45
+ * Get the dispatcher for a provider. Lazily constructs on first call.
46
+ * Always returns a non-recycling Agent — callers pass it via fetch options.
47
+ */
48
+ export function getFetchPool(provider) {
49
+ let entry = pools.get(provider);
50
+ if (!entry) {
51
+ entry = makeEntry();
52
+ pools.set(provider, entry);
53
+ }
54
+ return entry.agent;
55
+ }
56
+ const POISONED_PATTERNS = /fetch\s+failed|ECONNRESET|socket\s+hang\s+up|other\s+side\s+closed|EPIPE|ETIMEDOUT|UND_ERR_SOCKET/i;
57
+ /**
58
+ * Recycle the dispatcher for a provider when a fetch failure indicates pool
59
+ * poisoning. Idempotent under concurrent calls — multiple in-flight failures
60
+ * coalesce to one recycle. The promise resolves once the new pool is ready.
61
+ * Safe to fire-and-forget — the throw site re-throws the original error to
62
+ * feed the chain walker.
63
+ */
64
+ export function recycleFetchPoolOnFailure(provider, err) {
65
+ const msg = err instanceof Error ? err.message : String(err);
66
+ const causeMsg = err instanceof Error && 'cause' in err && err.cause
67
+ ? err.cause instanceof Error
68
+ ? err.cause.message
69
+ : String(err.cause)
70
+ : '';
71
+ const haystack = `${msg} ${causeMsg}`;
72
+ if (!POISONED_PATTERNS.test(haystack))
73
+ return Promise.resolve();
74
+ let entry = pools.get(provider);
75
+ if (!entry) {
76
+ entry = makeEntry();
77
+ pools.set(provider, entry);
78
+ return Promise.resolve();
79
+ }
80
+ if (entry.recycling)
81
+ return entry.recycling;
82
+ const reason = (msg + (causeMsg ? ` :: ${causeMsg}` : '')).slice(0, 200);
83
+ const old = entry.agent;
84
+ const newAgent = new Agent(POOL_OPTS);
85
+ entry.agent = newAgent;
86
+ entry.recycleCount += 1;
87
+ entry.lastRecycleAt = new Date().toISOString();
88
+ entry.lastRecycleReason = reason;
89
+ const recycling = (async () => {
90
+ try {
91
+ await old.close();
92
+ }
93
+ catch {
94
+ // Old pool was already broken — that's why we're recycling.
95
+ }
96
+ })().finally(() => {
97
+ if (entry.recycling === recycling)
98
+ entry.recycling = null;
99
+ });
100
+ entry.recycling = recycling;
101
+ return recycling;
102
+ }
103
+ /**
104
+ * Diagnostic: read-only snapshot of every pool's state.
105
+ */
106
+ export function getPoolMetrics() {
107
+ const out = [];
108
+ for (const [provider, entry] of pools) {
109
+ out.push({
110
+ provider,
111
+ createdAt: entry.createdAt,
112
+ recycleCount: entry.recycleCount,
113
+ lastRecycleAt: entry.lastRecycleAt,
114
+ lastRecycleReason: entry.lastRecycleReason,
115
+ active: entry.recycling === null,
116
+ });
117
+ }
118
+ return out;
119
+ }
120
+ /**
121
+ * Test utility — drops every pool and resets counters. Not exported via
122
+ * the package barrel; tests import from the subpath.
123
+ */
124
+ export async function resetAllPools() {
125
+ const closes = [];
126
+ for (const entry of pools.values()) {
127
+ closes.push(entry.agent.close().catch(() => { }));
128
+ }
129
+ pools.clear();
130
+ await Promise.all(closes);
131
+ }
132
+ //# sourceMappingURL=fetch-pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-pool.js","sourceRoot":"","sources":["../src/fetch-pool.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"gemini-adapter.d.ts","sourceRoot":"","sources":["../src/gemini-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACrB,MAAM,YAAY,CAAC;AA4BpB,qBAAa,aAAc,YAAW,YAAY;IAChD,QAAQ,CAAC,QAAQ,EAAG,QAAQ,CAAU;IAEtC,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAczC;IAEF,OAAO,CAAC,eAAe,CAAgC;IAEvD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIvC,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAInD,iBAAiB,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAQ5F,IAAI,IAAI,IAAI;IAON,GAAG,CACP,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,MAAM,CAAC,cAAc,GAC5B,OAAO,CAAC,kBAAkB,CAAC;CAyJ/B"}
1
+ {"version":3,"file":"gemini-adapter.d.ts","sourceRoot":"","sources":["../src/gemini-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACrB,MAAM,YAAY,CAAC;AA8BpB,qBAAa,aAAc,YAAW,YAAY;IAChD,QAAQ,CAAC,QAAQ,EAAG,QAAQ,CAAU;IAEtC,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAczC;IAEF,OAAO,CAAC,eAAe,CAAgC;IAEvD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIvC,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAInD,iBAAiB,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAQ5F,IAAI,IAAI,IAAI;IAON,GAAG,CACP,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,MAAM,CAAC,cAAc,GAC5B,OAAO,CAAC,kBAAkB,CAAC;CAuN/B"}
@@ -6,6 +6,8 @@
6
6
  */
7
7
  import { emitContent, emitThinking, emitResult } from './stream-format.js';
8
8
  import { UpstreamError } from './upstream-error.js';
9
+ import { getFetchPool, recycleFetchPoolOnFailure } from './fetch-pool.js';
10
+ import { createNullTurnRecorder } from './turn-recorder/index.js';
9
11
  const API_BASE = 'https://generativelanguage.googleapis.com/v1beta/models';
10
12
  const PRICING = {
11
13
  'gemini-2.5-pro': [1.25, 10.0],
@@ -78,6 +80,47 @@ export class GeminiAdapter {
78
80
  if (projectPrompt) {
79
81
  body.systemInstruction = { parts: [{ text: projectPrompt }] };
80
82
  }
83
+ // §H4 cross-vendor turn recording. Records this single-shot exchange so
84
+ // a later (possibly different-provider) resume can reconstruct prior
85
+ // turns via `reconstructSessionHistory`. NullTurnRecorder no-ops when
86
+ // the pipeline didn't inject a durable recorder.
87
+ const recorder = config.turnRecorder ?? createNullTurnRecorder({
88
+ runId: config.sessionId,
89
+ stepId: config.stage,
90
+ });
91
+ const { turn, replayed } = await recorder.startTurn({
92
+ model,
93
+ provider: 'gemini',
94
+ system: projectPrompt,
95
+ messages: body.contents,
96
+ userPrompt,
97
+ });
98
+ // Same-runId replay (Fix A): the turn already ran on a prior process —
99
+ // skip the upstream call and re-present the recorded result verbatim.
100
+ if (replayed) {
101
+ const u = replayed.usage;
102
+ const replayCost = computeCost(model, u.inputTokens, u.outputTokens);
103
+ if (replayed.text)
104
+ emitContent(output, replayed.text);
105
+ emitResult(output, {
106
+ text: replayed.text,
107
+ costUsd: replayCost,
108
+ inputTokens: u.inputTokens,
109
+ outputTokens: u.outputTokens,
110
+ durationMs: 0,
111
+ });
112
+ return {
113
+ output: replayed.text,
114
+ inputTokens: u.inputTokens,
115
+ outputTokens: u.outputTokens,
116
+ costUsd: replayCost,
117
+ durationMs: 0,
118
+ provider: 'gemini',
119
+ model,
120
+ cacheReadTokens: u.cacheReadTokens ?? 0,
121
+ reasoningTokens: 0,
122
+ };
123
+ }
81
124
  this.abortController = new AbortController();
82
125
  const startMs = Date.now();
83
126
  let fullText = '';
@@ -85,12 +128,24 @@ export class GeminiAdapter {
85
128
  let outputTokens = 0;
86
129
  let thinkingTokens = 0;
87
130
  let cacheReadTokens = 0;
88
- const response = await fetch(url, {
89
- method: 'POST',
90
- headers: { 'Content-Type': 'application/json' },
91
- body: JSON.stringify(body),
92
- signal: this.abortController.signal,
93
- });
131
+ let response;
132
+ try {
133
+ response = await fetch(url, {
134
+ method: 'POST',
135
+ headers: { 'Content-Type': 'application/json' },
136
+ body: JSON.stringify(body),
137
+ signal: this.abortController.signal,
138
+ // @ts-expect-error — undici dispatcher accepted by Node fetch at runtime
139
+ dispatcher: getFetchPool('gemini'),
140
+ });
141
+ }
142
+ catch (err) {
143
+ if (this.abortController.signal.aborted)
144
+ throw err;
145
+ void recycleFetchPoolOnFailure('gemini', err);
146
+ const msg = err instanceof Error ? err.message : String(err);
147
+ throw new UpstreamError(0, `fetch failed: ${msg}`, { provider: 'gemini', retryable: true });
148
+ }
94
149
  if (!response.ok) {
95
150
  const errBody = await response.text();
96
151
  throw new UpstreamError(response.status, errBody, { provider: 'gemini' });
@@ -172,6 +227,10 @@ export class GeminiAdapter {
172
227
  this.abortController = null;
173
228
  throw new UpstreamError(503, `gemini model "${model}" returned empty final text (outputTokens=${outputTokens})`, { provider: 'gemini', retryable: true });
174
229
  }
230
+ // §H4 — record the completed turn (assistant-end) so a later resume can
231
+ // reconstruct it. Only on success: an empty/burned turn threw above, so
232
+ // we never persist an empty assistant-end.
233
+ await recorder.endTurn(turn, fullText, 'end_turn', { inputTokens, outputTokens, cacheReadTokens }, { segments: [{ model, provider: 'gemini', range: [0, fullText.length], source: 'live' }] });
175
234
  emitResult(output, {
176
235
  text: fullText,
177
236
  costUsd,
@@ -1 +1 @@
1
- {"version":3,"file":"gemini-adapter.js","sourceRoot":"","sources":["../src/gemini-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gemini-adapter.js","sourceRoot":"","sources":["../src/gemini-adapter.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -40,5 +40,16 @@ export { DEFAULT_SPEC, getModelSpec, getContextWindow, getMaxOutput, } from './m
40
40
  export type { ModelSpec } from './model-catalog.js';
41
41
  export { resolveModelByTier, setDiscoveryResult, invalidateResolverCache, } from './model-tier-resolver.js';
42
42
  export type { ResolverTier, ResolverCapability, ResolverModelWeight, ResolverModel, ResolverDiscoveryResult, } from './model-tier-resolver.js';
43
+ export { getFetchPool, recycleFetchPoolOnFailure, getPoolMetrics, } from './fetch-pool.js';
44
+ export type { ProviderId as FetchPoolProviderId, PoolMetrics as FetchPoolMetrics, } from './fetch-pool.js';
45
+ export { TurnRecorder, createNullTurnRecorder, createNullEffectRuntime, createNullPartialSink, } from './turn-recorder/index.js';
46
+ export type { AssistantPartial, AssistantStartRequest, AssistantTurn, EffectRuntimeLike, EffectInvokeOptions, NeutralToolResult, PartialReason, PartialSink, Prefill, PrefillToolUse, PrefillTurn, Provenance, ProvenanceSegment, RecordedToolUse, TurnRecorderDeps, TurnTokenUsage, } from './turn-recorder/types.js';
47
+ export { contentHashFromArgs } from './turn-recorder/hash.js';
48
+ export { stripForTarget } from './prefill/strip.js';
49
+ export type { StripContext } from './prefill/strip.js';
50
+ export { translateToolResult } from './prefill/translate.js';
51
+ export type { TranslatedToolResult } from './prefill/translate.js';
52
+ export { truncatePrefillForBudget, DEFAULT_MAX_INPUT_TOKENS, DEFAULT_MARGIN_TOKENS, } from './prefill/truncate.js';
53
+ export type { TruncatePrefillArgs } from './prefill/truncate.js';
43
54
  export { VERSION } from './version.js';
44
55
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,uBAAuB,EACvB,2BAA2B,EAC3B,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACb,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAKlC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,uBAAuB,EACvB,2BAA2B,EAC3B,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACb,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAKlC,OAAO,EACL,YAAY,EACZ,yBAAyB,EACzB,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,UAAU,IAAI,mBAAmB,EACjC,WAAW,IAAI,gBAAgB,GAChC,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,OAAO,EACP,cAAc,EACd,WAAW,EACX,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,cAAc,GACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -42,5 +42,11 @@ export { resolveModelByTier, setDiscoveryResult, invalidateResolverCache, } from
42
42
  // NOT re-exported here to avoid colliding with `router/model-registry.ts`'s
43
43
  // `ModelTier` (different vocabulary). Reach the alias via the subpath
44
44
  // import `@esankhan3/anvil-agent-core/model-tier-resolver.js` if needed.
45
+ export { getFetchPool, recycleFetchPoolOnFailure, getPoolMetrics, } from './fetch-pool.js';
46
+ export { TurnRecorder, createNullTurnRecorder, createNullEffectRuntime, createNullPartialSink, } from './turn-recorder/index.js';
47
+ export { contentHashFromArgs } from './turn-recorder/hash.js';
48
+ export { stripForTarget } from './prefill/strip.js';
49
+ export { translateToolResult } from './prefill/translate.js';
50
+ export { truncatePrefillForBudget, DEFAULT_MAX_INPUT_TOKENS, DEFAULT_MARGIN_TOKENS, } from './prefill/truncate.js';
45
51
  export { VERSION } from './version.js';
46
52
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,uBAAuB,EACvB,2BAA2B,EAC3B,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,YAAY,GACb,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAQlC,qEAAqE;AACrE,4EAA4E;AAC5E,sEAAsE;AACtE,yEAAyE;AACzE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,uBAAuB,EACvB,2BAA2B,EAC3B,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,YAAY,GACb,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAQlC,qEAAqE;AACrE,4EAA4E;AAC5E,sEAAsE;AACtE,yEAAyE;AACzE,OAAO,EACL,YAAY,EACZ,yBAAyB,EACzB,cAAc,GACf,MAAM,iBAAiB,CAAC;AAKzB,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,0BAA0B,CAAC;AAmBlC,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ollama-adapter.d.ts","sourceRoot":"","sources":["../src/ollama-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EAIrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,aAAa,EAAiB,MAAM,4BAA4B,CAAC;AAmB1E;;;wBAGwB;AACxB,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;gBACnB,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;CAUjE;AAuBD,UAAU,cAAc;IACtB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;KAC7C,CAAC;CACH;AAWD,UAAU,WAAW;IACnB,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAMD,qBAAa,aAAc,YAAW,YAAY;IA+BpC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IA9BrC,QAAQ,CAAC,QAAQ,EAAG,QAAQ,CAAU;IAEtC,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAUzC;IAEF;;;;;;;;OAQG;IACH,OAAO,CAAC,iBAAiB,CAA8B;IAEvD;;;;OAIG;gBAC0B,QAAQ,GAAE,aAA6B;IAEpE,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAKxC,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAIpD,iBAAiB,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAY5F,IAAI,IAAI,IAAI;IAON,GAAG,CACP,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,MAAM,CAAC,cAAc,GAC5B,OAAO,CAAC,kBAAkB,CAAC;YAOhB,QAAQ;IAuHtB;;;;OAIG;YACW,UAAU;CAuGzB;AAwED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,WAAW,EAAE,EACvB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,IAAI,CAgBN"}
1
+ {"version":3,"file":"ollama-adapter.d.ts","sourceRoot":"","sources":["../src/ollama-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EAIrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,aAAa,EAAiB,MAAM,4BAA4B,CAAC;AAuB1E;;;wBAGwB;AACxB,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;gBACnB,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;CAUjE;AAuBD,UAAU,cAAc;IACtB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;KAC7C,CAAC;CACH;AAWD,UAAU,WAAW;IACnB,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAMD,qBAAa,aAAc,YAAW,YAAY;IA+BpC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IA9BrC,QAAQ,CAAC,QAAQ,EAAG,QAAQ,CAAU;IAEtC,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAUzC;IAEF;;;;;;;;OAQG;IACH,OAAO,CAAC,iBAAiB,CAA8B;IAEvD;;;;OAIG;gBAC0B,QAAQ,GAAE,aAA6B;IAEpE,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAKxC,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAIpD,iBAAiB,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAmB5F,IAAI,IAAI,IAAI;IAON,GAAG,CACP,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,MAAM,CAAC,cAAc,GAC5B,OAAO,CAAC,kBAAkB,CAAC;YAOhB,QAAQ;IA6PtB;;;;OAIG;YACW,UAAU;CAiHzB;AAwED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,WAAW,EAAE,EACvB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,IAAI,CAgBN"}
@@ -25,6 +25,9 @@ import { randomUUID } from 'node:crypto';
25
25
  import { emitContent, emitResult, emitToolResult, emitToolUse } from './stream-format.js';
26
26
  import { localExecutor } from './router/local-executor.js';
27
27
  import { UpstreamError } from './upstream-error.js';
28
+ import { getFetchPool, recycleFetchPoolOnFailure } from './fetch-pool.js';
29
+ import { createNullTurnRecorder } from './turn-recorder/index.js';
30
+ import { contentHashFromArgs } from './turn-recorder/hash.js';
28
31
  const DEFAULT_MAX_ITERATIONS = 32;
29
32
  const DEFAULT_CONTEXT_WINDOW = 16_384;
30
33
  /**
@@ -105,12 +108,19 @@ export class OllamaAdapter {
105
108
  async checkAvailability() {
106
109
  const baseUrl = getBaseUrl();
107
110
  try {
108
- const res = await fetch(`${baseUrl}/api/tags`, { method: 'GET' });
111
+ const res = await fetch(`${baseUrl}/api/tags`, {
112
+ method: 'GET',
113
+ // @ts-expect-error — undici dispatcher accepted by Node fetch at runtime
114
+ dispatcher: getFetchPool('ollama'),
115
+ });
109
116
  if (res.ok)
110
117
  return { available: true };
111
118
  return { available: false, error: `Ollama returned ${res.status}` };
112
119
  }
113
120
  catch (err) {
121
+ // Probe failures are tolerated, but still recycle the pool so the
122
+ // next real request lands on fresh sockets.
123
+ void recycleFetchPoolOnFailure('ollama', err);
114
124
  const msg = err instanceof Error ? err.message : String(err);
115
125
  return { available: false, error: `Cannot reach Ollama at ${baseUrl}: ${msg}` };
116
126
  }
@@ -146,6 +156,22 @@ export class OllamaAdapter {
146
156
  let totalOut = 0;
147
157
  let totalDurMs = 0;
148
158
  let stopReason;
159
+ // §H4 cross-vendor turn recording — mirrors the OpenRouter agentic port
160
+ // (startTurn → replay-skip → live tool-loop with runTool → endTurn, plus a
161
+ // burned-sentinel on a mid-turn upstream burn). Records every turn so a
162
+ // later resume can reconstruct history; HONORS `replayed` on a same-runId
163
+ // resume so the recorded tool effects re-issue in order (exec never fires)
164
+ // instead of re-running live and tripping a DeterminismViolation. NullTurn-
165
+ // Recorder no-ops without a durable recorder.
166
+ const recorder = config.turnRecorder ?? createNullTurnRecorder({
167
+ runId: config.sessionId,
168
+ stepId: config.stage,
169
+ });
170
+ let activeTurnIdx = -1;
171
+ // false while runOneTurn is streaming → a throw here is a mid-stream burn
172
+ // (record a sentinel). true once it returns → a later throw (endTurn /
173
+ // determinism) must propagate untouched, never get a spurious sentinel.
174
+ let activeTurnComplete = true;
149
175
  try {
150
176
  for (let iter = 0; iter < maxIter; iter++) {
151
177
  // Bound conversation history before each turn. Drops oldest
@@ -153,22 +179,90 @@ export class OllamaAdapter {
153
179
  // them once) keeping the system + user + recent turns intact.
154
180
  // If trimming can't bring history below num_ctx, escalate.
155
181
  trimHistoryIfNeeded(messages, numCtx, config.model);
182
+ const { turn: turnIdx, replayed } = await recorder.startTurn({
183
+ model: config.model,
184
+ provider: 'ollama',
185
+ system: config.projectPrompt,
186
+ messages: messages.slice(),
187
+ userPrompt: config.userPrompt,
188
+ });
189
+ activeTurnIdx = turnIdx;
190
+ // ── H3 replay-skip ────────────────────────────────────────────
191
+ // This turn's assistant-end is already in the durable log. Skip the
192
+ // upstream call; re-append the EXACT recorded native history (so the
193
+ // first live turn past the replay frontier continues from coherent
194
+ // context) and re-issue runTool in order so the replay cursor advances
195
+ // (exec never fires — recorded tool_results return verbatim).
196
+ if (replayed) {
197
+ // Burned sentinel: this turn burned mid-stream live. Re-issue its
198
+ // recorded sub-effects (none for ollama — burns happen before any
199
+ // runTool), re-record the sentinel, then re-throw a retryable
200
+ // upstream error so the chain walker re-derives the SAME model→turn
201
+ // mapping it had live, independent of whether the error cleared.
202
+ if (replayed.stopReason === 'burned') {
203
+ for (const m of replayed.historyDelta)
204
+ messages.push(m);
205
+ for (const tu of replayed.toolUses) {
206
+ await recorder.runTool(turnIdx, tu.name, tu.arguments, tu.idempotencyKey, async () => { throw new Error(`replay invariant: exec ran for recorded burned-turn tool ${tu.name}`); });
207
+ }
208
+ await recorder.endTurn(turnIdx, replayed.text, 'burned', replayed.usage, replayed.provenance, replayed.historyDelta);
209
+ throw new UpstreamError(503, `ollama replayed burn for "${config.model}" (deterministic chain re-derivation)`, { provider: 'ollama', retryable: true });
210
+ }
211
+ aggregatedText += replayed.text;
212
+ totalIn += replayed.usage.inputTokens;
213
+ totalOut += replayed.usage.outputTokens;
214
+ if (replayed.text)
215
+ emitContent(output, replayed.text);
216
+ if (replayed.toolUses.length === 0 || !config.toolExecutor) {
217
+ stopReason = replayed.stopReason;
218
+ await recorder.endTurn(turnIdx, replayed.text, replayed.stopReason, replayed.usage, replayed.provenance, replayed.historyDelta);
219
+ break;
220
+ }
221
+ for (const m of replayed.historyDelta)
222
+ messages.push(m);
223
+ for (const tu of replayed.toolUses) {
224
+ emitToolUse(output, tu.name, tu.arguments, tu.id);
225
+ const r = await recorder.runTool(turnIdx, tu.name, tu.arguments, tu.idempotencyKey, async () => { throw new Error(`replay invariant: exec ran for recorded tool ${tu.name}`); });
226
+ const rc = typeof r.content === 'string' ? r.content : JSON.stringify(r.content);
227
+ emitToolResult(output, { toolUseId: tu.id, content: rc, isError: !r.ok });
228
+ }
229
+ await recorder.endTurn(turnIdx, replayed.text, replayed.stopReason, replayed.usage, replayed.provenance, replayed.historyDelta);
230
+ if (abortController.signal.aborted) {
231
+ stopReason = 'aborted';
232
+ break;
233
+ }
234
+ continue;
235
+ }
236
+ // ── live ──────────────────────────────────────────────────────
237
+ activeTurnComplete = false;
156
238
  const turn = await this.runOneTurn(messages, tools, numCtx, config, output, abortController.signal);
239
+ activeTurnComplete = true;
157
240
  aggregatedText += turn.text;
158
241
  totalIn += turn.inputTokens;
159
242
  totalOut += turn.outputTokens;
160
243
  totalDurMs += turn.durationMs;
244
+ const usage = { inputTokens: turn.inputTokens, outputTokens: turn.outputTokens };
245
+ const provenance = {
246
+ segments: [{ model: config.model, provider: 'ollama', range: [0, turn.text.length], source: 'live' }],
247
+ };
161
248
  if (turn.toolCalls.length === 0 || !config.toolExecutor) {
162
249
  stopReason = turn.toolCalls.length === 0 ? 'end_turn' : 'tools_unsupported';
250
+ await recorder.endTurn(turnIdx, turn.text, stopReason, usage, provenance);
163
251
  break;
164
252
  }
165
253
  // Append the assistant's tool-call turn to history so the model
166
- // sees its own request when we re-prompt with results.
167
- messages.push({
254
+ // sees its own request when we re-prompt with results. Track the
255
+ // exact native messages appended this turn as `historyDelta` so
256
+ // crash-resume re-appends them verbatim (order: assistant, then each
257
+ // tool-result — mirroring the live push order).
258
+ const historyDelta = [];
259
+ const assistantMsg = {
168
260
  role: 'assistant',
169
261
  content: turn.text,
170
262
  tool_calls: turn.toolCalls,
171
- });
263
+ };
264
+ messages.push(assistantMsg);
265
+ historyDelta.push(assistantMsg);
172
266
  // Execute every tool call. Sequential — parallel exec via the
173
267
  // builtin executor is safe today (each tool is independent),
174
268
  // but bash/edit ordering matters when the model chains them,
@@ -178,14 +272,24 @@ export class OllamaAdapter {
178
272
  const args = normalizeArgs(toolCall.function.arguments);
179
273
  const call = { id: callId, name: toolCall.function.name, arguments: args };
180
274
  emitToolUse(output, call.name, args, callId);
181
- const result = await invokeTool(config.toolExecutor, call, config.workingDir, abortController.signal);
182
- emitToolResult(output, { toolUseId: callId, content: result.content, isError: result.isError });
183
- messages.push({
275
+ const idempotencyKey = contentHashFromArgs({ name: call.name, arguments: args });
276
+ const neutralResult = await recorder.runTool(turnIdx, call.name, args, idempotencyKey, async () => {
277
+ const r = await invokeTool(config.toolExecutor, call, config.workingDir, abortController.signal);
278
+ return { toolUseId: callId, toolName: call.name, ok: !r.isError, content: r.content };
279
+ });
280
+ const resultContent = typeof neutralResult.content === 'string'
281
+ ? neutralResult.content
282
+ : JSON.stringify(neutralResult.content);
283
+ emitToolResult(output, { toolUseId: callId, content: resultContent, isError: !neutralResult.ok });
284
+ const toolMsg = {
184
285
  role: 'tool',
185
- content: result.content,
286
+ content: resultContent,
186
287
  tool_call_id: callId,
187
- });
288
+ };
289
+ messages.push(toolMsg);
290
+ historyDelta.push(toolMsg);
188
291
  }
292
+ await recorder.endTurn(turnIdx, turn.text, 'tool_use', usage, provenance, historyDelta);
189
293
  if (abortController.signal.aborted) {
190
294
  stopReason = 'aborted';
191
295
  break;
@@ -194,6 +298,20 @@ export class OllamaAdapter {
194
298
  if (stopReason === undefined)
195
299
  stopReason = 'iteration_limit';
196
300
  }
301
+ catch (err) {
302
+ // §H3 burn sentinel: a mid-stream upstream burn (runOneTurn threw before
303
+ // returning). Record a `stopReason:'burned'` assistant-end for the
304
+ // active turn so a same-runId resume replays it deterministically
305
+ // (re-throws here) and the chain walker re-derives the identical
306
+ // model→turn mapping. ollama records no tool effects until runOneTurn
307
+ // returns, so the burned turn carries empty historyDelta + toolUses.
308
+ // Only genuine mid-stream burns (NOT aborts, NOT post-turn throws like a
309
+ // DeterminismViolation) get a sentinel.
310
+ if (activeTurnIdx >= 0 && !activeTurnComplete && !abortController.signal.aborted) {
311
+ await recorder.endTurn(activeTurnIdx, '', 'burned', { inputTokens: 0, outputTokens: 0 }, { segments: [] }, []);
312
+ }
313
+ throw err;
314
+ }
197
315
  finally {
198
316
  this.activeControllers.delete(abortController);
199
317
  }
@@ -247,12 +365,24 @@ export class OllamaAdapter {
247
365
  if (typeof config.maxOutputTokens === 'number' && config.maxOutputTokens > 0) {
248
366
  body.options.num_predict = config.maxOutputTokens;
249
367
  }
250
- const response = await fetch(`${baseUrl}/api/chat`, {
251
- method: 'POST',
252
- headers: { 'Content-Type': 'application/json' },
253
- body: JSON.stringify(body),
254
- signal,
255
- });
368
+ let response;
369
+ try {
370
+ response = await fetch(`${baseUrl}/api/chat`, {
371
+ method: 'POST',
372
+ headers: { 'Content-Type': 'application/json' },
373
+ body: JSON.stringify(body),
374
+ signal,
375
+ // @ts-expect-error — undici dispatcher accepted by Node fetch at runtime
376
+ dispatcher: getFetchPool('ollama'),
377
+ });
378
+ }
379
+ catch (err) {
380
+ if (signal?.aborted)
381
+ throw err;
382
+ void recycleFetchPoolOnFailure('ollama', err);
383
+ const msg = err instanceof Error ? err.message : String(err);
384
+ throw new UpstreamError(0, `fetch failed: ${msg}`, { provider: 'ollama', retryable: true });
385
+ }
256
386
  if (!response.ok) {
257
387
  const errBody = await response.text();
258
388
  throw new UpstreamError(response.status, errBody, { provider: 'ollama' });