@rynfar/meridian 1.67.0 → 1.69.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 (63) hide show
  1. package/dist/{cli-35jx6vmy.js → cli-x1sv3bqf.js} +2445 -1070
  2. package/dist/{cli-n8t34zmq.js → cli-zdbv40d5.js} +45 -10
  3. package/dist/cli.js +7 -7
  4. package/dist/meridian/index.js +252 -0
  5. package/dist/meridian/package.json +6 -0
  6. package/dist/meridian-v2/index.js +334 -0
  7. package/dist/meridian-v2/package.json +6 -0
  8. package/dist/proxy/adapter.d.ts +35 -0
  9. package/dist/proxy/adapter.d.ts.map +1 -1
  10. package/dist/proxy/adapters/claudecode.d.ts +23 -0
  11. package/dist/proxy/adapters/claudecode.d.ts.map +1 -1
  12. package/dist/proxy/adapters/opencode.d.ts.map +1 -1
  13. package/dist/proxy/adapters/passthrough.d.ts.map +1 -1
  14. package/dist/proxy/adapters/pi.d.ts.map +1 -1
  15. package/dist/proxy/errors.d.ts +2 -0
  16. package/dist/proxy/errors.d.ts.map +1 -1
  17. package/dist/proxy/idleStallCeiling.d.ts +74 -0
  18. package/dist/proxy/idleStallCeiling.d.ts.map +1 -0
  19. package/dist/proxy/messages.d.ts +7 -4
  20. package/dist/proxy/messages.d.ts.map +1 -1
  21. package/dist/proxy/openai.d.ts +12 -0
  22. package/dist/proxy/openai.d.ts.map +1 -1
  23. package/dist/proxy/openaiResponses.d.ts +106 -3
  24. package/dist/proxy/openaiResponses.d.ts.map +1 -1
  25. package/dist/proxy/passthroughEarlyStop.d.ts +20 -5
  26. package/dist/proxy/passthroughEarlyStop.d.ts.map +1 -1
  27. package/dist/proxy/passthroughTools.d.ts +82 -3
  28. package/dist/proxy/passthroughTools.d.ts.map +1 -1
  29. package/dist/proxy/query.d.ts +43 -10
  30. package/dist/proxy/query.d.ts.map +1 -1
  31. package/dist/proxy/replay.d.ts +24 -0
  32. package/dist/proxy/replay.d.ts.map +1 -0
  33. package/dist/proxy/sdkFeatures.d.ts +1 -1
  34. package/dist/proxy/sdkFeatures.d.ts.map +1 -1
  35. package/dist/proxy/server.d.ts.map +1 -1
  36. package/dist/proxy/session/cache.d.ts +20 -3
  37. package/dist/proxy/session/cache.d.ts.map +1 -1
  38. package/dist/proxy/session/lineage.d.ts +73 -3
  39. package/dist/proxy/session/lineage.d.ts.map +1 -1
  40. package/dist/proxy/session/processIncarnation.d.ts +55 -0
  41. package/dist/proxy/session/processIncarnation.d.ts.map +1 -1
  42. package/dist/proxy/sessionLifecycle.d.ts +6 -0
  43. package/dist/proxy/sessionLifecycle.d.ts.map +1 -1
  44. package/dist/proxy/setup.d.ts +14 -4
  45. package/dist/proxy/setup.d.ts.map +1 -1
  46. package/dist/proxy/streamIdleGuard.d.ts +7 -3
  47. package/dist/proxy/streamIdleGuard.d.ts.map +1 -1
  48. package/dist/proxy/structuredOutput.d.ts +6 -1
  49. package/dist/proxy/structuredOutput.d.ts.map +1 -1
  50. package/dist/proxy/transforms/codex.d.ts +12 -0
  51. package/dist/proxy/transforms/codex.d.ts.map +1 -1
  52. package/dist/proxy/turnOutcome.d.ts +2 -0
  53. package/dist/proxy/turnOutcome.d.ts.map +1 -1
  54. package/dist/server.js +2 -2
  55. package/dist/{setup-8fwgwhqh.js → setup-b3ymd9z8.js} +5 -3
  56. package/dist/telemetry/index.d.ts.map +1 -1
  57. package/package.json +7 -7
  58. package/plugin/meridian/index.js +1 -0
  59. package/plugin/meridian/package.json +6 -0
  60. package/plugin/meridian-v2/index.js +1 -0
  61. package/plugin/meridian-v2/package.json +6 -0
  62. package/plugin/meridian-v2.ts +1 -1
  63. package/plugin/meridian.ts +1 -1
