chatroom-cli 1.55.0 → 1.55.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 +5 -4
- package/dist/index.js +497 -249
- package/dist/index.js.map +23 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -26374,12 +26374,7 @@ var init_pi_agent_service = __esm(() => {
|
|
|
26374
26374
|
});
|
|
26375
26375
|
}
|
|
26376
26376
|
spawnPiRpcProcess(args2) {
|
|
26377
|
-
const rpcArgs = [
|
|
26378
|
-
"--mode",
|
|
26379
|
-
"rpc",
|
|
26380
|
-
"--session-dir",
|
|
26381
|
-
getPiSessionDir(args2.workingDir)
|
|
26382
|
-
];
|
|
26377
|
+
const rpcArgs = ["--mode", "rpc", "--session-dir", getPiSessionDir(args2.workingDir)];
|
|
26383
26378
|
if (args2.sessionId) {
|
|
26384
26379
|
rpcArgs.push("--session", args2.sessionId);
|
|
26385
26380
|
}
|
|
@@ -26441,6 +26436,9 @@ var init_pi_agent_service = __esm(() => {
|
|
|
26441
26436
|
wireRpcProcess(args2) {
|
|
26442
26437
|
const { childProcess, context: context4, workingDir, model, harnessSessionId } = args2;
|
|
26443
26438
|
const pid = childProcess.pid;
|
|
26439
|
+
if (pid == null) {
|
|
26440
|
+
throw new Error("Pi RPC process has no PID");
|
|
26441
|
+
}
|
|
26444
26442
|
this.childProcesses.set(pid, childProcess);
|
|
26445
26443
|
this.trackedSessions.set(pid, { harnessSessionId, workingDir, model });
|
|
26446
26444
|
const entry = this.registerProcess(pid, context4);
|
|
@@ -26448,7 +26446,12 @@ var init_pi_agent_service = __esm(() => {
|
|
|
26448
26446
|
const chatroomSuffix = context4.chatroomId ? `@${context4.chatroomId.slice(-6)}` : "";
|
|
26449
26447
|
const logPrefix = `[pi:${roleTag}${chatroomSuffix}`;
|
|
26450
26448
|
const outputCallbacks = [];
|
|
26449
|
+
const logLineCallbacks = [];
|
|
26451
26450
|
const agentEndCallbacks = [];
|
|
26451
|
+
const emitLogLine = (line) => {
|
|
26452
|
+
for (const cb of logLineCallbacks)
|
|
26453
|
+
cb(line);
|
|
26454
|
+
};
|
|
26452
26455
|
const onExit4 = (cb) => {
|
|
26453
26456
|
childProcess.on("exit", (code2, signal) => {
|
|
26454
26457
|
this.childProcesses.delete(pid);
|
|
@@ -26463,6 +26466,9 @@ var init_pi_agent_service = __esm(() => {
|
|
|
26463
26466
|
const onAgentEnd = (cb) => {
|
|
26464
26467
|
agentEndCallbacks.push(cb);
|
|
26465
26468
|
};
|
|
26469
|
+
const onLogLine = (cb) => {
|
|
26470
|
+
logLineCallbacks.push(cb);
|
|
26471
|
+
};
|
|
26466
26472
|
const baseResult = {
|
|
26467
26473
|
pid,
|
|
26468
26474
|
harnessSessionId,
|
|
@@ -26471,43 +26477,54 @@ var init_pi_agent_service = __esm(() => {
|
|
|
26471
26477
|
...model ? { model } : {}
|
|
26472
26478
|
},
|
|
26473
26479
|
onExit: onExit4,
|
|
26474
|
-
onOutput
|
|
26480
|
+
onOutput,
|
|
26481
|
+
onLogLine
|
|
26482
|
+
};
|
|
26483
|
+
const writeFormattedLogLine = (formatted) => {
|
|
26484
|
+
process.stdout.write(`${formatted}
|
|
26485
|
+
`);
|
|
26486
|
+
emitLogLine(formatted);
|
|
26487
|
+
};
|
|
26488
|
+
const onStderrData = (chunk2) => {
|
|
26489
|
+
entry.lastOutputAt = Date.now();
|
|
26490
|
+
for (const cb of outputCallbacks)
|
|
26491
|
+
cb();
|
|
26492
|
+
emitLogLine(chunk2.toString());
|
|
26493
|
+
};
|
|
26494
|
+
const attachStderr = (stream) => {
|
|
26495
|
+
if (!stream)
|
|
26496
|
+
return;
|
|
26497
|
+
stream.pipe(process.stderr, { end: false });
|
|
26498
|
+
stream.on("data", onStderrData);
|
|
26475
26499
|
};
|
|
26476
26500
|
if (!childProcess.stdout) {
|
|
26477
|
-
|
|
26478
|
-
childProcess.stderr.pipe(process.stderr, { end: false });
|
|
26479
|
-
childProcess.stderr.on("data", () => {
|
|
26480
|
-
entry.lastOutputAt = Date.now();
|
|
26481
|
-
for (const cb of outputCallbacks)
|
|
26482
|
-
cb();
|
|
26483
|
-
});
|
|
26484
|
-
}
|
|
26501
|
+
attachStderr(childProcess.stderr);
|
|
26485
26502
|
return baseResult;
|
|
26486
26503
|
}
|
|
26487
26504
|
const reader = args2.reader ?? new PiRpcReader(childProcess.stdout);
|
|
26488
26505
|
let textBuffer = "";
|
|
26489
26506
|
let thinkingBuffer = "";
|
|
26490
|
-
const
|
|
26491
|
-
if (!
|
|
26507
|
+
const flushBufferedLines = (buffer, kind, clear) => {
|
|
26508
|
+
if (!buffer)
|
|
26492
26509
|
return;
|
|
26493
|
-
for (const line of
|
|
26510
|
+
for (const line of buffer.split(`
|
|
26494
26511
|
`)) {
|
|
26495
|
-
if (line)
|
|
26496
|
-
|
|
26497
|
-
|
|
26512
|
+
if (line) {
|
|
26513
|
+
writeFormattedLogLine(`${logPrefix} ${kind}] ${line}`);
|
|
26514
|
+
}
|
|
26498
26515
|
}
|
|
26499
|
-
|
|
26516
|
+
clear();
|
|
26500
26517
|
};
|
|
26501
|
-
const
|
|
26502
|
-
|
|
26503
|
-
|
|
26504
|
-
|
|
26505
|
-
`)) {
|
|
26506
|
-
if (line)
|
|
26507
|
-
process.stdout.write(`${logPrefix} thinking] ${line}
|
|
26508
|
-
`);
|
|
26509
|
-
}
|
|
26518
|
+
const flushText = () => flushBufferedLines(textBuffer, "text", () => {
|
|
26519
|
+
textBuffer = "";
|
|
26520
|
+
});
|
|
26521
|
+
const flushThinking = () => flushBufferedLines(thinkingBuffer, "thinking", () => {
|
|
26510
26522
|
thinkingBuffer = "";
|
|
26523
|
+
});
|
|
26524
|
+
const notifyOutput = () => {
|
|
26525
|
+
entry.lastOutputAt = Date.now();
|
|
26526
|
+
for (const cb of outputCallbacks)
|
|
26527
|
+
cb();
|
|
26511
26528
|
};
|
|
26512
26529
|
reader.onTextDelta((delta) => {
|
|
26513
26530
|
flushThinking();
|
|
@@ -26515,9 +26532,7 @@ var init_pi_agent_service = __esm(() => {
|
|
|
26515
26532
|
if (textBuffer.includes(`
|
|
26516
26533
|
`))
|
|
26517
26534
|
flushText();
|
|
26518
|
-
|
|
26519
|
-
for (const cb of outputCallbacks)
|
|
26520
|
-
cb();
|
|
26535
|
+
notifyOutput();
|
|
26521
26536
|
});
|
|
26522
26537
|
reader.onThinkingDelta((delta) => {
|
|
26523
26538
|
flushText();
|
|
@@ -26525,14 +26540,10 @@ var init_pi_agent_service = __esm(() => {
|
|
|
26525
26540
|
if (thinkingBuffer.includes(`
|
|
26526
26541
|
`))
|
|
26527
26542
|
flushThinking();
|
|
26528
|
-
|
|
26529
|
-
for (const cb of outputCallbacks)
|
|
26530
|
-
cb();
|
|
26543
|
+
notifyOutput();
|
|
26531
26544
|
});
|
|
26532
26545
|
reader.onAnyEvent(() => {
|
|
26533
|
-
|
|
26534
|
-
for (const cb of outputCallbacks)
|
|
26535
|
-
cb();
|
|
26546
|
+
notifyOutput();
|
|
26536
26547
|
});
|
|
26537
26548
|
reader.onAgentEnd(() => {
|
|
26538
26549
|
flushText();
|
|
@@ -26546,22 +26557,13 @@ var init_pi_agent_service = __esm(() => {
|
|
|
26546
26557
|
flushText();
|
|
26547
26558
|
flushThinking();
|
|
26548
26559
|
const argsStr = toolArgs != null ? ` args: ${JSON.stringify(toolArgs)}` : "";
|
|
26549
|
-
|
|
26550
|
-
`);
|
|
26560
|
+
writeFormattedLogLine(`${logPrefix} tool: ${name}${argsStr}]`);
|
|
26551
26561
|
});
|
|
26552
26562
|
reader.onToolResult((name, result) => {
|
|
26553
26563
|
const resultStr = typeof result === "string" ? result : JSON.stringify(result);
|
|
26554
|
-
|
|
26555
|
-
`);
|
|
26564
|
+
writeFormattedLogLine(`${logPrefix} tool_result: ${name} result: ${resultStr}]`);
|
|
26556
26565
|
});
|
|
26557
|
-
|
|
26558
|
-
childProcess.stderr.pipe(process.stderr, { end: false });
|
|
26559
|
-
childProcess.stderr.on("data", () => {
|
|
26560
|
-
entry.lastOutputAt = Date.now();
|
|
26561
|
-
for (const cb of outputCallbacks)
|
|
26562
|
-
cb();
|
|
26563
|
-
});
|
|
26564
|
-
}
|
|
26566
|
+
attachStderr(childProcess.stderr);
|
|
26565
26567
|
return {
|
|
26566
26568
|
...baseResult,
|
|
26567
26569
|
onAgentEnd
|
|
@@ -27629,12 +27631,14 @@ function closeCursorAgentOnFailure(agent, session, exitCode, force = false) {
|
|
|
27629
27631
|
// src/infrastructure/services/remote-agents/cursor-sdk/cursor-sdk-stream-adapter.ts
|
|
27630
27632
|
class CursorSdkStreamAdapter {
|
|
27631
27633
|
logPrefix;
|
|
27634
|
+
emitLogLine;
|
|
27632
27635
|
agentEndCallbacks = [];
|
|
27633
27636
|
outputCallbacks = [];
|
|
27634
27637
|
agentEndEmitted = false;
|
|
27635
27638
|
textBuffer = "";
|
|
27636
|
-
constructor(logPrefix) {
|
|
27639
|
+
constructor(logPrefix, emitLogLine) {
|
|
27637
27640
|
this.logPrefix = logPrefix;
|
|
27641
|
+
this.emitLogLine = emitLogLine;
|
|
27638
27642
|
}
|
|
27639
27643
|
onAgentEnd(cb) {
|
|
27640
27644
|
this.agentEndCallbacks.push(cb);
|
|
@@ -27650,21 +27654,17 @@ class CursorSdkStreamAdapter {
|
|
|
27650
27654
|
break;
|
|
27651
27655
|
case "tool_call":
|
|
27652
27656
|
this.flushText();
|
|
27653
|
-
|
|
27654
|
-
`);
|
|
27657
|
+
this.writeLine(`${this.logPrefix} tool: ${message.call_id} ${message.name} ${JSON.stringify({ status: message.status, args: message.args })}]`);
|
|
27655
27658
|
break;
|
|
27656
27659
|
case "status":
|
|
27657
|
-
|
|
27658
|
-
`);
|
|
27660
|
+
this.writeLine(`${this.logPrefix} status: ${message.status}]`);
|
|
27659
27661
|
break;
|
|
27660
27662
|
case "thinking":
|
|
27661
|
-
|
|
27662
|
-
`);
|
|
27663
|
+
this.writeLine(`${this.logPrefix} thinking] ${message.text}`);
|
|
27663
27664
|
break;
|
|
27664
27665
|
case "system":
|
|
27665
27666
|
if (message.subtype === "init") {
|
|
27666
|
-
|
|
27667
|
-
`);
|
|
27667
|
+
this.writeLine(`${this.logPrefix} system: init]`);
|
|
27668
27668
|
}
|
|
27669
27669
|
break;
|
|
27670
27670
|
default:
|
|
@@ -27695,8 +27695,7 @@ class CursorSdkStreamAdapter {
|
|
|
27695
27695
|
for (const line of this.textBuffer.split(`
|
|
27696
27696
|
`)) {
|
|
27697
27697
|
if (line)
|
|
27698
|
-
|
|
27699
|
-
`);
|
|
27698
|
+
this.writeLine(`${this.logPrefix} text] ${line}`);
|
|
27700
27699
|
}
|
|
27701
27700
|
this.textBuffer = "";
|
|
27702
27701
|
}
|
|
@@ -27705,11 +27704,15 @@ class CursorSdkStreamAdapter {
|
|
|
27705
27704
|
return;
|
|
27706
27705
|
this.agentEndEmitted = true;
|
|
27707
27706
|
this.flushText();
|
|
27708
|
-
|
|
27709
|
-
`);
|
|
27707
|
+
this.writeLine(`${this.logPrefix} agent_end]`);
|
|
27710
27708
|
for (const cb of this.agentEndCallbacks)
|
|
27711
27709
|
cb();
|
|
27712
27710
|
}
|
|
27711
|
+
writeLine(formatted) {
|
|
27712
|
+
process.stdout.write(`${formatted}
|
|
27713
|
+
`);
|
|
27714
|
+
this.emitLogLine?.(formatted);
|
|
27715
|
+
}
|
|
27713
27716
|
notifyOutput() {
|
|
27714
27717
|
for (const cb of this.outputCallbacks)
|
|
27715
27718
|
cb();
|
|
@@ -27782,10 +27785,11 @@ function buildLogPrefix(context4) {
|
|
|
27782
27785
|
function resolveModelId(model) {
|
|
27783
27786
|
return model ? resolveCursorSdkModel(model) : DEFAULT_MODEL;
|
|
27784
27787
|
}
|
|
27785
|
-
function writeSpawnError(logPrefix, err) {
|
|
27786
|
-
const
|
|
27787
|
-
process.stderr.write(`${
|
|
27788
|
+
function writeSpawnError(logPrefix, err, emitLogLine) {
|
|
27789
|
+
const line = `${logPrefix} spawn-error] ${formatCursorSdkLoadError(err)}`;
|
|
27790
|
+
process.stderr.write(`${line}
|
|
27788
27791
|
`);
|
|
27792
|
+
emitLogLine?.(line);
|
|
27789
27793
|
console.error(`[${new Date().toISOString()}] ${logPrefix} spawn-error]`, err);
|
|
27790
27794
|
}
|
|
27791
27795
|
var NO_SUBAGENT_DIRECTIVE2 = "NEVER spawn subagents. Follow the chatroom instructions strictly.", _sdkCache, _sdkLoadError, CURSOR_SDK_COMMAND = "cursor-sdk", DEFAULT_MODEL = "composer-2.5", AGENT_CREATE_TIMEOUT_MS = 60000, SEND_TIMEOUT_MS = 60000, RUN_WAIT_TIMEOUT_MS = 3600000, MODELS_LIST_TIMEOUT_MS = 1e4, RUN_CANCEL_TIMEOUT_MS = 5000, cachedSdkPackageVersion, CursorSdkAgentService;
|
|
@@ -27973,6 +27977,11 @@ ${options.prompt}`;
|
|
|
27973
27977
|
const exitCallbacks = [];
|
|
27974
27978
|
const outputCallbacks = [];
|
|
27975
27979
|
const agentEndCallbacks = [];
|
|
27980
|
+
const logLineCallbacks = [];
|
|
27981
|
+
const emitLogLine = (line) => {
|
|
27982
|
+
for (const cb of logLineCallbacks)
|
|
27983
|
+
cb(line);
|
|
27984
|
+
};
|
|
27976
27985
|
const finishExit = (code2, signal) => {
|
|
27977
27986
|
this.sessions.delete(pid);
|
|
27978
27987
|
this.deleteProcess(pid);
|
|
@@ -27991,7 +28000,8 @@ ${options.prompt}`;
|
|
|
27991
28000
|
forceFirstTurn,
|
|
27992
28001
|
finishExit,
|
|
27993
28002
|
outputCallbacks,
|
|
27994
|
-
agentEndCallbacks
|
|
28003
|
+
agentEndCallbacks,
|
|
28004
|
+
emitLogLine
|
|
27995
28005
|
});
|
|
27996
28006
|
return {
|
|
27997
28007
|
pid,
|
|
@@ -28008,6 +28018,9 @@ ${options.prompt}`;
|
|
|
28008
28018
|
},
|
|
28009
28019
|
onAgentEnd: (cb) => {
|
|
28010
28020
|
agentEndCallbacks.push(cb);
|
|
28021
|
+
},
|
|
28022
|
+
onLogLine: (cb) => {
|
|
28023
|
+
logLineCallbacks.push(cb);
|
|
28011
28024
|
}
|
|
28012
28025
|
};
|
|
28013
28026
|
}
|
|
@@ -28021,7 +28034,8 @@ ${options.prompt}`;
|
|
|
28021
28034
|
finishExit,
|
|
28022
28035
|
entry,
|
|
28023
28036
|
outputCallbacks,
|
|
28024
|
-
agentEndCallbacks
|
|
28037
|
+
agentEndCallbacks,
|
|
28038
|
+
emitLogLine
|
|
28025
28039
|
} = args2;
|
|
28026
28040
|
let exited = false;
|
|
28027
28041
|
(async () => {
|
|
@@ -28038,7 +28052,7 @@ ${options.prompt}`;
|
|
|
28038
28052
|
}), SEND_TIMEOUT_MS, "agent.send");
|
|
28039
28053
|
session.run = run3;
|
|
28040
28054
|
isFirstTurn = false;
|
|
28041
|
-
const adapter = new CursorSdkStreamAdapter(logPrefix);
|
|
28055
|
+
const adapter = new CursorSdkStreamAdapter(logPrefix, emitLogLine);
|
|
28042
28056
|
adapter.onOutput(() => {
|
|
28043
28057
|
entry.lastOutputAt = Date.now();
|
|
28044
28058
|
for (const cb of outputCallbacks)
|
|
@@ -28056,7 +28070,7 @@ ${options.prompt}`;
|
|
|
28056
28070
|
}
|
|
28057
28071
|
} catch (streamErr) {
|
|
28058
28072
|
exitCode = 1;
|
|
28059
|
-
writeSpawnError(logPrefix, streamErr);
|
|
28073
|
+
writeSpawnError(logPrefix, streamErr, emitLogLine);
|
|
28060
28074
|
break;
|
|
28061
28075
|
}
|
|
28062
28076
|
if (session.aborted) {
|
|
@@ -28069,14 +28083,16 @@ ${options.prompt}`;
|
|
|
28069
28083
|
result = await withTimeout(run3.wait(), RUN_WAIT_TIMEOUT_MS, "run.wait");
|
|
28070
28084
|
} catch (waitErr) {
|
|
28071
28085
|
exitCode = 1;
|
|
28072
|
-
writeSpawnError(logPrefix, waitErr);
|
|
28086
|
+
writeSpawnError(logPrefix, waitErr, emitLogLine);
|
|
28073
28087
|
break;
|
|
28074
28088
|
}
|
|
28075
28089
|
adapter.flushPendingOutput();
|
|
28076
28090
|
if (result.status === "error") {
|
|
28077
28091
|
exitCode = 2;
|
|
28078
|
-
|
|
28092
|
+
const runErrorLine = `${logPrefix} run-error] run ${result.id} failed`;
|
|
28093
|
+
process.stderr.write(`${runErrorLine}
|
|
28079
28094
|
`);
|
|
28095
|
+
emitLogLine(runErrorLine);
|
|
28080
28096
|
break;
|
|
28081
28097
|
}
|
|
28082
28098
|
const resumePromise = waitForResumeOrAbort(session);
|
|
@@ -28092,13 +28108,13 @@ ${options.prompt}`;
|
|
|
28092
28108
|
nextPrompt = resumePrompt;
|
|
28093
28109
|
} catch (turnErr) {
|
|
28094
28110
|
exitCode = 1;
|
|
28095
|
-
writeSpawnError(logPrefix, turnErr);
|
|
28111
|
+
writeSpawnError(logPrefix, turnErr, emitLogLine);
|
|
28096
28112
|
break;
|
|
28097
28113
|
}
|
|
28098
28114
|
}
|
|
28099
28115
|
} catch (err) {
|
|
28100
28116
|
exitCode = 1;
|
|
28101
|
-
writeSpawnError(logPrefix, err);
|
|
28117
|
+
writeSpawnError(logPrefix, err, emitLogLine);
|
|
28102
28118
|
} finally {
|
|
28103
28119
|
if (exited)
|
|
28104
28120
|
return;
|
|
@@ -28110,7 +28126,7 @@ ${options.prompt}`;
|
|
|
28110
28126
|
finishExit(exitCode, exitSignal);
|
|
28111
28127
|
}
|
|
28112
28128
|
})().catch((err) => {
|
|
28113
|
-
writeSpawnError(logPrefix, err);
|
|
28129
|
+
writeSpawnError(logPrefix, err, emitLogLine);
|
|
28114
28130
|
if (exited)
|
|
28115
28131
|
return;
|
|
28116
28132
|
exited = true;
|
|
@@ -30181,6 +30197,12 @@ function formatLogLine(options, kind, payload) {
|
|
|
30181
30197
|
const ts = options.now ? options.now() : new Date().toISOString();
|
|
30182
30198
|
return `[${ts}] role:${options.role} ${kind}]${payload ? ` ${payload}` : ""}`;
|
|
30183
30199
|
}
|
|
30200
|
+
function writeLogLine(target, options, kind, payload) {
|
|
30201
|
+
const line = formatLogLine(options, kind, payload);
|
|
30202
|
+
target.write(`${line}
|
|
30203
|
+
`);
|
|
30204
|
+
options.onLogLine?.(line);
|
|
30205
|
+
}
|
|
30184
30206
|
function isUsageLimitError(error) {
|
|
30185
30207
|
if (!error || typeof error !== "object")
|
|
30186
30208
|
return false;
|
|
@@ -30236,8 +30258,7 @@ function startSessionEventForwarder(client4, options) {
|
|
|
30236
30258
|
continue;
|
|
30237
30259
|
if (!sessionStarted) {
|
|
30238
30260
|
sessionStarted = true;
|
|
30239
|
-
target
|
|
30240
|
-
`);
|
|
30261
|
+
writeLogLine(target, options, "session] Started", `role: ${options.role}`);
|
|
30241
30262
|
}
|
|
30242
30263
|
switch (event.type) {
|
|
30243
30264
|
case "message.part.updated": {
|
|
@@ -30246,14 +30267,12 @@ function startSessionEventForwarder(client4, options) {
|
|
|
30246
30267
|
if (part?.type === "text") {
|
|
30247
30268
|
const chunk2 = props?.delta !== undefined && props.delta !== "" ? props.delta : part.text;
|
|
30248
30269
|
if (chunk2) {
|
|
30249
|
-
target
|
|
30250
|
-
`);
|
|
30270
|
+
writeLogLine(target, options, "text", chunk2);
|
|
30251
30271
|
}
|
|
30252
30272
|
} else if (part?.type === "reasoning") {
|
|
30253
30273
|
const chunk2 = props?.delta !== undefined && props.delta !== "" ? props.delta : part.text;
|
|
30254
30274
|
if (chunk2) {
|
|
30255
|
-
target
|
|
30256
|
-
`);
|
|
30275
|
+
writeLogLine(target, options, "thinking", chunk2);
|
|
30257
30276
|
}
|
|
30258
30277
|
} else if (part?.type === "tool" && part.tool) {
|
|
30259
30278
|
let appendInput = function(base, input, tool) {
|
|
@@ -30280,8 +30299,7 @@ function startSessionEventForwarder(client4, options) {
|
|
|
30280
30299
|
const seenKey = `${callID}:${state}`;
|
|
30281
30300
|
if (!seenToolStates.has(seenKey)) {
|
|
30282
30301
|
seenToolStates.set(seenKey, payload);
|
|
30283
|
-
target
|
|
30284
|
-
`);
|
|
30302
|
+
writeLogLine(target, options, "tool: " + part.tool, payload);
|
|
30285
30303
|
}
|
|
30286
30304
|
if (state === "completed" || state === "error") {
|
|
30287
30305
|
seenToolStates.delete(seenKey);
|
|
@@ -30293,20 +30311,17 @@ function startSessionEventForwarder(client4, options) {
|
|
|
30293
30311
|
const props = event.properties;
|
|
30294
30312
|
const kind = props?.action ?? props?.kind;
|
|
30295
30313
|
const filePayload = kind ? `${props?.file} (${kind})` : props?.file;
|
|
30296
|
-
target
|
|
30297
|
-
`);
|
|
30314
|
+
writeLogLine(target, options, "file", filePayload);
|
|
30298
30315
|
break;
|
|
30299
30316
|
}
|
|
30300
30317
|
case "session.idle": {
|
|
30301
|
-
target
|
|
30302
|
-
`);
|
|
30318
|
+
writeLogLine(target, options, "agent_end");
|
|
30303
30319
|
for (const cb of agentEndCallbacks)
|
|
30304
30320
|
cb();
|
|
30305
30321
|
break;
|
|
30306
30322
|
}
|
|
30307
30323
|
case "session.compacted": {
|
|
30308
|
-
target
|
|
30309
|
-
`);
|
|
30324
|
+
writeLogLine(target, options, "compacted");
|
|
30310
30325
|
break;
|
|
30311
30326
|
}
|
|
30312
30327
|
case "session.status": {
|
|
@@ -30314,8 +30329,7 @@ function startSessionEventForwarder(client4, options) {
|
|
|
30314
30329
|
const currentStatus = props?.status?.type;
|
|
30315
30330
|
if (currentStatus !== lastStatus) {
|
|
30316
30331
|
lastStatus = currentStatus;
|
|
30317
|
-
target
|
|
30318
|
-
`);
|
|
30332
|
+
writeLogLine(target, options, "status", currentStatus);
|
|
30319
30333
|
}
|
|
30320
30334
|
break;
|
|
30321
30335
|
}
|
|
@@ -30329,11 +30343,9 @@ function startSessionEventForwarder(client4, options) {
|
|
|
30329
30343
|
} else if (props?.command) {
|
|
30330
30344
|
payload += ` [command: ${props.command}]`;
|
|
30331
30345
|
}
|
|
30332
|
-
errorTarget
|
|
30333
|
-
`);
|
|
30346
|
+
writeLogLine(errorTarget, options, "error", payload);
|
|
30334
30347
|
if (isUsageLimitError(err)) {
|
|
30335
|
-
target
|
|
30336
|
-
`);
|
|
30348
|
+
writeLogLine(target, options, "agent_end", "reason: usage_limit_reached");
|
|
30337
30349
|
for (const cb of agentEndCallbacks)
|
|
30338
30350
|
cb();
|
|
30339
30351
|
}
|
|
@@ -30345,8 +30357,7 @@ function startSessionEventForwarder(client4, options) {
|
|
|
30345
30357
|
}
|
|
30346
30358
|
} catch (err) {
|
|
30347
30359
|
const message = err instanceof Error ? err.message : String(err);
|
|
30348
|
-
errorTarget
|
|
30349
|
-
`);
|
|
30360
|
+
writeLogLine(errorTarget, options, "error", message);
|
|
30350
30361
|
} finally {
|
|
30351
30362
|
doneResolve();
|
|
30352
30363
|
}
|
|
@@ -30502,8 +30513,12 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30502
30513
|
baseUrl,
|
|
30503
30514
|
agentName,
|
|
30504
30515
|
model,
|
|
30505
|
-
|
|
30516
|
+
logLineCallbacks
|
|
30506
30517
|
} = args2;
|
|
30518
|
+
const emitLogLine = (line) => {
|
|
30519
|
+
for (const lineCb of logLineCallbacks)
|
|
30520
|
+
lineCb(line);
|
|
30521
|
+
};
|
|
30507
30522
|
const meta = {
|
|
30508
30523
|
sessionId,
|
|
30509
30524
|
machineId: context4.machineId,
|
|
@@ -30530,10 +30545,11 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30530
30545
|
});
|
|
30531
30546
|
}
|
|
30532
30547
|
if (childProcess.stderr) {
|
|
30533
|
-
childProcess.stderr.on("data", () => {
|
|
30548
|
+
childProcess.stderr.on("data", (chunk2) => {
|
|
30534
30549
|
entry.lastOutputAt = Date.now();
|
|
30535
30550
|
for (const cb of outputCallbacks)
|
|
30536
30551
|
cb();
|
|
30552
|
+
emitLogLine(chunk2.toString());
|
|
30537
30553
|
});
|
|
30538
30554
|
}
|
|
30539
30555
|
return {
|
|
@@ -30560,6 +30576,19 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30560
30576
|
},
|
|
30561
30577
|
onAgentEnd: (cb) => {
|
|
30562
30578
|
forwarder?.onAgentEnd(cb);
|
|
30579
|
+
},
|
|
30580
|
+
onLogLine: (cb) => {
|
|
30581
|
+
logLineCallbacks.push(cb);
|
|
30582
|
+
}
|
|
30583
|
+
};
|
|
30584
|
+
}
|
|
30585
|
+
createLogLineEmitter() {
|
|
30586
|
+
const logLineCallbacks = [];
|
|
30587
|
+
return {
|
|
30588
|
+
logLineCallbacks,
|
|
30589
|
+
emitLogLine: (line) => {
|
|
30590
|
+
for (const cb of logLineCallbacks)
|
|
30591
|
+
cb(line);
|
|
30563
30592
|
}
|
|
30564
30593
|
};
|
|
30565
30594
|
}
|
|
@@ -30571,6 +30600,9 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30571
30600
|
const workingDir = session.workingDir;
|
|
30572
30601
|
const childProcess = this.spawnServeProcess(workingDir);
|
|
30573
30602
|
const pid = childProcess.pid;
|
|
30603
|
+
if (pid == null) {
|
|
30604
|
+
throw new Error("Failed to spawn opencode serve process");
|
|
30605
|
+
}
|
|
30574
30606
|
const baseUrl = await waitForListeningUrl(childProcess, {
|
|
30575
30607
|
timeoutMs: SERVE_STARTUP_TIMEOUT_MS
|
|
30576
30608
|
}).catch((err) => {
|
|
@@ -30579,6 +30611,7 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30579
30611
|
});
|
|
30580
30612
|
const client4 = createOpencodeClient({ baseUrl });
|
|
30581
30613
|
let forwarder;
|
|
30614
|
+
const { logLineCallbacks, emitLogLine } = this.createLogLineEmitter();
|
|
30582
30615
|
try {
|
|
30583
30616
|
const sessionInfo = await withTimeout2(client4.session.get({ path: { id: sessionId } }), SESSION_GET_TIMEOUT_MS, "session.get");
|
|
30584
30617
|
if (!sessionInfo.data?.id) {
|
|
@@ -30586,7 +30619,8 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30586
30619
|
}
|
|
30587
30620
|
forwarder = startSessionEventForwarder(client4, {
|
|
30588
30621
|
sessionId,
|
|
30589
|
-
role: context4.role
|
|
30622
|
+
role: context4.role,
|
|
30623
|
+
onLogLine: emitLogLine
|
|
30590
30624
|
});
|
|
30591
30625
|
const agentsResponse = await withTimeout2(client4.app.agents(), AGENTS_LIST_TIMEOUT_MS, "app.agents");
|
|
30592
30626
|
const availableAgents = agentsResponse.data ?? [];
|
|
@@ -30624,13 +30658,17 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30624
30658
|
baseUrl,
|
|
30625
30659
|
agentName,
|
|
30626
30660
|
model: modelForSession,
|
|
30627
|
-
workingDir
|
|
30661
|
+
workingDir,
|
|
30662
|
+
logLineCallbacks
|
|
30628
30663
|
});
|
|
30629
30664
|
}
|
|
30630
30665
|
async spawn(options) {
|
|
30631
30666
|
const { prompt, systemPrompt, model, context: context4 } = options;
|
|
30632
30667
|
const childProcess = this.spawnServeProcess(options.workingDir);
|
|
30633
30668
|
const pid = childProcess.pid;
|
|
30669
|
+
if (pid == null) {
|
|
30670
|
+
throw new Error("Failed to spawn opencode serve process");
|
|
30671
|
+
}
|
|
30634
30672
|
const baseUrl = await waitForListeningUrl(childProcess, {
|
|
30635
30673
|
timeoutMs: SERVE_STARTUP_TIMEOUT_MS
|
|
30636
30674
|
}).catch((err) => {
|
|
@@ -30643,6 +30681,7 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30643
30681
|
let sessionId;
|
|
30644
30682
|
let forwarder;
|
|
30645
30683
|
let agentName;
|
|
30684
|
+
const { logLineCallbacks, emitLogLine } = this.createLogLineEmitter();
|
|
30646
30685
|
try {
|
|
30647
30686
|
const sessionCreateResult = await withTimeout2(client4.session.create({ body: {} }), SESSION_CREATE_TIMEOUT_MS, "session.create");
|
|
30648
30687
|
if (!sessionCreateResult.data?.id) {
|
|
@@ -30651,7 +30690,8 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30651
30690
|
sessionId = sessionCreateResult.data.id;
|
|
30652
30691
|
forwarder = startSessionEventForwarder(client4, {
|
|
30653
30692
|
sessionId,
|
|
30654
|
-
role: context4.role
|
|
30693
|
+
role: context4.role,
|
|
30694
|
+
onLogLine: emitLogLine
|
|
30655
30695
|
});
|
|
30656
30696
|
const agentsResponse = await withTimeout2(client4.app.agents(), AGENTS_LIST_TIMEOUT_MS, "app.agents");
|
|
30657
30697
|
const availableAgents = agentsResponse.data ?? [];
|
|
@@ -30683,6 +30723,9 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30683
30723
|
this.sessionStore.remove(sessionId);
|
|
30684
30724
|
throw err;
|
|
30685
30725
|
}
|
|
30726
|
+
if (!sessionId || !agentName) {
|
|
30727
|
+
throw new Error("OpenCode session was not initialized");
|
|
30728
|
+
}
|
|
30686
30729
|
return this.registerRunningSession({
|
|
30687
30730
|
childProcess,
|
|
30688
30731
|
pid,
|
|
@@ -30692,7 +30735,8 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30692
30735
|
baseUrl,
|
|
30693
30736
|
agentName,
|
|
30694
30737
|
model,
|
|
30695
|
-
workingDir: options.workingDir
|
|
30738
|
+
workingDir: options.workingDir,
|
|
30739
|
+
logLineCallbacks
|
|
30696
30740
|
});
|
|
30697
30741
|
}
|
|
30698
30742
|
getHarnessReconnectContext(pid) {
|
|
@@ -31402,11 +31446,15 @@ This ensures you never forget to pass control back — just copy the command fro
|
|
|
31402
31446
|
|
|
31403
31447
|
### Command Reference
|
|
31404
31448
|
|
|
31449
|
+
**Production (default):** omit \`CHATROOM_CONVEX_URL\` — the CLI connects to chatroom cloud automatically. Do not look up the Convex URL from \`.env\`.
|
|
31450
|
+
|
|
31405
31451
|
| Action | Command |
|
|
31406
31452
|
|--------|---------|
|
|
31407
|
-
| Fetch next task | \`
|
|
31408
|
-
| Pass control | \`
|
|
31409
|
-
| Refresh system prompt (after compaction) | \`
|
|
31453
|
+
| Fetch next task | \`chatroom get-next-task --chatroom-id=<id> --role=<role>\` |
|
|
31454
|
+
| Pass control | \`chatroom handoff --chatroom-id=<id> --role=<role> --next-role=<role>\` |
|
|
31455
|
+
| Refresh system prompt (after compaction) | \`chatroom get-system-prompt --chatroom-id=<id> --role=<role>\` |
|
|
31456
|
+
|
|
31457
|
+
**Non-production only** (local dev, preview): prefix commands with \`CHATROOM_CONVEX_URL=<convex-url> \` (e.g. \`CHATROOM_CONVEX_URL=http://127.0.0.1:3210 chatroom get-next-task ...\`).
|
|
31410
31458
|
|
|
31411
31459
|
### Context Recovery
|
|
31412
31460
|
|
|
@@ -80735,6 +80783,7 @@ var init_command_runner = __esm(() => {
|
|
|
80735
80783
|
// src/events/lifecycle/on-daemon-shutdown.ts
|
|
80736
80784
|
async function onDaemonShutdown(ctx) {
|
|
80737
80785
|
await shutdownAllCommands(ctx);
|
|
80786
|
+
await ctx.deps.agentProcessManager.whenTurnEndsIdle();
|
|
80738
80787
|
const activeAgents = ctx.deps.agentProcessManager.listActive();
|
|
80739
80788
|
if (activeAgents.length > 0) {
|
|
80740
80789
|
console.log(`[${formatTimestamp()}] Stopping ${activeAgents.length} agent(s)...`);
|
|
@@ -85370,66 +85419,6 @@ var init_crash_loop_tracker = __esm(() => {
|
|
|
85370
85419
|
];
|
|
85371
85420
|
});
|
|
85372
85421
|
|
|
85373
|
-
// src/infrastructure/deps/process.ts
|
|
85374
|
-
function isProcessAlive(kill, pid) {
|
|
85375
|
-
try {
|
|
85376
|
-
kill(pid, 0);
|
|
85377
|
-
return true;
|
|
85378
|
-
} catch {
|
|
85379
|
-
return false;
|
|
85380
|
-
}
|
|
85381
|
-
}
|
|
85382
|
-
|
|
85383
|
-
// src/domain/agent-lifecycle/entities/stop-reason.ts
|
|
85384
|
-
function resolveStopReason(code2, signal) {
|
|
85385
|
-
if (signal !== null)
|
|
85386
|
-
return "agent_process.signal";
|
|
85387
|
-
if (code2 === 0)
|
|
85388
|
-
return "agent_process.exited_clean";
|
|
85389
|
-
return "agent_process.crashed";
|
|
85390
|
-
}
|
|
85391
|
-
|
|
85392
|
-
// src/domain/agent-lifecycle/policies/preserve-session.ts
|
|
85393
|
-
function shouldRetainHarnessSessionForReconnect(reason) {
|
|
85394
|
-
switch (reason) {
|
|
85395
|
-
case "user.stop":
|
|
85396
|
-
case "agent_process.exited_clean":
|
|
85397
|
-
case "agent_process.signal":
|
|
85398
|
-
case "agent_process.crashed":
|
|
85399
|
-
return true;
|
|
85400
|
-
default:
|
|
85401
|
-
return false;
|
|
85402
|
-
}
|
|
85403
|
-
}
|
|
85404
|
-
function shouldPreserveHarnessTeardown(reason, supportsSessionResume, hasHarnessSessionId) {
|
|
85405
|
-
return hasHarnessSessionId && supportsSessionResume && shouldRetainHarnessSessionForReconnect(reason);
|
|
85406
|
-
}
|
|
85407
|
-
|
|
85408
|
-
// src/domain/agent-lifecycle/policies/decide-resume-path.ts
|
|
85409
|
-
function decideResumePathOnRestart(input) {
|
|
85410
|
-
if (!input.supportsSessionResume) {
|
|
85411
|
-
return "cold";
|
|
85412
|
-
}
|
|
85413
|
-
if (input.wantResume && input.hasStoredSnapshot) {
|
|
85414
|
-
return "daemon_memory";
|
|
85415
|
-
}
|
|
85416
|
-
return "cold";
|
|
85417
|
-
}
|
|
85418
|
-
function shouldAutoRestartAfterProcessExit(stopReason) {
|
|
85419
|
-
switch (stopReason) {
|
|
85420
|
-
case "user.stop":
|
|
85421
|
-
case "platform.team_switch":
|
|
85422
|
-
case "daemon.shutdown":
|
|
85423
|
-
case "daemon.respawn":
|
|
85424
|
-
return false;
|
|
85425
|
-
default:
|
|
85426
|
-
return true;
|
|
85427
|
-
}
|
|
85428
|
-
}
|
|
85429
|
-
|
|
85430
|
-
// src/domain/agent-lifecycle/index.ts
|
|
85431
|
-
var init_agent_lifecycle = () => {};
|
|
85432
|
-
|
|
85433
85422
|
// ../../services/backend/src/domain/entities/harness/claude.config.ts
|
|
85434
85423
|
var claudeCapabilities;
|
|
85435
85424
|
var init_claude_config = __esm(() => {
|
|
@@ -85584,6 +85573,273 @@ var init_types = __esm(() => {
|
|
|
85584
85573
|
};
|
|
85585
85574
|
});
|
|
85586
85575
|
|
|
85576
|
+
// src/infrastructure/services/agent-process-manager/turn-completed-backend.ts
|
|
85577
|
+
function createTurnCompletedBackend(deps) {
|
|
85578
|
+
return {
|
|
85579
|
+
emitResumeStormAborted: (args2) => deps.backend.mutation(api.agentResumeStorm.emitResumeStormAborted, {
|
|
85580
|
+
sessionId: deps.sessionId,
|
|
85581
|
+
machineId: deps.machineId,
|
|
85582
|
+
...args2
|
|
85583
|
+
}),
|
|
85584
|
+
emitSessionResumed: (args2) => deps.backend.mutation(api.machines.emitSessionResumed, {
|
|
85585
|
+
sessionId: deps.sessionId,
|
|
85586
|
+
machineId: deps.machineId,
|
|
85587
|
+
...args2
|
|
85588
|
+
}),
|
|
85589
|
+
emitSessionResumeFailed: (args2) => deps.backend.mutation(api.machines.emitSessionResumeFailed, {
|
|
85590
|
+
sessionId: deps.sessionId,
|
|
85591
|
+
machineId: deps.machineId,
|
|
85592
|
+
...args2
|
|
85593
|
+
})
|
|
85594
|
+
};
|
|
85595
|
+
}
|
|
85596
|
+
var init_turn_completed_backend = __esm(() => {
|
|
85597
|
+
init_api3();
|
|
85598
|
+
});
|
|
85599
|
+
|
|
85600
|
+
// src/infrastructure/services/agent-process-manager/turn-end-queue.ts
|
|
85601
|
+
class TurnEndQueue {
|
|
85602
|
+
tail = Promise.resolve();
|
|
85603
|
+
enqueue(work) {
|
|
85604
|
+
this.tail = this.tail.then(work).catch((err) => {
|
|
85605
|
+
console.log(`[TurnEndQueue] turn-end handler failed: ${err.message}`);
|
|
85606
|
+
});
|
|
85607
|
+
}
|
|
85608
|
+
async whenIdle() {
|
|
85609
|
+
await this.tail;
|
|
85610
|
+
}
|
|
85611
|
+
}
|
|
85612
|
+
|
|
85613
|
+
// src/domain/agent-lifecycle/entities/stop-reason.ts
|
|
85614
|
+
function resolveStopReason(code2, signal) {
|
|
85615
|
+
if (signal !== null)
|
|
85616
|
+
return "agent_process.signal";
|
|
85617
|
+
if (code2 === 0)
|
|
85618
|
+
return "agent_process.exited_clean";
|
|
85619
|
+
return "agent_process.crashed";
|
|
85620
|
+
}
|
|
85621
|
+
|
|
85622
|
+
// src/domain/agent-lifecycle/policies/preserve-session.ts
|
|
85623
|
+
function shouldRetainHarnessSessionForReconnect(reason) {
|
|
85624
|
+
switch (reason) {
|
|
85625
|
+
case "user.stop":
|
|
85626
|
+
case "agent_process.exited_clean":
|
|
85627
|
+
case "agent_process.signal":
|
|
85628
|
+
case "agent_process.crashed":
|
|
85629
|
+
return true;
|
|
85630
|
+
default:
|
|
85631
|
+
return false;
|
|
85632
|
+
}
|
|
85633
|
+
}
|
|
85634
|
+
function shouldPreserveHarnessTeardown(reason, supportsSessionResume, hasHarnessSessionId) {
|
|
85635
|
+
return hasHarnessSessionId && supportsSessionResume && shouldRetainHarnessSessionForReconnect(reason);
|
|
85636
|
+
}
|
|
85637
|
+
|
|
85638
|
+
// src/domain/agent-lifecycle/policies/decide-resume-path.ts
|
|
85639
|
+
function decideResumePathOnRestart(input) {
|
|
85640
|
+
if (!input.supportsSessionResume) {
|
|
85641
|
+
return "cold";
|
|
85642
|
+
}
|
|
85643
|
+
if (input.wantResume && input.hasStoredSnapshot) {
|
|
85644
|
+
return "daemon_memory";
|
|
85645
|
+
}
|
|
85646
|
+
return "cold";
|
|
85647
|
+
}
|
|
85648
|
+
function shouldAutoRestartAfterProcessExit(stopReason) {
|
|
85649
|
+
return !NO_AUTO_RESTART_STOP_REASONS.has(stopReason);
|
|
85650
|
+
}
|
|
85651
|
+
var NO_AUTO_RESTART_STOP_REASONS;
|
|
85652
|
+
var init_decide_resume_path = __esm(() => {
|
|
85653
|
+
NO_AUTO_RESTART_STOP_REASONS = new Set([
|
|
85654
|
+
"user.stop",
|
|
85655
|
+
"platform.team_switch",
|
|
85656
|
+
"platform.resume_storm",
|
|
85657
|
+
"daemon.shutdown",
|
|
85658
|
+
"daemon.respawn"
|
|
85659
|
+
]);
|
|
85660
|
+
});
|
|
85661
|
+
|
|
85662
|
+
// src/domain/agent-lifecycle/index.ts
|
|
85663
|
+
var init_agent_lifecycle = __esm(() => {
|
|
85664
|
+
init_decide_resume_path();
|
|
85665
|
+
});
|
|
85666
|
+
|
|
85667
|
+
// src/domain/agent-lifecycle/policies/append-recent-log-line.ts
|
|
85668
|
+
function appendRecentLogLine(slot, line) {
|
|
85669
|
+
if (!slot.recentLogLines) {
|
|
85670
|
+
slot.recentLogLines = [];
|
|
85671
|
+
}
|
|
85672
|
+
slot.recentLogLines.push(line);
|
|
85673
|
+
if (slot.recentLogLines.length > RECENT_LOG_LINE_CAP) {
|
|
85674
|
+
slot.recentLogLines.shift();
|
|
85675
|
+
}
|
|
85676
|
+
}
|
|
85677
|
+
var RECENT_LOG_LINE_CAP = 100;
|
|
85678
|
+
|
|
85679
|
+
// src/domain/agent-lifecycle/policies/classify-resume-storm-reason.ts
|
|
85680
|
+
function classifyResumeStormReason(logLines) {
|
|
85681
|
+
const blob = logLines.join(`
|
|
85682
|
+
`);
|
|
85683
|
+
if (!blob.trim()) {
|
|
85684
|
+
return "unknown";
|
|
85685
|
+
}
|
|
85686
|
+
for (const rule of CLASSIFICATION_RULES) {
|
|
85687
|
+
if (rule.patterns.some((pattern) => pattern.test(blob))) {
|
|
85688
|
+
return rule.reason;
|
|
85689
|
+
}
|
|
85690
|
+
}
|
|
85691
|
+
return "unknown";
|
|
85692
|
+
}
|
|
85693
|
+
var CLASSIFICATION_RULES;
|
|
85694
|
+
var init_classify_resume_storm_reason = __esm(() => {
|
|
85695
|
+
CLASSIFICATION_RULES = [
|
|
85696
|
+
{
|
|
85697
|
+
reason: "rate_limit",
|
|
85698
|
+
patterns: [
|
|
85699
|
+
/\b429\b/,
|
|
85700
|
+
/rate.?limit/i,
|
|
85701
|
+
/too many requests/i,
|
|
85702
|
+
/quota exceeded/i,
|
|
85703
|
+
/usage.?limit/i,
|
|
85704
|
+
/tokens per minute/i
|
|
85705
|
+
]
|
|
85706
|
+
},
|
|
85707
|
+
{
|
|
85708
|
+
reason: "auth_error",
|
|
85709
|
+
patterns: [
|
|
85710
|
+
/\b401\b/,
|
|
85711
|
+
/\b403\b/,
|
|
85712
|
+
/unauthorized/i,
|
|
85713
|
+
/unauthenticated/i,
|
|
85714
|
+
/authentication failed/i,
|
|
85715
|
+
/invalid.{0,24}api.{0,12}key/i,
|
|
85716
|
+
/api key.{0,20}(invalid|missing|expired)/i
|
|
85717
|
+
]
|
|
85718
|
+
},
|
|
85719
|
+
{
|
|
85720
|
+
reason: "config_error",
|
|
85721
|
+
patterns: [
|
|
85722
|
+
/model not found/i,
|
|
85723
|
+
/invalid model/i,
|
|
85724
|
+
/missing model/i,
|
|
85725
|
+
/config(uration)? error/i,
|
|
85726
|
+
/ENOENT.{0,40}(config|\.env)/i,
|
|
85727
|
+
/no such file.{0,40}(config|credentials)/i
|
|
85728
|
+
]
|
|
85729
|
+
}
|
|
85730
|
+
];
|
|
85731
|
+
});
|
|
85732
|
+
|
|
85733
|
+
// src/domain/agent-lifecycle/policies/abort-resume-storm.ts
|
|
85734
|
+
async function tryAbortResumeStorm(deps, input, slot) {
|
|
85735
|
+
const stormCheck = deps.resumeStormTracker.record(input.chatroomId, input.role, deps.now());
|
|
85736
|
+
if (!stormCheck.isStorm) {
|
|
85737
|
+
return false;
|
|
85738
|
+
}
|
|
85739
|
+
deps.resumeStormTracker.reset(input.chatroomId, input.role);
|
|
85740
|
+
if (slot) {
|
|
85741
|
+
slot.resumeInFlight = false;
|
|
85742
|
+
}
|
|
85743
|
+
try {
|
|
85744
|
+
await deps.backend.emitResumeStormAborted({
|
|
85745
|
+
chatroomId: input.chatroomId,
|
|
85746
|
+
role: input.role,
|
|
85747
|
+
reason: classifyResumeStormReason(slot?.recentLogLines ?? []),
|
|
85748
|
+
endCount: stormCheck.endCount,
|
|
85749
|
+
windowMs: stormCheck.windowMs,
|
|
85750
|
+
...slot?.harnessSessionId ? { harnessSessionId: slot.harnessSessionId } : {}
|
|
85751
|
+
});
|
|
85752
|
+
} catch {}
|
|
85753
|
+
if (slot?.state === "running" && slot.pid === input.pid) {
|
|
85754
|
+
await deps.stopAgent({
|
|
85755
|
+
chatroomId: input.chatroomId,
|
|
85756
|
+
role: input.role,
|
|
85757
|
+
reason: "platform.resume_storm"
|
|
85758
|
+
});
|
|
85759
|
+
}
|
|
85760
|
+
return true;
|
|
85761
|
+
}
|
|
85762
|
+
var init_abort_resume_storm = __esm(() => {
|
|
85763
|
+
init_classify_resume_storm_reason();
|
|
85764
|
+
});
|
|
85765
|
+
|
|
85766
|
+
// src/domain/agent-lifecycle/use-cases/handle-turn-completed.ts
|
|
85767
|
+
async function handleTurnCompleted(deps, input, slot) {
|
|
85768
|
+
if (slot?.resumeInFlight) {
|
|
85769
|
+
return { outcome: "skipped_duplicate" };
|
|
85770
|
+
}
|
|
85771
|
+
if (await tryAbortResumeStorm(deps, input, slot)) {
|
|
85772
|
+
return { outcome: "storm_aborted" };
|
|
85773
|
+
}
|
|
85774
|
+
if (input.supportsSessionResume) {
|
|
85775
|
+
if (slot) {
|
|
85776
|
+
slot.resumeInFlight = true;
|
|
85777
|
+
}
|
|
85778
|
+
try {
|
|
85779
|
+
await deps.resumeTurn(input.pid, deps.composeResumePrompt({ chatroomId: input.chatroomId, role: input.role }));
|
|
85780
|
+
try {
|
|
85781
|
+
await deps.backend.emitSessionResumed({
|
|
85782
|
+
chatroomId: input.chatroomId,
|
|
85783
|
+
role: input.role,
|
|
85784
|
+
...slot?.harnessSessionId ? { harnessSessionId: slot.harnessSessionId } : {}
|
|
85785
|
+
});
|
|
85786
|
+
} catch {}
|
|
85787
|
+
return { outcome: "resumed" };
|
|
85788
|
+
} catch (err) {
|
|
85789
|
+
try {
|
|
85790
|
+
await deps.backend.emitSessionResumeFailed({
|
|
85791
|
+
chatroomId: input.chatroomId,
|
|
85792
|
+
role: input.role,
|
|
85793
|
+
reason: err.message,
|
|
85794
|
+
...slot?.harnessSessionId ? { harnessSessionId: slot.harnessSessionId } : {}
|
|
85795
|
+
});
|
|
85796
|
+
} catch {}
|
|
85797
|
+
} finally {
|
|
85798
|
+
if (slot) {
|
|
85799
|
+
slot.resumeInFlight = false;
|
|
85800
|
+
}
|
|
85801
|
+
}
|
|
85802
|
+
}
|
|
85803
|
+
deps.killProcess(input.pid);
|
|
85804
|
+
return { outcome: "killed" };
|
|
85805
|
+
}
|
|
85806
|
+
var init_handle_turn_completed = __esm(() => {
|
|
85807
|
+
init_abort_resume_storm();
|
|
85808
|
+
});
|
|
85809
|
+
|
|
85810
|
+
// src/infrastructure/deps/process.ts
|
|
85811
|
+
function isProcessAlive(kill, pid) {
|
|
85812
|
+
try {
|
|
85813
|
+
kill(pid, 0);
|
|
85814
|
+
return true;
|
|
85815
|
+
} catch {
|
|
85816
|
+
return false;
|
|
85817
|
+
}
|
|
85818
|
+
}
|
|
85819
|
+
|
|
85820
|
+
// src/infrastructure/machine/rapid-resume-tracker.ts
|
|
85821
|
+
class RapidResumeTracker {
|
|
85822
|
+
history = new Map;
|
|
85823
|
+
record(chatroomId, role, now = Date.now()) {
|
|
85824
|
+
const key = `${chatroomId}:${role.toLowerCase()}`;
|
|
85825
|
+
const windowStart = now - RAPID_RESUME_WINDOW_MS;
|
|
85826
|
+
const recent = (this.history.get(key) ?? []).filter((ts) => ts >= windowStart);
|
|
85827
|
+
recent.push(now);
|
|
85828
|
+
this.history.set(key, recent);
|
|
85829
|
+
return {
|
|
85830
|
+
isStorm: recent.length >= RAPID_RESUME_THRESHOLD,
|
|
85831
|
+
recentEnds: recent,
|
|
85832
|
+
endCount: recent.length,
|
|
85833
|
+
windowMs: RAPID_RESUME_WINDOW_MS,
|
|
85834
|
+
threshold: RAPID_RESUME_THRESHOLD
|
|
85835
|
+
};
|
|
85836
|
+
}
|
|
85837
|
+
reset(chatroomId, role) {
|
|
85838
|
+
this.history.delete(`${chatroomId}:${role.toLowerCase()}`);
|
|
85839
|
+
}
|
|
85840
|
+
}
|
|
85841
|
+
var RAPID_RESUME_WINDOW_MS = 30000, RAPID_RESUME_THRESHOLD = 5;
|
|
85842
|
+
|
|
85587
85843
|
// src/infrastructure/services/remote-agents/spawn-prompt.ts
|
|
85588
85844
|
function createSpawnPrompt(raw) {
|
|
85589
85845
|
const trimmed = raw?.trim();
|
|
@@ -85602,8 +85858,15 @@ class AgentProcessManager {
|
|
|
85602
85858
|
lastHarnessSessions = new Map;
|
|
85603
85859
|
exitRetryQueue = [];
|
|
85604
85860
|
exitRetryTimer = null;
|
|
85861
|
+
turnEndQueue = new TurnEndQueue;
|
|
85605
85862
|
constructor(deps) {
|
|
85606
|
-
this.deps =
|
|
85863
|
+
this.deps = {
|
|
85864
|
+
...deps,
|
|
85865
|
+
resumeStormTracker: deps.resumeStormTracker ?? new RapidResumeTracker
|
|
85866
|
+
};
|
|
85867
|
+
}
|
|
85868
|
+
async whenTurnEndsIdle() {
|
|
85869
|
+
await this.turnEndQueue.whenIdle();
|
|
85607
85870
|
}
|
|
85608
85871
|
async ensureRunning(opts) {
|
|
85609
85872
|
const key = agentKey2(opts.chatroomId, opts.role);
|
|
@@ -85672,67 +85935,46 @@ class AgentProcessManager {
|
|
|
85672
85935
|
await operation;
|
|
85673
85936
|
return { success: true };
|
|
85674
85937
|
}
|
|
85675
|
-
async
|
|
85676
|
-
const
|
|
85677
|
-
const
|
|
85678
|
-
if (slot?.resumeInFlight) {
|
|
85679
|
-
console.log(`[AgentProcessManager] lifecycle.turn.completed: skipping duplicate resume for ${opts.role} (resume already in flight)`);
|
|
85680
|
-
return;
|
|
85681
|
-
}
|
|
85938
|
+
async runHandleAgentEnd(opts) {
|
|
85939
|
+
const slot = this.slots.get(agentKey2(opts.chatroomId, opts.role));
|
|
85940
|
+
const service = this.deps.agentServices.get(opts.harness);
|
|
85682
85941
|
const capabilities = getHarnessCapabilities(opts.harness);
|
|
85683
|
-
|
|
85684
|
-
|
|
85685
|
-
|
|
85686
|
-
|
|
85687
|
-
|
|
85688
|
-
|
|
85942
|
+
const supportsSessionResume = capabilities.supportsSessionResume && typeof service?.resumeTurn === "function";
|
|
85943
|
+
console.log(`[AgentProcessManager] lifecycle.turn.completed: role=${opts.role} pid=${opts.pid} harness=${opts.harness} supportsResume=${supportsSessionResume}`);
|
|
85944
|
+
const result = await handleTurnCompleted({
|
|
85945
|
+
resumeStormTracker: this.deps.resumeStormTracker,
|
|
85946
|
+
backend: createTurnCompletedBackend(this.deps),
|
|
85947
|
+
now: () => this.deps.clock.now(),
|
|
85948
|
+
composeResumePrompt: ({ chatroomId, role }) => composeResumeMessage({
|
|
85949
|
+
chatroomId,
|
|
85950
|
+
role,
|
|
85951
|
+
convexUrl: this.deps.convexUrl
|
|
85952
|
+
}),
|
|
85953
|
+
resumeTurn: async (pid, prompt) => {
|
|
85954
|
+
if (!service?.resumeTurn) {
|
|
85955
|
+
throw new Error("Harness does not support resumeTurn");
|
|
85689
85956
|
}
|
|
85957
|
+
await service.resumeTurn(pid, prompt);
|
|
85958
|
+
},
|
|
85959
|
+
killProcess: (pid) => {
|
|
85690
85960
|
try {
|
|
85691
|
-
|
|
85692
|
-
|
|
85693
|
-
|
|
85694
|
-
|
|
85695
|
-
|
|
85696
|
-
|
|
85697
|
-
|
|
85698
|
-
|
|
85699
|
-
|
|
85700
|
-
|
|
85701
|
-
|
|
85702
|
-
|
|
85703
|
-
|
|
85704
|
-
|
|
85705
|
-
|
|
85706
|
-
|
|
85707
|
-
console.log(` ⚠️ Failed to emit sessionResumed event: ${err.message}`);
|
|
85708
|
-
}
|
|
85709
|
-
return;
|
|
85710
|
-
} catch (err) {
|
|
85711
|
-
const reason = err.message;
|
|
85712
|
-
try {
|
|
85713
|
-
await this.deps.backend.mutation(api.machines.emitSessionResumeFailed, {
|
|
85714
|
-
sessionId: this.deps.sessionId,
|
|
85715
|
-
machineId: this.deps.machineId,
|
|
85716
|
-
chatroomId: opts.chatroomId,
|
|
85717
|
-
role: opts.role,
|
|
85718
|
-
reason,
|
|
85719
|
-
...slot?.harnessSessionId ? { harnessSessionId: slot.harnessSessionId } : {}
|
|
85720
|
-
});
|
|
85721
|
-
console.log(`[AgentProcessManager] ✅ Emitted agent.sessionResumeFailed for ${opts.role}`);
|
|
85722
|
-
} catch (emitErr) {
|
|
85723
|
-
console.log(` ⚠️ Failed to emit sessionResumeFailed event: ${emitErr.message}`);
|
|
85724
|
-
}
|
|
85725
|
-
console.log(`[AgentProcessManager] ⚠️ resumeTurn failed for ${opts.role} (pid ${opts.pid}): ${reason} — falling back to kill`);
|
|
85726
|
-
} finally {
|
|
85727
|
-
if (slot) {
|
|
85728
|
-
slot.resumeInFlight = false;
|
|
85729
|
-
}
|
|
85730
|
-
}
|
|
85731
|
-
}
|
|
85961
|
+
this.deps.processes.kill(-pid, "SIGTERM");
|
|
85962
|
+
} catch {}
|
|
85963
|
+
},
|
|
85964
|
+
stopAgent: (args2) => this.stop(args2)
|
|
85965
|
+
}, {
|
|
85966
|
+
chatroomId: opts.chatroomId,
|
|
85967
|
+
role: opts.role,
|
|
85968
|
+
pid: opts.pid,
|
|
85969
|
+
supportsSessionResume
|
|
85970
|
+
}, slot);
|
|
85971
|
+
if (result.outcome === "skipped_duplicate") {
|
|
85972
|
+
console.log(`[AgentProcessManager] lifecycle.turn.completed: skipping duplicate resume for ${opts.role} (resume already in flight)`);
|
|
85973
|
+
} else if (result.outcome === "storm_aborted") {
|
|
85974
|
+
console.log(`[AgentProcessManager] ✅ Handled rapid resume storm for ${opts.role}`);
|
|
85975
|
+
} else if (result.outcome === "resumed") {
|
|
85976
|
+
console.log(`[AgentProcessManager] ✅ Emitted agent.sessionResumed for ${opts.role}`);
|
|
85732
85977
|
}
|
|
85733
|
-
try {
|
|
85734
|
-
this.deps.processes.kill(-opts.pid, "SIGTERM");
|
|
85735
|
-
} catch {}
|
|
85736
85978
|
}
|
|
85737
85979
|
async handleExit(opts) {
|
|
85738
85980
|
const key = agentKey2(opts.chatroomId, opts.role);
|
|
@@ -85853,18 +86095,11 @@ class AgentProcessManager {
|
|
|
85853
86095
|
signal: undefined,
|
|
85854
86096
|
agentHarness: entry.harness
|
|
85855
86097
|
};
|
|
85856
|
-
this.
|
|
85857
|
-
|
|
85858
|
-
this.queueExitRetry({ role, args: exitArgs });
|
|
85859
|
-
});
|
|
85860
|
-
try {
|
|
85861
|
-
await this.deps.persistence.clearAgentPid(this.deps.machineId, chatroomId, role);
|
|
85862
|
-
} catch {}
|
|
86098
|
+
this.recordAgentExitedOrQueueRetry(role, exitArgs, "Failed to record agent exit on recovery");
|
|
86099
|
+
await this.clearAgentPidQuietly(chatroomId, role);
|
|
85863
86100
|
killed++;
|
|
85864
86101
|
} else {
|
|
85865
|
-
|
|
85866
|
-
await this.deps.persistence.clearAgentPid(this.deps.machineId, chatroomId, role);
|
|
85867
|
-
} catch {}
|
|
86102
|
+
await this.clearAgentPidQuietly(chatroomId, role);
|
|
85868
86103
|
cleaned++;
|
|
85869
86104
|
}
|
|
85870
86105
|
}
|
|
@@ -85936,13 +86171,8 @@ class AgentProcessManager {
|
|
|
85936
86171
|
signal: undefined,
|
|
85937
86172
|
agentHarness: harness
|
|
85938
86173
|
};
|
|
85939
|
-
this.
|
|
85940
|
-
|
|
85941
|
-
this.queueExitRetry({ role, args: exitArgs });
|
|
85942
|
-
});
|
|
85943
|
-
try {
|
|
85944
|
-
await this.deps.persistence.clearAgentPid(this.deps.machineId, chatroomId, role);
|
|
85945
|
-
} catch {}
|
|
86174
|
+
this.recordAgentExitedOrQueueRetry(role, exitArgs, "Failed to record agent exit before respawn");
|
|
86175
|
+
await this.clearAgentPidQuietly(chatroomId, role);
|
|
85946
86176
|
}
|
|
85947
86177
|
async executeEnsureRunning(key, slot, opts) {
|
|
85948
86178
|
try {
|
|
@@ -85962,6 +86192,17 @@ class AgentProcessManager {
|
|
|
85962
86192
|
}
|
|
85963
86193
|
}
|
|
85964
86194
|
}
|
|
86195
|
+
recordAgentExitedOrQueueRetry(role, exitArgs, failureLog) {
|
|
86196
|
+
this.deps.backend.mutation(api.machines.recordAgentExited, exitArgs).catch((err) => {
|
|
86197
|
+
console.log(` ⚠️ ${failureLog}: ${err.message}`);
|
|
86198
|
+
this.queueExitRetry({ role, args: exitArgs });
|
|
86199
|
+
});
|
|
86200
|
+
}
|
|
86201
|
+
async clearAgentPidQuietly(chatroomId, role) {
|
|
86202
|
+
try {
|
|
86203
|
+
await this.deps.persistence.clearAgentPid(this.deps.machineId, chatroomId, role);
|
|
86204
|
+
} catch {}
|
|
86205
|
+
}
|
|
85965
86206
|
queueExitRetry(item) {
|
|
85966
86207
|
this.exitRetryQueue.push(item);
|
|
85967
86208
|
if (this.exitRetryTimer === null) {
|
|
@@ -86205,6 +86446,11 @@ class AgentProcessManager {
|
|
|
86205
86446
|
slot.workingDir = opts.workingDir;
|
|
86206
86447
|
slot.startedAt = this.deps.clock.now();
|
|
86207
86448
|
slot.pendingOperation = undefined;
|
|
86449
|
+
slot.recentLogLines = [];
|
|
86450
|
+
this.deps.resumeStormTracker.reset(opts.chatroomId, opts.role);
|
|
86451
|
+
if (spawnResult.onLogLine) {
|
|
86452
|
+
spawnResult.onLogLine((line) => appendRecentLogLine(slot, line));
|
|
86453
|
+
}
|
|
86208
86454
|
this.deps.backend.mutation(api.machines.updateSpawnedAgent, {
|
|
86209
86455
|
sessionId: this.deps.sessionId,
|
|
86210
86456
|
machineId: this.deps.machineId,
|
|
@@ -86231,12 +86477,12 @@ class AgentProcessManager {
|
|
|
86231
86477
|
});
|
|
86232
86478
|
if (spawnResult.onAgentEnd) {
|
|
86233
86479
|
spawnResult.onAgentEnd(() => {
|
|
86234
|
-
this.
|
|
86480
|
+
this.turnEndQueue.enqueue(() => this.runHandleAgentEnd({
|
|
86235
86481
|
chatroomId: opts.chatroomId,
|
|
86236
86482
|
role: opts.role,
|
|
86237
86483
|
pid,
|
|
86238
86484
|
harness: opts.agentHarness
|
|
86239
|
-
});
|
|
86485
|
+
}));
|
|
86240
86486
|
});
|
|
86241
86487
|
}
|
|
86242
86488
|
let lastReportedTokenAt = 0;
|
|
@@ -86348,11 +86594,13 @@ class AgentProcessManager {
|
|
|
86348
86594
|
}
|
|
86349
86595
|
var AGENT_EXIT_RETRY_INTERVAL_MS = 1e4;
|
|
86350
86596
|
var init_agent_process_manager = __esm(() => {
|
|
86351
|
-
|
|
86597
|
+
init_generator();
|
|
86598
|
+
init_types();
|
|
86599
|
+
init_turn_completed_backend();
|
|
86352
86600
|
init_api3();
|
|
86601
|
+
init_orphan_tracker();
|
|
86353
86602
|
init_agent_lifecycle();
|
|
86354
|
-
|
|
86355
|
-
init_generator();
|
|
86603
|
+
init_handle_turn_completed();
|
|
86356
86604
|
});
|
|
86357
86605
|
|
|
86358
86606
|
// src/infrastructure/services/harness-spawning/rate-limiter.ts
|
|
@@ -88578,4 +88826,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
88578
88826
|
});
|
|
88579
88827
|
program2.parse();
|
|
88580
88828
|
|
|
88581
|
-
//# debugId=
|
|
88829
|
+
//# debugId=A9CDF253A2BBC68F64756E2164756E21
|