@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.
- package/dist/{cli-35jx6vmy.js → cli-x1sv3bqf.js} +2445 -1070
- package/dist/{cli-n8t34zmq.js → cli-zdbv40d5.js} +45 -10
- package/dist/cli.js +7 -7
- package/dist/meridian/index.js +252 -0
- package/dist/meridian/package.json +6 -0
- package/dist/meridian-v2/index.js +334 -0
- package/dist/meridian-v2/package.json +6 -0
- package/dist/proxy/adapter.d.ts +35 -0
- package/dist/proxy/adapter.d.ts.map +1 -1
- package/dist/proxy/adapters/claudecode.d.ts +23 -0
- package/dist/proxy/adapters/claudecode.d.ts.map +1 -1
- package/dist/proxy/adapters/opencode.d.ts.map +1 -1
- package/dist/proxy/adapters/passthrough.d.ts.map +1 -1
- package/dist/proxy/adapters/pi.d.ts.map +1 -1
- package/dist/proxy/errors.d.ts +2 -0
- package/dist/proxy/errors.d.ts.map +1 -1
- package/dist/proxy/idleStallCeiling.d.ts +74 -0
- package/dist/proxy/idleStallCeiling.d.ts.map +1 -0
- package/dist/proxy/messages.d.ts +7 -4
- package/dist/proxy/messages.d.ts.map +1 -1
- package/dist/proxy/openai.d.ts +12 -0
- package/dist/proxy/openai.d.ts.map +1 -1
- package/dist/proxy/openaiResponses.d.ts +106 -3
- package/dist/proxy/openaiResponses.d.ts.map +1 -1
- package/dist/proxy/passthroughEarlyStop.d.ts +20 -5
- package/dist/proxy/passthroughEarlyStop.d.ts.map +1 -1
- package/dist/proxy/passthroughTools.d.ts +82 -3
- package/dist/proxy/passthroughTools.d.ts.map +1 -1
- package/dist/proxy/query.d.ts +43 -10
- package/dist/proxy/query.d.ts.map +1 -1
- package/dist/proxy/replay.d.ts +24 -0
- package/dist/proxy/replay.d.ts.map +1 -0
- package/dist/proxy/sdkFeatures.d.ts +1 -1
- package/dist/proxy/sdkFeatures.d.ts.map +1 -1
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/session/cache.d.ts +20 -3
- package/dist/proxy/session/cache.d.ts.map +1 -1
- package/dist/proxy/session/lineage.d.ts +73 -3
- package/dist/proxy/session/lineage.d.ts.map +1 -1
- package/dist/proxy/session/processIncarnation.d.ts +55 -0
- package/dist/proxy/session/processIncarnation.d.ts.map +1 -1
- package/dist/proxy/sessionLifecycle.d.ts +6 -0
- package/dist/proxy/sessionLifecycle.d.ts.map +1 -1
- package/dist/proxy/setup.d.ts +14 -4
- package/dist/proxy/setup.d.ts.map +1 -1
- package/dist/proxy/streamIdleGuard.d.ts +7 -3
- package/dist/proxy/streamIdleGuard.d.ts.map +1 -1
- package/dist/proxy/structuredOutput.d.ts +6 -1
- package/dist/proxy/structuredOutput.d.ts.map +1 -1
- package/dist/proxy/transforms/codex.d.ts +12 -0
- package/dist/proxy/transforms/codex.d.ts.map +1 -1
- package/dist/proxy/turnOutcome.d.ts +2 -0
- package/dist/proxy/turnOutcome.d.ts.map +1 -1
- package/dist/server.js +2 -2
- package/dist/{setup-8fwgwhqh.js → setup-b3ymd9z8.js} +5 -3
- package/dist/telemetry/index.d.ts.map +1 -1
- package/package.json +7 -7
- package/plugin/meridian/index.js +1 -0
- package/plugin/meridian/package.json +6 -0
- package/plugin/meridian-v2/index.js +1 -0
- package/plugin/meridian-v2/package.json +6 -0
- package/plugin/meridian-v2.ts +1 -1
- 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
|
+
};
|
package/dist/proxy/adapter.d.ts
CHANGED
|
@@ -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":"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;;;;;;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;;;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;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/proxy/adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -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;
|
|
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;
|
|
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,
|
|
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,
|
|
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"}
|
package/dist/proxy/errors.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/proxy/messages.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
50
|
+
* Legacy content rendering used for adapter compatibility and diagnostics.
|
|
51
51
|
* Handles both string content and array content (Anthropic content blocks).
|
|
52
|
-
*
|
|
52
|
+
* Omits cache_control metadata and opaque thinking blocks.
|
|
53
53
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
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
|
|
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"}
|
package/dist/proxy/openai.d.ts
CHANGED
|
@@ -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 {
|