chatroom-cli 1.52.0 → 1.53.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/dist/index.js +854 -309
- package/dist/index.js.map +18 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -26010,7 +26010,7 @@ class BaseCLIAgentService {
|
|
|
26010
26010
|
}
|
|
26011
26011
|
return null;
|
|
26012
26012
|
}
|
|
26013
|
-
async stop(pid) {
|
|
26013
|
+
async stop(pid, _options) {
|
|
26014
26014
|
try {
|
|
26015
26015
|
this.deps.kill(-pid, "SIGTERM");
|
|
26016
26016
|
} catch {
|
|
@@ -26172,6 +26172,7 @@ class PiRpcReader {
|
|
|
26172
26172
|
toolCallCallbacks = [];
|
|
26173
26173
|
toolResultCallbacks = [];
|
|
26174
26174
|
anyEventCallbacks = [];
|
|
26175
|
+
stateResponseCallbacks = [];
|
|
26175
26176
|
constructor(stream) {
|
|
26176
26177
|
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
26177
26178
|
rl.on("line", (line) => this._handleLine(line));
|
|
@@ -26194,6 +26195,9 @@ class PiRpcReader {
|
|
|
26194
26195
|
onAnyEvent(cb) {
|
|
26195
26196
|
this.anyEventCallbacks.push(cb);
|
|
26196
26197
|
}
|
|
26198
|
+
onStateResponse(cb) {
|
|
26199
|
+
this.stateResponseCallbacks.push(cb);
|
|
26200
|
+
}
|
|
26197
26201
|
_handleLine(line) {
|
|
26198
26202
|
const trimmed = line.trim();
|
|
26199
26203
|
if (!trimmed)
|
|
@@ -26207,6 +26211,15 @@ class PiRpcReader {
|
|
|
26207
26211
|
for (const cb of this.anyEventCallbacks)
|
|
26208
26212
|
cb();
|
|
26209
26213
|
const type = event["type"];
|
|
26214
|
+
if (type === "response" && event["command"] === "get_state") {
|
|
26215
|
+
const data = event["data"];
|
|
26216
|
+
const sessionId = data?.["sessionId"];
|
|
26217
|
+
if (event["success"] === true && typeof sessionId === "string") {
|
|
26218
|
+
for (const cb of this.stateResponseCallbacks)
|
|
26219
|
+
cb(sessionId);
|
|
26220
|
+
}
|
|
26221
|
+
return;
|
|
26222
|
+
}
|
|
26210
26223
|
if (type === "message_update") {
|
|
26211
26224
|
const assistantMessageEvent = event["assistantMessageEvent"];
|
|
26212
26225
|
if (assistantMessageEvent?.["type"] === "text_delta") {
|
|
@@ -26252,7 +26265,11 @@ class PiRpcReader {
|
|
|
26252
26265
|
var init_pi_rpc_reader = () => {};
|
|
26253
26266
|
|
|
26254
26267
|
// src/infrastructure/services/remote-agents/pi/pi-agent-service.ts
|
|
26255
|
-
|
|
26268
|
+
import { join as join6 } from "node:path";
|
|
26269
|
+
function getPiSessionDir(workingDir) {
|
|
26270
|
+
return join6(workingDir, ".chatroom", "pi-sessions");
|
|
26271
|
+
}
|
|
26272
|
+
var PI_COMMAND = "pi", PI_AGENT_NAME = "pi", GET_STATE_TIMEOUT_MS = 5000, SPAWN_READY_DELAY_MS = 500, PiAgentService;
|
|
26256
26273
|
var init_pi_agent_service = __esm(() => {
|
|
26257
26274
|
init_base_cli_agent_service();
|
|
26258
26275
|
init_pi_rpc_reader();
|
|
@@ -26261,23 +26278,50 @@ var init_pi_agent_service = __esm(() => {
|
|
|
26261
26278
|
displayName = "Pi";
|
|
26262
26279
|
command = PI_COMMAND;
|
|
26263
26280
|
childProcesses = new Map;
|
|
26281
|
+
trackedSessions = new Map;
|
|
26264
26282
|
constructor(deps) {
|
|
26265
26283
|
super(deps);
|
|
26266
26284
|
}
|
|
26267
26285
|
untrack(pid) {
|
|
26268
26286
|
this.childProcesses.delete(pid);
|
|
26287
|
+
this.trackedSessions.delete(pid);
|
|
26269
26288
|
super.untrack(pid);
|
|
26270
26289
|
}
|
|
26290
|
+
getHarnessReconnectContext(pid) {
|
|
26291
|
+
const session = this.trackedSessions.get(pid);
|
|
26292
|
+
if (!session) {
|
|
26293
|
+
return;
|
|
26294
|
+
}
|
|
26295
|
+
return {
|
|
26296
|
+
agentName: PI_AGENT_NAME,
|
|
26297
|
+
...session.model ? { model: session.model } : {}
|
|
26298
|
+
};
|
|
26299
|
+
}
|
|
26300
|
+
async resumeFromDaemonMemory(options, stored) {
|
|
26301
|
+
const { prompt, systemPrompt, model, context: context4 } = options;
|
|
26302
|
+
const modelForSession = model ?? stored.model;
|
|
26303
|
+
const childProcess = this.spawnPiRpcProcess({
|
|
26304
|
+
workingDir: stored.workingDir,
|
|
26305
|
+
systemPrompt,
|
|
26306
|
+
model: modelForSession,
|
|
26307
|
+
sessionId: stored.harnessSessionId
|
|
26308
|
+
});
|
|
26309
|
+
await this.waitForSpawnReady(childProcess);
|
|
26310
|
+
await this.writePrompt(childProcess, prompt);
|
|
26311
|
+
return this.wireRpcProcess({
|
|
26312
|
+
childProcess,
|
|
26313
|
+
context: context4,
|
|
26314
|
+
workingDir: stored.workingDir,
|
|
26315
|
+
model: modelForSession,
|
|
26316
|
+
harnessSessionId: stored.harnessSessionId
|
|
26317
|
+
});
|
|
26318
|
+
}
|
|
26271
26319
|
async resumeTurn(pid, prompt) {
|
|
26272
26320
|
const child = this.childProcesses.get(pid);
|
|
26273
|
-
if (!child
|
|
26321
|
+
if (!child) {
|
|
26274
26322
|
throw new Error(`No tracked pi process or stdin for pid=${pid}`);
|
|
26275
26323
|
}
|
|
26276
|
-
|
|
26277
|
-
`;
|
|
26278
|
-
await new Promise((resolve, reject) => {
|
|
26279
|
-
child.stdin.write(message, (err) => err ? reject(err) : resolve());
|
|
26280
|
-
});
|
|
26324
|
+
await this.writePrompt(child, prompt);
|
|
26281
26325
|
}
|
|
26282
26326
|
async isInstalled() {
|
|
26283
26327
|
return this.checkInstalled(PI_COMMAND);
|
|
@@ -26307,16 +26351,46 @@ var init_pi_agent_service = __esm(() => {
|
|
|
26307
26351
|
return models;
|
|
26308
26352
|
}
|
|
26309
26353
|
async spawn(options) {
|
|
26310
|
-
const { prompt, systemPrompt, model } = options;
|
|
26311
|
-
const
|
|
26312
|
-
|
|
26313
|
-
|
|
26354
|
+
const { prompt, systemPrompt, model, context: context4, workingDir } = options;
|
|
26355
|
+
const childProcess = this.spawnPiRpcProcess({
|
|
26356
|
+
workingDir,
|
|
26357
|
+
systemPrompt,
|
|
26358
|
+
model
|
|
26359
|
+
});
|
|
26360
|
+
await this.waitForSpawnReady(childProcess);
|
|
26361
|
+
if (!childProcess.stdout) {
|
|
26362
|
+
throw new Error("Pi RPC process has no stdout");
|
|
26363
|
+
}
|
|
26364
|
+
const reader = new PiRpcReader(childProcess.stdout);
|
|
26365
|
+
const harnessSessionId = await this.querySessionId(reader, childProcess.stdin);
|
|
26366
|
+
await this.writePrompt(childProcess, prompt);
|
|
26367
|
+
return this.wireRpcProcess({
|
|
26368
|
+
childProcess,
|
|
26369
|
+
context: context4,
|
|
26370
|
+
workingDir,
|
|
26371
|
+
model,
|
|
26372
|
+
harnessSessionId,
|
|
26373
|
+
reader
|
|
26374
|
+
});
|
|
26375
|
+
}
|
|
26376
|
+
spawnPiRpcProcess(args2) {
|
|
26377
|
+
const rpcArgs = [
|
|
26378
|
+
"--mode",
|
|
26379
|
+
"rpc",
|
|
26380
|
+
"--session-dir",
|
|
26381
|
+
getPiSessionDir(args2.workingDir)
|
|
26382
|
+
];
|
|
26383
|
+
if (args2.sessionId) {
|
|
26384
|
+
rpcArgs.push("--session", args2.sessionId);
|
|
26314
26385
|
}
|
|
26315
|
-
if (
|
|
26316
|
-
|
|
26386
|
+
if (args2.model) {
|
|
26387
|
+
rpcArgs.push("--model", args2.model);
|
|
26317
26388
|
}
|
|
26318
|
-
|
|
26319
|
-
|
|
26389
|
+
if (args2.systemPrompt) {
|
|
26390
|
+
rpcArgs.push("--system-prompt", args2.systemPrompt);
|
|
26391
|
+
}
|
|
26392
|
+
const childProcess = this.deps.spawn(PI_COMMAND, rpcArgs, {
|
|
26393
|
+
cwd: args2.workingDir,
|
|
26320
26394
|
stdio: ["pipe", "pipe", "pipe"],
|
|
26321
26395
|
shell: false,
|
|
26322
26396
|
detached: true,
|
|
@@ -26326,117 +26400,160 @@ var init_pi_agent_service = __esm(() => {
|
|
|
26326
26400
|
GIT_SEQUENCE_EDITOR: "true"
|
|
26327
26401
|
}
|
|
26328
26402
|
});
|
|
26329
|
-
childProcess
|
|
26330
|
-
|
|
26331
|
-
|
|
26403
|
+
return childProcess;
|
|
26404
|
+
}
|
|
26405
|
+
async waitForSpawnReady(childProcess) {
|
|
26406
|
+
await new Promise((resolve) => setTimeout(resolve, SPAWN_READY_DELAY_MS));
|
|
26332
26407
|
if (childProcess.killed || childProcess.exitCode !== null) {
|
|
26333
26408
|
throw new Error(`Agent process exited immediately (exit code: ${childProcess.exitCode})`);
|
|
26334
26409
|
}
|
|
26335
26410
|
if (!childProcess.pid) {
|
|
26336
26411
|
throw new Error("Agent process started but has no PID");
|
|
26337
26412
|
}
|
|
26413
|
+
}
|
|
26414
|
+
writePrompt(child, prompt) {
|
|
26415
|
+
return new Promise((resolve, reject) => {
|
|
26416
|
+
if (!child.stdin) {
|
|
26417
|
+
reject(new Error("Pi RPC process has no stdin"));
|
|
26418
|
+
return;
|
|
26419
|
+
}
|
|
26420
|
+
const message = JSON.stringify({ type: "prompt", message: prompt }) + `
|
|
26421
|
+
`;
|
|
26422
|
+
child.stdin.write(message, (err) => err ? reject(err) : resolve());
|
|
26423
|
+
});
|
|
26424
|
+
}
|
|
26425
|
+
async querySessionId(reader, stdin) {
|
|
26426
|
+
if (!stdin) {
|
|
26427
|
+
throw new Error("Pi RPC process has no stdin");
|
|
26428
|
+
}
|
|
26429
|
+
return new Promise((resolve, reject) => {
|
|
26430
|
+
const timer = setTimeout(() => {
|
|
26431
|
+
reject(new Error(`get_state timed out after ${GET_STATE_TIMEOUT_MS}ms`));
|
|
26432
|
+
}, GET_STATE_TIMEOUT_MS);
|
|
26433
|
+
reader.onStateResponse((sessionId) => {
|
|
26434
|
+
clearTimeout(timer);
|
|
26435
|
+
resolve(sessionId);
|
|
26436
|
+
});
|
|
26437
|
+
stdin.write(JSON.stringify({ type: "get_state" }) + `
|
|
26438
|
+
`);
|
|
26439
|
+
});
|
|
26440
|
+
}
|
|
26441
|
+
wireRpcProcess(args2) {
|
|
26442
|
+
const { childProcess, context: context4, workingDir, model, harnessSessionId } = args2;
|
|
26338
26443
|
const pid = childProcess.pid;
|
|
26339
|
-
const context4 = options.context;
|
|
26340
26444
|
this.childProcesses.set(pid, childProcess);
|
|
26445
|
+
this.trackedSessions.set(pid, { harnessSessionId, workingDir, model });
|
|
26341
26446
|
const entry = this.registerProcess(pid, context4);
|
|
26342
26447
|
const roleTag = context4.role ?? "unknown";
|
|
26343
26448
|
const chatroomSuffix = context4.chatroomId ? `@${context4.chatroomId.slice(-6)}` : "";
|
|
26344
26449
|
const logPrefix = `[pi:${roleTag}${chatroomSuffix}`;
|
|
26345
26450
|
const outputCallbacks = [];
|
|
26346
|
-
|
|
26347
|
-
|
|
26348
|
-
|
|
26349
|
-
|
|
26350
|
-
|
|
26351
|
-
|
|
26352
|
-
|
|
26353
|
-
|
|
26451
|
+
const agentEndCallbacks = [];
|
|
26452
|
+
const onExit4 = (cb) => {
|
|
26453
|
+
childProcess.on("exit", (code2, signal) => {
|
|
26454
|
+
this.childProcesses.delete(pid);
|
|
26455
|
+
this.trackedSessions.delete(pid);
|
|
26456
|
+
this.deleteProcess(pid);
|
|
26457
|
+
cb({ code: code2, signal, context: context4 });
|
|
26458
|
+
});
|
|
26459
|
+
};
|
|
26460
|
+
const onOutput = (cb) => {
|
|
26461
|
+
outputCallbacks.push(cb);
|
|
26462
|
+
};
|
|
26463
|
+
const onAgentEnd = (cb) => {
|
|
26464
|
+
agentEndCallbacks.push(cb);
|
|
26465
|
+
};
|
|
26466
|
+
const baseResult = {
|
|
26467
|
+
pid,
|
|
26468
|
+
harnessSessionId,
|
|
26469
|
+
harnessReconnect: {
|
|
26470
|
+
agentName: PI_AGENT_NAME,
|
|
26471
|
+
...model ? { model } : {}
|
|
26472
|
+
},
|
|
26473
|
+
onExit: onExit4,
|
|
26474
|
+
onOutput
|
|
26475
|
+
};
|
|
26476
|
+
if (!childProcess.stdout) {
|
|
26477
|
+
if (childProcess.stderr) {
|
|
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
|
+
}
|
|
26485
|
+
return baseResult;
|
|
26486
|
+
}
|
|
26487
|
+
const reader = args2.reader ?? new PiRpcReader(childProcess.stdout);
|
|
26488
|
+
let textBuffer = "";
|
|
26489
|
+
let thinkingBuffer = "";
|
|
26490
|
+
const flushText = () => {
|
|
26491
|
+
if (!textBuffer)
|
|
26492
|
+
return;
|
|
26493
|
+
for (const line of textBuffer.split(`
|
|
26354
26494
|
`)) {
|
|
26355
|
-
|
|
26356
|
-
|
|
26495
|
+
if (line)
|
|
26496
|
+
process.stdout.write(`${logPrefix} text] ${line}
|
|
26357
26497
|
`);
|
|
26358
|
-
|
|
26359
|
-
|
|
26360
|
-
|
|
26361
|
-
|
|
26362
|
-
|
|
26363
|
-
|
|
26364
|
-
|
|
26498
|
+
}
|
|
26499
|
+
textBuffer = "";
|
|
26500
|
+
};
|
|
26501
|
+
const flushThinking = () => {
|
|
26502
|
+
if (!thinkingBuffer)
|
|
26503
|
+
return;
|
|
26504
|
+
for (const line of thinkingBuffer.split(`
|
|
26365
26505
|
`)) {
|
|
26366
|
-
|
|
26367
|
-
|
|
26506
|
+
if (line)
|
|
26507
|
+
process.stdout.write(`${logPrefix} thinking] ${line}
|
|
26368
26508
|
`);
|
|
26369
|
-
|
|
26370
|
-
|
|
26371
|
-
|
|
26372
|
-
|
|
26373
|
-
|
|
26374
|
-
|
|
26375
|
-
|
|
26509
|
+
}
|
|
26510
|
+
thinkingBuffer = "";
|
|
26511
|
+
};
|
|
26512
|
+
reader.onTextDelta((delta) => {
|
|
26513
|
+
flushThinking();
|
|
26514
|
+
textBuffer += delta;
|
|
26515
|
+
if (textBuffer.includes(`
|
|
26376
26516
|
`))
|
|
26377
|
-
flushText();
|
|
26378
|
-
entry.lastOutputAt = Date.now();
|
|
26379
|
-
for (const cb of outputCallbacks)
|
|
26380
|
-
cb();
|
|
26381
|
-
});
|
|
26382
|
-
reader.onThinkingDelta((delta) => {
|
|
26383
26517
|
flushText();
|
|
26384
|
-
|
|
26385
|
-
|
|
26518
|
+
entry.lastOutputAt = Date.now();
|
|
26519
|
+
for (const cb of outputCallbacks)
|
|
26520
|
+
cb();
|
|
26521
|
+
});
|
|
26522
|
+
reader.onThinkingDelta((delta) => {
|
|
26523
|
+
flushText();
|
|
26524
|
+
thinkingBuffer += delta;
|
|
26525
|
+
if (thinkingBuffer.includes(`
|
|
26386
26526
|
`))
|
|
26387
|
-
flushThinking();
|
|
26388
|
-
entry.lastOutputAt = Date.now();
|
|
26389
|
-
for (const cb of outputCallbacks)
|
|
26390
|
-
cb();
|
|
26391
|
-
});
|
|
26392
|
-
reader.onAnyEvent(() => {
|
|
26393
|
-
entry.lastOutputAt = Date.now();
|
|
26394
|
-
for (const cb of outputCallbacks)
|
|
26395
|
-
cb();
|
|
26396
|
-
});
|
|
26397
|
-
reader.onAgentEnd(() => {
|
|
26398
|
-
flushText();
|
|
26399
26527
|
flushThinking();
|
|
26400
|
-
|
|
26528
|
+
entry.lastOutputAt = Date.now();
|
|
26529
|
+
for (const cb of outputCallbacks)
|
|
26530
|
+
cb();
|
|
26531
|
+
});
|
|
26532
|
+
reader.onAnyEvent(() => {
|
|
26533
|
+
entry.lastOutputAt = Date.now();
|
|
26534
|
+
for (const cb of outputCallbacks)
|
|
26535
|
+
cb();
|
|
26536
|
+
});
|
|
26537
|
+
reader.onAgentEnd(() => {
|
|
26538
|
+
flushText();
|
|
26539
|
+
flushThinking();
|
|
26540
|
+
process.stdout.write(`${logPrefix} agent_end]
|
|
26401
26541
|
`);
|
|
26402
|
-
|
|
26403
|
-
|
|
26404
|
-
|
|
26405
|
-
|
|
26406
|
-
|
|
26407
|
-
|
|
26542
|
+
for (const cb of agentEndCallbacks)
|
|
26543
|
+
cb();
|
|
26544
|
+
});
|
|
26545
|
+
reader.onToolCall((name, toolArgs) => {
|
|
26546
|
+
flushText();
|
|
26547
|
+
flushThinking();
|
|
26548
|
+
const argsStr = toolArgs != null ? ` args: ${JSON.stringify(toolArgs)}` : "";
|
|
26549
|
+
process.stdout.write(`${logPrefix} tool: ${name}${argsStr}]
|
|
26408
26550
|
`);
|
|
26409
|
-
|
|
26410
|
-
|
|
26411
|
-
|
|
26412
|
-
|
|
26551
|
+
});
|
|
26552
|
+
reader.onToolResult((name, result) => {
|
|
26553
|
+
const resultStr = typeof result === "string" ? result : JSON.stringify(result);
|
|
26554
|
+
process.stdout.write(`${logPrefix} tool_result: ${name} result: ${resultStr}]
|
|
26413
26555
|
`);
|
|
26414
|
-
|
|
26415
|
-
if (childProcess.stderr) {
|
|
26416
|
-
childProcess.stderr.pipe(process.stderr, { end: false });
|
|
26417
|
-
childProcess.stderr.on("data", () => {
|
|
26418
|
-
entry.lastOutputAt = Date.now();
|
|
26419
|
-
for (const cb of outputCallbacks)
|
|
26420
|
-
cb();
|
|
26421
|
-
});
|
|
26422
|
-
}
|
|
26423
|
-
return {
|
|
26424
|
-
pid,
|
|
26425
|
-
onExit: (cb) => {
|
|
26426
|
-
childProcess.on("exit", (code2, signal) => {
|
|
26427
|
-
this.childProcesses.delete(pid);
|
|
26428
|
-
this.deleteProcess(pid);
|
|
26429
|
-
cb({ code: code2, signal, context: context4 });
|
|
26430
|
-
});
|
|
26431
|
-
},
|
|
26432
|
-
onOutput: (cb) => {
|
|
26433
|
-
outputCallbacks.push(cb);
|
|
26434
|
-
},
|
|
26435
|
-
onAgentEnd: (cb) => {
|
|
26436
|
-
reader.onAgentEnd(cb);
|
|
26437
|
-
}
|
|
26438
|
-
};
|
|
26439
|
-
}
|
|
26556
|
+
});
|
|
26440
26557
|
if (childProcess.stderr) {
|
|
26441
26558
|
childProcess.stderr.pipe(process.stderr, { end: false });
|
|
26442
26559
|
childProcess.stderr.on("data", () => {
|
|
@@ -26446,17 +26563,8 @@ var init_pi_agent_service = __esm(() => {
|
|
|
26446
26563
|
});
|
|
26447
26564
|
}
|
|
26448
26565
|
return {
|
|
26449
|
-
|
|
26450
|
-
|
|
26451
|
-
childProcess.on("exit", (code2, signal) => {
|
|
26452
|
-
this.childProcesses.delete(pid);
|
|
26453
|
-
this.deleteProcess(pid);
|
|
26454
|
-
cb({ code: code2, signal, context: context4 });
|
|
26455
|
-
});
|
|
26456
|
-
},
|
|
26457
|
-
onOutput: (cb) => {
|
|
26458
|
-
outputCallbacks.push(cb);
|
|
26459
|
-
}
|
|
26566
|
+
...baseResult,
|
|
26567
|
+
onAgentEnd
|
|
26460
26568
|
};
|
|
26461
26569
|
}
|
|
26462
26570
|
};
|
|
@@ -27556,7 +27664,7 @@ class CursorSdkStreamAdapter {
|
|
|
27556
27664
|
|
|
27557
27665
|
// src/infrastructure/services/remote-agents/cursor-sdk/cursor-sdk-agent-service.ts
|
|
27558
27666
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
27559
|
-
import { join as
|
|
27667
|
+
import { join as join7 } from "node:path";
|
|
27560
27668
|
import { createRequire as createRequire3 } from "node:module";
|
|
27561
27669
|
import { randomUUID } from "node:crypto";
|
|
27562
27670
|
async function loadSdk() {
|
|
@@ -27577,7 +27685,7 @@ function getSdkPackageVersion() {
|
|
|
27577
27685
|
return cachedSdkPackageVersion;
|
|
27578
27686
|
const require3 = createRequire3(import.meta.url);
|
|
27579
27687
|
const entry = require3.resolve("@cursor/sdk");
|
|
27580
|
-
const packageJsonPath =
|
|
27688
|
+
const packageJsonPath = join7(entry, "..", "..", "..", "package.json");
|
|
27581
27689
|
const pkg = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
|
|
27582
27690
|
cachedSdkPackageVersion = pkg.version;
|
|
27583
27691
|
return pkg.version;
|
|
@@ -27596,6 +27704,9 @@ async function withTimeout(p, ms, label) {
|
|
|
27596
27704
|
clearTimeout(timer);
|
|
27597
27705
|
}
|
|
27598
27706
|
}
|
|
27707
|
+
function buildAgentName(context4) {
|
|
27708
|
+
return `${context4.role}@${context4.chatroomId.slice(-6)}`;
|
|
27709
|
+
}
|
|
27599
27710
|
function waitForResumeOrAbort(session) {
|
|
27600
27711
|
if (session.aborted)
|
|
27601
27712
|
return Promise.resolve(null);
|
|
@@ -27690,10 +27801,13 @@ var init_cursor_sdk_agent_service = __esm(() => {
|
|
|
27690
27801
|
session.abortResolve = undefined;
|
|
27691
27802
|
resolve(prompt);
|
|
27692
27803
|
}
|
|
27693
|
-
async stop(pid) {
|
|
27804
|
+
async stop(pid, options) {
|
|
27694
27805
|
const session = this.sessions.get(pid);
|
|
27695
27806
|
if (session) {
|
|
27696
27807
|
session.aborted = true;
|
|
27808
|
+
if (options?.preserveForResume) {
|
|
27809
|
+
session.preserveForResume = true;
|
|
27810
|
+
}
|
|
27697
27811
|
session.abortResolve?.();
|
|
27698
27812
|
const run3 = session.run;
|
|
27699
27813
|
if (run3?.supports("cancel")) {
|
|
@@ -27703,7 +27817,7 @@ var init_cursor_sdk_agent_service = __esm(() => {
|
|
|
27703
27817
|
console.warn(`[cursor-sdk] run.cancel for pid=${pid} failed:`, err instanceof Error ? err.message : err);
|
|
27704
27818
|
}
|
|
27705
27819
|
}
|
|
27706
|
-
if (!session.agentClosed) {
|
|
27820
|
+
if (!session.preserveForResume && !session.agentClosed) {
|
|
27707
27821
|
try {
|
|
27708
27822
|
session.agent.close();
|
|
27709
27823
|
session.agentClosed = true;
|
|
@@ -27713,52 +27827,85 @@ var init_cursor_sdk_agent_service = __esm(() => {
|
|
|
27713
27827
|
}
|
|
27714
27828
|
await super.stop(pid);
|
|
27715
27829
|
}
|
|
27716
|
-
|
|
27830
|
+
getHarnessReconnectContext(pid) {
|
|
27831
|
+
const session = this.sessions.get(pid);
|
|
27832
|
+
if (!session) {
|
|
27833
|
+
return;
|
|
27834
|
+
}
|
|
27835
|
+
return {
|
|
27836
|
+
agentName: session.agentName,
|
|
27837
|
+
...session.model ? { model: session.model } : {}
|
|
27838
|
+
};
|
|
27839
|
+
}
|
|
27840
|
+
async resumeFromDaemonMemory(options, stored) {
|
|
27717
27841
|
const apiKey = process.env.CURSOR_API_KEY?.trim();
|
|
27718
27842
|
if (!apiKey) {
|
|
27719
27843
|
throw new Error("CURSOR_API_KEY is not set");
|
|
27720
27844
|
}
|
|
27721
|
-
const keeper = this.
|
|
27722
|
-
cwd: options.workingDir,
|
|
27723
|
-
stdio: "ignore",
|
|
27724
|
-
shell: false,
|
|
27725
|
-
detached: true
|
|
27726
|
-
});
|
|
27727
|
-
if (!keeper.pid) {
|
|
27728
|
-
keeper.kill();
|
|
27729
|
-
throw new Error("Failed to spawn cursor-sdk keeper process");
|
|
27730
|
-
}
|
|
27845
|
+
const keeper = this.spawnKeeper(options.workingDir);
|
|
27731
27846
|
const pid = keeper.pid;
|
|
27732
27847
|
const context4 = options.context;
|
|
27733
|
-
const
|
|
27734
|
-
const
|
|
27848
|
+
const agentName = stored.agentName;
|
|
27849
|
+
const modelId = resolveModelId(options.model ?? stored.model);
|
|
27735
27850
|
const fullPrompt = options.systemPrompt ? `${options.systemPrompt}
|
|
27736
27851
|
|
|
27737
27852
|
${options.prompt}` : options.prompt;
|
|
27738
|
-
const exitCallbacks = [];
|
|
27739
|
-
const outputCallbacks = [];
|
|
27740
|
-
const agentEndCallbacks = [];
|
|
27741
27853
|
let agent;
|
|
27742
27854
|
try {
|
|
27743
27855
|
const { Agent } = await loadSdk();
|
|
27744
|
-
agent = await withTimeout(Agent.
|
|
27856
|
+
agent = await withTimeout(Agent.resume(stored.harnessSessionId, {
|
|
27745
27857
|
apiKey,
|
|
27746
|
-
|
|
27747
|
-
|
|
27748
|
-
|
|
27749
|
-
}), AGENT_CREATE_TIMEOUT_MS, "Agent.create");
|
|
27858
|
+
model: { id: modelId },
|
|
27859
|
+
local: { cwd: stored.workingDir, settingSources: [] }
|
|
27860
|
+
}), AGENT_CREATE_TIMEOUT_MS, "Agent.resume");
|
|
27750
27861
|
} catch (err) {
|
|
27751
27862
|
keeper.kill();
|
|
27752
27863
|
this.deleteProcess(pid);
|
|
27753
27864
|
throw err;
|
|
27754
27865
|
}
|
|
27866
|
+
return this.startRunningSession({
|
|
27867
|
+
pid,
|
|
27868
|
+
keeper,
|
|
27869
|
+
agent,
|
|
27870
|
+
context: context4,
|
|
27871
|
+
agentName,
|
|
27872
|
+
model: options.model ?? stored.model,
|
|
27873
|
+
workingDir: stored.workingDir,
|
|
27874
|
+
initialPrompt: fullPrompt,
|
|
27875
|
+
forceFirstTurn: true
|
|
27876
|
+
});
|
|
27877
|
+
}
|
|
27878
|
+
spawnKeeper(workingDir) {
|
|
27879
|
+
const keeper = this.deps.spawn(process.execPath, ["-e", "setInterval(()=>{},2147483647)"], {
|
|
27880
|
+
cwd: workingDir,
|
|
27881
|
+
stdio: "ignore",
|
|
27882
|
+
shell: false,
|
|
27883
|
+
detached: true
|
|
27884
|
+
});
|
|
27885
|
+
if (!keeper.pid) {
|
|
27886
|
+
keeper.kill();
|
|
27887
|
+
throw new Error("Failed to spawn cursor-sdk keeper process");
|
|
27888
|
+
}
|
|
27889
|
+
return keeper;
|
|
27890
|
+
}
|
|
27891
|
+
startRunningSession(args2) {
|
|
27892
|
+
const { pid, keeper, agent, context: context4, agentName, model, workingDir, initialPrompt, forceFirstTurn } = args2;
|
|
27893
|
+
const entry = this.registerProcess(pid, context4);
|
|
27894
|
+
const logPrefix = buildLogPrefix(context4);
|
|
27755
27895
|
const session = {
|
|
27756
27896
|
agent,
|
|
27757
27897
|
keeper,
|
|
27758
27898
|
aborted: false,
|
|
27759
|
-
agentClosed: false
|
|
27899
|
+
agentClosed: false,
|
|
27900
|
+
preserveForResume: false,
|
|
27901
|
+
agentName,
|
|
27902
|
+
model,
|
|
27903
|
+
workingDir
|
|
27760
27904
|
};
|
|
27761
27905
|
this.sessions.set(pid, session);
|
|
27906
|
+
const exitCallbacks = [];
|
|
27907
|
+
const outputCallbacks = [];
|
|
27908
|
+
const agentEndCallbacks = [];
|
|
27762
27909
|
const finishExit = (code2, signal) => {
|
|
27763
27910
|
this.sessions.delete(pid);
|
|
27764
27911
|
this.deleteProcess(pid);
|
|
@@ -27766,11 +27913,54 @@ ${options.prompt}` : options.prompt;
|
|
|
27766
27913
|
cb({ code: code2, signal, context: context4 });
|
|
27767
27914
|
}
|
|
27768
27915
|
};
|
|
27916
|
+
this.runTurnLoop({
|
|
27917
|
+
pid,
|
|
27918
|
+
agent,
|
|
27919
|
+
session,
|
|
27920
|
+
context: context4,
|
|
27921
|
+
entry,
|
|
27922
|
+
logPrefix,
|
|
27923
|
+
initialPrompt,
|
|
27924
|
+
forceFirstTurn,
|
|
27925
|
+
finishExit,
|
|
27926
|
+
outputCallbacks,
|
|
27927
|
+
agentEndCallbacks
|
|
27928
|
+
});
|
|
27929
|
+
return {
|
|
27930
|
+
pid,
|
|
27931
|
+
harnessSessionId: agent.agentId,
|
|
27932
|
+
harnessReconnect: {
|
|
27933
|
+
agentName,
|
|
27934
|
+
...model ? { model } : {}
|
|
27935
|
+
},
|
|
27936
|
+
onExit: (cb) => {
|
|
27937
|
+
exitCallbacks.push(cb);
|
|
27938
|
+
},
|
|
27939
|
+
onOutput: (cb) => {
|
|
27940
|
+
outputCallbacks.push(cb);
|
|
27941
|
+
},
|
|
27942
|
+
onAgentEnd: (cb) => {
|
|
27943
|
+
agentEndCallbacks.push(cb);
|
|
27944
|
+
}
|
|
27945
|
+
};
|
|
27946
|
+
}
|
|
27947
|
+
runTurnLoop(args2) {
|
|
27948
|
+
const {
|
|
27949
|
+
agent,
|
|
27950
|
+
session,
|
|
27951
|
+
logPrefix,
|
|
27952
|
+
initialPrompt,
|
|
27953
|
+
forceFirstTurn,
|
|
27954
|
+
finishExit,
|
|
27955
|
+
entry,
|
|
27956
|
+
outputCallbacks,
|
|
27957
|
+
agentEndCallbacks
|
|
27958
|
+
} = args2;
|
|
27769
27959
|
(async () => {
|
|
27770
27960
|
let exitCode = 0;
|
|
27771
27961
|
let exitSignal = null;
|
|
27772
|
-
let nextPrompt =
|
|
27773
|
-
let isFirstTurn =
|
|
27962
|
+
let nextPrompt = initialPrompt;
|
|
27963
|
+
let isFirstTurn = forceFirstTurn;
|
|
27774
27964
|
try {
|
|
27775
27965
|
while (!session.aborted) {
|
|
27776
27966
|
const run3 = await withTimeout(agent.send(nextPrompt, {
|
|
@@ -27825,30 +28015,57 @@ ${options.prompt}` : options.prompt;
|
|
|
27825
28015
|
process.stderr.write(`${logPrefix} spawn-error] ${reason}
|
|
27826
28016
|
`);
|
|
27827
28017
|
} finally {
|
|
27828
|
-
if (!session.agentClosed) {
|
|
28018
|
+
if (!session.agentClosed && !session.preserveForResume) {
|
|
27829
28019
|
try {
|
|
27830
28020
|
agent.close();
|
|
27831
28021
|
session.agentClosed = true;
|
|
27832
28022
|
} catch {}
|
|
27833
28023
|
}
|
|
27834
28024
|
try {
|
|
27835
|
-
keeper.kill();
|
|
28025
|
+
session.keeper.kill();
|
|
27836
28026
|
} catch {}
|
|
27837
28027
|
finishExit(exitCode, exitSignal);
|
|
27838
28028
|
}
|
|
27839
28029
|
})();
|
|
27840
|
-
|
|
28030
|
+
}
|
|
28031
|
+
async spawn(options) {
|
|
28032
|
+
const apiKey = process.env.CURSOR_API_KEY?.trim();
|
|
28033
|
+
if (!apiKey) {
|
|
28034
|
+
throw new Error("CURSOR_API_KEY is not set");
|
|
28035
|
+
}
|
|
28036
|
+
const keeper = this.spawnKeeper(options.workingDir);
|
|
28037
|
+
const pid = keeper.pid;
|
|
28038
|
+
const context4 = options.context;
|
|
28039
|
+
const agentName = buildAgentName(context4);
|
|
28040
|
+
const modelId = resolveModelId(options.model);
|
|
28041
|
+
const fullPrompt = options.systemPrompt ? `${options.systemPrompt}
|
|
28042
|
+
|
|
28043
|
+
${options.prompt}` : options.prompt;
|
|
28044
|
+
let agent;
|
|
28045
|
+
try {
|
|
28046
|
+
const { Agent } = await loadSdk();
|
|
28047
|
+
agent = await withTimeout(Agent.create({
|
|
28048
|
+
apiKey,
|
|
28049
|
+
name: agentName,
|
|
28050
|
+
model: { id: modelId },
|
|
28051
|
+
local: { cwd: options.workingDir, settingSources: [] }
|
|
28052
|
+
}), AGENT_CREATE_TIMEOUT_MS, "Agent.create");
|
|
28053
|
+
} catch (err) {
|
|
28054
|
+
keeper.kill();
|
|
28055
|
+
this.deleteProcess(pid);
|
|
28056
|
+
throw err;
|
|
28057
|
+
}
|
|
28058
|
+
return this.startRunningSession({
|
|
27841
28059
|
pid,
|
|
27842
|
-
|
|
27843
|
-
|
|
27844
|
-
|
|
27845
|
-
|
|
27846
|
-
|
|
27847
|
-
|
|
27848
|
-
|
|
27849
|
-
|
|
27850
|
-
|
|
27851
|
-
};
|
|
28060
|
+
keeper,
|
|
28061
|
+
agent,
|
|
28062
|
+
context: context4,
|
|
28063
|
+
agentName,
|
|
28064
|
+
model: options.model,
|
|
28065
|
+
workingDir: options.workingDir,
|
|
28066
|
+
initialPrompt: fullPrompt,
|
|
28067
|
+
forceFirstTurn: true
|
|
28068
|
+
});
|
|
27852
28069
|
}
|
|
27853
28070
|
};
|
|
27854
28071
|
});
|
|
@@ -30039,12 +30256,12 @@ function startSessionEventForwarder(client4, options) {
|
|
|
30039
30256
|
// src/infrastructure/services/remote-agents/opencode-sdk/session-metadata-store.ts
|
|
30040
30257
|
import { existsSync, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "node:fs";
|
|
30041
30258
|
import { homedir as homedir2 } from "node:os";
|
|
30042
|
-
import { dirname as dirname2, join as
|
|
30259
|
+
import { dirname as dirname2, join as join8 } from "node:path";
|
|
30043
30260
|
|
|
30044
30261
|
class FileSessionMetadataStore {
|
|
30045
30262
|
filePath;
|
|
30046
30263
|
constructor(filePath) {
|
|
30047
|
-
this.filePath = filePath ??
|
|
30264
|
+
this.filePath = filePath ?? join8(homedir2(), ".chatroom", "opencode-sdk-sessions.json");
|
|
30048
30265
|
}
|
|
30049
30266
|
load() {
|
|
30050
30267
|
try {
|
|
@@ -30098,7 +30315,7 @@ async function withTimeout2(p, ms, label) {
|
|
|
30098
30315
|
clearTimeout(timer);
|
|
30099
30316
|
}
|
|
30100
30317
|
}
|
|
30101
|
-
var OPENCODE_COMMAND2 = "opencode", SERVE_STARTUP_TIMEOUT_MS = 1e4, SESSION_CREATE_TIMEOUT_MS = 30000, PROMPT_ASYNC_TIMEOUT_MS = 60000, SESSION_ABORT_TIMEOUT_MS = 5000, AGENTS_LIST_TIMEOUT_MS = 1e4, OpenCodeSdkAgentService;
|
|
30318
|
+
var OPENCODE_COMMAND2 = "opencode", SERVE_STARTUP_TIMEOUT_MS = 1e4, SESSION_CREATE_TIMEOUT_MS = 30000, PROMPT_ASYNC_TIMEOUT_MS = 60000, SESSION_ABORT_TIMEOUT_MS = 5000, SESSION_GET_TIMEOUT_MS = 1e4, AGENTS_LIST_TIMEOUT_MS = 1e4, OpenCodeSdkAgentService;
|
|
30102
30319
|
var init_opencode_sdk_agent_service = __esm(() => {
|
|
30103
30320
|
init_dist();
|
|
30104
30321
|
init_base_cli_agent_service();
|
|
@@ -30127,28 +30344,30 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30127
30344
|
return output.split(`
|
|
30128
30345
|
`).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
30129
30346
|
}
|
|
30130
|
-
async stop(pid) {
|
|
30347
|
+
async stop(pid, options) {
|
|
30131
30348
|
const forwarder = this.forwarders.get(pid);
|
|
30132
30349
|
if (forwarder) {
|
|
30133
30350
|
forwarder.stop();
|
|
30134
30351
|
this.forwarders.delete(pid);
|
|
30135
30352
|
}
|
|
30353
|
+
const preserveForResume = options?.preserveForResume === true;
|
|
30136
30354
|
const meta = this.sessionStore.findByPid(pid);
|
|
30137
30355
|
if (meta) {
|
|
30138
|
-
|
|
30139
|
-
|
|
30140
|
-
|
|
30141
|
-
|
|
30142
|
-
|
|
30356
|
+
if (!preserveForResume) {
|
|
30357
|
+
try {
|
|
30358
|
+
const client4 = createOpencodeClient({ baseUrl: meta.baseUrl });
|
|
30359
|
+
await withTimeout2(client4.session.abort({ path: { id: meta.sessionId } }), SESSION_ABORT_TIMEOUT_MS, "session.abort");
|
|
30360
|
+
} catch (err) {
|
|
30361
|
+
console.warn(`[opencode-sdk] session.abort for pid=${pid} sessionId=${meta.sessionId} failed (continuing with SIGTERM):`, err instanceof Error ? err.message : err);
|
|
30362
|
+
}
|
|
30143
30363
|
}
|
|
30144
30364
|
this.sessionStore.remove(meta.sessionId);
|
|
30145
30365
|
}
|
|
30146
30366
|
await super.stop(pid);
|
|
30147
30367
|
}
|
|
30148
|
-
|
|
30149
|
-
const { prompt, systemPrompt, model, context: context4 } = options;
|
|
30368
|
+
spawnServeProcess(workingDir) {
|
|
30150
30369
|
const childProcess = this.deps.spawn(OPENCODE_COMMAND2, ["serve", "--print-logs"], {
|
|
30151
|
-
cwd:
|
|
30370
|
+
cwd: workingDir,
|
|
30152
30371
|
stdio: ["pipe", "pipe", "pipe"],
|
|
30153
30372
|
shell: false,
|
|
30154
30373
|
detached: true,
|
|
@@ -30161,6 +30380,146 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30161
30380
|
if (!childProcess.pid) {
|
|
30162
30381
|
throw new Error("Failed to spawn opencode serve process");
|
|
30163
30382
|
}
|
|
30383
|
+
return childProcess;
|
|
30384
|
+
}
|
|
30385
|
+
registerRunningSession(args2) {
|
|
30386
|
+
const {
|
|
30387
|
+
childProcess,
|
|
30388
|
+
pid,
|
|
30389
|
+
sessionId,
|
|
30390
|
+
context: context4,
|
|
30391
|
+
forwarder,
|
|
30392
|
+
baseUrl,
|
|
30393
|
+
agentName,
|
|
30394
|
+
model,
|
|
30395
|
+
workingDir
|
|
30396
|
+
} = args2;
|
|
30397
|
+
const meta = {
|
|
30398
|
+
sessionId,
|
|
30399
|
+
machineId: context4.machineId,
|
|
30400
|
+
chatroomId: context4.chatroomId,
|
|
30401
|
+
role: context4.role,
|
|
30402
|
+
agentName,
|
|
30403
|
+
...model ? { model } : {},
|
|
30404
|
+
pid,
|
|
30405
|
+
createdAt: new Date().toISOString(),
|
|
30406
|
+
baseUrl
|
|
30407
|
+
};
|
|
30408
|
+
this.sessionStore.upsert(meta);
|
|
30409
|
+
const entry = this.registerProcess(pid, context4);
|
|
30410
|
+
if (forwarder)
|
|
30411
|
+
this.forwarders.set(pid, forwarder);
|
|
30412
|
+
const outputCallbacks = [];
|
|
30413
|
+
forwardFiltered(childProcess.stdout ?? undefined, process.stdout, isInfoLine);
|
|
30414
|
+
forwardFiltered(childProcess.stderr ?? undefined, process.stderr, isInfoLine);
|
|
30415
|
+
if (childProcess.stdout) {
|
|
30416
|
+
childProcess.stdout.on("data", () => {
|
|
30417
|
+
entry.lastOutputAt = Date.now();
|
|
30418
|
+
for (const cb of outputCallbacks)
|
|
30419
|
+
cb();
|
|
30420
|
+
});
|
|
30421
|
+
}
|
|
30422
|
+
if (childProcess.stderr) {
|
|
30423
|
+
childProcess.stderr.on("data", () => {
|
|
30424
|
+
entry.lastOutputAt = Date.now();
|
|
30425
|
+
for (const cb of outputCallbacks)
|
|
30426
|
+
cb();
|
|
30427
|
+
});
|
|
30428
|
+
}
|
|
30429
|
+
return {
|
|
30430
|
+
pid,
|
|
30431
|
+
harnessSessionId: sessionId,
|
|
30432
|
+
harnessReconnect: {
|
|
30433
|
+
agentName,
|
|
30434
|
+
...model ? { model } : {}
|
|
30435
|
+
},
|
|
30436
|
+
onExit: (cb) => {
|
|
30437
|
+
childProcess.on("exit", (code2, signal) => {
|
|
30438
|
+
const fwd = this.forwarders.get(pid);
|
|
30439
|
+
if (fwd) {
|
|
30440
|
+
fwd.stop();
|
|
30441
|
+
this.forwarders.delete(pid);
|
|
30442
|
+
}
|
|
30443
|
+
this.sessionStore.remove(sessionId);
|
|
30444
|
+
this.deleteProcess(pid);
|
|
30445
|
+
cb({ code: code2, signal, context: context4 });
|
|
30446
|
+
});
|
|
30447
|
+
},
|
|
30448
|
+
onOutput: (cb) => {
|
|
30449
|
+
outputCallbacks.push(cb);
|
|
30450
|
+
},
|
|
30451
|
+
onAgentEnd: (cb) => {
|
|
30452
|
+
forwarder?.onAgentEnd(cb);
|
|
30453
|
+
}
|
|
30454
|
+
};
|
|
30455
|
+
}
|
|
30456
|
+
async resumeFromDaemonMemory(options, session) {
|
|
30457
|
+
const { prompt, systemPrompt, model, context: context4 } = options;
|
|
30458
|
+
const sessionId = session.harnessSessionId;
|
|
30459
|
+
const agentName = session.agentName;
|
|
30460
|
+
const modelForSession = model ?? session.model;
|
|
30461
|
+
const workingDir = session.workingDir;
|
|
30462
|
+
const childProcess = this.spawnServeProcess(workingDir);
|
|
30463
|
+
const pid = childProcess.pid;
|
|
30464
|
+
const baseUrl = await waitForListeningUrl(childProcess, {
|
|
30465
|
+
timeoutMs: SERVE_STARTUP_TIMEOUT_MS
|
|
30466
|
+
}).catch((err) => {
|
|
30467
|
+
childProcess.kill();
|
|
30468
|
+
throw err;
|
|
30469
|
+
});
|
|
30470
|
+
const client4 = createOpencodeClient({ baseUrl });
|
|
30471
|
+
let forwarder;
|
|
30472
|
+
try {
|
|
30473
|
+
const sessionInfo = await withTimeout2(client4.session.get({ path: { id: sessionId } }), SESSION_GET_TIMEOUT_MS, "session.get");
|
|
30474
|
+
if (!sessionInfo.data?.id) {
|
|
30475
|
+
throw new Error(`OpenCode session ${sessionId} not found (sessions may not survive serve restart)`);
|
|
30476
|
+
}
|
|
30477
|
+
forwarder = startSessionEventForwarder(client4, {
|
|
30478
|
+
sessionId,
|
|
30479
|
+
role: context4.role
|
|
30480
|
+
});
|
|
30481
|
+
const agentsResponse = await withTimeout2(client4.app.agents(), AGENTS_LIST_TIMEOUT_MS, "app.agents");
|
|
30482
|
+
const availableAgents = agentsResponse.data ?? [];
|
|
30483
|
+
const agentDef = availableAgents.find((a) => a.name === agentName);
|
|
30484
|
+
const composedSystem = composeSystemPrompt(agentDef?.prompt, systemPrompt);
|
|
30485
|
+
const modelParts = modelForSession ? parseModelId(modelForSession) : undefined;
|
|
30486
|
+
await withTimeout2(client4.session.promptAsync({
|
|
30487
|
+
path: { id: sessionId },
|
|
30488
|
+
body: {
|
|
30489
|
+
agent: agentName,
|
|
30490
|
+
...composedSystem ? { system: composedSystem } : {},
|
|
30491
|
+
parts: [{ type: "text", text: prompt }],
|
|
30492
|
+
...modelParts ? { model: modelParts } : {},
|
|
30493
|
+
tools: {
|
|
30494
|
+
task: false,
|
|
30495
|
+
question: false,
|
|
30496
|
+
external_directory: false
|
|
30497
|
+
}
|
|
30498
|
+
}
|
|
30499
|
+
}), PROMPT_ASYNC_TIMEOUT_MS, "session.promptAsync");
|
|
30500
|
+
} catch (err) {
|
|
30501
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
30502
|
+
process.stderr.write(`[${new Date().toISOString()}] role:${context4.role} resume-error] ${reason}
|
|
30503
|
+
`);
|
|
30504
|
+
forwarder?.stop();
|
|
30505
|
+
childProcess.kill();
|
|
30506
|
+
throw err;
|
|
30507
|
+
}
|
|
30508
|
+
return this.registerRunningSession({
|
|
30509
|
+
childProcess,
|
|
30510
|
+
pid,
|
|
30511
|
+
sessionId,
|
|
30512
|
+
context: context4,
|
|
30513
|
+
forwarder,
|
|
30514
|
+
baseUrl,
|
|
30515
|
+
agentName,
|
|
30516
|
+
model: modelForSession,
|
|
30517
|
+
workingDir
|
|
30518
|
+
});
|
|
30519
|
+
}
|
|
30520
|
+
async spawn(options) {
|
|
30521
|
+
const { prompt, systemPrompt, model, context: context4 } = options;
|
|
30522
|
+
const childProcess = this.spawnServeProcess(options.workingDir);
|
|
30164
30523
|
const pid = childProcess.pid;
|
|
30165
30524
|
const baseUrl = await waitForListeningUrl(childProcess, {
|
|
30166
30525
|
timeoutMs: SERVE_STARTUP_TIMEOUT_MS
|
|
@@ -30214,59 +30573,26 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30214
30573
|
this.sessionStore.remove(sessionId);
|
|
30215
30574
|
throw err;
|
|
30216
30575
|
}
|
|
30217
|
-
|
|
30576
|
+
return this.registerRunningSession({
|
|
30577
|
+
childProcess,
|
|
30578
|
+
pid,
|
|
30218
30579
|
sessionId,
|
|
30219
|
-
|
|
30220
|
-
|
|
30221
|
-
|
|
30580
|
+
context: context4,
|
|
30581
|
+
forwarder,
|
|
30582
|
+
baseUrl,
|
|
30222
30583
|
agentName,
|
|
30223
|
-
|
|
30224
|
-
|
|
30225
|
-
|
|
30226
|
-
|
|
30227
|
-
|
|
30228
|
-
this.sessionStore.
|
|
30229
|
-
|
|
30230
|
-
|
|
30231
|
-
this.forwarders.set(pid, forwarder);
|
|
30232
|
-
const outputCallbacks = [];
|
|
30233
|
-
forwardFiltered(childProcess.stdout, process.stdout, isInfoLine);
|
|
30234
|
-
forwardFiltered(childProcess.stderr, process.stderr, isInfoLine);
|
|
30235
|
-
if (childProcess.stdout) {
|
|
30236
|
-
childProcess.stdout.on("data", () => {
|
|
30237
|
-
entry.lastOutputAt = Date.now();
|
|
30238
|
-
for (const cb of outputCallbacks)
|
|
30239
|
-
cb();
|
|
30240
|
-
});
|
|
30241
|
-
}
|
|
30242
|
-
if (childProcess.stderr) {
|
|
30243
|
-
childProcess.stderr.on("data", () => {
|
|
30244
|
-
entry.lastOutputAt = Date.now();
|
|
30245
|
-
for (const cb of outputCallbacks)
|
|
30246
|
-
cb();
|
|
30247
|
-
});
|
|
30584
|
+
model,
|
|
30585
|
+
workingDir: options.workingDir
|
|
30586
|
+
});
|
|
30587
|
+
}
|
|
30588
|
+
getHarnessReconnectContext(pid) {
|
|
30589
|
+
const meta = this.sessionStore.findByPid(pid);
|
|
30590
|
+
if (!meta) {
|
|
30591
|
+
return;
|
|
30248
30592
|
}
|
|
30249
30593
|
return {
|
|
30250
|
-
|
|
30251
|
-
|
|
30252
|
-
onExit: (cb) => {
|
|
30253
|
-
childProcess.on("exit", (code2, signal) => {
|
|
30254
|
-
const fwd = this.forwarders.get(pid);
|
|
30255
|
-
if (fwd) {
|
|
30256
|
-
fwd.stop();
|
|
30257
|
-
this.forwarders.delete(pid);
|
|
30258
|
-
}
|
|
30259
|
-
this.sessionStore.remove(sessionId);
|
|
30260
|
-
this.deleteProcess(pid);
|
|
30261
|
-
cb({ code: code2, signal, context: context4 });
|
|
30262
|
-
});
|
|
30263
|
-
},
|
|
30264
|
-
onOutput: (cb) => {
|
|
30265
|
-
outputCallbacks.push(cb);
|
|
30266
|
-
},
|
|
30267
|
-
onAgentEnd: (cb) => {
|
|
30268
|
-
forwarder?.onAgentEnd(cb);
|
|
30269
|
-
}
|
|
30594
|
+
agentName: meta.agentName,
|
|
30595
|
+
...meta.model ? { model: meta.model } : {}
|
|
30270
30596
|
};
|
|
30271
30597
|
}
|
|
30272
30598
|
async resumeTurn(pid, prompt) {
|
|
@@ -30401,16 +30727,16 @@ var MACHINE_CONFIG_VERSION = "1";
|
|
|
30401
30727
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
30402
30728
|
import * as fs2 from "node:fs/promises";
|
|
30403
30729
|
import { homedir as homedir3, hostname as hostname2 } from "node:os";
|
|
30404
|
-
import { join as
|
|
30730
|
+
import { join as join9 } from "node:path";
|
|
30405
30731
|
function chatroomConfigDir() {
|
|
30406
|
-
return
|
|
30732
|
+
return join9(homedir3(), ".chatroom");
|
|
30407
30733
|
}
|
|
30408
30734
|
async function ensureConfigDir2() {
|
|
30409
30735
|
const dir = chatroomConfigDir();
|
|
30410
30736
|
await fs2.mkdir(dir, { recursive: true, mode: 448 });
|
|
30411
30737
|
}
|
|
30412
30738
|
function getMachineConfigPath() {
|
|
30413
|
-
return
|
|
30739
|
+
return join9(chatroomConfigDir(), MACHINE_FILE);
|
|
30414
30740
|
}
|
|
30415
30741
|
async function loadConfigFile() {
|
|
30416
30742
|
const configPath = getMachineConfigPath();
|
|
@@ -30502,7 +30828,7 @@ var init_storage2 = __esm(() => {
|
|
|
30502
30828
|
// src/infrastructure/machine/daemon-state.ts
|
|
30503
30829
|
import * as fs3 from "node:fs/promises";
|
|
30504
30830
|
import { homedir as homedir4 } from "node:os";
|
|
30505
|
-
import { join as
|
|
30831
|
+
import { join as join10 } from "node:path";
|
|
30506
30832
|
function agentKey(chatroomId, role) {
|
|
30507
30833
|
return `${chatroomId}/${role}`;
|
|
30508
30834
|
}
|
|
@@ -30510,7 +30836,7 @@ async function ensureStateDir() {
|
|
|
30510
30836
|
await fs3.mkdir(STATE_DIR, { recursive: true, mode: 448 });
|
|
30511
30837
|
}
|
|
30512
30838
|
function stateFilePath(machineId) {
|
|
30513
|
-
return
|
|
30839
|
+
return join10(STATE_DIR, `${machineId}.json`);
|
|
30514
30840
|
}
|
|
30515
30841
|
async function loadDaemonState(machineId) {
|
|
30516
30842
|
const filePath = stateFilePath(machineId);
|
|
@@ -30592,8 +30918,8 @@ async function loadEventCursor(machineId) {
|
|
|
30592
30918
|
}
|
|
30593
30919
|
var CHATROOM_DIR2, STATE_DIR, STATE_VERSION = "1";
|
|
30594
30920
|
var init_daemon_state = __esm(() => {
|
|
30595
|
-
CHATROOM_DIR2 =
|
|
30596
|
-
STATE_DIR =
|
|
30921
|
+
CHATROOM_DIR2 = join10(homedir4(), ".chatroom");
|
|
30922
|
+
STATE_DIR = join10(CHATROOM_DIR2, "machines", "state");
|
|
30597
30923
|
});
|
|
30598
30924
|
|
|
30599
30925
|
// src/infrastructure/machine/index.ts
|
|
@@ -30966,9 +31292,9 @@ var init_init = __esm(() => {
|
|
|
30966
31292
|
});
|
|
30967
31293
|
|
|
30968
31294
|
// src/tools/output.ts
|
|
30969
|
-
import { join as
|
|
31295
|
+
import { join as join11 } from "node:path";
|
|
30970
31296
|
function resolveChatroomDir(workingDir) {
|
|
30971
|
-
return
|
|
31297
|
+
return join11(workingDir, CHATROOM_DIR3);
|
|
30972
31298
|
}
|
|
30973
31299
|
async function ensureChatroomDir(deps, workingDir) {
|
|
30974
31300
|
const dir = resolveChatroomDir(workingDir);
|
|
@@ -30976,7 +31302,7 @@ async function ensureChatroomDir(deps, workingDir) {
|
|
|
30976
31302
|
return dir;
|
|
30977
31303
|
}
|
|
30978
31304
|
async function ensureGitignore(deps, workingDir) {
|
|
30979
|
-
const gitignorePath =
|
|
31305
|
+
const gitignorePath = join11(workingDir, ".gitignore");
|
|
30980
31306
|
const entry = CHATROOM_DIR3;
|
|
30981
31307
|
let content = "";
|
|
30982
31308
|
try {
|
|
@@ -31006,7 +31332,7 @@ function formatOutputTimestamp(date = new Date) {
|
|
|
31006
31332
|
function generateOutputPath(workingDir, toolName, extension, date) {
|
|
31007
31333
|
const timestamp = formatOutputTimestamp(date);
|
|
31008
31334
|
const filename = `${toolName}-${timestamp}.${extension}`;
|
|
31009
|
-
return
|
|
31335
|
+
return join11(resolveChatroomDir(workingDir), filename);
|
|
31010
31336
|
}
|
|
31011
31337
|
var CHATROOM_DIR3 = ".chatroom";
|
|
31012
31338
|
var init_output = () => {};
|
|
@@ -79409,20 +79735,20 @@ function formatTimestamp() {
|
|
|
79409
79735
|
|
|
79410
79736
|
// src/infrastructure/services/workspace/workspace-resolver.ts
|
|
79411
79737
|
import { readFile as readFile5, readdir, stat } from "node:fs/promises";
|
|
79412
|
-
import { join as
|
|
79738
|
+
import { join as join13, basename, resolve as resolve3 } from "node:path";
|
|
79413
79739
|
async function resolveGlobPattern(rootDir, pattern) {
|
|
79414
79740
|
const cleaned = pattern.replace(/\/+$/, "");
|
|
79415
79741
|
if (cleaned.includes("..")) {
|
|
79416
79742
|
return [];
|
|
79417
79743
|
}
|
|
79418
79744
|
if (cleaned.endsWith("/*")) {
|
|
79419
|
-
const parentDir =
|
|
79745
|
+
const parentDir = join13(rootDir, cleaned.slice(0, -2));
|
|
79420
79746
|
try {
|
|
79421
79747
|
const entries2 = await readdir(parentDir, { withFileTypes: true });
|
|
79422
79748
|
const dirs = [];
|
|
79423
79749
|
for (const entry of entries2) {
|
|
79424
79750
|
if (entry.isDirectory()) {
|
|
79425
|
-
const dirPath =
|
|
79751
|
+
const dirPath = join13(parentDir, entry.name);
|
|
79426
79752
|
if (resolve3(dirPath).startsWith(resolve3(rootDir))) {
|
|
79427
79753
|
dirs.push(dirPath);
|
|
79428
79754
|
}
|
|
@@ -79433,7 +79759,7 @@ async function resolveGlobPattern(rootDir, pattern) {
|
|
|
79433
79759
|
return [];
|
|
79434
79760
|
}
|
|
79435
79761
|
} else {
|
|
79436
|
-
const dir =
|
|
79762
|
+
const dir = join13(rootDir, cleaned);
|
|
79437
79763
|
try {
|
|
79438
79764
|
if (!resolve3(dir).startsWith(resolve3(rootDir)))
|
|
79439
79765
|
return [];
|
|
@@ -79446,7 +79772,7 @@ async function resolveGlobPattern(rootDir, pattern) {
|
|
|
79446
79772
|
}
|
|
79447
79773
|
async function readPackageJson(dir) {
|
|
79448
79774
|
try {
|
|
79449
|
-
const content = await readFile5(
|
|
79775
|
+
const content = await readFile5(join13(dir, "package.json"), "utf-8");
|
|
79450
79776
|
const pkg = JSON.parse(content);
|
|
79451
79777
|
return {
|
|
79452
79778
|
name: pkg.name || basename(dir),
|
|
@@ -79458,7 +79784,7 @@ async function readPackageJson(dir) {
|
|
|
79458
79784
|
}
|
|
79459
79785
|
async function readPnpmWorkspacePatterns(rootDir) {
|
|
79460
79786
|
try {
|
|
79461
|
-
const content = await readFile5(
|
|
79787
|
+
const content = await readFile5(join13(rootDir, "pnpm-workspace.yaml"), "utf-8");
|
|
79462
79788
|
const patterns = [];
|
|
79463
79789
|
let inPackages = false;
|
|
79464
79790
|
for (const line of content.split(`
|
|
@@ -79485,7 +79811,7 @@ async function readPnpmWorkspacePatterns(rootDir) {
|
|
|
79485
79811
|
}
|
|
79486
79812
|
async function readPackageJsonWorkspacePatterns(rootDir) {
|
|
79487
79813
|
try {
|
|
79488
|
-
const content = await readFile5(
|
|
79814
|
+
const content = await readFile5(join13(rootDir, "package.json"), "utf-8");
|
|
79489
79815
|
const pkg = JSON.parse(content);
|
|
79490
79816
|
if (!pkg.workspaces)
|
|
79491
79817
|
return [];
|
|
@@ -79534,11 +79860,11 @@ var init_workspace_resolver = () => {};
|
|
|
79534
79860
|
|
|
79535
79861
|
// src/infrastructure/services/workspace/command-discovery.ts
|
|
79536
79862
|
import { access as access2, readFile as readFile6 } from "node:fs/promises";
|
|
79537
|
-
import { join as
|
|
79863
|
+
import { join as join14, relative, basename as basename2 } from "node:path";
|
|
79538
79864
|
async function detectPackageManager(workingDir) {
|
|
79539
79865
|
for (const { file, manager } of LOCKFILE_MAP) {
|
|
79540
79866
|
try {
|
|
79541
|
-
await access2(
|
|
79867
|
+
await access2(join14(workingDir, file));
|
|
79542
79868
|
return manager;
|
|
79543
79869
|
} catch {}
|
|
79544
79870
|
}
|
|
@@ -79585,7 +79911,7 @@ async function discoverCommands(workingDir) {
|
|
|
79585
79911
|
const turboTaskNames = [];
|
|
79586
79912
|
let rootPackageName = basename2(workingDir);
|
|
79587
79913
|
try {
|
|
79588
|
-
const pkgPath =
|
|
79914
|
+
const pkgPath = join14(workingDir, "package.json");
|
|
79589
79915
|
const pkgContent = await readFile6(pkgPath, "utf-8");
|
|
79590
79916
|
const pkg = JSON.parse(pkgContent);
|
|
79591
79917
|
if (pkg.name)
|
|
@@ -79606,7 +79932,7 @@ async function discoverCommands(workingDir) {
|
|
|
79606
79932
|
} catch {}
|
|
79607
79933
|
const rootSubWorkspace = { type: "npm", path: ".", name: rootPackageName };
|
|
79608
79934
|
try {
|
|
79609
|
-
const turboPath =
|
|
79935
|
+
const turboPath = join14(workingDir, "turbo.json");
|
|
79610
79936
|
const turboContent = await readFile6(turboPath, "utf-8");
|
|
79611
79937
|
const turbo = JSON.parse(turboContent);
|
|
79612
79938
|
if (turbo.tasks && typeof turbo.tasks === "object") {
|
|
@@ -79660,19 +79986,43 @@ var init_command_discovery = __esm(() => {
|
|
|
79660
79986
|
];
|
|
79661
79987
|
});
|
|
79662
79988
|
|
|
79663
|
-
// src/commands/machine/daemon-start/
|
|
79664
|
-
|
|
79665
|
-
|
|
79666
|
-
|
|
79989
|
+
// src/commands/machine/daemon-start/workspace-cache.ts
|
|
79990
|
+
function invalidateWorkspacesForMachineCache(ctx) {
|
|
79991
|
+
delete ctx._workspacesCache;
|
|
79992
|
+
}
|
|
79993
|
+
async function getWorkspacesForMachine(ctx) {
|
|
79994
|
+
const extended = ctx;
|
|
79995
|
+
const now = Date.now();
|
|
79996
|
+
const cached4 = extended._workspacesCache;
|
|
79997
|
+
if (cached4 && now - cached4.fetchedAt < CACHE_TTL_MS) {
|
|
79998
|
+
return cached4.workspaces;
|
|
79999
|
+
}
|
|
79667
80000
|
try {
|
|
79668
|
-
workspaces = await ctx.deps.backend.query(api.workspaces.listWorkspacesForMachine, {
|
|
80001
|
+
const workspaces = await ctx.deps.backend.query(api.workspaces.listWorkspacesForMachine, {
|
|
79669
80002
|
sessionId: ctx.sessionId,
|
|
79670
80003
|
machineId: ctx.machineId
|
|
79671
80004
|
});
|
|
80005
|
+
extended._workspacesCache = { fetchedAt: now, workspaces };
|
|
80006
|
+
return workspaces;
|
|
79672
80007
|
} catch (err) {
|
|
79673
|
-
console.warn(`[${formatTimestamp()}] ⚠️ Failed to query workspaces
|
|
79674
|
-
return;
|
|
80008
|
+
console.warn(`[${formatTimestamp()}] ⚠️ Failed to query workspaces: ${getErrorMessage(err)}`);
|
|
80009
|
+
return [];
|
|
79675
80010
|
}
|
|
80011
|
+
}
|
|
80012
|
+
var CACHE_TTL_MS;
|
|
80013
|
+
var init_workspace_cache = __esm(() => {
|
|
80014
|
+
init_reliability();
|
|
80015
|
+
init_api3();
|
|
80016
|
+
init_convex_error();
|
|
80017
|
+
CACHE_TTL_MS = DAEMON_HEARTBEAT_INTERVAL_MS;
|
|
80018
|
+
});
|
|
80019
|
+
|
|
80020
|
+
// src/commands/machine/daemon-start/command-sync-heartbeat.ts
|
|
80021
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
80022
|
+
async function pushCommands(ctx) {
|
|
80023
|
+
const workspaces = await getWorkspacesForMachine(ctx);
|
|
80024
|
+
if (workspaces.length === 0)
|
|
80025
|
+
return;
|
|
79676
80026
|
const uniqueWorkingDirs = new Set(workspaces.map((ws) => ws.workingDir));
|
|
79677
80027
|
if (uniqueWorkingDirs.size === 0)
|
|
79678
80028
|
return;
|
|
@@ -79704,6 +80054,7 @@ var init_command_sync_heartbeat = __esm(() => {
|
|
|
79704
80054
|
init_api3();
|
|
79705
80055
|
init_command_discovery();
|
|
79706
80056
|
init_convex_error();
|
|
80057
|
+
init_workspace_cache();
|
|
79707
80058
|
});
|
|
79708
80059
|
|
|
79709
80060
|
// src/events/daemon/agent/on-request-start-agent.ts
|
|
@@ -79720,7 +80071,8 @@ async function onRequestStartAgent(ctx, event) {
|
|
|
79720
80071
|
agentHarness: event.agentHarness,
|
|
79721
80072
|
model: event.model,
|
|
79722
80073
|
workingDir: event.workingDir,
|
|
79723
|
-
reason: event.reason
|
|
80074
|
+
reason: event.reason,
|
|
80075
|
+
wantResume: event.wantResume ?? true
|
|
79724
80076
|
});
|
|
79725
80077
|
if (!result.success) {
|
|
79726
80078
|
console.log(`[daemon] Agent start rejected for role=${event.role}: ${result.error ?? "unknown"}`);
|
|
@@ -79787,7 +80139,7 @@ var init_on_request_stop_agent = () => {};
|
|
|
79787
80139
|
import { createHash as createHash3 } from "node:crypto";
|
|
79788
80140
|
import { existsSync as existsSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3, unlinkSync, mkdirSync as mkdirSync4 } from "node:fs";
|
|
79789
80141
|
import { homedir as homedir5 } from "node:os";
|
|
79790
|
-
import { join as
|
|
80142
|
+
import { join as join15 } from "node:path";
|
|
79791
80143
|
function getUrlHash() {
|
|
79792
80144
|
const url2 = getConvexUrl();
|
|
79793
80145
|
return createHash3("sha256").update(url2).digest("hex").substring(0, 8);
|
|
@@ -79801,7 +80153,7 @@ function ensureChatroomDir2() {
|
|
|
79801
80153
|
}
|
|
79802
80154
|
}
|
|
79803
80155
|
function getPidFilePath() {
|
|
79804
|
-
return
|
|
80156
|
+
return join15(CHATROOM_DIR4, getPidFileName());
|
|
79805
80157
|
}
|
|
79806
80158
|
function isProcessRunning(pid) {
|
|
79807
80159
|
try {
|
|
@@ -79866,7 +80218,7 @@ function releaseLock() {
|
|
|
79866
80218
|
var CHATROOM_DIR4;
|
|
79867
80219
|
var init_pid = __esm(() => {
|
|
79868
80220
|
init_client2();
|
|
79869
|
-
CHATROOM_DIR4 =
|
|
80221
|
+
CHATROOM_DIR4 = join15(homedir5(), ".chatroom");
|
|
79870
80222
|
});
|
|
79871
80223
|
|
|
79872
80224
|
// src/infrastructure/git/types.ts
|
|
@@ -80395,16 +80747,9 @@ function makeBranchDependentFields(branch) {
|
|
|
80395
80747
|
];
|
|
80396
80748
|
}
|
|
80397
80749
|
async function pushGitState(ctx) {
|
|
80398
|
-
|
|
80399
|
-
|
|
80400
|
-
workspaces = await ctx.deps.backend.query(api.workspaces.listWorkspacesForMachine, {
|
|
80401
|
-
sessionId: ctx.sessionId,
|
|
80402
|
-
machineId: ctx.machineId
|
|
80403
|
-
});
|
|
80404
|
-
} catch (err) {
|
|
80405
|
-
console.warn(`[${formatTimestamp()}] ⚠️ Failed to query workspaces for git sync: ${getErrorMessage(err)}`);
|
|
80750
|
+
const workspaces = await getWorkspacesForMachine(ctx);
|
|
80751
|
+
if (workspaces.length === 0)
|
|
80406
80752
|
return;
|
|
80407
|
-
}
|
|
80408
80753
|
const uniqueWorkingDirs = new Set(workspaces.map((ws) => ws.workingDir));
|
|
80409
80754
|
if (uniqueWorkingDirs.size === 0)
|
|
80410
80755
|
return;
|
|
@@ -80555,6 +80900,7 @@ var lastFullPushMs, branchField, GIT_STATE_FIELDS;
|
|
|
80555
80900
|
var init_git_heartbeat = __esm(() => {
|
|
80556
80901
|
init_reliability();
|
|
80557
80902
|
init_api3();
|
|
80903
|
+
init_workspace_cache();
|
|
80558
80904
|
init_git_reader();
|
|
80559
80905
|
init_git_state_pipeline();
|
|
80560
80906
|
init_convex_error();
|
|
@@ -80956,16 +81302,9 @@ var init_git_subscription = __esm(() => {
|
|
|
80956
81302
|
|
|
80957
81303
|
// src/commands/machine/daemon-start/commit-detail-sync.ts
|
|
80958
81304
|
async function syncCommitDetails(ctx, seenShasMap) {
|
|
80959
|
-
|
|
80960
|
-
|
|
80961
|
-
workspaces = await ctx.deps.backend.query(api.workspaces.listWorkspacesForMachine, {
|
|
80962
|
-
sessionId: ctx.sessionId,
|
|
80963
|
-
machineId: ctx.machineId
|
|
80964
|
-
});
|
|
80965
|
-
} catch (err) {
|
|
80966
|
-
console.warn(`[${formatTimestamp()}] ⚠️ Failed to query workspaces for commit-detail sync: ${getErrorMessage(err)}`);
|
|
81305
|
+
const workspaces = await getWorkspacesForMachine(ctx);
|
|
81306
|
+
if (workspaces.length === 0)
|
|
80967
81307
|
return;
|
|
80968
|
-
}
|
|
80969
81308
|
const uniqueWorkingDirs = new Set(workspaces.map((ws) => ws.workingDir));
|
|
80970
81309
|
if (uniqueWorkingDirs.size === 0)
|
|
80971
81310
|
return;
|
|
@@ -81070,6 +81409,7 @@ var init_commit_detail_sync = __esm(() => {
|
|
|
81070
81409
|
init_api3();
|
|
81071
81410
|
init_git_reader();
|
|
81072
81411
|
init_convex_error();
|
|
81412
|
+
init_workspace_cache();
|
|
81073
81413
|
seenShas = new Map;
|
|
81074
81414
|
});
|
|
81075
81415
|
|
|
@@ -82496,17 +82836,17 @@ import {
|
|
|
82496
82836
|
writeFileSync as writeFileSync4
|
|
82497
82837
|
} from "node:fs";
|
|
82498
82838
|
import { homedir as homedir6 } from "node:os";
|
|
82499
|
-
import { join as
|
|
82839
|
+
import { join as join17 } from "node:path";
|
|
82500
82840
|
function getUrlHash2() {
|
|
82501
82841
|
const url2 = getConvexUrl();
|
|
82502
82842
|
return createHash6("sha256").update(url2).digest("hex").substring(0, 8);
|
|
82503
82843
|
}
|
|
82504
82844
|
function getChildPidsFilePath() {
|
|
82505
|
-
const dir =
|
|
82506
|
-
return
|
|
82845
|
+
const dir = join17(homedir6(), ".chatroom");
|
|
82846
|
+
return join17(dir, `daemon-children-${getUrlHash2()}.pids`);
|
|
82507
82847
|
}
|
|
82508
82848
|
function ensureChatroomDir3() {
|
|
82509
|
-
const dir =
|
|
82849
|
+
const dir = join17(homedir6(), ".chatroom");
|
|
82510
82850
|
if (!existsSync3(dir)) {
|
|
82511
82851
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
82512
82852
|
}
|
|
@@ -82600,7 +82940,7 @@ async function reapOrphanedProcessGroups() {
|
|
|
82600
82940
|
var CHATROOM_DIR5;
|
|
82601
82941
|
var init_orphan_tracker = __esm(() => {
|
|
82602
82942
|
init_client2();
|
|
82603
|
-
CHATROOM_DIR5 =
|
|
82943
|
+
CHATROOM_DIR5 = join17(homedir6(), ".chatroom");
|
|
82604
82944
|
});
|
|
82605
82945
|
|
|
82606
82946
|
// src/commands/machine/daemon-start/handlers/process/state.ts
|
|
@@ -82738,7 +83078,7 @@ var init_killer = __esm(() => {
|
|
|
82738
83078
|
// src/commands/machine/daemon-start/handlers/process/output-store.ts
|
|
82739
83079
|
import { appendFile as appendFile2, mkdir as mkdir6, readFile as readFile8, rm } from "node:fs/promises";
|
|
82740
83080
|
import { tmpdir } from "node:os";
|
|
82741
|
-
import { join as
|
|
83081
|
+
import { join as join18 } from "node:path";
|
|
82742
83082
|
|
|
82743
83083
|
class TempFileOutputStore {
|
|
82744
83084
|
state;
|
|
@@ -82801,7 +83141,7 @@ function createOutputStore(runId) {
|
|
|
82801
83141
|
if (!RUN_ID_RE.test(runId)) {
|
|
82802
83142
|
throw new Error(`Invalid runId: ${runId}`);
|
|
82803
83143
|
}
|
|
82804
|
-
const filePath =
|
|
83144
|
+
const filePath = join18(TEMP_DIR, `${runId}.log`);
|
|
82805
83145
|
return new TempFileOutputStore(filePath);
|
|
82806
83146
|
}
|
|
82807
83147
|
async function ensureTempDir() {
|
|
@@ -82815,7 +83155,7 @@ async function cleanOrphanTempFiles() {
|
|
|
82815
83155
|
var TAIL_WINDOW_BYTES, TEMP_DIR, RUN_ID_RE, MAX_TAIL_LINES_V2 = 50;
|
|
82816
83156
|
var init_output_store = __esm(() => {
|
|
82817
83157
|
TAIL_WINDOW_BYTES = 32 * 1024;
|
|
82818
|
-
TEMP_DIR =
|
|
83158
|
+
TEMP_DIR = join18(tmpdir(), "chatroom-cli", "runs");
|
|
82819
83159
|
RUN_ID_RE = /^[a-z0-9]+$/i;
|
|
82820
83160
|
});
|
|
82821
83161
|
|
|
@@ -82831,9 +83171,28 @@ function consumePendingFullSync(runId) {
|
|
|
82831
83171
|
}
|
|
82832
83172
|
function startLogObserverPoll(ctx) {
|
|
82833
83173
|
let stopped = false;
|
|
82834
|
-
|
|
83174
|
+
let consecutiveIdlePolls = 0;
|
|
83175
|
+
let pollInFlight = false;
|
|
83176
|
+
let timeoutHandle = null;
|
|
83177
|
+
const scheduleNext = (delayMs) => {
|
|
82835
83178
|
if (stopped)
|
|
82836
83179
|
return;
|
|
83180
|
+
if (timeoutHandle)
|
|
83181
|
+
clearTimeout(timeoutHandle);
|
|
83182
|
+
timeoutHandle = setTimeout(() => {
|
|
83183
|
+
poll4();
|
|
83184
|
+
}, delayMs);
|
|
83185
|
+
timeoutHandle.unref?.();
|
|
83186
|
+
};
|
|
83187
|
+
const poll4 = async () => {
|
|
83188
|
+
if (stopped || pollInFlight)
|
|
83189
|
+
return;
|
|
83190
|
+
const hasLocalObservers = observedRunIds.size > 0;
|
|
83191
|
+
if (!hasLocalObservers && consecutiveIdlePolls >= IDLE_SKIP_AFTER_CONSECUTIVE) {
|
|
83192
|
+
scheduleNext(IDLE_POLL_INTERVAL_MS);
|
|
83193
|
+
return;
|
|
83194
|
+
}
|
|
83195
|
+
pollInFlight = true;
|
|
82837
83196
|
try {
|
|
82838
83197
|
const runs = await ctx.deps.backend.query(api.commands.listRunsWithLogObservers, {
|
|
82839
83198
|
sessionId: ctx.sessionId,
|
|
@@ -82847,31 +83206,40 @@ function startLogObserverPoll(ctx) {
|
|
|
82847
83206
|
pendingFullSyncRunIds.add(run3._id);
|
|
82848
83207
|
}
|
|
82849
83208
|
}
|
|
83209
|
+
const isActive2 = runs.length > 0 || hasLocalObservers;
|
|
83210
|
+
if (isActive2) {
|
|
83211
|
+
consecutiveIdlePolls = 0;
|
|
83212
|
+
scheduleNext(ACTIVE_POLL_INTERVAL_MS);
|
|
83213
|
+
} else {
|
|
83214
|
+
consecutiveIdlePolls++;
|
|
83215
|
+
scheduleNext(consecutiveIdlePolls >= IDLE_SKIP_AFTER_CONSECUTIVE ? IDLE_POLL_INTERVAL_MS : ACTIVE_POLL_INTERVAL_MS);
|
|
83216
|
+
}
|
|
82850
83217
|
} catch (err) {
|
|
82851
83218
|
console.warn(`[${formatTimestamp()}] ⚠️ Log-observer poll failed: ${getErrorMessage(err)}`);
|
|
83219
|
+
scheduleNext(ACTIVE_POLL_INTERVAL_MS);
|
|
83220
|
+
} finally {
|
|
83221
|
+
pollInFlight = false;
|
|
82852
83222
|
}
|
|
82853
83223
|
};
|
|
82854
83224
|
poll4();
|
|
82855
|
-
const handle = setInterval(() => {
|
|
82856
|
-
poll4();
|
|
82857
|
-
}, OUTPUT_FLUSH_INTERVAL_MS);
|
|
82858
|
-
handle.unref?.();
|
|
82859
83225
|
return {
|
|
82860
83226
|
stop: () => {
|
|
82861
83227
|
stopped = true;
|
|
82862
|
-
|
|
83228
|
+
if (timeoutHandle)
|
|
83229
|
+
clearTimeout(timeoutHandle);
|
|
82863
83230
|
observedRunIds.clear();
|
|
82864
83231
|
pendingFullSyncRunIds.clear();
|
|
82865
83232
|
}
|
|
82866
83233
|
};
|
|
82867
83234
|
}
|
|
82868
|
-
var observedRunIds, pendingFullSyncRunIds;
|
|
83235
|
+
var observedRunIds, pendingFullSyncRunIds, ACTIVE_POLL_INTERVAL_MS, IDLE_POLL_INTERVAL_MS = 15000, IDLE_SKIP_AFTER_CONSECUTIVE = 3;
|
|
82869
83236
|
var init_log_observer_sync = __esm(() => {
|
|
82870
83237
|
init_api3();
|
|
82871
83238
|
init_convex_error();
|
|
82872
83239
|
init_state2();
|
|
82873
83240
|
observedRunIds = new Set;
|
|
82874
83241
|
pendingFullSyncRunIds = new Set;
|
|
83242
|
+
ACTIVE_POLL_INTERVAL_MS = OUTPUT_FLUSH_INTERVAL_MS;
|
|
82875
83243
|
});
|
|
82876
83244
|
|
|
82877
83245
|
// src/commands/machine/daemon-start/handlers/process/spawner.ts
|
|
@@ -83565,6 +83933,7 @@ function agentKey2(chatroomId, role) {
|
|
|
83565
83933
|
class AgentProcessManager {
|
|
83566
83934
|
deps;
|
|
83567
83935
|
slots = new Map;
|
|
83936
|
+
lastHarnessSessions = new Map;
|
|
83568
83937
|
exitRetryQueue = [];
|
|
83569
83938
|
exitRetryTimer = null;
|
|
83570
83939
|
constructor(deps) {
|
|
@@ -83638,11 +84007,20 @@ class AgentProcessManager {
|
|
|
83638
84007
|
return { success: true };
|
|
83639
84008
|
}
|
|
83640
84009
|
async handleAgentEnd(opts) {
|
|
84010
|
+
const key = agentKey2(opts.chatroomId, opts.role);
|
|
84011
|
+
const slot = this.slots.get(key);
|
|
84012
|
+
if (slot?.resumeInFlight) {
|
|
84013
|
+
console.log(`[AgentProcessManager] agent_end: skipping duplicate resume for ${opts.role} (resume already in flight)`);
|
|
84014
|
+
return;
|
|
84015
|
+
}
|
|
83641
84016
|
const capabilities = getHarnessCapabilities(opts.harness);
|
|
83642
|
-
console.log(`[AgentProcessManager] agent_end: role=${opts.role} pid=${opts.pid} harness=${opts.harness}
|
|
83643
|
-
if (capabilities.supportsSessionResume
|
|
84017
|
+
console.log(`[AgentProcessManager] agent_end: role=${opts.role} pid=${opts.pid} harness=${opts.harness} supportsResume=${capabilities.supportsSessionResume}`);
|
|
84018
|
+
if (capabilities.supportsSessionResume) {
|
|
83644
84019
|
const service = this.deps.agentServices.get(opts.harness);
|
|
83645
84020
|
if (service?.resumeTurn) {
|
|
84021
|
+
if (slot) {
|
|
84022
|
+
slot.resumeInFlight = true;
|
|
84023
|
+
}
|
|
83646
84024
|
try {
|
|
83647
84025
|
const resumePrompt = composeResumeMessage({
|
|
83648
84026
|
chatroomId: opts.chatroomId,
|
|
@@ -83650,9 +84028,39 @@ class AgentProcessManager {
|
|
|
83650
84028
|
convexUrl: this.deps.convexUrl
|
|
83651
84029
|
});
|
|
83652
84030
|
await service.resumeTurn(opts.pid, resumePrompt);
|
|
84031
|
+
try {
|
|
84032
|
+
await this.deps.backend.mutation(api.machines.emitSessionResumed, {
|
|
84033
|
+
sessionId: this.deps.sessionId,
|
|
84034
|
+
machineId: this.deps.machineId,
|
|
84035
|
+
chatroomId: opts.chatroomId,
|
|
84036
|
+
role: opts.role,
|
|
84037
|
+
...slot?.harnessSessionId ? { harnessSessionId: slot.harnessSessionId } : {}
|
|
84038
|
+
});
|
|
84039
|
+
console.log(`[AgentProcessManager] ✅ Emitted agent.sessionResumed for ${opts.role}`);
|
|
84040
|
+
} catch (err) {
|
|
84041
|
+
console.log(` ⚠️ Failed to emit sessionResumed event: ${err.message}`);
|
|
84042
|
+
}
|
|
83653
84043
|
return;
|
|
83654
84044
|
} catch (err) {
|
|
83655
|
-
|
|
84045
|
+
const reason = err.message;
|
|
84046
|
+
try {
|
|
84047
|
+
await this.deps.backend.mutation(api.machines.emitSessionResumeFailed, {
|
|
84048
|
+
sessionId: this.deps.sessionId,
|
|
84049
|
+
machineId: this.deps.machineId,
|
|
84050
|
+
chatroomId: opts.chatroomId,
|
|
84051
|
+
role: opts.role,
|
|
84052
|
+
reason,
|
|
84053
|
+
...slot?.harnessSessionId ? { harnessSessionId: slot.harnessSessionId } : {}
|
|
84054
|
+
});
|
|
84055
|
+
console.log(`[AgentProcessManager] ✅ Emitted agent.sessionResumeFailed for ${opts.role}`);
|
|
84056
|
+
} catch (emitErr) {
|
|
84057
|
+
console.log(` ⚠️ Failed to emit sessionResumeFailed event: ${emitErr.message}`);
|
|
84058
|
+
}
|
|
84059
|
+
console.log(`[AgentProcessManager] ⚠️ resumeTurn failed for ${opts.role} (pid ${opts.pid}): ${reason} — falling back to kill`);
|
|
84060
|
+
} finally {
|
|
84061
|
+
if (slot) {
|
|
84062
|
+
slot.resumeInFlight = false;
|
|
84063
|
+
}
|
|
83656
84064
|
}
|
|
83657
84065
|
}
|
|
83658
84066
|
}
|
|
@@ -83789,7 +84197,7 @@ class AgentProcessManager {
|
|
|
83789
84197
|
getOrCreateSlot(key) {
|
|
83790
84198
|
let slot = this.slots.get(key);
|
|
83791
84199
|
if (!slot) {
|
|
83792
|
-
slot = { state: "idle"
|
|
84200
|
+
slot = { state: "idle" };
|
|
83793
84201
|
this.slots.set(key, slot);
|
|
83794
84202
|
}
|
|
83795
84203
|
return slot;
|
|
@@ -83863,7 +84271,15 @@ class AgentProcessManager {
|
|
|
83863
84271
|
async executeEnsureRunning(key, slot, opts) {
|
|
83864
84272
|
try {
|
|
83865
84273
|
await this.killExistingBeforeSpawn(opts.chatroomId, opts.role);
|
|
83866
|
-
|
|
84274
|
+
const result = await this.doEnsureRunning(key, slot, opts);
|
|
84275
|
+
if (!result.success && opts.reason === "platform.auto_restart_on_new_context") {
|
|
84276
|
+
console.log(`[AgentProcessManager] Context auto-restart failed (${result.error ?? "unknown"}), ` + `attempting crash recovery (rate-limited)`);
|
|
84277
|
+
return await this.doEnsureRunning(key, slot, {
|
|
84278
|
+
...opts,
|
|
84279
|
+
reason: "platform.crash_recovery"
|
|
84280
|
+
});
|
|
84281
|
+
}
|
|
84282
|
+
return result;
|
|
83867
84283
|
} finally {
|
|
83868
84284
|
if (slot.pendingOperation) {
|
|
83869
84285
|
slot.pendingOperation = undefined;
|
|
@@ -83903,10 +84319,87 @@ class AgentProcessManager {
|
|
|
83903
84319
|
this.exitRetryTimer = null;
|
|
83904
84320
|
}
|
|
83905
84321
|
}
|
|
84322
|
+
async tryDaemonMemoryResume(opts) {
|
|
84323
|
+
const capabilities = getHarnessCapabilities(opts.agentHarness);
|
|
84324
|
+
if (!capabilities.supportsSessionResume) {
|
|
84325
|
+
return null;
|
|
84326
|
+
}
|
|
84327
|
+
const stored = this.lastHarnessSessions.get(opts.key);
|
|
84328
|
+
if (!stored) {
|
|
84329
|
+
return null;
|
|
84330
|
+
}
|
|
84331
|
+
if (stored.workingDir !== opts.workingDir) {
|
|
84332
|
+
this.clearLastHarnessSession(opts.key);
|
|
84333
|
+
await this.emitSessionResumeFailed(opts.chatroomId, opts.role, "working directory changed", stored.harnessSessionId);
|
|
84334
|
+
return null;
|
|
84335
|
+
}
|
|
84336
|
+
if (stored.harness !== opts.agentHarness || !stored.agentName) {
|
|
84337
|
+
this.clearLastHarnessSession(opts.key);
|
|
84338
|
+
await this.emitSessionResumeFailed(opts.chatroomId, opts.role, stored.harness !== opts.agentHarness ? "harness changed" : "incomplete session in daemon memory", stored.harnessSessionId);
|
|
84339
|
+
return null;
|
|
84340
|
+
}
|
|
84341
|
+
if (!opts.service.resumeFromDaemonMemory) {
|
|
84342
|
+
await this.emitSessionResumeFailed(opts.chatroomId, opts.role, "daemon-memory session resume not yet supported", stored.harnessSessionId);
|
|
84343
|
+
return null;
|
|
84344
|
+
}
|
|
84345
|
+
try {
|
|
84346
|
+
const spawnResult = await opts.service.resumeFromDaemonMemory({
|
|
84347
|
+
workingDir: stored.workingDir,
|
|
84348
|
+
prompt: createSpawnPrompt(opts.initPrompt),
|
|
84349
|
+
systemPrompt: opts.systemPrompt,
|
|
84350
|
+
model: opts.model ?? stored.model,
|
|
84351
|
+
context: {
|
|
84352
|
+
machineId: this.deps.machineId,
|
|
84353
|
+
chatroomId: opts.chatroomId,
|
|
84354
|
+
role: opts.role
|
|
84355
|
+
}
|
|
84356
|
+
}, {
|
|
84357
|
+
harnessSessionId: stored.harnessSessionId,
|
|
84358
|
+
agentName: stored.agentName,
|
|
84359
|
+
workingDir: stored.workingDir,
|
|
84360
|
+
model: stored.model
|
|
84361
|
+
});
|
|
84362
|
+
await this.emitSessionResumed(opts.chatroomId, opts.role, stored.harnessSessionId);
|
|
84363
|
+
return spawnResult;
|
|
84364
|
+
} catch (err) {
|
|
84365
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
84366
|
+
await this.emitSessionResumeFailed(opts.chatroomId, opts.role, reason, stored.harnessSessionId);
|
|
84367
|
+
return null;
|
|
84368
|
+
}
|
|
84369
|
+
}
|
|
84370
|
+
async emitSessionResumed(chatroomId, role, harnessSessionId) {
|
|
84371
|
+
try {
|
|
84372
|
+
await this.deps.backend.mutation(api.machines.emitSessionResumed, {
|
|
84373
|
+
sessionId: this.deps.sessionId,
|
|
84374
|
+
machineId: this.deps.machineId,
|
|
84375
|
+
chatroomId,
|
|
84376
|
+
role,
|
|
84377
|
+
...harnessSessionId ? { harnessSessionId } : {}
|
|
84378
|
+
});
|
|
84379
|
+
console.log(`[AgentProcessManager] ✅ Emitted agent.sessionResumed for ${role}`);
|
|
84380
|
+
} catch (err) {
|
|
84381
|
+
console.log(` ⚠️ Failed to emit sessionResumed event: ${err.message}`);
|
|
84382
|
+
}
|
|
84383
|
+
}
|
|
84384
|
+
async emitSessionResumeFailed(chatroomId, role, reason, harnessSessionId) {
|
|
84385
|
+
try {
|
|
84386
|
+
await this.deps.backend.mutation(api.machines.emitSessionResumeFailed, {
|
|
84387
|
+
sessionId: this.deps.sessionId,
|
|
84388
|
+
machineId: this.deps.machineId,
|
|
84389
|
+
chatroomId,
|
|
84390
|
+
role,
|
|
84391
|
+
reason,
|
|
84392
|
+
...harnessSessionId ? { harnessSessionId } : {}
|
|
84393
|
+
});
|
|
84394
|
+
console.log(`[AgentProcessManager] ✅ Emitted agent.sessionResumeFailed for ${role}`);
|
|
84395
|
+
} catch (err) {
|
|
84396
|
+
console.log(` ⚠️ Failed to emit sessionResumeFailed event: ${err.message}`);
|
|
84397
|
+
}
|
|
84398
|
+
}
|
|
83906
84399
|
async doEnsureRunning(key, slot, opts) {
|
|
83907
84400
|
slot.state = "spawning";
|
|
83908
|
-
|
|
83909
|
-
console.log(`[AgentProcessManager] harness start: role=${opts.role} harness=${opts.agentHarness} wantResume=${
|
|
84401
|
+
const wantResume = opts.wantResume ?? true;
|
|
84402
|
+
console.log(`[AgentProcessManager] harness start: role=${opts.role} harness=${opts.agentHarness} wantResume=${wantResume} reason=${opts.reason}`);
|
|
83910
84403
|
try {
|
|
83911
84404
|
const spawnCheck = this.deps.spawning.shouldAllowSpawn(opts.chatroomId, opts.reason, {
|
|
83912
84405
|
bypassConcurrentLimit: opts.reason.startsWith("user.")
|
|
@@ -83980,22 +84473,37 @@ class AgentProcessManager {
|
|
|
83980
84473
|
return { success: false, error: `Unknown agent harness: ${opts.agentHarness}` };
|
|
83981
84474
|
}
|
|
83982
84475
|
let spawnResult;
|
|
83983
|
-
|
|
83984
|
-
spawnResult = await
|
|
84476
|
+
if (wantResume) {
|
|
84477
|
+
spawnResult = await this.tryDaemonMemoryResume({
|
|
84478
|
+
key,
|
|
84479
|
+
chatroomId: opts.chatroomId,
|
|
84480
|
+
role: opts.role,
|
|
84481
|
+
agentHarness: opts.agentHarness,
|
|
83985
84482
|
workingDir: opts.workingDir,
|
|
83986
|
-
prompt: createSpawnPrompt(initPromptResult.initialMessage),
|
|
83987
|
-
systemPrompt: initPromptResult.rolePrompt,
|
|
83988
84483
|
model: opts.model,
|
|
83989
|
-
|
|
83990
|
-
|
|
83991
|
-
|
|
83992
|
-
|
|
83993
|
-
|
|
83994
|
-
|
|
83995
|
-
|
|
83996
|
-
|
|
83997
|
-
|
|
83998
|
-
|
|
84484
|
+
initPrompt: initPromptResult.initialMessage,
|
|
84485
|
+
systemPrompt: initPromptResult.rolePrompt,
|
|
84486
|
+
service
|
|
84487
|
+
}) ?? undefined;
|
|
84488
|
+
}
|
|
84489
|
+
if (!spawnResult) {
|
|
84490
|
+
try {
|
|
84491
|
+
spawnResult = await service.spawn({
|
|
84492
|
+
workingDir: opts.workingDir,
|
|
84493
|
+
prompt: createSpawnPrompt(initPromptResult.initialMessage),
|
|
84494
|
+
systemPrompt: initPromptResult.rolePrompt,
|
|
84495
|
+
model: opts.model,
|
|
84496
|
+
context: {
|
|
84497
|
+
machineId: this.deps.machineId,
|
|
84498
|
+
chatroomId: opts.chatroomId,
|
|
84499
|
+
role: opts.role
|
|
84500
|
+
}
|
|
84501
|
+
});
|
|
84502
|
+
} catch (e) {
|
|
84503
|
+
slot.state = "idle";
|
|
84504
|
+
slot.pendingOperation = undefined;
|
|
84505
|
+
return { success: false, error: `Failed to spawn agent: ${e.message}` };
|
|
84506
|
+
}
|
|
83999
84507
|
}
|
|
84000
84508
|
const { pid } = spawnResult;
|
|
84001
84509
|
this.deps.spawning.recordSpawn(opts.chatroomId);
|
|
@@ -84003,6 +84511,15 @@ class AgentProcessManager {
|
|
|
84003
84511
|
slot.pid = pid;
|
|
84004
84512
|
slot.harness = opts.agentHarness;
|
|
84005
84513
|
slot.harnessSessionId = spawnResult.harnessSessionId;
|
|
84514
|
+
if (spawnResult.harnessSessionId) {
|
|
84515
|
+
this.recordLastHarnessSession(key, {
|
|
84516
|
+
harnessSessionId: spawnResult.harnessSessionId,
|
|
84517
|
+
harness: opts.agentHarness,
|
|
84518
|
+
agentName: spawnResult.harnessReconnect?.agentName ?? "",
|
|
84519
|
+
workingDir: opts.workingDir,
|
|
84520
|
+
model: opts.model ?? spawnResult.harnessReconnect?.model
|
|
84521
|
+
});
|
|
84522
|
+
}
|
|
84006
84523
|
slot.model = opts.model;
|
|
84007
84524
|
slot.workingDir = opts.workingDir;
|
|
84008
84525
|
slot.startedAt = this.deps.clock.now();
|
|
@@ -84014,7 +84531,8 @@ class AgentProcessManager {
|
|
|
84014
84531
|
role: opts.role,
|
|
84015
84532
|
pid,
|
|
84016
84533
|
model: opts.model,
|
|
84017
|
-
reason: opts.reason
|
|
84534
|
+
reason: opts.reason,
|
|
84535
|
+
...spawnResult.harnessSessionId ? { harnessSessionId: spawnResult.harnessSessionId } : {}
|
|
84018
84536
|
}).catch((err) => {
|
|
84019
84537
|
console.log(` ⚠️ Failed to update PID in backend: ${err.message}`);
|
|
84020
84538
|
});
|
|
@@ -84036,8 +84554,7 @@ class AgentProcessManager {
|
|
|
84036
84554
|
chatroomId: opts.chatroomId,
|
|
84037
84555
|
role: opts.role,
|
|
84038
84556
|
pid,
|
|
84039
|
-
harness: opts.agentHarness
|
|
84040
|
-
wantResume: slot.wantResume
|
|
84557
|
+
harness: opts.agentHarness
|
|
84041
84558
|
});
|
|
84042
84559
|
});
|
|
84043
84560
|
}
|
|
@@ -84060,12 +84577,38 @@ class AgentProcessManager {
|
|
|
84060
84577
|
return { success: false, error: `Unexpected error: ${e.message}` };
|
|
84061
84578
|
}
|
|
84062
84579
|
}
|
|
84580
|
+
recordLastHarnessSession(key, ctx) {
|
|
84581
|
+
this.lastHarnessSessions.set(key, ctx);
|
|
84582
|
+
}
|
|
84583
|
+
clearLastHarnessSession(key) {
|
|
84584
|
+
this.lastHarnessSessions.delete(key);
|
|
84585
|
+
}
|
|
84586
|
+
readHarnessReconnectMetadata(service, pid) {
|
|
84587
|
+
return service.getHarnessReconnectContext?.(pid);
|
|
84588
|
+
}
|
|
84063
84589
|
async doStop(key, slot, pid, opts) {
|
|
84064
84590
|
try {
|
|
84065
84591
|
const harness = slot.harness;
|
|
84066
84592
|
const service = harness ? this.deps.agentServices.get(harness) : undefined;
|
|
84593
|
+
const preserveForResume = opts.reason === "user.stop" && Boolean(slot.harnessSessionId);
|
|
84594
|
+
if (harness && slot.harnessSessionId) {
|
|
84595
|
+
if (preserveForResume) {
|
|
84596
|
+
const harnessMeta = service ? this.readHarnessReconnectMetadata(service, pid) : undefined;
|
|
84597
|
+
this.recordLastHarnessSession(key, {
|
|
84598
|
+
harnessSessionId: slot.harnessSessionId,
|
|
84599
|
+
harness,
|
|
84600
|
+
agentName: harnessMeta?.agentName ?? "",
|
|
84601
|
+
workingDir: slot.workingDir ?? "",
|
|
84602
|
+
model: slot.model ?? harnessMeta?.model
|
|
84603
|
+
});
|
|
84604
|
+
} else {
|
|
84605
|
+
this.clearLastHarnessSession(key);
|
|
84606
|
+
}
|
|
84607
|
+
} else if (!preserveForResume) {
|
|
84608
|
+
this.clearLastHarnessSession(key);
|
|
84609
|
+
}
|
|
84067
84610
|
if (service) {
|
|
84068
|
-
await service.stop(pid);
|
|
84611
|
+
await service.stop(pid, { preserveForResume });
|
|
84069
84612
|
service.untrack(pid);
|
|
84070
84613
|
} else {
|
|
84071
84614
|
try {
|
|
@@ -84137,7 +84680,7 @@ class SpawnRateLimiter {
|
|
|
84137
84680
|
this.config = { ...DEFAULT_CONFIG2, ...config3 };
|
|
84138
84681
|
}
|
|
84139
84682
|
tryConsume(chatroomId, reason) {
|
|
84140
|
-
if (reason.startsWith("user.")) {
|
|
84683
|
+
if (reason.startsWith("user.") || reason === "platform.auto_restart_on_new_context") {
|
|
84141
84684
|
return { allowed: true };
|
|
84142
84685
|
}
|
|
84143
84686
|
const bucket = this._getOrCreateBucket(chatroomId);
|
|
@@ -85418,6 +85961,7 @@ async function startCommandLoop(ctx) {
|
|
|
85418
85961
|
}).then(() => {
|
|
85419
85962
|
heartbeatCount++;
|
|
85420
85963
|
console.log(`[${formatTimestamp()}] \uD83D\uDC93 Daemon heartbeat #${heartbeatCount} OK`);
|
|
85964
|
+
invalidateWorkspacesForMachineCache(ctx);
|
|
85421
85965
|
if (!ctx.observedSyncEnabled) {
|
|
85422
85966
|
pushGitState(ctx).catch((err) => {
|
|
85423
85967
|
console.warn(`[${formatTimestamp()}] ⚠️ Git state push failed: ${getErrorMessage(err)}`);
|
|
@@ -85584,6 +86128,7 @@ var init_command_loop = __esm(() => {
|
|
|
85584
86128
|
init_manager();
|
|
85585
86129
|
init_init2();
|
|
85586
86130
|
init_log_observer_sync();
|
|
86131
|
+
init_workspace_cache();
|
|
85587
86132
|
init_observed_sync();
|
|
85588
86133
|
init_api3();
|
|
85589
86134
|
init_client2();
|
|
@@ -86763,4 +87308,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
86763
87308
|
});
|
|
86764
87309
|
program2.parse();
|
|
86765
87310
|
|
|
86766
|
-
//# debugId=
|
|
87311
|
+
//# debugId=D59205F0E7B31DAD64756E2164756E21
|