@rynfar/meridian 1.44.0 → 1.44.1
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/README.md +4 -2
- package/dist/{cli-fc6mt326.js → cli-6rezv582.js} +85 -18
- package/dist/cli.js +1 -1
- package/dist/env.d.ts +16 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/proxy/adapters/claudecode.d.ts.map +1 -1
- package/dist/proxy/adapters/droid.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/server.d.ts.map +1 -1
- package/dist/proxy/streamIdleGuard.d.ts +28 -0
- package/dist/proxy/streamIdleGuard.d.ts.map +1 -0
- package/dist/proxy/transforms/opencode.d.ts.map +1 -1
- package/dist/proxy/transforms/pi.d.ts.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -197,7 +197,7 @@ The system prompt controls are independent — any combination works:
|
|
|
197
197
|
|
|
198
198
|
The core question is **who executes the tools** — the SDK or the client?
|
|
199
199
|
|
|
200
|
-
- **Passthrough mode** (default for OpenCode) — Claude generates tool calls, but Meridian captures them and sends them back to the client for execution. The client runs the tool using its own implementation, with its own sandboxing, file tracking, and UI, then sends the result in the next request. This is how OpenCode, oh-my-opencagent (OMO), and most coding agents work — they have their own read/write/bash tools and need to stay in control of what runs on the user's machine.
|
|
200
|
+
- **Passthrough mode** (default for OpenCode and Pi) — Claude generates tool calls, but Meridian captures them and sends them back to the client for execution. The client runs the tool using its own implementation, with its own sandboxing, file tracking, and UI, then sends the result in the next request. This is how OpenCode, oh-my-opencagent (OMO), and most coding agents work — they have their own read/write/bash tools and need to stay in control of what runs on the user's machine.
|
|
201
201
|
- **Internal mode** — Claude Code handles everything. The SDK executes tools directly on the host, runs its full agent loop, and returns the final result. This is for clients that are purely chat interfaces (Open WebUI, simple API consumers) with no tool execution of their own.
|
|
202
202
|
|
|
203
203
|
Most users don't need to configure anything — the adapter sets the right mode automatically. To override:
|
|
@@ -526,6 +526,8 @@ Pi uses the `@mariozechner/pi-ai` library which supports a configurable `baseUrl
|
|
|
526
526
|
|
|
527
527
|
Pi mimics Claude Code's User-Agent, so automatic detection isn't possible. The `x-meridian-agent: pi` header in the config above tells Meridian to use the Pi adapter. Alternatively, if Pi is your only agent, you can set `MERIDIAN_DEFAULT_AGENT=pi` as an env var instead.
|
|
528
528
|
|
|
529
|
+
Pi runs in passthrough mode by default — it executes its own tools and Meridian just forwards the `tool_use` blocks. Opt out with `MERIDIAN_PASSTHROUGH=0`.
|
|
530
|
+
|
|
529
531
|
### Claude Code
|
|
530
532
|
|
|
531
533
|
Claude Code can point at Meridian like any other Anthropic API client. The
|
|
@@ -573,7 +575,7 @@ export ANTHROPIC_BASE_URL=http://127.0.0.1:3456
|
|
|
573
575
|
| [Cline](https://github.com/cline/cline) | ✅ Verified | Config (see above) — full tool support, file read/write/edit, bash, session resume |
|
|
574
576
|
| [Aider](https://github.com/paul-gauthier/aider) | ✅ Verified | Env vars — file editing, streaming; `--no-stream` broken (litellm bug) |
|
|
575
577
|
| [Open WebUI](https://github.com/open-webui/open-webui) | ✅ Verified | OpenAI-compatible endpoints — set base URL to `http://127.0.0.1:3456` |
|
|
576
|
-
| [Pi](https://github.com/mariozechner/pi-coding-agent) | ✅ Verified | models.json config (see above) —
|
|
578
|
+
| [Pi](https://github.com/mariozechner/pi-coding-agent) | ✅ Verified | models.json config (see above) — full tool support via passthrough; detected via `x-meridian-agent: pi` header |
|
|
577
579
|
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | ✅ Verified | `ANTHROPIC_BASE_URL` — remote clients share a Max subscription over the network; client CWD preserved in system prompt |
|
|
578
580
|
| [Continue](https://github.com/continuedev/continue) | 🔲 Untested | OpenAI-compatible endpoints should work — set `apiBase` to `http://127.0.0.1:3456` |
|
|
579
581
|
|
|
@@ -3753,6 +3753,57 @@ class RateLimitStore {
|
|
|
3753
3753
|
}
|
|
3754
3754
|
var rateLimitStore = new RateLimitStore;
|
|
3755
3755
|
|
|
3756
|
+
// src/proxy/streamIdleGuard.ts
|
|
3757
|
+
class UpstreamIdleError extends Error {
|
|
3758
|
+
idleMs;
|
|
3759
|
+
sinceLastMs;
|
|
3760
|
+
constructor(idleMs, sinceLastMs) {
|
|
3761
|
+
super(`upstream idle for ${sinceLastMs}ms (limit ${idleMs}ms)`);
|
|
3762
|
+
this.name = "UpstreamIdleError";
|
|
3763
|
+
this.idleMs = idleMs;
|
|
3764
|
+
this.sinceLastMs = sinceLastMs;
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
async function* guardUpstreamIdle(source, idleMs, onStall) {
|
|
3768
|
+
if (idleMs <= 0) {
|
|
3769
|
+
yield* source;
|
|
3770
|
+
return;
|
|
3771
|
+
}
|
|
3772
|
+
const it = source[Symbol.asyncIterator]();
|
|
3773
|
+
let lastAt = Date.now();
|
|
3774
|
+
try {
|
|
3775
|
+
while (true) {
|
|
3776
|
+
const nextP = it.next();
|
|
3777
|
+
nextP.catch(() => {});
|
|
3778
|
+
let timer;
|
|
3779
|
+
const idle = new Promise((_, reject) => {
|
|
3780
|
+
const remaining = Math.max(0, idleMs - (Date.now() - lastAt));
|
|
3781
|
+
timer = setTimeout(() => {
|
|
3782
|
+
const sinceLastMs = Date.now() - lastAt;
|
|
3783
|
+
try {
|
|
3784
|
+
onStall?.(sinceLastMs);
|
|
3785
|
+
} catch {}
|
|
3786
|
+
reject(new UpstreamIdleError(idleMs, sinceLastMs));
|
|
3787
|
+
}, remaining);
|
|
3788
|
+
});
|
|
3789
|
+
let res;
|
|
3790
|
+
try {
|
|
3791
|
+
res = await Promise.race([nextP, idle]);
|
|
3792
|
+
} finally {
|
|
3793
|
+
if (timer)
|
|
3794
|
+
clearTimeout(timer);
|
|
3795
|
+
}
|
|
3796
|
+
if (res.done)
|
|
3797
|
+
return;
|
|
3798
|
+
lastAt = Date.now();
|
|
3799
|
+
yield res.value;
|
|
3800
|
+
}
|
|
3801
|
+
} finally {
|
|
3802
|
+
const returnP = it.return?.(undefined);
|
|
3803
|
+
returnP?.catch(() => {});
|
|
3804
|
+
}
|
|
3805
|
+
}
|
|
3806
|
+
|
|
3756
3807
|
// src/proxy/oauthUsage.ts
|
|
3757
3808
|
var OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
3758
3809
|
var OAUTH_BETA_HEADER = "oauth-2025-04-20";
|
|
@@ -3906,6 +3957,14 @@ function envBool(suffix) {
|
|
|
3906
3957
|
const val = env(suffix);
|
|
3907
3958
|
return val === "1" || val === "true" || val === "yes";
|
|
3908
3959
|
}
|
|
3960
|
+
function resolvePassthrough(defaultValue) {
|
|
3961
|
+
const val = env("PASSTHROUGH");
|
|
3962
|
+
if (val === "1" || val === "true" || val === "yes")
|
|
3963
|
+
return true;
|
|
3964
|
+
if (val === "0" || val === "false" || val === "no")
|
|
3965
|
+
return false;
|
|
3966
|
+
return defaultValue;
|
|
3967
|
+
}
|
|
3909
3968
|
function envInt(suffix, defaultValue) {
|
|
3910
3969
|
const val = env(suffix);
|
|
3911
3970
|
if (!val)
|
|
@@ -9981,8 +10040,7 @@ var openCodeTransforms = [
|
|
|
9981
10040
|
const incompatibleTools = CLAUDE_CODE_ONLY_TOOLS;
|
|
9982
10041
|
const allowedMcpTools = ALLOWED_MCP_TOOLS;
|
|
9983
10042
|
const coreToolNames = ["read", "write", "edit", "bash", "glob", "grep"];
|
|
9984
|
-
const
|
|
9985
|
-
const passthrough = !(envVal === "0" || envVal === "false" || envVal === "no");
|
|
10043
|
+
const passthrough = resolvePassthrough(true);
|
|
9986
10044
|
let sdkAgents = {};
|
|
9987
10045
|
if (Array.isArray(body.tools)) {
|
|
9988
10046
|
const taskTool = body.tools.find((t) => t.name === "task" || t.name === "Task");
|
|
@@ -10077,11 +10135,7 @@ var openCodeAdapter = {
|
|
|
10077
10135
|
return ["read", "write", "edit", "bash", "glob", "grep"];
|
|
10078
10136
|
},
|
|
10079
10137
|
usesPassthrough() {
|
|
10080
|
-
|
|
10081
|
-
if (envVal === "0" || envVal === "false" || envVal === "no") {
|
|
10082
|
-
return false;
|
|
10083
|
-
}
|
|
10084
|
-
return true;
|
|
10138
|
+
return resolvePassthrough(true);
|
|
10085
10139
|
},
|
|
10086
10140
|
supportsThinking() {
|
|
10087
10141
|
return true;
|
|
@@ -10152,8 +10206,7 @@ var DROID_ALLOWED_MCP_TOOLS = [
|
|
|
10152
10206
|
`mcp__${DROID_MCP_SERVER_NAME}__grep`
|
|
10153
10207
|
];
|
|
10154
10208
|
function resolveDroidPassthrough() {
|
|
10155
|
-
|
|
10156
|
-
return envVal === "1" || envVal === "true" || envVal === "yes";
|
|
10209
|
+
return resolvePassthrough(false);
|
|
10157
10210
|
}
|
|
10158
10211
|
var droidTransforms = [
|
|
10159
10212
|
{
|
|
@@ -10237,8 +10290,7 @@ var droidAdapter = {
|
|
|
10237
10290
|
return "";
|
|
10238
10291
|
},
|
|
10239
10292
|
usesPassthrough() {
|
|
10240
|
-
|
|
10241
|
-
return envVal === "1" || envVal === "true" || envVal === "yes";
|
|
10293
|
+
return resolvePassthrough(false);
|
|
10242
10294
|
}
|
|
10243
10295
|
};
|
|
10244
10296
|
|
|
@@ -10459,6 +10511,9 @@ var PI_ALLOWED_MCP_TOOLS = [
|
|
|
10459
10511
|
`mcp__${PI_MCP_SERVER_NAME}__glob`,
|
|
10460
10512
|
`mcp__${PI_MCP_SERVER_NAME}__grep`
|
|
10461
10513
|
];
|
|
10514
|
+
function resolvePiPassthrough() {
|
|
10515
|
+
return resolvePassthrough(true);
|
|
10516
|
+
}
|
|
10462
10517
|
var piTransforms = [
|
|
10463
10518
|
{
|
|
10464
10519
|
name: "pi-core",
|
|
@@ -10481,6 +10536,7 @@ var piTransforms = [
|
|
|
10481
10536
|
incompatibleTools: CLAUDE_CODE_ONLY_TOOLS,
|
|
10482
10537
|
allowedMcpTools: PI_ALLOWED_MCP_TOOLS,
|
|
10483
10538
|
sdkAgents: {},
|
|
10539
|
+
passthrough: resolvePiPassthrough(),
|
|
10484
10540
|
supportsThinking: true,
|
|
10485
10541
|
extractFileChangesFromToolUse
|
|
10486
10542
|
};
|
|
@@ -10549,6 +10605,9 @@ var piAdapter = {
|
|
|
10549
10605
|
buildSystemContextAddendum(_body, _sdkAgents) {
|
|
10550
10606
|
return "";
|
|
10551
10607
|
},
|
|
10608
|
+
usesPassthrough() {
|
|
10609
|
+
return resolvePassthrough(true);
|
|
10610
|
+
},
|
|
10552
10611
|
extractFileChangesFromToolUse(toolName, toolInput) {
|
|
10553
10612
|
const input = toolInput;
|
|
10554
10613
|
const filePath = input?.filePath ?? input?.file_path ?? input?.path;
|
|
@@ -10718,11 +10777,7 @@ var claudeCodeAdapter = {
|
|
|
10718
10777
|
return ["Read", "Write", "Edit", "Bash", "Glob", "Grep"];
|
|
10719
10778
|
},
|
|
10720
10779
|
usesPassthrough() {
|
|
10721
|
-
|
|
10722
|
-
if (envVal === "0" || envVal === "false" || envVal === "no") {
|
|
10723
|
-
return false;
|
|
10724
|
-
}
|
|
10725
|
-
return true;
|
|
10780
|
+
return resolvePassthrough(true);
|
|
10726
10781
|
},
|
|
10727
10782
|
supportsThinking() {
|
|
10728
10783
|
return true;
|
|
@@ -17525,6 +17580,7 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
|
|
|
17525
17580
|
// src/proxy/server.ts
|
|
17526
17581
|
var exec2 = promisify2(execCallback);
|
|
17527
17582
|
var claudeExecutable = "";
|
|
17583
|
+
var UPSTREAM_IDLE_MS = 90000;
|
|
17528
17584
|
function credentialStoreForProfile(profile) {
|
|
17529
17585
|
if (profile.type !== "claude-max")
|
|
17530
17586
|
return;
|
|
@@ -18799,8 +18855,15 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18799
18855
|
const taskToolJsonBuffer = new Map;
|
|
18800
18856
|
let nextClientBlockIndex = 0;
|
|
18801
18857
|
const sdkToClientIndex = new Map;
|
|
18858
|
+
const guardedResponse = guardUpstreamIdle(response, UPSTREAM_IDLE_MS, (sinceLastMs) => claudeLog("upstream.stalled", {
|
|
18859
|
+
mode: "stream",
|
|
18860
|
+
model,
|
|
18861
|
+
sinceLastMs,
|
|
18862
|
+
streamEventsSeen,
|
|
18863
|
+
firstChunkAt: firstChunkAt ?? null
|
|
18864
|
+
}));
|
|
18802
18865
|
try {
|
|
18803
|
-
for await (const message of
|
|
18866
|
+
for await (const message of guardedResponse) {
|
|
18804
18867
|
if (streamClosed) {
|
|
18805
18868
|
break;
|
|
18806
18869
|
}
|
|
@@ -19157,7 +19220,11 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
19157
19220
|
error: errMsg,
|
|
19158
19221
|
...stderrOutput ? { stderr: stderrOutput } : {}
|
|
19159
19222
|
});
|
|
19160
|
-
const streamErr =
|
|
19223
|
+
const streamErr = error instanceof UpstreamIdleError ? {
|
|
19224
|
+
status: 504,
|
|
19225
|
+
type: "upstream_timeout",
|
|
19226
|
+
message: `Upstream stalled: no data for ${error.sinceLastMs}ms`
|
|
19227
|
+
} : classifyError(errMsg);
|
|
19161
19228
|
claudeLog("proxy.anthropic.error", { error: errMsg, classified: streamErr.type });
|
|
19162
19229
|
const sdkTerm = extractSdkTermination(errMsg);
|
|
19163
19230
|
const canRecoverAsToolUse = sdkTerm.reason === "max_turns" && passthrough && capturedToolUses.length > 0 && messageStartEmitted;
|
package/dist/cli.js
CHANGED
package/dist/env.d.ts
CHANGED
|
@@ -17,6 +17,22 @@ export declare function envOr(suffix: string, defaultValue: string): string;
|
|
|
17
17
|
* Resolve a boolean env var (truthy = "1", "true", "yes").
|
|
18
18
|
*/
|
|
19
19
|
export declare function envBool(suffix: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Resolve passthrough mode from the env var (`MERIDIAN_PASSTHROUGH`, falling
|
|
22
|
+
* back to `CLAUDE_PROXY_PASSTHROUGH`), applying an adapter-specific default
|
|
23
|
+
* when the var is unset or holds an unrecognized value.
|
|
24
|
+
*
|
|
25
|
+
* Recognized: "1"/"true"/"yes" → true, "0"/"false"/"no" → false.
|
|
26
|
+
*
|
|
27
|
+
* This is the single source of truth for passthrough parsing. Each adapter's
|
|
28
|
+
* `usesPassthrough()` and its transform both call it with the same default, so
|
|
29
|
+
* they can never drift (the transform-parity tests enforce the pairing).
|
|
30
|
+
*
|
|
31
|
+
* @param defaultValue used when neither env var is set (or is unrecognized).
|
|
32
|
+
* Passthrough-by-default adapters pass `true` (opt out with `=0`); opt-in
|
|
33
|
+
* adapters pass `false` (opt in with `=1`).
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolvePassthrough(defaultValue: boolean): boolean;
|
|
20
36
|
/**
|
|
21
37
|
* Resolve an integer env var with a default.
|
|
22
38
|
*/
|
package/dist/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEtD;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAG/C;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAKnE"}
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEtD;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAG/C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAKjE;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAKnE"}
|
|
@@ -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;
|
|
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,eAAO,MAAM,iBAAiB,EAAE,YA0F/B,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"droid.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/droid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"droid.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/droid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAyC9C,eAAO,MAAM,YAAY,EAAE,YAkF1B,CAAA;AAED,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,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,EAAE,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,EAAE,MAAM,YAAY,CAAA;AAS9C,eAAO,MAAM,eAAe,EAAE,YA0H7B,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;
|
|
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;AAyC9C,eAAO,MAAM,SAAS,EAAE,YA2HvB,CAAA;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AAGvD,YAAY,EACV,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAA;AAKpB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAiCnG,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EAEpB,KAAK,aAAa,EAGnB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAGzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;AAiQ7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CA8qFhF;AAWD,wBAAgB,gCAAgC,IAAI,IAAI,CAavD;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmGhG"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upstream-idle guard for proxied model streams.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the provider SDK's streaming async iterable and enforces a maximum gap
|
|
5
|
+
* between *real* upstream messages. If the source goes silent for longer than
|
|
6
|
+
* `idleMs` — before the first chunk (slow TTFB) or mid-stream — the guard
|
|
7
|
+
* aborts iteration and throws `UpstreamIdleError`.
|
|
8
|
+
*
|
|
9
|
+
* Why this is needed: the proxy emits downstream SSE heartbeats (`: ping`) on a
|
|
10
|
+
* fixed interval, which resets the *client's* (pi's) byte-level idle timer. A
|
|
11
|
+
* stalled upstream is therefore invisible to the client and would wedge the
|
|
12
|
+
* turn forever. This guard is the authoritative upstream-liveness check.
|
|
13
|
+
*
|
|
14
|
+
* COORDINATION CONTRACT (Pylon Orchestrator): this guard owns *model-stream*
|
|
15
|
+
* liveness. Pylon's runtime stall watchdog is only a BACKSTOP for the
|
|
16
|
+
* model-wait gap with no tool in flight, and keeps its abort threshold above
|
|
17
|
+
* this guard's idle limit (default MERIDIAN_IDLE_TIMEOUT_SECONDS = 120s) so the
|
|
18
|
+
* two layers never race to abort the same hung model. If this default rises,
|
|
19
|
+
* re-check Pylon's STALL_ABORT_MS. See
|
|
20
|
+
* pylon-orchestrator/docs/circuit/specs/stall-watchdog-tool-exempt.md.
|
|
21
|
+
*/
|
|
22
|
+
export declare class UpstreamIdleError extends Error {
|
|
23
|
+
readonly idleMs: number;
|
|
24
|
+
readonly sinceLastMs: number;
|
|
25
|
+
constructor(idleMs: number, sinceLastMs: number);
|
|
26
|
+
}
|
|
27
|
+
export declare function guardUpstreamIdle<T>(source: AsyncIterable<T>, idleMs: number, onStall?: (sinceLastMs: number) => void): AsyncGenerator<T>;
|
|
28
|
+
//# sourceMappingURL=streamIdleGuard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streamIdleGuard.d.ts","sourceRoot":"","sources":["../../src/proxy/streamIdleGuard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;gBAChB,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;CAMhD;AAED,wBAAuB,iBAAiB,CAAC,CAAC,EACxC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EACxB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,GACtC,cAAc,CAAC,CAAC,CAAC,CA4CnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../../src/proxy/transforms/opencode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../../src/proxy/transforms/opencode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,cAAc,CAAA;AAO7D,eAAO,MAAM,kBAAkB,EAAE,SAAS,EA0FzC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../../../src/proxy/transforms/pi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../../../src/proxy/transforms/pi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,cAAc,CAAA;AA2B7D,eAAO,MAAM,YAAY,EAAE,SAAS,EA0BnC,CAAA"}
|
package/dist/server.js
CHANGED
package/package.json
CHANGED