chatroom-cli 1.0.69 → 1.0.72

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.
Files changed (2) hide show
  1. package/dist/index.js +393 -183
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -10199,6 +10199,205 @@ var init_machine = __esm(() => {
10199
10199
  init_daemon_state();
10200
10200
  });
10201
10201
 
10202
+ // src/infrastructure/agent-drivers/process-driver.ts
10203
+ import { spawn } from "node:child_process";
10204
+ import { randomUUID as randomUUID2 } from "node:crypto";
10205
+ import { writeFileSync as writeFileSync4, unlinkSync as unlinkSync2 } from "node:fs";
10206
+ import { tmpdir } from "node:os";
10207
+ import { join as join5 } from "node:path";
10208
+ function writeTempPromptFile(prompt) {
10209
+ const tempPath = join5(tmpdir(), `chatroom-prompt-${randomUUID2()}.txt`);
10210
+ writeFileSync4(tempPath, prompt, { encoding: "utf-8", mode: 384 });
10211
+ return tempPath;
10212
+ }
10213
+ function scheduleCleanup(filePath, delayMs = 5000) {
10214
+ setTimeout(() => {
10215
+ try {
10216
+ unlinkSync2(filePath);
10217
+ } catch {}
10218
+ }, delayMs);
10219
+ }
10220
+ function buildCombinedPrompt(rolePrompt, initialMessage) {
10221
+ return `${rolePrompt}
10222
+
10223
+ ${initialMessage}`;
10224
+ }
10225
+
10226
+ class ProcessDriver {
10227
+ async start(options) {
10228
+ const config = this.buildSpawnConfig(options);
10229
+ console.log(` Spawning ${this.harness} agent...`);
10230
+ console.log(` Working dir: ${options.workingDir}`);
10231
+ if (options.harnessVersion) {
10232
+ console.log(` Harness version: v${options.harnessVersion.version} (major: ${options.harnessVersion.major})`);
10233
+ }
10234
+ if (options.model) {
10235
+ console.log(` Model: ${options.model}`);
10236
+ }
10237
+ try {
10238
+ const childProcess = spawn(config.command, config.args, {
10239
+ cwd: options.workingDir,
10240
+ stdio: config.stdio,
10241
+ detached: true,
10242
+ shell: false
10243
+ });
10244
+ if (config.writePromptToStdin && config.stdinPrompt) {
10245
+ childProcess.stdin?.write(config.stdinPrompt);
10246
+ childProcess.stdin?.end();
10247
+ }
10248
+ config.afterSpawn?.(childProcess);
10249
+ childProcess.unref();
10250
+ await new Promise((resolve) => setTimeout(resolve, 500));
10251
+ if (childProcess.killed || childProcess.exitCode !== null) {
10252
+ return {
10253
+ success: false,
10254
+ message: `Agent process exited immediately (exit code: ${childProcess.exitCode})`
10255
+ };
10256
+ }
10257
+ const handle = {
10258
+ harness: this.harness,
10259
+ type: "process",
10260
+ pid: childProcess.pid,
10261
+ workingDir: options.workingDir
10262
+ };
10263
+ return {
10264
+ success: true,
10265
+ message: "Agent spawned successfully",
10266
+ handle
10267
+ };
10268
+ } catch (error) {
10269
+ return {
10270
+ success: false,
10271
+ message: `Failed to spawn agent: ${error.message}`
10272
+ };
10273
+ }
10274
+ }
10275
+ async stop(handle) {
10276
+ if (handle.type !== "process" || !handle.pid) {
10277
+ throw new Error(`Cannot stop: handle has no PID (type=${handle.type})`);
10278
+ }
10279
+ process.kill(handle.pid, "SIGTERM");
10280
+ }
10281
+ async isAlive(handle) {
10282
+ if (handle.type !== "process" || !handle.pid) {
10283
+ return false;
10284
+ }
10285
+ try {
10286
+ process.kill(handle.pid, 0);
10287
+ return true;
10288
+ } catch {
10289
+ return false;
10290
+ }
10291
+ }
10292
+ async recover(_workingDir) {
10293
+ return [];
10294
+ }
10295
+ async listModels() {
10296
+ return [];
10297
+ }
10298
+ }
10299
+ var init_process_driver = () => {};
10300
+
10301
+ // src/infrastructure/agent-drivers/opencode-process-driver.ts
10302
+ import { execSync as execSync2 } from "node:child_process";
10303
+ var OpenCodeProcessDriver;
10304
+ var init_opencode_process_driver = __esm(() => {
10305
+ init_process_driver();
10306
+ init_types();
10307
+ OpenCodeProcessDriver = class OpenCodeProcessDriver extends ProcessDriver {
10308
+ harness = "opencode";
10309
+ capabilities = {
10310
+ sessionPersistence: false,
10311
+ abort: false,
10312
+ modelSelection: true,
10313
+ compaction: false,
10314
+ eventStreaming: false,
10315
+ messageInjection: false,
10316
+ dynamicModelDiscovery: true
10317
+ };
10318
+ buildSpawnConfig(options) {
10319
+ const command = AGENT_HARNESS_COMMANDS[this.harness];
10320
+ const combinedPrompt = buildCombinedPrompt(options.rolePrompt, options.initialMessage);
10321
+ const args = ["run"];
10322
+ if (options.model) {
10323
+ args.push("--model", options.model);
10324
+ }
10325
+ return {
10326
+ command,
10327
+ args,
10328
+ stdio: ["pipe", "inherit", "inherit"],
10329
+ writePromptToStdin: true,
10330
+ stdinPrompt: combinedPrompt
10331
+ };
10332
+ }
10333
+ async listModels() {
10334
+ try {
10335
+ const output = execSync2("opencode models", {
10336
+ stdio: ["pipe", "pipe", "pipe"],
10337
+ timeout: 1e4
10338
+ }).toString().trim();
10339
+ if (!output)
10340
+ return [];
10341
+ return output.split(`
10342
+ `).map((line) => line.trim()).filter((line) => line.length > 0);
10343
+ } catch {
10344
+ return [];
10345
+ }
10346
+ }
10347
+ };
10348
+ });
10349
+
10350
+ // src/infrastructure/agent-drivers/registry.ts
10351
+ class DefaultDriverRegistry {
10352
+ drivers;
10353
+ constructor(drivers) {
10354
+ this.drivers = new Map;
10355
+ for (const driver of drivers) {
10356
+ this.drivers.set(driver.harness, driver);
10357
+ }
10358
+ }
10359
+ get(harness) {
10360
+ const driver = this.drivers.get(harness);
10361
+ if (!driver) {
10362
+ throw new Error(`No driver registered for harness: ${harness}`);
10363
+ }
10364
+ return driver;
10365
+ }
10366
+ all() {
10367
+ return Array.from(this.drivers.values());
10368
+ }
10369
+ capabilities(harness) {
10370
+ return this.get(harness).capabilities;
10371
+ }
10372
+ }
10373
+ function getDriverRegistry() {
10374
+ if (!registryInstance) {
10375
+ registryInstance = new DefaultDriverRegistry([new OpenCodeProcessDriver]);
10376
+ }
10377
+ return registryInstance;
10378
+ }
10379
+ var registryInstance = null;
10380
+ var init_registry = __esm(() => {
10381
+ init_opencode_process_driver();
10382
+ });
10383
+
10384
+ // src/infrastructure/agent-drivers/index.ts
10385
+ var exports_agent_drivers = {};
10386
+ __export(exports_agent_drivers, {
10387
+ writeTempPromptFile: () => writeTempPromptFile,
10388
+ scheduleCleanup: () => scheduleCleanup,
10389
+ getDriverRegistry: () => getDriverRegistry,
10390
+ buildCombinedPrompt: () => buildCombinedPrompt,
10391
+ ProcessDriver: () => ProcessDriver,
10392
+ OpenCodeProcessDriver: () => OpenCodeProcessDriver
10393
+ });
10394
+ var init_agent_drivers = __esm(() => {
10395
+ init_registry();
10396
+ init_process_driver();
10397
+ init_process_driver();
10398
+ init_opencode_process_driver();
10399
+ });
10400
+
10202
10401
  // src/commands/auth-status.ts
