@rynfar/meridian 1.66.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.
- package/dist/{cli-nzvbaqjp.js → cli-0zhb8ss4.js} +1603 -589
- package/dist/{cli-m9wqgk1q.js → cli-9e5cxp89.js} +1 -1
- package/dist/{cli-n8t34zmq.js → cli-sh8bzdwe.js} +24 -8
- package/dist/cli.js +9 -9
- package/dist/meridian-v2/index.js +334 -0
- package/dist/meridian-v2/package.json +6 -0
- package/dist/{profileCli-qhcr87tm.js → profileCli-1eecbmgk.js} +1 -1
- package/dist/proxy/adapter.d.ts +19 -0
- package/dist/proxy/adapter.d.ts.map +1 -1
- package/dist/proxy/adapters/claudecode.d.ts.map +1 -1
- package/dist/proxy/adapters/opencode.d.ts.map +1 -1
- package/dist/proxy/adapters/pi.d.ts.map +1 -1
- package/dist/proxy/errors.d.ts +1 -1
- 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/models.d.ts +3 -3
- package/dist/proxy/models.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/passthroughEarlyStop.d.ts +17 -2
- package/dist/proxy/passthroughEarlyStop.d.ts.map +1 -1
- package/dist/proxy/passthroughTools.d.ts +19 -1
- package/dist/proxy/passthroughTools.d.ts.map +1 -1
- package/dist/proxy/query.d.ts +40 -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.map +1 -1
- package/dist/proxy/session/lineage.d.ts +17 -3
- package/dist/proxy/session/lineage.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 +2 -2
- 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/turnOutcome.d.ts +2 -0
- package/dist/proxy/turnOutcome.d.ts.map +1 -1
- package/dist/server.js +3 -3
- package/dist/{setup-8fwgwhqh.js → setup-knvctar4.js} +3 -3
- package/dist/telemetry/pricing.d.ts.map +1 -1
- package/package.json +8 -8
- package/plugin/meridian-v2/index.js +1 -0
- package/plugin/meridian-v2/package.json +6 -0
- package/plugin/meridian-v2.ts +1 -1
|
@@ -50,7 +50,7 @@ import { promisify } from "util";
|
|
|
50
50
|
var exec = promisify(execCallback);
|
|
51
51
|
var execFile = promisify(execFileCallback);
|
|
52
52
|
var STUB_SIZE_THRESHOLD = 4096;
|
|
53
|
-
var CANONICAL_FABLE_MODEL = "claude-fable-5";
|
|
53
|
+
var CANONICAL_FABLE_MODEL = "claude-fable-5-1";
|
|
54
54
|
var CANONICAL_OPUS_MODEL = "claude-opus-5";
|
|
55
55
|
var CANONICAL_SONNET_MODEL = "claude-sonnet-5";
|
|
56
56
|
var CANONICAL_HAIKU_MODEL = "claude-haiku-4-5";
|
|
@@ -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
|
|
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
|
|
783
|
-
if (
|
|
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
|
|
788
|
-
if (
|
|
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,
|
|
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,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
startProxyServer
|
|
4
|
-
} from "./cli-
|
|
4
|
+
} from "./cli-0zhb8ss4.js";
|
|
5
5
|
import"./cli-5jxyma6z.js";
|
|
6
6
|
import"./cli-sry5aqdj.js";
|
|
7
7
|
import"./cli-8yp89fan.js";
|
|
8
8
|
import {
|
|
9
9
|
resolveClaudeExecutableAsync
|
|
10
|
-
} from "./cli-
|
|
10
|
+
} from "./cli-9e5cxp89.js";
|
|
11
11
|
import"./cli-khhjyk04.js";
|
|
12
12
|
import"./cli-vj9cv18n.js";
|
|
13
|
-
import"./cli-
|
|
13
|
+
import"./cli-sh8bzdwe.js";
|
|
14
14
|
import {
|
|
15
15
|
__require
|
|
16
16
|
} from "./cli-p9swy5t3.js";
|
|
@@ -60,7 +60,7 @@ See https://github.com/rynfar/meridian for full documentation.`);
|
|
|
60
60
|
process.exit(0);
|
|
61
61
|
}
|
|
62
62
|
if (args[0] === "profile") {
|
|
63
|
-
const { profileAdd, profileAddOauthToken, profileList, profileRemove, profileSwitch, profileLogin, profileHelp } = await import("./profileCli-
|
|
63
|
+
const { profileAdd, profileAddOauthToken, profileList, profileRemove, profileSwitch, profileLogin, profileHelp } = await import("./profileCli-1eecbmgk.js");
|
|
64
64
|
const subcommand = args[1];
|
|
65
65
|
const profileId = args[2];
|
|
66
66
|
const headless = args.includes("--headless");
|
|
@@ -91,9 +91,9 @@ if (args[0] === "setup") {
|
|
|
91
91
|
MissingV2PluginError,
|
|
92
92
|
pluginPathForGeneration,
|
|
93
93
|
runSetup,
|
|
94
|
-
|
|
94
|
+
SUPPORTED_OPENCODE_V2_VERSIONS,
|
|
95
95
|
UnparseableConfigError
|
|
96
|
-
} = await import("./setup-
|
|
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
|
|
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
|
|
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-
|
|
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
|
+
};
|
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.
|
|
@@ -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,
|
|
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;
|
|
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,
|
|
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
|
@@ -119,7 +119,7 @@ export declare function isExtraUsageRequiredError(errMsg: string): boolean;
|
|
|
119
119
|
* collapses into a generic api_error.
|
|
120
120
|
*/
|
|
121
121
|
export interface SdkTermination {
|
|
122
|
-
reason: "max_turns" | "process_exit" | "aborted" | "upstream_idle" | "unknown";
|
|
122
|
+
reason: "max_turns" | "process_exit" | "aborted" | "upstream_idle" | "context_overflow" | "unknown";
|
|
123
123
|
/** Turn count when reason=max_turns and parseable. */
|
|
124
124
|
turns?: number;
|
|
125
125
|
/** Exit code when reason=process_exit and parseable. */
|
|
@@ -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;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"}
|