squadrant 0.15.0 → 0.16.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.
@@ -233,22 +233,22 @@ var MINIMAL_TEMPLATE = [
233
233
  ``
234
234
  ].join("\n");
235
235
  function ensureSocketAutomation(opts = {}) {
236
- const path18 = opts.path ?? defaultCmuxConfigPath();
237
- if (!existsSync(path18)) {
238
- mkdirSync(dirname(path18), { recursive: true });
239
- writeFileSync(path18, MINIMAL_TEMPLATE);
240
- return { path: path18, changed: true, alreadySet: false };
236
+ const path19 = opts.path ?? defaultCmuxConfigPath();
237
+ if (!existsSync(path19)) {
238
+ mkdirSync(dirname(path19), { recursive: true });
239
+ writeFileSync(path19, MINIMAL_TEMPLATE);
240
+ return { path: path19, changed: true, alreadySet: false };
241
241
  }
242
- const text = readFileSync(path18, "utf-8");
242
+ const text = readFileSync(path19, "utf-8");
243
243
  const current = parse(text)?.automation?.socketControlMode;
244
244
  if (current === AUTOMATION_MODE) {
245
- return { path: path18, changed: false, alreadySet: true };
245
+ return { path: path19, changed: false, alreadySet: true };
246
246
  }
247
247
  const edits = modify(text, [...SOCKET_CONTROL_MODE_PATH], AUTOMATION_MODE, {
248
248
  formattingOptions: { insertSpaces: true, tabSize: 2 }
249
249
  });
250
- writeFileSync(path18, applyEdits(text, edits));
251
- return { path: path18, changed: true, alreadySet: false };
250
+ writeFileSync(path19, applyEdits(text, edits));
251
+ return { path: path19, changed: true, alreadySet: false };
252
252
  }
253
253
 
254
254
  // packages/shared/dist/lib/cmux-probe.js
@@ -370,9 +370,9 @@ function sleep(ms) {
370
370
  function defaultStatePath() {
371
371
  return join4(homedir3(), ".config", "squadrant", "state", "cmux-autoconfig.json");
372
372
  }
373
- function readState(path18) {
373
+ function readState(path19) {
374
374
  try {
375
- return JSON.parse(readFileSync4(path18, "utf-8"));
375
+ return JSON.parse(readFileSync4(path19, "utf-8"));
376
376
  } catch {
377
377
  return {};
378
378
  }
@@ -419,16 +419,26 @@ var compatManifest = {
419
419
  }
420
420
  };
421
421
 
422
+ // packages/shared/dist/lib/update-check.js
423
+ import fs3 from "fs";
424
+ import path3 from "path";
425
+ import os3 from "os";
426
+ import https from "https";
427
+ var UPDATE_CHECK_STATE_PATH = path3.join(os3.homedir(), ".config", "squadrant", "update-check.json");
428
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
429
+ var FAILURE_RETRY_MS = 60 * 60 * 1e3;
430
+
422
431
  // packages/shared/dist/lib/git-worktree.js
423
432
  import { execFileSync as execFileSync2 } from "child_process";
424
- import path3 from "path";
433
+ import fs4 from "fs";
434
+ import path4 from "path";
425
435
 
426
436
  // packages/shared/dist/lib/resolve-text-input.js
427
- import fs3 from "fs";
437
+ import fs5 from "fs";
428
438
 
429
439
  // packages/shared/dist/lib/runtime-sync.js
430
- import fs4 from "fs";
431
- import path4 from "path";
440
+ import fs6 from "fs";
441
+ import path5 from "path";
432
442
 
433
443
  // packages/shared/dist/lib/tool-compat.js
434
444
  function parseSemVer(v) {
@@ -462,13 +472,13 @@ function checkToolCompat(name, rawVersion, entry) {
462
472
  }
463
473
 
464
474
  // packages/shared/dist/lib/canonical-source.js
465
- import fs5 from "fs";
466
- import path5 from "path";
475
+ import fs7 from "fs";
476
+ import path6 from "path";
467
477
 
468
478
  // packages/shared/dist/lib/daily-logs.js
469
479
  import { execSync } from "child_process";
470
- import fs6 from "fs";
471
- import path6 from "path";
480
+ import fs8 from "fs";
481
+ import path7 from "path";
472
482
  import matter from "gray-matter";
473
483
 
474
484
  // packages/core/dist/state-machine.js
@@ -629,7 +639,7 @@ function formatMessage(rec, event) {
629
639
  return `CREW STALLED ${tag}: no heartbeat in ${rec.heartbeatBudgetMs}ms`;
630
640
  }
631
641
  case "awaiting-input":
632
- return `CREW IDLE ${tag}: turn ended / awaiting your input \u2014 review and reply or close.`;
642
+ return `CREW IDLE ${tag}: turn ended, awaiting your reply.`;
633
643
  default:
634
644
  return null;
635
645
  }
@@ -1010,7 +1020,7 @@ function createDaemon(deps) {
1010
1020
  }
1011
1021
 
1012
1022
  // packages/core/dist/mailbox.js
1013
- import { promises as fs7 } from "fs";
1023
+ import { promises as fs9 } from "fs";
1014
1024
  import { join as join5 } from "path";
1015
1025
  import { randomUUID } from "crypto";
1016
1026
  function inboxDir(stateRoot) {
@@ -1027,7 +1037,7 @@ async function listRotatedOldestFirst(stateRoot, project) {
1027
1037
  const dir = inboxDir(stateRoot);
1028
1038
  let entries;
1029
1039
  try {
1030
- entries = await fs7.readdir(dir);
1040
+ entries = await fs9.readdir(dir);
1031
1041
  } catch {
1032
1042
  return [];
1033
1043
  }
@@ -1036,7 +1046,7 @@ async function listRotatedOldestFirst(stateRoot, project) {
1036
1046
  }
1037
1047
  async function readMaxSeqFromFile(file) {
1038
1048
  try {
1039
- const buf = await fs7.readFile(file, "utf-8");
1049
+ const buf = await fs9.readFile(file, "utf-8");
1040
1050
  if (!buf.trim())
1041
1051
  return 0;
1042
1052
  const lines = buf.trim().split("\n");
@@ -1078,12 +1088,12 @@ function withProjectLock(project, fn) {
1078
1088
  function appendEntry(stateRoot, project, build) {
1079
1089
  return withProjectLock(project, async () => {
1080
1090
  const dir = inboxDir(stateRoot);
1081
- await fs7.mkdir(dir, { recursive: true });
1091
+ await fs9.mkdir(dir, { recursive: true });
1082
1092
  const file = logPath(stateRoot, project);
1083
1093
  const lastSeq = await readMaxSeq(stateRoot, project);
1084
1094
  const seq = lastSeq + 1;
1085
1095
  const entry = build(seq);
1086
- await fs7.appendFile(file, JSON.stringify(entry) + "\n", { encoding: "utf-8" });
1096
+ await fs9.appendFile(file, JSON.stringify(entry) + "\n", { encoding: "utf-8" });
1087
1097
  return seq;
1088
1098
  });
1089
1099
  }
@@ -1114,7 +1124,7 @@ function cursorPath(stateRoot, project, subscriber) {
1114
1124
  async function readCursor(opts) {
1115
1125
  let buf;
1116
1126
  try {
1117
- buf = await fs7.readFile(cursorPath(opts.stateRoot, opts.project, opts.subscriber), "utf-8");
1127
+ buf = await fs9.readFile(cursorPath(opts.stateRoot, opts.project, opts.subscriber), "utf-8");
1118
1128
  } catch (e) {
1119
1129
  if (e.code === "ENOENT")
1120
1130
  return null;
@@ -1129,7 +1139,7 @@ async function readCursor(opts) {
1129
1139
  }
1130
1140
  }
1131
1141
  async function writeCursor(opts) {
1132
- await fs7.mkdir(inboxDir(opts.stateRoot), { recursive: true });
1142
+ await fs9.mkdir(inboxDir(opts.stateRoot), { recursive: true });
1133
1143
  const dest = cursorPath(opts.stateRoot, opts.project, opts.subscriber);
1134
1144
  const tmp = `${dest}.${process.pid}.${randomUUID()}.tmp`;
1135
1145
  const data = {
@@ -1137,7 +1147,7 @@ async function writeCursor(opts) {
1137
1147
  subscriber: opts.subscriber,
1138
1148
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1139
1149
  };
1140
- const handle = await fs7.open(tmp, "w");
1150
+ const handle = await fs9.open(tmp, "w");
1141
1151
  try {
1142
1152
  await handle.writeFile(JSON.stringify(data), { encoding: "utf-8" });
1143
1153
  await handle.sync();
@@ -1145,9 +1155,9 @@ async function writeCursor(opts) {
1145
1155
  await handle.close();
1146
1156
  }
1147
1157
  try {
1148
- await fs7.rename(tmp, dest);
1158
+ await fs9.rename(tmp, dest);
1149
1159
  } catch (e) {
1150
- await fs7.unlink(tmp).catch(() => {
1160
+ await fs9.unlink(tmp).catch(() => {
1151
1161
  });
1152
1162
  throw e;
1153
1163
  }
@@ -1158,7 +1168,7 @@ async function* readFromCursor(opts) {
1158
1168
  for (const file of files) {
1159
1169
  let buf;
1160
1170
  try {
1161
- buf = await fs7.readFile(file, "utf-8");
1171
+ buf = await fs9.readFile(file, "utf-8");
1162
1172
  } catch (e) {
1163
1173
  if (e.code === "ENOENT")
1164
1174
  continue;
@@ -1184,7 +1194,7 @@ async function mailboxStats(stateRoot, project) {
1184
1194
  let sizeBytes = 0;
1185
1195
  for (const f of [file, ...rotated]) {
1186
1196
  try {
1187
- sizeBytes += (await fs7.stat(f)).size;
1197
+ sizeBytes += (await fs9.stat(f)).size;
1188
1198
  } catch (e) {
1189
1199
  if (e.code !== "ENOENT")
1190
1200
  throw e;
@@ -1200,7 +1210,7 @@ async function mailboxStats(stateRoot, project) {
1200
1210
  }
1201
1211
  async function oldestEntryAgeMs(file) {
1202
1212
  try {
1203
- const buf = await fs7.readFile(file, "utf-8");
1213
+ const buf = await fs9.readFile(file, "utf-8");
1204
1214
  const firstLine = buf.split("\n").find((l) => l.trim());
1205
1215
  if (!firstLine)
1206
1216
  return 0;
@@ -1215,7 +1225,7 @@ async function rotateIfNeeded(opts) {
1215
1225
  const file = logPath(opts.stateRoot, opts.project);
1216
1226
  let size = 0;
1217
1227
  try {
1218
- size = (await fs7.stat(file)).size;
1228
+ size = (await fs9.stat(file)).size;
1219
1229
  } catch (e) {
1220
1230
  if (e.code === "ENOENT")
1221
1231
  return { rotated: false };
@@ -1231,22 +1241,22 @@ async function rotateIfNeeded(opts) {
1231
1241
  const dst = `${file}.${n + 1}`;
1232
1242
  if (n + 1 > opts.keepCount) {
1233
1243
  try {
1234
- await fs7.unlink(src);
1244
+ await fs9.unlink(src);
1235
1245
  } catch (e) {
1236
1246
  if (e.code !== "ENOENT")
1237
1247
  throw e;
1238
1248
  }
1239
1249
  } else {
1240
1250
  try {
1241
- await fs7.rename(src, dst);
1251
+ await fs9.rename(src, dst);
1242
1252
  } catch (e) {
1243
1253
  if (e.code !== "ENOENT")
1244
1254
  throw e;
1245
1255
  }
1246
1256
  }
1247
1257
  }
1248
- await fs7.rename(file, `${file}.1`);
1249
- await fs7.writeFile(file, "", { encoding: "utf-8" });
1258
+ await fs9.rename(file, `${file}.1`);
1259
+ await fs9.writeFile(file, "", { encoding: "utf-8" });
1250
1260
  return { rotated: true, from: file, to: `${file}.1` };
1251
1261
  });
1252
1262
  }
@@ -2158,29 +2168,38 @@ async function runLivenessTick(deps) {
2158
2168
  return;
2159
2169
  }
2160
2170
  const seen = /* @__PURE__ */ new Set();
2171
+ const byProject = /* @__PURE__ */ new Map();
2161
2172
  for (const r of records) {
2162
2173
  if (r.role !== "captain")
2163
2174
  continue;
2164
- seen.add(r.project);
2175
+ let arr = byProject.get(r.project);
2176
+ if (!arr) {
2177
+ arr = [];
2178
+ byProject.set(r.project, arr);
2179
+ }
2180
+ arr.push(r);
2181
+ }
2182
+ for (const [project, recs] of byProject) {
2183
+ seen.add(project);
2184
+ const winner = recs.find((r) => r.pid == null || deps.isPidAlive(r.pid)) ?? recs[0];
2165
2185
  const entry = {
2166
- project: r.project,
2186
+ project,
2167
2187
  role: "captain",
2168
- pid: r.pid,
2169
- sessionId: r.sessionId,
2188
+ pid: winner.pid,
2189
+ sessionId: winner.sessionId,
2170
2190
  startedAt: now,
2171
2191
  lastState: "start",
2172
2192
  lastSeenAt: now,
2173
- pidAlive: r.pid != null ? deps.isPidAlive(r.pid) : true,
2174
- // pid:null hibernated → alive-unknown
2193
+ pidAlive: winner.pid != null ? deps.isPidAlive(winner.pid) : true,
2175
2194
  source: "runtime"
2176
2195
  };
2177
- const prev = deps.registry.get(r.project);
2196
+ const prev = deps.registry.get(project);
2178
2197
  if (prev && prev.lastState === "start")
2179
2198
  entry.startedAt = prev.startedAt;
2180
2199
  deps.registry.apply(entry);
2181
- if (r.pid != null)
2182
- deps.registry.setPidAlive(r.project, deps.isPidAlive(r.pid), now);
2183
- logEntry(deps.log, r.project, deps.registry.get(r.project));
2200
+ if (winner.pid != null)
2201
+ deps.registry.setPidAlive(project, deps.isPidAlive(winner.pid), now);
2202
+ logEntry(deps.log, project, deps.registry.get(project));
2184
2203
  }
2185
2204
  for (const e of deps.registry.all()) {
2186
2205
  if (e.role === "captain" && e.lastState === "start" && !seen.has(e.project)) {
@@ -2244,11 +2263,12 @@ function createDelivery(ctx, daemonCmux) {
2244
2263
  const allProjects = [.../* @__PURE__ */ new Set([
2245
2264
  ...Object.keys(cfg.projects ?? {}),
2246
2265
  ...Object.keys(injectedSurfaces),
2247
- ...store.listAll().map((t) => t.project)
2266
+ ...store.listAll().map((t) => t.project),
2267
+ cfg.commandName
2248
2268
  ])];
2249
2269
  for (const project of allProjects) {
2250
2270
  const projCfg = cfg.projects?.[project];
2251
- const captainTitle = projCfg?.captainName ?? `${project}-captain`;
2271
+ const captainTitle = project === cfg.commandName ? cfg.commandName : projCfg?.captainName ?? `${project}-captain`;
2252
2272
  const wsId = cmux2.findWorkspaceId ? await cmux2.findWorkspaceId(captainTitle) : null;
2253
2273
  let surface = null;
2254
2274
  if (wsId) {
@@ -2272,11 +2292,16 @@ function createDelivery(ctx, daemonCmux) {
2272
2292
  for await (const entry of readFromCursor({ stateRoot, project, fromSeq: lastAcked + 1 })) {
2273
2293
  if (new Date(entry.ts).getTime() < sessionStartMs - STALE_THRESHOLD_MS) {
2274
2294
  if (!TERMINAL_KINDS.has(entry.kind)) {
2275
- log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=stale-skipped`);
2276
- await writeCursor({ stateRoot, project, subscriber: CURSOR_SUBSCRIBER, lastAckedSeq: entry.seq });
2277
- continue;
2295
+ const isExemptMessage = entry.kind === "captain.message" && entry.payload?.source !== "daemon";
2296
+ if (!isExemptMessage) {
2297
+ log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=stale-skipped`);
2298
+ await writeCursor({ stateRoot, project, subscriber: CURSOR_SUBSCRIBER, lastAckedSeq: entry.seq });
2299
+ continue;
2300
+ }
2301
+ log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=stale-exempt-deliver`);
2302
+ } else {
2303
+ log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=stale-terminal-deliver`);
2278
2304
  }
2279
- log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=stale-terminal-deliver`);
2280
2305
  }
2281
2306
  const result = await d.deliver(entry, (text, sendOpts) => cmux2.send(surface, text, sendOpts));
2282
2307
  if ("delivered" in result) {
@@ -2389,10 +2414,10 @@ function distBuiltAt() {
2389
2414
  return 0;
2390
2415
  }
2391
2416
  }
2392
- function gatherLogStats(path18, now, windowMs) {
2417
+ function gatherLogStats(path19, now, windowMs) {
2393
2418
  let sizeBytes = 0;
2394
2419
  try {
2395
- sizeBytes = statSync2(path18).size;
2420
+ sizeBytes = statSync2(path19).size;
2396
2421
  } catch {
2397
2422
  return { errorCount: 0, sizeBytes: 0, windowMs };
2398
2423
  }
@@ -2403,7 +2428,7 @@ function gatherLogStats(path18, now, windowMs) {
2403
2428
  const len = sizeBytes - start;
2404
2429
  let text = "";
2405
2430
  try {
2406
- const fd = openSync2(path18, "r");
2431
+ const fd = openSync2(path19, "r");
2407
2432
  try {
2408
2433
  const buf = Buffer.alloc(len);
2409
2434
  readSync(fd, buf, 0, len, start);
@@ -2635,7 +2660,7 @@ function startDaemon(ctx, opts, pkgVersion) {
2635
2660
  }
2636
2661
  })();
2637
2662
  const server = createServer2(ctx, { buildHealth, gatherSnapshotInputs, cancelPromotionsFor, broadcast });
2638
- log(`started pid=${process.pid} sock=${ctx.sockPath} stateRoot=${stateRoot}`);
2663
+ log(`boot pid=${process.pid} version=${pkgVersion} socket=${ctx.sockPath} stateRoot=${stateRoot}`);
2639
2664
  let deliveryTick = initialDeliveryTick;
2640
2665
  let probeTick;
2641
2666
  if (daemonCmux) {
@@ -2696,7 +2721,8 @@ function startDaemon(ctx, opts, pkgVersion) {
2696
2721
  rotationTimer.unref?.();
2697
2722
  }
2698
2723
  return {
2699
- stop() {
2724
+ stop(reason = "requested") {
2725
+ log(`exit pid=${process.pid} reason=${reason}`);
2700
2726
  if (deliveryTimer)
2701
2727
  clearInterval(deliveryTimer);
2702
2728
  if (probeTimer)
@@ -2720,7 +2746,7 @@ function startDaemon(ctx, opts, pkgVersion) {
2720
2746
  for (const kill of ctx.activeHeadlessKills)
2721
2747
  kill();
2722
2748
  return new Promise((resolve2) => server.close(() => {
2723
- log("stopped");
2749
+ log(`exit-complete pid=${process.pid}`);
2724
2750
  resolve2();
2725
2751
  }));
2726
2752
  },
@@ -2731,8 +2757,8 @@ function startDaemon(ctx, opts, pkgVersion) {
2731
2757
 
2732
2758
  // packages/core/dist/session-freshness.js
2733
2759
  import crypto from "crypto";
2734
- import fs8 from "fs";
2735
- import path7 from "path";
2760
+ import fs10 from "fs";
2761
+ import path8 from "path";
2736
2762
 
2737
2763
  // packages/core/dist/crew-protocol.js
2738
2764
  function buildCompletionProtocol(taskId, project) {
@@ -2987,17 +3013,17 @@ function formatInbound(text) {
2987
3013
  }
2988
3014
 
2989
3015
  // packages/core/dist/telegram/state.js
2990
- import fs9 from "fs";
2991
- import path8 from "path";
3016
+ import fs11 from "fs";
3017
+ import path9 from "path";
2992
3018
  function statePath(stateRoot) {
2993
- return path8.join(stateRoot, "telegram-state.json");
3019
+ return path9.join(stateRoot, "telegram-state.json");
2994
3020
  }
2995
3021
  function topicKey(project, scope = "project") {
2996
3022
  return `${project}::${scope}`;
2997
3023
  }
2998
3024
  function loadState(stateRoot) {
2999
3025
  try {
3000
- const raw = fs9.readFileSync(statePath(stateRoot), "utf-8");
3026
+ const raw = fs11.readFileSync(statePath(stateRoot), "utf-8");
3001
3027
  const data = JSON.parse(raw);
3002
3028
  const result = {
3003
3029
  offset: typeof data.offset === "number" ? data.offset : 0,
@@ -3012,8 +3038,8 @@ function loadState(stateRoot) {
3012
3038
  }
3013
3039
  }
3014
3040
  function saveState(stateRoot, s) {
3015
- fs9.mkdirSync(stateRoot, { recursive: true });
3016
- fs9.writeFileSync(statePath(stateRoot), JSON.stringify(s, null, 2) + "\n");
3041
+ fs11.mkdirSync(stateRoot, { recursive: true });
3042
+ fs11.writeFileSync(statePath(stateRoot), JSON.stringify(s, null, 2) + "\n");
3017
3043
  }
3018
3044
  function setTopic(stateRoot, project, topicId, scope = "project") {
3019
3045
  const s = loadState(stateRoot);
@@ -3103,8 +3129,8 @@ function createTelegramClient(opts) {
3103
3129
  }
3104
3130
 
3105
3131
  // packages/core/dist/telegram/bridge.js
3106
- import os3 from "os";
3107
- import path9 from "path";
3132
+ import os4 from "os";
3133
+ import path10 from "path";
3108
3134
 
3109
3135
  // packages/core/dist/telegram/panels.js
3110
3136
  var mark = (on, label) => on ? `\u2022 ${label}` : label;
@@ -3209,7 +3235,7 @@ function notifyToggle(text) {
3209
3235
  }
3210
3236
  function createTelegramBridge(opts) {
3211
3237
  const { cfg, stateRoot, client, appendCaptainMessage: appendCaptainMessage2, log, ensureCaptainAlive, runCommand, sendReply } = opts;
3212
- const configRoot = opts.configRoot ?? path9.join(os3.homedir(), ".config", "squadrant");
3238
+ const configRoot = opts.configRoot ?? path10.join(os4.homedir(), ".config", "squadrant");
3213
3239
  const pollMs = cfg.pollMs ?? 1e3;
3214
3240
  let running = false;
3215
3241
  let lastSuccessfulPollAt = null;
@@ -3334,14 +3360,14 @@ function createTelegramBridge(opts) {
3334
3360
  }
3335
3361
  function currentEffort() {
3336
3362
  try {
3337
- return loadConfig(path9.join(configRoot, "config.json")).defaults.effort ?? "balance";
3363
+ return loadConfig(path10.join(configRoot, "config.json")).defaults.effort ?? "balance";
3338
3364
  } catch {
3339
3365
  return "balance";
3340
3366
  }
3341
3367
  }
3342
3368
  function projectNames() {
3343
3369
  try {
3344
- return Object.keys(loadConfig(path9.join(configRoot, "config.json")).projects);
3370
+ return Object.keys(loadConfig(path10.join(configRoot, "config.json")).projects);
3345
3371
  } catch {
3346
3372
  return [];
3347
3373
  }
@@ -3548,7 +3574,7 @@ function createTelegramBridge(opts) {
3548
3574
  }
3549
3575
 
3550
3576
  // packages/core/dist/telegram/setup.js
3551
- import fs10 from "fs";
3577
+ import fs12 from "fs";
3552
3578
 
3553
3579
  // packages/core/dist/restart-daemon.js
3554
3580
  import { execFileSync as execFileSync4 } from "child_process";
@@ -3564,14 +3590,14 @@ import { join as join12 } from "path";
3564
3590
  var DEFAULT_SOCK_PATH2 = join12(homedir7(), ".config", "squadrant", "squadrant.sock");
3565
3591
 
3566
3592
  // packages/core/dist/side-session.js
3567
- import fs11 from "fs";
3593
+ import fs13 from "fs";
3568
3594
 
3569
3595
  // packages/core/dist/crew-spawn.js
3570
- import fs12 from "fs";
3571
- import os4 from "os";
3572
- import path10 from "path";
3573
- var TEMPLATES_DIR = path10.join(os4.homedir(), ".config", "squadrant", "templates");
3574
- var STATE_ROOT = path10.join(os4.homedir(), ".config", "squadrant", "state");
3596
+ import fs14 from "fs";
3597
+ import os5 from "os";
3598
+ import path11 from "path";
3599
+ var TEMPLATES_DIR = path11.join(os5.homedir(), ".config", "squadrant", "templates");
3600
+ var STATE_ROOT = path11.join(os5.homedir(), ".config", "squadrant", "state");
3575
3601
 
3576
3602
  // packages/core/dist/lifecycle-source.js
3577
3603
  function reduceLifecycle(prev, next) {
@@ -3603,28 +3629,28 @@ import { execSync as execSync4 } from "child_process";
3603
3629
  import { execSync as execSync5 } from "child_process";
3604
3630
 
3605
3631
  // packages/agents/dist/drivers/launch-cmd.js
3606
- import fs13 from "fs";
3607
- import path11 from "path";
3632
+ import fs15 from "fs";
3633
+ import path12 from "path";
3608
3634
 
3609
3635
  // packages/agents/dist/projection/cursor.js
3610
3636
  import { mkdir, readFile, writeFile } from "fs/promises";
3611
- import path12 from "path";
3612
- import os5 from "os";
3637
+ import path13 from "path";
3638
+ import os6 from "os";
3613
3639
 
3614
3640
  // packages/agents/dist/projection/codex.js
3615
3641
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
3616
- import path13 from "path";
3617
- import os6 from "os";
3642
+ import path14 from "path";
3643
+ import os7 from "os";
3618
3644
 
3619
3645
  // packages/agents/dist/projection/gemini.js
3620
3646
  import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
3621
- import path14 from "path";
3622
- import os7 from "os";
3647
+ import path15 from "path";
3648
+ import os8 from "os";
3623
3649
 
3624
3650
  // packages/agents/dist/projection/opencode.js
3625
3651
  import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
3626
- import path15 from "path";
3627
- import os8 from "os";
3652
+ import path16 from "path";
3653
+ import os9 from "os";
3628
3654
 
3629
3655
  // packages/agents/dist/codex/app-server-client.js
3630
3656
  import { EventEmitter } from "events";
@@ -5053,9 +5079,9 @@ var RuntimeRegistry = class {
5053
5079
  import { execFileSync as execFileSync6, execSync as execSync7 } from "child_process";
5054
5080
 
5055
5081
  // packages/workspaces/dist/workspaces/obsidian.js
5056
- import fs14 from "fs/promises";
5082
+ import fs16 from "fs/promises";
5057
5083
  import { existsSync as existsSync9 } from "fs";
5058
- import path16 from "path";
5084
+ import path17 from "path";
5059
5085
 
5060
5086
  // packages/workspaces/dist/cmux-daemon/events-bridge.js
5061
5087
  import { spawn as nodeSpawn2 } from "child_process";
@@ -5482,9 +5508,9 @@ function defaultListFiles(dir) {
5482
5508
  return [];
5483
5509
  }
5484
5510
  }
5485
- function defaultReadFile(path18) {
5511
+ function defaultReadFile(path19) {
5486
5512
  try {
5487
- return readFileSync11(path18, "utf-8");
5513
+ return readFileSync11(path19, "utf-8");
5488
5514
  } catch {
5489
5515
  return void 0;
5490
5516
  }
@@ -5659,16 +5685,16 @@ function extractDetail(sub, payload) {
5659
5685
  }
5660
5686
  return void 0;
5661
5687
  }
5662
- function defaultReadFile2(path18) {
5688
+ function defaultReadFile2(path19) {
5663
5689
  try {
5664
- return readFileSync12(path18, "utf-8");
5690
+ return readFileSync12(path19, "utf-8");
5665
5691
  } catch {
5666
5692
  return void 0;
5667
5693
  }
5668
5694
  }
5669
- function defaultWriteFile(path18, content) {
5670
- mkdirSync6(path18.replace(/\/[^/]+$/, ""), { recursive: true });
5671
- writeFileSync8(path18, content, "utf-8");
5695
+ function defaultWriteFile(path19, content) {
5696
+ mkdirSync6(path19.replace(/\/[^/]+$/, ""), { recursive: true });
5697
+ writeFileSync8(path19, content, "utf-8");
5672
5698
  }
5673
5699
 
5674
5700
  // packages/workspaces/dist/crew-pane.js
@@ -5694,6 +5720,9 @@ async function settleInputBox(runtime, pane) {
5694
5720
  }
5695
5721
  async function confirmedSendToPane(runtime, pane, message) {
5696
5722
  const preSendScreen = await runtime.readPaneScreen(pane) ?? "";
5723
+ if (hasModalOptionList(preSendScreen)) {
5724
+ return { delivered: false, blockedByModal: true };
5725
+ }
5697
5726
  await runtime.pasteToPane(pane, message);
5698
5727
  let sawDraft = await settleInputBox(runtime, pane);
5699
5728
  await runtime.sendKeyToPane(pane, "Enter");
@@ -5736,17 +5765,17 @@ async function resendCrewFirstTurn(runtime, captainName, project, name, message)
5736
5765
  }
5737
5766
 
5738
5767
  // packages/cli/src/lib/daemon-restart-broadcast.ts
5739
- import fs15 from "fs";
5740
- import path17 from "path";
5768
+ import fs17 from "fs";
5769
+ import path18 from "path";
5741
5770
  function statePath2(stateRoot) {
5742
- return path17.join(stateRoot, "daemon-restart-state.json");
5771
+ return path18.join(stateRoot, "daemon-restart-state.json");
5743
5772
  }
5744
5773
  function computeRestartSignature(version, buildMtimeMs) {
5745
5774
  return `${version}::${buildMtimeMs}`;
5746
5775
  }
5747
5776
  function readPersistedRestartSignature(stateRoot) {
5748
5777
  try {
5749
- const raw = fs15.readFileSync(statePath2(stateRoot), "utf-8");
5778
+ const raw = fs17.readFileSync(statePath2(stateRoot), "utf-8");
5750
5779
  const data = JSON.parse(raw);
5751
5780
  return typeof data.signature === "string" ? data.signature : null;
5752
5781
  } catch {
@@ -5754,20 +5783,21 @@ function readPersistedRestartSignature(stateRoot) {
5754
5783
  }
5755
5784
  }
5756
5785
  function writePersistedRestartSignature(stateRoot, signature) {
5757
- fs15.mkdirSync(stateRoot, { recursive: true });
5758
- fs15.writeFileSync(statePath2(stateRoot), JSON.stringify({ signature }, null, 2) + "\n");
5786
+ fs17.mkdirSync(stateRoot, { recursive: true });
5787
+ fs17.writeFileSync(statePath2(stateRoot), JSON.stringify({ signature }, null, 2) + "\n");
5759
5788
  }
5760
5789
  function restartNotice(version, isDevRebuild) {
5761
5790
  const suffix = isDevRebuild ? " (dev build)" : "";
5762
5791
  return `\u26A0\uFE0F Daemon restarted \u2192 v${version}${suffix} (control-plane bounced). Re-verify in-flight crews \u2014 a crew mid-first-turn may need a crew send.`;
5763
5792
  }
5764
- async function notifyCaptainsOfDaemonRestart(version, config, driver, isDevRebuild = false) {
5793
+ async function notifyCaptainsOfDaemonRestart(version, config, driver, isDevRebuild = false, appendCaptainMessage2) {
5765
5794
  const notice = restartNotice(version, isDevRebuild);
5766
- for (const [, proj] of Object.entries(config.projects)) {
5795
+ for (const [projName] of Object.entries(config.projects)) {
5767
5796
  try {
5797
+ const proj = config.projects[projName];
5768
5798
  const ref = await driver.status(proj.captainName);
5769
5799
  if (ref) {
5770
- await driver.send(ref.id, notice);
5800
+ await appendCaptainMessage2(projName, notice);
5771
5801
  }
5772
5802
  } catch {
5773
5803
  }
@@ -5775,12 +5805,12 @@ async function notifyCaptainsOfDaemonRestart(version, config, driver, isDevRebui
5775
5805
  }
5776
5806
  async function maybeBroadcastDaemonRestart(opts) {
5777
5807
  try {
5778
- const { version, buildMtimeMs, stateRoot, config, driver } = opts;
5808
+ const { version, buildMtimeMs, stateRoot, config, driver, appendCaptainMessage: appendCaptainMessage2 } = opts;
5779
5809
  const signature = computeRestartSignature(version, buildMtimeMs);
5780
5810
  const previous = readPersistedRestartSignature(stateRoot);
5781
5811
  if (previous === signature) return;
5782
5812
  const isDevRebuild = previous !== null && previous.split("::")[0] === version;
5783
- await notifyCaptainsOfDaemonRestart(version, config, driver, isDevRebuild);
5813
+ await notifyCaptainsOfDaemonRestart(version, config, driver, isDevRebuild, appendCaptainMessage2);
5784
5814
  writePersistedRestartSignature(stateRoot, signature);
5785
5815
  } catch {
5786
5816
  }
@@ -6041,14 +6071,15 @@ ${buildCompletionProtocol(rec.id, rec.project)}`;
6041
6071
  buildMtimeMs,
6042
6072
  stateRoot,
6043
6073
  config: restartConfig,
6044
- driver: registry.global(restartConfig)
6074
+ driver: registry.global(restartConfig),
6075
+ appendCaptainMessage: (project, text) => appendCaptainMessage({ stateRoot, project, text, source: "daemon" })
6045
6076
  });
6046
6077
  } catch (e) {
6047
6078
  log(`daemon-restart broadcast setup failed: ${e.message}`);
6048
6079
  }
6049
6080
  }
6050
6081
  const origStop = h.stop.bind(h);
6051
- h.stop = async () => {
6082
+ h.stop = async (reason) => {
6052
6083
  try {
6053
6084
  cmuxStoreSource.stop();
6054
6085
  } catch {
@@ -6061,11 +6092,24 @@ ${buildCompletionProtocol(rec.id, rec.project)}`;
6061
6092
  codexAppServerSource.stop();
6062
6093
  } catch {
6063
6094
  }
6064
- return origStop();
6095
+ return origStop(reason);
6065
6096
  };
6066
6097
  return h;
6067
6098
  }
6099
+ function logCrashMarker(kind, err) {
6100
+ const message = err instanceof Error ? err.stack ?? err.message : String(err);
6101
+ process.stderr.write(`[squadrantd] ${(/* @__PURE__ */ new Date()).toISOString()} ${kind} pid=${process.pid} error=${message}
6102
+ `);
6103
+ }
6068
6104
  if (process.argv[1] && process.argv[1].endsWith("squadrantd.js")) {
6105
+ process.on("uncaughtException", (err) => {
6106
+ logCrashMarker("uncaughtException", err);
6107
+ process.exit(1);
6108
+ });
6109
+ process.on("unhandledRejection", (reason) => {
6110
+ logCrashMarker("unhandledRejection", reason);
6111
+ process.exit(1);
6112
+ });
6069
6113
  void (async () => {
6070
6114
  const arg = process.argv[2];
6071
6115
  if (arg === "--help" || arg === "-h" || arg === "--version" || arg === "-v") {
@@ -6079,10 +6123,11 @@ if (process.argv[1] && process.argv[1].endsWith("squadrantd.js")) {
6079
6123
  process.exit(0);
6080
6124
  }
6081
6125
  const h = startSquadrantd({ sweepMs: 3e4 });
6082
- process.on("SIGTERM", () => {
6083
- h.stop();
6084
- process.exit(0);
6085
- });
6126
+ const shutdown = (signal) => {
6127
+ void h.stop(signal).finally(() => process.exit(0));
6128
+ };
6129
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
6130
+ process.on("SIGINT", () => shutdown("SIGINT"));
6086
6131
  })();
6087
6132
  }
6088
6133
  export {