10203
10402
  var exports_auth_status = {};
10204
10403
  __export(exports_auth_status, {
@@ -10239,6 +10438,17 @@ ${"═".repeat(50)}`);
10239
10438
  }
10240
10439
  try {
10241
10440
  const machineInfo = ensureMachineRegistered();
10441
+ let availableModels = [];
10442
+ try {
10443
+ const { getDriverRegistry: getDriverRegistry2 } = await Promise.resolve().then(() => (init_agent_drivers(), exports_agent_drivers));
10444
+ const registry = getDriverRegistry2();
10445
+ for (const driver of registry.all()) {
10446
+ if (driver.capabilities.dynamicModelDiscovery) {
10447
+ const models = await driver.listModels();
10448
+ availableModels = availableModels.concat(models);
10449
+ }
10450
+ }
10451
+ } catch {}
10242
10452
  await client2.mutation(api.machines.register, {
10243
10453
  sessionId: authData.sessionId,
10244
10454
  machineId: machineInfo.machineId,
@@ -10246,7 +10456,7 @@ ${"═".repeat(50)}`);
10246
10456
  os: machineInfo.os,
10247
10457
  availableHarnesses: machineInfo.availableHarnesses,
10248
10458
  harnessVersions: machineInfo.harnessVersions,
10249
- availableModels: []
10459
+ availableModels
10250
10460
  });