@@ -0,0 +1,334 @@
1
+ // node_modules/@opencode-ai/plugin/dist/promise/plugin.js
2
+ function define(plugin) {
3
+ return plugin;
4
+ }
5
+
6
+ // plugin/priority-attestation.ts
7
+ import { createHash, createHmac } from "node:crypto";
8
+ import { readFileSync } from "node:fs";
9
+ import { homedir } from "node:os";
10
+ import { join } from "node:path";
11
+ var PRIORITY_ATTESTATION_HEADER = "x-meridian-opencode-turn";
12
+ var PRIORITY_ATTESTATION_KEY_ENV = "MERIDIAN_OPENCODE_ATTESTATION_KEY";
13
+ var PRIORITY_ATTESTATION_KEY_FILE = "opencode-turn.key";
14
+ var TOKEN_PREFIX = "v1";
15
+ var MAC_DOMAIN = "meridian.opencode.turn.v1\x00";
16
+ var TURN_DOMAIN = "meridian.opencode.human.v1\x00";
17
+ var MAX_HEADER_BYTES = 768;
18
+ var MAX_PAYLOAD_BYTES = 384;
19
+ var TURN_DIGEST_PATTERN = /^[A-Za-z0-9_-]{43}$/;
20
+ var SAFE_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
21
+ function configDirectory() {
22
+ return process.env.MERIDIAN_CONFIG_DIR ?? join(homedir(), ".config", "meridian");
23
+ }
24
+ function priorityAttestationKeyPath() {
25
+ return join(configDirectory(), PRIORITY_ATTESTATION_KEY_FILE);
26
+ }
27
+ function isSafeAgentId(value) {
28
+ return value.length >= 1 && value.length <= 64 && value.trim() === value && /^[\x20-\x7E]+$/.test(value);
29
+ }
30
+ function decodeCanonicalBase64Url(raw) {
31
+ if (!/^[A-Za-z0-9_-]+$/.test(raw))
32
+ return;
33
+ const decoded = Buffer.from(raw, "base64url");
34
+ return decoded.toString("base64url") === raw ? decoded : undefined;
35
+ }
36
+ function decodePriorityAttestationKey(raw) {
37
+ if (raw === undefined)
38
+ return;
39
+ const normalized = raw.trim();
40
+ const decoded = decodeCanonicalBase64Url(normalized);
41
+ return decoded?.length === 32 ? decoded : undefined;
42
+ }
43
+ function loadPriorityAttestationKey() {
44
+ const fromEnv = process.env[PRIORITY_ATTESTATION_KEY_ENV];
45
+ if (fromEnv !== undefined)
46
+ return decodePriorityAttestationKey(fromEnv);
47
+ try {
48
+ return decodePriorityAttestationKey(readFileSync(priorityAttestationKeyPath(), "utf8"));
49
+ } catch {
50
+ return;
51
+ }
52
+ }
53
+ function computePriorityTurnDigest(input) {
54
+ if (!SAFE_ID_PATTERN.test(input.sessionId) || !SAFE_ID_PATTERN.test(input.humanMessageId)) {
55
+ return;
56
+ }
57
+ return createHash("sha256").update(TURN_DOMAIN).update(input.generation).update("\x00").update(input.sessionId).update("\x00").update(input.humanMessageId).digest("base64url");
58
+ }
59
+ function createPriorityAttestation(input, key = loadPriorityAttestationKey() ?? Buffer.alloc(0)) {
60
+ if (key.length !== 32)
61
+ return;
62
+ if (!SAFE_ID_PATTERN.test(input.sessionId) || !isSafeAgentId(input.agentId))
63
+ return;
64
+ const turnDigest = computePriorityTurnDigest(input);
65
+ if (!turnDigest || !TURN_DIGEST_PATTERN.test(turnDigest))
66
+ return;
67
+ const issuedAt = input.issuedAt;
68
+ if (!Number.isSafeInteger(issuedAt) || issuedAt < 0)
69
+ return;
70
+ const payload = JSON.stringify({
71
+ v: 1,
72
+ g: input.generation,
73
+ s: input.sessionId,
74
+ a: input.agentId,
75
+ t: turnDigest,
76
+ iat: issuedAt
77
+ });
78
+ if (Buffer.byteLength(payload) > MAX_PAYLOAD_BYTES)
79
+ return;
80
+ const encodedPayload = Buffer.from(payload).toString("base64url");
81
+ const mac = createHmac("sha256", key).update(MAC_DOMAIN).update(payload).digest("base64url");
82
+ const token = `${TOKEN_PREFIX}.${encodedPayload}.${mac}`;
83
+ return Buffer.byteLength(token) <= MAX_HEADER_BYTES ? token : undefined;
84
+ }
85
+ function deleteHeader(headers, name) {
86
+ if (headers instanceof Headers) {
87
+ headers.delete(name);
88
+ return;
89
+ }
90
+ const lower = name.toLowerCase();
91
+ for (const key of Object.keys(headers)) {
92
+ if (key.toLowerCase() === lower)
93
+ delete headers[key];
94
+ }
95
+ }
96
+ function getHeader(headers, name) {
97
+ if (headers instanceof Headers)
98
+ return headers.get(name) ?? undefined;
99
+ const lower = name.toLowerCase();
100
+ for (const [key, value] of Object.entries(headers)) {
101
+ if (key.toLowerCase() === lower)
102
+ return value;
103
+ }
104
+ return;
105
+ }
106
+ function setHeader(headers, name, value) {
107
+ deleteHeader(headers, name);
108
+ if (headers instanceof Headers)
109
+ headers.set(name, value);
110
+ else
111
+ headers[name] = value;
112
+ }
113
+
114
+ // plugin/meridian-v2.ts
115
+ var MERIDIAN_PROVIDERS = new Set(["anthropic", "meridian"]);
116
+ var PARENT_SESSION_ONE_SHOTS = new Set(["title", "summary"]);
117
+ var ATTACHED_COMPACTION_AGENT = "compaction";
118
+ var SESSION_AFFINITY_HEADERS = [
119
+ "x-opencode-session",
120
+ "x-session-affinity",
121
+ "x-session-id",
122
+ "x-parent-session-id"
123
+ ];
124
+ var MERIDIAN_CONTROL_HEADERS = [
125
+ "x-meridian-source",
126
+ "x-opencode-agent-name",
127
+ "x-opencode-agent-mode",
128
+ PRIORITY_ATTESTATION_HEADER
129
+ ];
130
+ var BUILTIN_AGENT_TRAITS = {
131
+ build: { mode: "primary", hidden: false },
132
+ plan: { mode: "primary", hidden: false },
133
+ general: { mode: "subagent", hidden: false },
134
+ explore: { mode: "subagent", hidden: false },
135
+ title: { mode: "primary", hidden: true },
136
+ summary: { mode: "primary", hidden: true },
137
+ compaction: { mode: "primary", hidden: true }
138
+ };
139
+ function fallbackAgentTraits(agent) {
140
+ return BUILTIN_AGENT_TRAITS[agent] ?? { mode: "primary", hidden: false };
141
+ }
142
+ function safeAgentName(agent) {
143
+ return agent.replace(/[^\x20-\x7E]/g, "").trim() || "unknown";
144
+ }
145
+ function shouldDetachFromParentSession(agent, _traits) {
146
+ return PARENT_SESSION_ONE_SHOTS.has(agent);
147
+ }
148
+ function applyMeridianV2Headers(headers, input) {
149
+ for (const name2 of SESSION_AFFINITY_HEADERS)
150
+ deleteHeader(headers, name2);
151
+ for (const name2 of MERIDIAN_CONTROL_HEADERS)
152
+ deleteHeader(headers, name2);
153
+ const name = safeAgentName(input.agent);
154
+ const detached = shouldDetachFromParentSession(input.agent, input.traits);
155
+ if (detached) {
156
+ setHeader(headers, "x-meridian-source", `subagent-${name}`);
157
+ } else {
158
+ setHeader(headers, "x-opencode-session", input.sessionID);
159
+ setHeader(headers, "x-session-affinity", input.sessionID);
160
+ setHeader(headers, "x-session-id", input.sessionID);
161
+ if (input.agent === ATTACHED_COMPACTION_AGENT) {
162
+ setHeader(headers, "x-meridian-source", "subagent-compaction");
163
+ }
164
+ }
165
+ const internalMode = PARENT_SESSION_ONE_SHOTS.has(input.agent) ? "subagent" : input.agent === ATTACHED_COMPACTION_AGENT ? "primary" : input.traits.mode;
166
+ setHeader(headers, "x-opencode-agent-name", name);
167
+ setHeader(headers, "x-opencode-agent-mode", internalMode);
168
+ }
169
+ var INTERNAL_ROUTING_AGENT_IDS = new Set(["title", "summary", "compaction"]);
170
+ var AGENT_CACHE_MAX = 256;
171
+ var AGENT_CACHE_TTL_MS = 5000;
172
+ var LOOKUP_TIMEOUT_MS = 250;
173
+ var MAX_CONTEXT_ENTRIES = 2048;
174
+ var SAFE_ID_PATTERN2 = /^[A-Za-z0-9._:-]{1,128}$/;
175
+ function isRecord(value) {
176
+ return typeof value === "object" && value !== null && !Array.isArray(value);
177
+ }
178
+ async function withTimeout(pending, timeoutMs) {
179
+ let timer;
180
+ const timedOut = new Promise((resolve) => {
181
+ timer = setTimeout(() => resolve(undefined), timeoutMs);
182
+ timer.unref?.();
183
+ });
184
+ try {
185
+ return await Promise.race([pending, timedOut]);
186
+ } finally {
187
+ if (timer)
188
+ clearTimeout(timer);
189
+ }
190
+ }
191
+ function isRootV2Session(value, sessionID) {
192
+ return isRecord(value) && value.id === sessionID && value.parentID === undefined && value.fork === undefined;
193
+ }
194
+ function findLatestV2HumanTurn(value) {
195
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_CONTEXT_ENTRIES)
196
+ return;
197
+ for (let index = value.length - 1;index >= 0; index -= 1) {
198
+ const message = value[index];
199
+ if (!isRecord(message) || typeof message.type !== "string")
200
+ return;
201
+ if (message.type === "assistant" || message.type === "agent-switched" || message.type === "model-switched" || message.type === "location-switched") {
202
+ continue;
203
+ }
204
+ if (message.type !== "user" || typeof message.id !== "string" || !SAFE_ID_PATTERN2.test(message.id)) {
205
+ return;
206
+ }
207
+ const time = isRecord(message.time) ? message.time : undefined;
208
+ const createdAt = time?.created;
209
+ if (typeof createdAt !== "number" || !Number.isSafeInteger(createdAt) || createdAt < 0)
210
+ return;
211
+ return { id: message.id, issuedAt: Math.floor(createdAt / 1000) };
212
+ }
213
+ return;
214
+ }
215
+ var MeridianV2Plugin = define({
216
+ id: "meridian",
217
+ setup: async (context) => {
218
+ const traitsByAgent = new Map;
219
+ const registered = [];
220
+ const resolveAgentTraits = (agent, refresh = false) => {
221
+ const cached = traitsByAgent.get(agent);
222
+ if (!refresh && cached && cached.expiresAt > Date.now())
223
+ return cached.pending;
224
+ if (refresh)
225
+ traitsByAgent.delete(agent);
226
+ const request = Symbol(agent);
227
+ const pending = (async () => {
228
+ const controller = new AbortController;
229
+ try {
230
+ const result = await withTimeout(context.agent.get({ agentID: agent }, { signal: controller.signal }), LOOKUP_TIMEOUT_MS);
231
+ const data = result?.data;
232
+ if (!data || data.mode !== "primary" && data.mode !== "subagent" && data.mode !== "all" || typeof data.hidden !== "boolean") {
233
+ if (traitsByAgent.get(agent)?.request === request)
234
+ traitsByAgent.delete(agent);
235
+ return { traits: fallbackAgentTraits(agent), exactMode: undefined, authoritative: false };
236
+ }
237
+ return {
238
+ traits: {
239
+ mode: data.mode === "subagent" ? "subagent" : "primary",
240
+ hidden: data.hidden
241
+ },
242
+ exactMode: data.mode,
243
+ authoritative: true
244
+ };
245
+ } catch {
246
+ if (traitsByAgent.get(agent)?.request === request)
247
+ traitsByAgent.delete(agent);
248
+ return { traits: fallbackAgentTraits(agent), exactMode: undefined, authoritative: false };
249
+ } finally {
250
+ controller.abort();
251
+ }
252
+ })();
253
+ traitsByAgent.delete(agent);
254
+ traitsByAgent.set(agent, { request, expiresAt: Date.now() + AGENT_CACHE_TTL_MS, pending });
255
+ while (traitsByAgent.size > AGENT_CACHE_MAX) {
256
+ const oldest = traitsByAgent.keys().next().value;
257
+ if (oldest === undefined)
258
+ break;
259
+ traitsByAgent.delete(oldest);
260
+ }
261
+ return pending;
262
+ };
263
+ const apply = async (input, headers, refreshTraits = false) => {
264
+ if (!MERIDIAN_PROVIDERS.has(String(input.model.providerID)))
265
+ return;
266
+ const agent = String(input.agent);
267
+ const metadata = await resolveAgentTraits(agent, refreshTraits);
268
+ applyMeridianV2Headers(headers, {
269
+ sessionID: String(input.sessionID),
270
+ agent,
271
+ traits: metadata.traits
272
+ });
273
+ return metadata;
274
+ };
275
+ const resolveHumanTurn = async (input, headers, metadata) => {
276
+ const sessionID = String(input.sessionID);
277
+ const agent = String(input.agent);
278
+ if (!metadata.authoritative || metadata.exactMode !== "primary" || metadata.traits.hidden || INTERNAL_ROUTING_AGENT_IDS.has(agent) || safeAgentName(agent) !== agent || getHeader(headers, "x-meridian-profile") !== undefined) {
279
+ return;
280
+ }
281
+ const controller = new AbortController;
282
+ try {
283
+ const result = await withTimeout(Promise.all([
284
+ context.session.get({ sessionID }, { signal: controller.signal }),
285
+ context.session.context({ sessionID }, { signal: controller.signal })
286
+ ]), LOOKUP_TIMEOUT_MS);
287
+ if (!result || !isRootV2Session(result[0], sessionID))
288
+ return;
289
+ return findLatestV2HumanTurn(result[1]);
290
+ } catch {
291
+ return;
292
+ } finally {
293
+ controller.abort();
294
+ }
295
+ };
296
+ try {
297
+ for (const providerID of MERIDIAN_PROVIDERS) {
298
+ registered.push(await context.session.hook("model.request", async (input) => {
299
+ await apply(input, input.headers);
300
+ }, { providerID }));
301
+ registered.push(await context.session.hook("http.request", async (input) => {
302
+ const metadata = await apply(input, input.request.headers, true);
303
+ if (!metadata)
304
+ return;
305
+ const humanTurn = await resolveHumanTurn(input, input.request.headers, metadata);
306
+ if (!humanTurn)
307
+ return;
308
+ const token = createPriorityAttestation({
309
+ generation: "oc2b18314",
310
+ sessionId: String(input.sessionID),
311
+ agentId: String(input.agent),
312
+ humanMessageId: humanTurn.id,
313
+ issuedAt: humanTurn.issuedAt
314
+ });
315
+ if (token)
316
+ setHeader(input.request.headers, PRIORITY_ATTESTATION_HEADER, token);
317
+ }, { providerID }));
318
+ }
319
+ } catch (error) {
320
+ await Promise.allSettled(registered.map(({ dispose }) => dispose()));
321
+ throw error;
322
+ }
323
+ return async () => {
324
+ const results = await Promise.allSettled(registered.map(({ dispose }) => dispose()));
325
+ const failures = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
326
+ if (failures.length > 0)
327
+ throw new AggregateError(failures, "Failed to dispose Meridian V2 hooks");
328
+ };
329
+ }
330
+ });
331
+ var meridian_v2_default = MeridianV2Plugin;
332
+ export {
333
+ meridian_v2_default as default
334
+ };
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "@rynfar/meridian-opencode-v2-plugin",
3
+ "private": true,
4
+ "type": "module",
5
+ "main": "./index.js"
6
+ }
@@ -46,6 +46,18 @@ export interface AgentIdentity {
46
46
  * details; the proxy uses the normalized value for model-tier selection.
47
47
  */
48
48
  getAgentMode?(c: Context, body?: unknown): string | undefined;
49
+ /**
50
+ * True when the client is known to run several turns concurrently under one
51
+ * session key. Turn coordination still serializes them; what changes is the
52
+ * loser of a commit race. It is reclassified (`diverged`) and answered from
53
+ * its own body instead of refused with a 400 (#870). The pattern predates
54
+ * turn coordination and the upstream API answers both turns.
55
+ *
56
+ * Set this only for clients whose protocol makes the sharing unavoidable.
57
+ * For everyone else, a key that advanced under a waiting request carries a
58
+ * stale branch, and refusing it is what stops two histories from merging.
59
+ */
60
+ readonly runsConcurrentTurnsPerSessionKey?: boolean;
49
61
  /**
50
62
  * Optional trusted identity for a visible human turn.
51
63
  *
@@ -85,6 +97,13 @@ export interface AgentIdentity {
85
97
  * the client's own "working directory" hint from the request body.
86
98
  */
87
99
  extractClientWorkingDirectory?(body: any): string | undefined;
100
+ /**
101
+ * Whether the client's environment may be independent of the proxy-side SDK
102
+ * subprocess even when both report the same path text. When true, query
103
+ * construction keeps the client/proxy environment distinction explicit
104
+ * instead of treating lexical path equality as proof of one execution locus.
105
+ */
106
+ readonly clientEnvironmentMayDifferFromProxy?: boolean;
88
107
  /**
89
108
  * Content normalization — convert message content to a stable string
90
109
  * for hashing. Agents may send content in different formats.
@@ -108,6 +127,22 @@ export interface AgentIdentity {
108
127
  * Tools are registered as `mcp__{name}__{tool}`.
109
128
  */
110
129
  getMcpServerName(): string;
130
+ /**
131
+ * The namespace the CLIENT'S tools are nested under in passthrough mode,
132
+ * where they appear to the model as `mcp__{name}__{tool}`.
133
+ *
134
+ * Deliberately NOT `getMcpServerName()`, which names the server Meridian
135
+ * registers in INTERNAL mode. Reusing that would rename every OpenCode
136
+ * client tool from `mcp__oc__read` to `mcp__opencode__read`, moving the
137
+ * model-visible prompt — and so the prompt cache — for the entire existing
138
+ * user base, and colliding with the `mcp__opencode__*` names
139
+ * `passthroughEarlyStop` excludes precisely because they are internal.
140
+ *
141
+ * Defaults to `oc` when unset, which is what every adapter used before this
142
+ * existed. Override it only where the default actively misleads: `oc` reads
143
+ * as "opencode" to a model on an adapter that is not OpenCode (#893).
144
+ */
145
+ getPassthroughMcpName?(): string;
111
146
  }
