chatroom-cli 1.51.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 +919 -310
- package/dist/index.js.map +23 -22
- 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, {
|
|
@@ -27807,8 +27997,9 @@ ${options.prompt}` : options.prompt;
|
|
|
27807
27997
|
`);
|
|
27808
27998
|
break;
|
|
27809
27999
|
}
|
|
28000
|
+
const resumePromise = waitForResumeOrAbort(session);
|
|
27810
28001
|
adapter.finish();
|
|
27811
|
-
const resumePrompt = await
|
|
28002
|
+
const resumePrompt = await resumePromise;
|
|
27812
28003
|
if (resumePrompt === null || session.aborted) {
|
|
27813
28004
|
if (session.aborted) {
|
|
27814
28005
|
exitCode = 1;
|
|
@@ -27824,30 +28015,57 @@ ${options.prompt}` : options.prompt;
|
|
|
27824
28015
|
process.stderr.write(`${logPrefix} spawn-error] ${reason}
|
|
27825
28016
|
`);
|
|
27826
28017
|
} finally {
|
|
27827
|
-
if (!session.agentClosed) {
|
|
28018
|
+
if (!session.agentClosed && !session.preserveForResume) {
|
|
27828
28019
|
try {
|
|
27829
28020
|
agent.close();
|
|
27830
28021
|
session.agentClosed = true;
|
|
27831
28022
|
} catch {}
|
|
27832
28023
|
}
|
|
27833
28024
|
try {
|
|
27834
|
-
keeper.kill();
|
|
28025
|
+
session.keeper.kill();
|
|
27835
28026
|
} catch {}
|
|
27836
28027
|
finishExit(exitCode, exitSignal);
|
|
27837
28028
|
}
|
|
27838
28029
|
})();
|
|
27839
|
-
|
|
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({
|
|
27840
28059
|
pid,
|
|
27841
|
-
|
|
27842
|
-
|
|
27843
|
-
|
|
27844
|
-
|
|
27845
|
-
|
|
27846
|
-
|
|
27847
|
-
|
|
27848
|
-
|
|
27849
|
-
|
|
27850
|
-
};
|
|
28060
|
+
keeper,
|
|
28061
|
+
agent,
|
|
28062
|
+
context: context4,
|
|
28063
|
+
agentName,
|
|
28064
|
+
model: options.model,
|
|
28065
|
+
workingDir: options.workingDir,
|
|
28066
|
+
initialPrompt: fullPrompt,
|
|
28067
|
+
forceFirstTurn: true
|
|
28068
|
+
});
|
|
27851
28069
|
}
|
|
27852
28070
|
};
|
|
27853
28071
|
});
|
|
@@ -30038,12 +30256,12 @@ function startSessionEventForwarder(client4, options) {
|
|
|
30038
30256
|
// src/infrastructure/services/remote-agents/opencode-sdk/session-metadata-store.ts
|
|
30039
30257
|
import { existsSync, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "node:fs";
|
|
30040
30258
|
import { homedir as homedir2 } from "node:os";
|
|
30041
|
-
import { dirname as dirname2, join as
|
|
30259
|
+
import { dirname as dirname2, join as join8 } from "node:path";
|
|
30042
30260
|
|
|
30043
30261
|
class FileSessionMetadataStore {
|
|
30044
30262
|
filePath;
|
|
30045
30263
|
constructor(filePath) {
|
|
30046
|
-
this.filePath = filePath ??
|
|
30264
|
+
this.filePath = filePath ?? join8(homedir2(), ".chatroom", "opencode-sdk-sessions.json");
|
|
30047
30265
|
}
|
|
30048
30266
|
load() {
|
|
30049
30267
|
try {
|
|
@@ -30097,7 +30315,7 @@ async function withTimeout2(p, ms, label) {
|
|
|
30097
30315
|
clearTimeout(timer);
|
|
30098
30316
|
}
|
|
30099
30317
|
}
|
|
30100
|
-
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;
|
|
30101
30319
|
var init_opencode_sdk_agent_service = __esm(() => {
|
|
30102
30320
|
init_dist();
|
|
30103
30321
|
init_base_cli_agent_service();
|
|
@@ -30126,28 +30344,30 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30126
30344
|
return output.split(`
|
|
30127
30345
|
`).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
30128
30346
|
}
|
|
30129
|
-
async stop(pid) {
|
|
30347
|
+
async stop(pid, options) {
|
|
30130
30348
|
const forwarder = this.forwarders.get(pid);
|
|
30131
30349
|
if (forwarder) {
|
|
30132
30350
|
forwarder.stop();
|
|
30133
30351
|
this.forwarders.delete(pid);
|
|
30134
30352
|
}
|
|
30353
|
+
const preserveForResume = options?.preserveForResume === true;
|
|
30135
30354
|
const meta = this.sessionStore.findByPid(pid);
|
|
30136
30355
|
if (meta) {
|
|
30137
|
-
|
|
30138
|
-
|
|
30139
|
-
|
|
30140
|
-
|
|
30141
|
-
|
|
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
|
+
}
|
|
30142
30363
|
}
|
|
30143
30364
|
this.sessionStore.remove(meta.sessionId);
|
|
30144
30365
|
}
|
|
30145
30366
|
await super.stop(pid);
|
|
30146
30367
|
}
|
|
30147
|
-
|
|
30148
|
-
const { prompt, systemPrompt, model, context: context4 } = options;
|
|
30368
|
+
spawnServeProcess(workingDir) {
|
|
30149
30369
|
const childProcess = this.deps.spawn(OPENCODE_COMMAND2, ["serve", "--print-logs"], {
|
|
30150
|
-
cwd:
|
|
30370
|
+
cwd: workingDir,
|
|
30151
30371
|
stdio: ["pipe", "pipe", "pipe"],
|
|
30152
30372
|
shell: false,
|
|
30153
30373
|
detached: true,
|
|
@@ -30160,6 +30380,146 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30160
30380
|
if (!childProcess.pid) {
|
|
30161
30381
|
throw new Error("Failed to spawn opencode serve process");
|
|
30162
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);
|
|
30163
30523
|
const pid = childProcess.pid;
|
|
30164
30524
|
const baseUrl = await waitForListeningUrl(childProcess, {
|
|
30165
30525
|
timeoutMs: SERVE_STARTUP_TIMEOUT_MS
|
|
@@ -30213,59 +30573,26 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
30213
30573
|
this.sessionStore.remove(sessionId);
|
|
30214
30574
|
throw err;
|
|
30215
30575
|
}
|
|
30216
|
-
|
|
30576
|
+
return this.registerRunningSession({
|
|
30577
|
+
childProcess,
|
|
30578
|
+
pid,
|
|
30217
30579
|
sessionId,
|
|
30218
|
-
|
|
30219
|
-
|
|
30220
|
-
|
|
30580
|
+
context: context4,
|
|
30581
|
+
forwarder,
|
|
30582
|
+
baseUrl,
|
|
30221
30583
|
agentName,
|
|
30222
|
-
|
|
30223
|
-
|
|
30224
|
-
|
|
30225
|
-
|
|
30226
|
-
|
|
30227
|
-
this.sessionStore.
|
|
30228
|
-
|
|
30229
|
-
|
|
30230
|
-
this.forwarders.set(pid, forwarder);
|
|
30231
|
-
const outputCallbacks = [];
|
|
30232
|
-
forwardFiltered(childProcess.stdout, process.stdout, isInfoLine);
|
|
30233
|
-
forwardFiltered(childProcess.stderr, process.stderr, isInfoLine);
|
|
30234
|
-
if (childProcess.stdout) {
|
|
30235
|
-
childProcess.stdout.on("data", () => {
|
|
30236
|
-
entry.lastOutputAt = Date.now();
|
|
30237
|
-
for (const cb of outputCallbacks)
|
|
30238
|
-
cb();
|
|
30239
|
-
});
|
|
30240
|
-
}
|
|
30241
|
-
if (childProcess.stderr) {
|
|
30242
|
-
childProcess.stderr.on("data", () => {
|
|
30243
|
-
entry.lastOutputAt = Date.now();
|
|
30244
|
-
for (const cb of outputCallbacks)
|
|
30245
|
-
cb();
|
|
30246
|
-
});
|
|
30584
|
+
model,
|
|
30585
|
+
workingDir: options.workingDir
|
|
30586
|
+
});
|
|
30587
|
+
}
|
|
30588
|
+
getHarnessReconnectContext(pid) {
|
|
30589
|
+
const meta = this.sessionStore.findByPid(pid);
|
|
30590
|
+
if (!meta) {
|
|
30591
|
+
return;
|
|
30247
30592
|
}
|
|
30248
30593
|
return {
|
|
30249
|
-
|
|
30250
|
-
|
|
30251
|
-
onExit: (cb) => {
|
|
30252
|
-
childProcess.on("exit", (code2, signal) => {
|
|
30253
|
-
const fwd = this.forwarders.get(pid);
|
|
30254
|
-
if (fwd) {
|
|
30255
|
-
fwd.stop();
|
|
30256
|
-
this.forwarders.delete(pid);
|
|
30257
|
-
}
|
|
30258
|
-
this.sessionStore.remove(sessionId);
|
|
30259
|
-
this.deleteProcess(pid);
|
|
30260
|
-
cb({ code: code2, signal, context: context4 });
|
|
30261
|
-
});
|
|
30262
|
-
},
|
|
30263
|
-
onOutput: (cb) => {
|
|
30264
|
-
outputCallbacks.push(cb);
|
|
30265
|
-
},
|
|
30266
|
-
onAgentEnd: (cb) => {
|
|
30267
|
-
forwarder?.onAgentEnd(cb);
|
|
30268
|
-
}
|
|
30594
|
+
agentName: meta.agentName,
|
|
30595
|
+
...meta.model ? { model: meta.model } : {}
|
|
30269
30596
|
};
|
|
30270
30597
|
}
|
|
30271
30598
|
async resumeTurn(pid, prompt) {
|
|
@@ -30400,16 +30727,16 @@ var MACHINE_CONFIG_VERSION = "1";
|
|
|
30400
30727
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
30401
30728
|
import * as fs2 from "node:fs/promises";
|
|
30402
30729
|
import { homedir as homedir3, hostname as hostname2 } from "node:os";
|
|
30403
|
-
import { join as
|
|
30730
|
+
import { join as join9 } from "node:path";
|
|
30404
30731
|
function chatroomConfigDir() {
|
|
30405
|
-
return
|
|
30732
|
+
return join9(homedir3(), ".chatroom");
|
|
30406
30733
|
}
|
|
30407
30734
|
async function ensureConfigDir2() {
|
|
30408
30735
|
const dir = chatroomConfigDir();
|
|
30409
30736
|
await fs2.mkdir(dir, { recursive: true, mode: 448 });
|
|
30410
30737
|
}
|
|
30411
30738
|
function getMachineConfigPath() {
|
|
30412
|
-
return
|
|
30739
|
+
return join9(chatroomConfigDir(), MACHINE_FILE);
|
|
30413
30740
|
}
|
|
30414
30741
|
async function loadConfigFile() {
|
|
30415
30742
|
const configPath = getMachineConfigPath();
|
|
@@ -30501,7 +30828,7 @@ var init_storage2 = __esm(() => {
|
|
|
30501
30828
|
// src/infrastructure/machine/daemon-state.ts
|
|
30502
30829
|
import * as fs3 from "node:fs/promises";
|
|
30503
30830
|
import { homedir as homedir4 } from "node:os";
|
|
30504
|
-
import { join as
|
|
30831
|
+
import { join as join10 } from "node:path";
|
|
30505
30832
|
function agentKey(chatroomId, role) {
|
|
30506
30833
|
return `${chatroomId}/${role}`;
|
|
30507
30834
|
}
|
|
@@ -30509,7 +30836,7 @@ async function ensureStateDir() {
|
|
|
30509
30836
|
await fs3.mkdir(STATE_DIR, { recursive: true, mode: 448 });
|
|
30510
30837
|
}
|
|
30511
30838
|
function stateFilePath(machineId) {
|
|
30512
|
-
return
|
|
30839
|
+
return join10(STATE_DIR, `${machineId}.json`);
|
|
30513
30840
|
}
|
|
30514
30841
|
async function loadDaemonState(machineId) {
|
|
30515
30842
|
const filePath = stateFilePath(machineId);
|
|
@@ -30591,8 +30918,8 @@ async function loadEventCursor(machineId) {
|
|
|
30591
30918
|
}
|
|
30592
30919
|
var CHATROOM_DIR2, STATE_DIR, STATE_VERSION = "1";
|
|
30593
30920
|
var init_daemon_state = __esm(() => {
|
|
30594
|
-
CHATROOM_DIR2 =
|
|
30595
|
-
STATE_DIR =
|
|
30921
|
+
CHATROOM_DIR2 = join10(homedir4(), ".chatroom");
|
|
30922
|
+
STATE_DIR = join10(CHATROOM_DIR2, "machines", "state");
|
|
30596
30923
|
});
|
|
30597
30924
|
|
|
30598
30925
|
// src/infrastructure/machine/index.ts
|
|
@@ -30965,9 +31292,9 @@ var init_init = __esm(() => {
|
|
|
30965
31292
|
});
|
|
30966
31293
|
|
|
30967
31294
|
// src/tools/output.ts
|
|
30968
|
-
import { join as
|
|
31295
|
+
import { join as join11 } from "node:path";
|
|
30969
31296
|
function resolveChatroomDir(workingDir) {
|
|
30970
|
-
return
|
|
31297
|
+
return join11(workingDir, CHATROOM_DIR3);
|
|
30971
31298
|
}
|
|
30972
31299
|
async function ensureChatroomDir(deps, workingDir) {
|
|
30973
31300
|
const dir = resolveChatroomDir(workingDir);
|
|
@@ -30975,7 +31302,7 @@ async function ensureChatroomDir(deps, workingDir) {
|
|
|
30975
31302
|
return dir;
|
|
30976
31303
|
}
|
|
30977
31304
|
async function ensureGitignore(deps, workingDir) {
|
|
30978
|
-
const gitignorePath =
|
|
31305
|
+
const gitignorePath = join11(workingDir, ".gitignore");
|
|
30979
31306
|
const entry = CHATROOM_DIR3;
|
|
30980
31307
|
let content = "";
|
|
30981
31308
|
try {
|
|
@@ -31005,7 +31332,7 @@ function formatOutputTimestamp(date = new Date) {
|
|
|
31005
31332
|
function generateOutputPath(workingDir, toolName, extension, date) {
|
|
31006
31333
|
const timestamp = formatOutputTimestamp(date);
|
|
31007
31334
|
const filename = `${toolName}-${timestamp}.${extension}`;
|
|
31008
|
-
return
|
|
31335
|
+
return join11(resolveChatroomDir(workingDir), filename);
|
|
31009
31336
|
}
|
|
31010
31337
|
var CHATROOM_DIR3 = ".chatroom";
|
|
31011
31338
|
var init_output = () => {};
|
|
@@ -79408,20 +79735,20 @@ function formatTimestamp() {
|
|
|
79408
79735
|
|
|
79409
79736
|
// src/infrastructure/services/workspace/workspace-resolver.ts
|
|
79410
79737
|
import { readFile as readFile5, readdir, stat } from "node:fs/promises";
|
|
79411
|
-
import { join as
|
|
79738
|
+
import { join as join13, basename, resolve as resolve3 } from "node:path";
|
|
79412
79739
|
async function resolveGlobPattern(rootDir, pattern) {
|
|
79413
79740
|
const cleaned = pattern.replace(/\/+$/, "");
|
|
79414
79741
|
if (cleaned.includes("..")) {
|
|
79415
79742
|
return [];
|
|
79416
79743
|
}
|
|
79417
79744
|
if (cleaned.endsWith("/*")) {
|
|
79418
|
-
const parentDir =
|
|
79745
|
+
const parentDir = join13(rootDir, cleaned.slice(0, -2));
|
|
79419
79746
|
try {
|
|
79420
79747
|
const entries2 = await readdir(parentDir, { withFileTypes: true });
|
|
79421
79748
|
const dirs = [];
|
|
79422
79749
|
for (const entry of entries2) {
|
|
79423
79750
|
if (entry.isDirectory()) {
|
|
79424
|
-
const dirPath =
|
|
79751
|
+
const dirPath = join13(parentDir, entry.name);
|
|
79425
79752
|
if (resolve3(dirPath).startsWith(resolve3(rootDir))) {
|
|
79426
79753
|
dirs.push(dirPath);
|
|
79427
79754
|
}
|
|
@@ -79432,7 +79759,7 @@ async function resolveGlobPattern(rootDir, pattern) {
|
|
|
79432
79759
|
return [];
|
|
79433
79760
|
}
|
|
79434
79761
|
} else {
|
|
79435
|
-
const dir =
|
|
79762
|
+
const dir = join13(rootDir, cleaned);
|
|
79436
79763
|
try {
|
|
79437
79764
|
if (!resolve3(dir).startsWith(resolve3(rootDir)))
|
|
79438
79765
|
return [];
|
|
@@ -79445,7 +79772,7 @@ async function resolveGlobPattern(rootDir, pattern) {
|
|
|
79445
79772
|
}
|
|
79446
79773
|
async function readPackageJson(dir) {
|
|
79447
79774
|
try {
|
|
79448
|
-
const content = await readFile5(
|
|
79775
|
+
const content = await readFile5(join13(dir, "package.json"), "utf-8");
|
|
79449
79776
|
const pkg = JSON.parse(content);
|
|
79450
79777
|
return {
|
|
79451
79778
|
name: pkg.name || basename(dir),
|
|
@@ -79457,7 +79784,7 @@ async function readPackageJson(dir) {
|
|
|
79457
79784
|
}
|
|
79458
79785
|
async function readPnpmWorkspacePatterns(rootDir) {
|
|
79459
79786
|
try {
|
|
79460
|
-
const content = await readFile5(
|
|
79787
|
+
const content = await readFile5(join13(rootDir, "pnpm-workspace.yaml"), "utf-8");
|
|
79461
79788
|
const patterns = [];
|
|
79462
79789
|
let inPackages = false;
|
|
79463
79790
|
for (const line of content.split(`
|
|
@@ -79484,7 +79811,7 @@ async function readPnpmWorkspacePatterns(rootDir) {
|
|
|
79484
79811
|
}
|
|
79485
79812
|
async function readPackageJsonWorkspacePatterns(rootDir) {
|
|
79486
79813
|
try {
|
|
79487
|
-
const content = await readFile5(
|
|
79814
|
+
const content = await readFile5(join13(rootDir, "package.json"), "utf-8");
|
|
79488
79815
|
const pkg = JSON.parse(content);
|
|
79489
79816
|
if (!pkg.workspaces)
|
|
79490
79817
|
return [];
|
|
@@ -79533,11 +79860,11 @@ var init_workspace_resolver = () => {};
|
|
|
79533
79860
|
|
|
79534
79861
|
// src/infrastructure/services/workspace/command-discovery.ts
|
|
79535
79862
|
import { access as access2, readFile as readFile6 } from "node:fs/promises";
|
|
79536
|
-
import { join as
|
|
79863
|
+
import { join as join14, relative, basename as basename2 } from "node:path";
|
|
79537
79864
|
async function detectPackageManager(workingDir) {
|
|
79538
79865
|
for (const { file, manager } of LOCKFILE_MAP) {
|
|
79539
79866
|
try {
|
|
79540
|
-
await access2(
|
|
79867
|
+
await access2(join14(workingDir, file));
|
|
79541
79868
|
return manager;
|
|
79542
79869
|
} catch {}
|
|
79543
79870
|
}
|
|
@@ -79584,7 +79911,7 @@ async function discoverCommands(workingDir) {
|
|
|
79584
79911
|
const turboTaskNames = [];
|
|
79585
79912
|
let rootPackageName = basename2(workingDir);
|
|
79586
79913
|
try {
|
|
79587
|
-
const pkgPath =
|
|
79914
|
+
const pkgPath = join14(workingDir, "package.json");
|
|
79588
79915
|
const pkgContent = await readFile6(pkgPath, "utf-8");
|
|
79589
79916
|
const pkg = JSON.parse(pkgContent);
|
|
79590
79917
|
if (pkg.name)
|
|
@@ -79605,7 +79932,7 @@ async function discoverCommands(workingDir) {
|
|
|
79605
79932
|
} catch {}
|
|
79606
79933
|
const rootSubWorkspace = { type: "npm", path: ".", name: rootPackageName };
|
|
79607
79934
|
try {
|
|
79608
|
-
const turboPath =
|
|
79935
|
+
const turboPath = join14(workingDir, "turbo.json");
|
|
79609
79936
|
const turboContent = await readFile6(turboPath, "utf-8");
|
|
79610
79937
|
const turbo = JSON.parse(turboContent);
|
|
79611
79938
|
if (turbo.tasks && typeof turbo.tasks === "object") {
|
|
@@ -79659,19 +79986,43 @@ var init_command_discovery = __esm(() => {
|
|
|
79659
79986
|
];
|
|
79660
79987
|
});
|
|
79661
79988
|
|
|
79662
|
-
// src/commands/machine/daemon-start/
|
|
79663
|
-
|
|
79664
|
-
|
|
79665
|
-
|
|
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
|
+
}
|
|
79666
80000
|
try {
|
|
79667
|
-
workspaces = await ctx.deps.backend.query(api.workspaces.listWorkspacesForMachine, {
|
|
80001
|
+
const workspaces = await ctx.deps.backend.query(api.workspaces.listWorkspacesForMachine, {
|
|
79668
80002
|
sessionId: ctx.sessionId,
|
|
79669
80003
|
machineId: ctx.machineId
|
|
79670
80004
|
});
|
|
80005
|
+
extended._workspacesCache = { fetchedAt: now, workspaces };
|
|
80006
|
+
return workspaces;
|
|
79671
80007
|
} catch (err) {
|
|
79672
|
-
console.warn(`[${formatTimestamp()}] ⚠️ Failed to query workspaces
|
|
79673
|
-
return;
|
|
80008
|
+
console.warn(`[${formatTimestamp()}] ⚠️ Failed to query workspaces: ${getErrorMessage(err)}`);
|
|
80009
|
+
return [];
|
|
79674
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;
|
|
79675
80026
|
const uniqueWorkingDirs = new Set(workspaces.map((ws) => ws.workingDir));
|
|
79676
80027
|
if (uniqueWorkingDirs.size === 0)
|
|
79677
80028
|
return;
|
|
@@ -79703,6 +80054,7 @@ var init_command_sync_heartbeat = __esm(() => {
|
|
|
79703
80054
|
init_api3();
|
|
79704
80055
|
init_command_discovery();
|
|
79705
80056
|
init_convex_error();
|
|
80057
|
+
init_workspace_cache();
|
|
79706
80058
|
});
|
|
79707
80059
|
|
|
79708
80060
|
// src/events/daemon/agent/on-request-start-agent.ts
|
|
@@ -79719,7 +80071,8 @@ async function onRequestStartAgent(ctx, event) {
|
|
|
79719
80071
|
agentHarness: event.agentHarness,
|
|
79720
80072
|
model: event.model,
|
|
79721
80073
|
workingDir: event.workingDir,
|
|
79722
|
-
reason: event.reason
|
|
80074
|
+
reason: event.reason,
|
|
80075
|
+
wantResume: event.wantResume ?? true
|
|
79723
80076
|
});
|
|
79724
80077
|
if (!result.success) {
|
|
79725
80078
|
console.log(`[daemon] Agent start rejected for role=${event.role}: ${result.error ?? "unknown"}`);
|
|
@@ -79786,7 +80139,7 @@ var init_on_request_stop_agent = () => {};
|
|
|
79786
80139
|
import { createHash as createHash3 } from "node:crypto";
|
|
79787
80140
|
import { existsSync as existsSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3, unlinkSync, mkdirSync as mkdirSync4 } from "node:fs";
|
|
79788
80141
|
import { homedir as homedir5 } from "node:os";
|
|
79789
|
-
import { join as
|
|
80142
|
+
import { join as join15 } from "node:path";
|
|
79790
80143
|
function getUrlHash() {
|
|
79791
80144
|
const url2 = getConvexUrl();
|
|
79792
80145
|
return createHash3("sha256").update(url2).digest("hex").substring(0, 8);
|
|
@@ -79800,7 +80153,7 @@ function ensureChatroomDir2() {
|
|
|
79800
80153
|
}
|
|
79801
80154
|
}
|
|
79802
80155
|
function getPidFilePath() {
|
|
79803
|
-
return
|
|
80156
|
+
return join15(CHATROOM_DIR4, getPidFileName());
|
|
79804
80157
|
}
|
|
79805
80158
|
function isProcessRunning(pid) {
|
|
79806
80159
|
try {
|
|
@@ -79865,7 +80218,7 @@ function releaseLock() {
|
|
|
79865
80218
|
var CHATROOM_DIR4;
|
|
79866
80219
|
var init_pid = __esm(() => {
|
|
79867
80220
|
init_client2();
|
|
79868
|
-
CHATROOM_DIR4 =
|
|
80221
|
+
CHATROOM_DIR4 = join15(homedir5(), ".chatroom");
|
|
79869
80222
|
});
|
|
79870
80223
|
|
|
79871
80224
|
// src/infrastructure/git/types.ts
|
|
@@ -80251,6 +80604,13 @@ async function getCommitsAhead(workingDir) {
|
|
|
80251
80604
|
const count3 = parseInt(result.stdout.trim(), 10);
|
|
80252
80605
|
return Number.isNaN(count3) ? 0 : count3;
|
|
80253
80606
|
}
|
|
80607
|
+
async function getCommitsBehind(workingDir) {
|
|
80608
|
+
const result = await runGit("rev-list --count HEAD..@{upstream}", workingDir);
|
|
80609
|
+
if ("error" in result)
|
|
80610
|
+
return 0;
|
|
80611
|
+
const count3 = parseInt(result.stdout.trim(), 10);
|
|
80612
|
+
return Number.isNaN(count3) ? 0 : count3;
|
|
80613
|
+
}
|
|
80254
80614
|
async function getCommitStatusChecks(cwd, ref) {
|
|
80255
80615
|
const repoSlug = await getOriginRepoSlug(cwd);
|
|
80256
80616
|
if (!repoSlug)
|
|
@@ -80387,16 +80747,9 @@ function makeBranchDependentFields(branch) {
|
|
|
80387
80747
|
];
|
|
80388
80748
|
}
|
|
80389
80749
|
async function pushGitState(ctx) {
|
|
80390
|
-
|
|
80391
|
-
|
|
80392
|
-
workspaces = await ctx.deps.backend.query(api.workspaces.listWorkspacesForMachine, {
|
|
80393
|
-
sessionId: ctx.sessionId,
|
|
80394
|
-
machineId: ctx.machineId
|
|
80395
|
-
});
|
|
80396
|
-
} catch (err) {
|
|
80397
|
-
console.warn(`[${formatTimestamp()}] ⚠️ Failed to query workspaces for git sync: ${getErrorMessage(err)}`);
|
|
80750
|
+
const workspaces = await getWorkspacesForMachine(ctx);
|
|
80751
|
+
if (workspaces.length === 0)
|
|
80398
80752
|
return;
|
|
80399
|
-
}
|
|
80400
80753
|
const uniqueWorkingDirs = new Set(workspaces.map((ws) => ws.workingDir));
|
|
80401
80754
|
if (uniqueWorkingDirs.size === 0)
|
|
80402
80755
|
return;
|
|
@@ -80547,6 +80900,7 @@ var lastFullPushMs, branchField, GIT_STATE_FIELDS;
|
|
|
80547
80900
|
var init_git_heartbeat = __esm(() => {
|
|
80548
80901
|
init_reliability();
|
|
80549
80902
|
init_api3();
|
|
80903
|
+
init_workspace_cache();
|
|
80550
80904
|
init_git_reader();
|
|
80551
80905
|
init_git_state_pipeline();
|
|
80552
80906
|
init_convex_error();
|
|
@@ -80600,6 +80954,14 @@ var init_git_heartbeat = __esm(() => {
|
|
|
80600
80954
|
toMutationPartial: (raw) => ({ commitsAhead: raw }),
|
|
80601
80955
|
defaultValue: 0
|
|
80602
80956
|
},
|
|
80957
|
+
{
|
|
80958
|
+
key: "commitsBehind",
|
|
80959
|
+
includeInSlim: false,
|
|
80960
|
+
collect: (wd) => getCommitsBehind(wd),
|
|
80961
|
+
toHashable: (raw) => raw,
|
|
80962
|
+
toMutationPartial: (raw) => ({ commitsBehind: raw }),
|
|
80963
|
+
defaultValue: 0
|
|
80964
|
+
},
|
|
80603
80965
|
{
|
|
80604
80966
|
key: "remotes",
|
|
80605
80967
|
includeInSlim: false,
|
|
@@ -80940,16 +81302,9 @@ var init_git_subscription = __esm(() => {
|
|
|
80940
81302
|
|
|
80941
81303
|
// src/commands/machine/daemon-start/commit-detail-sync.ts
|
|
80942
81304
|
async function syncCommitDetails(ctx, seenShasMap) {
|
|
80943
|
-
|
|
80944
|
-
|
|
80945
|
-
workspaces = await ctx.deps.backend.query(api.workspaces.listWorkspacesForMachine, {
|
|
80946
|
-
sessionId: ctx.sessionId,
|
|
80947
|
-
machineId: ctx.machineId
|
|
80948
|
-
});
|
|
80949
|
-
} catch (err) {
|
|
80950
|
-
console.warn(`[${formatTimestamp()}] ⚠️ Failed to query workspaces for commit-detail sync: ${getErrorMessage(err)}`);
|
|
81305
|
+
const workspaces = await getWorkspacesForMachine(ctx);
|
|
81306
|
+
if (workspaces.length === 0)
|
|
80951
81307
|
return;
|
|
80952
|
-
}
|
|
80953
81308
|
const uniqueWorkingDirs = new Set(workspaces.map((ws) => ws.workingDir));
|
|
80954
81309
|
if (uniqueWorkingDirs.size === 0)
|
|
80955
81310
|
return;
|
|
@@ -81054,6 +81409,7 @@ var init_commit_detail_sync = __esm(() => {
|
|
|
81054
81409
|
init_api3();
|
|
81055
81410
|
init_git_reader();
|
|
81056
81411
|
init_convex_error();
|
|
81412
|
+
init_workspace_cache();
|
|
81057
81413
|
seenShas = new Map;
|
|
81058
81414
|
});
|
|
81059
81415
|
|
|
@@ -82480,17 +82836,17 @@ import {
|
|
|
82480
82836
|
writeFileSync as writeFileSync4
|
|
82481
82837
|
} from "node:fs";
|
|
82482
82838
|
import { homedir as homedir6 } from "node:os";
|
|
82483
|
-
import { join as
|
|
82839
|
+
import { join as join17 } from "node:path";
|
|
82484
82840
|
function getUrlHash2() {
|
|
82485
82841
|
const url2 = getConvexUrl();
|
|
82486
82842
|
return createHash6("sha256").update(url2).digest("hex").substring(0, 8);
|
|
82487
82843
|
}
|
|
82488
82844
|
function getChildPidsFilePath() {
|
|
82489
|
-
const dir =
|
|
82490
|
-
return
|
|
82845
|
+
const dir = join17(homedir6(), ".chatroom");
|
|
82846
|
+
return join17(dir, `daemon-children-${getUrlHash2()}.pids`);
|
|
82491
82847
|
}
|
|
82492
82848
|
function ensureChatroomDir3() {
|
|
82493
|
-
const dir =
|
|
82849
|
+
const dir = join17(homedir6(), ".chatroom");
|
|
82494
82850
|
if (!existsSync3(dir)) {
|
|
82495
82851
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
82496
82852
|
}
|
|
@@ -82584,7 +82940,7 @@ async function reapOrphanedProcessGroups() {
|
|
|
82584
82940
|
var CHATROOM_DIR5;
|
|
82585
82941
|
var init_orphan_tracker = __esm(() => {
|
|
82586
82942
|
init_client2();
|
|
82587
|
-
CHATROOM_DIR5 =
|
|
82943
|
+
CHATROOM_DIR5 = join17(homedir6(), ".chatroom");
|
|
82588
82944
|
});
|
|
82589
82945
|
|
|
82590
82946
|
// src/commands/machine/daemon-start/handlers/process/state.ts
|
|
@@ -82722,7 +83078,7 @@ var init_killer = __esm(() => {
|
|
|
82722
83078
|
// src/commands/machine/daemon-start/handlers/process/output-store.ts
|
|
82723
83079
|
import { appendFile as appendFile2, mkdir as mkdir6, readFile as readFile8, rm } from "node:fs/promises";
|
|
82724
83080
|
import { tmpdir } from "node:os";
|
|
82725
|
-
import { join as
|
|
83081
|
+
import { join as join18 } from "node:path";
|
|
82726
83082
|
|
|
82727
83083
|
class TempFileOutputStore {
|
|
82728
83084
|
state;
|
|
@@ -82785,7 +83141,7 @@ function createOutputStore(runId) {
|
|
|
82785
83141
|
if (!RUN_ID_RE.test(runId)) {
|
|
82786
83142
|
throw new Error(`Invalid runId: ${runId}`);
|
|
82787
83143
|
}
|
|
82788
|
-
const filePath =
|
|
83144
|
+
const filePath = join18(TEMP_DIR, `${runId}.log`);
|
|
82789
83145
|
return new TempFileOutputStore(filePath);
|
|
82790
83146
|
}
|
|
82791
83147
|
async function ensureTempDir() {
|
|
@@ -82799,7 +83155,7 @@ async function cleanOrphanTempFiles() {
|
|
|
82799
83155
|
var TAIL_WINDOW_BYTES, TEMP_DIR, RUN_ID_RE, MAX_TAIL_LINES_V2 = 50;
|
|
82800
83156
|
var init_output_store = __esm(() => {
|
|
82801
83157
|
TAIL_WINDOW_BYTES = 32 * 1024;
|
|
82802
|
-
TEMP_DIR =
|
|
83158
|
+
TEMP_DIR = join18(tmpdir(), "chatroom-cli", "runs");
|
|
82803
83159
|
RUN_ID_RE = /^[a-z0-9]+$/i;
|
|
82804
83160
|
});
|
|
82805
83161
|
|
|
@@ -82815,9 +83171,28 @@ function consumePendingFullSync(runId) {
|
|
|
82815
83171
|
}
|
|
82816
83172
|
function startLogObserverPoll(ctx) {
|
|
82817
83173
|
let stopped = false;
|
|
82818
|
-
|
|
83174
|
+
let consecutiveIdlePolls = 0;
|
|
83175
|
+
let pollInFlight = false;
|
|
83176
|
+
let timeoutHandle = null;
|
|
83177
|
+
const scheduleNext = (delayMs) => {
|
|
82819
83178
|
if (stopped)
|
|
82820
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;
|
|
82821
83196
|
try {
|
|
82822
83197
|
const runs = await ctx.deps.backend.query(api.commands.listRunsWithLogObservers, {
|
|
82823
83198
|
sessionId: ctx.sessionId,
|
|
@@ -82831,31 +83206,40 @@ function startLogObserverPoll(ctx) {
|
|
|
82831
83206
|
pendingFullSyncRunIds.add(run3._id);
|
|
82832
83207
|
}
|
|
82833
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
|
+
}
|
|
82834
83217
|
} catch (err) {
|
|
82835
83218
|
console.warn(`[${formatTimestamp()}] ⚠️ Log-observer poll failed: ${getErrorMessage(err)}`);
|
|
83219
|
+
scheduleNext(ACTIVE_POLL_INTERVAL_MS);
|
|
83220
|
+
} finally {
|
|
83221
|
+
pollInFlight = false;
|
|
82836
83222
|
}
|
|
82837
83223
|
};
|
|
82838
83224
|
poll4();
|
|
82839
|
-
const handle = setInterval(() => {
|
|
82840
|
-
poll4();
|
|
82841
|
-
}, OUTPUT_FLUSH_INTERVAL_MS);
|
|
82842
|
-
handle.unref?.();
|
|
82843
83225
|
return {
|
|
82844
83226
|
stop: () => {
|
|
82845
83227
|
stopped = true;
|
|
82846
|
-
|
|
83228
|
+
if (timeoutHandle)
|
|
83229
|
+
clearTimeout(timeoutHandle);
|
|
82847
83230
|
observedRunIds.clear();
|
|
82848
83231
|
pendingFullSyncRunIds.clear();
|
|
82849
83232
|
}
|
|
82850
83233
|
};
|
|
82851
83234
|
}
|
|
82852
|
-
var observedRunIds, pendingFullSyncRunIds;
|
|
83235
|
+
var observedRunIds, pendingFullSyncRunIds, ACTIVE_POLL_INTERVAL_MS, IDLE_POLL_INTERVAL_MS = 15000, IDLE_SKIP_AFTER_CONSECUTIVE = 3;
|
|
82853
83236
|
var init_log_observer_sync = __esm(() => {
|
|
82854
83237
|
init_api3();
|
|
82855
83238
|
init_convex_error();
|
|
82856
83239
|
init_state2();
|
|
82857
83240
|
observedRunIds = new Set;
|
|
82858
83241
|
pendingFullSyncRunIds = new Set;
|
|
83242
|
+
ACTIVE_POLL_INTERVAL_MS = OUTPUT_FLUSH_INTERVAL_MS;
|
|
82859
83243
|
});
|
|
82860
83244
|
|
|
82861
83245
|
// src/commands/machine/daemon-start/handlers/process/spawner.ts
|
|
@@ -83549,6 +83933,7 @@ function agentKey2(chatroomId, role) {
|
|
|
83549
83933
|
class AgentProcessManager {
|
|
83550
83934
|
deps;
|
|
83551
83935
|
slots = new Map;
|
|
83936
|
+
lastHarnessSessions = new Map;
|
|
83552
83937
|
exitRetryQueue = [];
|
|
83553
83938
|
exitRetryTimer = null;
|
|
83554
83939
|
constructor(deps) {
|
|
@@ -83622,11 +84007,20 @@ class AgentProcessManager {
|
|
|
83622
84007
|
return { success: true };
|
|
83623
84008
|
}
|
|
83624
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
|
+
}
|
|
83625
84016
|
const capabilities = getHarnessCapabilities(opts.harness);
|
|
83626
|
-
console.log(`[AgentProcessManager] agent_end: role=${opts.role} pid=${opts.pid} harness=${opts.harness}
|
|
83627
|
-
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) {
|
|
83628
84019
|
const service = this.deps.agentServices.get(opts.harness);
|
|
83629
84020
|
if (service?.resumeTurn) {
|
|
84021
|
+
if (slot) {
|
|
84022
|
+
slot.resumeInFlight = true;
|
|
84023
|
+
}
|
|
83630
84024
|
try {
|
|
83631
84025
|
const resumePrompt = composeResumeMessage({
|
|
83632
84026
|
chatroomId: opts.chatroomId,
|
|
@@ -83634,9 +84028,39 @@ class AgentProcessManager {
|
|
|
83634
84028
|
convexUrl: this.deps.convexUrl
|
|
83635
84029
|
});
|
|
83636
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
|
+
}
|
|
83637
84043
|
return;
|
|
83638
84044
|
} catch (err) {
|
|
83639
|
-
|
|
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
|
+
}
|
|
83640
84064
|
}
|
|
83641
84065
|
}
|
|
83642
84066
|
}
|
|
@@ -83773,7 +84197,7 @@ class AgentProcessManager {
|
|
|
83773
84197
|
getOrCreateSlot(key) {
|
|
83774
84198
|
let slot = this.slots.get(key);
|
|
83775
84199
|
if (!slot) {
|
|
83776
|
-
slot = { state: "idle"
|
|
84200
|
+
slot = { state: "idle" };
|
|
83777
84201
|
this.slots.set(key, slot);
|
|
83778
84202
|
}
|
|
83779
84203
|
return slot;
|
|
@@ -83847,7 +84271,15 @@ class AgentProcessManager {
|
|
|
83847
84271
|
async executeEnsureRunning(key, slot, opts) {
|
|
83848
84272
|
try {
|
|
83849
84273
|
await this.killExistingBeforeSpawn(opts.chatroomId, opts.role);
|
|
83850
|
-
|
|
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;
|
|
83851
84283
|
} finally {
|
|
83852
84284
|
if (slot.pendingOperation) {
|
|
83853
84285
|
slot.pendingOperation = undefined;
|
|
@@ -83887,10 +84319,87 @@ class AgentProcessManager {
|
|
|
83887
84319
|
this.exitRetryTimer = null;
|
|
83888
84320
|
}
|
|
83889
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
|
+
}
|
|
83890
84399
|
async doEnsureRunning(key, slot, opts) {
|
|
83891
84400
|
slot.state = "spawning";
|
|
83892
|
-
|
|
83893
|
-
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}`);
|
|
83894
84403
|
try {
|
|
83895
84404
|
const spawnCheck = this.deps.spawning.shouldAllowSpawn(opts.chatroomId, opts.reason, {
|
|
83896
84405
|
bypassConcurrentLimit: opts.reason.startsWith("user.")
|
|
@@ -83964,22 +84473,37 @@ class AgentProcessManager {
|
|
|
83964
84473
|
return { success: false, error: `Unknown agent harness: ${opts.agentHarness}` };
|
|
83965
84474
|
}
|
|
83966
84475
|
let spawnResult;
|
|
83967
|
-
|
|
83968
|
-
spawnResult = await
|
|
84476
|
+
if (wantResume) {
|
|
84477
|
+
spawnResult = await this.tryDaemonMemoryResume({
|
|
84478
|
+
key,
|
|
84479
|
+
chatroomId: opts.chatroomId,
|
|
84480
|
+
role: opts.role,
|
|
84481
|
+
agentHarness: opts.agentHarness,
|
|
83969
84482
|
workingDir: opts.workingDir,
|
|
83970
|
-
prompt: createSpawnPrompt(initPromptResult.initialMessage),
|
|
83971
|
-
systemPrompt: initPromptResult.rolePrompt,
|
|
83972
84483
|
model: opts.model,
|
|
83973
|
-
|
|
83974
|
-
|
|
83975
|
-
|
|
83976
|
-
|
|
83977
|
-
|
|
83978
|
-
|
|
83979
|
-
|
|
83980
|
-
|
|
83981
|
-
|
|
83982
|
-
|
|
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
|
+
}
|
|
83983
84507
|
}
|
|
83984
84508
|
const { pid } = spawnResult;
|
|
83985
84509
|
this.deps.spawning.recordSpawn(opts.chatroomId);
|
|
@@ -83987,6 +84511,15 @@ class AgentProcessManager {
|
|
|
83987
84511
|
slot.pid = pid;
|
|
83988
84512
|
slot.harness = opts.agentHarness;
|
|
83989
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
|
+
}
|
|
83990
84523
|
slot.model = opts.model;
|
|
83991
84524
|
slot.workingDir = opts.workingDir;
|
|
83992
84525
|
slot.startedAt = this.deps.clock.now();
|
|
@@ -83998,7 +84531,8 @@ class AgentProcessManager {
|
|
|
83998
84531
|
role: opts.role,
|
|
83999
84532
|
pid,
|
|
84000
84533
|
model: opts.model,
|
|
84001
|
-
reason: opts.reason
|
|
84534
|
+
reason: opts.reason,
|
|
84535
|
+
...spawnResult.harnessSessionId ? { harnessSessionId: spawnResult.harnessSessionId } : {}
|
|
84002
84536
|
}).catch((err) => {
|
|
84003
84537
|
console.log(` ⚠️ Failed to update PID in backend: ${err.message}`);
|
|
84004
84538
|
});
|
|
@@ -84020,8 +84554,7 @@ class AgentProcessManager {
|
|
|
84020
84554
|
chatroomId: opts.chatroomId,
|
|
84021
84555
|
role: opts.role,
|
|
84022
84556
|
pid,
|
|
84023
|
-
harness: opts.agentHarness
|
|
84024
|
-
wantResume: slot.wantResume
|
|
84557
|
+
harness: opts.agentHarness
|
|
84025
84558
|
});
|
|
84026
84559
|
});
|
|
84027
84560
|
}
|
|
@@ -84044,12 +84577,38 @@ class AgentProcessManager {
|
|
|
84044
84577
|
return { success: false, error: `Unexpected error: ${e.message}` };
|
|
84045
84578
|
}
|
|
84046
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
|
+
}
|
|
84047
84589
|
async doStop(key, slot, pid, opts) {
|
|
84048
84590
|
try {
|
|
84049
84591
|
const harness = slot.harness;
|
|
84050
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
|
+
}
|
|
84051
84610
|
if (service) {
|
|
84052
|
-
await service.stop(pid);
|
|
84611
|
+
await service.stop(pid, { preserveForResume });
|
|
84053
84612
|
service.untrack(pid);
|
|
84054
84613
|
} else {
|
|
84055
84614
|
try {
|
|
@@ -84121,7 +84680,7 @@ class SpawnRateLimiter {
|
|
|
84121
84680
|
this.config = { ...DEFAULT_CONFIG2, ...config3 };
|
|
84122
84681
|
}
|
|
84123
84682
|
tryConsume(chatroomId, reason) {
|
|
84124
|
-
if (reason.startsWith("user.")) {
|
|
84683
|
+
if (reason.startsWith("user.") || reason === "platform.auto_restart_on_new_context") {
|
|
84125
84684
|
return { allowed: true };
|
|
84126
84685
|
}
|
|
84127
84686
|
const bucket = this._getOrCreateBucket(chatroomId);
|
|
@@ -84750,6 +85309,37 @@ async function gitPull(workingDir) {
|
|
|
84750
85309
|
}
|
|
84751
85310
|
return { status: "available" };
|
|
84752
85311
|
}
|
|
85312
|
+
async function gitPush(workingDir) {
|
|
85313
|
+
const result = await runGit2("push", workingDir);
|
|
85314
|
+
if ("error" in result) {
|
|
85315
|
+
const message = result.error.message;
|
|
85316
|
+
if (message.includes("no upstream branch")) {
|
|
85317
|
+
return {
|
|
85318
|
+
status: "error",
|
|
85319
|
+
message: "No upstream branch configured. Set upstream with `git push -u`."
|
|
85320
|
+
};
|
|
85321
|
+
}
|
|
85322
|
+
if (message.includes("Authentication failed") || message.includes("could not read")) {
|
|
85323
|
+
return {
|
|
85324
|
+
status: "error",
|
|
85325
|
+
message: "Authentication failed. Run `gh auth status` to check your GitHub credentials."
|
|
85326
|
+
};
|
|
85327
|
+
}
|
|
85328
|
+
return classifyError2(message);
|
|
85329
|
+
}
|
|
85330
|
+
const stderr = result.stderr.trim();
|
|
85331
|
+
if (stderr && !stderr.includes("Everything up-to-date")) {
|
|
85332
|
+
return { status: "available", message: stderr };
|
|
85333
|
+
}
|
|
85334
|
+
return { status: "available" };
|
|
85335
|
+
}
|
|
85336
|
+
async function gitSync(workingDir) {
|
|
85337
|
+
const pullResult = await gitPull(workingDir);
|
|
85338
|
+
if (pullResult.status === "error") {
|
|
85339
|
+
return pullResult;
|
|
85340
|
+
}
|
|
85341
|
+
return gitPush(workingDir);
|
|
85342
|
+
}
|
|
84753
85343
|
var execAsync4;
|
|
84754
85344
|
var init_git_writer = __esm(() => {
|
|
84755
85345
|
execAsync4 = promisify5(exec5);
|
|
@@ -84855,6 +85445,20 @@ async function executeLocalAction(action, workingDir) {
|
|
|
84855
85445
|
}
|
|
84856
85446
|
return { success: true, message: result.message ?? "Pull successful" };
|
|
84857
85447
|
}
|
|
85448
|
+
case "git-push": {
|
|
85449
|
+
const result = await gitPush(workingDir);
|
|
85450
|
+
if (result.status === "error") {
|
|
85451
|
+
return { success: false, error: result.message };
|
|
85452
|
+
}
|
|
85453
|
+
return { success: true, message: result.message ?? "Push successful" };
|
|
85454
|
+
}
|
|
85455
|
+
case "git-sync": {
|
|
85456
|
+
const result = await gitSync(workingDir);
|
|
85457
|
+
if (result.status === "error") {
|
|
85458
|
+
return { success: false, error: result.message };
|
|
85459
|
+
}
|
|
85460
|
+
return { success: true, message: result.message ?? "Sync successful" };
|
|
85461
|
+
}
|
|
84858
85462
|
default: {
|
|
84859
85463
|
const _exhaustive = action;
|
|
84860
85464
|
return { success: false, error: `Unknown action: ${_exhaustive}` };
|
|
@@ -85297,6 +85901,9 @@ async function dispatchCommandEvent(ctx, event, tracker) {
|
|
|
85297
85901
|
const result = await executeLocalAction(event.action, event.workingDir);
|
|
85298
85902
|
if (!result.success) {
|
|
85299
85903
|
console.warn(`[${formatTimestamp()}] ⚠️ Local action failed: ${result.error}`);
|
|
85904
|
+
} else if (event.action === "git-pull" || event.action === "git-push" || event.action === "git-sync" || event.action === "git-discard-all") {
|
|
85905
|
+
ctx.lastPushedGitState.delete(makeGitStateKey(ctx.machineId, event.workingDir));
|
|
85906
|
+
await pushSingleWorkspaceGitState(ctx, event.workingDir);
|
|
85300
85907
|
}
|
|
85301
85908
|
tracker.localActionIds.set(eventId, Date.now());
|
|
85302
85909
|
} else if (eventType === "command.run") {
|
|
@@ -85354,6 +85961,7 @@ async function startCommandLoop(ctx) {
|
|
|
85354
85961
|
}).then(() => {
|
|
85355
85962
|
heartbeatCount++;
|
|
85356
85963
|
console.log(`[${formatTimestamp()}] \uD83D\uDC93 Daemon heartbeat #${heartbeatCount} OK`);
|
|
85964
|
+
invalidateWorkspacesForMachineCache(ctx);
|
|
85357
85965
|
if (!ctx.observedSyncEnabled) {
|
|
85358
85966
|
pushGitState(ctx).catch((err) => {
|
|
85359
85967
|
console.warn(`[${formatTimestamp()}] ⚠️ Git state push failed: ${getErrorMessage(err)}`);
|
|
@@ -85520,6 +86128,7 @@ var init_command_loop = __esm(() => {
|
|
|
85520
86128
|
init_manager();
|
|
85521
86129
|
init_init2();
|
|
85522
86130
|
init_log_observer_sync();
|
|
86131
|
+
init_workspace_cache();
|
|
85523
86132
|
init_observed_sync();
|
|
85524
86133
|
init_api3();
|
|
85525
86134
|
init_client2();
|
|
@@ -86699,4 +87308,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
86699
87308
|
});
|
|
86700
87309
|
program2.parse();
|
|
86701
87310
|
|
|
86702
|
-
//# debugId=
|
|
87311
|
+
//# debugId=D59205F0E7B31DAD64756E2164756E21
|