10251
10461
  console.log(`
10252
10462
  \uD83D\uDDA5️ Machine registered: ${machineInfo.hostname}`);
@@ -10254,6 +10464,9 @@ ${"═".repeat(50)}`);
10254
10464
  if (machineInfo.availableHarnesses.length > 0) {
10255
10465
  console.log(` Harnesses: ${machineInfo.availableHarnesses.join(", ")}`);
10256
10466
  }
10467
+ if (availableModels.length > 0) {
10468
+ console.log(` Models: ${availableModels.length} discovered`);
10469
+ }
10257
10470
  } catch (machineError) {
10258
10471
  const err = machineError;
10259
10472
  console.log(`
@@ -10498,180 +10711,6 @@ var init_config2 = __esm(() => {
10498
10711
  WEB_SERVER_PORT = parseInt(process.env.WEB_PORT || "3456", 10);
10499
10712
  });
10500
10713
 
10501
- // src/infrastructure/agent-drivers/process-driver.ts
10502
- import { spawn } from "node:child_process";
10503
- function buildCombinedPrompt(rolePrompt, initialMessage) {
10504
- return `${rolePrompt}
10505
-
10506
- ${initialMessage}`;
10507
- }
10508
-
10509
- class ProcessDriver {
10510
- async start(options) {
10511
- const config3 = this.buildSpawnConfig(options);
10512
- console.log(` Spawning ${this.harness} agent...`);
10513
- console.log(` Working dir: ${options.workingDir}`);
10514
- if (options.harnessVersion) {
10515
- console.log(` Harness version: v${options.harnessVersion.version} (major: ${options.harnessVersion.major})`);
10516
- }
10517
- if (options.model) {
10518
- console.log(` Model: ${options.model}`);
10519
- }
10520
- try {
10521
- const childProcess = spawn(config3.command, config3.args, {
10522
- cwd: options.workingDir,
10523
- stdio: config3.stdio,
10524
- detached: true,
10525
- shell: false
10526
- });
10527
- if (config3.writePromptToStdin && config3.stdinPrompt) {
10528
- childProcess.stdin?.write(config3.stdinPrompt);
10529
- childProcess.stdin?.end();
10530
- }
10531
- config3.afterSpawn?.(childProcess);
10532
- childProcess.unref();
10533
- await new Promise((resolve) => setTimeout(resolve, 500));
10534
- if (childProcess.killed || childProcess.exitCode !== null) {
10535
- return {
10536
- success: false,
10537
- message: `Agent process exited immediately (exit code: ${childProcess.exitCode})`
10538
- };
10539
- }
10540
- const handle = {
10541
- harness: this.harness,
10542
- type: "process",
10543
- pid: childProcess.pid,
10544
- workingDir: options.workingDir
10545
- };
10546
- return {
10547
- success: true,
10548
- message: "Agent spawned successfully",
10549
- handle
10550
- };
10551
- } catch (error) {
10552
- return {
10553
- success: false,
10554
- message: `Failed to spawn agent: ${error.message}`
10555
- };
10556
- }
10557
- }
10558
- async stop(handle) {
10559
- if (handle.type !== "process" || !handle.pid) {
10560
- throw new Error(`Cannot stop: handle has no PID (type=${handle.type})`);
10561
- }
10562
- process.kill(handle.pid, "SIGTERM");
10563
- }
10564
- async isAlive(handle) {
10565
- if (handle.type !== "process" || !handle.pid) {
10566
- return false;
10567
- }
10568
- try {
10569
- process.kill(handle.pid, 0);
10570
- return true;
10571
- } catch {
10572
- return false;
10573
- }
10574
- }
10575
- async recover(_workingDir) {
10576
- return [];
10577
- }
10578
- async listModels() {
10579
- return [];
10580
- }
10581
- }
10582
- var init_process_driver = () => {};
10583
-
10584
- // src/infrastructure/agent-drivers/opencode-process-driver.ts
10585
- import { execSync as execSync2 } from "node:child_process";
10586
- var OpenCodeProcessDriver;
10587
- var init_opencode_process_driver = __esm(() => {
10588
- init_process_driver();
10589
- init_types();
10590
- OpenCodeProcessDriver = class OpenCodeProcessDriver extends ProcessDriver {
10591
- harness = "opencode";
10592
- capabilities = {
10593
- sessionPersistence: false,
10594
- abort: false,
10595
- modelSelection: true,
10596
- compaction: false,
10597
- eventStreaming: false,
10598
- messageInjection: false,
10599
- dynamicModelDiscovery: true
10600
- };
10601
- buildSpawnConfig(options) {
10602
- const command = AGENT_HARNESS_COMMANDS[this.harness];
10603
- const combinedPrompt = buildCombinedPrompt(options.rolePrompt, options.initialMessage);
10604
- const args = ["run"];
10605
- if (options.model) {
10606
- args.push("--model", options.model);
10607
- }
10608
- return {
10609
- command,
10610
- args,
10611
- stdio: ["pipe", "inherit", "inherit"],
10612
- writePromptToStdin: true,
10613
- stdinPrompt: combinedPrompt
10614
- };
10615
- }
10616
- async listModels() {
10617
- try {
10618
- const output = execSync2("opencode models", {
10619
- stdio: ["pipe", "pipe", "pipe"],
10620
- timeout: 1e4
10621
- }).toString().trim();
10622
- if (!output)
10623
- return [];
10624
- return output.split(`
10625
- `).map((line) => line.trim()).filter((line) => line.length > 0);
10626
- } catch {
10627
- return [];
10628
- }
10629
- }
10630
- };
10631
- });
10632
-
10633
- // src/infrastructure/agent-drivers/registry.ts
10634
- class DefaultDriverRegistry {
10635
- drivers;
10636
- constructor(drivers) {
10637
- this.drivers = new Map;
10638
- for (const driver of drivers) {
10639
- this.drivers.set(driver.harness, driver);
10640
- }
10641
- }
10642
- get(harness) {
10643
- const driver = this.drivers.get(harness);
10644
- if (!driver) {
10645
- throw new Error(`No driver registered for harness: ${harness}`);
10646
- }
10647
- return driver;
10648
- }
10649
- all() {
10650
- return Array.from(this.drivers.values());
10651
- }
10652
- capabilities(harness) {
10653
- return this.get(harness).capabilities;
10654
- }
10655
- }
10656
- function getDriverRegistry() {
10657
- if (!registryInstance) {
10658
- registryInstance = new DefaultDriverRegistry([new OpenCodeProcessDriver]);
10659
- }
10660
- return registryInstance;
10661
- }
10662
- var registryInstance = null;
10663
- var init_registry = __esm(() => {
10664
- init_opencode_process_driver();
10665
- });
10666
-
10667
- // src/infrastructure/agent-drivers/index.ts
10668
- var init_agent_drivers = __esm(() => {
10669
- init_registry();
10670
- init_process_driver();
10671
- init_process_driver();
10672
- init_opencode_process_driver();
10673
- });
10674
-
10675
10714
  // src/commands/wait-for-task.ts