112
147
  /**
113
148
  * An agent adapter provides agent-specific configuration to the proxy.
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/proxy/adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/proxy/adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAEnE,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,gFAAgF;IAChF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,UAAU,EAAE,aAAa,GAAG,wBAAwB,CAAA;CAC9D,CAAC,CAAA;AAEF;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB;;;OAGG;IACH,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;IAE5D;;;;;;;;;;;;OAYG;IACH,kBAAkB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;IAEnE;;;OAGG;IACH,YAAY,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;IAE7D;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,gCAAgC,CAAC,EAAE,OAAO,CAAA;IAEnD;;;;;;OAMG;IACH,sBAAsB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,mBAAmB,GAAG,SAAS,CAAA;IAEpF;;;;;;;;;;;;;;;;OAgBG;IACH,uBAAuB,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAAA;IAEtD;;;;;;;;;;;OAWG;IACH,6BAA6B,CAAC,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAAA;IAE7D;;;;;OAKG;IACH,QAAQ,CAAC,mCAAmC,CAAC,EAAE,OAAO,CAAA;IAEtD;;;OAGG;IACH,gBAAgB,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,CAAA;IAEtC;;;;;OAKG;IACH,8BAA8B,CAAC,CAC7B,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,GAClD,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IAE5C;;;OAGG;IACH,gBAAgB,IAAI,MAAM,CAAA;IAE1B;;;;;;;;;;;;;;OAcG;IACH,qBAAqB,CAAC,IAAI,MAAM,CAAA;CACjC;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAa,SAAQ,aAAa;IACjD;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAE1B,6EAA6E;IAC7E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC,OAAO,eAAe,EAAE,eAAe,CAAC,CAAA;IAE5E,6EAA6E;IAC7E,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAEtC;;;OAGG;IACH,sBAAsB,IAAI,SAAS,MAAM,EAAE,CAAA;IAE3C;;;;OAIG;IACH,yBAAyB,IAAI,SAAS,MAAM,EAAE,CAAA;IAE9C;;OAEG;IACH,kBAAkB,IAAI,SAAS,MAAM,EAAE,CAAA;IAEvC;;;;OAIG;IACH,cAAc,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAEhF;;;OAGG;IACH,aAAa,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,CAAA;IAE9D;;;OAGG;IACH,0BAA0B,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAA;IAE9E;;;;;;OAMG;IACH,gBAAgB,CAAC,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAA;IAErC;;;;;;;;OAQG;IACH,eAAe,CAAC,IAAI,OAAO,CAAA;IAE3B;;;;;;;OAOG;IACH,gBAAgB,CAAC,IAAI,SAAS,MAAM,EAAE,CAAA;IAEtC;;;;;;;;;;;;;;OAcG;IACH,iBAAiB,CAAC,IAAI,aAAa,EAAE,CAAA;IAErC;;;;;;;OAOG;IACH,gBAAgB,CAAC,IAAI,OAAO,CAAA;IAE5B;;;;;;OAMG;IACH,sBAAsB,CAAC,IAAI,OAAO,CAAA;IAElC;;;;;;;;OAQG;IACH,yBAAyB,CAAC,IAAI,OAAO,CAAA;IAErC;;;;;;;;;;;;;;;OAeG;IACH,6BAA6B,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,eAAe,EAAE,UAAU,EAAE,CAAA;CAC3G"}
@@ -16,6 +16,7 @@
16
16
  * - Parses the client's local CWD via extractClientWorkingDirectory for
17
17
  * fingerprinting and a system-prompt hint (see server.ts + query.ts).
18
18
  */
