claudish 7.67.1 → 8.0.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/index.js +270 -152
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -731,7 +731,7 @@ var init_onepassword_config = __esm(() => {
|
|
|
731
731
|
});
|
|
732
732
|
|
|
733
733
|
// src/version.ts
|
|
734
|
-
var VERSION = "
|
|
734
|
+
var VERSION = "8.0.0";
|
|
735
735
|
|
|
736
736
|
// src/logger.ts
|
|
737
737
|
var exports_logger = {};
|
|
@@ -48403,10 +48403,12 @@ var init_team_stream_capture = __esm(() => {
|
|
|
48403
48403
|
});
|
|
48404
48404
|
|
|
48405
48405
|
// src/channel/stream-json-reducer.ts
|
|
48406
|
-
function reachesAnswer(parsedType8, isJson) {
|
|
48406
|
+
function reachesAnswer(parsedType8, isJson, keepUnrecognizedJson) {
|
|
48407
48407
|
if (!isJson)
|
|
48408
48408
|
return true;
|
|
48409
|
-
|
|
48409
|
+
if (parsedType8 !== null && STREAM_JSON_EVENT_TYPES.has(parsedType8))
|
|
48410
|
+
return true;
|
|
48411
|
+
return keepUnrecognizedJson;
|
|
48410
48412
|
}
|
|
48411
48413
|
function labelFor(type, subtype) {
|
|
48412
48414
|
if (type === null)
|
|
@@ -48478,6 +48480,9 @@ class StreamJsonReducer {
|
|
|
48478
48480
|
get toolUseCount() {
|
|
48479
48481
|
return this._toolUseCount;
|
|
48480
48482
|
}
|
|
48483
|
+
get idleMs() {
|
|
48484
|
+
return Math.max(0, Date.now() - this.lastFrameAt);
|
|
48485
|
+
}
|
|
48481
48486
|
get terminalReason() {
|
|
48482
48487
|
return this._terminalReason;
|
|
48483
48488
|
}
|
|
@@ -48570,7 +48575,7 @@ class StreamJsonReducer {
|
|
|
48570
48575
|
this.opts.onSemanticLine?.(line, labelFor(type, frame ? asString(frame.subtype) : null));
|
|
48571
48576
|
if (frame && type !== null)
|
|
48572
48577
|
this.applyFrame(frame, type);
|
|
48573
|
-
if (!reachesAnswer(type, frame !== null))
|
|
48578
|
+
if (!reachesAnswer(type, frame !== null, this.opts.keepUnrecognizedJson ?? false))
|
|
48574
48579
|
return "";
|
|
48575
48580
|
const prose = this.capture.write(terminated ? `${line}
|
|
48576
48581
|
` : line);
|
|
@@ -49319,6 +49324,16 @@ function resolveClaudishSpawn(env = process.env) {
|
|
|
49319
49324
|
}
|
|
49320
49325
|
var CLAUDISH_BIN_ENV = "CLAUDISH_BIN";
|
|
49321
49326
|
|
|
49327
|
+
// src/stdio-decode.ts
|
|
49328
|
+
import { StringDecoder } from "string_decoder";
|
|
49329
|
+
function decodeChunk(decoder, chunk) {
|
|
49330
|
+
return typeof chunk === "string" ? chunk : decoder.write(chunk);
|
|
49331
|
+
}
|
|
49332
|
+
function newStdioDecoder() {
|
|
49333
|
+
return new StringDecoder("utf8");
|
|
49334
|
+
}
|
|
49335
|
+
var init_stdio_decode = () => {};
|
|
49336
|
+
|
|
49322
49337
|
// src/team-stats.ts
|
|
49323
49338
|
import { existsSync as existsSync19, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "fs";
|
|
49324
49339
|
import { join as join28 } from "path";
|
|
@@ -49494,22 +49509,25 @@ var init_team_stats = () => {};
|
|
|
49494
49509
|
var exports_team_orchestrator = {};
|
|
49495
49510
|
__export(exports_team_orchestrator, {
|
|
49496
49511
|
DEFAULT_MIN_OUTPUT_BYTES: () => DEFAULT_MIN_OUTPUT_BYTES,
|
|
49497
|
-
DEFAULT_STALL_SECONDS: () => DEFAULT_STALL_SECONDS2,
|
|
49498
|
-
DRAIN_TIMEOUT_MS: () => DRAIN_TIMEOUT_MS,
|
|
49499
|
-
GRACE_INTERVAL_MS: () => GRACE_INTERVAL_MS,
|
|
49500
49512
|
STDOUT_TAIL_LIMIT: () => STDOUT_TAIL_LIMIT,
|
|
49501
49513
|
TEAM_CAPTURE_ENV_VAR: () => TEAM_CAPTURE_ENV_VAR,
|
|
49502
49514
|
aggregateVerdict: () => aggregateVerdict,
|
|
49503
49515
|
buildJudgePrompt: () => buildJudgePrompt,
|
|
49516
|
+
cancelTeamRun: () => cancelTeamRun,
|
|
49504
49517
|
classifyRunOutput: () => classifyRunOutput,
|
|
49505
49518
|
fisherYatesShuffle: () => fisherYatesShuffle,
|
|
49506
49519
|
getStatus: () => getStatus,
|
|
49507
49520
|
judgeResponses: () => judgeResponses,
|
|
49508
49521
|
meaningfulStderr: () => meaningfulStderr,
|
|
49509
49522
|
parseJudgeVotes: () => parseJudgeVotes,
|
|
49523
|
+
readTeamInputFile: () => readTeamInputFile,
|
|
49510
49524
|
resolveCaptureMode: () => resolveCaptureMode,
|
|
49511
49525
|
runModels: () => runModels,
|
|
49512
49526
|
setupSession: () => setupSession,
|
|
49527
|
+
shutdownAllTeamRuns: () => shutdownAllTeamRuns,
|
|
49528
|
+
startModels: () => startModels,
|
|
49529
|
+
teamSlotActivity: () => teamSlotActivity,
|
|
49530
|
+
teamSlotIdleSeconds: () => teamSlotIdleSeconds,
|
|
49513
49531
|
validateSessionPath: () => validateSessionPath
|
|
49514
49532
|
});
|
|
49515
49533
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -49521,12 +49539,57 @@ import {
|
|
|
49521
49539
|
readdirSync as readdirSync5,
|
|
49522
49540
|
writeFileSync as writeFileSync11
|
|
49523
49541
|
} from "fs";
|
|
49524
|
-
import { join as join29, resolve as resolve3 } from "path";
|
|
49542
|
+
import { basename as basename2, join as join29, resolve as resolve3 } from "path";
|
|
49525
49543
|
function resolveCaptureMode(explicit, env = process.env) {
|
|
49526
49544
|
if (explicit)
|
|
49527
49545
|
return explicit;
|
|
49528
49546
|
return env[TEAM_CAPTURE_ENV_VAR]?.trim().toLowerCase() === "print" ? "print" : "stream-json";
|
|
49529
49547
|
}
|
|
49548
|
+
function teamSlotIdleSeconds(teamSessionId) {
|
|
49549
|
+
const run = liveTeamRuns.get(teamSessionId);
|
|
49550
|
+
if (!run)
|
|
49551
|
+
return null;
|
|
49552
|
+
const out = {};
|
|
49553
|
+
for (const slotId of run.processes.keys()) {
|
|
49554
|
+
const idle = run.idleMsFor(slotId);
|
|
49555
|
+
if (idle !== null)
|
|
49556
|
+
out[slotId] = Math.round(idle / 1000);
|
|
49557
|
+
}
|
|
49558
|
+
return out;
|
|
49559
|
+
}
|
|
49560
|
+
function teamSlotActivity(teamSessionId) {
|
|
49561
|
+
const run = liveTeamRuns.get(teamSessionId);
|
|
49562
|
+
if (!run)
|
|
49563
|
+
return null;
|
|
49564
|
+
const out = {};
|
|
49565
|
+
for (const slotId of run.processes.keys()) {
|
|
49566
|
+
const activity = run.activityFor(slotId);
|
|
49567
|
+
if (activity !== null)
|
|
49568
|
+
out[slotId] = activity;
|
|
49569
|
+
}
|
|
49570
|
+
return out;
|
|
49571
|
+
}
|
|
49572
|
+
async function cancelTeamRun(teamSessionId, slotId) {
|
|
49573
|
+
const run = liveTeamRuns.get(teamSessionId);
|
|
49574
|
+
if (!run)
|
|
49575
|
+
return { found: false, cancelled: [] };
|
|
49576
|
+
const targets = slotId ? run.processes.has(slotId) ? [slotId] : [] : [...run.processes.keys()];
|
|
49577
|
+
const cancelled = [];
|
|
49578
|
+
for (const id of targets) {
|
|
49579
|
+
const proc = run.processes.get(id);
|
|
49580
|
+
if (!proc)
|
|
49581
|
+
continue;
|
|
49582
|
+
run.cancelledSlots.add(id);
|
|
49583
|
+
await terminateChildTree(proc);
|
|
49584
|
+
cancelled.push(id);
|
|
49585
|
+
}
|
|
49586
|
+
return { found: true, cancelled };
|
|
49587
|
+
}
|
|
49588
|
+
async function shutdownAllTeamRuns() {
|
|
49589
|
+
await Promise.all([...liveTeamRuns.keys()].map((id) => cancelTeamRun(id).catch(() => {
|
|
49590
|
+
return;
|
|
49591
|
+
})));
|
|
49592
|
+
}
|
|
49530
49593
|
function classifyRunOutput(opts) {
|
|
49531
49594
|
const {
|
|
49532
49595
|
outputSize,
|
|
@@ -49606,6 +49669,21 @@ function validateSessionPath(sessionPath) {
|
|
|
49606
49669
|
}
|
|
49607
49670
|
return resolved;
|
|
49608
49671
|
}
|
|
49672
|
+
function readTeamInputFile(inputPath) {
|
|
49673
|
+
const resolved = resolve3(inputPath);
|
|
49674
|
+
const cwd = process.cwd();
|
|
49675
|
+
if (!resolved.startsWith(`${cwd}/`) && resolved !== cwd) {
|
|
49676
|
+
throw new Error(`Input file must be within current directory: ${inputPath}`);
|
|
49677
|
+
}
|
|
49678
|
+
if (!existsSync20(resolved)) {
|
|
49679
|
+
throw new Error(`Input file not found: ${resolved}`);
|
|
49680
|
+
}
|
|
49681
|
+
const text = readFileSync19(resolved, "utf-8");
|
|
49682
|
+
if (text.trim().length === 0) {
|
|
49683
|
+
throw new Error(`Input file is empty: ${resolved}`);
|
|
49684
|
+
}
|
|
49685
|
+
return text;
|
|
49686
|
+
}
|
|
49609
49687
|
function setupSession(sessionPath, models, input) {
|
|
49610
49688
|
if (models.length === 0) {
|
|
49611
49689
|
throw new Error("At least one model is required");
|
|
@@ -49672,8 +49750,7 @@ function readFullOutputIfNeeded(opts) {
|
|
|
49672
49750
|
return;
|
|
49673
49751
|
}
|
|
49674
49752
|
}
|
|
49675
|
-
async function
|
|
49676
|
-
const timeoutMs = (opts.timeout ?? 300) * 1000;
|
|
49753
|
+
async function startModels(sessionPath, opts = {}) {
|
|
49677
49754
|
assertValidRequirePattern(opts.requirePattern);
|
|
49678
49755
|
const manifest = JSON.parse(readFileSync19(join29(sessionPath, "manifest.json"), "utf-8"));
|
|
49679
49756
|
const statusPath = join29(sessionPath, "status.json");
|
|
@@ -49719,6 +49796,7 @@ async function runModels(sessionPath, opts = {}) {
|
|
|
49719
49796
|
mkdirSync11(statsDir(sessionPath), { recursive: true });
|
|
49720
49797
|
const processes = new Map;
|
|
49721
49798
|
const runtimes = new Map;
|
|
49799
|
+
const cancelledSlots = new Set;
|
|
49722
49800
|
const sigintHandler = () => {
|
|
49723
49801
|
for (const [, proc] of processes) {
|
|
49724
49802
|
signalProcessTree(proc, "SIGTERM");
|
|
@@ -49730,6 +49808,7 @@ async function runModels(sessionPath, opts = {}) {
|
|
|
49730
49808
|
for (const [anonId, entry] of Object.entries(manifest.models)) {
|
|
49731
49809
|
const outputPath = join29(sessionPath, `response-${anonId}.md`);
|
|
49732
49810
|
const errorLogPath = join29(sessionPath, "errors", `${anonId}.log`);
|
|
49811
|
+
const upstreamErrorLogPath = join29(sessionPath, "errors", `${anonId}-upstream.jsonl`);
|
|
49733
49812
|
const spawnModel = spawnPlan.pinned.get(entry.model) ?? entry.model;
|
|
49734
49813
|
const args = [
|
|
49735
49814
|
"--model",
|
|
@@ -49750,21 +49829,37 @@ async function runModels(sessionPath, opts = {}) {
|
|
|
49750
49829
|
detached: KILL_PROCESS_GROUP,
|
|
49751
49830
|
env: {
|
|
49752
49831
|
...process.env,
|
|
49753
|
-
CLAUDISH_TOKEN_FILE: tokenFileFor(sessionPath, anonId)
|
|
49832
|
+
[ENV.CLAUDISH_TOKEN_FILE]: tokenFileFor(sessionPath, anonId),
|
|
49833
|
+
[UPSTREAM_ERROR_LOG_ENV]: upstreamErrorLogPath
|
|
49754
49834
|
}
|
|
49755
49835
|
});
|
|
49836
|
+
let lastOutputAt = Date.now();
|
|
49837
|
+
const stampLiveness = () => {
|
|
49838
|
+
lastOutputAt = Date.now();
|
|
49839
|
+
};
|
|
49840
|
+
const stdoutDecoder = newStdioDecoder();
|
|
49841
|
+
const stderrDecoder = newStdioDecoder();
|
|
49842
|
+
proc.stdout?.on("data", stampLiveness);
|
|
49843
|
+
proc.stderr?.on("data", stampLiveness);
|
|
49756
49844
|
let byteCount = 0;
|
|
49757
49845
|
let stdoutTail = "";
|
|
49758
49846
|
const outputStream = createWriteStream(outputPath);
|
|
49759
49847
|
let flushPartial = () => {};
|
|
49848
|
+
let reducer = null;
|
|
49760
49849
|
if (captureMode === "print") {
|
|
49761
49850
|
proc.stdout?.on("data", (chunk) => {
|
|
49762
49851
|
byteCount += chunk.length;
|
|
49763
|
-
stdoutTail = (stdoutTail + chunk
|
|
49852
|
+
stdoutTail = (stdoutTail + decodeChunk(stdoutDecoder, chunk)).slice(-STDOUT_TAIL_LIMIT);
|
|
49764
49853
|
});
|
|
49765
49854
|
proc.stdout?.pipe(outputStream);
|
|
49766
49855
|
} else {
|
|
49767
|
-
const
|
|
49856
|
+
const slotReducer = new StreamJsonReducer({
|
|
49857
|
+
sessionId: anonId,
|
|
49858
|
+
stallSeconds: 0,
|
|
49859
|
+
keepUnrecognizedJson: true,
|
|
49860
|
+
callback: () => {}
|
|
49861
|
+
});
|
|
49862
|
+
reducer = slotReducer;
|
|
49768
49863
|
const absorb = (text) => {
|
|
49769
49864
|
if (text.length === 0)
|
|
49770
49865
|
return;
|
|
@@ -49772,14 +49867,15 @@ async function runModels(sessionPath, opts = {}) {
|
|
|
49772
49867
|
stdoutTail = (stdoutTail + text).slice(-STDOUT_TAIL_LIMIT);
|
|
49773
49868
|
outputStream.write(text);
|
|
49774
49869
|
};
|
|
49775
|
-
proc.stdout?.on("data", (chunk) => absorb(
|
|
49776
|
-
flushPartial = () => absorb(
|
|
49870
|
+
proc.stdout?.on("data", (chunk) => absorb(slotReducer.feed(decodeChunk(stdoutDecoder, chunk))));
|
|
49871
|
+
flushPartial = () => absorb(slotReducer.end());
|
|
49777
49872
|
let captureFinalized = false;
|
|
49778
49873
|
const finalizeCapture = () => {
|
|
49779
49874
|
if (captureFinalized)
|
|
49780
49875
|
return;
|
|
49781
49876
|
captureFinalized = true;
|
|
49782
|
-
absorb(
|
|
49877
|
+
absorb(slotReducer.end());
|
|
49878
|
+
slotReducer.dispose();
|
|
49783
49879
|
outputStream.end();
|
|
49784
49880
|
};
|
|
49785
49881
|
proc.stdout?.on("end", finalizeCapture);
|
|
@@ -49787,7 +49883,7 @@ async function runModels(sessionPath, opts = {}) {
|
|
|
49787
49883
|
}
|
|
49788
49884
|
let stderr = "";
|
|
49789
49885
|
proc.stderr?.on("data", (chunk) => {
|
|
49790
|
-
stderr += chunk
|
|
49886
|
+
stderr += decodeChunk(stderrDecoder, chunk);
|
|
49791
49887
|
});
|
|
49792
49888
|
const command = `claudish ${args.join(" ")}`;
|
|
49793
49889
|
runtimes.set(anonId, {
|
|
@@ -49796,6 +49892,8 @@ async function runModels(sessionPath, opts = {}) {
|
|
|
49796
49892
|
getStderr: () => stderr,
|
|
49797
49893
|
getStdoutTail: () => stdoutTail,
|
|
49798
49894
|
getByteCount: () => byteCount,
|
|
49895
|
+
getIdleMs: () => Math.max(0, Date.now() - lastOutputAt),
|
|
49896
|
+
getActivity: () => reducer?.state ?? null,
|
|
49799
49897
|
flushPartial: () => flushPartial()
|
|
49800
49898
|
});
|
|
49801
49899
|
proc.stdin?.write(inputContent);
|
|
@@ -49833,8 +49931,9 @@ async function runModels(sessionPath, opts = {}) {
|
|
|
49833
49931
|
const failed = crashed || degraded !== null;
|
|
49834
49932
|
const state = crashed ? "FAILED" : degraded ? "EMPTY" : "COMPLETED";
|
|
49835
49933
|
if (failed) {
|
|
49836
|
-
const
|
|
49837
|
-
const
|
|
49934
|
+
const wasCancelled = cancelledSlots.has(anonId);
|
|
49935
|
+
const reason = wasCancelled ? "cancelled" : crashed ? "nonzero_exit" : degraded.reason;
|
|
49936
|
+
const detail = wasCancelled ? `Stopped on the caller's instruction via team(mode:"cancel"). ` + "Whatever the child had written up to that point is in its response file." : crashed ? `Child exited with code ${exitCode}.` : degraded.detail;
|
|
49838
49937
|
persistErrorLog(errorLogPath, `${state}: ${detail}`, stderr, stdoutTail);
|
|
49839
49938
|
updateModelStatus(anonId, {
|
|
49840
49939
|
state,
|
|
@@ -49849,6 +49948,7 @@ async function runModels(sessionPath, opts = {}) {
|
|
|
49849
49948
|
stderrSnippet: stderr ? redactSecrets(stderr).slice(-2000) : undefined,
|
|
49850
49949
|
stdoutSnippet: stdoutTail ? redactSecrets(stdoutTail).slice(-2000) : undefined,
|
|
49851
49950
|
errorLogPath,
|
|
49951
|
+
upstreamErrorLogPath: existsSync20(upstreamErrorLogPath) ? upstreamErrorLogPath : undefined,
|
|
49852
49952
|
workDir: sessionPath
|
|
49853
49953
|
}
|
|
49854
49954
|
});
|
|
@@ -49909,102 +50009,36 @@ async function runModels(sessionPath, opts = {}) {
|
|
|
49909
50009
|
emitProgress();
|
|
49910
50010
|
const progressHandle = setInterval(() => emitProgress("running"), POLL_MS);
|
|
49911
50011
|
progressHandle.unref?.();
|
|
49912
|
-
const
|
|
49913
|
-
|
|
49914
|
-
|
|
49915
|
-
|
|
49916
|
-
|
|
49917
|
-
|
|
49918
|
-
|
|
49919
|
-
|
|
49920
|
-
|
|
49921
|
-
|
|
49922
|
-
|
|
49923
|
-
|
|
49924
|
-
|
|
49925
|
-
|
|
49926
|
-
|
|
49927
|
-
|
|
49928
|
-
const proc = processes.get(id);
|
|
49929
|
-
if (!proc || statusCache.models[id]?.state !== "RUNNING")
|
|
49930
|
-
return;
|
|
49931
|
-
const rt = runtimes.get(id);
|
|
49932
|
-
rt?.flushPartial();
|
|
49933
|
-
const stderr = rt?.getStderr() ?? "";
|
|
49934
|
-
const stdoutTail = rt?.getStdoutTail() ?? "";
|
|
49935
|
-
const bytes2 = rt?.getByteCount() ?? 0;
|
|
49936
|
-
const grace = graceUsedMs(id, Date.now());
|
|
49937
|
-
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".`;
|
|
49938
|
-
if (rt)
|
|
49939
|
-
persistErrorLog(rt.errorLogPath, `TIMEOUT: ${detail}`, stderr, stdoutTail);
|
|
49940
|
-
updateModelStatus(id, {
|
|
49941
|
-
state: "TIMEOUT",
|
|
49942
|
-
completedAt: new Date().toISOString(),
|
|
49943
|
-
outputSize: bytes2,
|
|
49944
|
-
error: rt ? {
|
|
49945
|
-
model: id,
|
|
49946
|
-
command: rt.command,
|
|
49947
|
-
reason: "timeout",
|
|
49948
|
-
detail,
|
|
49949
|
-
stderrSnippet: stderr ? redactSecrets(stderr).slice(-2000) : undefined,
|
|
49950
|
-
stdoutSnippet: stdoutTail ? redactSecrets(stdoutTail).slice(-2000) : undefined,
|
|
49951
|
-
errorLogPath: rt.errorLogPath,
|
|
49952
|
-
workDir: sessionPath
|
|
49953
|
-
} : undefined
|
|
49954
|
-
});
|
|
49955
|
-
opts.onStatusChange?.(id, statusCache.models[id]);
|
|
49956
|
-
const stopped = await terminateChildTree(proc);
|
|
49957
|
-
if (!stopped) {
|
|
49958
|
-
persistErrorLog(rt?.errorLogPath ?? join29(sessionPath, "errors", `${id}.log`), "TIMEOUT: child survived SIGKILL \u2014 it may still be running and billing", stderr, stdoutTail);
|
|
50012
|
+
const teamSessionId = basename2(sessionPath);
|
|
50013
|
+
liveTeamRuns.set(teamSessionId, {
|
|
50014
|
+
sessionPath,
|
|
50015
|
+
processes,
|
|
50016
|
+
idleMsFor: (slotId) => runtimes.get(slotId)?.getIdleMs() ?? null,
|
|
50017
|
+
activityFor: (slotId) => runtimes.get(slotId)?.getActivity() ?? null,
|
|
50018
|
+
cancelledSlots
|
|
50019
|
+
});
|
|
50020
|
+
const done = (async () => {
|
|
50021
|
+
try {
|
|
50022
|
+
await Promise.all(completionPromises);
|
|
50023
|
+
} finally {
|
|
50024
|
+
clearInterval(progressHandle);
|
|
50025
|
+
emitProgress("settled");
|
|
50026
|
+
process.off("SIGINT", sigintHandler);
|
|
50027
|
+
liveTeamRuns.delete(teamSessionId);
|
|
49959
50028
|
}
|
|
50029
|
+
return statusCache;
|
|
50030
|
+
})();
|
|
50031
|
+
done.catch(() => {});
|
|
50032
|
+
return {
|
|
50033
|
+
teamSessionId,
|
|
50034
|
+
sessionPath,
|
|
50035
|
+
slots: Object.fromEntries(Object.entries(manifest.models).map(([anonId, entry]) => [entry.model, anonId])),
|
|
50036
|
+
done
|
|
49960
50037
|
};
|
|
49961
|
-
|
|
49962
|
-
|
|
49963
|
-
|
|
49964
|
-
|
|
49965
|
-
}, () => {
|
|
49966
|
-
settled = true;
|
|
49967
|
-
});
|
|
49968
|
-
const deadlineWatcher = (async () => {
|
|
49969
|
-
await delay(timeoutMs);
|
|
49970
|
-
for (;; ) {
|
|
49971
|
-
if (settled)
|
|
49972
|
-
return;
|
|
49973
|
-
const running = runningIds();
|
|
49974
|
-
if (running.length === 0)
|
|
49975
|
-
return;
|
|
49976
|
-
const extended = [];
|
|
49977
|
-
const now2 = Date.now();
|
|
49978
|
-
for (const id of running) {
|
|
49979
|
-
const idleMs = idleMsFor(id);
|
|
49980
|
-
const usedGrace = graceUsedMs(id, now2);
|
|
49981
|
-
if (!graceEnabled) {
|
|
49982
|
-
await timeoutModel(id, "deadline reached (grace extension disabled)");
|
|
49983
|
-
} else if (usedGrace >= maxGraceMs) {
|
|
49984
|
-
await timeoutModel(id, `grace exhausted after ${Math.round(usedGrace / 1000)}s of extra time`);
|
|
49985
|
-
} else if (idleMs === null) {
|
|
49986
|
-
await timeoutModel(id, "deadline reached with no measurable progress to extend for");
|
|
49987
|
-
} else if (idleMs >= stallMs) {
|
|
49988
|
-
await timeoutModel(id, `no measurable progress for ${Math.round(idleMs / 1000)}s`);
|
|
49989
|
-
} else {
|
|
49990
|
-
if (!graceStartedAt.has(id))
|
|
49991
|
-
graceStartedAt.set(id, now2);
|
|
49992
|
-
extended.push(id);
|
|
49993
|
-
}
|
|
49994
|
-
}
|
|
49995
|
-
if (extended.length === 0)
|
|
49996
|
-
return;
|
|
49997
|
-
emitProgress("running");
|
|
49998
|
-
await delay(Math.min(GRACE_INTERVAL_MS, Math.max(1000, stallMs)));
|
|
49999
|
-
}
|
|
50000
|
-
})().catch(() => {});
|
|
50001
|
-
await Promise.race([allDone, deadlineWatcher]);
|
|
50002
|
-
if (!settled)
|
|
50003
|
-
await Promise.race([allDone, delay(DRAIN_TIMEOUT_MS)]);
|
|
50004
|
-
clearInterval(progressHandle);
|
|
50005
|
-
emitProgress("settled");
|
|
50006
|
-
process.off("SIGINT", sigintHandler);
|
|
50007
|
-
return statusCache;
|
|
50038
|
+
}
|
|
50039
|
+
async function runModels(sessionPath, opts = {}) {
|
|
50040
|
+
const handle = await startModels(sessionPath, opts);
|
|
50041
|
+
return handle.done;
|
|
50008
50042
|
}
|
|
50009
50043
|
async function judgeResponses(sessionPath, opts = {}) {
|
|
50010
50044
|
const responseFiles = readdirSync5(sessionPath).filter((f) => f.startsWith("response-") && f.endsWith(".md")).sort();
|
|
@@ -50186,16 +50220,17 @@ function formatVerdict(verdict, sessionPath) {
|
|
|
50186
50220
|
}
|
|
50187
50221
|
return output;
|
|
50188
50222
|
}
|
|
50189
|
-
var TEAM_CAPTURE_ENV_VAR = "CLAUDISH_TEAM_CAPTURE", STDOUT_TAIL_LIMIT = 4000, API_ERROR_RE, BG_CEILING_RE, DEFAULT_MIN_OUTPUT_BYTES = 0,
|
|
50190
|
-
const t = setTimeout(resolve4, ms);
|
|
50191
|
-
t.unref?.();
|
|
50192
|
-
}), BENIGN_STDERR_PATTERNS;
|
|
50223
|
+
var TEAM_CAPTURE_ENV_VAR = "CLAUDISH_TEAM_CAPTURE", liveTeamRuns, STDOUT_TAIL_LIMIT = 4000, API_ERROR_RE, BG_CEILING_RE, DEFAULT_MIN_OUTPUT_BYTES = 0, BENIGN_STDERR_PATTERNS;
|
|
50193
50224
|
var init_team_orchestrator = __esm(() => {
|
|
50194
50225
|
init_prehydrate();
|
|
50226
|
+
init_stream_json_reducer();
|
|
50227
|
+
init_config2();
|
|
50228
|
+
init_upstream_error_capture();
|
|
50195
50229
|
init_process_tree();
|
|
50196
50230
|
init_redact();
|
|
50231
|
+
init_stdio_decode();
|
|
50197
50232
|
init_team_stats();
|
|
50198
|
-
|
|
50233
|
+
liveTeamRuns = new Map;
|
|
50199
50234
|
API_ERROR_RE = /\[API Error:\s*([^\]]{0,300})\]/i;
|
|
50200
50235
|
BG_CEILING_RE = /Background tasks still running after (\d+)s; terminating/i;
|
|
50201
50236
|
BENIGN_STDERR_PATTERNS = [/^\s*\[claude-code:unrecognized_model\]/];
|
|
@@ -50217,7 +50252,7 @@ import {
|
|
|
50217
50252
|
} from "fs";
|
|
50218
50253
|
import { homedir as homedir28 } from "os";
|
|
50219
50254
|
import { join as join30, resolve as resolve4, sep } from "path";
|
|
50220
|
-
import { StringDecoder } from "string_decoder";
|
|
50255
|
+
import { StringDecoder as StringDecoder2 } from "string_decoder";
|
|
50221
50256
|
function buildChannelSpawnArgs(opts) {
|
|
50222
50257
|
return [
|
|
50223
50258
|
"--model",
|
|
@@ -50248,9 +50283,6 @@ function assertNoReservedFlags(flags) {
|
|
|
50248
50283
|
}
|
|
50249
50284
|
}
|
|
50250
50285
|
}
|
|
50251
|
-
function decodeChunk(decoder, chunk) {
|
|
50252
|
-
return typeof chunk === "string" ? chunk : decoder.write(chunk);
|
|
50253
|
-
}
|
|
50254
50286
|
function readTailText(path, maxBytes) {
|
|
50255
50287
|
let fd = null;
|
|
50256
50288
|
try {
|
|
@@ -50359,11 +50391,14 @@ class SessionManager {
|
|
|
50359
50391
|
throw new Error(`Max sessions (${this.maxSessions}) reached`);
|
|
50360
50392
|
}
|
|
50361
50393
|
assertNoReservedFlags(opts.claudishFlags);
|
|
50362
|
-
|
|
50394
|
+
if (opts.sessionId !== undefined && this.sessions.has(opts.sessionId)) {
|
|
50395
|
+
throw new Error(`Session id already in use: ${opts.sessionId}`);
|
|
50396
|
+
}
|
|
50397
|
+
const sessionId2 = opts.sessionId ?? randomUUID4().slice(0, 8);
|
|
50363
50398
|
const claudeSessionId = randomUUID4();
|
|
50364
50399
|
const timeout = Math.min(opts.timeoutSeconds ?? DEFAULT_TIMEOUT, MAX_TIMEOUT);
|
|
50365
50400
|
const startedAt = new Date().toISOString();
|
|
50366
|
-
const sessionDir = join30(this.sessionsDir, sessionId2);
|
|
50401
|
+
const sessionDir = opts.sessionDir ?? join30(this.sessionsDir, sessionId2);
|
|
50367
50402
|
mkdirSync12(sessionDir, { recursive: true });
|
|
50368
50403
|
if (opts.prompt) {
|
|
50369
50404
|
writeFileSync12(join30(sessionDir, "prompt.md"), opts.prompt, "utf-8");
|
|
@@ -50373,7 +50408,7 @@ class SessionManager {
|
|
|
50373
50408
|
claudeSessionId,
|
|
50374
50409
|
claudishFlags: opts.claudishFlags
|
|
50375
50410
|
});
|
|
50376
|
-
const tokenFile = join30(sessionDir, "tokens.json");
|
|
50411
|
+
const tokenFile = opts.tokenFile ?? join30(sessionDir, "tokens.json");
|
|
50377
50412
|
const eventLogPath = join30(sessionDir, "events.jsonl");
|
|
50378
50413
|
const upstreamErrorLogPath = join30(sessionDir, "upstream-errors.jsonl");
|
|
50379
50414
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -50399,6 +50434,7 @@ class SessionManager {
|
|
|
50399
50434
|
status: "starting",
|
|
50400
50435
|
pid: proc.pid ?? null,
|
|
50401
50436
|
startedAt,
|
|
50437
|
+
idleSeconds: 0,
|
|
50402
50438
|
completedAt: null,
|
|
50403
50439
|
exitCode: null,
|
|
50404
50440
|
turnsCompleted: 0,
|
|
@@ -50419,8 +50455,8 @@ class SessionManager {
|
|
|
50419
50455
|
evictHandle: null,
|
|
50420
50456
|
stderr: "",
|
|
50421
50457
|
stderrTruncated: false,
|
|
50422
|
-
stdoutDecoder: new
|
|
50423
|
-
stderrDecoder: new
|
|
50458
|
+
stdoutDecoder: new StringDecoder2("utf8"),
|
|
50459
|
+
stderrDecoder: new StringDecoder2("utf8"),
|
|
50424
50460
|
outputLogStream,
|
|
50425
50461
|
sessionDir,
|
|
50426
50462
|
eventLogPath,
|
|
@@ -50441,6 +50477,7 @@ class SessionManager {
|
|
|
50441
50477
|
entry.reducer = new StreamJsonReducer({
|
|
50442
50478
|
sessionId: sessionId2,
|
|
50443
50479
|
stallSeconds: this.stallSeconds,
|
|
50480
|
+
keepUnrecognizedJson: opts.keepUnrecognizedJson,
|
|
50444
50481
|
callback: (sid, data) => {
|
|
50445
50482
|
const current = this.sessions.get(sid);
|
|
50446
50483
|
if (!current)
|
|
@@ -50527,6 +50564,7 @@ class SessionManager {
|
|
|
50527
50564
|
if (!entry)
|
|
50528
50565
|
return this.diskOutput(this.requireDiskRecord(sessionId2), tailLines);
|
|
50529
50566
|
entry.info.elapsedSeconds = this.getElapsed(entry.info.startedAt);
|
|
50567
|
+
entry.info.idleSeconds = entry.reducer ? Math.round(entry.reducer.idleMs / 1000) : null;
|
|
50530
50568
|
this.refreshAccounting(entry);
|
|
50531
50569
|
const lines = entry.scrollback.getLines(tailLines);
|
|
50532
50570
|
return {
|
|
@@ -50537,7 +50575,8 @@ class SessionManager {
|
|
|
50537
50575
|
totalLines: entry.scrollback.totalLines,
|
|
50538
50576
|
turnsCompleted: entry.info.turnsCompleted,
|
|
50539
50577
|
tokensUsed: entry.info.tokensUsed,
|
|
50540
|
-
elapsedSeconds: entry.info.elapsedSeconds
|
|
50578
|
+
elapsedSeconds: entry.info.elapsedSeconds,
|
|
50579
|
+
idleSeconds: entry.info.idleSeconds
|
|
50541
50580
|
};
|
|
50542
50581
|
}
|
|
50543
50582
|
getDiagnostics(sessionId2, eventLimit = DEFAULT_EVENT_LIMIT) {
|
|
@@ -50547,6 +50586,7 @@ class SessionManager {
|
|
|
50547
50586
|
return this.diskDiagnostics(this.requireDiskRecord(sessionId2), limit);
|
|
50548
50587
|
this.refreshAccounting(entry);
|
|
50549
50588
|
entry.info.elapsedSeconds = this.getElapsed(entry.info.startedAt);
|
|
50589
|
+
entry.info.idleSeconds = entry.reducer ? Math.round(entry.reducer.idleMs / 1000) : null;
|
|
50550
50590
|
return {
|
|
50551
50591
|
sessionId: sessionId2,
|
|
50552
50592
|
status: entry.info.status,
|
|
@@ -50555,6 +50595,7 @@ class SessionManager {
|
|
|
50555
50595
|
exitCode: entry.info.exitCode,
|
|
50556
50596
|
terminalReason: entry.info.terminalReason,
|
|
50557
50597
|
elapsedSeconds: entry.info.elapsedSeconds,
|
|
50598
|
+
idleSeconds: entry.info.idleSeconds,
|
|
50558
50599
|
timeoutSeconds: entry.timeoutSeconds,
|
|
50559
50600
|
outputBytes: entry.proseBytes,
|
|
50560
50601
|
turnsCompleted: entry.info.turnsCompleted,
|
|
@@ -50613,6 +50654,7 @@ class SessionManager {
|
|
|
50613
50654
|
continue;
|
|
50614
50655
|
if (!isTerminal2) {
|
|
50615
50656
|
entry.info.elapsedSeconds = this.getElapsed(entry.info.startedAt);
|
|
50657
|
+
entry.info.idleSeconds = entry.reducer ? Math.round(entry.reducer.idleMs / 1000) : null;
|
|
50616
50658
|
this.refreshAccounting(entry);
|
|
50617
50659
|
}
|
|
50618
50660
|
sessions2.push({ ...entry.info });
|
|
@@ -50624,6 +50666,7 @@ class SessionManager {
|
|
|
50624
50666
|
if (!entry)
|
|
50625
50667
|
return this.requireDiskRecord(sessionId2).info;
|
|
50626
50668
|
entry.info.elapsedSeconds = this.getElapsed(entry.info.startedAt);
|
|
50669
|
+
entry.info.idleSeconds = entry.reducer ? Math.round(entry.reducer.idleMs / 1000) : null;
|
|
50627
50670
|
this.refreshAccounting(entry);
|
|
50628
50671
|
return { ...entry.info };
|
|
50629
50672
|
}
|
|
@@ -50706,6 +50749,7 @@ class SessionManager {
|
|
|
50706
50749
|
model: metaString(meta3?.model) ?? "unknown",
|
|
50707
50750
|
spawnModel: metaString(meta3?.spawnModel),
|
|
50708
50751
|
status: metaStatus(meta3?.status) ?? "failed",
|
|
50752
|
+
idleSeconds: null,
|
|
50709
50753
|
pid: null,
|
|
50710
50754
|
startedAt,
|
|
50711
50755
|
completedAt,
|
|
@@ -50735,7 +50779,8 @@ class SessionManager {
|
|
|
50735
50779
|
totalLines: buffer.totalLines,
|
|
50736
50780
|
turnsCompleted: record4.info.turnsCompleted,
|
|
50737
50781
|
tokensUsed: record4.info.tokensUsed,
|
|
50738
|
-
elapsedSeconds: record4.info.elapsedSeconds
|
|
50782
|
+
elapsedSeconds: record4.info.elapsedSeconds,
|
|
50783
|
+
idleSeconds: null
|
|
50739
50784
|
};
|
|
50740
50785
|
}
|
|
50741
50786
|
diskDiagnostics(record4, limit) {
|
|
@@ -50753,6 +50798,7 @@ class SessionManager {
|
|
|
50753
50798
|
exitCode: info.exitCode,
|
|
50754
50799
|
terminalReason: info.terminalReason,
|
|
50755
50800
|
elapsedSeconds: info.elapsedSeconds,
|
|
50801
|
+
idleSeconds: null,
|
|
50756
50802
|
timeoutSeconds: 0,
|
|
50757
50803
|
outputBytes: diskProseBytes(outputTail, fileSize(outputLogPath)),
|
|
50758
50804
|
turnsCompleted: info.turnsCompleted,
|
|
@@ -50912,6 +50958,7 @@ ${STDERR_TRUNCATION_MARKER} ${STDERR_SIDE_LIMIT} bytes per end \u2026
|
|
|
50912
50958
|
entry.info.exitCode = code;
|
|
50913
50959
|
entry.info.completedAt = at;
|
|
50914
50960
|
entry.info.elapsedSeconds = this.getElapsed(entry.info.startedAt);
|
|
50961
|
+
entry.info.idleSeconds = entry.reducer ? Math.round(entry.reducer.idleMs / 1000) : null;
|
|
50915
50962
|
entry.stdinClosed = true;
|
|
50916
50963
|
this.flushDecoders(entry);
|
|
50917
50964
|
const priorVerdict = TERMINAL_STATUSES.includes(entry.info.status) ? entry.info.status : null;
|
|
@@ -51076,13 +51123,14 @@ ${STDERR_TRUNCATION_MARKER} ${STDERR_SIDE_LIMIT} bytes per end \u2026
|
|
|
51076
51123
|
}
|
|
51077
51124
|
}
|
|
51078
51125
|
}
|
|
51079
|
-
var DEFAULT_MAX_SESSIONS = 20, DEFAULT_SCROLLBACK = 2000, DEFAULT_TIMEOUT = 600, MAX_TIMEOUT = 3600, KILL_GRACE_MS = 5000, TERMINAL_RETENTION_MS, MAX_TERMINAL_SESSIONS = 50, STDERR_SIDE_LIMIT, EVENT_LOG_LIMIT, EVENT_RING_SIZE = 200, EVENT_PREVIEW_CHARS = 800, DEFAULT_EVENT_LIMIT = 40, UPSTREAM_ERROR_TAIL_BYTES, STDERR_TRUNCATION_MARKER = "[claudish] \u2026 stderr truncated to", TERMINAL_STATUSES, KNOWN_STATUSES, SESSION_ID_RE, META_READ_LIMIT, OUTPUT_TAIL_BYTES, STDERR_READ_BYTES, EVENT_TAIL_BYTES, NO_TERMINAL_RECORD = "claudish_no_terminal_record", RESERVED_FLAG_ALIASES, TRANSPORT_BREAKING_FLAGS, RESERVED_CHILD_FLAGS, metaString = (v) => typeof v === "string" && v.length > 0 ? v : null, metaNumber = (v) => typeof v === "number" && Number.isFinite(v) ? v : null, metaStatus = (v) => typeof v === "string" && KNOWN_STATUSES.includes(v) ? v : null, CLAUDISH_NOTE_PREFIX = "[claudish] ";
|
|
51126
|
+
var DEFAULT_MAX_SESSIONS = 20, DRAIN_TIMEOUT_MS = 1e4, DEFAULT_SCROLLBACK = 2000, DEFAULT_TIMEOUT = 600, MAX_TIMEOUT = 3600, KILL_GRACE_MS = 5000, TERMINAL_RETENTION_MS, MAX_TERMINAL_SESSIONS = 50, STDERR_SIDE_LIMIT, EVENT_LOG_LIMIT, EVENT_RING_SIZE = 200, EVENT_PREVIEW_CHARS = 800, DEFAULT_EVENT_LIMIT = 40, UPSTREAM_ERROR_TAIL_BYTES, STDERR_TRUNCATION_MARKER = "[claudish] \u2026 stderr truncated to", TERMINAL_STATUSES, KNOWN_STATUSES, SESSION_ID_RE, META_READ_LIMIT, OUTPUT_TAIL_BYTES, STDERR_READ_BYTES, EVENT_TAIL_BYTES, NO_TERMINAL_RECORD = "claudish_no_terminal_record", RESERVED_FLAG_ALIASES, TRANSPORT_BREAKING_FLAGS, RESERVED_CHILD_FLAGS, metaString = (v) => typeof v === "string" && v.length > 0 ? v : null, metaNumber = (v) => typeof v === "number" && Number.isFinite(v) ? v : null, metaStatus = (v) => typeof v === "string" && KNOWN_STATUSES.includes(v) ? v : null, CLAUDISH_NOTE_PREFIX = "[claudish] ";
|
|
51080
51127
|
var init_session_manager = __esm(() => {
|
|
51081
51128
|
init_config2();
|
|
51082
51129
|
init_upstream_error_capture();
|
|
51083
51130
|
init_process_tree();
|
|
51084
51131
|
init_redact();
|
|
51085
51132
|
init_session_discovery();
|
|
51133
|
+
init_stdio_decode();
|
|
51086
51134
|
init_team_orchestrator();
|
|
51087
51135
|
init_team_stats();
|
|
51088
51136
|
init_scrollback_buffer();
|
|
@@ -56687,14 +56735,18 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
|
|
|
56687
56735
|
});
|
|
56688
56736
|
tools.push({
|
|
56689
56737
|
name: "team",
|
|
56690
|
-
description: "Run AI models on a task with anonymized outputs and optional blind judging. Modes: 'run' (
|
|
56738
|
+
description: "Run AI models on a task with anonymized outputs and optional blind judging. " + "Modes: 'run' (START the models and return a slot map immediately \u2014 it does NOT " + "wait), 'status' (per-slot state, plus how long each slot has been silent), " + "'cancel' (stop one slot or the whole run), 'judge' (blind-vote on existing " + "outputs), 'run-and-judge' (the blocking pipeline). " + "NO SLOT IS EVER KILLED ON A TIMER. A team slot is a full Claude Code session and " + "may work for a long time; a slot inside a build or test suite emits nothing for " + "minutes and is working, not stuck. Poll 'status', judge the silence against the " + "task you set, and use 'cancel' if you decide a slot is wedged.",
|
|
56691
56739
|
inputSchema: {
|
|
56692
56740
|
type: "object",
|
|
56693
56741
|
properties: {
|
|
56694
56742
|
mode: {
|
|
56695
56743
|
type: "string",
|
|
56696
|
-
enum: ["run", "judge", "run-and-judge", "status"],
|
|
56697
|
-
description: "Operation mode"
|
|
56744
|
+
enum: ["run", "judge", "run-and-judge", "status", "cancel"],
|
|
56745
|
+
description: "Operation mode. 'run' STARTS the models and returns immediately with a " + "slot map \u2014 it does not wait. Poll 'status' for progress, then 'judge' once " + "the slots have finished. 'run-and-judge' is the blocking pipeline and holds " + "the call open for the whole run. 'cancel' stops one slot or the whole run."
|
|
56746
|
+
},
|
|
56747
|
+
slot: {
|
|
56748
|
+
type: "string",
|
|
56749
|
+
description: "For 'cancel': the anonymised slot id to stop (e.g. '02'), from the slot map " + "'run' returned. Omit to cancel every slot in the run."
|
|
56698
56750
|
},
|
|
56699
56751
|
path: {
|
|
56700
56752
|
type: "string",
|
|
@@ -56710,11 +56762,14 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
|
|
|
56710
56762
|
items: { type: "string" },
|
|
56711
56763
|
description: "Model IDs to use as judges (default: same as runners)"
|
|
56712
56764
|
},
|
|
56765
|
+
input_file: {
|
|
56766
|
+
type: "string",
|
|
56767
|
+
description: "PREFERRED. Path to a file holding the task prompt, relative to the working " + "directory. Use this rather than `input` for anything longer than a sentence: " + "a prompt passed inline is echoed verbatim in the caller's terminal, where a " + "200-line review brief buries every other argument and makes the call " + "unreadable. Write the brief to the session directory first (input.md is the " + "conventional name) and point here."
|
|
56768
|
+
},
|
|
56713
56769
|
input: {
|
|
56714
56770
|
type: "string",
|
|
56715
|
-
description: "Task prompt text
|
|
56771
|
+
description: "Task prompt as inline text. Prefer `input_file` \u2014 inline text is rendered in " + "full in the caller's terminal. Passing both is an error. If neither is given, " + "an input.md already present in the session directory is used."
|
|
56716
56772
|
},
|
|
56717
|
-
timeout: { type: "number", description: "Per-model timeout in seconds (default: 300)" },
|
|
56718
56773
|
require_pattern: {
|
|
56719
56774
|
type: "string",
|
|
56720
56775
|
description: "Regex the response MUST match, or the slot is reported FAILED (state EMPTY, " + "reason 'shape_mismatch') instead of succeeded. Strongly recommended whenever " + "your prompt mandates an output shape \u2014 e.g. '```vote' for a voting panel. " + "Exit code 0 is not a success oracle: it is 0 on API errors and on a child " + "that simply never followed the format. Answers are no longer LOST to print " + "mode (every assistant message is captured), so a mismatch now means the model " + "did not produce the shape, not that the shape was discarded."
|
|
@@ -56742,8 +56797,12 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
|
|
|
56742
56797
|
const path = args.path;
|
|
56743
56798
|
const models = args.models;
|
|
56744
56799
|
const judges = args.judges;
|
|
56745
|
-
const
|
|
56746
|
-
const
|
|
56800
|
+
const inlineInput = args.input;
|
|
56801
|
+
const inputFile = args.input_file;
|
|
56802
|
+
if (inlineInput !== undefined && inputFile !== undefined) {
|
|
56803
|
+
throw new Error("Pass `input_file` or `input`, not both. Prefer `input_file` \u2014 inline text is " + "rendered verbatim in the caller's terminal.");
|
|
56804
|
+
}
|
|
56805
|
+
const input = inputFile !== undefined ? readTeamInputFile(inputFile) : inlineInput;
|
|
56747
56806
|
const requirePattern = args.require_pattern;
|
|
56748
56807
|
const minOutputBytes = args.min_output_bytes;
|
|
56749
56808
|
const childFlags = buildChildClaudeFlags(args.agent, args.claude_flags);
|
|
@@ -56752,7 +56811,6 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
|
|
|
56752
56811
|
const teamSessionId = resolved.split("/").filter(Boolean).pop() ?? "team";
|
|
56753
56812
|
const teamCreatedAt = new Date().toISOString();
|
|
56754
56813
|
const runOpts = {
|
|
56755
|
-
timeout,
|
|
56756
56814
|
requirePattern,
|
|
56757
56815
|
minOutputBytes,
|
|
56758
56816
|
claudeFlags: childFlags,
|
|
@@ -56773,9 +56831,51 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
|
|
|
56773
56831
|
if (!models?.length)
|
|
56774
56832
|
throw new Error("'models' is required for 'run' mode");
|
|
56775
56833
|
setupSession(resolved, models, input);
|
|
56776
|
-
const
|
|
56834
|
+
const handle = await startModels(resolved, runOpts);
|
|
56777
56835
|
return {
|
|
56778
|
-
content: [
|
|
56836
|
+
content: [
|
|
56837
|
+
{
|
|
56838
|
+
type: "text",
|
|
56839
|
+
text: JSON.stringify({
|
|
56840
|
+
started: true,
|
|
56841
|
+
team_session_id: handle.teamSessionId,
|
|
56842
|
+
session_path: handle.sessionPath,
|
|
56843
|
+
slots: handle.slots,
|
|
56844
|
+
next: {
|
|
56845
|
+
status: `team(mode:"status", path:"${handle.sessionPath}")`,
|
|
56846
|
+
cancel: `team(mode:"cancel", path:"${handle.sessionPath}", slot:"<id>")`,
|
|
56847
|
+
judge: `team(mode:"judge", path:"${handle.sessionPath}") once every slot has finished`
|
|
56848
|
+
},
|
|
56849
|
+
note: "Nothing terminates a slot on a timer. `status` reports how many " + "seconds each slot has been silent; a slot inside a long build is " + "quiet and working. You decide whether to cancel."
|
|
56850
|
+
}, null, 2)
|
|
56851
|
+
}
|
|
56852
|
+
]
|
|
56853
|
+
};
|
|
56854
|
+
}
|
|
56855
|
+
case "cancel": {
|
|
56856
|
+
const slot = args.slot;
|
|
56857
|
+
const teamSessionId2 = resolved.split("/").filter(Boolean).pop() ?? "team";
|
|
56858
|
+
const result = await cancelTeamRun(teamSessionId2, slot);
|
|
56859
|
+
if (!result.found) {
|
|
56860
|
+
return {
|
|
56861
|
+
content: [
|
|
56862
|
+
{
|
|
56863
|
+
type: "text",
|
|
56864
|
+
text: JSON.stringify({
|
|
56865
|
+
cancelled: [],
|
|
56866
|
+
note: "No live run for that path. It already settled (read `status`), or " + "it was started by a different process \u2014 this server can only stop " + "children it spawned."
|
|
56867
|
+
})
|
|
56868
|
+
}
|
|
56869
|
+
]
|
|
56870
|
+
};
|
|
56871
|
+
}
|
|
56872
|
+
return {
|
|
56873
|
+
content: [
|
|
56874
|
+
{
|
|
56875
|
+
type: "text",
|
|
56876
|
+
text: JSON.stringify({ cancelled: result.cancelled }, null, 2)
|
|
56877
|
+
}
|
|
56878
|
+
]
|
|
56779
56879
|
};
|
|
56780
56880
|
}
|
|
56781
56881
|
case "judge": {
|
|
@@ -56792,7 +56892,25 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
|
|
|
56792
56892
|
}
|
|
56793
56893
|
case "status": {
|
|
56794
56894
|
const status = getStatus(resolved);
|
|
56795
|
-
|
|
56895
|
+
const teamSessionId2 = resolved.split("/").filter(Boolean).pop() ?? "team";
|
|
56896
|
+
const idle = teamSlotIdleSeconds(teamSessionId2);
|
|
56897
|
+
const settled = !Object.values(status.models).some((m) => m.state === "RUNNING");
|
|
56898
|
+
return {
|
|
56899
|
+
content: [
|
|
56900
|
+
{
|
|
56901
|
+
type: "text",
|
|
56902
|
+
text: JSON.stringify({
|
|
56903
|
+
...status,
|
|
56904
|
+
idle_seconds_by_slot: idle,
|
|
56905
|
+
activity_by_slot: teamSlotActivity(teamSessionId2),
|
|
56906
|
+
...idle ? {
|
|
56907
|
+
note: "idle_seconds_by_slot is how long each slot has been silent; " + "read it against activity_by_slot. Silence in tool_executing " + "is a build or test suite running, and is not a failure " + 'signal. Nothing cancels on your behalf \u2014 use mode:"cancel" ' + "if you decide to."
|
|
56908
|
+
} : {},
|
|
56909
|
+
...settled ? { summary: formatTeamResult(status, resolved) } : {}
|
|
56910
|
+
}, null, 2)
|
|
56911
|
+
}
|
|
56912
|
+
]
|
|
56913
|
+
};
|
|
56796
56914
|
}
|
|
56797
56915
|
default:
|
|
56798
56916
|
throw new Error(`Unknown mode: ${mode}`);
|
|
@@ -57112,7 +57230,7 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
|
|
|
57112
57230
|
});
|
|
57113
57231
|
tools.push({
|
|
57114
57232
|
name: "list_sessions",
|
|
57115
|
-
description: "List all active channel sessions. Optionally include completed sessions.",
|
|
57233
|
+
description: "List all active channel sessions. Optionally include completed sessions. " + "Each session reports `idleSeconds`: how long since the child last emitted " + "anything. Nothing kills a session for being idle \u2014 a child inside a long " + "Bash call is silent and working \u2014 so this is yours to judge against the " + "task you set, and `cancel_session` is yours to call if the answer is no.",
|
|
57116
57234
|
inputSchema: {
|
|
57117
57235
|
type: "object",
|
|
57118
57236
|
properties: {
|
|
@@ -57132,7 +57250,7 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
|
|
|
57132
57250
|
});
|
|
57133
57251
|
tools.push({
|
|
57134
57252
|
name: "get_diagnostics",
|
|
57135
|
-
description: "Explain what a channel session actually did \u2014 stderr, upstream error bodies, the " + "recent event frames, the resolved model chain, accounting, and the paths to the " + "full records. Call this FIRST whenever a session fails, times out, or completes " + "with empty or surprising output; it needs no re-run and no debug flag.",
|
|
57253
|
+
description: "Explain what a channel session actually did \u2014 stderr, upstream error bodies, the " + "recent event frames, the resolved model chain, accounting, and the paths to the " + "full records. Call this FIRST whenever a session fails, times out, or completes " + "with empty or surprising output; it needs no re-run and no debug flag. " + "`idleSeconds` reports how long since the child last emitted a frame, and is " + "null once the session is no longer live. It is information, never a verdict: " + "claudish does not terminate a session for silence.",
|
|
57136
57254
|
inputSchema: {
|
|
57137
57255
|
type: "object",
|
|
57138
57256
|
properties: {
|
|
@@ -57289,6 +57407,7 @@ Call get_diagnostics with session_id: "${sessionId2}" for the stderr, the upstre
|
|
|
57289
57407
|
await server.connect(transport);
|
|
57290
57408
|
process.on("SIGTERM", () => {
|
|
57291
57409
|
sessionManager.shutdownAll().catch(() => {});
|
|
57410
|
+
shutdownAllTeamRuns().catch(() => {});
|
|
57292
57411
|
});
|
|
57293
57412
|
}
|
|
57294
57413
|
function startMcpServer() {
|
|
@@ -57356,7 +57475,8 @@ var init_mcp_server = __esm(() => {
|
|
|
57356
57475
|
ALL_MODELS_CACHE_PATH2 = join34(CLAUDISH_CACHE_DIR, "all-models.json");
|
|
57357
57476
|
NEXT_STEP = {
|
|
57358
57477
|
nonzero_exit: "read the evidence log, then retry or drop the model",
|
|
57359
|
-
|
|
57478
|
+
cancelled: "you stopped this slot; nothing is wrong with it. Re-run it if you still want its vote",
|
|
57479
|
+
timeout: "grid mode only \u2014 magmux ended the pane. The orchestrator has no deadline",
|
|
57360
57480
|
api_error: "retry once, or route via a different provider (or@<model>)",
|
|
57361
57481
|
background_task_ceiling: "set CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS=0 for children, or forbid background work in the prompt",
|
|
57362
57482
|
empty_output: "retry once; if it repeats, drop the model",
|
|
@@ -58239,7 +58359,8 @@ Options (run / run-and-judge):
|
|
|
58239
58359
|
--path <dir> Session directory (default: .)
|
|
58240
58360
|
--models <a,b,...> Comma-separated model IDs to run
|
|
58241
58361
|
--input <text> Task prompt (or create input.md in --path beforehand)
|
|
58242
|
-
--timeout <secs>
|
|
58362
|
+
--timeout <secs> Grid modes only: magmux's own per-pane timeout (default: 300).
|
|
58363
|
+
json mode has no deadline \u2014 nothing kills a working model.
|
|
58243
58364
|
--grid Show all models in a magmux grid with live output + status bar
|
|
58244
58365
|
|
|
58245
58366
|
Options (judge / run-and-judge):
|
|
@@ -58306,7 +58427,6 @@ async function teamCommand(args) {
|
|
|
58306
58427
|
if (effectiveMode === "json") {
|
|
58307
58428
|
setupSession(sessionPath, models, input);
|
|
58308
58429
|
const runStatus = await runModels(sessionPath, {
|
|
58309
|
-
timeout,
|
|
58310
58430
|
onStatusChange: (id, s) => {
|
|
58311
58431
|
process.stderr.write(`[team] ${id}: ${s.state}
|
|
58312
58432
|
`);
|
|
@@ -58335,7 +58455,6 @@ async function teamCommand(args) {
|
|
|
58335
58455
|
}
|
|
58336
58456
|
setupSession(sessionPath, models, input);
|
|
58337
58457
|
const status = await runModels(sessionPath, {
|
|
58338
|
-
timeout,
|
|
58339
58458
|
onStatusChange: (id, s) => {
|
|
58340
58459
|
process.stderr.write(`[team] ${id}: ${s.state}
|
|
58341
58460
|
`);
|
|
@@ -66218,9 +66337,9 @@ var require_internal = __commonJS(function(exports, module) {
|
|
|
66218
66337
|
}
|
|
66219
66338
|
InternalCodec.prototype.encoder = InternalEncoder;
|
|
66220
66339
|
InternalCodec.prototype.decoder = InternalDecoder;
|
|
66221
|
-
var
|
|
66340
|
+
var StringDecoder3 = __require("string_decoder").StringDecoder;
|
|
66222
66341
|
function InternalDecoder(options, codec2) {
|
|
66223
|
-
this.decoder = new
|
|
66342
|
+
this.decoder = new StringDecoder3(codec2.enc);
|
|
66224
66343
|
}
|
|
66225
66344
|
InternalDecoder.prototype.write = function(buf) {
|
|
66226
66345
|
if (!Buffer2.isBuffer(buf)) {
|
|
@@ -86111,7 +86230,7 @@ var init_widgets = __esm(() => {
|
|
|
86111
86230
|
|
|
86112
86231
|
// src/session/conversation.ts
|
|
86113
86232
|
import { closeSync as closeSync9, openSync as openSync9, readSync as readSync4, statSync as statSync9 } from "fs";
|
|
86114
|
-
import { StringDecoder as
|
|
86233
|
+
import { StringDecoder as StringDecoder3 } from "string_decoder";
|
|
86115
86234
|
function looksLikeTurn(line) {
|
|
86116
86235
|
const assistant = line.includes('"type":"assistant"');
|
|
86117
86236
|
if (!assistant && !line.includes('"type":"user"'))
|
|
@@ -86168,7 +86287,7 @@ function readConversation(file2, opts = {}) {
|
|
|
86168
86287
|
const size = statSync9(file2).size;
|
|
86169
86288
|
fd = openSync9(file2, "r");
|
|
86170
86289
|
const buf = Buffer.allocUnsafe(chunkBytes);
|
|
86171
|
-
const decoder = new
|
|
86290
|
+
const decoder = new StringDecoder3("utf-8");
|
|
86172
86291
|
let pending = "";
|
|
86173
86292
|
let pos = 0;
|
|
86174
86293
|
const consume = (line) => {
|
|
@@ -88463,7 +88582,6 @@ async function runCli() {
|
|
|
88463
88582
|
const { setupSession: setupSession2, runModels: runModels2 } = await Promise.resolve().then(() => (init_team_orchestrator(), exports_team_orchestrator));
|
|
88464
88583
|
setupSession2(sessionPath, cliConfig.team, prompt);
|
|
88465
88584
|
const status2 = await runModels2(sessionPath, {
|
|
88466
|
-
timeout: 300,
|
|
88467
88585
|
claudeFlags: ["--json"]
|
|
88468
88586
|
});
|
|
88469
88587
|
const result = { ...status2, responses: {} };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudish",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.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": "
|
|
64
|
-
"@claudish/magmux-darwin-x64": "
|
|
65
|
-
"@claudish/magmux-linux-arm64": "
|
|
66
|
-
"@claudish/magmux-linux-x64": "
|
|
63
|
+
"@claudish/magmux-darwin-arm64": "8.0.0",
|
|
64
|
+
"@claudish/magmux-darwin-x64": "8.0.0",
|
|
65
|
+
"@claudish/magmux-linux-arm64": "8.0.0",
|
|
66
|
+
"@claudish/magmux-linux-x64": "8.0.0"
|
|
67
67
|
},
|
|
68
68
|
"author": "Jack Rudenko <i@madappgang.com>",
|
|
69
69
|
"license": "MIT",
|