chatroom-cli 1.47.0 → 1.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -26260,9 +26260,25 @@ var init_pi_agent_service = __esm(() => {
26260
26260
  id = "pi";
26261
26261
  displayName = "Pi";
26262
26262
  command = PI_COMMAND;
26263
+ childProcesses = new Map;
26263
26264
  constructor(deps) {
26264
26265
  super(deps);
26265
26266
  }
26267
+ untrack(pid) {
26268
+ this.childProcesses.delete(pid);
26269
+ super.untrack(pid);
26270
+ }
26271
+ async resumeTurn(pid, prompt) {
26272
+ const child = this.childProcesses.get(pid);
26273
+ if (!child?.stdin) {
26274
+ throw new Error(`No tracked pi process or stdin for pid=${pid}`);
26275
+ }
26276
+ const message = JSON.stringify({ type: "prompt", message: prompt }) + `
26277
+ `;
26278
+ await new Promise((resolve, reject) => {
26279
+ child.stdin.write(message, (err) => err ? reject(err) : resolve());
26280
+ });
26281
+ }
26266
26282
  async isInstalled() {
26267
26283
  return this.checkInstalled(PI_COMMAND);
26268
26284
  }
@@ -26321,6 +26337,7 @@ var init_pi_agent_service = __esm(() => {
26321
26337
  }
26322
26338
  const pid = childProcess.pid;
26323
26339
  const context4 = options.context;
26340
+ this.childProcesses.set(pid, childProcess);
26324
26341
  const entry = this.registerProcess(pid, context4);
26325
26342
  const roleTag = context4.role ?? "unknown";
26326
26343
  const chatroomSuffix = context4.chatroomId ? `@${context4.chatroomId.slice(-6)}` : "";
@@ -26407,6 +26424,7 @@ var init_pi_agent_service = __esm(() => {
26407
26424
  pid,
26408
26425
  onExit: (cb) => {
26409
26426
  childProcess.on("exit", (code2, signal) => {
26427
+ this.childProcesses.delete(pid);
26410
26428
  this.deleteProcess(pid);
26411
26429
  cb({ code: code2, signal, context: context4 });
26412
26430
  });
@@ -26431,6 +26449,7 @@ var init_pi_agent_service = __esm(() => {
26431
26449
  pid,
26432
26450
  onExit: (cb) => {
26433
26451
  childProcess.on("exit", (code2, signal) => {
26452
+ this.childProcesses.delete(pid);
26434
26453
  this.deleteProcess(pid);
26435
26454
  cb({ code: code2, signal, context: context4 });
26436
26455
  });
@@ -27543,6 +27562,26 @@ async function withTimeout(p, ms, label) {
27543
27562
  clearTimeout(timer);
27544
27563
  }
27545
27564
  }
27565
+ function waitForResumeOrAbort(session) {
27566
+ if (session.aborted)
27567
+ return Promise.resolve(null);
27568
+ return Promise.race([
27569
+ new Promise((resolve) => {
27570
+ session.resumeResolve = (prompt) => {
27571
+ session.resumeResolve = undefined;
27572
+ session.abortResolve = undefined;
27573
+ resolve(prompt);
27574
+ };
27575
+ }),
27576
+ new Promise((resolve) => {
27577
+ session.abortResolve = () => {
27578
+ session.resumeResolve = undefined;
27579
+ session.abortResolve = undefined;
27580
+ resolve(null);
27581
+ };
27582
+ })
27583
+ ]);
27584
+ }
27546
27585
  function buildLogPrefix(context4) {
27547
27586
  const roleTag = context4.role ?? "unknown";
27548
27587
  const chatroomSuffix = context4.chatroomId ? `@${context4.chatroomId.slice(-6)}` : "";
@@ -27605,10 +27644,24 @@ var init_cursor_sdk_agent_service = __esm(() => {
27605
27644
  return [...CURSOR_SDK_FALLBACK_MODELS];
27606
27645
  }
27607
27646
  }
27647
+ async resumeTurn(pid, prompt) {
27648
+ const session = this.sessions.get(pid);
27649
+ if (!session) {
27650
+ throw new Error(`No cursor-sdk session for pid=${pid}`);
27651
+ }
27652
+ if (!session.resumeResolve) {
27653
+ throw new Error(`cursor-sdk session pid=${pid} not waiting for resume`);
27654
+ }
27655
+ const resolve = session.resumeResolve;
27656
+ session.resumeResolve = undefined;
27657
+ session.abortResolve = undefined;
27658
+ resolve(prompt);
27659
+ }
27608
27660
  async stop(pid) {
27609
27661
  const session = this.sessions.get(pid);
27610
27662
  if (session) {
27611
27663
  session.aborted = true;
27664
+ session.abortResolve?.();
27612
27665
  const run3 = session.run;
27613
27666
  if (run3?.supports("cancel")) {
27614
27667
  try {
@@ -27683,38 +27736,51 @@ ${options.prompt}` : options.prompt;
27683
27736
  (async () => {
27684
27737
  let exitCode = 0;
27685
27738
  let exitSignal = null;
27739
+ let nextPrompt = fullPrompt;
27740
+ let isFirstTurn = true;
27686
27741
  try {
27687
- const run3 = await withTimeout(agent.send(fullPrompt, {
27688
- local: { force: true },
27689
- idempotencyKey: randomUUID()
27690
- }), SEND_TIMEOUT_MS, "agent.send");
27691
- session.run = run3;
27692
- const adapter = new CursorSdkStreamAdapter(logPrefix);
27693
- adapter.onOutput(() => {
27694
- entry.lastOutputAt = Date.now();
27695
- for (const cb of outputCallbacks)
27696
- cb();
27697
- });
27698
- adapter.onAgentEnd(() => {
27699
- for (const cb of agentEndCallbacks)
27700
- cb();
27701
- });
27702
- for await (const message of run3.stream()) {
27703
- if (session.aborted)
27742
+ while (!session.aborted) {
27743
+ const run3 = await withTimeout(agent.send(nextPrompt, {
27744
+ local: { force: isFirstTurn },
27745
+ idempotencyKey: randomUUID()
27746
+ }), SEND_TIMEOUT_MS, "agent.send");
27747
+ session.run = run3;
27748
+ isFirstTurn = false;
27749
+ const adapter = new CursorSdkStreamAdapter(logPrefix);
27750
+ adapter.onOutput(() => {
27751
+ entry.lastOutputAt = Date.now();
27752
+ for (const cb of outputCallbacks)
27753
+ cb();
27754
+ });
27755
+ for await (const message of run3.stream()) {
27756
+ if (session.aborted)
27757
+ break;
27758
+ adapter.handleMessage(message);
27759
+ }
27760
+ if (session.aborted) {
27761
+ exitCode = 1;
27762
+ exitSignal = "SIGTERM";
27704
27763
  break;
27705
- adapter.handleMessage(message);
27706
- }
27707
- if (!session.aborted) {
27764
+ }
27708
27765
  const result = await withTimeout(run3.wait(), RUN_WAIT_TIMEOUT_MS, "run.wait");
27709
27766
  adapter.finish();
27710
27767
  if (result.status === "error") {
27711
27768
  exitCode = 2;
27712
27769
  process.stderr.write(`${logPrefix} run-error] run ${result.id} failed
27713
27770
  `);
27771
+ break;
27714
27772
  }
27715
- } else {
27716
- exitCode = 1;
27717
- exitSignal = "SIGTERM";
27773
+ for (const cb of agentEndCallbacks)
27774
+ cb();
27775
+ const resumePrompt = await waitForResumeOrAbort(session);
27776
+ if (resumePrompt === null || session.aborted) {
27777
+ if (session.aborted) {
27778
+ exitCode = 1;
27779
+ exitSignal = "SIGTERM";
27780
+ }
27781
+ break;
27782
+ }
27783
+ nextPrompt = resumePrompt;
27718
27784
  }
27719
27785
  } catch (err) {
27720
27786
  exitCode = 1;
@@ -30070,6 +30136,7 @@ var init_opencode_sdk_agent_service = __esm(() => {
30070
30136
  });
30071
30137
  let sessionId;
30072
30138
  let forwarder;
30139
+ let agentName;
30073
30140
  try {
30074
30141
  const sessionCreateResult = await withTimeout2(client4.session.create({ body: {} }), SESSION_CREATE_TIMEOUT_MS, "session.create");
30075
30142
  if (!sessionCreateResult.data?.id) {
@@ -30083,6 +30150,7 @@ var init_opencode_sdk_agent_service = __esm(() => {
30083
30150
  const agentsResponse = await withTimeout2(client4.app.agents(), AGENTS_LIST_TIMEOUT_MS, "app.agents");
30084
30151
  const availableAgents = agentsResponse.data ?? [];
30085
30152
  const selected = selectAgent(availableAgents);
30153
+ agentName = selected.name;
30086
30154
  const composedSystem = composeSystemPrompt(selected.prompt, systemPrompt);
30087
30155
  const modelParts = model ? parseModelId(model) : undefined;
30088
30156
  await withTimeout2(client4.session.promptAsync({
@@ -30114,6 +30182,8 @@ var init_opencode_sdk_agent_service = __esm(() => {
30114
30182
  machineId: context4.machineId,
30115
30183
  chatroomId: context4.chatroomId,
30116
30184
  role: context4.role,
30185
+ agentName,
30186
+ ...model ? { model } : {},
30117
30187
  pid,
30118
30188
  createdAt: new Date().toISOString(),
30119
30189
  baseUrl
@@ -30141,6 +30211,7 @@ var init_opencode_sdk_agent_service = __esm(() => {
30141
30211
  }
30142
30212
  return {
30143
30213
  pid,
30214
+ harnessSessionId: sessionId,
30144
30215
  onExit: (cb) => {
30145
30216
  childProcess.on("exit", (code2, signal) => {
30146
30217
  const fwd = this.forwarders.get(pid);
@@ -30161,6 +30232,27 @@ var init_opencode_sdk_agent_service = __esm(() => {
30161
30232
  }
30162
30233
  };
30163
30234
  }
30235
+ async resumeTurn(pid, prompt) {
30236
+ const meta = this.sessionStore.findByPid(pid);
30237
+ if (!meta) {
30238
+ throw new Error(`No opencode-sdk session metadata for pid=${pid}`);
30239
+ }
30240
+ const client4 = createOpencodeClient({ baseUrl: meta.baseUrl });
30241
+ const modelParts = meta.model ? parseModelId(meta.model) : undefined;
30242
+ await withTimeout2(client4.session.promptAsync({
30243
+ path: { id: meta.sessionId },
30244
+ body: {
30245
+ agent: meta.agentName,
30246
+ parts: [{ type: "text", text: prompt }],
30247
+ ...modelParts ? { model: modelParts } : {},
30248
+ tools: {
30249
+ task: false,
30250
+ question: false,
30251
+ external_directory: false
30252
+ }
30253
+ }
30254
+ }), PROMPT_ASYNC_TIMEOUT_MS, "session.promptAsync");
30255
+ }
30164
30256
  };
30165
30257
  });
30166
30258
 
@@ -76596,6 +76688,30 @@ var init_fromContext4 = __esm(() => {
76596
76688
  // ../../services/backend/prompts/review-guidelines/index.ts
76597
76689
  var init_review_guidelines = () => {};
76598
76690
 
76691
+ // ../../services/backend/prompts/cli/resume-session/message.ts
76692
+ function composeResumeMessage(params) {
76693
+ const cliEnvPrefix = getCliEnvPrefix(params.convexUrl);
76694
+ const getNextTask2 = getNextTaskCommand({
76695
+ chatroomId: params.chatroomId,
76696
+ role: params.role,
76697
+ cliEnvPrefix
76698
+ });
76699
+ const contextRead = `${cliEnvPrefix}chatroom context read --chatroom-id="${params.chatroomId}" --role="${params.role}"`;
76700
+ return [
76701
+ "Your previous turn has ended.",
76702
+ "Please rejoin the chatroom and get your next task by running:",
76703
+ ` ${getNextTask2}`,
76704
+ "",
76705
+ "If you need context on what you were doing, run:",
76706
+ ` ${contextRead}`
76707
+ ].join(`
76708
+ `);
76709
+ }
76710
+ var init_message = __esm(() => {
76711
+ init_command();
76712
+ init_env();
76713
+ });
76714
+
76599
76715
  // ../../services/backend/prompts/generator.ts
76600
76716
  function generateHandoffOutput(params) {
76601
76717
  const { role, nextRole, chatroomId, convexUrl } = params;
@@ -76629,6 +76745,7 @@ var init_generator = __esm(() => {
76629
76745
  init_fromContext4();
76630
76746
  init_utils3();
76631
76747
  init_review_guidelines();
76748
+ init_message();
76632
76749
  });
76633
76750
 
76634
76751
  // src/commands/handoff/index.ts
@@ -83184,6 +83301,96 @@ function resolveStopReason(code2, signal) {
83184
83301
  return "agent_process.crashed";
83185
83302
  }
83186
83303
 
83304
+ // ../../services/backend/src/domain/entities/harness/claude.config.ts
83305
+ var claudeCapabilities;
83306
+ var init_claude_config = __esm(() => {
83307
+ claudeCapabilities = {
83308
+ supportsSessionResume: false
83309
+ };
83310
+ });
83311
+
83312
+ // ../../services/backend/src/domain/entities/harness/commandcode.config.ts
83313
+ var commandcodeCapabilities;
83314
+ var init_commandcode_config = __esm(() => {
83315
+ commandcodeCapabilities = {
83316
+ supportsSessionResume: false
83317
+ };
83318
+ });
83319
+
83320
+ // ../../services/backend/src/domain/entities/harness/copilot.config.ts
83321
+ var copilotCapabilities;
83322
+ var init_copilot_config = __esm(() => {
83323
+ copilotCapabilities = {
83324
+ supportsSessionResume: false
83325
+ };
83326
+ });
83327
+
83328
+ // ../../services/backend/src/domain/entities/harness/cursor.config.ts
83329
+ var cursorCapabilities;
83330
+ var init_cursor_config = __esm(() => {
83331
+ cursorCapabilities = {
83332
+ supportsSessionResume: false
83333
+ };
83334
+ });
83335
+
83336
+ // ../../services/backend/src/domain/entities/harness/cursor-sdk.config.ts
83337
+ var cursorSdkCapabilities;
83338
+ var init_cursor_sdk_config = __esm(() => {
83339
+ cursorSdkCapabilities = {
83340
+ supportsSessionResume: true
83341
+ };
83342
+ });
83343
+
83344
+ // ../../services/backend/src/domain/entities/harness/opencode.config.ts
83345
+ var opencodeCapabilities;
83346
+ var init_opencode_config = __esm(() => {
83347
+ opencodeCapabilities = {
83348
+ supportsSessionResume: false
83349
+ };
83350
+ });
83351
+
83352
+ // ../../services/backend/src/domain/entities/harness/opencode-sdk.config.ts
83353
+ var opencodeSdkCapabilities;
83354
+ var init_opencode_sdk_config = __esm(() => {
83355
+ opencodeSdkCapabilities = {
83356
+ supportsSessionResume: true
83357
+ };
83358
+ });
83359
+
83360
+ // ../../services/backend/src/domain/entities/harness/pi.config.ts
83361
+ var piCapabilities;
83362
+ var init_pi_config = __esm(() => {
83363
+ piCapabilities = {
83364
+ supportsSessionResume: true
83365
+ };
83366
+ });
83367
+
83368
+ // ../../services/backend/src/domain/entities/harness/types.ts
83369
+ function getHarnessCapabilities(harness) {
83370
+ return HARNESS_CAPABILITIES[harness];
83371
+ }
83372
+ var HARNESS_CAPABILITIES;
83373
+ var init_types = __esm(() => {
83374
+ init_claude_config();
83375
+ init_commandcode_config();
83376
+ init_copilot_config();
83377
+ init_cursor_config();
83378
+ init_cursor_sdk_config();
83379
+ init_opencode_config();
83380
+ init_opencode_sdk_config();
83381
+ init_pi_config();
83382
+ HARNESS_CAPABILITIES = {
83383
+ claude: claudeCapabilities,
83384
+ commandcode: commandcodeCapabilities,
83385
+ copilot: copilotCapabilities,
83386
+ cursor: cursorCapabilities,
83387
+ "cursor-sdk": cursorSdkCapabilities,
83388
+ opencode: opencodeCapabilities,
83389
+ "opencode-sdk": opencodeSdkCapabilities,
83390
+ pi: piCapabilities
83391
+ };
83392
+ });
83393
+
83187
83394
  // src/infrastructure/services/remote-agents/spawn-prompt.ts
83188
83395
  function createSpawnPrompt(raw) {
83189
83396
  const trimmed = raw?.trim();
@@ -83263,6 +83470,29 @@ class AgentProcessManager {
83263
83470
  await operation;
83264
83471
  return { success: true };
83265
83472
  }
83473
+ async handleAgentEnd(opts) {
83474
+ const capabilities = getHarnessCapabilities(opts.harness);
83475
+ console.log(`[AgentProcessManager] agent_end: role=${opts.role} pid=${opts.pid} harness=${opts.harness} wantResume=${opts.wantResume} supportsResume=${capabilities.supportsSessionResume}`);
83476
+ if (capabilities.supportsSessionResume && opts.wantResume) {
83477
+ const service = this.deps.agentServices.get(opts.harness);
83478
+ if (service?.resumeTurn) {
83479
+ try {
83480
+ const resumePrompt = composeResumeMessage({
83481
+ chatroomId: opts.chatroomId,
83482
+ role: opts.role,
83483
+ convexUrl: this.deps.convexUrl
83484
+ });
83485
+ await service.resumeTurn(opts.pid, resumePrompt);
83486
+ return;
83487
+ } catch (err) {
83488
+ console.log(`[AgentProcessManager] ⚠️ resumeTurn failed for ${opts.role} (pid ${opts.pid}): ${err.message} — falling back to kill`);
83489
+ }
83490
+ }
83491
+ }
83492
+ try {
83493
+ this.deps.processes.kill(-opts.pid, "SIGTERM");
83494
+ } catch {}
83495
+ }
83266
83496
  async handleExit(opts) {
83267
83497
  const key = agentKey2(opts.chatroomId, opts.role);
83268
83498
  const slot = this.slots.get(key);
@@ -83371,7 +83601,8 @@ class AgentProcessManager {
83371
83601
  this.slots.set(key, {
83372
83602
  state: "running",
83373
83603
  pid: entry.pid,
83374
- harness: entry.harness
83604
+ harness: entry.harness,
83605
+ wantResume: true
83375
83606
  });
83376
83607
  recovered++;
83377
83608
  } else {
@@ -83384,7 +83615,7 @@ class AgentProcessManager {
83384
83615
  getOrCreateSlot(key) {
83385
83616
  let slot = this.slots.get(key);
83386
83617
  if (!slot) {
83387
- slot = { state: "idle" };
83618
+ slot = { state: "idle", wantResume: true };
83388
83619
  this.slots.set(key, slot);
83389
83620
  }
83390
83621
  return slot;
@@ -83424,6 +83655,8 @@ class AgentProcessManager {
83424
83655
  }
83425
83656
  async doEnsureRunning(key, slot, opts) {
83426
83657
  slot.state = "spawning";
83658
+ slot.wantResume = opts.wantResume ?? true;
83659
+ console.log(`[AgentProcessManager] harness start: role=${opts.role} harness=${opts.agentHarness} wantResume=${slot.wantResume} reason=${opts.reason}`);
83427
83660
  try {
83428
83661
  const spawnCheck = this.deps.spawning.shouldAllowSpawn(opts.chatroomId, opts.reason, {
83429
83662
  bypassConcurrentLimit: opts.reason.startsWith("user.")
@@ -83525,6 +83758,7 @@ class AgentProcessManager {
83525
83758
  slot.state = "running";
83526
83759
  slot.pid = pid;
83527
83760
  slot.harness = opts.agentHarness;
83761
+ slot.harnessSessionId = spawnResult.harnessSessionId;
83528
83762
  slot.model = opts.model;
83529
83763
  slot.workingDir = opts.workingDir;
83530
83764
  slot.startedAt = this.deps.clock.now();
@@ -83554,9 +83788,13 @@ class AgentProcessManager {
83554
83788
  });
83555
83789
  if (spawnResult.onAgentEnd) {
83556
83790
  spawnResult.onAgentEnd(() => {
83557
- try {
83558
- this.deps.processes.kill(-pid, "SIGTERM");
83559
- } catch {}
83791
+ this.handleAgentEnd({
83792
+ chatroomId: opts.chatroomId,
83793
+ role: opts.role,
83794
+ pid,
83795
+ harness: opts.agentHarness,
83796
+ wantResume: slot.wantResume
83797
+ });
83560
83798
  });
83561
83799
  }
83562
83800
  let lastReportedTokenAt = 0;
@@ -83646,6 +83884,8 @@ class AgentProcessManager {
83646
83884
  var AGENT_EXIT_RETRY_INTERVAL_MS = 1e4;
83647
83885
  var init_agent_process_manager = __esm(() => {
83648
83886
  init_api3();
83887
+ init_types();
83888
+ init_generator();
83649
83889
  });
83650
83890
 
83651
83891
  // src/infrastructure/services/harness-spawning/rate-limiter.ts
@@ -86229,4 +86469,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
86229
86469
  });
86230
86470
  program2.parse();
86231
86471
 
86232
- //# debugId=20A53A569D865DC964756E2164756E21
86472
+ //# debugId=AD4499C508D701CE64756E2164756E21