19
+ import type { Context } from "hono";
19
20
  import type { AgentAdapter } from "../adapter";
20
21
  /**
21
22
  * Session identity declared in `metadata.user_id`.
@@ -48,5 +49,27 @@ export declare function extractClaudeCodeSessionIdentity(body: unknown): ClaudeC
48
49
  export declare function extractClaudeCodeSessionId(body: unknown): string | undefined;
49
50
  /** Extract the immediate parent session key, when the client declares lineage. */
50
51
  export declare function extractClaudeCodeParentSessionId(body: unknown): string | undefined;
52
+ /**
53
+ * Is this request from the Claude Code CLI, whatever adapter is handling it?
54
+ *
55
+ * Claude Code sends `x-claude-code-session-id` on every request — verified
56
+ * against 2.1.266 that it is the CLI session UUID, pinned exactly by
57
+ * `--session-id`. No other client sends it, so it identifies the client even
58
+ * when a gateway has rewritten the User-Agent and the LiteLLM heuristic has
59
+ * already claimed the request.
60
+ *
61
+ * This answers "who owns the tool loop", not "which adapter should run". The
62
+ * header is deliberately NOT used for adapter selection: routing gateway
63
+ * traffic to the claude-code adapter would swap tool handling, MCP naming and
64
+ * prompt shape for every existing LiteLLM user and move their cache prefix.
65
+ *
66
+ * It is also NOT a session key. The CLI reuses one session id across the
67
+ * auxiliary requests it makes alongside a conversation, so keying on it puts
68
+ * two unrelated histories under one key — measured live as
69
+ * `unrelated-history` and an HTTP 400 concurrent conflict, i.e. a hard failure
70
+ * where there had only been a silent inefficiency. See
71
+ * `scripts/e2e-passthrough-claude-code-session.mjs`, which guards that.
72
+ */
73
+ export declare function isClaudeCodeClient(c: Context): boolean;
51
74
  export declare const claudeCodeAdapter: AgentAdapter;
