claudish 7.51.0 → 7.52.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/bin/claudish.cjs +80 -4
- package/dist/index.js +433 -92
- package/package.json +5 -5
package/bin/claudish.cjs
CHANGED
|
@@ -3,8 +3,38 @@
|
|
|
3
3
|
// Launcher script: checks for Bun runtime before starting claudish.
|
|
4
4
|
// Claudish uses Bun-specific APIs (bun:ffi for TUI, Bun.spawn, etc.)
|
|
5
5
|
// so it cannot run under Node.js directly.
|
|
6
|
+
//
|
|
7
|
+
// ── Why this is spawn() and not spawnSync() ──────────────────────────────────
|
|
8
|
+
//
|
|
9
|
+
// This process is a THIN WRAPPER around the real CLI, and it is the process
|
|
10
|
+
// every caller's signal actually lands on. It used to run the child with
|
|
11
|
+
// `spawnSync(bun, ..., { stdio: "inherit" })`, which made the whole tree
|
|
12
|
+
// unkillable in a way that looked like the child was ignoring signals:
|
|
13
|
+
//
|
|
14
|
+
// · `spawnSync` blocks the event loop, so this process cannot forward
|
|
15
|
+
// anything while the child runs.
|
|
16
|
+
// · `stdio: "inherit"` gives the Bun child THIS process's fd 0/1/2. When a
|
|
17
|
+
// caller pipes our stdout, the Bun child holds the write end of that pipe.
|
|
18
|
+
// · So SIGTERM killed only this launcher. The Bun child was ORPHANED, not
|
|
19
|
+
// killed — it kept running, kept billing, and kept the caller's pipe open,
|
|
20
|
+
// writing into it long after the caller had given up.
|
|
21
|
+
//
|
|
22
|
+
// Measured 2026-08-15 with a repro of this exact topology: SIGTERM to the
|
|
23
|
+
// launcher pid leaked 330 B of post-kill output, and so did SIGKILL — because
|
|
24
|
+
// the problem is signalling the wrong process, not a stubborn one. It cost a
|
|
25
|
+
// real `team` run: a model declared TIMEOUT with 0 B wrote a complete 40,699 B
|
|
26
|
+
// answer into the response file 375 s later, and was scored as empty.
|
|
27
|
+
//
|
|
28
|
+
// Now: async spawn, forward the signals a supervisor actually sends, and exit
|
|
29
|
+
// with the child's own status. A caller that terminates `claudish` terminates
|
|
30
|
+
// claudish.
|
|
31
|
+
//
|
|
32
|
+
// This does NOT cover being SIGKILLed ourselves — nothing running in this
|
|
33
|
+
// process can. Callers that need a hard guarantee should spawn claudish
|
|
34
|
+
// `detached` and signal the process GROUP, which reaches the Bun child (and its
|
|
35
|
+
// own descendants) directly; `team-orchestrator.ts` does exactly that.
|
|
6
36
|
|
|
7
|
-
const { execFileSync, execSync } = require("node:child_process");
|
|
37
|
+
const { execFileSync, execSync, spawn } = require("node:child_process");
|
|
8
38
|
const { resolve } = require("node:path");
|
|
9
39
|
|
|
10
40
|
/**
|
|
@@ -75,18 +105,64 @@ Learn more: https://bun.sh`);
|
|
|
75
105
|
process.exit(1);
|
|
76
106
|
}
|
|
77
107
|
|
|
78
|
-
// Exec into bun with the real entry point
|
|
79
108
|
const entry = resolve(__dirname, "..", "dist", "index.js");
|
|
109
|
+
|
|
110
|
+
let child;
|
|
80
111
|
try {
|
|
81
|
-
|
|
112
|
+
child = spawn(bun, [entry, ...process.argv.slice(2)], {
|
|
82
113
|
stdio: "inherit",
|
|
83
114
|
env: process.env,
|
|
84
115
|
});
|
|
85
|
-
process.exit(result.status ?? 1);
|
|
86
116
|
} catch (err) {
|
|
87
117
|
console.error("Failed to start claudish:", err.message);
|
|
88
118
|
process.exit(1);
|
|
89
119
|
}
|
|
120
|
+
|
|
121
|
+
// Forward the signals a supervisor, terminal, or parent process actually sends.
|
|
122
|
+
// SIGKILL is deliberately absent — it cannot be caught, which is the whole
|
|
123
|
+
// reason callers wanting a hard kill must target the process group instead.
|
|
124
|
+
//
|
|
125
|
+
// Handlers are installed only AFTER a successful spawn, so a signal arriving
|
|
126
|
+
// during startup keeps Node's default terminate-immediately behaviour rather
|
|
127
|
+
// than being swallowed by a forward to a child that does not exist yet.
|
|
128
|
+
const FORWARDED =
|
|
129
|
+
process.platform === "win32" ? ["SIGINT", "SIGTERM"] : ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
130
|
+
for (const signal of FORWARDED) {
|
|
131
|
+
process.on(signal, () => {
|
|
132
|
+
// Best-effort: the child may have exited between the signal and this line.
|
|
133
|
+
try {
|
|
134
|
+
if (!child.killed) child.kill(signal);
|
|
135
|
+
} catch {}
|
|
136
|
+
// Do NOT exit here. Waiting for the child's own "exit" keeps our status
|
|
137
|
+
// honest and, more importantly, keeps this process alive as long as the
|
|
138
|
+
// child is — a caller watching OUR pid must not see us disappear while the
|
|
139
|
+
// real work is still running.
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
child.on("error", (err) => {
|
|
144
|
+
console.error("Failed to start claudish:", err.message);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
child.on("exit", (code, signal) => {
|
|
149
|
+
if (signal) {
|
|
150
|
+
// Re-raise the signal on ourselves so our parent observes the same cause
|
|
151
|
+
// of death the child had, rather than a synthesised exit code. The handler
|
|
152
|
+
// is removed first, or we would forward it straight back to a dead child.
|
|
153
|
+
process.removeAllListeners(signal);
|
|
154
|
+
try {
|
|
155
|
+
process.kill(process.pid, signal);
|
|
156
|
+
return;
|
|
157
|
+
} catch {
|
|
158
|
+
// Re-raise failed (unsupported signal on this platform) — fall back to
|
|
159
|
+
// the shell convention for "died from signal N".
|
|
160
|
+
const NUMBERS = { SIGINT: 2, SIGQUIT: 3, SIGKILL: 9, SIGTERM: 15, SIGHUP: 1 };
|
|
161
|
+
process.exit(128 + (NUMBERS[signal] ?? 0));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
process.exit(code ?? 1);
|
|
165
|
+
});
|
|
90
166
|
}
|
|
91
167
|
|
|
92
168
|
// Running it as a program launches. Requiring it exposes the helpers so the
|
package/dist/index.js
CHANGED
|
@@ -729,7 +729,7 @@ var init_onepassword_config = __esm(() => {
|
|
|
729
729
|
});
|
|
730
730
|
|
|
731
731
|
// src/version.ts
|
|
732
|
-
var VERSION = "7.
|
|
732
|
+
var VERSION = "7.52.0";
|
|
733
733
|
|
|
734
734
|
// src/logger.ts
|
|
735
735
|
var exports_logger = {};
|
|
@@ -30776,6 +30776,7 @@ class AntigravityProviderTransport {
|
|
|
30776
30776
|
if (!data?.buckets?.length)
|
|
30777
30777
|
return;
|
|
30778
30778
|
const lines = [];
|
|
30779
|
+
let sawThrottledModel = false;
|
|
30779
30780
|
for (const bucket of data.buckets) {
|
|
30780
30781
|
if (!bucket.modelId)
|
|
30781
30782
|
continue;
|
|
@@ -30784,15 +30785,24 @@ class AntigravityProviderTransport {
|
|
|
30784
30785
|
hour: "2-digit",
|
|
30785
30786
|
minute: "2-digit"
|
|
30786
30787
|
}) : "?";
|
|
30787
|
-
|
|
30788
|
+
const mine = this.isThrottledModel(bucket.modelId);
|
|
30789
|
+
if (mine)
|
|
30790
|
+
sawThrottledModel = true;
|
|
30791
|
+
lines.push(` ${mine ? "> " : " "}${bucket.modelId}: ${pct} remaining (resets ${reset})`);
|
|
30788
30792
|
}
|
|
30789
|
-
if (lines.length
|
|
30790
|
-
|
|
30793
|
+
if (lines.length === 0)
|
|
30794
|
+
return;
|
|
30795
|
+
const requested = this.servedModelName && this.servedModelName !== this.modelName ? `${this.modelName} (served as ${this.servedModelName})` : this.modelName;
|
|
30796
|
+
const header = sawThrottledModel ? `[Antigravity] Quota status (> = ${requested}):` : `[Antigravity] Quota status \u2014 NOTE: ${requested} has no quota bucket below, ` + "so these figures do not explain its rate limit:";
|
|
30797
|
+
logStderr(`${header}
|
|
30791
30798
|
${lines.join(`
|
|
30792
30799
|
`)}`);
|
|
30793
|
-
}
|
|
30794
30800
|
} catch {}
|
|
30795
30801
|
}
|
|
30802
|
+
isThrottledModel(bucketModelId) {
|
|
30803
|
+
const id = bucketModelId.toLowerCase();
|
|
30804
|
+
return id === this.modelName.toLowerCase() || id === this.servedModelName.toLowerCase();
|
|
30805
|
+
}
|
|
30796
30806
|
async getQuotaRemaining(modelName) {
|
|
30797
30807
|
if (!this.accessToken || !this.projectId)
|
|
30798
30808
|
return;
|
|
@@ -32675,12 +32685,14 @@ var init_deepseek_model_dialect = __esm(() => {
|
|
|
32675
32685
|
}
|
|
32676
32686
|
applyNativeReasoning(request, originalRequest) {
|
|
32677
32687
|
const effort = this.resolveEffortLevel(originalRequest);
|
|
32678
|
-
if (effort && this.
|
|
32688
|
+
if (effort && this.acceptsReasoningControls()) {
|
|
32679
32689
|
if (effort === "none" || effort === "minimal") {
|
|
32680
32690
|
request.thinking = { type: "disabled" };
|
|
32681
32691
|
log(`[DeepSeekModelDialect] effort ${effort} -> thinking.type: disabled for ${this.modelId}`);
|
|
32682
32692
|
} else {
|
|
32683
|
-
const
|
|
32693
|
+
const reasoning = this.lookupReasoningCapability();
|
|
32694
|
+
const clamped = reasoning?.control === "effort" && reasoning.efforts?.length ? this.clampToAdvertisedEffort(effort, reasoning) : undefined;
|
|
32695
|
+
const value = clamped ?? (effort === "xhigh" || effort === "max" ? "max" : "high");
|
|
32684
32696
|
request.reasoning_effort = value;
|
|
32685
32697
|
log(`[DeepSeekModelDialect] effort ${effort} -> reasoning_effort: ${value} for ${this.modelId}`);
|
|
32686
32698
|
}
|
|
@@ -32692,9 +32704,15 @@ var init_deepseek_model_dialect = __esm(() => {
|
|
|
32692
32704
|
}
|
|
32693
32705
|
return request;
|
|
32694
32706
|
}
|
|
32695
|
-
|
|
32707
|
+
acceptsReasoningControls() {
|
|
32708
|
+
const reasoning = this.lookupReasoningCapability();
|
|
32709
|
+
if (reasoning)
|
|
32710
|
+
return reasoning.supported === true && reasoning.control === "effort";
|
|
32696
32711
|
const model = this.modelId.toLowerCase();
|
|
32697
|
-
|
|
32712
|
+
if (model.includes("deepseek-chat") || model.includes("deepseek-reasoner"))
|
|
32713
|
+
return true;
|
|
32714
|
+
const version2 = /(?:^|[^a-z0-9])v(\d+)/.exec(model);
|
|
32715
|
+
return version2 ? Number(version2[1]) >= 4 : false;
|
|
32698
32716
|
}
|
|
32699
32717
|
shouldHandle(modelId) {
|
|
32700
32718
|
return matchesModelFamily(modelId, "deepseek");
|
|
@@ -34105,11 +34123,87 @@ var init_glm_model_dialect = __esm(() => {
|
|
|
34105
34123
|
};
|
|
34106
34124
|
});
|
|
34107
34125
|
|
|
34126
|
+
// src/adapters/grok-effort-support.ts
|
|
34127
|
+
function acceptsReasoningEffort(modelId) {
|
|
34128
|
+
const id = norm(modelId);
|
|
34129
|
+
if (!id)
|
|
34130
|
+
return false;
|
|
34131
|
+
if (learnedRejects.has(id))
|
|
34132
|
+
return false;
|
|
34133
|
+
const reasoning = lookupModelReasoning(id) ?? lookupModelReasoning(modelId);
|
|
34134
|
+
if (reasoning)
|
|
34135
|
+
return reasoning.supported === true && reasoning.control === "effort";
|
|
34136
|
+
return !SEED_REJECTS.some((re) => re.test(id));
|
|
34137
|
+
}
|
|
34138
|
+
function catalogReasoningFor(modelId) {
|
|
34139
|
+
return lookupModelReasoning(norm(modelId)) ?? lookupModelReasoning(modelId);
|
|
34140
|
+
}
|
|
34141
|
+
function rememberReasoningEffortRejected(modelId) {
|
|
34142
|
+
const id = norm(modelId);
|
|
34143
|
+
if (!id || learnedRejects.has(id))
|
|
34144
|
+
return;
|
|
34145
|
+
learnedRejects.add(id);
|
|
34146
|
+
log(`[GrokEffortSupport] ${id} rejected reasoning_effort \u2014 not sending it again this session.`);
|
|
34147
|
+
}
|
|
34148
|
+
function isReasoningEffortRejection(errorText) {
|
|
34149
|
+
if (!errorText)
|
|
34150
|
+
return false;
|
|
34151
|
+
return /does not support parameter\s+`?reasoning[_]?effort`?/i.test(errorText);
|
|
34152
|
+
}
|
|
34153
|
+
function acceptsReasoningEffortValue(modelId, value) {
|
|
34154
|
+
return !learnedValueRejects.has(pairKey(modelId, value));
|
|
34155
|
+
}
|
|
34156
|
+
function rememberReasoningEffortValueRejected(modelId, value) {
|
|
34157
|
+
const key = pairKey(modelId, value);
|
|
34158
|
+
if (learnedValueRejects.has(key))
|
|
34159
|
+
return;
|
|
34160
|
+
learnedValueRejects.add(key);
|
|
34161
|
+
log(`[GrokEffortSupport] ${norm(modelId)} rejected reasoning_effort value "${value}" \u2014 ` + "falling back and not sending it again this session.");
|
|
34162
|
+
}
|
|
34163
|
+
function rejectedReasoningEffortValue(errorText) {
|
|
34164
|
+
if (!errorText)
|
|
34165
|
+
return null;
|
|
34166
|
+
const m = /does not support\s+`?reasoning[_]?effort`?\s+value\s+`?([a-z]+)`?/i.exec(errorText);
|
|
34167
|
+
return m ? m[1].toLowerCase() : null;
|
|
34168
|
+
}
|
|
34169
|
+
function fallbackReasoningEffortValue(value) {
|
|
34170
|
+
switch (value.toLowerCase()) {
|
|
34171
|
+
case "none":
|
|
34172
|
+
return "low";
|
|
34173
|
+
case "minimal":
|
|
34174
|
+
return "low";
|
|
34175
|
+
case "low":
|
|
34176
|
+
return null;
|
|
34177
|
+
case "medium":
|
|
34178
|
+
return "low";
|
|
34179
|
+
case "high":
|
|
34180
|
+
return "medium";
|
|
34181
|
+
default:
|
|
34182
|
+
return null;
|
|
34183
|
+
}
|
|
34184
|
+
}
|
|
34185
|
+
var SEED_REJECTS, learnedRejects, norm = (modelId) => modelId.trim().toLowerCase().replace(/^x-ai\//, ""), learnedValueRejects, pairKey = (modelId, value) => `${norm(modelId)}\x00${value.toLowerCase()}`;
|
|
34186
|
+
var init_grok_effort_support = __esm(() => {
|
|
34187
|
+
init_logger();
|
|
34188
|
+
init_model_catalog();
|
|
34189
|
+
SEED_REJECTS = [
|
|
34190
|
+
/non-reasoning/i,
|
|
34191
|
+
/^grok-2/i,
|
|
34192
|
+
/^grok-4(?![.\d])(?!.*fast-reasoning)/i,
|
|
34193
|
+
/^grok-build-/i,
|
|
34194
|
+
/^grok-code-/i,
|
|
34195
|
+
/^grok-4\.20/i
|
|
34196
|
+
];
|
|
34197
|
+
learnedRejects = new Set;
|
|
34198
|
+
learnedValueRejects = new Set;
|
|
34199
|
+
});
|
|
34200
|
+
|
|
34108
34201
|
// src/adapters/grok-model-dialect.ts
|
|
34109
34202
|
var GrokModelDialect;
|
|
34110
34203
|
var init_grok_model_dialect = __esm(() => {
|
|
34111
34204
|
init_logger();
|
|
34112
34205
|
init_base_api_format();
|
|
34206
|
+
init_grok_effort_support();
|
|
34113
34207
|
init_model_catalog();
|
|
34114
34208
|
GrokModelDialect = class GrokModelDialect extends BaseAPIFormat {
|
|
34115
34209
|
xmlBuffer = "";
|
|
@@ -34172,34 +34266,78 @@ var init_grok_model_dialect = __esm(() => {
|
|
|
34172
34266
|
return request;
|
|
34173
34267
|
}
|
|
34174
34268
|
effortToReasoningEffort(effort) {
|
|
34175
|
-
|
|
34176
|
-
const isMini = model.includes("mini");
|
|
34177
|
-
const isGrok43 = /grok-4\.3(\b|[-.]|$)/.test(model);
|
|
34178
|
-
const isFastReasoning = model.includes("fast-reasoning");
|
|
34179
|
-
if (!(isMini || isGrok43 || isFastReasoning)) {
|
|
34269
|
+
if (!acceptsReasoningEffort(this.modelId)) {
|
|
34180
34270
|
return;
|
|
34181
34271
|
}
|
|
34182
|
-
|
|
34183
|
-
|
|
34184
|
-
|
|
34185
|
-
|
|
34186
|
-
|
|
34187
|
-
|
|
34188
|
-
|
|
34189
|
-
|
|
34272
|
+
let value;
|
|
34273
|
+
const reasoning = catalogReasoningFor(this.modelId);
|
|
34274
|
+
const clamped = reasoning ? this.clampToAdvertisedEffort(effort, reasoning) : undefined;
|
|
34275
|
+
if (clamped) {
|
|
34276
|
+
value = clamped;
|
|
34277
|
+
} else {
|
|
34278
|
+
const model = this.modelId.toLowerCase();
|
|
34279
|
+
const isMini = model.includes("mini");
|
|
34280
|
+
if (isMini) {
|
|
34281
|
+
switch (effort) {
|
|
34282
|
+
case "high":
|
|
34283
|
+
case "xhigh":
|
|
34284
|
+
case "max":
|
|
34285
|
+
value = "high";
|
|
34286
|
+
break;
|
|
34287
|
+
default:
|
|
34288
|
+
value = "low";
|
|
34289
|
+
}
|
|
34290
|
+
} else {
|
|
34291
|
+
switch (effort) {
|
|
34292
|
+
case "none":
|
|
34293
|
+
value = "none";
|
|
34294
|
+
break;
|
|
34295
|
+
case "minimal":
|
|
34296
|
+
case "low":
|
|
34297
|
+
value = "low";
|
|
34298
|
+
break;
|
|
34299
|
+
case "medium":
|
|
34300
|
+
value = "medium";
|
|
34301
|
+
break;
|
|
34302
|
+
default:
|
|
34303
|
+
value = "high";
|
|
34304
|
+
}
|
|
34190
34305
|
}
|
|
34191
34306
|
}
|
|
34192
|
-
|
|
34193
|
-
|
|
34194
|
-
|
|
34195
|
-
|
|
34196
|
-
|
|
34197
|
-
|
|
34198
|
-
|
|
34199
|
-
|
|
34200
|
-
|
|
34201
|
-
|
|
34307
|
+
let guard = 0;
|
|
34308
|
+
while (!acceptsReasoningEffortValue(this.modelId, value) && guard++ < 8) {
|
|
34309
|
+
const next = fallbackReasoningEffortValue(value);
|
|
34310
|
+
if (!next)
|
|
34311
|
+
return;
|
|
34312
|
+
value = next;
|
|
34313
|
+
}
|
|
34314
|
+
return value;
|
|
34315
|
+
}
|
|
34316
|
+
recoverFromRejection(payload, errorText) {
|
|
34317
|
+
if (!payload || payload.reasoning_effort === undefined)
|
|
34318
|
+
return null;
|
|
34319
|
+
if (isReasoningEffortRejection(errorText)) {
|
|
34320
|
+
rememberReasoningEffortRejected(this.modelId);
|
|
34321
|
+
const next = { ...payload };
|
|
34322
|
+
delete next.reasoning_effort;
|
|
34323
|
+
return { payload: next, note: `dropped reasoning_effort for ${this.modelId}` };
|
|
34324
|
+
}
|
|
34325
|
+
const rejected = rejectedReasoningEffortValue(errorText);
|
|
34326
|
+
if (rejected) {
|
|
34327
|
+
rememberReasoningEffortValueRejected(this.modelId, rejected);
|
|
34328
|
+
const fallback = fallbackReasoningEffortValue(rejected);
|
|
34329
|
+
const next = { ...payload };
|
|
34330
|
+
if (fallback) {
|
|
34331
|
+
next.reasoning_effort = fallback;
|
|
34332
|
+
return {
|
|
34333
|
+
payload: next,
|
|
34334
|
+
note: `reasoning_effort "${rejected}" -> "${fallback}" for ${this.modelId}`
|
|
34335
|
+
};
|
|
34336
|
+
}
|
|
34337
|
+
delete next.reasoning_effort;
|
|
34338
|
+
return { payload: next, note: `dropped unsupported reasoning_effort for ${this.modelId}` };
|
|
34202
34339
|
}
|
|
34340
|
+
return null;
|
|
34203
34341
|
}
|
|
34204
34342
|
parseXmlParameters(xmlContent) {
|
|
34205
34343
|
const params = {};
|
|
@@ -36079,6 +36217,45 @@ Do not invent a different filename, and do not derive one from the task. Claude
|
|
|
36079
36217
|
PLAN_MODE_RULES = [planFilePathRule];
|
|
36080
36218
|
});
|
|
36081
36219
|
|
|
36220
|
+
// src/behavior/rules/session-context.ts
|
|
36221
|
+
function hasInjectedSessionContext(systemText2, messages) {
|
|
36222
|
+
const haystacks = [systemText2 ?? ""];
|
|
36223
|
+
const first = Array.isArray(messages) ? messages[0] : undefined;
|
|
36224
|
+
if (first && typeof first === "object") {
|
|
36225
|
+
const content = first.content;
|
|
36226
|
+
if (typeof content === "string") {
|
|
36227
|
+
haystacks.push(content);
|
|
36228
|
+
} else if (Array.isArray(content)) {
|
|
36229
|
+
for (const block of content) {
|
|
36230
|
+
const text = block?.text;
|
|
36231
|
+
if (typeof text === "string")
|
|
36232
|
+
haystacks.push(text);
|
|
36233
|
+
}
|
|
36234
|
+
}
|
|
36235
|
+
}
|
|
36236
|
+
return haystacks.some((h) => {
|
|
36237
|
+
const lower = h.toLowerCase();
|
|
36238
|
+
return SESSION_CONTEXT_MARKERS.some((m) => lower.includes(m));
|
|
36239
|
+
});
|
|
36240
|
+
}
|
|
36241
|
+
var SESSION_CONTEXT_MARKERS, NOTE, noHarnessEchoRule, SESSION_CONTEXT_RULES;
|
|
36242
|
+
var init_session_context = __esm(() => {
|
|
36243
|
+
SESSION_CONTEXT_MARKERS = ["sessionstart hook additional context", "hook additional context"];
|
|
36244
|
+
NOTE = "Operational context note. This session may include text injected by the harness " + "itself \u2014 SessionStart hook output, coaching or insight blocks, tooling banners, " + "status lines. That material is addressed to you, not to the user, and it is not " + "part of the task. Never reproduce, summarise, or quote it in your answer, and never " + "let it open your response. Begin your answer with the requested deliverable and " + "nothing before it.";
|
|
36245
|
+
noHarnessEchoRule = {
|
|
36246
|
+
id: "session-context/no-harness-echo",
|
|
36247
|
+
description: "Stop foreign models opening their answer with harness-injected session context " + "(SessionStart hook output, coaching/insight blocks).",
|
|
36248
|
+
defaultSeverity: "fix",
|
|
36249
|
+
appliesTo: ({ isNativeAnthropic }) => !isNativeAnthropic,
|
|
36250
|
+
onRequest(ctx) {
|
|
36251
|
+
if (!hasInjectedSessionContext(ctx.systemText, ctx.messages))
|
|
36252
|
+
return [];
|
|
36253
|
+
return [{ type: "injectSystemNote", text: NOTE }];
|
|
36254
|
+
}
|
|
36255
|
+
};
|
|
36256
|
+
SESSION_CONTEXT_RULES = [noHarnessEchoRule];
|
|
36257
|
+
});
|
|
36258
|
+
|
|
36082
36259
|
// src/behavior/hooks.ts
|
|
36083
36260
|
import { isAbsolute, resolve } from "path";
|
|
36084
36261
|
function isBehaviorRule(value) {
|
|
@@ -36288,6 +36465,7 @@ var init_behavior = __esm(() => {
|
|
|
36288
36465
|
init_config();
|
|
36289
36466
|
init_engine();
|
|
36290
36467
|
init_plan_mode();
|
|
36468
|
+
init_session_context();
|
|
36291
36469
|
init_engine();
|
|
36292
36470
|
init_config();
|
|
36293
36471
|
init_harness();
|
|
@@ -36297,7 +36475,7 @@ var init_behavior = __esm(() => {
|
|
|
36297
36475
|
init_corpus();
|
|
36298
36476
|
init_aggregate();
|
|
36299
36477
|
init_upload();
|
|
36300
|
-
BUILTIN_RULES = [...PLAN_MODE_RULES];
|
|
36478
|
+
BUILTIN_RULES = [...PLAN_MODE_RULES, ...SESSION_CONTEXT_RULES];
|
|
36301
36479
|
hookRules = [];
|
|
36302
36480
|
});
|
|
36303
36481
|
|
|
@@ -40840,6 +41018,30 @@ class ComposedHandler {
|
|
|
40840
41018
|
}
|
|
40841
41019
|
log(`[${this.provider.displayName}] Response status: ${response.status}`);
|
|
40842
41020
|
this.capturePlanUsage(response);
|
|
41021
|
+
if (!response.ok) {
|
|
41022
|
+
if (response.status >= 400 && response.status < 500 && this.modelAdapter?.recoverFromRejection) {
|
|
41023
|
+
const errorText = await response.clone().text();
|
|
41024
|
+
const recovery = this.modelAdapter.recoverFromRejection(requestPayload, errorText);
|
|
41025
|
+
if (recovery) {
|
|
41026
|
+
log(`[${this.provider.displayName}] Parameter rejected \u2014 retrying: ${recovery.note}`);
|
|
41027
|
+
requestPayload = recovery.payload;
|
|
41028
|
+
const retrySerialized = this.provider.serializeBody?.(requestPayload);
|
|
41029
|
+
const retryHeaders = await this.provider.getHeaders();
|
|
41030
|
+
retryHeaders["Content-Type"] = retrySerialized?.contentType ?? "application/json";
|
|
41031
|
+
const retryResp = await fetch(endpoint, {
|
|
41032
|
+
method: "POST",
|
|
41033
|
+
headers: retryHeaders,
|
|
41034
|
+
body: retrySerialized?.body ?? JSON.stringify(requestPayload),
|
|
41035
|
+
...this.provider.getRequestInit?.() || {}
|
|
41036
|
+
});
|
|
41037
|
+
if (retryResp.ok) {
|
|
41038
|
+
response = retryResp;
|
|
41039
|
+
} else {
|
|
41040
|
+
log(`[${this.provider.displayName}] Retry after ${recovery.note} still failed ` + `(HTTP ${retryResp.status})`);
|
|
41041
|
+
}
|
|
41042
|
+
}
|
|
41043
|
+
}
|
|
41044
|
+
}
|
|
40843
41045
|
if (!response.ok) {
|
|
40844
41046
|
if (response.status === 401 && this.provider.forceRefreshAuth) {
|
|
40845
41047
|
log(`[${this.provider.displayName}] Got 401, forcing auth refresh and retrying`);
|
|
@@ -44643,6 +44845,48 @@ var init_signal_watcher = __esm(() => {
|
|
|
44643
44845
|
QUESTION_PATTERNS = [/\?\s*$/m, /\bchoose\b.*:/im, /\bselect\b.*:/im, /\benter\b.*:/im];
|
|
44644
44846
|
});
|
|
44645
44847
|
|
|
44848
|
+
// src/process-tree.ts
|
|
44849
|
+
function signalProcessTree(proc, signal) {
|
|
44850
|
+
if (KILL_PROCESS_GROUP && proc.pid) {
|
|
44851
|
+
try {
|
|
44852
|
+
process.kill(-proc.pid, signal);
|
|
44853
|
+
} catch {}
|
|
44854
|
+
}
|
|
44855
|
+
try {
|
|
44856
|
+
if (!proc.killed)
|
|
44857
|
+
proc.kill(signal);
|
|
44858
|
+
} catch {}
|
|
44859
|
+
}
|
|
44860
|
+
function waitForExit(proc, ms) {
|
|
44861
|
+
if (proc.exitCode !== null || proc.signalCode !== null)
|
|
44862
|
+
return Promise.resolve(true);
|
|
44863
|
+
return new Promise((resolve2) => {
|
|
44864
|
+
let settled = false;
|
|
44865
|
+
const done = (value) => {
|
|
44866
|
+
if (settled)
|
|
44867
|
+
return;
|
|
44868
|
+
settled = true;
|
|
44869
|
+
proc.off("exit", onExit);
|
|
44870
|
+
resolve2(value);
|
|
44871
|
+
};
|
|
44872
|
+
const onExit = () => done(true);
|
|
44873
|
+
proc.once("exit", onExit);
|
|
44874
|
+
const t = setTimeout(() => done(false), ms);
|
|
44875
|
+
t.unref?.();
|
|
44876
|
+
});
|
|
44877
|
+
}
|
|
44878
|
+
async function terminateChildTree(proc, graceMs = TERMINATE_GRACE_MS) {
|
|
44879
|
+
signalProcessTree(proc, "SIGTERM");
|
|
44880
|
+
if (await waitForExit(proc, graceMs))
|
|
44881
|
+
return true;
|
|
44882
|
+
signalProcessTree(proc, "SIGKILL");
|
|
44883
|
+
return waitForExit(proc, graceMs);
|
|
44884
|
+
}
|
|
44885
|
+
var KILL_PROCESS_GROUP, TERMINATE_GRACE_MS = 5000;
|
|
44886
|
+
var init_process_tree = __esm(() => {
|
|
44887
|
+
KILL_PROCESS_GROUP = process.platform !== "win32";
|
|
44888
|
+
});
|
|
44889
|
+
|
|
44646
44890
|
// src/spawn-claudish.ts
|
|
44647
44891
|
function resolveClaudishSpawn(env = process.env) {
|
|
44648
44892
|
const bin = env[CLAUDISH_BIN_ENV]?.trim();
|
|
@@ -44699,7 +44943,8 @@ class SessionManager {
|
|
|
44699
44943
|
const proc = spawn(spawnTarget.command, [...spawnTarget.prefixArgs, ...args], {
|
|
44700
44944
|
cwd: opts.cwd ?? process.cwd(),
|
|
44701
44945
|
stdio: ["pipe", "pipe", "pipe"],
|
|
44702
|
-
shell: false
|
|
44946
|
+
shell: false,
|
|
44947
|
+
detached: KILL_PROCESS_GROUP
|
|
44703
44948
|
});
|
|
44704
44949
|
const scrollback = new ScrollbackBuffer(this.scrollbackCapacity);
|
|
44705
44950
|
const watcher = new SignalWatcher(sessionId2, (sid, data) => {
|
|
@@ -44779,10 +45024,10 @@ class SessionManager {
|
|
|
44779
45024
|
});
|
|
44780
45025
|
entry.timeoutHandle = setTimeout(() => {
|
|
44781
45026
|
if (!proc.killed) {
|
|
44782
|
-
proc
|
|
45027
|
+
signalProcessTree(proc, "SIGTERM");
|
|
44783
45028
|
entry.killHandle = setTimeout(() => {
|
|
44784
45029
|
try {
|
|
44785
|
-
proc
|
|
45030
|
+
signalProcessTree(proc, "SIGKILL");
|
|
44786
45031
|
} catch {}
|
|
44787
45032
|
}, KILL_GRACE_MS);
|
|
44788
45033
|
entry.info.status = "timeout";
|
|
@@ -44840,10 +45085,10 @@ class SessionManager {
|
|
|
44840
45085
|
entry.info.completedAt = new Date().toISOString();
|
|
44841
45086
|
entry.watcher.forceState("cancelled", "Session cancelled");
|
|
44842
45087
|
if (!entry.process.killed) {
|
|
44843
|
-
entry.process
|
|
45088
|
+
signalProcessTree(entry.process, "SIGTERM");
|
|
44844
45089
|
entry.killHandle = setTimeout(() => {
|
|
44845
45090
|
try {
|
|
44846
|
-
entry.process
|
|
45091
|
+
signalProcessTree(entry.process, "SIGKILL");
|
|
44847
45092
|
} catch {}
|
|
44848
45093
|
}, KILL_GRACE_MS);
|
|
44849
45094
|
}
|
|
@@ -44874,11 +45119,11 @@ class SessionManager {
|
|
|
44874
45119
|
const promises = [];
|
|
44875
45120
|
for (const [, entry] of this.sessions) {
|
|
44876
45121
|
if (!entry.process.killed) {
|
|
44877
|
-
entry.process
|
|
45122
|
+
signalProcessTree(entry.process, "SIGTERM");
|
|
44878
45123
|
promises.push(new Promise((resolve2) => {
|
|
44879
45124
|
const timeout = setTimeout(() => {
|
|
44880
45125
|
try {
|
|
44881
|
-
entry.process
|
|
45126
|
+
signalProcessTree(entry.process, "SIGKILL");
|
|
44882
45127
|
} catch {}
|
|
44883
45128
|
resolve2();
|
|
44884
45129
|
}, KILL_GRACE_MS);
|
|
@@ -44924,6 +45169,7 @@ class SessionManager {
|
|
|
44924
45169
|
}
|
|
44925
45170
|
var DEFAULT_MAX_SESSIONS = 20, DEFAULT_SCROLLBACK = 2000, DEFAULT_TIMEOUT = 600, MAX_TIMEOUT = 3600, KILL_GRACE_MS = 5000;
|
|
44926
45171
|
var init_session_manager = __esm(() => {
|
|
45172
|
+
init_process_tree();
|
|
44927
45173
|
init_scrollback_buffer();
|
|
44928
45174
|
init_signal_watcher();
|
|
44929
45175
|
});
|
|
@@ -50810,6 +51056,7 @@ __export(exports_team_orchestrator, {
|
|
|
50810
51056
|
runModels: () => runModels,
|
|
50811
51057
|
resolveCaptureMode: () => resolveCaptureMode,
|
|
50812
51058
|
parseJudgeVotes: () => parseJudgeVotes,
|
|
51059
|
+
meaningfulStderr: () => meaningfulStderr,
|
|
50813
51060
|
judgeResponses: () => judgeResponses,
|
|
50814
51061
|
getStatus: () => getStatus,
|
|
50815
51062
|
fisherYatesShuffle: () => fisherYatesShuffle,
|
|
@@ -50818,6 +51065,9 @@ __export(exports_team_orchestrator, {
|
|
|
50818
51065
|
aggregateVerdict: () => aggregateVerdict,
|
|
50819
51066
|
TEAM_CAPTURE_ENV_VAR: () => TEAM_CAPTURE_ENV_VAR,
|
|
50820
51067
|
STDOUT_TAIL_LIMIT: () => STDOUT_TAIL_LIMIT,
|
|
51068
|
+
GRACE_INTERVAL_MS: () => GRACE_INTERVAL_MS,
|
|
51069
|
+
DRAIN_TIMEOUT_MS: () => DRAIN_TIMEOUT_MS,
|
|
51070
|
+
DEFAULT_STALL_SECONDS: () => DEFAULT_STALL_SECONDS,
|
|
50821
51071
|
DEFAULT_MIN_OUTPUT_BYTES: () => DEFAULT_MIN_OUTPUT_BYTES
|
|
50822
51072
|
});
|
|
50823
51073
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -50890,6 +51140,13 @@ function classifyRunOutput(opts) {
|
|
|
50890
51140
|
}
|
|
50891
51141
|
return null;
|
|
50892
51142
|
}
|
|
51143
|
+
function meaningfulStderr(stderr) {
|
|
51144
|
+
if (!stderr)
|
|
51145
|
+
return "";
|
|
51146
|
+
return stderr.split(`
|
|
51147
|
+
`).filter((line) => line.trim().length > 0).filter((line) => !BENIGN_STDERR_PATTERNS.some((re) => re.test(line))).join(`
|
|
51148
|
+
`).trim();
|
|
51149
|
+
}
|
|
50893
51150
|
function persistErrorLog(errorLogPath, header, stderr, stdoutTail) {
|
|
50894
51151
|
const parts = [`=== ${redactSecrets(header)} ===`, ""];
|
|
50895
51152
|
parts.push("--- stderr ---", stderr.trim() ? redactSecrets(stderr) : "(empty)", "");
|
|
@@ -51001,13 +51258,40 @@ async function runModels(sessionPath, opts = {}) {
|
|
|
51001
51258
|
const minOutputBytes = opts.minOutputBytes ?? DEFAULT_MIN_OUTPUT_BYTES;
|
|
51002
51259
|
const requirePattern = opts.requirePattern;
|
|
51003
51260
|
const captureMode = resolveCaptureMode(opts.captureMode);
|
|
51261
|
+
function reconcileTimedOutOutput(id, finalBytes, stdoutTail, stderr) {
|
|
51262
|
+
const current = statusCache.models[id];
|
|
51263
|
+
if (!current || current.state !== "TIMEOUT")
|
|
51264
|
+
return;
|
|
51265
|
+
if (finalBytes <= current.outputSize)
|
|
51266
|
+
return;
|
|
51267
|
+
const degraded = classifyRunOutput({
|
|
51268
|
+
outputSize: finalBytes,
|
|
51269
|
+
stdoutTail,
|
|
51270
|
+
stderr,
|
|
51271
|
+
minOutputBytes
|
|
51272
|
+
});
|
|
51273
|
+
if (degraded) {
|
|
51274
|
+
updateModelStatus(id, { outputSize: finalBytes });
|
|
51275
|
+
return;
|
|
51276
|
+
}
|
|
51277
|
+
updateModelStatus(id, {
|
|
51278
|
+
state: "COMPLETED",
|
|
51279
|
+
outputSize: finalBytes,
|
|
51280
|
+
completedAt: new Date().toISOString(),
|
|
51281
|
+
error: undefined
|
|
51282
|
+
});
|
|
51283
|
+
const note = `Recovered after the deadline: the child flushed ${finalBytes} B while shutting down, ` + "so its answer is complete and is being counted. The run still exceeded its timeout.";
|
|
51284
|
+
const rt = runtimes.get(id);
|
|
51285
|
+
if (rt)
|
|
51286
|
+
persistErrorLog(rt.errorLogPath, `RECOVERED: ${note}`, stderr, stdoutTail);
|
|
51287
|
+
opts.onStatusChange?.(id, statusCache.models[id]);
|
|
51288
|
+
}
|
|
51004
51289
|
mkdirSync12(statsDir(sessionPath), { recursive: true });
|
|
51005
51290
|
const processes = new Map;
|
|
51006
51291
|
const runtimes = new Map;
|
|
51007
51292
|
const sigintHandler = () => {
|
|
51008
51293
|
for (const [, proc] of processes) {
|
|
51009
|
-
|
|
51010
|
-
proc.kill("SIGTERM");
|
|
51294
|
+
signalProcessTree(proc, "SIGTERM");
|
|
51011
51295
|
}
|
|
51012
51296
|
process.exit(1);
|
|
51013
51297
|
};
|
|
@@ -51033,6 +51317,7 @@ async function runModels(sessionPath, opts = {}) {
|
|
|
51033
51317
|
const proc = spawn2(teamSpawnTarget.command, [...teamSpawnTarget.prefixArgs, ...args], {
|
|
51034
51318
|
stdio: ["pipe", "pipe", "pipe"],
|
|
51035
51319
|
shell: false,
|
|
51320
|
+
detached: KILL_PROCESS_GROUP,
|
|
51036
51321
|
env: {
|
|
51037
51322
|
...process.env,
|
|
51038
51323
|
CLAUDISH_TOKEN_FILE: tokenFileFor(sessionPath, anonId)
|
|
@@ -51093,6 +51378,7 @@ async function runModels(sessionPath, opts = {}) {
|
|
|
51093
51378
|
return;
|
|
51094
51379
|
if (statusCache.models[anonId].state === "TIMEOUT") {
|
|
51095
51380
|
resolved = true;
|
|
51381
|
+
reconcileTimedOutOutput(anonId, byteCount, stdoutTail, stderr);
|
|
51096
51382
|
resolve4();
|
|
51097
51383
|
return;
|
|
51098
51384
|
}
|
|
@@ -51150,13 +51436,8 @@ async function runModels(sessionPath, opts = {}) {
|
|
|
51150
51436
|
};
|
|
51151
51437
|
outputStream.on("close", finish);
|
|
51152
51438
|
proc.on("exit", (code) => {
|
|
51153
|
-
const
|
|
51154
|
-
if (
|
|
51155
|
-
resolved = true;
|
|
51156
|
-
resolve4();
|
|
51157
|
-
return;
|
|
51158
|
-
}
|
|
51159
|
-
if (stderr) {
|
|
51439
|
+
const timedOut = statusCache.models[anonId]?.state === "TIMEOUT";
|
|
51440
|
+
if (!timedOut && meaningfulStderr(stderr)) {
|
|
51160
51441
|
writeFileSync12(errorLogPath, redactSecrets(stderr), "utf-8");
|
|
51161
51442
|
}
|
|
51162
51443
|
exitCode = code;
|
|
@@ -51198,48 +51479,98 @@ async function runModels(sessionPath, opts = {}) {
|
|
|
51198
51479
|
emitProgress();
|
|
51199
51480
|
const progressHandle = setInterval(() => emitProgress("running"), POLL_MS);
|
|
51200
51481
|
progressHandle.unref?.();
|
|
51201
|
-
|
|
51202
|
-
|
|
51203
|
-
|
|
51204
|
-
|
|
51205
|
-
|
|
51206
|
-
|
|
51207
|
-
|
|
51208
|
-
|
|
51209
|
-
|
|
51210
|
-
|
|
51211
|
-
|
|
51212
|
-
|
|
51213
|
-
|
|
51214
|
-
|
|
51215
|
-
|
|
51216
|
-
|
|
51217
|
-
|
|
51218
|
-
|
|
51219
|
-
|
|
51220
|
-
|
|
51221
|
-
|
|
51222
|
-
|
|
51223
|
-
|
|
51224
|
-
|
|
51225
|
-
|
|
51226
|
-
|
|
51227
|
-
|
|
51228
|
-
|
|
51229
|
-
|
|
51230
|
-
|
|
51231
|
-
|
|
51232
|
-
|
|
51233
|
-
|
|
51234
|
-
|
|
51235
|
-
|
|
51482
|
+
const graceEnabled = opts.graceExtension ?? true;
|
|
51483
|
+
const maxGraceMs = Math.max(0, (opts.maxGraceSeconds ?? timeoutMs / 1000) * 1000);
|
|
51484
|
+
const stallMs = Math.max(0, (opts.stallSeconds ?? DEFAULT_STALL_SECONDS) * 1000);
|
|
51485
|
+
const idleMsFor = (id) => {
|
|
51486
|
+
const s = readTokenStats(sessionPath, id);
|
|
51487
|
+
if (!s || typeof s.updated_at !== "number" || s.updated_at <= 0)
|
|
51488
|
+
return null;
|
|
51489
|
+
return Math.max(0, Date.now() - s.updated_at);
|
|
51490
|
+
};
|
|
51491
|
+
const graceStartedAt = new Map;
|
|
51492
|
+
const graceUsedMs = (id, now) => {
|
|
51493
|
+
const start = graceStartedAt.get(id);
|
|
51494
|
+
return start === undefined ? 0 : Math.max(0, now - start);
|
|
51495
|
+
};
|
|
51496
|
+
const runningIds = () => [...processes.keys()].filter((id) => statusCache.models[id]?.state === "RUNNING");
|
|
51497
|
+
const timeoutModel = async (id, why) => {
|
|
51498
|
+
const proc = processes.get(id);
|
|
51499
|
+
if (!proc || statusCache.models[id]?.state !== "RUNNING")
|
|
51500
|
+
return;
|
|
51501
|
+
const rt = runtimes.get(id);
|
|
51502
|
+
rt?.flushPartial();
|
|
51503
|
+
const stderr = rt?.getStderr() ?? "";
|
|
51504
|
+
const stdoutTail = rt?.getStdoutTail() ?? "";
|
|
51505
|
+
const bytes2 = rt?.getByteCount() ?? 0;
|
|
51506
|
+
const grace = graceUsedMs(id, Date.now());
|
|
51507
|
+
const detail = `Killed by the orchestrator after ${(timeoutMs + grace) / 1000}s ` + `(deadline ${timeoutMs / 1000}s${grace ? ` + ${grace / 1000}s grace` : ""}) ` + `with ${bytes2} B of stdout \u2014 ${why}. ` + "That figure counts the ANSWER, not the wire format, so 0 B means the child had " + `not produced an assistant message yet \u2014 "did not finish", not "produced nothing".`;
|
|
51508
|
+
if (rt)
|
|
51509
|
+
persistErrorLog(rt.errorLogPath, `TIMEOUT: ${detail}`, stderr, stdoutTail);
|
|
51510
|
+
updateModelStatus(id, {
|
|
51511
|
+
state: "TIMEOUT",
|
|
51512
|
+
completedAt: new Date().toISOString(),
|
|
51513
|
+
outputSize: bytes2,
|
|
51514
|
+
error: rt ? {
|
|
51515
|
+
model: id,
|
|
51516
|
+
command: rt.command,
|
|
51517
|
+
reason: "timeout",
|
|
51518
|
+
detail,
|
|
51519
|
+
stderrSnippet: stderr ? redactSecrets(stderr).slice(-2000) : undefined,
|
|
51520
|
+
stdoutSnippet: stdoutTail ? redactSecrets(stdoutTail).slice(-2000) : undefined,
|
|
51521
|
+
errorLogPath: rt.errorLogPath,
|
|
51522
|
+
workDir: sessionPath
|
|
51523
|
+
} : undefined
|
|
51524
|
+
});
|
|
51525
|
+
opts.onStatusChange?.(id, statusCache.models[id]);
|
|
51526
|
+
const stopped = await terminateChildTree(proc);
|
|
51527
|
+
if (!stopped) {
|
|
51528
|
+
persistErrorLog(rt?.errorLogPath ?? join28(sessionPath, "errors", `${id}.log`), "TIMEOUT: child survived SIGKILL \u2014 it may still be running and billing", stderr, stdoutTail);
|
|
51529
|
+
}
|
|
51530
|
+
};
|
|
51531
|
+
const allDone = Promise.all(completionPromises);
|
|
51532
|
+
let settled = false;
|
|
51533
|
+
allDone.then(() => {
|
|
51534
|
+
settled = true;
|
|
51535
|
+
}, () => {
|
|
51536
|
+
settled = true;
|
|
51537
|
+
});
|
|
51538
|
+
const deadlineWatcher = (async () => {
|
|
51539
|
+
await delay(timeoutMs);
|
|
51540
|
+
for (;; ) {
|
|
51541
|
+
if (settled)
|
|
51542
|
+
return;
|
|
51543
|
+
const running = runningIds();
|
|
51544
|
+
if (running.length === 0)
|
|
51545
|
+
return;
|
|
51546
|
+
const extended = [];
|
|
51547
|
+
const now = Date.now();
|
|
51548
|
+
for (const id of running) {
|
|
51549
|
+
const idleMs = idleMsFor(id);
|
|
51550
|
+
const usedGrace = graceUsedMs(id, now);
|
|
51551
|
+
if (!graceEnabled) {
|
|
51552
|
+
await timeoutModel(id, "deadline reached (grace extension disabled)");
|
|
51553
|
+
} else if (usedGrace >= maxGraceMs) {
|
|
51554
|
+
await timeoutModel(id, `grace exhausted after ${Math.round(usedGrace / 1000)}s of extra time`);
|
|
51555
|
+
} else if (idleMs === null) {
|
|
51556
|
+
await timeoutModel(id, "deadline reached with no measurable progress to extend for");
|
|
51557
|
+
} else if (idleMs >= stallMs) {
|
|
51558
|
+
await timeoutModel(id, `no measurable progress for ${Math.round(idleMs / 1000)}s`);
|
|
51559
|
+
} else {
|
|
51560
|
+
if (!graceStartedAt.has(id))
|
|
51561
|
+
graceStartedAt.set(id, now);
|
|
51562
|
+
extended.push(id);
|
|
51236
51563
|
}
|
|
51237
|
-
|
|
51238
|
-
|
|
51239
|
-
|
|
51240
|
-
|
|
51241
|
-
|
|
51242
|
-
|
|
51564
|
+
}
|
|
51565
|
+
if (extended.length === 0)
|
|
51566
|
+
return;
|
|
51567
|
+
emitProgress("running");
|
|
51568
|
+
await delay(Math.min(GRACE_INTERVAL_MS, Math.max(1000, stallMs)));
|
|
51569
|
+
}
|
|
51570
|
+
})().catch(() => {});
|
|
51571
|
+
await Promise.race([allDone, deadlineWatcher]);
|
|
51572
|
+
if (!settled)
|
|
51573
|
+
await Promise.race([allDone, delay(DRAIN_TIMEOUT_MS)]);
|
|
51243
51574
|
clearInterval(progressHandle);
|
|
51244
51575
|
emitProgress("settled");
|
|
51245
51576
|
process.off("SIGINT", sigintHandler);
|
|
@@ -51425,14 +51756,19 @@ function formatVerdict(verdict, sessionPath) {
|
|
|
51425
51756
|
}
|
|
51426
51757
|
return output;
|
|
51427
51758
|
}
|
|
51428
|
-
var TEAM_CAPTURE_ENV_VAR = "CLAUDISH_TEAM_CAPTURE", STDOUT_TAIL_LIMIT = 4000, API_ERROR_RE, BG_CEILING_RE, DEFAULT_MIN_OUTPUT_BYTES = 0,
|
|
51759
|
+
var TEAM_CAPTURE_ENV_VAR = "CLAUDISH_TEAM_CAPTURE", STDOUT_TAIL_LIMIT = 4000, API_ERROR_RE, BG_CEILING_RE, DEFAULT_MIN_OUTPUT_BYTES = 0, DRAIN_TIMEOUT_MS = 1e4, GRACE_INTERVAL_MS = 60000, DEFAULT_STALL_SECONDS = 90, delay = (ms) => new Promise((resolve4) => {
|
|
51760
|
+
const t = setTimeout(resolve4, ms);
|
|
51761
|
+
t.unref?.();
|
|
51762
|
+
}), BENIGN_STDERR_PATTERNS, SENTINEL_MODELS;
|
|
51429
51763
|
var init_team_orchestrator = __esm(() => {
|
|
51430
51764
|
init_prehydrate();
|
|
51765
|
+
init_process_tree();
|
|
51431
51766
|
init_redact();
|
|
51432
51767
|
init_team_stats();
|
|
51433
51768
|
init_team_stream_capture();
|
|
51434
51769
|
API_ERROR_RE = /\[API Error:\s*([^\]]{0,300})\]/i;
|
|
51435
51770
|
BG_CEILING_RE = /Background tasks still running after (\d+)s; terminating/i;
|
|
51771
|
+
BENIGN_STDERR_PATTERNS = [/^\s*\[claude-code:unrecognized_model\]/];
|
|
51436
51772
|
SENTINEL_MODELS = new Set([
|
|
51437
51773
|
"internal",
|
|
51438
51774
|
"default",
|
|
@@ -52976,6 +53312,11 @@ function findMagmuxBinary() {
|
|
|
52976
53312
|
throw new Error(`magmux not found. Install it:
|
|
52977
53313
|
brew install MadAppGang/tap/magmux`);
|
|
52978
53314
|
}
|
|
53315
|
+
function withoutControlPanes(evt) {
|
|
53316
|
+
if (!Array.isArray(evt.panes))
|
|
53317
|
+
return evt;
|
|
53318
|
+
return { ...evt, panes: evt.panes.filter((p) => p?.control !== true) };
|
|
53319
|
+
}
|
|
52979
53320
|
async function subscribeToMagmux(sockPath, onEvent) {
|
|
52980
53321
|
let client = null;
|
|
52981
53322
|
for (let attempt = 0;attempt < 40; attempt++) {
|
|
@@ -53012,7 +53353,7 @@ async function subscribeToMagmux(sockPath, onEvent) {
|
|
|
53012
53353
|
const evt = JSON.parse(line);
|
|
53013
53354
|
onEvent?.(evt);
|
|
53014
53355
|
if (evt.type === "results") {
|
|
53015
|
-
finalResults = evt;
|
|
53356
|
+
finalResults = withoutControlPanes(evt);
|
|
53016
53357
|
}
|
|
53017
53358
|
} catch {}
|
|
53018
53359
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudish",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.52.0",
|
|
4
4
|
"description": "Run Claude Code with any model - OpenRouter, Ollama, LM Studio & local models",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -60,10 +60,10 @@
|
|
|
60
60
|
"ai"
|
|
61
61
|
],
|
|
62
62
|
"optionalDependencies": {
|
|
63
|
-
"@claudish/magmux-darwin-arm64": "7.
|
|
64
|
-
"@claudish/magmux-darwin-x64": "7.
|
|
65
|
-
"@claudish/magmux-linux-arm64": "7.
|
|
66
|
-
"@claudish/magmux-linux-x64": "7.
|
|
63
|
+
"@claudish/magmux-darwin-arm64": "7.52.0",
|
|
64
|
+
"@claudish/magmux-darwin-x64": "7.52.0",
|
|
65
|
+
"@claudish/magmux-linux-arm64": "7.52.0",
|
|
66
|
+
"@claudish/magmux-linux-x64": "7.52.0"
|
|
67
67
|
},
|
|
68
68
|
"author": "Jack Rudenko <i@madappgang.com>",
|
|
69
69
|
"license": "MIT",
|