chatroom-cli 1.47.0 → 1.49.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
  });
@@ -26918,6 +26937,40 @@ var init_registry2 = __esm(() => {
26918
26937
  registry = new Map;
26919
26938
  });
26920
26939
 
26940
+ // src/infrastructure/services/remote-agents/claude/claude-models.ts
26941
+ async function fetchClaudeModels() {
26942
+ const apiKey = process.env.ANTHROPIC_API_KEY?.trim();
26943
+ if (!apiKey)
26944
+ return;
26945
+ try {
26946
+ const resp = await fetch("https://api.anthropic.com/v1/models", {
26947
+ headers: {
26948
+ "x-api-key": apiKey,
26949
+ "anthropic-version": "2023-06-01"
26950
+ }
26951
+ });
26952
+ if (!resp.ok)
26953
+ return;
26954
+ const json = await resp.json();
26955
+ const ids3 = json.data.map((m) => m.id).filter((id3) => id3.startsWith("claude-"));
26956
+ return ids3.length > 0 ? ids3 : undefined;
26957
+ } catch {
26958
+ return;
26959
+ }
26960
+ }
26961
+ var CLAUDE_FALLBACK_MODELS;
26962
+ var init_claude_models = __esm(() => {
26963
+ CLAUDE_FALLBACK_MODELS = [
26964
+ "opus",
26965
+ "sonnet",
26966
+ "haiku",
26967
+ "claude-opus-4-8",
26968
+ "claude-opus-4-6",
26969
+ "claude-sonnet-4-6",
26970
+ "claude-haiku-4-5"
26971
+ ];
26972
+ });
26973
+
26921
26974
  // src/infrastructure/services/remote-agents/claude/claude-stream-reader.ts
26922
26975
  import { createInterface as createInterface4 } from "node:readline";
26923
26976
 
@@ -26983,6 +27036,7 @@ var init_claude_stream_reader = () => {};
26983
27036
  var CLAUDE_COMMAND = "claude", DEFAULT_MAX_TURNS = 200, ClaudeCodeAgentService;
