squadrant 0.19.1 → 0.19.3

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
@@ -86,22 +86,22 @@ var init_config_io = __esm({
86
86
  import path2 from "path";
87
87
  import os from "os";
88
88
  import chalk from "chalk";
89
- function isThinkingLevel(v) {
90
- return THINKING_LEVELS.includes(v);
89
+ function isThinkingLevel(v2) {
90
+ return THINKING_LEVELS.includes(v2);
91
91
  }
92
- function parseThinkingLevel(v) {
93
- if (!isThinkingLevel(v)) {
94
- throw new Error(`Invalid --thinking value '${v}'. Valid values: ${THINKING_LEVELS.join(", ")}`);
92
+ function parseThinkingLevel(v2) {
93
+ if (!isThinkingLevel(v2)) {
94
+ throw new Error(`Invalid --thinking value '${v2}'. Valid values: ${THINKING_LEVELS.join(", ")}`);
95
95
  }
96
- return v;
96
+ return v2;
97
97
  }
98
98
  function resolveControlChannelMode(cfg, agent) {
99
- const v = cfg?.[agent];
100
- return v && CONTROL_CHANNEL_MODES.has(v) ? v : "off";
99
+ const v2 = cfg?.[agent];
100
+ return v2 && CONTROL_CHANNEL_MODES.has(v2) ? v2 : "off";
101
101
  }
102
102
  function resolveCaptainChannelMode(defaults) {
103
- const v = defaults?.captainChannel;
104
- return v && CONTROL_CHANNEL_MODES.has(v) ? v : "off";
103
+ const v2 = defaults?.captainChannel;
104
+ return v2 && CONTROL_CHANNEL_MODES.has(v2) ? v2 : "off";
105
105
  }
106
106
  function getDefaultConfig() {
107
107
  return {
@@ -219,8 +219,8 @@ function deepMerge(base, patch) {
219
219
  if (patch === null || typeof patch !== "object" || Array.isArray(patch))
220
220
  return patch ?? base;
221
221
  const out = { ...base };
222
- for (const [k, v] of Object.entries(patch)) {
223
- out[k] = deepMerge(out[k], v);
222
+ for (const [k, v2] of Object.entries(patch)) {
223
+ out[k] = deepMerge(out[k], v2);
224
224
  }
225
225
  return out;
226
226
  }
@@ -730,7 +730,7 @@ function isCacheStale(state, now, intervalMs = CHECK_INTERVAL_MS, failureInterva
730
730
  return now - state.lastChecked >= (state.lastCheckFailed ? failureIntervalMs : intervalMs);
731
731
  }
732
732
  function isNewerVersion(latest, current) {
733
- const parse2 = (v) => v.trim().replace(/^v/, "").split("-")[0].split(".").map((n) => Number(n) || 0);
733
+ const parse2 = (v2) => v2.trim().replace(/^v/, "").split("-")[0].split(".").map((n) => Number(n) || 0);
734
734
  const [la = 0, lb = 0, lc = 0] = parse2(latest);
735
735
  const [ca = 0, cb = 0, cc = 0] = parse2(current);
736
736
  if (la !== ca)
@@ -1111,8 +1111,8 @@ var init_runtime_sync = __esm({
1111
1111
  });
1112
1112
 
1113
1113
  // packages/shared/dist/lib/tool-compat.js
1114
- function parseSemVer(v) {
1115
- const m = v.match(/(\d+)\.(\d+)\.(\d+)/);
1114
+ function parseSemVer(v2) {
1115
+ const m = v2.match(/(\d+)\.(\d+)\.(\d+)/);
1116
1116
  if (!m)
1117
1117
  return null;
1118
1118
  return [parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10)];
@@ -2523,12 +2523,12 @@ function isDaemonSocketLive(sockPath, timeoutMs = 500) {
2523
2523
  return;
2524
2524
  }
2525
2525
  const conn = createConnection(sockPath);
2526
- const finish = (v) => {
2526
+ const finish = (v2) => {
2527
2527
  try {
2528
2528
  conn.destroy();
2529
2529
  } catch {
2530
2530
  }
2531
- resolve4(v);
2531
+ resolve4(v2);
2532
2532
  };
2533
2533
  const timer = setTimeout(() => finish(false), timeoutMs);
2534
2534
  conn.on("connect", () => {
@@ -3194,17 +3194,20 @@ function getDaemonPid(target) {
3194
3194
  function forceKickstartAndVerify(target, opts = {}) {
3195
3195
  const pollAttempts = opts.pollAttempts ?? 15;
3196
3196
  const pollDelayMs = opts.pollDelayMs ?? 300;
3197
- const kickstartRetries = opts.kickstartRetries ?? 5;
3197
+ const kickstartRetries = opts.kickstartRetries ?? 10;
3198
3198
  const kickstartRetryDelayMs = opts.kickstartRetryDelayMs ?? 300;
3199
3199
  const pidBefore = getDaemonPid(target);
3200
+ let kickstartError = null;
3200
3201
  for (let i = 0; i < kickstartRetries; i++) {
3201
3202
  try {
3202
3203
  execFileSync3("launchctl", ["kickstart", "-k", target], { stdio: "ignore" });
3204
+ kickstartError = null;
3203
3205
  break;
3204
3206
  } catch (e) {
3205
- if (i === kickstartRetries - 1)
3206
- throw e;
3207
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, kickstartRetryDelayMs);
3207
+ kickstartError = e;
3208
+ if (i < kickstartRetries - 1) {
3209
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, kickstartRetryDelayMs);
3210
+ }
3208
3211
  }
3209
3212
  }
3210
3213
  let pidAfter = null;
@@ -3216,7 +3219,16 @@ function forceKickstartAndVerify(target, opts = {}) {
3216
3219
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, pollDelayMs);
3217
3220
  }
3218
3221
  }
3219
- return { target, pidBefore, pidAfter, restarted: pidAfter !== null && pidAfter !== pidBefore };
3222
+ const restarted = pidAfter !== null && pidAfter !== pidBefore;
3223
+ if (kickstartError && !restarted)
3224
+ throw kickstartError;
3225
+ return {
3226
+ target,
3227
+ pidBefore,
3228
+ pidAfter,
3229
+ restarted,
3230
+ ...kickstartError ? { note: "kickstart -k refused; daemon restarted by bootstrap" } : {}
3231
+ };
3220
3232
  }
3221
3233
  function isOperatorInitiatedCommand(topLevelArg) {
3222
3234
  return topLevelArg !== void 0 && OPERATOR_INITIATED_COMMANDS.has(topLevelArg);
@@ -4125,6 +4137,38 @@ import fs9 from "fs";
4125
4137
  import os4 from "os";
4126
4138
  import path9 from "path";
4127
4139
  import { randomUUID as randomUUID3 } from "crypto";
4140
+ function ensureSocksDir(dir = CC_SOCKS_DIR) {
4141
+ fs9.mkdirSync(dir, { recursive: true, mode: 448 });
4142
+ if ((fs9.statSync(dir).mode & 511) !== 448)
4143
+ fs9.chmodSync(dir, 448);
4144
+ }
4145
+ async function pollFirstTurnConfirmedAt(getTaskRecord, project, id) {
4146
+ const deadline = Date.now() + FIRST_TURN_HOOK_CONFIRM_WINDOW_MS;
4147
+ for (; ; ) {
4148
+ const rec = await getTaskRecord(project, id).catch(() => void 0);
4149
+ if (rec?.firstTurnConfirmedAt)
4150
+ return true;
4151
+ if (Date.now() >= deadline)
4152
+ return false;
4153
+ await new Promise((r) => setTimeout(r, FIRST_TURN_HOOK_POLL_INTERVAL_MS));
4154
+ }
4155
+ }
4156
+ function firstTrueOrBothFalse(a, b) {
4157
+ return new Promise((resolve4) => {
4158
+ let settledFalseCount = 0;
4159
+ const onSettle = (ok2) => {
4160
+ if (ok2) {
4161
+ resolve4(true);
4162
+ return;
4163
+ }
4164
+ settledFalseCount++;
4165
+ if (settledFalseCount === 2)
4166
+ resolve4(false);
4167
+ };
4168
+ a.then(onSettle, () => onSettle(false));
4169
+ b.then(onSettle, () => onSettle(false));
4170
+ });
4171
+ }
4128
4172
  async function listCrewPanes(runtime, workspaceId, project) {
4129
4173
  const surfaces = await runtime.listSurfaces(workspaceId);
4130
4174
  return surfaces.filter((s) => s.title && isCrewTitle(project, s.title));
@@ -4235,7 +4279,7 @@ async function runCrewSpawn(input, config, deps) {
4235
4279
  deps.onModelResolved?.({ agentName, model: crewModel });
4236
4280
  }
4237
4281
  if (agentName === "claude") {
4238
- fs9.mkdirSync(CC_SOCKS_DIR, { recursive: true });
4282
+ ensureSocksDir();
4239
4283
  const messagingSocketPath = path9.join(CC_SOCKS_DIR, `squadrant-${randomUUID3()}.sock`);
4240
4284
  const rec = await deps.dispatchCrew({
4241
4285
  provider: "claude",
@@ -4281,10 +4325,12 @@ async function runCrewSpawn(input, config, deps) {
4281
4325
  fs9.writeFileSync(spillFile, claudeFirstTurn, "utf8");
4282
4326
  claudeFirstTurn = `Full task is at ${spillFile} \u2014 cat it and follow it exactly.`;
4283
4327
  }
4284
- const claudeResult = await deps.sendFirstTurn(pane2, `${claudeFirstTurn}
4328
+ const sendPromise = deps.sendFirstTurn(pane2, `${claudeFirstTurn}
4285
4329
 
4286
4330
  ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
4287
- if (!claudeResult.delivered) {
4331
+ const scrapeDelivered = sendPromise.then((r) => r.delivered).catch(() => false);
4332
+ const delivered = hooksInstalled && deps.getTaskRecord ? await firstTrueOrBothFalse(scrapeDelivered, pollFirstTurnConfirmedAt(deps.getTaskRecord, input.project, rec.id)) : await scrapeDelivered;
4333
+ if (!delivered) {
4288
4334
  process.stderr.write(`\u26A0\uFE0F First turn not delivered for crew '${name}' \u2014 use 'squadrant crew send ${input.project} ${name}' to re-send the task.
4289
4335
  `);
4290
4336
  } else if (!hooksInstalled) {
@@ -4395,6 +4441,10 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps, o
4395
4441
  const heldForMin = Math.round((Date.now() - task.operatorHold.since) / 6e4);
4396
4442
  throw new Error(`Crew '${name}' is under operator takeover (held ${heldForMin}m${task.operatorHold.note ? `: ${task.operatorHold.note}` : ""}). The operator is working in that tab \u2014 sending a message disrupts their conversation. Ask them to run 'squadrant crew handback ${project} ${name}', or pass --force if they told you to.`);
4397
4443
  }
4444
+ const isAttentionState = task?.state === "blocked" || task?.state === "awaiting-input" || task?.state === "review";
4445
+ if (task && !isAttentionState && task.firstTurnConfirmedAt && task.task === message) {
4446
+ throw new Error(`Crew '${name}' already confirmed receipt of this task \u2014 its first turn was delivered and is not being re-sent to avoid running it twice. If you have new instructions, send different text.`);
4447
+ }
4398
4448
  let reopened = false;
4399
4449
  try {
4400
4450
  if (task) {
@@ -4561,7 +4611,7 @@ async function runCrewList(project, runtime, workspaceId) {
4561
4611
  surfaceId: c.surfaceId
4562
4612
  }));
4563
4613
  }
4564
- var CC_SOCKS_DIR, FIRST_TURN_INLINE_MAX_BYTES, TEMPLATES_DIR, STATE_ROOT, CLOSE_LOOKUP_RETRIES, CLOSE_LOOKUP_RETRY_DELAY_MS;
4614
+ var CC_SOCKS_DIR, FIRST_TURN_INLINE_MAX_BYTES, TEMPLATES_DIR, STATE_ROOT, FIRST_TURN_HOOK_CONFIRM_WINDOW_MS, FIRST_TURN_HOOK_POLL_INTERVAL_MS, CLOSE_LOOKUP_RETRIES, CLOSE_LOOKUP_RETRY_DELAY_MS;
4565
4615
  var init_crew_spawn = __esm({
4566
4616
  "packages/core/dist/crew-spawn.js"() {
4567
4617
  init_control_channel();
@@ -4573,6 +4623,8 @@ var init_crew_spawn = __esm({
4573
4623
  FIRST_TURN_INLINE_MAX_BYTES = 1200;
4574
4624
  TEMPLATES_DIR = path9.join(os4.homedir(), ".config", "squadrant", "templates");
4575
4625
  STATE_ROOT = path9.join(os4.homedir(), ".config", "squadrant", "state");
4626
+ FIRST_TURN_HOOK_CONFIRM_WINDOW_MS = 1e5;
4627
+ FIRST_TURN_HOOK_POLL_INTERVAL_MS = 2e3;
4576
4628
  CLOSE_LOOKUP_RETRIES = 3;
4577
4629
  CLOSE_LOOKUP_RETRY_DELAY_MS = 150;
4578
4630
  }
@@ -4742,9 +4794,9 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
4742
4794
  const lastDeferred = /* @__PURE__ */ new Map();
4743
4795
  const inFlightDelivery = () => {
4744
4796
  let worst2 = null;
4745
- for (const [project, v] of lastDeferred) {
4746
- if (!worst2 || v.deferCount > worst2.deferCount)
4747
- worst2 = { project, ...v };
4797
+ for (const [project, v2] of lastDeferred) {
4798
+ if (!worst2 || v2.deferCount > worst2.deferCount)
4799
+ worst2 = { project, ...v2 };
4748
4800
  }
4749
4801
  return worst2;
4750
4802
  };
@@ -7108,6 +7160,326 @@ var init_lifecycle_source = __esm({
7108
7160
  }
7109
7161
  });
7110
7162
 
7163
+ // packages/core/dist/events/fact.js
7164
+ function stampFact(raw, id) {
7165
+ return { ...raw, ...id };
7166
+ }
7167
+ var init_fact = __esm({
7168
+ "packages/core/dist/events/fact.js"() {
7169
+ }
7170
+ });
7171
+
7172
+ // packages/core/dist/events/log.js
7173
+ var FactLog;
7174
+ var init_log = __esm({
7175
+ "packages/core/dist/events/log.js"() {
7176
+ FactLog = class {
7177
+ capacity;
7178
+ buffers = /* @__PURE__ */ new Map();
7179
+ constructor(opts = {}) {
7180
+ this.capacity = opts.capacity ?? 256;
7181
+ }
7182
+ push(fact) {
7183
+ let buf = this.buffers.get(fact.taskId);
7184
+ if (!buf) {
7185
+ buf = [];
7186
+ this.buffers.set(fact.taskId, buf);
7187
+ }
7188
+ buf.push(fact);
7189
+ while (buf.length > this.capacity)
7190
+ buf.shift();
7191
+ }
7192
+ /** Oldest-first snapshot. A fresh array; later pushes never grow it. */
7193
+ recent(taskId) {
7194
+ return [...this.buffers.get(taskId) ?? []];
7195
+ }
7196
+ /** Newline-delimited JSON, one fact per line, oldest first. */
7197
+ serialize(taskId) {
7198
+ return this.recent(taskId).map((f) => JSON.stringify(f)).join("\n") + "\n";
7199
+ }
7200
+ /** Release a finished crew's buffer. */
7201
+ drop(taskId) {
7202
+ this.buffers.delete(taskId);
7203
+ }
7204
+ };
7205
+ }
7206
+ });
7207
+
7208
+ // packages/core/dist/events/invariant.js
7209
+ function freshTrace() {
7210
+ return {
7211
+ depth: 0,
7212
+ oldestOpenAt: null,
7213
+ stallReported: false,
7214
+ unknownSeen: 0,
7215
+ liveness: /* @__PURE__ */ new Map()
7216
+ };
7217
+ }
7218
+ function checkFact(trace, fact, opts) {
7219
+ const out = [];
7220
+ if (fact.origin === "inferred" && TERMINALISING.has(fact.kind)) {
7221
+ out.push(v("I4", `inferred fact "${fact.kind}" from ${fact.source} cannot terminalise alone`, fact));
7222
+ }
7223
+ switch (fact.kind) {
7224
+ case "tool.opened":
7225
+ if (trace.depth === 0) {
7226
+ trace.oldestOpenAt = fact.at;
7227
+ trace.stallReported = false;
7228
+ }
7229
+ trace.depth += 1;
7230
+ break;
7231
+ case "tool.closed":
7232
+ if (trace.depth === 0) {
7233
+ out.push(v("I1", `tool.closed from ${fact.source} with no open tool`, fact));
7234
+ } else {
7235
+ trace.depth -= 1;
7236
+ if (trace.depth === 0) {
7237
+ trace.oldestOpenAt = null;
7238
+ trace.stallReported = false;
7239
+ }
7240
+ }
7241
+ break;
7242
+ case "turn.ended":
7243
+ if (trace.depth > 0) {
7244
+ out.push(v("I2", `turn.ended with ${trace.depth} tool call(s) still open`, fact));
7245
+ trace.depth = 0;
7246
+ trace.oldestOpenAt = null;
7247
+ trace.stallReported = false;
7248
+ }
7249
+ break;
7250
+ case "unknown":
7251
+ trace.unknownSeen += 1;
7252
+ out.push(v("I5", `unrecognised frame "${fact.name}" from ${fact.source}`, fact));
7253
+ break;
7254
+ case "process.observed": {
7255
+ const prior = [...trace.liveness.entries()].find(([src, s]) => src !== fact.source && s.alive !== fact.alive && fact.at - s.at <= (opts.disagreeWindowMs ?? -1));
7256
+ if (prior) {
7257
+ out.push(v("I6", `liveness disagreement: ${prior[0]} said alive=${prior[1].alive}, ${fact.source} says alive=${fact.alive}`, fact));
7258
+ }
7259
+ trace.liveness.set(fact.source, { alive: fact.alive, at: fact.at });
7260
+ break;
7261
+ }
7262
+ default:
7263
+ break;
7264
+ }
7265
+ if (opts.stallBudgetMs !== void 0 && trace.depth > 0 && trace.oldestOpenAt !== null && !trace.stallReported && fact.at - trace.oldestOpenAt > opts.stallBudgetMs) {
7266
+ trace.stallReported = true;
7267
+ out.push(v("I3", `tool open for ${fact.at - trace.oldestOpenAt}ms, past the stall budget`, fact));
7268
+ }
7269
+ return out;
7270
+ }
7271
+ var v, TERMINALISING;
7272
+ var init_invariant = __esm({
7273
+ "packages/core/dist/events/invariant.js"() {
7274
+ v = (code, message, f) => ({ code, message, taskId: f.taskId, at: f.at });
7275
+ TERMINALISING = /* @__PURE__ */ new Set(["session.ended"]);
7276
+ }
7277
+ });
7278
+
7279
+ // packages/core/dist/events/to-control-event.js
7280
+ function toControlEvent(fact) {
7281
+ if (fact.origin === "inferred" && TERMINALISING2.has(fact.kind))
7282
+ return [];
7283
+ switch (fact.kind) {
7284
+ case "turn.ended":
7285
+ return [{ type: "task.turn.completed", id: fact.taskId, turnId: fact.turnId ?? fact.source }];
7286
+ case "permission.requested":
7287
+ return [{
7288
+ type: "task.approval.requested",
7289
+ id: fact.taskId,
7290
+ requestId: fact.requestId,
7291
+ question: fact.question,
7292
+ kind: fact.tool
7293
+ }];
7294
+ case "input.requested":
7295
+ return [{
7296
+ type: "task.input.requested",
7297
+ id: fact.taskId,
7298
+ requestId: fact.requestId,
7299
+ question: fact.question
7300
+ }];
7301
+ case "session.ended":
7302
+ return [{ type: "task.session.ended", id: fact.taskId }];
7303
+ case "session.started":
7304
+ return [{
7305
+ type: "task.started",
7306
+ id: fact.taskId,
7307
+ ...fact.pid === void 0 ? {} : { pid: fact.pid },
7308
+ ...fact.sessionId === void 0 ? {} : { sessionId: fact.sessionId }
7309
+ }];
7310
+ case "prompt.submitted":
7311
+ return [{ type: "task.first-turn.confirmed", id: fact.taskId }];
7312
+ // Liveness-only. The facade still feeds these to reduceLifecycle; they
7313
+ // simply carry no ControlEvent of their own.
7314
+ case "tool.opened":
7315
+ case "tool.closed":
7316
+ case "activity":
7317
+ case "process.observed":
7318
+ case "unknown":
7319
+ return [];
7320
+ }
7321
+ }
7322
+ var TERMINALISING2;
7323
+ var init_to_control_event = __esm({
7324
+ "packages/core/dist/events/to-control-event.js"() {
7325
+ TERMINALISING2 = /* @__PURE__ */ new Set(["session.ended"]);
7326
+ }
7327
+ });
7328
+
7329
+ // packages/core/dist/events/conformance.js
7330
+ function assert(cond, msg) {
7331
+ if (!cond)
7332
+ throw new Error(`conformance: ${msg}`);
7333
+ }
7334
+ function runAdapterConformance(adapter, samples) {
7335
+ const call = (raw) => adapter.translate(raw);
7336
+ return [
7337
+ {
7338
+ name: `${adapter.name}: never throws on garbage`,
7339
+ run: () => {
7340
+ for (const g of GARBAGE) {
7341
+ try {
7342
+ call(g);
7343
+ } catch (e) {
7344
+ throw new Error(`threw on ${JSON.stringify(g)}: ${String(e)}`);
7345
+ }
7346
+ }
7347
+ }
7348
+ },
7349
+ {
7350
+ name: `${adapter.name}: never returns null or undefined`,
7351
+ run: () => {
7352
+ for (const g of [...GARBAGE, ...samples]) {
7353
+ const out = call(g);
7354
+ assert(Array.isArray(out), `returned a non-array for ${JSON.stringify(g)}`);
7355
+ }
7356
+ }
7357
+ },
7358
+ {
7359
+ name: `${adapter.name}: an unrecognised frame yields unknown, not an empty array`,
7360
+ run: () => {
7361
+ const out = call({ type: "definitely-not-a-real-event-name" });
7362
+ assert(out.length > 0, "silently dropped an unrecognised frame (the #542 shape)");
7363
+ assert(out.every((f) => f.kind === "unknown"), "an unrecognised frame must translate to kind 'unknown'");
7364
+ }
7365
+ },
7366
+ {
7367
+ name: `${adapter.name}: recognises its own samples`,
7368
+ run: () => {
7369
+ for (const s of samples) {
7370
+ const out = call(s);
7371
+ assert(out.length > 0, `produced nothing for its own sample ${JSON.stringify(s)}`);
7372
+ assert(out.some((f) => f.kind !== "unknown"), `failed to recognise its own sample ${JSON.stringify(s)}`);
7373
+ }
7374
+ }
7375
+ },
7376
+ {
7377
+ name: `${adapter.name}: declares a constant origin`,
7378
+ run: () => {
7379
+ assert(adapter.origin === "agent" || adapter.origin === "scan" || adapter.origin === "inferred", `invalid origin "${String(adapter.origin)}"`);
7380
+ }
7381
+ }
7382
+ ];
7383
+ }
7384
+ var GARBAGE;
7385
+ var init_conformance = __esm({
7386
+ "packages/core/dist/events/conformance.js"() {
7387
+ GARBAGE = [
7388
+ null,
7389
+ void 0,
7390
+ 0,
7391
+ "",
7392
+ "not json",
7393
+ [],
7394
+ {},
7395
+ { type: 42 },
7396
+ { type: "definitely-not-a-real-event-name" }
7397
+ ];
7398
+ }
7399
+ });
7400
+
7401
+ // packages/core/dist/events/source.js
7402
+ function createEventsSource(opts) {
7403
+ const now = opts.now ?? (() => Date.now());
7404
+ const log = new FactLog({ capacity: opts.capacity });
7405
+ const adapters = new Map(opts.adapters.map((a) => [a.name, a]));
7406
+ const traces = /* @__PURE__ */ new Map();
7407
+ const seqs = /* @__PURE__ */ new Map();
7408
+ let deps;
7409
+ const traceFor = (taskId) => {
7410
+ let t = traces.get(taskId);
7411
+ if (!t) {
7412
+ t = freshTrace();
7413
+ traces.set(taskId, t);
7414
+ }
7415
+ return t;
7416
+ };
7417
+ const nextSeq = (taskId) => {
7418
+ const n = seqs.get(taskId) ?? 0;
7419
+ seqs.set(taskId, n + 1);
7420
+ return n;
7421
+ };
7422
+ return {
7423
+ name: "events",
7424
+ start(d) {
7425
+ deps = d;
7426
+ },
7427
+ stop() {
7428
+ deps = void 0;
7429
+ },
7430
+ health() {
7431
+ return { active: deps !== void 0, error: null };
7432
+ },
7433
+ recent(taskId) {
7434
+ return log.recent(taskId);
7435
+ },
7436
+ dump(taskId) {
7437
+ return log.serialize(taskId);
7438
+ },
7439
+ ingest(source, raw, hint) {
7440
+ const adapter = adapters.get(source);
7441
+ if (!adapter || !deps)
7442
+ return;
7443
+ const rec = deps.resolve(hint);
7444
+ if (!rec)
7445
+ return;
7446
+ const taskId = rec.id;
7447
+ const at = now();
7448
+ let produced;
7449
+ try {
7450
+ const out = adapter.translate(raw);
7451
+ produced = Array.isArray(out) ? out : [{ kind: "unknown", name: `${source} returned non-array` }];
7452
+ } catch (e) {
7453
+ opts.log?.(`events: adapter ${source} threw: ${String(e)}`);
7454
+ produced = [{ kind: "unknown", name: `${source} threw` }];
7455
+ }
7456
+ for (const rawFact of produced) {
7457
+ const fact = stampFact(rawFact, {
7458
+ seq: nextSeq(taskId),
7459
+ taskId,
7460
+ at,
7461
+ source,
7462
+ origin: adapter.origin
7463
+ });
7464
+ log.push(fact);
7465
+ for (const v2 of checkFact(traceFor(taskId), fact, opts.check ?? {})) {
7466
+ opts.onViolation(v2);
7467
+ }
7468
+ for (const ev of toControlEvent(fact))
7469
+ opts.emit(ev);
7470
+ }
7471
+ }
7472
+ };
7473
+ }
7474
+ var init_source = __esm({
7475
+ "packages/core/dist/events/source.js"() {
7476
+ init_fact();
7477
+ init_log();
7478
+ init_invariant();
7479
+ init_to_control_event();
7480
+ }
7481
+ });
7482
+
7111
7483
  // packages/core/dist/index.js
7112
7484
  var dist_exports2 = {};
7113
7485
  __export(dist_exports2, {
@@ -7122,6 +7494,7 @@ __export(dist_exports2, {
7122
7494
  DEFAULT_TASK_TIMEOUT_MS: () => DEFAULT_TASK_TIMEOUT_MS,
7123
7495
  DeferDelivery: () => DeferDelivery,
7124
7496
  FIRST_TURN_INLINE_MAX_BYTES: () => FIRST_TURN_INLINE_MAX_BYTES,
7497
+ FactLog: () => FactLog,
7125
7498
  GROUP_DISPATCH_WARMUP_POLL_MS: () => GROUP_DISPATCH_WARMUP_POLL_MS,
7126
7499
  GROUP_DISPATCH_WARMUP_TIMEOUT_MS: () => GROUP_DISPATCH_WARMUP_TIMEOUT_MS,
7127
7500
  IDLE_DEBOUNCE_MS: () => IDLE_DEBOUNCE_MS,
@@ -7151,6 +7524,7 @@ __export(dist_exports2, {
7151
7524
  capAllowed: () => capAllowed,
7152
7525
  capOutput: () => capOutput,
7153
7526
  captainSocketPath: () => captainSocketPath,
7527
+ checkFact: () => checkFact,
7154
7528
  classifyHealth: () => classifyHealth,
7155
7529
  closeWorkItem: () => closeWorkItem,
7156
7530
  computeTemplateHash: () => computeTemplateHash,
@@ -7163,6 +7537,7 @@ __export(dist_exports2, {
7163
7537
  createDirectCrewPaneReader: () => createDirectCrewPaneReader,
7164
7538
  createDirectSurfaceLivenessProbe: () => createDirectSurfaceLivenessProbe,
7165
7539
  createEnsureCaptainAlive: () => createEnsureCaptainAlive,
7540
+ createEventsSource: () => createEventsSource,
7166
7541
  createInteractiveProbe: () => createInteractiveProbe,
7167
7542
  createIsCaptainAlive: () => createIsCaptainAlive,
7168
7543
  createLaunch: () => createLaunch,
@@ -7194,6 +7569,7 @@ __export(dist_exports2, {
7194
7569
  encodeFrame: () => encodeFrame,
7195
7570
  encodeMsg: () => encodeMsg,
7196
7571
  ensureDaemon: () => ensureDaemon,
7572
+ ensureSocksDir: () => ensureSocksDir,
7197
7573
  evaluateStall: () => evaluateStall,
7198
7574
  exitMarkerPath: () => exitMarkerPath,
7199
7575
  fallsBackToPane: () => fallsBackToPane,
@@ -7205,6 +7581,7 @@ __export(dist_exports2, {
7205
7581
  formatInbound: () => formatInbound,
7206
7582
  formatInboundReceipt: () => formatInboundReceipt,
7207
7583
  formatLifecycle: () => formatLifecycle,
7584
+ freshTrace: () => freshTrace,
7208
7585
  getDaemonPid: () => getDaemonPid,
7209
7586
  healCmdFor: () => healCmdFor,
7210
7587
  isAuthorized: () => isAuthorized,
@@ -7261,6 +7638,7 @@ __export(dist_exports2, {
7261
7638
  resolveSetupUserId: () => resolveSetupUserId,
7262
7639
  restartDaemonIfRunning: () => restartDaemonIfRunning,
7263
7640
  rotateIfNeeded: () => rotateIfNeeded,
7641
+ runAdapterConformance: () => runAdapterConformance,
7264
7642
  runCrewAnswer: () => runCrewAnswer,
7265
7643
  runCrewClose: () => runCrewClose,
7266
7644
  runCrewList: () => runCrewList,
@@ -7295,12 +7673,14 @@ __export(dist_exports2, {
7295
7673
  sideNameFromTitle: () => sideNameFromTitle,
7296
7674
  sideNextAutoName: () => sideNextAutoName,
7297
7675
  sideTitleFor: () => sideTitleFor,
7676
+ stampFact: () => stampFact,
7298
7677
  startDaemon: () => startDaemon,
7299
7678
  startServer: () => startServer,
7300
7679
  stripBotMention: () => stripBotMention,
7301
7680
  surfaceVerdict: () => surfaceVerdict,
7302
7681
  timeoutGate: () => timeoutGate,
7303
7682
  titleFor: () => titleFor,
7683
+ toControlEvent: () => toControlEvent,
7304
7684
  topicKey: () => topicKey,
7305
7685
  topicName: () => topicName,
7306
7686
  tryAcquireDaemonLock: () => tryAcquireDaemonLock,
@@ -7347,6 +7727,12 @@ var init_dist2 = __esm({
7347
7727
  init_crew_spawn();
7348
7728
  init_crew_answer();
7349
7729
  init_lifecycle_source();
7730
+ init_fact();
7731
+ init_log();
7732
+ init_invariant();
7733
+ init_to_control_event();
7734
+ init_conformance();
7735
+ init_source();
7350
7736
  init_control_channel();
7351
7737
  init_captain_channel();
7352
7738
  }
@@ -10308,9 +10694,9 @@ ${directive}` : directive;
10308
10694
  function withTimeout(p, ms, msg) {
10309
10695
  return new Promise((resolve4, reject) => {
10310
10696
  const t = setTimeout(() => reject(new Error(msg)), ms);
10311
- p.then((v) => {
10697
+ p.then((v2) => {
10312
10698
  clearTimeout(t);
10313
- resolve4(v);
10699
+ resolve4(v2);
10314
10700
  }, (e) => {
10315
10701
  clearTimeout(t);
10316
10702
  reject(e);
@@ -10544,9 +10930,10 @@ var init_driver = __esm({
10544
10930
  });
10545
10931
 
10546
10932
  // packages/agents/dist/opencode/sse-bridge.js
10547
- var OpencodeSseBridge;
10933
+ var IGNORED_FRAME, OpencodeSseBridge;
10548
10934
  var init_sse_bridge = __esm({
10549
10935
  "packages/agents/dist/opencode/sse-bridge.js"() {
10936
+ IGNORED_FRAME = /^(message|storage|file|lsp|installation)\./;
10550
10937
  OpencodeSseBridge = class {
10551
10938
  controllers = /* @__PURE__ */ new Map();
10552
10939
  /** taskId → the crew's opencode server port (for permission-reply POSTs). */
@@ -10683,6 +11070,10 @@ var init_sse_bridge = __esm({
10683
11070
  return;
10684
11071
  }
10685
11072
  if (json?.type === "session.idle") {
11073
+ if (this.deps.ingest) {
11074
+ this.deps.ingest(json, taskId);
11075
+ return;
11076
+ }
10686
11077
  this.deps.emit({
10687
11078
  type: "task.turn.completed",
10688
11079
  id: taskId,
@@ -10692,6 +11083,10 @@ var init_sse_bridge = __esm({
10692
11083
  const p = json.properties;
10693
11084
  if (p?.id && p?.sessionID) {
10694
11085
  this.pendingPermByTask.set(taskId, { permID: p.id, sessionID: p.sessionID });
11086
+ if (this.deps.ingest) {
11087
+ this.deps.ingest(json, taskId);
11088
+ return;
11089
+ }
10695
11090
  const tool = p.permission ?? "a tool";
10696
11091
  const cmd = Array.isArray(p.patterns) && p.patterns.length ? `: ${p.patterns.join(" ")}` : "";
10697
11092
  this.deps.emit({
@@ -10701,11 +11096,24 @@ var init_sse_bridge = __esm({
10701
11096
  question: `opencode requests permission to run ${tool}${cmd}`,
10702
11097
  kind: tool
10703
11098
  });
11099
+ } else if (this.deps.ingest) {
11100
+ this.deps.ingest(json, taskId);
10704
11101
  }
10705
11102
  } else if (json?.type === "permission.replied") {
10706
11103
  this.pendingPermByTask.delete(taskId);
11104
+ this.deps.ingest?.(json, taskId);
11105
+ } else if (!IGNORED_FRAME.test(json?.type ?? "")) {
11106
+ this.deps.ingest?.(json, taskId);
10707
11107
  }
10708
11108
  }
11109
+ /** Test seam: exercise handleLine without an SSE stream. */
11110
+ handleLineForTest(rawLine, taskId) {
11111
+ this.handleLine(taskId, rawLine);
11112
+ }
11113
+ /** Test seam: read pendingPermByTask without exposing it publicly. */
11114
+ pendingPermForTest(taskId) {
11115
+ return this.pendingPermByTask.get(taskId);
11116
+ }
10709
11117
  };
10710
11118
  }
10711
11119
  });
@@ -11705,77 +12113,6 @@ var init_receipt_listener = __esm({
11705
12113
  }
11706
12114
  });
11707
12115
 
11708
- // packages/agents/dist/opencode/control-source.js
11709
- function toSnapshot2(ev) {
11710
- const now = Date.now();
11711
- switch (ev.type) {
11712
- // A permission was answered on the bus and the turn resumed.
11713
- case "task.started":
11714
- return { taskId: ev.id, state: "running", alive: true, origin: "agent", at: now };
11715
- // session.idle — the turn finished. Liveness, NOT completion (anti-#2576).
11716
- case "task.turn.completed":
11717
- return { taskId: ev.id, state: "idle", alive: true, origin: "agent", at: now };
11718
- // permission.asked — opencode STATES it is gated. No guessing from pixels.
11719
- case "task.approval.requested":
11720
- return {
11721
- taskId: ev.id,
11722
- state: "needsInput",
11723
- alive: true,
11724
- origin: "agent",
11725
- at: now,
11726
- detail: { note: ev.question, reason: ev.kind }
11727
- };
11728
- // Terminal (task.done/blocked/cancelled) and notify-only events are ignored:
11729
- // terminal state comes exclusively from `squadrant crew signal`.
11730
- default:
11731
- return null;
11732
- }
11733
- }
11734
- var OpencodeControlSource;
11735
- var init_control_source = __esm({
11736
- "packages/agents/dist/opencode/control-source.js"() {
11737
- OpencodeControlSource = class {
11738
- name = "opencode-control";
11739
- deps;
11740
- active = false;
11741
- cache = /* @__PURE__ */ new Map();
11742
- start(deps) {
11743
- this.deps = deps;
11744
- this.active = true;
11745
- }
11746
- stop() {
11747
- this.deps = void 0;
11748
- this.active = false;
11749
- this.cache.clear();
11750
- }
11751
- /** Push-only source — no fallible startup of its own. */
11752
- health() {
11753
- return { active: this.active, error: null };
11754
- }
11755
- /** Liveness floor: origin must be "scan" and must not assert needsInput. */
11756
- snapshot(taskId) {
11757
- const s = this.cache.get(taskId);
11758
- if (!s)
11759
- return void 0;
11760
- return { ...s, origin: "scan", state: s.state === "needsInput" ? "running" : s.state };
11761
- }
11762
- /**
11763
- * Feed one ControlEvent from OpencodeSseBridge into the port.
11764
- * Wired in squadrantd.ts as: emit = (ev) => { source.observe(ev); …existing… }
11765
- */
11766
- observe(ev) {
11767
- if (!this.deps)
11768
- return;
11769
- const snap = toSnapshot2(ev);
11770
- if (!snap)
11771
- return;
11772
- this.cache.set(snap.taskId, snap);
11773
- this.deps.report(snap);
11774
- }
11775
- };
11776
- }
11777
- });
11778
-
11779
12116
  // packages/agents/dist/opencode/http-channel.js
11780
12117
  var OpencodeHttpChannel;
11781
12118
  var init_http_channel = __esm({
@@ -11887,6 +12224,47 @@ var init_http_channel = __esm({
11887
12224
  }
11888
12225
  });
11889
12226
 
12227
+ // packages/agents/dist/opencode/fact-adapter.js
12228
+ function createOpencodeFactAdapter(deps) {
12229
+ return {
12230
+ name: "opencode-sse",
12231
+ origin: "agent",
12232
+ translate(raw) {
12233
+ const f = typeof raw === "object" && raw !== null ? raw : {};
12234
+ const type = typeof f.type === "string" ? f.type : void 0;
12235
+ if (type === void 0)
12236
+ return [{ kind: "unknown", name: "non-object" }];
12237
+ const p = f.properties ?? {};
12238
+ if (type === "session.idle") {
12239
+ return [{
12240
+ kind: "turn.ended",
12241
+ turnId: typeof p.sessionID === "string" ? p.sessionID : void 0
12242
+ }];
12243
+ }
12244
+ if (type === "permission.asked") {
12245
+ if (typeof p.id !== "string" || typeof p.sessionID !== "string") {
12246
+ return [{ kind: "unknown", name: "permission.asked:incomplete" }];
12247
+ }
12248
+ const tool = typeof p.permission === "string" ? p.permission : "a tool";
12249
+ const cmd = Array.isArray(p.patterns) && p.patterns.length ? `: ${p.patterns.join(" ")}` : "";
12250
+ return [{
12251
+ kind: "permission.requested",
12252
+ question: `opencode requests permission to run ${tool}${cmd}`,
12253
+ requestId: deps.nextRequestId(),
12254
+ tool
12255
+ }];
12256
+ }
12257
+ if (type === "permission.replied")
12258
+ return [{ kind: "activity" }];
12259
+ return [{ kind: "unknown", name: type }];
12260
+ }
12261
+ };
12262
+ }
12263
+ var init_fact_adapter = __esm({
12264
+ "packages/agents/dist/opencode/fact-adapter.js"() {
12265
+ }
12266
+ });
12267
+
11890
12268
  // packages/agents/dist/index.js
11891
12269
  var dist_exports4 = {};
11892
12270
  __export(dist_exports4, {
@@ -11901,7 +12279,6 @@ __export(dist_exports4, {
11901
12279
  HEADLESS_ERROR_TAIL: () => HEADLESS_ERROR_TAIL,
11902
12280
  MARKER_END: () => MARKER_END,
11903
12281
  MARKER_START: () => MARKER_START,
11904
- OpencodeControlSource: () => OpencodeControlSource,
11905
12282
  OpencodeHttpChannel: () => OpencodeHttpChannel,
11906
12283
  OpencodeSseBridge: () => OpencodeSseBridge,
11907
12284
  ProjectionRegistry: () => ProjectionRegistry,
@@ -11921,6 +12298,7 @@ __export(dist_exports4, {
11921
12298
  createGeminiEmitter: () => createGeminiEmitter,
11922
12299
  createOpencodeDriver: () => createOpencodeDriver,
11923
12300
  createOpencodeEmitter: () => createOpencodeEmitter,
12301
+ createOpencodeFactAdapter: () => createOpencodeFactAdapter,
11924
12302
  decideCaptainMemoryWrite: () => decideCaptainMemoryWrite,
11925
12303
  deriveTranscriptPath: () => deriveTranscriptPath,
11926
12304
  detectTrailingQuestion: () => detectTrailingQuestion2,
@@ -11963,8 +12341,8 @@ var init_dist4 = __esm({
11963
12341
  init_receipt_listener();
11964
12342
  init_peer_wire();
11965
12343
  init_registry8();
11966
- init_control_source();
11967
12344
  init_http_channel();
12345
+ init_fact_adapter();
11968
12346
  }
11969
12347
  });
11970
12348
 
@@ -13963,6 +14341,10 @@ async function runCrewSpawn2(input) {
13963
14341
  emitEvent: async (p, event) => {
13964
14342
  await squadrantdCall({ kind: "event", project: p, event });
13965
14343
  },
14344
+ // #745: check the daemon's hook-confirmed state before reporting a false
14345
+ // "first turn not delivered" — swallow errors (offline/unreachable daemon)
14346
+ // so this optional check never itself breaks the spawn.
14347
+ getTaskRecord: async (p, id) => await squadrantdCall(buildStatusRequest(p, id)).catch(() => void 0),
13966
14348
  onRouted: (route) => console.log(
13967
14349
  chalk10.dim(
13968
14350
  `routed: tier=${route.tier} \u2192 ${route.agent}${route.model ? `/${route.model}` : ""} (rule: "${route.matchedRule}")`
@@ -14941,12 +15323,12 @@ function donut(t) {
14941
15323
  const C = 2 * Math.PI * r;
14942
15324
  let acc = 0;
14943
15325
  const segs = DONUT_ORDER.map((k) => {
14944
- const v = t[k];
14945
- if (v <= 0)
15326
+ const v2 = t[k];
15327
+ if (v2 <= 0)
14946
15328
  return "";
14947
- const len = t.total ? v / t.total * C : 0;
15329
+ const len = t.total ? v2 / t.total * C : 0;
14948
15330
  const rot = t.total ? acc / t.total * 360 : 0;
14949
- acc += v;
15331
+ acc += v2;
14950
15332
  return `<circle class="seg s-${k}" cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke-width="${w}" stroke-dasharray="${len.toFixed(2)} ${(C - len).toFixed(2)}" transform="rotate(${(rot - 90).toFixed(2)} ${cx} ${cy})"></circle>`;
14951
15333
  }).join("");
14952
15334
  const track = `<circle class="donut-track" cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke-width="${w}"></circle>`;
@@ -15250,7 +15632,7 @@ function renderLiveGrid(snap, now) {
15250
15632
  const headerLabels = STATE_ORDER.map((s) => `${STATE_LABEL[s]}`);
15251
15633
  out.push(`<div class="live-header" data-live-header="">`);
15252
15634
  out.push(headerLabels.map((l) => {
15253
- const raw = Object.entries(STATE_LABEL).find(([, v]) => v === l)[0];
15635
+ const raw = Object.entries(STATE_LABEL).find(([, v2]) => v2 === l)[0];
15254
15636
  const c = stateCounts[raw];
15255
15637
  const cls = c === 0 ? "zero" : "";
15256
15638
  return `<span class="live-stat ${cls}"><span class="pdot ${STATE_CLS[raw]}"></span>${c} ${l}</span>`;
@@ -15802,7 +16184,7 @@ async function runDashboardWeb(input) {
15802
16184
  console.log(chalk14.green(`\u2714 Squadrant system dashboard \u2192 http://127.0.0.1:${handle.port}`));
15803
16185
  console.log(chalk14.dim(` polling the daemon every ${input.interval}s \xB7 localhost only \xB7 read-only \xB7 Ctrl-C to stop`));
15804
16186
  }
15805
- var dashboardCommand = new Command13("dashboard").description("Live status grid of all projects (derived from daemon task state)").option("--once", "Print one snapshot and exit (used by --pane's refresh loop)").option("--pane", "Open a refreshing sidebar pane in the current cmux workspace").option("--web", "Serve the live system-health web dashboard on 127.0.0.1 (HTTP + SSE)").option("--port <port>", "Port for --web (default 7878)", (v) => parseInt(v, 10), 7878).option("--direction <dir>", "Pane split direction (right|left|up|down)", "right").option("--interval <seconds>", "Daemon poll interval for --web (default 5); refresh interval for --pane (default 10)", (v) => parseInt(v, 10)).action(async (opts) => {
16187
+ var dashboardCommand = new Command13("dashboard").description("Live status grid of all projects (derived from daemon task state)").option("--once", "Print one snapshot and exit (used by --pane's refresh loop)").option("--pane", "Open a refreshing sidebar pane in the current cmux workspace").option("--web", "Serve the live system-health web dashboard on 127.0.0.1 (HTTP + SSE)").option("--port <port>", "Port for --web (default 7878)", (v2) => parseInt(v2, 10), 7878).option("--direction <dir>", "Pane split direction (right|left|up|down)", "right").option("--interval <seconds>", "Daemon poll interval for --web (default 5); refresh interval for --pane (default 10)", (v2) => parseInt(v2, 10)).action(async (opts) => {
15806
16188
  try {
15807
16189
  if (opts.web) {
15808
16190
  await runDashboardWeb({ port: opts.port, interval: opts.interval ?? 5 });
@@ -16022,7 +16404,7 @@ var launchCommand = new Command14("launch").description(
16022
16404
  agentCmdFactory: (forceFresh) => {
16023
16405
  const captainChannelEnabled = shouldWireCaptainChannel(agentName, config);
16024
16406
  if (captainChannelEnabled) {
16025
- fs21.mkdirSync(CC_SOCKS_DIR, { recursive: true });
16407
+ ensureSocksDir();
16026
16408
  }
16027
16409
  return buildAgentCmd(
16028
16410
  agentName,
@@ -16733,11 +17115,11 @@ import chalk22 from "chalk";
16733
17115
  import fs23 from "fs";
16734
17116
  import path28 from "path";
16735
17117
  import { fileURLToPath as fileURLToPath4 } from "url";
16736
- function parseScope(v) {
16737
- if (v !== "user" && v !== "project") {
17118
+ function parseScope(v2) {
17119
+ if (v2 !== "user" && v2 !== "project") {
16738
17120
  throw new Error("--scope must be 'user' or 'project'");
16739
17121
  }
16740
- return v;
17122
+ return v2;
16741
17123
  }
16742
17124
  function findPackageRoot3() {
16743
17125
  let dir = path28.dirname(fileURLToPath4(import.meta.url));
@@ -17185,8 +17567,10 @@ async function runHealDaemon(opts) {
17185
17567
  const { stdout, stderr } = opts;
17186
17568
  stdout.write("restarting squadrantd via launchd kickstart...\n");
17187
17569
  try {
17188
- opts.ensureDaemon();
17189
- stdout.write(chalk24.green("\u2714 daemon kickstart complete\n"));
17570
+ const result = opts.ensureDaemon();
17571
+ const noteSuffix = result?.note ? ` (${result.note})` : "";
17572
+ stdout.write(chalk24.green(`\u2714 daemon kickstart complete${noteSuffix}
17573
+ `));
17190
17574
  return 0;
17191
17575
  } catch (e) {
17192
17576
  stderr.write(`heal daemon failed: ${e.message}
@@ -17389,12 +17773,12 @@ async function dispatchAction(toProject, task, opts) {
17389
17773
  process.exit(1);
17390
17774
  }
17391
17775
  }
17392
- var dispatchCommand = new Command25("dispatch").description("Dispatch a task to any registered project (tracked, reports back on settle)").argument("<project>", "Target project name").argument("<task>", "Task description to dispatch").option("--provider <p>", "claude|opencode|codex", "claude").option("--mode <m>", "headless|interactive", "headless").option("--warmup-timeout <s>", "seconds to wait for target captain to boot (same-group only; default: 120)", (v) => parseInt(v, 10) * 1e3).action(dispatchAction);
17776
+ var dispatchCommand = new Command25("dispatch").description("Dispatch a task to any registered project (tracked, reports back on settle)").argument("<project>", "Target project name").argument("<task>", "Task description to dispatch").option("--provider <p>", "claude|opencode|codex", "claude").option("--mode <m>", "headless|interactive", "headless").option("--warmup-timeout <s>", "seconds to wait for target captain to boot (same-group only; default: 120)", (v2) => parseInt(v2, 10) * 1e3).action(dispatchAction);
17393
17777
 
17394
17778
  // packages/cli/src/commands/group.ts
17395
17779
  init_dist2();
17396
17780
  var groupCommand = new Command26("group").description("Cross-project intra-group operations (Phase 1: dispatch)").addCommand(
17397
- new Command26("dispatch").description("[DEPRECATED \u2014 use 'squadrant dispatch'] Dispatch a task to a sibling project in the same group").argument("<to-project>", "Target project name").argument("<task>", "Task description to dispatch").option("--provider <p>", "claude|opencode|codex", "claude").option("--mode <m>", "headless|interactive", "headless").option("--warmup-timeout <s>", "seconds to wait for target captain to boot (default: 120)", (v) => parseInt(v, 10) * 1e3).action(async (toProject, task, opts) => {
17781
+ new Command26("dispatch").description("[DEPRECATED \u2014 use 'squadrant dispatch'] Dispatch a task to a sibling project in the same group").argument("<to-project>", "Target project name").argument("<task>", "Task description to dispatch").option("--provider <p>", "claude|opencode|codex", "claude").option("--mode <m>", "headless|interactive", "headless").option("--warmup-timeout <s>", "seconds to wait for target captain to boot (default: 120)", (v2) => parseInt(v2, 10) * 1e3).action(async (toProject, task, opts) => {
17398
17782
  console.error(chalk26.yellow(
17399
17783
  `\u26A0 'squadrant group dispatch' is deprecated \u2014 use 'squadrant dispatch <project> "<task>"' instead.`
17400
17784
  ));
@@ -17448,6 +17832,7 @@ function registerSenderIdentity(socketPath2) {
17448
17832
  }
17449
17833
  async function sharedReceiptListener() {
17450
17834
  if (shared) return shared;
17835
+ ensureSocksDir();
17451
17836
  const socketPath2 = `${CC_SOCKS_DIR}/squadrantd-${process.pid}.sock`;
17452
17837
  const listener = new ClaudeReceiptListener({
17453
17838
  socketPath: socketPath2,
@@ -18070,7 +18455,7 @@ telegramCommand.command("link").argument("<project>", "project to bind to a Tele
18070
18455
  const { topicId, created } = await runTelegramLink({ project, cfg, client, stateRoot: defaultStateRoot() });
18071
18456
  console.log(chalk32.green(`${created ? "linked" : "already linked"}: ${project} \u2192 topic ${topicId}`));
18072
18457
  });
18073
- telegramCommand.command("setup").description("Interactive wizard \u2014 bot token, validate, auto-detect supergroup, write config").option("--reset-token", "force re-entry of the bot token even if one already exists").option("--redetect", "force group re-detection even when a supergroup is already configured").option("--user-id <id>", "allowlist user-id \u2014 enables remote control on a re-run without getUpdates detection", (v) => parseInt(v, 10)).action(async (opts) => {
18458
+ telegramCommand.command("setup").description("Interactive wizard \u2014 bot token, validate, auto-detect supergroup, write config").option("--reset-token", "force re-entry of the bot token even if one already exists").option("--redetect", "force group re-detection even when a supergroup is already configured").option("--user-id <id>", "allowlist user-id \u2014 enables remote control on a re-run without getUpdates detection", (v2) => parseInt(v2, 10)).action(async (opts) => {
18074
18459
  if (!process.stdin.isTTY) {
18075
18460
  console.error(chalk32.red("setup requires a TTY \u2014 pipe input is not supported"));
18076
18461
  process.exit(1);
@@ -18580,7 +18965,7 @@ function printTree(items) {
18580
18965
  function printFlat(items) {
18581
18966
  for (const item of items) printItem(item, 0);
18582
18967
  }
18583
- var startCmd = new Command33("start").description("Start a new work item").argument("<title>", "what you're doing").option("--project <name>", "project this work belongs to (defaults to the current registered project)").option("--parent <id>", "id of the wave/parent item this nests under").option("--tag <tag>", "attach a tag (repeatable)", (v, prev) => [...prev, v], []).action((title, opts) => {
18968
+ var startCmd = new Command33("start").description("Start a new work item").argument("<title>", "what you're doing").option("--project <name>", "project this work belongs to (defaults to the current registered project)").option("--parent <id>", "id of the wave/parent item this nests under").option("--tag <tag>", "attach a tag (repeatable)", (v2, prev) => [...prev, v2], []).action((title, opts) => {
18584
18969
  const config = loadConfig();
18585
18970
  const store = createWorkStore();
18586
18971
  purgeExpiredWorkItems(store);
@@ -18902,7 +19287,7 @@ function queryClaudeMem(dbPath, project) {
18902
19287
  createdAt: r.created_at
18903
19288
  }));
18904
19289
  const candidates = [summaryRow?.created_at, ...decisionRows.map((r) => r.created_at)].filter(
18905
- (v) => !!v
19290
+ (v2) => !!v2
18906
19291
  );
18907
19292
  const oldestCreatedAt = candidates.length > 0 ? candidates.reduce((a, b) => a < b ? a : b) : null;
18908
19293
  return {