52
75
  //# sourceMappingURL=claudecode.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"claudecode.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/claudecode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAiC9C;;;;;;;GAOG;AACH,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAClC;AAED;;;;;;;;GAQG;AACH,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,OAAO,GAAG,yBAAyB,GAAG,SAAS,CA+BrG;AAED,sFAAsF;AACtF,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAE5E;AAED,kFAAkF;AAClF,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAElF;AAED,eAAO,MAAM,iBAAiB,EAAE,YAkG/B,CAAA"}
1
+ {"version":3,"file":"claudecode.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/claudecode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAiC9C;;;;;;;GAOG;AACH,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAClC;AAED;;;;;;;;GAQG;AACH,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,OAAO,GAAG,yBAAyB,GAAG,SAAS,CA+BrG;AAED,sFAAsF;AACtF,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAE5E;AAED,kFAAkF;AAClF,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAElF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAEtD;AAED,eAAO,MAAM,iBAAiB,EAAE,YAqG/B,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/opencode.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAuB,MAAM,YAAY,CAAA;AAsCnE,wBAAgB,sCAAsC,CACpD,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,GAClD,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAU3C;AAED,eAAO,MAAM,eAAe,EAAE,YAqM7B,CAAA;AAED,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
1
+ {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/opencode.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAuB,MAAM,YAAY,CAAA;AAuDnE,wBAAgB,sCAAsC,CACpD,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,GAClD,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAU3C;AAED,eAAO,MAAM,eAAe,EAAE,YAqN7B,CAAA;AAED,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"passthrough.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/passthrough.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAiD9C,eAAO,MAAM,kBAAkB,EAAE,YA4EhC,CAAA;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAA;AACjE,OAAO,EAAE,qBAAqB,EAAE,CAAA"}
1
+ {"version":3,"file":"passthrough.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/passthrough.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAiD9C,eAAO,MAAM,kBAAkB,EAAE,YAuFhC,CAAA;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAA;AACjE,OAAO,EAAE,qBAAqB,EAAE,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/pi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AA0C9C,eAAO,MAAM,SAAS,EAAE,YAoJvB,CAAA;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/pi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AA0C9C,eAAO,MAAM,SAAS,EAAE,YA+JvB,CAAA;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,CAAA"}
@@ -25,6 +25,8 @@ export interface ClassifiedError {
25
25
  * - The tier is unrecognized, where the global switch is the only safe advice.
26
26
  */
27
27
  export declare function extendedContextHint(model?: string): string;
28
+ /** True when a turn failed only because it hit the client's output cap. */
29
+ export declare function isOutputTokenCapExceeded(message: string | undefined | null): boolean;
28
30
  /**
29
31
  * Detect specific SDK errors and return helpful messages to the client.
30
32
  *
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/proxy/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAc1D;AA4JD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,eAAe,CAqJ7E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAM3D;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,aAAa,GAAG,iBAAiB,CAAA;AAEtE;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAahG;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAI3E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGxD;AAuBD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,IAAI,MAAM,CAEhG;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAE5E;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGjE;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,GAAG,cAAc,GAAG,SAAS,GAAG,eAAe,GAAG,kBAAkB,GAAG,SAAS,CAAA;IACnG,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;wCAEoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAuBD;;;;;;GAMG;AACH;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE;IAChD,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAA;IAChC,WAAW,EAAE,OAAO,CAAA;IACpB,gBAAgB,EAAE,MAAM,CAAA;IACxB,WAAW,EAAE,OAAO,CAAA;CACrB,GAAG,OAAO,CAYV;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAiEpE;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,CAAC,EAAE,cAAc,EACjB,GAAG,EAAE;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,GACA,MAAM,CAYR"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/proxy/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAc1D;AAuID,2EAA2E;AAC3E,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAEpF;AAqFD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,eAAe,CA2K7E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAM3D;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,aAAa,GAAG,iBAAiB,CAAA;AAEtE;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAahG;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAI3E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGxD;AAuBD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,IAAI,MAAM,CAEhG;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAE5E;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGjE;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,GAAG,cAAc,GAAG,SAAS,GAAG,eAAe,GAAG,kBAAkB,GAAG,SAAS,CAAA;IACnG,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;wCAEoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAuBD;;;;;;GAMG;AACH;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE;IAChD,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAA;IAChC,WAAW,EAAE,OAAO,CAAA;IACpB,gBAAgB,EAAE,MAAM,CAAA;IACxB,WAAW,EAAE,OAAO,CAAA;CACrB,GAAG,OAAO,CAYV;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAiEpE;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,CAAC,EAAE,cAAc,EACjB,GAAG,EAAE;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,GACA,MAAM,CAYR"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Consecutive upstream-idle ceiling.
3
+ *
4
+ * guardUpstreamIdle turns a stalled model stream into a 504, which is the right
5
+ * answer for a one-off stall: a retry usually clears it. What a 504 cannot say
6
+ * is that the SAME session has now stalled the same way several turns running.
7
+ * "Transient, try again" is what every client retry policy reads in a 5xx, so a
8
+ * session that stalls deterministically gets replayed forever at full upstream
9
+ * cost — on 2026-08-16 that ran twenty turns into a half-hour loop, each killed
10
+ * at exactly the idle limit, each retried, none ever completing.
11
+ *
12
+ * The proxy does not retry; the client does. Past the ceiling it emits a
13
+ * terminal error and briefly rejects identical retries before opening another
14
+ * stream. A changed request can try immediately.
15
+ *
16
+ * Counts are scoped to the session and exact request. Changed requests and
17
+ * completed turns clear the streak. A terminal verdict is checked before the
18
+ * next HTTP response opens because some clients retry terminal SSE errors.
19
+ * The pause expires after one idle window (at least 60 seconds), so a transient
20
+ * outage cannot permanently lock the request out. All times are caller-supplied.
21
+ */
22
+ /** Exact request identity: changes must never inherit another turn's retry block. */
23
+ export declare function idleStallRequestKey(body: unknown): string;
24
+ export declare function idleStallPauseMs(idleMs: number): number;
25
+ export interface IdleStallRequestContext {
26
+ key: string;
27
+ now: number;
28
+ }
29
+ export interface IdleStallVerdict {
30
+ status: number;
31
+ type: string;
32
+ message: string;
33
+ /** Consecutive stalls on this session, including the one being judged. */
34
+ consecutive: number;
35
+ /** True when the ceiling fired and the client is being told to stop. */
36
+ terminal: boolean;
37
+ }
38
+ /** Carries a classified idle-stall response through the non-stream error path. */
39
+ export declare class IdleStallCeilingError extends Error {
40
+ readonly verdict: IdleStallVerdict;
41
+ constructor(verdict: IdleStallVerdict);
42
+ }
43
+ /** Tracks consecutive idle stalls per session and rules on each one.
44
+ *
45
+ * Bounded by construction. The session maps in server.ts that predate this are
46
+ * plain unbounded Maps; a tracker keyed by session id would leak the same way,
47
+ * so capacity is required rather than optional. */
48
+ export declare class IdleStallTracker {
49
+ private readonly ceiling;
50
+ private readonly capacity;
51
+ private readonly counts;
52
+ /** @param ceiling Consecutive stalls tolerated before the verdict turns
53
+ * terminal. 0 disables the ceiling — every stall stays a 504,
54
+ * which is the pre-ceiling behaviour.
55
+ * @param capacity Maximum sessions tracked; the oldest entry is evicted
56
+ * first. */
57
+ constructor(ceiling: number, capacity: number);
58
+ /** Clear a session's streak. Call on any completed turn — a turn that
59
+ * finished is proof the session can still make progress. */
60
+ clear(sessionKey: string): void;
61
+ /** Check before opening HTTP/SSE. Time is supplied by the caller, not read here. */
62
+ preflight(sessionKey: string, requestKey: string, idleMs: number, now: number): IdleStallVerdict | undefined;
63
+ /** Record a stall and rule on it.
64
+ *
65
+ * An empty session key means the turn carries no session identity to
66
+ * correlate against (a first turn that stalled before the SDK reported its
67
+ * session id). Those are counted as one-offs rather than pooled under a
68
+ * shared "" key, which would let unrelated sessions trip each other's
69
+ * ceiling. */
70
+ record(sessionKey: string, idleMs: number, sinceLastMs: number, request?: IdleStallRequestContext): IdleStallVerdict;
71
+ /** Test/telemetry accessor. */
72
+ streak(sessionKey: string): number;
73
+ }
74
+ //# sourceMappingURL=idleStallCeiling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idleStallCeiling.d.ts","sourceRoot":"","sources":["../../src/proxy/idleStallCeiling.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAEzD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;CACZ;AAQD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAA;IACnB,wEAAwE;IACxE,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,kFAAkF;AAClF,qBAAa,qBAAsB,SAAQ,KAAK;IAClC,QAAQ,CAAC,OAAO,EAAE,gBAAgB;gBAAzB,OAAO,EAAE,gBAAgB;CAI/C;AAED;;;;oDAIoD;AACpD,qBAAa,gBAAgB;IASzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAT3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyC;IAEhE;;;;iCAI6B;gBAEV,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM;IAGnC;iEAC6D;IAC7D,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAI/B,oFAAoF;IACpF,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAW5G;;;;;;mBAMe;IACf,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,gBAAgB;IAoCpH,+BAA+B;IAC/B,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;CAGnC"}
@@ -47,12 +47,13 @@ export declare const HASH_HANDLED_BLOCK_TYPES: Set<string>;
47
47
  */
48
48
  export declare const HASH_SERIALIZED_BLOCK_TYPES: Set<string>;
49
49
  /**
50
- * Normalize message content to a string for hashing and comparison.
50
+ * Legacy content rendering used for adapter compatibility and diagnostics.
51
51
  * Handles both string content and array content (Anthropic content blocks).
52
- * Strips cache_control metadata to ensure hash stability across requests.
52
+ * Omits cache_control metadata and opaque thinking blocks.
53
53
  *
54
- * Used only for lineage hashing see {@link HASH_IGNORED_BLOCK_TYPES}, which
55
- * drops content a display-oriented normalizer would need to keep.
54
+ * This representation is ambiguous across block types and boundaries. Never
55
+ * use it to prove lineage: session/lineage.ts uses structured domain-separated
56
+ * hashes instead.
56
57
  *
57
58
  * NOTE: OpenCode sends content as a string on the first request but as
58
59
  * an array on subsequent ones. This normalizer handles both formats.
@@ -96,6 +97,8 @@ export declare function getLastUserMessage(messages: Array<{
96
97
  * and histories that don't end with a user turn are returned as a plain
97
98
  * join (nothing to separate).
98
99
  */
100
+ export declare const REPLAY_CONTEXT_OPEN = "<conversation_history>\n";
101
+ export declare const REPLAY_CONTEXT_CLOSE: string;
99
102
  export declare function frameReplayTurns(turns: Array<{
100
103
  role: string;
101
104
  text: string;
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/proxy/messages.ts"],"names":[],"mappings":"AAAA;;GAEG;AAcH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,wBAAwB,aAA6C,CAAA;AAElF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,aAA+C,CAAA;AAEpF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,2BAA2B,aAYtC,CAAA;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,CAmBrD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAUtE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAM7D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,CAKzH;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,CAerF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAU3D;AAED,yEAAyE;AACzE,eAAO,MAAM,gBAAgB,aAAyC,CAAA;AAEtE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iCAAiC,CAC/C,CAAC,SAAS;IAAE,OAAO,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,EAC3C,UAAU,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAyCtB;AAED,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,8EAA8E;IAC9E,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B;;;;;;;OAOG;IACH,cAAc,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AA4FD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAC9C,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAyB3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAO3D;AA6BD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAQzD"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/proxy/messages.ts"],"names":[],"mappings":"AAAA;;GAEG;AAcH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,wBAAwB,aAA6C,CAAA;AAElF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,aAA+C,CAAA;AAEpF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,2BAA2B,aAYtC,CAAA;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,CAmBrD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAUtE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAM7D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,CAKzH;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,6BAA6B,CAAA;AAC7D,eAAO,MAAM,oBAAoB,QAIiC,CAAA;AAElE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,CAQrF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAU3D;AAED,yEAAyE;AACzE,eAAO,MAAM,gBAAgB,aAAyC,CAAA;AAEtE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iCAAiC,CAC/C,CAAC,SAAS;IAAE,OAAO,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,EAC3C,UAAU,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAyCtB;AAED,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,8EAA8E;IAC9E,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B;;;;;;;OAOG;IACH,cAAc,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AA4FD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAC9C,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAyB3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAO3D;AA6BD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAQzD"}
@@ -52,6 +52,14 @@ export interface OpenAiChatToolCustom {
52
52
  type: "custom";
53
53
  }
54
54
  export type OpenAiChatTool = OpenAiChatToolFunction | OpenAiChatToolCustom;
55
+ export interface OpenAiResponseFormat {
56
+ type: "text" | "json_object" | "json_schema";
57
+ json_schema?: {
58
+ name?: string;
59
+ schema?: unknown;
60
+ strict?: boolean;
61
+ };
62
+ }
55
63
  export interface OpenAiChatRequest {
56
64
  model?: string;
57
65
  messages?: OpenAiMessage[];
@@ -66,7 +74,10 @@ export interface OpenAiChatRequest {
66
74
  /** Anthropic-style nesting some clients use. */
67
75
  output_config?: {
68
76
  effort?: string;
77
+ format?: unknown;
69
78
  };
79
+ /** Standard OpenAI structured output (json_schema / json_object / text). */
80
+ response_format?: OpenAiResponseFormat;
70
81
  stream_options?: {
71
82
  include_usage?: boolean;
72
83
  };
@@ -116,6 +127,7 @@ export interface AnthropicRequestBody {
116
127
  reasoning_effort?: string;
117
128
  output_config?: {
118
129
  effort?: string;
130
+ format?: unknown;
119
131
  };
120
132
  }
121
133
  export interface AnthropicUsage {