chatroom-cli 1.36.1 → 1.37.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 CHANGED
@@ -13426,6 +13426,10 @@ function startSessionEventForwarder(client4, options) {
13426
13426
  const errorTarget = options.errorTarget ?? process.stderr;
13427
13427
  let cancelled = false;
13428
13428
  let doneResolve;
13429
+ let sessionStarted = false;
13430
+ const seenToolStates = new Map;
13431
+ let lastStatus;
13432
+ const agentEndCallbacks = [];
13429
13433
  const donePromise = new Promise((resolve) => {
13430
13434
  doneResolve = resolve;
13431
13435
  });
@@ -13445,31 +13449,76 @@ function startSessionEventForwarder(client4, options) {
13445
13449
  const eventSession = eventSessionId(event);
13446
13450
  if (eventSession && eventSession !== options.sessionId)
13447
13451
  continue;
13448
- if (eventSession === undefined && event.type !== "session.idle" && event.type !== "session.compacted" && event.type !== "session.error" && event.type !== "session.status" && event.type !== "file.edited")
13452
+ if (eventSession === undefined && event.type !== "message.part.updated" && event.type !== "session.idle" && event.type !== "session.compacted" && event.type !== "session.error" && event.type !== "session.status" && event.type !== "file.edited")
13449
13453
  continue;
13454
+ if (!sessionStarted) {
13455
+ sessionStarted = true;
13456
+ target.write(formatLogLine(options, "session] Started", `role: ${options.role}`) + `
13457
+ `);
13458
+ }
13450
13459
  switch (event.type) {
13451
13460
  case "message.part.updated": {
13452
13461
  const props = event.properties;
13453
13462
  const part = props?.part;
13454
- if (part?.type === "text" && part.delta) {
13455
- target.write(formatLogLine(options, "text", part.delta) + `
13463
+ if (part?.type === "text") {
13464
+ const chunk = props?.delta !== undefined && props.delta !== "" ? props.delta : part.text;
13465
+ if (chunk) {
13466
+ target.write(formatLogLine(options, "text", chunk) + `
13467
+ `);
13468
+ }
13469
+ } else if (part?.type === "reasoning") {
13470
+ const chunk = props?.delta !== undefined && props.delta !== "" ? props.delta : part.text;
13471
+ if (chunk) {
13472
+ target.write(formatLogLine(options, "thinking", chunk) + `
13456
13473
  `);
13474
+ }
13457
13475
  } else if (part?.type === "tool" && part.tool) {
13458
- const state = props?.state ?? "started";
13459
- target.write(formatLogLine(options, "tool: " + part.tool, state) + `
13476
+ let appendInput = function(base, input, tool) {
13477
+ if (!input || typeof input === "object" && Object.keys(input).length === 0) {
13478
+ return base;
13479
+ }
13480
+ const inp = input;
13481
+ if (tool === "bash" && typeof inp.command === "string") {
13482
+ return `${base}: ${inp.command}`;
13483
+ }
13484
+ const inputStr = typeof inp === "string" ? inp : JSON.stringify(inp);
13485
+ return `${base}: ${inputStr}`;
13486
+ };
13487
+ const state = typeof props?.state === "string" ? props.state : typeof part.state?.status === "string" ? part.state.status : "started";
13488
+ let payload = state;
13489
+ if (part.state?.input) {
13490
+ payload = appendInput(payload, part.state.input, part.tool);
13491
+ }
13492
+ if (state === "completed" && part.state?.time?.start !== undefined && part.state?.time?.end !== undefined) {
13493
+ const duration = ((part.state.time.end - part.state.time.start) / 1000).toFixed(1);
13494
+ payload = appendInput(`${state} (${duration}s)`, part.state.input, part.tool);
13495
+ }
13496
+ const callID = part.callID ?? "unknown";
13497
+ const seenKey = `${callID}:${state}`;
13498
+ if (!seenToolStates.has(seenKey)) {
13499
+ seenToolStates.set(seenKey, payload);
13500
+ target.write(formatLogLine(options, "tool: " + part.tool, payload) + `
13460
13501
  `);
13502
+ }
13503
+ if (state === "completed" || state === "error") {
13504
+ seenToolStates.delete(seenKey);
13505
+ }
13461
13506
  }
13462
13507
  break;
13463
13508
  }
13464
13509
  case "file.edited": {
13465
13510
  const props = event.properties;
13466
- target.write(formatLogLine(options, "file", props?.file) + `
13511
+ const kind = props?.action ?? props?.kind;
13512
+ const filePayload = kind ? `${props?.file} (${kind})` : props?.file;
13513
+ target.write(formatLogLine(options, "file", filePayload) + `
13467
13514
  `);
13468
13515
  break;
13469
13516
  }
13470
13517
  case "session.idle": {
13471
13518
  target.write(formatLogLine(options, "agent_end") + `
13472
13519
  `);
13520
+ for (const cb of agentEndCallbacks)
13521
+ cb();
13473
13522
  break;
13474
13523
  }
13475
13524
  case "session.compacted": {
@@ -13479,15 +13528,25 @@ function startSessionEventForwarder(client4, options) {
13479
13528
  }
13480
13529
  case "session.status": {
13481
13530
  const props = event.properties;
13482
- target.write(formatLogLine(options, "status", props?.status?.type) + `
13531
+ const currentStatus = props?.status?.type;
13532
+ if (currentStatus !== lastStatus) {
13533
+ lastStatus = currentStatus;
13534
+ target.write(formatLogLine(options, "status", currentStatus) + `
13483
13535
  `);
13536
+ }
13484
13537
  break;
13485
13538
  }
13486
13539
  case "session.error": {
13487
13540
  const props = event.properties;
13488
13541
  const err = props?.error;
13489
13542
  const errMsg = err?.name ? `${err.name}${err?.data?.message ? ": " + err.data.message : ""}` : String(err ?? "unknown");
13490
- errorTarget.write(formatLogLine(options, "error", errMsg) + `
13543
+ let payload = errMsg;
13544
+ if (props?.tool) {
13545
+ payload += ` [tool: ${props.tool}]`;
13546
+ } else if (props?.command) {
13547
+ payload += ` [command: ${props.command}]`;
13548
+ }
13549
+ errorTarget.write(formatLogLine(options, "error", payload) + `
13491
13550
  `);
13492
13551
  break;
13493
13552
  }
@@ -13508,7 +13567,10 @@ function startSessionEventForwarder(client4, options) {
13508
13567
  stop: () => {
13509
13568
  cancelled = true;
13510
13569
  },
13511
- done: donePromise
13570
+ done: donePromise,
13571
+ onAgentEnd: (cb) => {
13572
+ agentEndCallbacks.push(cb);
13573
+ }
13512
13574
  };
13513
13575
  }
13514
13576
 
@@ -13629,7 +13691,12 @@ var init_opencode_sdk_agent_service = __esm(() => {
13629
13691
  agent: selected.name,
13630
13692
  ...composedSystem ? { system: composedSystem } : {},
13631
13693
  parts: [{ type: "text", text: prompt }],
13632
- ...modelParts ? { model: modelParts } : {}
13694
+ ...modelParts ? { model: modelParts } : {},
13695
+ tools: {
13696
+ task: false,
13697
+ question: false,
13698
+ external_directory: false
13699
+ }
13633
13700
  }
13634
13701
  }), PROMPT_ASYNC_TIMEOUT_MS, "session.promptAsync");
13635
13702
  } catch (err) {
@@ -13688,6 +13755,9 @@ var init_opencode_sdk_agent_service = __esm(() => {
13688
13755
  },
13689
13756
  onOutput: (cb) => {
13690
13757
  outputCallbacks.push(cb);
13758
+ },
13759
+ onAgentEnd: (cb) => {
13760
+ forwarder?.onAgentEnd(cb);
13691
13761
  }
13692
13762
  };
13693
13763
  }
@@ -58879,6 +58949,13 @@ function handlePing() {
58879
58949
  // src/commands/machine/daemon-start/handlers/command-runner.ts
58880
58950
  import { spawn as spawn3 } from "node:child_process";
58881
58951
  import { access as access3 } from "node:fs/promises";
58952
+ function evictStalePendingStops() {
58953
+ const evictBefore = Date.now() - PENDING_STOP_TTL_MS;
58954
+ for (const [runId, ts] of pendingStops) {
58955
+ if (ts < evictBefore)
58956
+ pendingStops.delete(runId);
58957
+ }
58958
+ }
58882
58959
  async function reportRunFailed(ctx, runId, reason) {
58883
58960
  try {
58884
58961
  await ctx.deps.backend.mutation(api.commands.updateRunStatus, {
@@ -58923,6 +59000,21 @@ async function onCommandRun(ctx, event) {
58923
59000
  console.log(`[${formatTimestamp()}] ⚠️ Command already running: ${runIdStr}`);
58924
59001
  return;
58925
59002
  }
59003
+ if (pendingStops.has(runIdStr)) {
59004
+ pendingStops.delete(runIdStr);
59005
+ console.log(`[${formatTimestamp()}] ⏭️ Skipping command run due to pending stop: ${commandName} (${runIdStr})`);
59006
+ try {
59007
+ await ctx.deps.backend.mutation(api.commands.updateRunStatus, {
59008
+ sessionId: ctx.sessionId,
59009
+ machineId: ctx.machineId,
59010
+ runId,
59011
+ status: "stopped"
59012
+ });
59013
+ } catch (err) {
59014
+ console.warn(`[${formatTimestamp()}] ⚠️ Failed to update status to stopped for pending-stop skip: ${getErrorMessage(err)}`);
59015
+ }
59016
+ return;
59017
+ }
58926
59018
  console.log(`[${formatTimestamp()}] \uD83D\uDE80 Running command: ${commandName} → ${script}`);
58927
59019
  if (!workingDir.startsWith("/")) {
58928
59020
  console.error(`[${formatTimestamp()}] ❌ Rejected command: workingDir is not absolute: ${workingDir}`);
@@ -58942,6 +59034,34 @@ async function onCommandRun(ctx, event) {
58942
59034
  stdio: ["ignore", "pipe", "pipe"],
58943
59035
  detached: true
58944
59036
  });
59037
+ const timeoutTimer = setTimeout(() => {
59038
+ console.log(`[${formatTimestamp()}] ⏰ Command timed out after ${DEFAULT_COMMAND_TIMEOUT_MS / 60000} minutes: ${commandName} (runId: ${runIdStr})`);
59039
+ const pid = child.pid;
59040
+ if (pid) {
59041
+ try {
59042
+ process.kill(-pid, "SIGTERM");
59043
+ } catch {
59044
+ child.kill("SIGTERM");
59045
+ }
59046
+ } else {
59047
+ child.kill("SIGTERM");
59048
+ }
59049
+ setTimeout(() => {
59050
+ if (!runningProcesses.has(runIdStr))
59051
+ return;
59052
+ console.log(`[${formatTimestamp()}] \uD83D\uDD2A Force-killing timed-out process: ${runIdStr}`);
59053
+ if (pid) {
59054
+ try {
59055
+ process.kill(-pid, "SIGKILL");
59056
+ } catch {
59057
+ child.kill("SIGKILL");
59058
+ }
59059
+ } else {
59060
+ child.kill("SIGKILL");
59061
+ }
59062
+ }, FORCE_KILL_DELAY_MS);
59063
+ }, DEFAULT_COMMAND_TIMEOUT_MS);
59064
+ timeoutTimer.unref?.();
58945
59065
  const tracked = {
58946
59066
  process: child,
58947
59067
  runId: runIdStr,
@@ -58949,7 +59069,8 @@ async function onCommandRun(ctx, event) {
58949
59069
  chunkIndex: 0,
58950
59070
  flushTimer: setInterval(() => {
58951
59071
  flushOutput(ctx, tracked).catch(() => {});
58952
- }, OUTPUT_FLUSH_INTERVAL_MS)
59072
+ }, OUTPUT_FLUSH_INTERVAL_MS),
59073
+ timeoutTimer
58953
59074
  };
58954
59075
  tracked.flushTimer.unref?.();
58955
59076
  runningProcesses.set(runIdStr, tracked);
@@ -58974,6 +59095,8 @@ async function onCommandRun(ctx, event) {
58974
59095
  console.log(`[${formatTimestamp()}] \uD83C\uDFC1 Command exited: ${commandName} (code=${code2}, signal=${signal})`);
58975
59096
  await flushOutput(ctx, tracked).catch(() => {});
58976
59097
  clearInterval(tracked.flushTimer);
59098
+ if (tracked.timeoutTimer)
59099
+ clearTimeout(tracked.timeoutTimer);
58977
59100
  runningProcesses.delete(runIdStr);
58978
59101
  const status = code2 === 0 ? "completed" : signal ? "stopped" : "failed";
58979
59102
  try {
@@ -58991,6 +59114,8 @@ async function onCommandRun(ctx, event) {
58991
59114
  child.on("error", async (err) => {
58992
59115
  console.error(`[${formatTimestamp()}] ❌ Command spawn failed: ${commandName}: ${err.message}`);
58993
59116
  clearInterval(tracked.flushTimer);
59117
+ if (tracked.timeoutTimer)
59118
+ clearTimeout(tracked.timeoutTimer);
58994
59119
  runningProcesses.delete(runIdStr);
58995
59120
  try {
58996
59121
  await ctx.deps.backend.mutation(api.commands.updateRunStatus, {
@@ -59009,6 +59134,8 @@ async function onCommandStop(ctx, event) {
59009
59134
  const tracked = runningProcesses.get(runIdStr);
59010
59135
  if (!tracked) {
59011
59136
  console.log(`[${formatTimestamp()}] ⚠️ No running process found for run: ${runIdStr}`);
59137
+ pendingStops.set(runIdStr, Date.now());
59138
+ console.log(`[${formatTimestamp()}] \uD83D\uDCDD Registered pending stop for run: ${runIdStr}`);
59012
59139
  try {
59013
59140
  await ctx.deps.backend.mutation(api.commands.updateRunStatus, {
59014
59141
  sessionId: ctx.sessionId,
@@ -59018,11 +59145,16 @@ async function onCommandStop(ctx, event) {
59018
59145
  });
59019
59146
  console.log(`[${formatTimestamp()}] \uD83D\uDCDD Marked orphaned run as stopped: ${runIdStr}`);
59020
59147
  } catch (err) {
59021
- console.warn(`[${formatTimestamp()}] ⚠️ Failed to mark orphaned run as stopped: ${getErrorMessage(err)}`);
59148
+ console.warn(`[${formatTimestamp()}] ⚠️ Failed to mark orphaned run as stopped (will retry): ${getErrorMessage(err)}`);
59149
+ throw err;
59022
59150
  }
59023
59151
  return;
59024
59152
  }
59025
59153
  console.log(`[${formatTimestamp()}] \uD83D\uDED1 Stopping command run: ${runIdStr}`);
59154
+ if (tracked.timeoutTimer) {
59155
+ clearTimeout(tracked.timeoutTimer);
59156
+ tracked.timeoutTimer = null;
59157
+ }
59026
59158
  const pid = tracked.process.pid;
59027
59159
  if (pid) {
59028
59160
  try {
@@ -59034,7 +59166,7 @@ async function onCommandStop(ctx, event) {
59034
59166
  tracked.process.kill("SIGTERM");
59035
59167
  }
59036
59168
  setTimeout(() => {
59037
- if (tracked.process.killed)
59169
+ if (!runningProcesses.has(runIdStr))
59038
59170
  return;
59039
59171
  console.log(`[${formatTimestamp()}] \uD83D\uDD2A Force-killing process: ${runIdStr}`);
59040
59172
  if (pid) {
@@ -59046,14 +59178,55 @@ async function onCommandStop(ctx, event) {
59046
59178
  } else {
59047
59179
  tracked.process.kill("SIGKILL");
59048
59180
  }
59049
- }, 5000);
59181
+ }, FORCE_KILL_DELAY_MS);
59050
59182
  }
59051
- var runningProcesses, OUTPUT_FLUSH_INTERVAL_MS = 3000, MAX_BUFFER_SIZE;
59183
+ async function shutdownAllCommands(ctx) {
59184
+ if (runningProcesses.size === 0)
59185
+ return;
59186
+ console.log(`[${formatTimestamp()}] Shutting down ${runningProcesses.size} running command(s)...`);
59187
+ for (const [, tracked] of runningProcesses) {
59188
+ clearInterval(tracked.flushTimer);
59189
+ if (tracked.timeoutTimer)
59190
+ clearTimeout(tracked.timeoutTimer);
59191
+ await flushOutput(ctx, tracked).catch(() => {});
59192
+ const pid = tracked.process.pid;
59193
+ if (pid) {
59194
+ try {
59195
+ process.kill(-pid, "SIGTERM");
59196
+ } catch {
59197
+ tracked.process.kill("SIGTERM");
59198
+ }
59199
+ } else {
59200
+ tracked.process.kill("SIGTERM");
59201
+ }
59202
+ }
59203
+ await new Promise((resolve5) => {
59204
+ const t = setTimeout(resolve5, 3000);
59205
+ t.unref?.();
59206
+ });
59207
+ for (const [, tracked] of runningProcesses) {
59208
+ const pid = tracked.process.pid;
59209
+ if (pid) {
59210
+ try {
59211
+ process.kill(-pid, "SIGKILL");
59212
+ } catch {}
59213
+ } else {
59214
+ try {
59215
+ tracked.process.kill("SIGKILL");
59216
+ } catch {}
59217
+ }
59218
+ }
59219
+ runningProcesses.clear();
59220
+ console.log(`[${formatTimestamp()}] All commands stopped`);
59221
+ }
59222
+ var runningProcesses, pendingStops, PENDING_STOP_TTL_MS = 60000, OUTPUT_FLUSH_INTERVAL_MS = 3000, MAX_BUFFER_SIZE, DEFAULT_COMMAND_TIMEOUT_MS, FORCE_KILL_DELAY_MS = 5000;
59052
59223
  var init_command_runner = __esm(() => {
59053
59224
  init_api3();
59054
59225
  init_convex_error();
59055
59226
  runningProcesses = new Map;
59227
+ pendingStops = new Map;
59056
59228
  MAX_BUFFER_SIZE = 100 * 1024;
59229
+ DEFAULT_COMMAND_TIMEOUT_MS = 30 * 60 * 1000;
59057
59230
  });
59058
59231
 
59059
59232
  // src/commands/machine/daemon-start/handlers/state-recovery.ts
@@ -59097,6 +59270,14 @@ var init_state_recovery = __esm(() => {
59097
59270
  init_api3();
59098
59271
  });
59099
59272
 
59273
+ // src/commands/machine/daemon-start/capabilities-snapshot.ts
59274
+ function harnessCapabilitiesFingerprint(harnesses, versions) {
59275
+ const h = [...harnesses].sort().join("\x01");
59276
+ const keys = Object.keys(versions).sort();
59277
+ const v3 = keys.map((k) => `${k}:${JSON.stringify(versions[k] ?? null)}`).join("\x02");
59278
+ return `${h}::${v3}`;
59279
+ }
59280
+
59100
59281
  // src/infrastructure/local-api/routes/identity.ts
59101
59282
  async function handleIdentity(_req, ctx) {
59102
59283
  const identity = {
@@ -60294,6 +60475,17 @@ async function recoverState(ctx) {
60294
60475
  } catch (e) {
60295
60476
  console.log(` ⚠️ Failed to clear stale PIDs: ${getErrorMessage(e)}`);
60296
60477
  }
60478
+ try {
60479
+ const runResult = await ctx.deps.backend.mutation(api.commands.clearStaleCommandRuns, {
60480
+ sessionId: ctx.sessionId,
60481
+ machineId: ctx.machineId
60482
+ });
60483
+ if (runResult.clearedCount > 0) {
60484
+ console.log(` \uD83E\uDDF9 Cleared ${runResult.clearedCount} stale command run(s) from backend`);
60485
+ }
60486
+ } catch (e) {
60487
+ console.log(` ⚠️ Failed to clear stale command runs: ${getErrorMessage(e)}`);
60488
+ }
60297
60489
  }
60298
60490
  async function initDaemon() {
60299
60491
  if (!acquireLock()) {
@@ -60344,6 +60536,7 @@ async function initDaemon() {
60344
60536
  agentServices,
60345
60537
  lastPushedGitState: new Map,
60346
60538
  lastPushedModels: availableModels,
60539
+ lastPushedHarnessFingerprint: harnessCapabilitiesFingerprint(config3.availableHarnesses, config3.harnessVersions),
60347
60540
  observedSyncEnabled: featureFlags.observedSyncEnabled ?? false
60348
60541
  };
60349
60542
  registerEventListeners(ctx);
@@ -60392,6 +60585,7 @@ var init_init2 = __esm(() => {
60392
60585
 
60393
60586
  // src/events/lifecycle/on-daemon-shutdown.ts
60394
60587
  async function onDaemonShutdown(ctx) {
60588
+ await shutdownAllCommands(ctx);
60395
60589
  const activeAgents = ctx.deps.agentProcessManager.listActive();
60396
60590
  if (activeAgents.length > 0) {
60397
60591
  console.log(`[${formatTimestamp()}] Stopping ${activeAgents.length} agent(s)...`);
@@ -60420,6 +60614,7 @@ async function onDaemonShutdown(ctx) {
60420
60614
  }
60421
60615
  var init_on_daemon_shutdown = __esm(() => {
60422
60616
  init_api3();
60617
+ init_command_runner();
60423
60618
  });
60424
60619
 
60425
60620
  // src/infrastructure/git/git-writer.ts
@@ -60611,15 +60806,18 @@ function formatModelMap(map) {
60611
60806
  return Object.entries(map).map(([harness, models]) => `${harness}: ${models.join(", ")}`).join("; ");
60612
60807
  }
60613
60808
  async function refreshModels(ctx) {
60614
- if (!ctx.config)
60615
- return;
60809
+ if (!ctx.config) {
60810
+ return { kind: "noop" };
60811
+ }
60616
60812
  const models = await discoverModels(ctx.agentServices);
60617
60813
  const freshConfig = ensureMachineRegistered();
60618
60814
  ctx.config.availableHarnesses = freshConfig.availableHarnesses;
60619
60815
  ctx.config.harnessVersions = freshConfig.harnessVersions;
60620
- const diff = diffModels(ctx.lastPushedModels, models);
60621
- if (!diff.hasChanges) {
60622
- return;
60816
+ const modelDiff = diffModels(ctx.lastPushedModels, models);
60817
+ const nextHarnessFingerprint = harnessCapabilitiesFingerprint(ctx.config.availableHarnesses, ctx.config.harnessVersions);
60818
+ const harnessFingerprintChanged = ctx.lastPushedHarnessFingerprint !== null && nextHarnessFingerprint !== ctx.lastPushedHarnessFingerprint;
60819
+ if (!modelDiff.hasChanges && !harnessFingerprintChanged) {
60820
+ return { kind: "skipped_no_changes" };
60623
60821
  }
60624
60822
  const totalCount = Object.values(models).flat().length;
60625
60823
  try {
@@ -60631,15 +60829,19 @@ async function refreshModels(ctx) {
60631
60829
  availableModels: models
60632
60830
  });
60633
60831
  ctx.lastPushedModels = models;
60634
- if (Object.keys(diff.added).length > 0) {
60635
- console.log(`[${formatTimestamp()}] New models detected — ${formatModelMap(diff.added)}`);
60832
+ ctx.lastPushedHarnessFingerprint = nextHarnessFingerprint;
60833
+ if (Object.keys(modelDiff.added).length > 0) {
60834
+ console.log(`[${formatTimestamp()}] ➕ New models detected — ${formatModelMap(modelDiff.added)}`);
60636
60835
  }
60637
- if (Object.keys(diff.removed).length > 0) {
60638
- console.log(`[${formatTimestamp()}] ➖ Models no longer available — ${formatModelMap(diff.removed)}`);
60836
+ if (Object.keys(modelDiff.removed).length > 0) {
60837
+ console.log(`[${formatTimestamp()}] ➖ Models no longer available — ${formatModelMap(modelDiff.removed)}`);
60639
60838
  }
60640
60839
  console.log(`[${formatTimestamp()}] \uD83D\uDD04 Model refresh pushed: ${totalCount > 0 ? `${totalCount} models` : "none discovered"}`);
60840
+ return { kind: "pushed" };
60641
60841
  } catch (error) {
60642
- console.warn(`[${formatTimestamp()}] ⚠️ Model refresh failed: ${getErrorMessage(error)}`);
60842
+ const message = getErrorMessage(error);
60843
+ console.warn(`[${formatTimestamp()}] ⚠️ Model refresh failed: ${message}`);
60844
+ return { kind: "failed", message };
60643
60845
  }
60644
60846
  }
60645
60847
  function evictStaleDedupEntries(tracker) {
@@ -60656,6 +60858,10 @@ function evictStaleDedupEntries(tracker) {
60656
60858
  if (ts2 < evictBefore)
60657
60859
  tracker.gitRefreshIds.delete(id);
60658
60860
  }
60861
+ for (const [id, ts2] of tracker.capabilitiesRefreshIds) {
60862
+ if (ts2 < evictBefore)
60863
+ tracker.capabilitiesRefreshIds.delete(id);
60864
+ }
60659
60865
  for (const [id, ts2] of tracker.localActionIds) {
60660
60866
  if (ts2 < evictBefore)
60661
60867
  tracker.localActionIds.delete(id);
@@ -60668,6 +60874,7 @@ function evictStaleDedupEntries(tracker) {
60668
60874
  if (ts2 < evictBefore)
60669
60875
  tracker.commandStopIds.delete(id);
60670
60876
  }
60877
+ evictStalePendingStops();
60671
60878
  }
60672
60879
  async function dispatchCommandEvent(ctx, event, tracker) {
60673
60880
  const eventId = event._id.toString();
@@ -60675,40 +60882,40 @@ async function dispatchCommandEvent(ctx, event, tracker) {
60675
60882
  if (event.type === "agent.requestStart") {
60676
60883
  if (tracker.commandIds.has(eventId))
60677
60884
  return;
60678
- tracker.commandIds.set(eventId, Date.now());
60679
60885
  await onRequestStartAgent(ctx, event);
60886
+ tracker.commandIds.set(eventId, Date.now());
60680
60887
  } else if (event.type === "agent.requestStop") {
60681
60888
  if (tracker.commandIds.has(eventId))
60682
60889
  return;
60683
- tracker.commandIds.set(eventId, Date.now());
60684
60890
  await onRequestStopAgent(ctx, event);
60891
+ tracker.commandIds.set(eventId, Date.now());
60685
60892
  } else if (event.type === "daemon.ping") {
60686
60893
  if (tracker.pingIds.has(eventId))
60687
60894
  return;
60688
- tracker.pingIds.set(eventId, Date.now());
60689
60895
  handlePing();
60690
60896
  await ctx.deps.backend.mutation(api.machines.ackPing, {
60691
60897
  sessionId: ctx.sessionId,
60692
60898
  machineId: ctx.machineId,
60693
60899
  pingEventId: event._id
60694
60900
  });
60901
+ tracker.pingIds.set(eventId, Date.now());
60695
60902
  } else if (event.type === "daemon.gitRefresh") {
60696
60903
  if (tracker.gitRefreshIds.has(eventId))
60697
60904
  return;
60698
- tracker.gitRefreshIds.set(eventId, Date.now());
60699
60905
  const stateKey = makeGitStateKey(ctx.machineId, event.workingDir);
60700
60906
  ctx.lastPushedGitState.delete(stateKey);
60701
60907
  console.log(`[${formatTimestamp()}] \uD83D\uDD04 Git refresh requested for ${event.workingDir}`);
60702
60908
  await pushGitState(ctx);
60909
+ tracker.gitRefreshIds.set(eventId, Date.now());
60703
60910
  } else if (event.type === "daemon.localAction") {
60704
60911
  if (tracker.localActionIds.has(eventId))
60705
60912
  return;
60706
- tracker.localActionIds.set(eventId, Date.now());
60707
60913
  console.log(`[${formatTimestamp()}] \uD83D\uDDA5️ Local action: ${event.action} → ${event.workingDir}`);
60708
60914
  const result = await executeLocalAction(event.action, event.workingDir);
60709
60915
  if (!result.success) {
60710
60916
  console.warn(`[${formatTimestamp()}] ⚠️ Local action failed: ${result.error}`);
60711
60917
  }
60918
+ tracker.localActionIds.set(eventId, Date.now());
60712
60919
  } else if (eventType === "command.run") {
60713
60920
  if (tracker.commandRunIds.has(eventId))
60714
60921
  return;
@@ -60717,8 +60924,42 @@ async function dispatchCommandEvent(ctx, event, tracker) {
60717
60924
  } else if (eventType === "command.stop") {
60718
60925
  if (tracker.commandStopIds.has(eventId))
60719
60926
  return;
60720
- tracker.commandStopIds.set(eventId, Date.now());
60721
60927
  await onCommandStop(ctx, event);
60928
+ tracker.commandStopIds.set(eventId, Date.now());
60929
+ } else if (event.type === "daemon.refreshCapabilities") {
60930
+ if (tracker.capabilitiesRefreshIds.has(eventId))
60931
+ return;
60932
+ console.log(`[${formatTimestamp()}] \uD83D\uDD04 Manual capabilities refresh requested`);
60933
+ const outcome = await refreshModels(ctx);
60934
+ tracker.capabilitiesRefreshIds.set(eventId, Date.now());
60935
+ const batchId = "batchId" in event && event.batchId !== undefined ? event.batchId : undefined;
60936
+ if (!batchId) {
60937
+ return;
60938
+ }
60939
+ let status;
60940
+ let errorMessage;
60941
+ if (outcome.kind === "pushed") {
60942
+ status = "completed";
60943
+ } else if (outcome.kind === "skipped_no_changes") {
60944
+ status = "skipped_no_changes";
60945
+ } else if (outcome.kind === "failed") {
60946
+ status = "failed";
60947
+ errorMessage = outcome.message;
60948
+ } else {
60949
+ status = "failed";
60950
+ errorMessage = "Daemon configuration unavailable";
60951
+ }
60952
+ try {
60953
+ await ctx.deps.backend.mutation(api.machines.reportCapabilitiesRefreshResult, {
60954
+ sessionId: ctx.sessionId,
60955
+ batchId,
60956
+ machineId: ctx.machineId,
60957
+ status,
60958
+ errorMessage
60959
+ });
60960
+ } catch (error) {
60961
+ console.warn(`[${formatTimestamp()}] ⚠️ Capabilities refresh report failed: ${getErrorMessage(error)}`);
60962
+ }
60722
60963
  }
60723
60964
  }
60724
60965
  async function startCommandLoop(ctx) {
@@ -60790,6 +61031,7 @@ Listening for commands...`);
60790
61031
  commandIds: new Map,
60791
61032
  pingIds: new Map,
60792
61033
  gitRefreshIds: new Map,
61034
+ capabilitiesRefreshIds: new Map,
60793
61035
  localActionIds: new Map,
60794
61036
  commandRunIds: new Map,
60795
61037
  commandStopIds: new Map
@@ -60810,15 +61052,8 @@ Listening for commands...`);
60810
61052
  }
60811
61053
  }
60812
61054
  });
60813
- const modelRefreshTimer = setInterval(() => {
60814
- refreshModels(ctx).catch((err) => {
60815
- console.warn(`[${formatTimestamp()}] ⚠️ Model refresh error: ${getErrorMessage(err)}`);
60816
- });
60817
- }, MODEL_REFRESH_INTERVAL_MS);
60818
- modelRefreshTimer.unref();
60819
61055
  return await new Promise(() => {});
60820
61056
  }
60821
- var MODEL_REFRESH_INTERVAL_MS;
60822
61057
  var init_command_loop = __esm(() => {
60823
61058
  init_on_request_start_agent();
60824
61059
  init_on_request_stop_agent();
@@ -60837,7 +61072,6 @@ var init_command_loop = __esm(() => {
60837
61072
  init_local_actions();
60838
61073
  init_machine();
60839
61074
  init_convex_error();
60840
- MODEL_REFRESH_INTERVAL_MS = 10 * 1000;
60841
61075
  });
60842
61076
 
60843
61077
  // src/commands/machine/daemon-start/index.ts
@@ -61934,5 +62168,5 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
61934
62168
  });
61935
62169
  program2.parse();
61936
62170
 
61937
- //# debugId=C6BB51F249BBA12864756E2164756E21
62171
+ //# debugId=1C43E3169710F16B64756E2164756E21
61938
62172
  //# sourceMappingURL=index.js.map