10676
10715
  var exports_wait_for_task = {};
10677
10716
  __export(exports_wait_for_task, {
@@ -10985,6 +11024,24 @@ ATTACHED BACKLOG (${originMessage.attachedTasks.length})`);
10985
11024
  console.log(`${attachedTask.content}`);
10986
11025
  }
10987
11026
  }
11027
+ const followUpCount = taskDeliveryPrompt.json?.contextWindow?.followUpCountSinceOrigin ?? 0;
11028
+ const originCreatedAt = taskDeliveryPrompt.json?.contextWindow?.originMessageCreatedAt;
11029
+ if (followUpCount >= 5) {
11030
+ console.log(`
11031
+ ⚠️ WARNING: ${followUpCount} follow-up messages since this pinned message.`);
11032
+ console.log(` The user may have moved on to a different topic.`);
11033
+ console.log(` Consider asking if this context is still relevant.`);
11034
+ }
11035
+ if (originCreatedAt) {
11036
+ const ageMs = Date.now() - originCreatedAt;
11037
+ const ageHours = ageMs / (1000 * 60 * 60);
11038
+ if (ageHours >= 24) {
11039
+ const ageDays = Math.floor(ageHours / 24);
11040
+ console.log(`
11041
+ ⚠️ WARNING: This pinned message is ${ageDays} day(s) old.`);
11042
+ console.log(` The context may be outdated.`);
11043
+ }
11044
+ }
10988
11045
  console.log(`</user-message>`);
10989
11046
  }
10990
11047
  console.log(`
@@ -11692,6 +11749,7 @@ var init_report_progress = __esm(() => {
11692
11749
  // src/commands/backlog.ts
11693
11750
  var exports_backlog = {};
11694
11751
  __export(exports_backlog, {
11752
+ scoreBacklog: () => scoreBacklog,
11695
11753
  resetBacklog: () => resetBacklog,
11696
11754
  reopenBacklog: () => reopenBacklog,
11697
11755
  patchBacklog: () => patchBacklog,
@@ -11990,6 +12048,70 @@ async function patchBacklog(chatroomId, options) {
11990
12048
  process.exit(1);
11991
12049
  }
11992
12050
  }
12051
+ async function scoreBacklog(chatroomId, options) {
12052
+ const client2 = await getConvexClient();
12053
+ const sessionId = getSessionId();
12054
+ if (!sessionId) {
12055
+ console.error(`❌ Not authenticated. Please run: chatroom auth login`);
12056
+ process.exit(1);
12057
+ }
12058
+ if (!chatroomId || typeof chatroomId !== "string" || chatroomId.length < 20 || chatroomId.length > 40) {
12059
+ console.error(`❌ Invalid chatroom ID format: ID must be 20-40 characters (got ${chatroomId?.length || 0})`);
12060
+ process.exit(1);
12061
+ }
12062
+ if (!options.taskId || options.taskId.trim().length === 0) {
12063
+ console.error(`❌ Task ID is required`);
12064
+ process.exit(1);
12065
+ }
12066
+ if (options.complexity === undefined && options.value === undefined && options.priority === undefined) {
12067
+ console.error(`❌ At least one of --complexity, --value, or --priority is required`);
12068
+ console.error(` Example: chatroom backlog score --task-id=... --complexity=medium --value=high`);
12069
+ process.exit(1);
12070
+ }
12071
+ const validComplexity = ["low", "medium", "high"];
12072
+ if (options.complexity !== undefined && !validComplexity.includes(options.complexity)) {
12073
+ console.error(`❌ Invalid complexity: ${options.complexity}. Must be one of: ${validComplexity.join(", ")}`);
12074
+ process.exit(1);
12075
+ }
12076
+ const validValue = ["low", "medium", "high"];
12077
+ if (options.value !== undefined && !validValue.includes(options.value)) {
12078
+ console.error(`❌ Invalid value: ${options.value}. Must be one of: ${validValue.join(", ")}`);
12079
+ process.exit(1);
12080
+ }
12081
+ let priorityNum;
12082
+ if (options.priority !== undefined) {
12083
+ priorityNum = parseInt(options.priority, 10);
12084
+ if (isNaN(priorityNum)) {
12085
+ console.error(`❌ Invalid priority: ${options.priority}. Must be a number.`);
12086
+ process.exit(1);
12087
+ }
12088
+ }
12089
+ try {
12090
+ await client2.mutation(api.tasks.patchTask, {
12091
+ sessionId,
12092
+ taskId: options.taskId,
12093
+ complexity: options.complexity,
12094
+ value: options.value,
12095
+ priority: priorityNum
12096
+ });
12097
+ console.log("");
12098
+ console.log("✅ Task scored");
12099
+ console.log(` ID: ${options.taskId}`);
12100
+ if (options.complexity !== undefined) {
12101
+ console.log(` Complexity: ${options.complexity}`);
12102
+ }
12103
+ if (options.value !== undefined) {
12104
+ console.log(` Value: ${options.value}`);
12105
+ }
12106
+ if (priorityNum !== undefined) {
12107
+ console.log(` Priority: ${priorityNum}`);
12108
+ }
12109
+ console.log("");
12110
+ } catch (error) {
12111
+ console.error(`❌ Failed to score task: ${error.message}`);
12112
+ process.exit(1);
12113
+ }
12114
+ }
11993
12115
  async function markForReviewBacklog(chatroomId, options) {
11994
12116
  const client2 = await getConvexClient();
11995
12117
  const sessionId = getSessionId();
@@ -12571,9 +12693,9 @@ var init_artifact = __esm(() => {
12571
12693
 
12572
12694
  // src/commands/machine/pid.ts
12573
12695
  import { createHash } from "node:crypto";
12574
- import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, mkdirSync as mkdirSync4 } from "node:fs";
12696
+ import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync5, unlinkSync as unlinkSync3, mkdirSync as mkdirSync4 } from "node:fs";
12575
12697
  import { homedir as homedir4 } from "node:os";
12576
- import { join as join5 } from "node:path";
12698
+ import { join as join6 } from "node:path";
12577
12699
  function getUrlHash() {
12578
12700
  const url = getConvexUrl();
12579
12701
  return createHash("sha256").update(url).digest("hex").substring(0, 8);
@@ -12587,7 +12709,7 @@ function ensureChatroomDir() {
12587
12709
  }
12588
12710
  }
12589
12711
  function getPidFilePath() {
12590
- return join5(CHATROOM_DIR4, getPidFileName());
12712
+ return join6(CHATROOM_DIR4, getPidFileName());
12591
12713
  }
12592
12714
  function isProcessRunning(pid) {
12593
12715
  try {
@@ -12616,13 +12738,13 @@ function readPid() {
12616
12738
  function writePid() {
12617
12739
  ensureChatroomDir();
12618
12740
  const pidPath = getPidFilePath();
12619
- writeFileSync4(pidPath, process.pid.toString(), "utf-8");
12741
+ writeFileSync5(pidPath, process.pid.toString(), "utf-8");
12620
12742
  }
12621
12743
  function removePid() {
12622
12744
  const pidPath = getPidFilePath();
12623
12745
  try {
12624
12746
  if (existsSync4(pidPath)) {
12625
- unlinkSync2(pidPath);
12747
+ unlinkSync3(pidPath);
12626
12748
  }
12627
12749
  } catch {}
12628
12750
  }
@@ -12652,7 +12774,7 @@ function releaseLock() {
12652
12774
  var CHATROOM_DIR4;
12653
12775
  var init_pid = __esm(() => {
12654
12776
  init_client2();
12655
- CHATROOM_DIR4 = join5(homedir4(), ".chatroom");
12777
+ CHATROOM_DIR4 = join6(homedir4(), ".chatroom");
12656
12778
  });
12657
12779
 
12658
12780
  // src/commands/machine/daemon-start.ts
@@ -12854,7 +12976,8 @@ async function handleStartAgent(ctx, command) {
12854
12976
  machineId: ctx.machineId,
12855
12977
  chatroomId,
12856
12978
  role,
12857
- pid: startResult.handle.pid
12979
+ pid: startResult.handle.pid,
12980
+ model
12858
12981
  });
12859
12982
  console.log(` Updated backend with PID: ${startResult.handle.pid}`);
12860
12983
  persistAgentPid(ctx.machineId, chatroomId, role, startResult.handle.pid, agentHarness);
@@ -12903,6 +13026,14 @@ async function handleStopAgent(ctx, command) {
12903
13026
  console.log(` ⚠️ PID ${pidToKill} does not appear to belong to the expected agent`);
12904
13027
  await clearAgentPidEverywhere(ctx, chatroomId, role);
12905
13028
  console.log(` Cleared stale PID`);
13029
+ try {
13030
+ await ctx.client.mutation(api.participants.leave, {
13031
+ sessionId: ctx.sessionId,
13032
+ chatroomId,
13033
+ role
13034
+ });
13035
+ console.log(` Removed participant record`);
13036
+ } catch {}
12906
13037
  return {
12907
13038
  result: `PID ${pidToKill} appears stale (process not found or belongs to different program)`,
12908
13039
  failed: true
@@ -12918,11 +13049,28 @@ async function handleStopAgent(ctx, command) {
12918
13049
  console.log(` ✅ ${msg}`);
12919
13050
  await clearAgentPidEverywhere(ctx, chatroomId, role);
12920
13051
  console.log(` Cleared PID`);
13052
+ try {
13053
+ await ctx.client.mutation(api.participants.leave, {
13054
+ sessionId: ctx.sessionId,
13055
+ chatroomId,
13056
+ role
13057
+ });
13058
+ console.log(` Removed participant record`);
13059
+ } catch (leaveErr) {
13060
+ console.log(` ⚠️ Could not remove participant: ${leaveErr.message}`);
13061
+ }
12921
13062
  return { result: msg, failed: false };
12922
13063
  } catch (e) {
12923
13064
  const err = e;
12924
13065
  if (err.code === "ESRCH") {
12925
13066
  await clearAgentPidEverywhere(ctx, chatroomId, role);
13067
+ try {
13068
+ await ctx.client.mutation(api.participants.leave, {
13069
+ sessionId: ctx.sessionId,
13070
+ chatroomId,
13071
+ role
13072
+ });
13073
+ } catch {}
12926
13074
  const msg2 = "Process not found (may have already exited)";
12927
13075
  console.log(` ⚠️ ${msg2}`);
12928
13076
  return { result: msg2, failed: true };
@@ -12986,6 +13134,38 @@ async function processCommand(ctx, command) {
12986
13134
  } catch {}
12987
13135
  }
12988
13136
  }
13137
+ async function discoverModels() {
13138
+ const models = [];
13139
+ try {
13140
+ const registry = getDriverRegistry();
13141
+ for (const driver of registry.all()) {
13142
+ if (driver.capabilities.dynamicModelDiscovery) {
13143
+ const driverModels = await driver.listModels();
13144
+ models.push(...driverModels);
13145
+ }
13146
+ }
13147
+ } catch {}
13148
+ return models;
13149
+ }
13150
+ async function refreshModels(ctx) {
13151
+ const models = await discoverModels();
13152
+ if (!ctx.config)
13153
+ return;
13154
+ try {
13155
+ await ctx.client.mutation(api.machines.register, {
13156
+ sessionId: ctx.sessionId,
13157
+ machineId: ctx.machineId,
13158
+ hostname: ctx.config.hostname,
13159
+ os: ctx.config.os,
13160
+ availableHarnesses: ctx.config.availableHarnesses,
13161
+ harnessVersions: ctx.config.harnessVersions,
13162
+ availableModels: models
13163
+ });
13164
+ console.log(`[${formatTimestamp()}] \uD83D\uDD04 Model refresh: ${models.length > 0 ? `${models.length} models` : "none discovered"}`);
13165
+ } catch (error) {
13166
+ console.warn(`[${formatTimestamp()}] ⚠️ Model refresh failed: ${error.message}`);
13167
+ }
13168
+ }
12989
13169
  async function initDaemon() {
12990
13170
  if (!acquireLock()) {
12991
13171
  process.exit(1);
@@ -13018,6 +13198,23 @@ Run any chatroom command first to register this machine,`);
13018
13198
  }
13019
13199
  const client2 = await getConvexClient();
13020
13200
  const typedSessionId = sessionId;
13201
+ const config3 = loadMachineConfig();
13202
+ const availableModels = await discoverModels();
13203
+ if (config3) {
13204
+ try {
13205
+ await client2.mutation(api.machines.register, {
13206
+ sessionId: typedSessionId,
13207
+ machineId,
13208
+ hostname: config3.hostname,
13209
+ os: config3.os,
13210
+ availableHarnesses: config3.availableHarnesses,
13211
+ harnessVersions: config3.harnessVersions,
13212
+ availableModels
13213
+ });
13214
+ } catch (error) {
13215
+ console.warn(`⚠️ Machine registration update failed: ${error.message}`);
13216
+ }
13217
+ }
13021
13218
  try {
13022
13219
  await client2.mutation(api.machines.updateDaemonStatus, {
13023
13220
  sessionId: typedSessionId,
@@ -13029,12 +13226,12 @@ Run any chatroom command first to register this machine,`);
13029
13226
  releaseLock();
13030
13227
  process.exit(1);
13031
13228
  }
13032
- const config3 = loadMachineConfig();
13033
13229
  const ctx = { client: client2, sessionId: typedSessionId, machineId, config: config3 };
13034
13230
  console.log(`[${formatTimestamp()}] \uD83D\uDE80 Daemon started`);
13035
13231
  console.log(` Machine ID: ${machineId}`);
13036
13232
  console.log(` Hostname: ${config3?.hostname ?? "Unknown"}`);
13037
13233
  console.log(` Available harnesses: ${config3?.availableHarnesses.join(", ") || "none"}`);
13234
+ console.log(` Available models: ${availableModels.length > 0 ? availableModels.length : "none discovered"}`);
13038
13235
  console.log(` PID: ${process.pid}`);
13039
13236
  console.log(`
13040
13237
  [${formatTimestamp()}] \uD83D\uDD04 Recovering agent state...`);
@@ -13109,12 +13306,19 @@ Listening for commands...`);
13109
13306
  enqueueCommands(parsed);
13110
13307
  await drainQueue();
13111
13308
  });
13309
+ const modelRefreshTimer = setInterval(() => {
13310
+ refreshModels(ctx).catch((err) => {
13311
+ console.warn(`[${formatTimestamp()}] ⚠️ Model refresh error: ${err.message}`);
13312
+ });
13313
+ }, MODEL_REFRESH_INTERVAL_MS);
13314
+ modelRefreshTimer.unref();
13112
13315
  return await new Promise(() => {});
13113
13316
  }
13114
13317
  async function daemonStart() {
13115
13318
  const ctx = await initDaemon();
13116
13319
  await startCommandLoop(ctx);
13117
13320
  }
13321
+ var MODEL_REFRESH_INTERVAL_MS;
13118
13322
  var init_daemon_start = __esm(() => {
13119
13323
  init_pid();
13120
13324
  init_api3();
@@ -13122,6 +13326,7 @@ var init_daemon_start = __esm(() => {
13122
13326
  init_storage();
13123
13327
  init_client2();
13124
13328
  init_machine();
13329
+ MODEL_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
13125
13330
  });
13126
13331
 
13127
13332
  // src/commands/machine/daemon-stop.ts
@@ -13809,6 +14014,11 @@ backlogCommand.command("patch-task").description("Update task scoring fields (co
13809
14014
  const { patchBacklog: patchBacklog2 } = await Promise.resolve().then(() => (init_backlog(), exports_backlog));
13810
14015
  await patchBacklog2(options.chatroomId, options);
13811
14016
  });
14017
+ backlogCommand.command("score").description("Score a backlog task by complexity, value, and priority").requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Your role").requiredOption("--task-id <taskId>", "Task ID to score").option("--complexity <level>", "Complexity level: low, medium, high").option("--value <level>", "Value level: low, medium, high").option("--priority <n>", "Priority number (higher = more important)").action(async (options) => {
14018
+ await maybeRequireAuth();
14019
+ const { scoreBacklog: scoreBacklog2 } = await Promise.resolve().then(() => (init_backlog(), exports_backlog));
14020
+ await scoreBacklog2(options.chatroomId, options);
14021
+ });
13812
14022
  backlogCommand.command("reset-task").description("Reset a stuck in_progress task back to pending").requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Your role").requiredOption("--task-id <taskId>", "Task ID to reset").action(async (options) => {
13813
14023
  await maybeRequireAuth();
13814
14024
  const { resetBacklog: resetBacklog2 } = await Promise.resolve().then(() => (init_backlog(), exports_backlog));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatroom-cli",
3
- "version": "1.0.69",
3
+ "version": "1.0.72",
4
4
  "description": "CLI for multi-agent chatroom collaboration",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,7 +17,8 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "commander": "^14.0.0",
20
- "convex": "^1.31.0"
20
+ "convex": "^1.31.0",
21
+ "convex-helpers": "^0.1.108"
21
22
  },
22
23
  "devDependencies": {
23
24
  "@types/bun": "latest",