squadrant 0.19.1 → 0.19.2

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,33 @@ 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
+ async function pollFirstTurnConfirmedAt(getTaskRecord, project, id) {
4141
+ const deadline = Date.now() + FIRST_TURN_HOOK_CONFIRM_WINDOW_MS;
4142
+ for (; ; ) {
4143
+ const rec = await getTaskRecord(project, id).catch(() => void 0);
4144
+ if (rec?.firstTurnConfirmedAt)
4145
+ return true;
4146
+ if (Date.now() >= deadline)
4147
+ return false;
4148
+ await new Promise((r) => setTimeout(r, FIRST_TURN_HOOK_POLL_INTERVAL_MS));
4149
+ }
4150
+ }
4151
+ function firstTrueOrBothFalse(a, b) {
4152
+ return new Promise((resolve4) => {
4153
+ let settledFalseCount = 0;
4154
+ const onSettle = (ok2) => {
4155
+ if (ok2) {
4156
+ resolve4(true);
4157
+ return;
4158
+ }
4159
+ settledFalseCount++;
4160
+ if (settledFalseCount === 2)
4161
+ resolve4(false);
4162
+ };
4163
+ a.then(onSettle, () => onSettle(false));
4164
+ b.then(onSettle, () => onSettle(false));
4165
+ });
4166
+ }
4128
4167
  async function listCrewPanes(runtime, workspaceId, project) {
4129
4168
  const surfaces = await runtime.listSurfaces(workspaceId);
4130
4169
  return surfaces.filter((s) => s.title && isCrewTitle(project, s.title));
@@ -4281,10 +4320,12 @@ async function runCrewSpawn(input, config, deps) {
4281
4320
  fs9.writeFileSync(spillFile, claudeFirstTurn, "utf8");
4282
4321
  claudeFirstTurn = `Full task is at ${spillFile} \u2014 cat it and follow it exactly.`;
4283
4322
  }
4284
- const claudeResult = await deps.sendFirstTurn(pane2, `${claudeFirstTurn}
4323
+ const sendPromise = deps.sendFirstTurn(pane2, `${claudeFirstTurn}
4285
4324
 
4286
4325
  ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
4287
- if (!claudeResult.delivered) {
4326
+ const scrapeDelivered = sendPromise.then((r) => r.delivered).catch(() => false);
4327
+ const delivered = hooksInstalled && deps.getTaskRecord ? await firstTrueOrBothFalse(scrapeDelivered, pollFirstTurnConfirmedAt(deps.getTaskRecord, input.project, rec.id)) : await scrapeDelivered;
4328
+ if (!delivered) {
4288
4329
  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
4330
  `);
4290
4331
  } else if (!hooksInstalled) {
@@ -4395,6 +4436,10 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps, o
4395
4436
  const heldForMin = Math.round((Date.now() - task.operatorHold.since) / 6e4);
4396
4437
  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
4438
  }
4439
+ const isAttentionState = task?.state === "blocked" || task?.state === "awaiting-input" || task?.state === "review";
4440
+ if (task && !isAttentionState && task.firstTurnConfirmedAt && task.task === message) {
4441
+ 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.`);
4442
+ }
4398
4443
  let reopened = false;
4399
4444
  try {
4400
4445
  if (task) {
@@ -4561,7 +4606,7 @@ async function runCrewList(project, runtime, workspaceId) {
4561
4606
  surfaceId: c.surfaceId
4562
4607
  }));
4563
4608
  }
4564
- var CC_SOCKS_DIR, FIRST_TURN_INLINE_MAX_BYTES, TEMPLATES_DIR, STATE_ROOT, CLOSE_LOOKUP_RETRIES, CLOSE_LOOKUP_RETRY_DELAY_MS;
4609
+ 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
4610
  var init_crew_spawn = __esm({
4566
4611
  "packages/core/dist/crew-spawn.js"() {
4567
4612
  init_control_channel();
@@ -4573,6 +4618,8 @@ var init_crew_spawn = __esm({
4573
4618
  FIRST_TURN_INLINE_MAX_BYTES = 1200;
4574
4619
  TEMPLATES_DIR = path9.join(os4.homedir(), ".config", "squadrant", "templates");
4575
4620
  STATE_ROOT = path9.join(os4.homedir(), ".config", "squadrant", "state");
4621
+ FIRST_TURN_HOOK_CONFIRM_WINDOW_MS = 1e5;
4622
+ FIRST_TURN_HOOK_POLL_INTERVAL_MS = 2e3;
4576
4623
  CLOSE_LOOKUP_RETRIES = 3;
4577
4624
  CLOSE_LOOKUP_RETRY_DELAY_MS = 150;
4578
4625
  }
@@ -4742,9 +4789,9 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
4742
4789
  const lastDeferred = /* @__PURE__ */ new Map();
4743
4790
  const inFlightDelivery = () => {
4744
4791
  let worst2 = null;
4745
- for (const [project, v] of lastDeferred) {
4746
- if (!worst2 || v.deferCount > worst2.deferCount)
4747
- worst2 = { project, ...v };
4792
+ for (const [project, v2] of lastDeferred) {
4793
+ if (!worst2 || v2.deferCount > worst2.deferCount)
4794
+ worst2 = { project, ...v2 };
4748
4795
  }
4749
4796
  return worst2;
4750
4797
  };
@@ -7108,6 +7155,326 @@ var init_lifecycle_source = __esm({
7108
7155
  }
7109
7156
  });
7110
7157
 
7158
+ // packages/core/dist/events/fact.js
7159
+ function stampFact(raw, id) {
7160
+ return { ...raw, ...id };
7161
+ }
7162
+ var init_fact = __esm({
7163
+ "packages/core/dist/events/fact.js"() {
7164
+ }
7165
+ });
7166
+
7167
+ // packages/core/dist/events/log.js
7168
+ var FactLog;
7169
+ var init_log = __esm({
7170
+ "packages/core/dist/events/log.js"() {
7171
+ FactLog = class {
7172
+ capacity;
7173
+ buffers = /* @__PURE__ */ new Map();
7174
+ constructor(opts = {}) {
7175
+ this.capacity = opts.capacity ?? 256;
7176
+ }
7177
+ push(fact) {
7178
+ let buf = this.buffers.get(fact.taskId);
7179
+ if (!buf) {
7180
+ buf = [];
7181
+ this.buffers.set(fact.taskId, buf);
7182
+ }
7183
+ buf.push(fact);
7184
+ while (buf.length > this.capacity)
7185
+ buf.shift();
7186
+ }
7187
+ /** Oldest-first snapshot. A fresh array; later pushes never grow it. */
7188
+ recent(taskId) {
7189
+ return [...this.buffers.get(taskId) ?? []];
7190
+ }
7191
+ /** Newline-delimited JSON, one fact per line, oldest first. */
7192
+ serialize(taskId) {
7193
+ return this.recent(taskId).map((f) => JSON.stringify(f)).join("\n") + "\n";
7194
+ }
7195
+ /** Release a finished crew's buffer. */
7196
+ drop(taskId) {
7197
+ this.buffers.delete(taskId);
7198
+ }
7199
+ };
7200
+ }
7201
+ });
7202
+
7203
+ // packages/core/dist/events/invariant.js
7204
+ function freshTrace() {
7205
+ return {
7206
+ depth: 0,
7207
+ oldestOpenAt: null,
7208
+ stallReported: false,
7209
+ unknownSeen: 0,
7210
+ liveness: /* @__PURE__ */ new Map()
7211
+ };
7212
+ }
7213
+ function checkFact(trace, fact, opts) {
7214
+ const out = [];
7215
+ if (fact.origin === "inferred" && TERMINALISING.has(fact.kind)) {
7216
+ out.push(v("I4", `inferred fact "${fact.kind}" from ${fact.source} cannot terminalise alone`, fact));
7217
+ }
7218
+ switch (fact.kind) {
7219
+ case "tool.opened":
7220
+ if (trace.depth === 0) {
7221
+ trace.oldestOpenAt = fact.at;
7222
+ trace.stallReported = false;
7223
+ }
7224
+ trace.depth += 1;
7225
+ break;
7226
+ case "tool.closed":
7227
+ if (trace.depth === 0) {
7228
+ out.push(v("I1", `tool.closed from ${fact.source} with no open tool`, fact));
7229
+ } else {
7230
+ trace.depth -= 1;
7231
+ if (trace.depth === 0) {
7232
+ trace.oldestOpenAt = null;
7233
+ trace.stallReported = false;
7234
+ }
7235
+ }
7236
+ break;
7237
+ case "turn.ended":
7238
+ if (trace.depth > 0) {
7239
+ out.push(v("I2", `turn.ended with ${trace.depth} tool call(s) still open`, fact));
7240
+ trace.depth = 0;
7241
+ trace.oldestOpenAt = null;
7242
+ trace.stallReported = false;
7243
+ }
7244
+ break;
7245
+ case "unknown":
7246
+ trace.unknownSeen += 1;
7247
+ out.push(v("I5", `unrecognised frame "${fact.name}" from ${fact.source}`, fact));
7248
+ break;
7249
+ case "process.observed": {
7250
+ const prior = [...trace.liveness.entries()].find(([src, s]) => src !== fact.source && s.alive !== fact.alive && fact.at - s.at <= (opts.disagreeWindowMs ?? -1));
7251
+ if (prior) {
7252
+ out.push(v("I6", `liveness disagreement: ${prior[0]} said alive=${prior[1].alive}, ${fact.source} says alive=${fact.alive}`, fact));
7253
+ }
7254
+ trace.liveness.set(fact.source, { alive: fact.alive, at: fact.at });
7255
+ break;
7256
+ }
7257
+ default:
7258
+ break;
7259
+ }
7260
+ if (opts.stallBudgetMs !== void 0 && trace.depth > 0 && trace.oldestOpenAt !== null && !trace.stallReported && fact.at - trace.oldestOpenAt > opts.stallBudgetMs) {
7261
+ trace.stallReported = true;
7262
+ out.push(v("I3", `tool open for ${fact.at - trace.oldestOpenAt}ms, past the stall budget`, fact));
7263
+ }
7264
+ return out;
7265
+ }
7266
+ var v, TERMINALISING;
7267
+ var init_invariant = __esm({
7268
+ "packages/core/dist/events/invariant.js"() {
7269
+ v = (code, message, f) => ({ code, message, taskId: f.taskId, at: f.at });
7270
+ TERMINALISING = /* @__PURE__ */ new Set(["session.ended"]);
7271
+ }
7272
+ });
7273
+
7274
+ // packages/core/dist/events/to-control-event.js
7275
+ function toControlEvent(fact) {
7276
+ if (fact.origin === "inferred" && TERMINALISING2.has(fact.kind))
7277
+ return [];
7278
+ switch (fact.kind) {
7279
+ case "turn.ended":
7280
+ return [{ type: "task.turn.completed", id: fact.taskId, turnId: fact.turnId ?? fact.source }];
7281
+ case "permission.requested":
7282
+ return [{
7283
+ type: "task.approval.requested",
7284
+ id: fact.taskId,
7285
+ requestId: fact.requestId,
7286
+ question: fact.question,
7287
+ kind: fact.tool
7288
+ }];
7289
+ case "input.requested":
7290
+ return [{
7291
+ type: "task.input.requested",
7292
+ id: fact.taskId,
7293
+ requestId: fact.requestId,
7294
+ question: fact.question
7295
+ }];
7296
+ case "session.ended":
7297
+ return [{ type: "task.session.ended", id: fact.taskId }];
7298
+ case "session.started":
7299
+ return [{
7300
+ type: "task.started",
7301
+ id: fact.taskId,
7302
+ ...fact.pid === void 0 ? {} : { pid: fact.pid },
7303
+ ...fact.sessionId === void 0 ? {} : { sessionId: fact.sessionId }
7304
+ }];
7305
+ case "prompt.submitted":
7306
+ return [{ type: "task.first-turn.confirmed", id: fact.taskId }];
7307
+ // Liveness-only. The facade still feeds these to reduceLifecycle; they
7308
+ // simply carry no ControlEvent of their own.
7309
+ case "tool.opened":
7310
+ case "tool.closed":
7311
+ case "activity":
7312
+ case "process.observed":
7313
+ case "unknown":
7314
+ return [];
7315
+ }
7316
+ }
7317
+ var TERMINALISING2;
7318
+ var init_to_control_event = __esm({
7319
+ "packages/core/dist/events/to-control-event.js"() {
7320
+ TERMINALISING2 = /* @__PURE__ */ new Set(["session.ended"]);
7321
+ }
7322
+ });
7323
+
7324
+ // packages/core/dist/events/conformance.js
7325
+ function assert(cond, msg) {
7326
+ if (!cond)
7327
+ throw new Error(`conformance: ${msg}`);
7328
+ }
7329
+ function runAdapterConformance(adapter, samples) {
7330
+ const call = (raw) => adapter.translate(raw);
7331
+ return [
7332
+ {
7333
+ name: `${adapter.name}: never throws on garbage`,
7334
+ run: () => {
7335
+ for (const g of GARBAGE) {
7336
+ try {
7337
+ call(g);
7338
+ } catch (e) {
7339
+ throw new Error(`threw on ${JSON.stringify(g)}: ${String(e)}`);
7340
+ }
7341
+ }
7342
+ }
7343
+ },
7344
+ {
7345
+ name: `${adapter.name}: never returns null or undefined`,
7346
+ run: () => {
7347
+ for (const g of [...GARBAGE, ...samples]) {
7348
+ const out = call(g);
7349
+ assert(Array.isArray(out), `returned a non-array for ${JSON.stringify(g)}`);
7350
+ }
7351
+ }
7352
+ },
7353
+ {
7354
+ name: `${adapter.name}: an unrecognised frame yields unknown, not an empty array`,
7355
+ run: () => {
7356
+ const out = call({ type: "definitely-not-a-real-event-name" });
7357
+ assert(out.length > 0, "silently dropped an unrecognised frame (the #542 shape)");
7358
+ assert(out.every((f) => f.kind === "unknown"), "an unrecognised frame must translate to kind 'unknown'");
7359
+ }
7360
+ },
7361
+ {
7362
+ name: `${adapter.name}: recognises its own samples`,
7363
+ run: () => {
7364
+ for (const s of samples) {
7365
+ const out = call(s);
7366
+ assert(out.length > 0, `produced nothing for its own sample ${JSON.stringify(s)}`);
7367
+ assert(out.some((f) => f.kind !== "unknown"), `failed to recognise its own sample ${JSON.stringify(s)}`);
7368
+ }
7369
+ }
7370
+ },
7371
+ {
7372
+ name: `${adapter.name}: declares a constant origin`,
7373
+ run: () => {
7374
+ assert(adapter.origin === "agent" || adapter.origin === "scan" || adapter.origin === "inferred", `invalid origin "${String(adapter.origin)}"`);
7375
+ }
7376
+ }
7377
+ ];
7378
+ }
7379
+ var GARBAGE;
7380
+ var init_conformance = __esm({
7381
+ "packages/core/dist/events/conformance.js"() {
7382
+ GARBAGE = [
7383
+ null,
7384
+ void 0,
7385
+ 0,
7386
+ "",
7387
+ "not json",
7388
+ [],
7389
+ {},
7390
+ { type: 42 },
7391
+ { type: "definitely-not-a-real-event-name" }
7392
+ ];
7393
+ }
7394
+ });
7395
+
7396
+ // packages/core/dist/events/source.js
7397
+ function createEventsSource(opts) {
7398
+ const now = opts.now ?? (() => Date.now());
7399
+ const log = new FactLog({ capacity: opts.capacity });
7400
+ const adapters = new Map(opts.adapters.map((a) => [a.name, a]));
7401
+ const traces = /* @__PURE__ */ new Map();
7402
+ const seqs = /* @__PURE__ */ new Map();
7403
+ let deps;
7404
+ const traceFor = (taskId) => {
7405
+ let t = traces.get(taskId);
7406
+ if (!t) {
7407
+ t = freshTrace();
7408
+ traces.set(taskId, t);
7409
+ }
7410
+ return t;
7411
+ };
7412
+ const nextSeq = (taskId) => {
7413
+ const n = seqs.get(taskId) ?? 0;
7414
+ seqs.set(taskId, n + 1);
7415
+ return n;
7416
+ };
7417
+ return {
7418
+ name: "events",
7419
+ start(d) {
7420
+ deps = d;
7421
+ },
7422
+ stop() {
7423
+ deps = void 0;
7424
+ },
7425
+ health() {
7426
+ return { active: deps !== void 0, error: null };
7427
+ },
7428
+ recent(taskId) {
7429
+ return log.recent(taskId);
7430
+ },
7431
+ dump(taskId) {
7432
+ return log.serialize(taskId);
7433
+ },
7434
+ ingest(source, raw, hint) {
7435
+ const adapter = adapters.get(source);
7436
+ if (!adapter || !deps)
7437
+ return;
7438
+ const rec = deps.resolve(hint);
7439
+ if (!rec)
7440
+ return;
7441
+ const taskId = rec.id;
7442
+ const at = now();
7443
+ let produced;
7444
+ try {
7445
+ const out = adapter.translate(raw);
7446
+ produced = Array.isArray(out) ? out : [{ kind: "unknown", name: `${source} returned non-array` }];
7447
+ } catch (e) {
7448
+ opts.log?.(`events: adapter ${source} threw: ${String(e)}`);
7449
+ produced = [{ kind: "unknown", name: `${source} threw` }];
7450
+ }
7451
+ for (const rawFact of produced) {
7452
+ const fact = stampFact(rawFact, {
7453
+ seq: nextSeq(taskId),
7454
+ taskId,
7455
+ at,
7456
+ source,
7457
+ origin: adapter.origin
7458
+ });
7459
+ log.push(fact);
7460
+ for (const v2 of checkFact(traceFor(taskId), fact, opts.check ?? {})) {
7461
+ opts.onViolation(v2);
7462
+ }
7463
+ for (const ev of toControlEvent(fact))
7464
+ opts.emit(ev);
7465
+ }
7466
+ }
7467
+ };
7468
+ }
7469
+ var init_source = __esm({
7470
+ "packages/core/dist/events/source.js"() {
7471
+ init_fact();
7472
+ init_log();
7473
+ init_invariant();
7474
+ init_to_control_event();
7475
+ }
7476
+ });
7477
+
7111
7478
  // packages/core/dist/index.js
7112
7479
  var dist_exports2 = {};
7113
7480
  __export(dist_exports2, {
@@ -7122,6 +7489,7 @@ __export(dist_exports2, {
7122
7489
  DEFAULT_TASK_TIMEOUT_MS: () => DEFAULT_TASK_TIMEOUT_MS,
7123
7490
  DeferDelivery: () => DeferDelivery,
7124
7491
  FIRST_TURN_INLINE_MAX_BYTES: () => FIRST_TURN_INLINE_MAX_BYTES,
7492
+ FactLog: () => FactLog,
7125
7493
  GROUP_DISPATCH_WARMUP_POLL_MS: () => GROUP_DISPATCH_WARMUP_POLL_MS,
7126
7494
  GROUP_DISPATCH_WARMUP_TIMEOUT_MS: () => GROUP_DISPATCH_WARMUP_TIMEOUT_MS,
7127
7495
  IDLE_DEBOUNCE_MS: () => IDLE_DEBOUNCE_MS,
@@ -7151,6 +7519,7 @@ __export(dist_exports2, {
7151
7519
  capAllowed: () => capAllowed,
7152
7520
  capOutput: () => capOutput,
7153
7521
  captainSocketPath: () => captainSocketPath,
7522
+ checkFact: () => checkFact,
7154
7523
  classifyHealth: () => classifyHealth,
7155
7524
  closeWorkItem: () => closeWorkItem,
7156
7525
  computeTemplateHash: () => computeTemplateHash,
@@ -7163,6 +7532,7 @@ __export(dist_exports2, {
7163
7532
  createDirectCrewPaneReader: () => createDirectCrewPaneReader,
7164
7533
  createDirectSurfaceLivenessProbe: () => createDirectSurfaceLivenessProbe,
7165
7534
  createEnsureCaptainAlive: () => createEnsureCaptainAlive,
7535
+ createEventsSource: () => createEventsSource,
7166
7536
  createInteractiveProbe: () => createInteractiveProbe,
7167
7537
  createIsCaptainAlive: () => createIsCaptainAlive,
7168
7538
  createLaunch: () => createLaunch,
@@ -7205,6 +7575,7 @@ __export(dist_exports2, {
7205
7575
  formatInbound: () => formatInbound,
7206
7576
  formatInboundReceipt: () => formatInboundReceipt,
7207
7577
  formatLifecycle: () => formatLifecycle,
7578
+ freshTrace: () => freshTrace,
7208
7579
  getDaemonPid: () => getDaemonPid,
7209
7580
  healCmdFor: () => healCmdFor,
7210
7581
  isAuthorized: () => isAuthorized,
@@ -7261,6 +7632,7 @@ __export(dist_exports2, {
7261
7632
  resolveSetupUserId: () => resolveSetupUserId,
7262
7633
  restartDaemonIfRunning: () => restartDaemonIfRunning,
7263
7634
  rotateIfNeeded: () => rotateIfNeeded,
7635
+ runAdapterConformance: () => runAdapterConformance,
7264
7636
  runCrewAnswer: () => runCrewAnswer,
7265
7637
  runCrewClose: () => runCrewClose,
7266
7638
  runCrewList: () => runCrewList,
@@ -7295,12 +7667,14 @@ __export(dist_exports2, {
7295
7667
  sideNameFromTitle: () => sideNameFromTitle,
7296
7668
  sideNextAutoName: () => sideNextAutoName,
7297
7669
  sideTitleFor: () => sideTitleFor,
7670
+ stampFact: () => stampFact,
7298
7671
  startDaemon: () => startDaemon,
7299
7672
  startServer: () => startServer,
7300
7673
  stripBotMention: () => stripBotMention,
7301
7674
  surfaceVerdict: () => surfaceVerdict,
7302
7675
  timeoutGate: () => timeoutGate,
7303
7676
  titleFor: () => titleFor,
7677
+ toControlEvent: () => toControlEvent,
7304
7678
  topicKey: () => topicKey,
7305
7679
  topicName: () => topicName,
7306
7680
  tryAcquireDaemonLock: () => tryAcquireDaemonLock,
@@ -7347,6 +7721,12 @@ var init_dist2 = __esm({
7347
7721
  init_crew_spawn();
7348
7722
  init_crew_answer();
7349
7723
  init_lifecycle_source();
7724
+ init_fact();
7725
+ init_log();
7726
+ init_invariant();
7727
+ init_to_control_event();
7728
+ init_conformance();
7729
+ init_source();
7350
7730
  init_control_channel();
7351
7731
  init_captain_channel();
7352
7732
  }
@@ -10308,9 +10688,9 @@ ${directive}` : directive;
10308
10688
  function withTimeout(p, ms, msg) {
10309
10689
  return new Promise((resolve4, reject) => {
10310
10690
  const t = setTimeout(() => reject(new Error(msg)), ms);
10311
- p.then((v) => {
10691
+ p.then((v2) => {
10312
10692
  clearTimeout(t);
10313
- resolve4(v);
10693
+ resolve4(v2);
10314
10694
  }, (e) => {
10315
10695
  clearTimeout(t);
10316
10696
  reject(e);
@@ -10544,9 +10924,10 @@ var init_driver = __esm({
10544
10924
  });
10545
10925
 
10546
10926
  // packages/agents/dist/opencode/sse-bridge.js
10547
- var OpencodeSseBridge;
10927
+ var IGNORED_FRAME, OpencodeSseBridge;
10548
10928
  var init_sse_bridge = __esm({
10549
10929
  "packages/agents/dist/opencode/sse-bridge.js"() {
10930
+ IGNORED_FRAME = /^(message|storage|file|lsp|installation)\./;
10550
10931
  OpencodeSseBridge = class {
10551
10932
  controllers = /* @__PURE__ */ new Map();
10552
10933
  /** taskId → the crew's opencode server port (for permission-reply POSTs). */
@@ -10683,6 +11064,10 @@ var init_sse_bridge = __esm({
10683
11064
  return;
10684
11065
  }
10685
11066
  if (json?.type === "session.idle") {
11067
+ if (this.deps.ingest) {
11068
+ this.deps.ingest(json, taskId);
11069
+ return;
11070
+ }
10686
11071
  this.deps.emit({
10687
11072
  type: "task.turn.completed",
10688
11073
  id: taskId,
@@ -10692,6 +11077,10 @@ var init_sse_bridge = __esm({
10692
11077
  const p = json.properties;
10693
11078
  if (p?.id && p?.sessionID) {
10694
11079
  this.pendingPermByTask.set(taskId, { permID: p.id, sessionID: p.sessionID });
11080
+ if (this.deps.ingest) {
11081
+ this.deps.ingest(json, taskId);
11082
+ return;
11083
+ }
10695
11084
  const tool = p.permission ?? "a tool";
10696
11085
  const cmd = Array.isArray(p.patterns) && p.patterns.length ? `: ${p.patterns.join(" ")}` : "";
10697
11086
  this.deps.emit({
@@ -10701,11 +11090,24 @@ var init_sse_bridge = __esm({
10701
11090
  question: `opencode requests permission to run ${tool}${cmd}`,
10702
11091
  kind: tool
10703
11092
  });
11093
+ } else if (this.deps.ingest) {
11094
+ this.deps.ingest(json, taskId);
10704
11095
  }
10705
11096
  } else if (json?.type === "permission.replied") {
10706
11097
  this.pendingPermByTask.delete(taskId);
11098
+ this.deps.ingest?.(json, taskId);
11099
+ } else if (!IGNORED_FRAME.test(json?.type ?? "")) {
11100
+ this.deps.ingest?.(json, taskId);
10707
11101
  }
10708
11102
  }
11103
+ /** Test seam: exercise handleLine without an SSE stream. */
11104
+ handleLineForTest(rawLine, taskId) {
11105
+ this.handleLine(taskId, rawLine);
11106
+ }
11107
+ /** Test seam: read pendingPermByTask without exposing it publicly. */
11108
+ pendingPermForTest(taskId) {
11109
+ return this.pendingPermByTask.get(taskId);
11110
+ }
10709
11111
  };
10710
11112
  }
10711
11113
  });
@@ -11705,77 +12107,6 @@ var init_receipt_listener = __esm({
11705
12107
  }
11706
12108
  });
11707
12109
 
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
12110
  // packages/agents/dist/opencode/http-channel.js
11780
12111
  var OpencodeHttpChannel;
11781
12112
  var init_http_channel = __esm({
@@ -11887,6 +12218,47 @@ var init_http_channel = __esm({
11887
12218
  }
11888
12219
  });
11889
12220
 
12221
+ // packages/agents/dist/opencode/fact-adapter.js
12222
+ function createOpencodeFactAdapter(deps) {
12223
+ return {
12224
+ name: "opencode-sse",
12225
+ origin: "agent",
12226
+ translate(raw) {
12227
+ const f = typeof raw === "object" && raw !== null ? raw : {};
12228
+ const type = typeof f.type === "string" ? f.type : void 0;
12229
+ if (type === void 0)
12230
+ return [{ kind: "unknown", name: "non-object" }];
12231
+ const p = f.properties ?? {};
12232
+ if (type === "session.idle") {
12233
+ return [{
12234
+ kind: "turn.ended",
12235
+ turnId: typeof p.sessionID === "string" ? p.sessionID : void 0
12236
+ }];
12237
+ }
12238
+ if (type === "permission.asked") {
12239
+ if (typeof p.id !== "string" || typeof p.sessionID !== "string") {
12240
+ return [{ kind: "unknown", name: "permission.asked:incomplete" }];
12241
+ }
12242
+ const tool = typeof p.permission === "string" ? p.permission : "a tool";
12243
+ const cmd = Array.isArray(p.patterns) && p.patterns.length ? `: ${p.patterns.join(" ")}` : "";
12244
+ return [{
12245
+ kind: "permission.requested",
12246
+ question: `opencode requests permission to run ${tool}${cmd}`,
12247
+ requestId: deps.nextRequestId(),
12248
+ tool
12249
+ }];
12250
+ }
12251
+ if (type === "permission.replied")
12252
+ return [{ kind: "activity" }];
12253
+ return [{ kind: "unknown", name: type }];
12254
+ }
12255
+ };
12256
+ }
12257
+ var init_fact_adapter = __esm({
12258
+ "packages/agents/dist/opencode/fact-adapter.js"() {
12259
+ }
12260
+ });
12261
+
11890
12262
  // packages/agents/dist/index.js
11891
12263
  var dist_exports4 = {};
11892
12264
  __export(dist_exports4, {
@@ -11901,7 +12273,6 @@ __export(dist_exports4, {
11901
12273
  HEADLESS_ERROR_TAIL: () => HEADLESS_ERROR_TAIL,
11902
12274
  MARKER_END: () => MARKER_END,
11903
12275
  MARKER_START: () => MARKER_START,
11904
- OpencodeControlSource: () => OpencodeControlSource,
11905
12276
  OpencodeHttpChannel: () => OpencodeHttpChannel,
11906
12277
  OpencodeSseBridge: () => OpencodeSseBridge,
11907
12278
  ProjectionRegistry: () => ProjectionRegistry,
@@ -11921,6 +12292,7 @@ __export(dist_exports4, {
11921
12292
  createGeminiEmitter: () => createGeminiEmitter,
11922
12293
  createOpencodeDriver: () => createOpencodeDriver,
11923
12294
  createOpencodeEmitter: () => createOpencodeEmitter,
12295
+ createOpencodeFactAdapter: () => createOpencodeFactAdapter,
11924
12296
  decideCaptainMemoryWrite: () => decideCaptainMemoryWrite,
11925
12297
  deriveTranscriptPath: () => deriveTranscriptPath,
11926
12298
  detectTrailingQuestion: () => detectTrailingQuestion2,
@@ -11963,8 +12335,8 @@ var init_dist4 = __esm({
11963
12335
  init_receipt_listener();
11964
12336
  init_peer_wire();
11965
12337
  init_registry8();
11966
- init_control_source();
11967
12338
  init_http_channel();
12339
+ init_fact_adapter();
11968
12340
  }
11969
12341
  });
11970
12342
 
@@ -13963,6 +14335,10 @@ async function runCrewSpawn2(input) {
13963
14335
  emitEvent: async (p, event) => {
13964
14336
  await squadrantdCall({ kind: "event", project: p, event });
13965
14337
  },
14338
+ // #745: check the daemon's hook-confirmed state before reporting a false
14339
+ // "first turn not delivered" — swallow errors (offline/unreachable daemon)
14340
+ // so this optional check never itself breaks the spawn.
14341
+ getTaskRecord: async (p, id) => await squadrantdCall(buildStatusRequest(p, id)).catch(() => void 0),
13966
14342
  onRouted: (route) => console.log(
13967
14343
  chalk10.dim(
13968
14344
  `routed: tier=${route.tier} \u2192 ${route.agent}${route.model ? `/${route.model}` : ""} (rule: "${route.matchedRule}")`
@@ -14941,12 +15317,12 @@ function donut(t) {
14941
15317
  const C = 2 * Math.PI * r;
14942
15318
  let acc = 0;
14943
15319
  const segs = DONUT_ORDER.map((k) => {
14944
- const v = t[k];
14945
- if (v <= 0)
15320
+ const v2 = t[k];
15321
+ if (v2 <= 0)
14946
15322
  return "";
14947
- const len = t.total ? v / t.total * C : 0;
15323
+ const len = t.total ? v2 / t.total * C : 0;
14948
15324
  const rot = t.total ? acc / t.total * 360 : 0;
14949
- acc += v;
15325
+ acc += v2;
14950
15326
  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
15327
  }).join("");
14952
15328
  const track = `<circle class="donut-track" cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke-width="${w}"></circle>`;
@@ -15250,7 +15626,7 @@ function renderLiveGrid(snap, now) {
15250
15626
  const headerLabels = STATE_ORDER.map((s) => `${STATE_LABEL[s]}`);
15251
15627
  out.push(`<div class="live-header" data-live-header="">`);
15252
15628
  out.push(headerLabels.map((l) => {
15253
- const raw = Object.entries(STATE_LABEL).find(([, v]) => v === l)[0];
15629
+ const raw = Object.entries(STATE_LABEL).find(([, v2]) => v2 === l)[0];
15254
15630
  const c = stateCounts[raw];
15255
15631
  const cls = c === 0 ? "zero" : "";
15256
15632
  return `<span class="live-stat ${cls}"><span class="pdot ${STATE_CLS[raw]}"></span>${c} ${l}</span>`;
@@ -15802,7 +16178,7 @@ async function runDashboardWeb(input) {
15802
16178
  console.log(chalk14.green(`\u2714 Squadrant system dashboard \u2192 http://127.0.0.1:${handle.port}`));
15803
16179
  console.log(chalk14.dim(` polling the daemon every ${input.interval}s \xB7 localhost only \xB7 read-only \xB7 Ctrl-C to stop`));
15804
16180
  }
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) => {
16181
+ 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
16182
  try {
15807
16183
  if (opts.web) {
15808
16184
  await runDashboardWeb({ port: opts.port, interval: opts.interval ?? 5 });
@@ -16733,11 +17109,11 @@ import chalk22 from "chalk";
16733
17109
  import fs23 from "fs";
16734
17110
  import path28 from "path";
16735
17111
  import { fileURLToPath as fileURLToPath4 } from "url";
16736
- function parseScope(v) {
16737
- if (v !== "user" && v !== "project") {
17112
+ function parseScope(v2) {
17113
+ if (v2 !== "user" && v2 !== "project") {
16738
17114
  throw new Error("--scope must be 'user' or 'project'");
16739
17115
  }
16740
- return v;
17116
+ return v2;
16741
17117
  }
16742
17118
  function findPackageRoot3() {
16743
17119
  let dir = path28.dirname(fileURLToPath4(import.meta.url));
@@ -17185,8 +17561,10 @@ async function runHealDaemon(opts) {
17185
17561
  const { stdout, stderr } = opts;
17186
17562
  stdout.write("restarting squadrantd via launchd kickstart...\n");
17187
17563
  try {
17188
- opts.ensureDaemon();
17189
- stdout.write(chalk24.green("\u2714 daemon kickstart complete\n"));
17564
+ const result = opts.ensureDaemon();
17565
+ const noteSuffix = result?.note ? ` (${result.note})` : "";
17566
+ stdout.write(chalk24.green(`\u2714 daemon kickstart complete${noteSuffix}
17567
+ `));
17190
17568
  return 0;
17191
17569
  } catch (e) {
17192
17570
  stderr.write(`heal daemon failed: ${e.message}
@@ -17389,12 +17767,12 @@ async function dispatchAction(toProject, task, opts) {
17389
17767
  process.exit(1);
17390
17768
  }
17391
17769
  }
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);
17770
+ 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
17771
 
17394
17772
  // packages/cli/src/commands/group.ts
17395
17773
  init_dist2();
17396
17774
  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) => {
17775
+ 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
17776
  console.error(chalk26.yellow(
17399
17777
  `\u26A0 'squadrant group dispatch' is deprecated \u2014 use 'squadrant dispatch <project> "<task>"' instead.`
17400
17778
  ));
@@ -18070,7 +18448,7 @@ telegramCommand.command("link").argument("<project>", "project to bind to a Tele
18070
18448
  const { topicId, created } = await runTelegramLink({ project, cfg, client, stateRoot: defaultStateRoot() });
18071
18449
  console.log(chalk32.green(`${created ? "linked" : "already linked"}: ${project} \u2192 topic ${topicId}`));
18072
18450
  });
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) => {
18451
+ 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
18452
  if (!process.stdin.isTTY) {
18075
18453
  console.error(chalk32.red("setup requires a TTY \u2014 pipe input is not supported"));
18076
18454
  process.exit(1);
@@ -18580,7 +18958,7 @@ function printTree(items) {
18580
18958
  function printFlat(items) {
18581
18959
  for (const item of items) printItem(item, 0);
18582
18960
  }
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) => {
18961
+ 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
18962
  const config = loadConfig();
18585
18963
  const store = createWorkStore();
18586
18964
  purgeExpiredWorkItems(store);
@@ -18902,7 +19280,7 @@ function queryClaudeMem(dbPath, project) {
18902
19280
  createdAt: r.created_at
18903
19281
  }));
18904
19282
  const candidates = [summaryRow?.created_at, ...decisionRows.map((r) => r.created_at)].filter(
18905
- (v) => !!v
19283
+ (v2) => !!v2
18906
19284
  );
18907
19285
  const oldestCreatedAt = candidates.length > 0 ? candidates.reduce((a, b) => a < b ? a : b) : null;
18908
19286
  return {