@rynfar/meridian 1.67.0 → 1.68.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 (47) hide show
  1. package/dist/{cli-35jx6vmy.js → cli-0zhb8ss4.js} +1568 -586
  2. package/dist/{cli-n8t34zmq.js → cli-sh8bzdwe.js} +24 -8
  3. package/dist/cli.js +7 -7
  4. package/dist/meridian-v2/index.js +334 -0
  5. package/dist/meridian-v2/package.json +6 -0
  6. package/dist/proxy/adapter.d.ts +19 -0
  7. package/dist/proxy/adapter.d.ts.map +1 -1
  8. package/dist/proxy/adapters/claudecode.d.ts.map +1 -1
  9. package/dist/proxy/adapters/opencode.d.ts.map +1 -1
  10. package/dist/proxy/adapters/pi.d.ts.map +1 -1
  11. package/dist/proxy/errors.d.ts.map +1 -1
  12. package/dist/proxy/idleStallCeiling.d.ts +74 -0
  13. package/dist/proxy/idleStallCeiling.d.ts.map +1 -0
  14. package/dist/proxy/messages.d.ts +7 -4
  15. package/dist/proxy/messages.d.ts.map +1 -1
  16. package/dist/proxy/openai.d.ts +12 -0
  17. package/dist/proxy/openai.d.ts.map +1 -1
  18. package/dist/proxy/passthroughEarlyStop.d.ts +17 -2
  19. package/dist/proxy/passthroughEarlyStop.d.ts.map +1 -1
  20. package/dist/proxy/passthroughTools.d.ts +19 -1
  21. package/dist/proxy/passthroughTools.d.ts.map +1 -1
  22. package/dist/proxy/query.d.ts +40 -10
  23. package/dist/proxy/query.d.ts.map +1 -1
  24. package/dist/proxy/replay.d.ts +24 -0
  25. package/dist/proxy/replay.d.ts.map +1 -0
  26. package/dist/proxy/sdkFeatures.d.ts +1 -1
  27. package/dist/proxy/sdkFeatures.d.ts.map +1 -1
  28. package/dist/proxy/server.d.ts.map +1 -1
  29. package/dist/proxy/session/cache.d.ts.map +1 -1
  30. package/dist/proxy/session/lineage.d.ts +17 -3
  31. package/dist/proxy/session/lineage.d.ts.map +1 -1
  32. package/dist/proxy/sessionLifecycle.d.ts +6 -0
  33. package/dist/proxy/sessionLifecycle.d.ts.map +1 -1
  34. package/dist/proxy/setup.d.ts +2 -2
  35. package/dist/proxy/setup.d.ts.map +1 -1
  36. package/dist/proxy/streamIdleGuard.d.ts +7 -3
  37. package/dist/proxy/streamIdleGuard.d.ts.map +1 -1
  38. package/dist/proxy/structuredOutput.d.ts +6 -1
  39. package/dist/proxy/structuredOutput.d.ts.map +1 -1
  40. package/dist/proxy/turnOutcome.d.ts +2 -0
  41. package/dist/proxy/turnOutcome.d.ts.map +1 -1
  42. package/dist/server.js +2 -2
  43. package/dist/{setup-8fwgwhqh.js → setup-knvctar4.js} +3 -3
  44. package/package.json +7 -7
  45. package/plugin/meridian-v2/index.js +1 -0
  46. package/plugin/meridian-v2/package.json +6 -0
  47. package/plugin/meridian-v2.ts +1 -1
@@ -637,7 +637,7 @@ var init_priorityAttestation = __esm(() => {
637
637
 
638
638
  // src/proxy/setup.ts
639
639
  var import_cross_spawn = __toESM(require_cross_spawn(), 1);
640
- import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
640
+ import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
641
641
  import { homedir as homedir2, platform as platform2 } from "os";
642
642
  import { basename, dirname as dirname2, join as join2 } from "path";
643
643
  import { fileURLToPath } from "url";
@@ -774,18 +774,34 @@ function findPluginPath(fromUrl) {
774
774
  const dir = dirname2(fileURLToPath(fromUrl));
775
775
  return join2(dir, "..", "plugin", "meridian.ts");
776
776
  }
777
- var SUPPORTED_OPENCODE_V2_VERSION = "0.0.0-beta-18314";
777
+ var SUPPORTED_OPENCODE_V2_VERSIONS = new Set([
778
+ "0.0.0-beta-18314",
779
+ "0.0.0-beta-18866"
780
+ ]);
781
+ function hasV2PluginEntry(path) {
782
+ try {
783
+ if (!statSync(join2(path, "index.js"), { throwIfNoEntry: false })?.isFile())
784
+ return false;
785
+ const manifest = JSON.parse(readFileSync2(join2(path, "package.json"), "utf8"));
786
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest))
787
+ return false;
788
+ const fields = manifest;
789
+ return fields.type === "module" && fields.main === "./index.js";
790
+ } catch {
791
+ return false;
792
+ }
793
+ }
778
794
  function findV2PluginPath(fromUrl) {
779
795
  const entryPath = fileURLToPath(fromUrl);
780
796
  const dir = dirname2(entryPath);
781
797
  if (entryPath.endsWith(".ts")) {
782
- const sourcePlugin = join2(dir, "..", "plugin", "meridian-v2.ts");
783
- if (existsSync(sourcePlugin))
798
+ const sourcePlugin = join2(dir, "..", "plugin", "meridian-v2");
799
+ if (hasV2PluginEntry(sourcePlugin))
784
800
  return sourcePlugin;
785
801
  throw new MissingV2PluginError(sourcePlugin);
786
802
  }
787
- const bundledPlugin = join2(dir, "meridian-v2.js");
788
- if (existsSync(bundledPlugin))
803
+ const bundledPlugin = join2(dir, "meridian-v2");
804
+ if (hasV2PluginEntry(bundledPlugin))
789
805
  return bundledPlugin;
790
806
  throw new MissingV2PluginError(bundledPlugin);
791
807
  }
