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