26984
27037
  var init_claude_code_agent_service = __esm(() => {
26985
27038
  init_base_cli_agent_service();
27039
+ init_claude_models();
26986
27040
  init_claude_stream_reader();
26987
27041
  ClaudeCodeAgentService = class ClaudeCodeAgentService extends BaseCLIAgentService {
26988
27042
  id = "claude";
@@ -26998,7 +27052,10 @@ var init_claude_code_agent_service = __esm(() => {
26998
27052
  return this.checkVersion(CLAUDE_COMMAND);
26999
27053
  }
27000
27054
  async listModels() {
27001
- return ["claude-haiku-4-5", "claude-sonnet-4-6", "claude-opus-4-6"];
27055
+ const dynamic = await fetchClaudeModels();
27056
+ if (dynamic)
27057
+ return dynamic;
27058
+ return [...CLAUDE_FALLBACK_MODELS];
27002
27059
  }
27003
27060
  async spawn(options) {
27004
27061
  const { prompt, systemPrompt, model } = options;
@@ -27543,6 +27600,26 @@ async function withTimeout(p, ms, label) {
27543
27600
  clearTimeout(timer);
27544
27601
  }
27545
27602
  }
27603
+ function waitForResumeOrAbort(session) {
27604
+ if (session.aborted)
27605
+ return Promise.resolve(null);
27606
+ return Promise.race([
27607
+ new Promise((resolve) => {
27608
+ session.resumeResolve = (prompt) => {
27609
+ session.resumeResolve = undefined;
27610
+ session.abortResolve = undefined;
27611
+ resolve(prompt);
27612
+ };
27613
+ }),
27614
+ new Promise((resolve) => {
27615
+ session.abortResolve = () => {
27616
+ session.resumeResolve = undefined;
27617
+ session.abortResolve = undefined;
27618
+ resolve(null);
27619
+ };
27620
+ })
27621
+ ]);
27622
+ }
27546
27623
  function buildLogPrefix(context4) {
27547
27624
  const roleTag = context4.role ?? "unknown";
27548
27625
  const chatroomSuffix = context4.chatroomId ? `@${context4.chatroomId.slice(-6)}` : "";
@@ -27605,10 +27682,24 @@ var init_cursor_sdk_agent_service = __esm(() => {
27605
27682
  return [...CURSOR_SDK_FALLBACK_MODELS];
27606
27683
  }
27607
27684
  }
27685
+ async resumeTurn(pid, prompt) {
27686
+ const session = this.sessions.get(pid);
27687
+ if (!session) {
27688
+ throw new Error(`No cursor-sdk session for pid=${pid}`);
27689
+ }
27690
+ if (!session.resumeResolve) {
27691
+ throw new Error(`cursor-sdk session pid=${pid} not waiting for resume`);
27692
+ }
27693
+ const resolve = session.resumeResolve;
27694
+ session.resumeResolve = undefined;
27695
+ session.abortResolve = undefined;
27696
+ resolve(prompt);
27697
+ }
27608
27698
  async stop(pid) {
27609
27699
  const session = this.sessions.get(pid);
27610
27700
  if (session) {
27611
27701
  session.aborted = true;
27702
+ session.abortResolve?.();
27612
27703
  const run3 = session.run;
27613
27704
  if (run3?.supports("cancel")) {
27614
27705
  try {
@@ -27683,38 +27774,51 @@ ${options.prompt}` : options.prompt;
27683
27774
  (async () => {
27684
27775
  let exitCode = 0;
27685
27776
  let exitSignal = null;
27777
+ let nextPrompt = fullPrompt;
27778
+ let isFirstTurn = true;
27686
27779
  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)
27780
+ while (!session.aborted) {
27781
+ const run3 = await withTimeout(agent.send(nextPrompt, {
27782
+ local: { force: isFirstTurn },
27783
+ idempotencyKey: randomUUID()
27784
+ }), SEND_TIMEOUT_MS, "agent.send");
27785
+ session.run = run3;
27786
+ isFirstTurn = false;
27787
+ const adapter = new CursorSdkStreamAdapter(logPrefix);
27788
+ adapter.onOutput(() => {
27789
+ entry.lastOutputAt = Date.now();
27790
+ for (const cb of outputCallbacks)
27791
+ cb();
27792
+ });
27793
+ for await (const message of run3.stream()) {
27794
+ if (session.aborted)
27795
+ break;
27796
+ adapter.handleMessage(message);
27797
+ }
27798
+ if (session.aborted) {
27799
+ exitCode = 1;
27800
+ exitSignal = "SIGTERM";
27704
27801
  break;
27705
- adapter.handleMessage(message);
27706
- }
27707
- if (!session.aborted) {
27802
+ }
27708
27803
  const result = await withTimeout(run3.wait(), RUN_WAIT_TIMEOUT_MS, "run.wait");
27709
27804
  adapter.finish();
27710
27805
  if (result.status === "error") {
27711
27806
  exitCode = 2;
27712
27807
  process.stderr.write(`${logPrefix} run-error] run ${result.id} failed
27713
27808
  `);
27809
+ break;
27714
27810
  }
27715
- } else {
27716
- exitCode = 1;
27717
- exitSignal = "SIGTERM";
27811
+ for (const cb of agentEndCallbacks)
27812
+ cb();
27813
+ const resumePrompt = await waitForResumeOrAbort(session);
27814
+ if (resumePrompt === null || session.aborted) {
27815
+ if (session.aborted) {
27816
+ exitCode = 1;
27817
+ exitSignal = "SIGTERM";
27818
+ }
27819
+ break;
27820
+ }
27821
+ nextPrompt = resumePrompt;
27718
27822
  }
27719
27823
  } catch (err) {
27720
27824
  exitCode = 1;
@@ -30070,6 +30174,7 @@ var init_opencode_sdk_agent_service = __esm(() => {
30070
30174
  });
30071
30175
  let sessionId;
30072
30176
  let forwarder;
30177
+ let agentName;
30073
30178
  try {
30074
30179
  const sessionCreateResult = await withTimeout2(client4.session.create({ body: {} }), SESSION_CREATE_TIMEOUT_MS, "session.create");
30075
30180
  if (!sessionCreateResult.data?.id) {
@@ -30083,6 +30188,7 @@ var init_opencode_sdk_agent_service = __esm(() => {
30083
30188
  const agentsResponse = await withTimeout2(client4.app.agents(), AGENTS_LIST_TIMEOUT_MS, "app.agents");
30084
30189
  const availableAgents = agentsResponse.data ?? [];
30085
30190
  const selected = selectAgent(availableAgents);
30191
+ agentName = selected.name;
30086
30192
  const composedSystem = composeSystemPrompt(selected.prompt, systemPrompt);
30087
30193
  const modelParts = model ? parseModelId(model) : undefined;
30088
30194
  await withTimeout2(client4.session.promptAsync({
@@ -30114,6 +30220,8 @@ var init_opencode_sdk_agent_service = __esm(() => {
30114
30220
  machineId: context4.machineId,
30115
30221
  chatroomId: context4.chatroomId,
30116
30222
  role: context4.role,
30223
+ agentName,
30224
+ ...model ? { model } : {},
30117
30225
  pid,
30118
30226
  createdAt: new Date().toISOString(),
30119
30227
  baseUrl
@@ -30141,6 +30249,7 @@ var init_opencode_sdk_agent_service = __esm(() => {
30141
30249
  }
30142
30250
  return {
30143
30251
  pid,
30252
+ harnessSessionId: sessionId,
30144
30253
  onExit: (cb) => {
30145
30254
  childProcess.on("exit", (code2, signal) => {
30146
30255
  const fwd = this.forwarders.get(pid);
@@ -30161,6 +30270,27 @@ var init_opencode_sdk_agent_service = __esm(() => {
30161
30270
  }
30162
30271
  };
30163
30272
  }
30273
+ async resumeTurn(pid, prompt) {
30274
+ const meta = this.sessionStore.findByPid(pid);
30275
+ if (!meta) {
30276
+ throw new Error(`No opencode-sdk session metadata for pid=${pid}`);
30277
+ }
30278
+ const client4 = createOpencodeClient({ baseUrl: meta.baseUrl });
30279
+ const modelParts = meta.model ? parseModelId(meta.model) : undefined;
30280
+ await withTimeout2(client4.session.promptAsync({
30281
+ path: { id: meta.sessionId },
30282
+ body: {
30283
+ agent: meta.agentName,
30284
+ parts: [{ type: "text", text: prompt }],
30285
+ ...modelParts ? { model: modelParts } : {},
30286
+ tools: {
30287
+ task: false,
30288
+ question: false,
30289
+ external_directory: false
30290
+ }
30291
+ }
30292
+ }), PROMPT_ASYNC_TIMEOUT_MS, "session.promptAsync");
30293
+ }
30164
30294
  };
30165
30295
  });
30166
30296
 
@@ -76596,6 +76726,30 @@ var init_fromContext4 = __esm(() => {
76596
76726
  // ../../services/backend/prompts/review-guidelines/index.ts
76597
76727
  var init_review_guidelines = () => {};
76598
76728
 
76729
+ // ../../services/backend/prompts/cli/resume-session/message.ts
76730
+ function composeResumeMessage(params) {
76731
+ const cliEnvPrefix = getCliEnvPrefix(params.convexUrl);
76732
+ const getNextTask2 = getNextTaskCommand({
76733
+ chatroomId: params.chatroomId,
76734
+ role: params.role,
76735
+ cliEnvPrefix
76736
+ });
76737
+ const contextRead = `${cliEnvPrefix}chatroom context read --chatroom-id="${params.chatroomId}" --role="${params.role}"`;
76738
+ return [
76739
+ "Your previous turn has ended.",
76740
+ "Please rejoin the chatroom and get your next task by running:",
76741
+ ` ${getNextTask2}`,
76742
+ "",
76743
+ "If you need context on what you were doing, run:",
76744
+ ` ${contextRead}`
76745
+ ].join(`
76746
+ `);
76747
+ }
76748
+ var init_message = __esm(() => {
76749
+ init_command();
76750
+ init_env();
76751
+ });
76752
+
76599
76753
  // ../../services/backend/prompts/generator.ts
76600
76754
  function generateHandoffOutput(params) {
76601
76755
  const { role, nextRole, chatroomId, convexUrl } = params;
@@ -76629,6 +76783,7 @@ var init_generator = __esm(() => {
76629
76783
  init_fromContext4();
76630
76784
  init_utils3();
76631
76785
  init_review_guidelines();
76786
+ init_message();
76632
76787
  });
76633
76788
 
76634
76789
  // src/commands/handoff/index.ts
@@ -83184,6 +83339,96 @@ function resolveStopReason(code2, signal) {
83184
83339
  return "agent_process.crashed";
83185
83340
  }
83186
83341
 
83342
+ // ../../services/backend/src/domain/entities/harness/claude.config.ts
83343
+ var claudeCapabilities;
83344
+ var init_claude_config = __esm(() => {
83345
+ claudeCapabilities = {
83346
+ supportsSessionResume: false
83347
+ };
83348
+ });
83349
+
83350
+ // ../../services/backend/src/domain/entities/harness/commandcode.config.ts
83351
+ var commandcodeCapabilities;
83352
+ var init_commandcode_config = __esm(() => {
83353
+ commandcodeCapabilities = {
83354
+ supportsSessionResume: false
83355
+ };
83356
+ });
83357
+
83358
+ // ../../services/backend/src/domain/entities/harness/copilot.config.ts
83359
+ var copilotCapabilities;
83360
+ var init_copilot_config = __esm(() => {
83361
+ copilotCapabilities = {
83362
+ supportsSessionResume: false
83363
+ };
83364
+ });
83365
+
83366
+ // ../../services/backend/src/domain/entities/harness/cursor.config.ts
83367
+ var cursorCapabilities;
83368
+ var init_cursor_config = __esm(() => {
83369
+ cursorCapabilities = {
83370
+ supportsSessionResume: false
83371
+ };
83372
+ });
83373
+
83374
+ // ../../services/backend/src/domain/entities/harness/cursor-sdk.config.ts
83375
+ var cursorSdkCapabilities;
83376
+ var init_cursor_sdk_config = __esm(() => {
83377
+ cursorSdkCapabilities = {
83378
+ supportsSessionResume: true
83379
+ };
83380
+ });
83381
+
83382
+ // ../../services/backend/src/domain/entities/harness/opencode.config.ts
83383
+ var opencodeCapabilities;
83384
+ var init_opencode_config = __esm(() => {
83385
+ opencodeCapabilities = {
83386
+ supportsSessionResume: false
83387
+ };
83388
+ });
83389
+
83390
+ // ../../services/backend/src/domain/entities/harness/opencode-sdk.config.ts
83391
+ var opencodeSdkCapabilities;
83392
+ var init_opencode_sdk_config = __esm(() => {
83393
+ opencodeSdkCapabilities = {
83394
+ supportsSessionResume: true
83395
+ };
83396
+ });
83397
+
83398
+ // ../../services/backend/src/domain/entities/harness/pi.config.ts
83399
+ var piCapabilities;
83400
+ var init_pi_config = __esm(() => {
83401
+ piCapabilities = {
83402
+ supportsSessionResume: true
83403
+ };
83404
+ });
83405
+
83406
+ // ../../services/backend/src/domain/entities/harness/types.ts
83407
+ function getHarnessCapabilities(harness) {
83408
+ return HARNESS_CAPABILITIES[harness];
83409
+ }
83410
+ var HARNESS_CAPABILITIES;
83411
+ var init_types = __esm(() => {
83412
+ init_claude_config();
83413
+ init_commandcode_config();
83414
+ init_copilot_config();
83415
+ init_cursor_config();
83416
+ init_cursor_sdk_config();
83417
+ init_opencode_config();
83418
+ init_opencode_sdk_config();
83419
+ init_pi_config();
83420
+ HARNESS_CAPABILITIES = {
83421
+ claude: claudeCapabilities,
83422
+ commandcode: commandcodeCapabilities,
83423
+ copilot: copilotCapabilities,
83424
+ cursor: cursorCapabilities,
83425
+ "cursor-sdk": cursorSdkCapabilities,
83426
+ opencode: opencodeCapabilities,
83427
+ "opencode-sdk": opencodeSdkCapabilities,
83428
+ pi: piCapabilities
83429
+ };
83430
+ });
83431
+
83187
83432
  // src/infrastructure/services/remote-agents/spawn-prompt.ts
83188
83433
  function createSpawnPrompt(raw) {
83189
83434
  const trimmed = raw?.trim();
@@ -83263,6 +83508,29 @@ class AgentProcessManager {
83263
83508
  await operation;
83264
83509
  return { success: true };
83265
83510
  }
83511
+ async handleAgentEnd(opts) {
83512
+ const capabilities = getHarnessCapabilities(opts.harness);
83513
+ console.log(`[AgentProcessManager] agent_end: role=${opts.role} pid=${opts.pid} harness=${opts.harness} wantResume=${opts.wantResume} supportsResume=${capabilities.supportsSessionResume}`);
83514
+ if (capabilities.supportsSessionResume && opts.wantResume) {
83515
+ const service = this.deps.agentServices.get(opts.harness);
83516
+ if (service?.resumeTurn) {
83517
+ try {
83518
+ const resumePrompt = composeResumeMessage({
83519
+ chatroomId: opts.chatroomId,
83520
+ role: opts.role,
83521
+ convexUrl: this.deps.convexUrl
83522
+ });
83523
+ await service.resumeTurn(opts.pid, resumePrompt);
83524
+ return;
83525
+ } catch (err) {
83526
+ console.log(`[AgentProcessManager] ⚠️ resumeTurn failed for ${opts.role} (pid ${opts.pid}): ${err.message} — falling back to kill`);
83527
+ }
83528
+ }
83529
+ }
83530
+ try {
83531
+ this.deps.processes.kill(-opts.pid, "SIGTERM");
83532
+ } catch {}
83533
+ }
83266
83534
  async handleExit(opts) {
83267
83535
  const key = agentKey2(opts.chatroomId, opts.role);
83268
83536
  const slot = this.slots.get(key);
@@ -83371,7 +83639,8 @@ class AgentProcessManager {
83371
83639
  this.slots.set(key, {
83372
83640
  state: "running",
83373
83641
  pid: entry.pid,
83374
- harness: entry.harness
83642
+ harness: entry.harness,
83643
+ wantResume: true
83375
83644
  });
83376
83645
  recovered++;
83377
83646
  } else {
@@ -83384,7 +83653,7 @@ class AgentProcessManager {
83384
83653
  getOrCreateSlot(key) {
83385
83654
  let slot = this.slots.get(key);
83386
83655
  if (!slot) {
83387
- slot = { state: "idle" };
83656
+ slot = { state: "idle", wantResume: true };
83388
83657
  this.slots.set(key, slot);
83389
83658
  }
83390
83659
  return slot;
@@ -83424,6 +83693,8 @@ class AgentProcessManager {
83424
83693
  }
83425
83694
  async doEnsureRunning(key, slot, opts) {
83426
83695
  slot.state = "spawning";
83696
+ slot.wantResume = opts.wantResume ?? true;
83697
+ console.log(`[AgentProcessManager] harness start: role=${opts.role} harness=${opts.agentHarness} wantResume=${slot.wantResume} reason=${opts.reason}`);
83427
83698
  try {
83428
83699
  const spawnCheck = this.deps.spawning.shouldAllowSpawn(opts.chatroomId, opts.reason, {
83429
83700
  bypassConcurrentLimit: opts.reason.startsWith("user.")
@@ -83525,6 +83796,7 @@ class AgentProcessManager {
83525
83796
  slot.state = "running";
83526
83797
  slot.pid = pid;
83527
83798
  slot.harness = opts.agentHarness;
83799
+ slot.harnessSessionId = spawnResult.harnessSessionId;
83528
83800
  slot.model = opts.model;
83529
83801
  slot.workingDir = opts.workingDir;
83530
83802
  slot.startedAt = this.deps.clock.now();
@@ -83554,9 +83826,13 @@ class AgentProcessManager {
83554
83826
  });
83555
83827
  if (spawnResult.onAgentEnd) {
83556
83828
  spawnResult.onAgentEnd(() => {
83557
- try {
83558
- this.deps.processes.kill(-pid, "SIGTERM");
83559
- } catch {}
83829
+ this.handleAgentEnd({
83830
+ chatroomId: opts.chatroomId,
83831
+ role: opts.role,
83832
+ pid,
83833
+ harness: opts.agentHarness,
83834
+ wantResume: slot.wantResume
83835
+ });
83560
83836
  });
83561
83837
  }
83562
83838
  let lastReportedTokenAt = 0;
@@ -83646,6 +83922,8 @@ class AgentProcessManager {
83646
83922
  var AGENT_EXIT_RETRY_INTERVAL_MS = 1e4;
83647
83923
  var init_agent_process_manager = __esm(() => {
83648
83924
  init_api3();
83925
+ init_types();
83926
+ init_generator();
83649
83927
  });
83650
83928
 
83651
83929
  // src/infrastructure/services/harness-spawning/rate-limiter.ts
@@ -86229,4 +86507,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
86229
86507
  });
86230
86508
  program2.parse();
86231
86509
 
86232
- //# debugId=20A53A569D865DC964756E2164756E21
86510
+ //# debugId=2DBCEF13548BACBB64756E2164756E21