@@ -840,7 +856,7 @@ function isMeridianEntry(entry) {
840
856
  const packageName = pluginEntryPackage(entry);
841
857
  if (!packageName)
842
858
  return false;
843
- return STALE_PATTERNS.some((pattern) => packageName.includes(pattern)) || packageName.includes("meridian.ts") || packageName.includes("meridian-v2.") || packageName.includes("@rynfar/meridian");
859
+ return STALE_PATTERNS.some((pattern) => packageName.includes(pattern)) || packageName.includes("meridian.ts") || packageName.includes("meridian-v2.") || packageName.endsWith("/meridian-v2") || packageName.includes("@rynfar/meridian");
844
860
  }
845
861
  function checkPluginConfigured(configPath, expectedPluginPath) {
846
862
  const selectedPath = configPath ?? findOpencodeConfigPath();
@@ -936,4 +952,4 @@ function runSetup(pluginPath, configPath, generation = "v1") {
936
952
  return { configPath: path, pluginPath, alreadyConfigured, removedStale, created: false };
937
953
  }
938
954
 
939
- export { LRUMap, PRIORITY_ATTESTATION_HEADER, verifyPriorityAttestation, init_priorityAttestation, UnparseableConfigError, MissingV2PluginError, DuplicateMeridianConfigError, findOpencodeConfigPath, findPluginPath, SUPPORTED_OPENCODE_V2_VERSION, findV2PluginPath, classifyOpenCodeVersion, detectOpenCodeGeneration, pluginPathForGeneration, checkPluginConfigured, clearPluginlessWarnings, notePluginlessOpenCodeRequest, runSetup };
955
+ export { LRUMap, PRIORITY_ATTESTATION_HEADER, verifyPriorityAttestation, init_priorityAttestation, UnparseableConfigError, MissingV2PluginError, DuplicateMeridianConfigError, findOpencodeConfigPath, findPluginPath, SUPPORTED_OPENCODE_V2_VERSIONS, findV2PluginPath, classifyOpenCodeVersion, detectOpenCodeGeneration, pluginPathForGeneration, checkPluginConfigured, clearPluginlessWarnings, notePluginlessOpenCodeRequest, runSetup };
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startProxyServer
4
- } from "./cli-35jx6vmy.js";
4
+ } from "./cli-0zhb8ss4.js";
5
5
  import"./cli-5jxyma6z.js";
6
6
  import"./cli-sry5aqdj.js";
7
7
  import"./cli-8yp89fan.js";
@@ -10,7 +10,7 @@ import {
10
10
  } from "./cli-9e5cxp89.js";
11
11
  import"./cli-khhjyk04.js";
12
12
  import"./cli-vj9cv18n.js";
13
- import"./cli-n8t34zmq.js";
13
+ import"./cli-sh8bzdwe.js";
14
14
  import {
15
15
  __require
16
16
  } from "./cli-p9swy5t3.js";
@@ -91,9 +91,9 @@ if (args[0] === "setup") {
91
91
  MissingV2PluginError,
92
92
  pluginPathForGeneration,
93
93
  runSetup,
94
- SUPPORTED_OPENCODE_V2_VERSION,
94
+ SUPPORTED_OPENCODE_V2_VERSIONS,
95
95
  UnparseableConfigError
96
- } = await import("./setup-8fwgwhqh.js");
96
+ } = await import("./setup-knvctar4.js");
97
97
  const forceV1 = args.includes("--v1");
98
98
  const forceV2 = args.includes("--v2");
99
99
  if (forceV1 && forceV2) {
@@ -117,9 +117,9 @@ if (args[0] === "setup") {
117
117
  console.error("Could not find an OpenCode V2 beta. Install the pinned beta or pass --opencode-bin <path>.");
118
118
  process.exit(1);
119
119
  }
120
- if (detected.generation === "v2" && detected.version !== SUPPORTED_OPENCODE_V2_VERSION) {
120
+ if (detected.generation === "v2" && !SUPPORTED_OPENCODE_V2_VERSIONS.has(detected.version ?? "")) {
121
121
  console.error(`OpenCode V2 ${detected.version ?? "unknown"} is not supported by this Meridian build.`);
122
- console.error(`Install @opencode-ai/cli@${SUPPORTED_OPENCODE_V2_VERSION}, then re-run meridian setup --v2.`);
122
+ console.error(`Install a supported OpenCode beta (${[...SUPPORTED_OPENCODE_V2_VERSIONS].join(", ")}), then re-run meridian setup --v2.`);
123
123
  process.exit(1);
124
124
  }
125
125
  let pluginPath;
@@ -205,7 +205,7 @@ async function runCli(start = startProxyServer, runAuthCheck = async () => {
205
205
  return execFile(claudePath, ["auth", "status"], { timeout: 5000 });
206
206
  }) {
207
207
  try {
208
- const { findOpencodeConfigPath, checkPluginConfigured, findPluginPath } = await import("./setup-8fwgwhqh.js");
208
+ const { findOpencodeConfigPath, checkPluginConfigured, findPluginPath } = await import("./setup-knvctar4.js");
209
209
  const configPath = findOpencodeConfigPath();
210
210
  const { existsSync } = await import("fs");
211
211
  if (existsSync(configPath) && !checkPluginConfigured(configPath)) {
@@ -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.
@@ -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":"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"}
@@ -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;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,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":"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"}
@@ -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;AAqLD;;;;;;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"}