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.
@@ -199,8 +199,8 @@ function deepMerge(base, patch) {
199
199
  if (patch === null || typeof patch !== "object" || Array.isArray(patch))
200
200
  return patch ?? base;
201
201
  const out = { ...base };
202
- for (const [k, v] of Object.entries(patch)) {
203
- out[k] = deepMerge(out[k], v);
202
+ for (const [k, v2] of Object.entries(patch)) {
203
+ out[k] = deepMerge(out[k], v2);
204
204
  }
205
205
  return out;
206
206
  }
@@ -676,8 +676,8 @@ var init_runtime_sync = __esm({
676
676
  });
677
677
 
678
678
  // packages/shared/dist/lib/tool-compat.js
679
- function parseSemVer(v) {
680
- const m = v.match(/(\d+)\.(\d+)\.(\d+)/);
679
+ function parseSemVer(v2) {
680
+ const m = v2.match(/(\d+)\.(\d+)\.(\d+)/);
681
681
  if (!m)
682
682
  return null;
683
683
  return [parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10)];
@@ -1823,12 +1823,12 @@ function isDaemonSocketLive(sockPath, timeoutMs = 500) {
1823
1823
  return;
1824
1824
  }
1825
1825
  const conn = createConnection(sockPath);
1826
- const finish = (v) => {
1826
+ const finish = (v2) => {
1827
1827
  try {
1828
1828
  conn.destroy();
1829
1829
  } catch {
1830
1830
  }
1831
- resolve4(v);
1831
+ resolve4(v2);
1832
1832
  };
1833
1833
  const timer = setTimeout(() => finish(false), timeoutMs);
1834
1834
  conn.on("connect", () => {
@@ -2494,17 +2494,20 @@ function getDaemonPid(target) {
2494
2494
  function forceKickstartAndVerify(target, opts = {}) {
2495
2495
  const pollAttempts = opts.pollAttempts ?? 15;
2496
2496
  const pollDelayMs = opts.pollDelayMs ?? 300;
2497
- const kickstartRetries = opts.kickstartRetries ?? 5;
2497
+ const kickstartRetries = opts.kickstartRetries ?? 10;
2498
2498
  const kickstartRetryDelayMs = opts.kickstartRetryDelayMs ?? 300;
2499
2499
  const pidBefore = getDaemonPid(target);
2500
+ let kickstartError = null;
2500
2501
  for (let i = 0; i < kickstartRetries; i++) {
2501
2502
  try {
2502
2503
  execFileSync3("launchctl", ["kickstart", "-k", target], { stdio: "ignore" });
2504
+ kickstartError = null;
2503
2505
  break;
2504
2506
  } catch (e) {
2505
- if (i === kickstartRetries - 1)
2506
- throw e;
2507
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, kickstartRetryDelayMs);
2507
+ kickstartError = e;
2508
+ if (i < kickstartRetries - 1) {
2509
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, kickstartRetryDelayMs);
2510
+ }
2508
2511
  }
2509
2512
  }
2510
2513
  let pidAfter = null;
@@ -2516,7 +2519,16 @@ function forceKickstartAndVerify(target, opts = {}) {
2516
2519
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, pollDelayMs);
2517
2520
  }
2518
2521
  }
2519
- return { target, pidBefore, pidAfter, restarted: pidAfter !== null && pidAfter !== pidBefore };
2522
+ const restarted = pidAfter !== null && pidAfter !== pidBefore;
2523
+ if (kickstartError && !restarted)
2524
+ throw kickstartError;
2525
+ return {
2526
+ target,
2527
+ pidBefore,
2528
+ pidAfter,
2529
+ restarted,
2530
+ ...kickstartError ? { note: "kickstart -k refused; daemon restarted by bootstrap" } : {}
2531
+ };
2520
2532
  }
2521
2533
  function isOperatorInitiatedCommand(topLevelArg) {
2522
2534
  return topLevelArg !== void 0 && OPERATOR_INITIATED_COMMANDS.has(topLevelArg);
@@ -3425,6 +3437,38 @@ import fs9 from "fs";
3425
3437
  import os4 from "os";
3426
3438
  import path9 from "path";
3427
3439
  import { randomUUID as randomUUID3 } from "crypto";
3440
+ function ensureSocksDir(dir = CC_SOCKS_DIR) {
3441
+ fs9.mkdirSync(dir, { recursive: true, mode: 448 });
3442
+ if ((fs9.statSync(dir).mode & 511) !== 448)
3443
+ fs9.chmodSync(dir, 448);
3444
+ }
3445
+ async function pollFirstTurnConfirmedAt(getTaskRecord, project, id) {
3446
+ const deadline = Date.now() + FIRST_TURN_HOOK_CONFIRM_WINDOW_MS;
3447
+ for (; ; ) {
3448
+ const rec = await getTaskRecord(project, id).catch(() => void 0);
3449
+ if (rec?.firstTurnConfirmedAt)
3450
+ return true;
3451
+ if (Date.now() >= deadline)
3452
+ return false;
3453
+ await new Promise((r) => setTimeout(r, FIRST_TURN_HOOK_POLL_INTERVAL_MS));
3454
+ }
3455
+ }
3456
+ function firstTrueOrBothFalse(a, b) {
3457
+ return new Promise((resolve4) => {
3458
+ let settledFalseCount = 0;
3459
+ const onSettle = (ok2) => {
3460
+ if (ok2) {
3461
+ resolve4(true);
3462
+ return;
3463
+ }
3464
+ settledFalseCount++;
3465
+ if (settledFalseCount === 2)
3466
+ resolve4(false);
3467
+ };
3468
+ a.then(onSettle, () => onSettle(false));
3469
+ b.then(onSettle, () => onSettle(false));
3470
+ });
3471
+ }
3428
3472
  async function listCrewPanes(runtime, workspaceId, project) {
3429
3473
  const surfaces = await runtime.listSurfaces(workspaceId);
3430
3474
  return surfaces.filter((s) => s.title && isCrewTitle(project, s.title));
@@ -3535,7 +3579,7 @@ async function runCrewSpawn(input, config, deps) {
3535
3579
  deps.onModelResolved?.({ agentName, model: crewModel });
3536
3580
  }
3537
3581
  if (agentName === "claude") {
3538
- fs9.mkdirSync(CC_SOCKS_DIR, { recursive: true });
3582
+ ensureSocksDir();
3539
3583
  const messagingSocketPath = path9.join(CC_SOCKS_DIR, `squadrant-${randomUUID3()}.sock`);
3540
3584
  const rec = await deps.dispatchCrew({
3541
3585
  provider: "claude",
@@ -3581,10 +3625,12 @@ async function runCrewSpawn(input, config, deps) {
3581
3625
  fs9.writeFileSync(spillFile, claudeFirstTurn, "utf8");
3582
3626
  claudeFirstTurn = `Full task is at ${spillFile} \u2014 cat it and follow it exactly.`;
3583
3627
  }
3584
- const claudeResult = await deps.sendFirstTurn(pane2, `${claudeFirstTurn}
3628
+ const sendPromise = deps.sendFirstTurn(pane2, `${claudeFirstTurn}
3585
3629
 
3586
3630
  ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
3587
- if (!claudeResult.delivered) {
3631
+ const scrapeDelivered = sendPromise.then((r) => r.delivered).catch(() => false);
3632
+ const delivered = hooksInstalled && deps.getTaskRecord ? await firstTrueOrBothFalse(scrapeDelivered, pollFirstTurnConfirmedAt(deps.getTaskRecord, input.project, rec.id)) : await scrapeDelivered;
3633
+ if (!delivered) {
3588
3634
  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.
3589
3635
  `);
3590
3636
  } else if (!hooksInstalled) {
@@ -3695,6 +3741,10 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps, o
3695
3741
  const heldForMin = Math.round((Date.now() - task.operatorHold.since) / 6e4);
3696
3742
  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.`);
3697
3743
  }
3744
+ const isAttentionState = task?.state === "blocked" || task?.state === "awaiting-input" || task?.state === "review";
3745
+ if (task && !isAttentionState && task.firstTurnConfirmedAt && task.task === message) {
3746
+ 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.`);
3747
+ }
3698
3748
  let reopened = false;
3699
3749
  try {
3700
3750
  if (task) {
@@ -3861,7 +3911,7 @@ async function runCrewList(project, runtime, workspaceId) {
3861
3911
  surfaceId: c.surfaceId
3862
3912
  }));
3863
3913
  }
3864
- var CC_SOCKS_DIR, FIRST_TURN_INLINE_MAX_BYTES, TEMPLATES_DIR, STATE_ROOT, CLOSE_LOOKUP_RETRIES, CLOSE_LOOKUP_RETRY_DELAY_MS;
3914
+ 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;
3865
3915
  var init_crew_spawn = __esm({
3866
3916
  "packages/core/dist/crew-spawn.js"() {
3867
3917
  init_control_channel();
@@ -3873,6 +3923,8 @@ var init_crew_spawn = __esm({
3873
3923
  FIRST_TURN_INLINE_MAX_BYTES = 1200;
3874
3924
  TEMPLATES_DIR = path9.join(os4.homedir(), ".config", "squadrant", "templates");
3875
3925
  STATE_ROOT = path9.join(os4.homedir(), ".config", "squadrant", "state");
3926
+ FIRST_TURN_HOOK_CONFIRM_WINDOW_MS = 1e5;
3927
+ FIRST_TURN_HOOK_POLL_INTERVAL_MS = 2e3;
3876
3928
  CLOSE_LOOKUP_RETRIES = 3;
3877
3929
  CLOSE_LOOKUP_RETRY_DELAY_MS = 150;
3878
3930
  }
@@ -4042,9 +4094,9 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
4042
4094
  const lastDeferred = /* @__PURE__ */ new Map();
4043
4095
  const inFlightDelivery = () => {
4044
4096
  let worst = null;
4045
- for (const [project, v] of lastDeferred) {
4046
- if (!worst || v.deferCount > worst.deferCount)
4047
- worst = { project, ...v };
4097
+ for (const [project, v2] of lastDeferred) {
4098
+ if (!worst || v2.deferCount > worst.deferCount)
4099
+ worst = { project, ...v2 };
4048
4100
  }
4049
4101
  return worst;
4050
4102
  };
@@ -6408,6 +6460,326 @@ var init_lifecycle_source = __esm({
6408
6460
  }
6409
6461
  });
6410
6462
 
6463
+ // packages/core/dist/events/fact.js
6464
+ function stampFact(raw, id) {
6465
+ return { ...raw, ...id };
6466
+ }
6467
+ var init_fact = __esm({
6468
+ "packages/core/dist/events/fact.js"() {
6469
+ }
6470
+ });
6471
+
6472
+ // packages/core/dist/events/log.js
6473
+ var FactLog;
6474
+ var init_log = __esm({
6475
+ "packages/core/dist/events/log.js"() {
6476
+ FactLog = class {
6477
+ capacity;
6478
+ buffers = /* @__PURE__ */ new Map();
6479
+ constructor(opts = {}) {
6480
+ this.capacity = opts.capacity ?? 256;
6481
+ }
6482
+ push(fact) {
6483
+ let buf = this.buffers.get(fact.taskId);
6484
+ if (!buf) {
6485
+ buf = [];
6486
+ this.buffers.set(fact.taskId, buf);
6487
+ }
6488
+ buf.push(fact);
6489
+ while (buf.length > this.capacity)
6490
+ buf.shift();
6491
+ }
6492
+ /** Oldest-first snapshot. A fresh array; later pushes never grow it. */
6493
+ recent(taskId) {
6494
+ return [...this.buffers.get(taskId) ?? []];
6495
+ }
6496
+ /** Newline-delimited JSON, one fact per line, oldest first. */
6497
+ serialize(taskId) {
6498
+ return this.recent(taskId).map((f) => JSON.stringify(f)).join("\n") + "\n";
6499
+ }
6500
+ /** Release a finished crew's buffer. */
6501
+ drop(taskId) {
6502
+ this.buffers.delete(taskId);
6503
+ }
6504
+ };
6505
+ }
6506
+ });
6507
+
6508
+ // packages/core/dist/events/invariant.js
6509
+ function freshTrace() {
6510
+ return {
6511
+ depth: 0,
6512
+ oldestOpenAt: null,
6513
+ stallReported: false,
6514
+ unknownSeen: 0,
6515
+ liveness: /* @__PURE__ */ new Map()
6516
+ };
6517
+ }
6518
+ function checkFact(trace, fact, opts) {
6519
+ const out = [];
6520
+ if (fact.origin === "inferred" && TERMINALISING.has(fact.kind)) {
6521
+ out.push(v("I4", `inferred fact "${fact.kind}" from ${fact.source} cannot terminalise alone`, fact));
6522
+ }
6523
+ switch (fact.kind) {
6524
+ case "tool.opened":
6525
+ if (trace.depth === 0) {
6526
+ trace.oldestOpenAt = fact.at;
6527
+ trace.stallReported = false;
6528
+ }
6529
+ trace.depth += 1;
6530
+ break;
6531
+ case "tool.closed":
6532
+ if (trace.depth === 0) {
6533
+ out.push(v("I1", `tool.closed from ${fact.source} with no open tool`, fact));
6534
+ } else {
6535
+ trace.depth -= 1;
6536
+ if (trace.depth === 0) {
6537
+ trace.oldestOpenAt = null;
6538
+ trace.stallReported = false;
6539
+ }
6540
+ }
6541
+ break;
6542
+ case "turn.ended":
6543
+ if (trace.depth > 0) {
6544
+ out.push(v("I2", `turn.ended with ${trace.depth} tool call(s) still open`, fact));
6545
+ trace.depth = 0;
6546
+ trace.oldestOpenAt = null;
6547
+ trace.stallReported = false;
6548
+ }
6549
+ break;
6550
+ case "unknown":
6551
+ trace.unknownSeen += 1;
6552
+ out.push(v("I5", `unrecognised frame "${fact.name}" from ${fact.source}`, fact));
6553
+ break;
6554
+ case "process.observed": {
6555
+ const prior = [...trace.liveness.entries()].find(([src, s]) => src !== fact.source && s.alive !== fact.alive && fact.at - s.at <= (opts.disagreeWindowMs ?? -1));
6556
+ if (prior) {
6557
+ out.push(v("I6", `liveness disagreement: ${prior[0]} said alive=${prior[1].alive}, ${fact.source} says alive=${fact.alive}`, fact));
6558
+ }
6559
+ trace.liveness.set(fact.source, { alive: fact.alive, at: fact.at });
6560
+ break;
6561
+ }
6562
+ default:
6563
+ break;
6564
+ }
6565
+ if (opts.stallBudgetMs !== void 0 && trace.depth > 0 && trace.oldestOpenAt !== null && !trace.stallReported && fact.at - trace.oldestOpenAt > opts.stallBudgetMs) {
6566
+ trace.stallReported = true;
6567
+ out.push(v("I3", `tool open for ${fact.at - trace.oldestOpenAt}ms, past the stall budget`, fact));
6568
+ }
6569
+ return out;
6570
+ }
6571
+ var v, TERMINALISING;
6572
+ var init_invariant = __esm({
6573
+ "packages/core/dist/events/invariant.js"() {
6574
+ v = (code, message, f) => ({ code, message, taskId: f.taskId, at: f.at });
6575
+ TERMINALISING = /* @__PURE__ */ new Set(["session.ended"]);
6576
+ }
6577
+ });
6578
+
6579
+ // packages/core/dist/events/to-control-event.js
6580
+ function toControlEvent(fact) {
6581
+ if (fact.origin === "inferred" && TERMINALISING2.has(fact.kind))
6582
+ return [];
6583
+ switch (fact.kind) {
6584
+ case "turn.ended":
6585
+ return [{ type: "task.turn.completed", id: fact.taskId, turnId: fact.turnId ?? fact.source }];
6586
+ case "permission.requested":
6587
+ return [{
6588
+ type: "task.approval.requested",
6589
+ id: fact.taskId,
6590
+ requestId: fact.requestId,
6591
+ question: fact.question,
6592
+ kind: fact.tool
6593
+ }];
6594
+ case "input.requested":
6595
+ return [{
6596
+ type: "task.input.requested",
6597
+ id: fact.taskId,
6598
+ requestId: fact.requestId,
6599
+ question: fact.question
6600
+ }];
6601
+ case "session.ended":
6602
+ return [{ type: "task.session.ended", id: fact.taskId }];
6603
+ case "session.started":
6604
+ return [{
6605
+ type: "task.started",
6606
+ id: fact.taskId,
6607
+ ...fact.pid === void 0 ? {} : { pid: fact.pid },
6608
+ ...fact.sessionId === void 0 ? {} : { sessionId: fact.sessionId }
6609
+ }];
6610
+ case "prompt.submitted":
6611
+ return [{ type: "task.first-turn.confirmed", id: fact.taskId }];
6612
+ // Liveness-only. The facade still feeds these to reduceLifecycle; they
6613
+ // simply carry no ControlEvent of their own.
6614
+ case "tool.opened":
6615
+ case "tool.closed":
6616
+ case "activity":
6617
+ case "process.observed":
6618
+ case "unknown":
6619
+ return [];
6620
+ }
6621
+ }
6622
+ var TERMINALISING2;
6623
+ var init_to_control_event = __esm({
6624
+ "packages/core/dist/events/to-control-event.js"() {
6625
+ TERMINALISING2 = /* @__PURE__ */ new Set(["session.ended"]);
6626
+ }
6627
+ });
6628
+
6629
+ // packages/core/dist/events/conformance.js
6630
+ function assert(cond, msg) {
6631
+ if (!cond)
6632
+ throw new Error(`conformance: ${msg}`);
6633
+ }
6634
+ function runAdapterConformance(adapter, samples) {
6635
+ const call = (raw) => adapter.translate(raw);
6636
+ return [
6637
+ {
6638
+ name: `${adapter.name}: never throws on garbage`,
6639
+ run: () => {
6640
+ for (const g of GARBAGE) {
6641
+ try {
6642
+ call(g);
6643
+ } catch (e) {
6644
+ throw new Error(`threw on ${JSON.stringify(g)}: ${String(e)}`);
6645
+ }
6646
+ }
6647
+ }
6648
+ },
6649
+ {
6650
+ name: `${adapter.name}: never returns null or undefined`,
6651
+ run: () => {
6652
+ for (const g of [...GARBAGE, ...samples]) {
6653
+ const out = call(g);
6654
+ assert(Array.isArray(out), `returned a non-array for ${JSON.stringify(g)}`);
6655
+ }
6656
+ }
6657
+ },
6658
+ {
6659
+ name: `${adapter.name}: an unrecognised frame yields unknown, not an empty array`,
6660
+ run: () => {
6661
+ const out = call({ type: "definitely-not-a-real-event-name" });
6662
+ assert(out.length > 0, "silently dropped an unrecognised frame (the #542 shape)");
6663
+ assert(out.every((f) => f.kind === "unknown"), "an unrecognised frame must translate to kind 'unknown'");
6664
+ }
6665
+ },
6666
+ {
6667
+ name: `${adapter.name}: recognises its own samples`,
6668
+ run: () => {
6669
+ for (const s of samples) {
6670
+ const out = call(s);
6671
+ assert(out.length > 0, `produced nothing for its own sample ${JSON.stringify(s)}`);
6672
+ assert(out.some((f) => f.kind !== "unknown"), `failed to recognise its own sample ${JSON.stringify(s)}`);
6673
+ }
6674
+ }
6675
+ },
6676
+ {
6677
+ name: `${adapter.name}: declares a constant origin`,
6678
+ run: () => {
6679
+ assert(adapter.origin === "agent" || adapter.origin === "scan" || adapter.origin === "inferred", `invalid origin "${String(adapter.origin)}"`);
6680
+ }
6681
+ }
6682
+ ];
6683
+ }
6684
+ var GARBAGE;
6685
+ var init_conformance = __esm({
6686
+ "packages/core/dist/events/conformance.js"() {
6687
+ GARBAGE = [
6688
+ null,
6689
+ void 0,
6690
+ 0,
6691
+ "",
6692
+ "not json",
6693
+ [],
6694
+ {},
6695
+ { type: 42 },
6696
+ { type: "definitely-not-a-real-event-name" }
6697
+ ];
6698
+ }
6699
+ });
6700
+
6701
+ // packages/core/dist/events/source.js
6702
+ function createEventsSource(opts) {
6703
+ const now = opts.now ?? (() => Date.now());
6704
+ const log = new FactLog({ capacity: opts.capacity });
6705
+ const adapters = new Map(opts.adapters.map((a) => [a.name, a]));
6706
+ const traces = /* @__PURE__ */ new Map();
6707
+ const seqs = /* @__PURE__ */ new Map();
6708
+ let deps;
6709
+ const traceFor = (taskId) => {
6710
+ let t = traces.get(taskId);
6711
+ if (!t) {
6712
+ t = freshTrace();
6713
+ traces.set(taskId, t);
6714
+ }
6715
+ return t;
6716
+ };
6717
+ const nextSeq = (taskId) => {
6718
+ const n = seqs.get(taskId) ?? 0;
6719
+ seqs.set(taskId, n + 1);
6720
+ return n;
6721
+ };
6722
+ return {
6723
+ name: "events",
6724
+ start(d) {
6725
+ deps = d;
6726
+ },
6727
+ stop() {
6728
+ deps = void 0;
6729
+ },
6730
+ health() {
6731
+ return { active: deps !== void 0, error: null };
6732
+ },
6733
+ recent(taskId) {
6734
+ return log.recent(taskId);
6735
+ },
6736
+ dump(taskId) {
6737
+ return log.serialize(taskId);
6738
+ },
6739
+ ingest(source, raw, hint) {
6740
+ const adapter = adapters.get(source);
6741
+ if (!adapter || !deps)
6742
+ return;
6743
+ const rec = deps.resolve(hint);
6744
+ if (!rec)
6745
+ return;
6746
+ const taskId = rec.id;
6747
+ const at = now();
6748
+ let produced;
6749
+ try {
6750
+ const out = adapter.translate(raw);
6751
+ produced = Array.isArray(out) ? out : [{ kind: "unknown", name: `${source} returned non-array` }];
6752
+ } catch (e) {
6753
+ opts.log?.(`events: adapter ${source} threw: ${String(e)}`);
6754
+ produced = [{ kind: "unknown", name: `${source} threw` }];
6755
+ }
6756
+ for (const rawFact of produced) {
6757
+ const fact = stampFact(rawFact, {
6758
+ seq: nextSeq(taskId),
6759
+ taskId,
6760
+ at,
6761
+ source,
6762
+ origin: adapter.origin
6763
+ });
6764
+ log.push(fact);
6765
+ for (const v2 of checkFact(traceFor(taskId), fact, opts.check ?? {})) {
6766
+ opts.onViolation(v2);
6767
+ }
6768
+ for (const ev of toControlEvent(fact))
6769
+ opts.emit(ev);
6770
+ }
6771
+ }
6772
+ };
6773
+ }
6774
+ var init_source = __esm({
6775
+ "packages/core/dist/events/source.js"() {
6776
+ init_fact();
6777
+ init_log();
6778
+ init_invariant();
6779
+ init_to_control_event();
6780
+ }
6781
+ });
6782
+
6411
6783
  // packages/core/dist/index.js
6412
6784
  var dist_exports = {};
6413
6785
  __export(dist_exports, {
@@ -6422,6 +6794,7 @@ __export(dist_exports, {
6422
6794
  DEFAULT_TASK_TIMEOUT_MS: () => DEFAULT_TASK_TIMEOUT_MS,
6423
6795
  DeferDelivery: () => DeferDelivery,
6424
6796
  FIRST_TURN_INLINE_MAX_BYTES: () => FIRST_TURN_INLINE_MAX_BYTES,
6797
+ FactLog: () => FactLog,
6425
6798
  GROUP_DISPATCH_WARMUP_POLL_MS: () => GROUP_DISPATCH_WARMUP_POLL_MS,
6426
6799
  GROUP_DISPATCH_WARMUP_TIMEOUT_MS: () => GROUP_DISPATCH_WARMUP_TIMEOUT_MS,
6427
6800
  IDLE_DEBOUNCE_MS: () => IDLE_DEBOUNCE_MS,
@@ -6451,6 +6824,7 @@ __export(dist_exports, {
6451
6824
  capAllowed: () => capAllowed,
6452
6825
  capOutput: () => capOutput,
6453
6826
  captainSocketPath: () => captainSocketPath,
6827
+ checkFact: () => checkFact,
6454
6828
  classifyHealth: () => classifyHealth,
6455
6829
  closeWorkItem: () => closeWorkItem,
6456
6830
  computeTemplateHash: () => computeTemplateHash,
@@ -6463,6 +6837,7 @@ __export(dist_exports, {
6463
6837
  createDirectCrewPaneReader: () => createDirectCrewPaneReader,
6464
6838
  createDirectSurfaceLivenessProbe: () => createDirectSurfaceLivenessProbe,
6465
6839
  createEnsureCaptainAlive: () => createEnsureCaptainAlive,
6840
+ createEventsSource: () => createEventsSource,
6466
6841
  createInteractiveProbe: () => createInteractiveProbe,
6467
6842
  createIsCaptainAlive: () => createIsCaptainAlive,
6468
6843
  createLaunch: () => createLaunch,
@@ -6494,6 +6869,7 @@ __export(dist_exports, {
6494
6869
  encodeFrame: () => encodeFrame,
6495
6870
  encodeMsg: () => encodeMsg,
6496
6871
  ensureDaemon: () => ensureDaemon,
6872
+ ensureSocksDir: () => ensureSocksDir,
6497
6873
  evaluateStall: () => evaluateStall,
6498
6874
  exitMarkerPath: () => exitMarkerPath,
6499
6875
  fallsBackToPane: () => fallsBackToPane,
@@ -6505,6 +6881,7 @@ __export(dist_exports, {
6505
6881
  formatInbound: () => formatInbound,
6506
6882
  formatInboundReceipt: () => formatInboundReceipt,
6507
6883
  formatLifecycle: () => formatLifecycle,
6884
+ freshTrace: () => freshTrace,
6508
6885
  getDaemonPid: () => getDaemonPid,
6509
6886
  healCmdFor: () => healCmdFor,
6510
6887
  isAuthorized: () => isAuthorized,
@@ -6561,6 +6938,7 @@ __export(dist_exports, {
6561
6938
  resolveSetupUserId: () => resolveSetupUserId,
6562
6939
  restartDaemonIfRunning: () => restartDaemonIfRunning,
6563
6940
  rotateIfNeeded: () => rotateIfNeeded,
6941
+ runAdapterConformance: () => runAdapterConformance,
6564
6942
  runCrewAnswer: () => runCrewAnswer,
6565
6943
  runCrewClose: () => runCrewClose,
6566
6944
  runCrewList: () => runCrewList,
@@ -6595,12 +6973,14 @@ __export(dist_exports, {
6595
6973
  sideNameFromTitle: () => sideNameFromTitle,
6596
6974
  sideNextAutoName: () => sideNextAutoName,
6597
6975
  sideTitleFor: () => sideTitleFor,
6976
+ stampFact: () => stampFact,
6598
6977
  startDaemon: () => startDaemon,
6599
6978
  startServer: () => startServer,
6600
6979
  stripBotMention: () => stripBotMention,
6601
6980
  surfaceVerdict: () => surfaceVerdict,
6602
6981
  timeoutGate: () => timeoutGate,
6603
6982
  titleFor: () => titleFor,
6983
+ toControlEvent: () => toControlEvent,
6604
6984
  topicKey: () => topicKey,
6605
6985
  topicName: () => topicName,
6606
6986
  tryAcquireDaemonLock: () => tryAcquireDaemonLock,
@@ -6647,6 +7027,12 @@ var init_dist2 = __esm({
6647
7027
  init_crew_spawn();
6648
7028
  init_crew_answer();
6649
7029
  init_lifecycle_source();
7030
+ init_fact();
7031
+ init_log();
7032
+ init_invariant();
7033
+ init_to_control_event();
7034
+ init_conformance();
7035
+ init_source();
6650
7036
  init_control_channel();
6651
7037
  init_captain_channel();
6652
7038
  }
@@ -6663,6 +7049,7 @@ init_dist2();
6663
7049
  init_dist2();
6664
7050
  init_dist2();
6665
7051
  init_dist2();
7052
+ init_dist2();
6666
7053
  import { join as join22, dirname as dirname4, resolve as resolve3 } from "path";
6667
7054
  import { homedir as homedir13 } from "os";
6668
7055
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -7301,9 +7688,9 @@ ${directive}` : directive;
7301
7688
  function withTimeout(p, ms, msg) {
7302
7689
  return new Promise((resolve4, reject) => {
7303
7690
  const t = setTimeout(() => reject(new Error(msg)), ms);
7304
- p.then((v) => {
7691
+ p.then((v2) => {
7305
7692
  clearTimeout(t);
7306
- resolve4(v);
7693
+ resolve4(v2);
7307
7694
  }, (e) => {
7308
7695
  clearTimeout(t);
7309
7696
  reject(e);
@@ -7312,6 +7699,7 @@ function withTimeout(p, ms, msg) {
7312
7699
  }
7313
7700
 
7314
7701
  // packages/agents/dist/opencode/sse-bridge.js
7702
+ var IGNORED_FRAME = /^(message|storage|file|lsp|installation)\./;
7315
7703
  var OpencodeSseBridge = class {
7316
7704
  controllers = /* @__PURE__ */ new Map();
7317
7705
  /** taskId → the crew's opencode server port (for permission-reply POSTs). */
@@ -7448,6 +7836,10 @@ var OpencodeSseBridge = class {
7448
7836
  return;
7449
7837
  }
7450
7838
  if (json?.type === "session.idle") {
7839
+ if (this.deps.ingest) {
7840
+ this.deps.ingest(json, taskId);
7841
+ return;
7842
+ }
7451
7843
  this.deps.emit({
7452
7844
  type: "task.turn.completed",
7453
7845
  id: taskId,
@@ -7457,6 +7849,10 @@ var OpencodeSseBridge = class {
7457
7849
  const p = json.properties;
7458
7850
  if (p?.id && p?.sessionID) {
7459
7851
  this.pendingPermByTask.set(taskId, { permID: p.id, sessionID: p.sessionID });
7852
+ if (this.deps.ingest) {
7853
+ this.deps.ingest(json, taskId);
7854
+ return;
7855
+ }
7460
7856
  const tool = p.permission ?? "a tool";
7461
7857
  const cmd = Array.isArray(p.patterns) && p.patterns.length ? `: ${p.patterns.join(" ")}` : "";
7462
7858
  this.deps.emit({
@@ -7466,11 +7862,24 @@ var OpencodeSseBridge = class {
7466
7862
  question: `opencode requests permission to run ${tool}${cmd}`,
7467
7863
  kind: tool
7468
7864
  });
7865
+ } else if (this.deps.ingest) {
7866
+ this.deps.ingest(json, taskId);
7469
7867
  }
7470
7868
  } else if (json?.type === "permission.replied") {
7471
7869
  this.pendingPermByTask.delete(taskId);
7870
+ this.deps.ingest?.(json, taskId);
7871
+ } else if (!IGNORED_FRAME.test(json?.type ?? "")) {
7872
+ this.deps.ingest?.(json, taskId);
7472
7873
  }
7473
7874
  }
7875
+ /** Test seam: exercise handleLine without an SSE stream. */
7876
+ handleLineForTest(rawLine, taskId) {
7877
+ this.handleLine(taskId, rawLine);
7878
+ }
7879
+ /** Test seam: read pendingPermByTask without exposing it publicly. */
7880
+ pendingPermForTest(taskId) {
7881
+ return this.pendingPermByTask.get(taskId);
7882
+ }
7474
7883
  };
7475
7884
 
7476
7885
  // packages/agents/dist/interactive/claude.js
@@ -8038,70 +8447,41 @@ var ClaudeReceiptListener = class {
8038
8447
  }
8039
8448
  };
8040
8449
 
8041
- // packages/agents/dist/opencode/control-source.js
8042
- var OpencodeControlSource = class {
8043
- name = "opencode-control";
8044
- deps;
8045
- active = false;
8046
- cache = /* @__PURE__ */ new Map();
8047
- start(deps) {
8048
- this.deps = deps;
8049
- this.active = true;
8050
- }
8051
- stop() {
8052
- this.deps = void 0;
8053
- this.active = false;
8054
- this.cache.clear();
8055
- }
8056
- /** Push-only source — no fallible startup of its own. */
8057
- health() {
8058
- return { active: this.active, error: null };
8059
- }
8060
- /** Liveness floor: origin must be "scan" and must not assert needsInput. */
8061
- snapshot(taskId) {
8062
- const s = this.cache.get(taskId);
8063
- if (!s)
8064
- return void 0;
8065
- return { ...s, origin: "scan", state: s.state === "needsInput" ? "running" : s.state };
8066
- }
8067
- /**
8068
- * Feed one ControlEvent from OpencodeSseBridge into the port.
8069
- * Wired in squadrantd.ts as: emit = (ev) => { source.observe(ev); …existing… }
8070
- */
8071
- observe(ev) {
8072
- if (!this.deps)
8073
- return;
8074
- const snap = toSnapshot2(ev);
8075
- if (!snap)
8076
- return;
8077
- this.cache.set(snap.taskId, snap);
8078
- this.deps.report(snap);
8079
- }
8080
- };
8081
- function toSnapshot2(ev) {
8082
- const now = Date.now();
8083
- switch (ev.type) {
8084
- // A permission was answered on the bus and the turn resumed.
8085
- case "task.started":
8086
- return { taskId: ev.id, state: "running", alive: true, origin: "agent", at: now };
8087
- // session.idle — the turn finished. Liveness, NOT completion (anti-#2576).
8088
- case "task.turn.completed":
8089
- return { taskId: ev.id, state: "idle", alive: true, origin: "agent", at: now };
8090
- // permission.asked — opencode STATES it is gated. No guessing from pixels.
8091
- case "task.approval.requested":
8092
- return {
8093
- taskId: ev.id,
8094
- state: "needsInput",
8095
- alive: true,
8096
- origin: "agent",
8097
- at: now,
8098
- detail: { note: ev.question, reason: ev.kind }
8099
- };
8100
- // Terminal (task.done/blocked/cancelled) and notify-only events are ignored:
8101
- // terminal state comes exclusively from `squadrant crew signal`.
8102
- default:
8103
- return null;
8104
- }
8450
+ // packages/agents/dist/opencode/fact-adapter.js
8451
+ function createOpencodeFactAdapter(deps) {
8452
+ return {
8453
+ name: "opencode-sse",
8454
+ origin: "agent",
8455
+ translate(raw) {
8456
+ const f = typeof raw === "object" && raw !== null ? raw : {};
8457
+ const type = typeof f.type === "string" ? f.type : void 0;
8458
+ if (type === void 0)
8459
+ return [{ kind: "unknown", name: "non-object" }];
8460
+ const p = f.properties ?? {};
8461
+ if (type === "session.idle") {
8462
+ return [{
8463
+ kind: "turn.ended",
8464
+ turnId: typeof p.sessionID === "string" ? p.sessionID : void 0
8465
+ }];
8466
+ }
8467
+ if (type === "permission.asked") {
8468
+ if (typeof p.id !== "string" || typeof p.sessionID !== "string") {
8469
+ return [{ kind: "unknown", name: "permission.asked:incomplete" }];
8470
+ }
8471
+ const tool = typeof p.permission === "string" ? p.permission : "a tool";
8472
+ const cmd = Array.isArray(p.patterns) && p.patterns.length ? `: ${p.patterns.join(" ")}` : "";
8473
+ return [{
8474
+ kind: "permission.requested",
8475
+ question: `opencode requests permission to run ${tool}${cmd}`,
8476
+ requestId: deps.nextRequestId(),
8477
+ tool
8478
+ }];
8479
+ }
8480
+ if (type === "permission.replied")
8481
+ return [{ kind: "activity" }];
8482
+ return [{ kind: "unknown", name: type }];
8483
+ }
8484
+ };
8105
8485
  }
8106
8486
 
8107
8487
  // packages/workspaces/dist/runtimes/cmux.js
@@ -9556,6 +9936,7 @@ function registerSenderIdentity(socketPath) {
9556
9936
  }
9557
9937
  async function sharedReceiptListener() {
9558
9938
  if (shared) return shared;
9939
+ ensureSocksDir();
9559
9940
  const socketPath = `${CC_SOCKS_DIR}/squadrantd-${process.pid}.sock`;
9560
9941
  const listener = new ClaudeReceiptListener({
9561
9942
  socketPath,
@@ -9716,7 +10097,37 @@ function startSquadrantd(opts = {}) {
9716
10097
  codexAppServerSource.observe(ev);
9717
10098
  }
9718
10099
  });
9719
- const opencodeControlSource = new OpencodeControlSource();
10100
+ const violationLogState = /* @__PURE__ */ new Map();
10101
+ const VIOLATION_LOG_SUMMARY_MS = 6e4;
10102
+ const onEventsViolation = (v2) => {
10103
+ const key = `${v2.taskId}:${v2.code}`;
10104
+ const prev = violationLogState.get(key);
10105
+ if (!prev) {
10106
+ violationLogState.set(key, { count: 1, loggedAt: Date.now() });
10107
+ log(`[events] ${v2.code} ${v2.taskId}: ${v2.message}`);
10108
+ return;
10109
+ }
10110
+ prev.count++;
10111
+ const now = Date.now();
10112
+ if (now - prev.loggedAt > VIOLATION_LOG_SUMMARY_MS) {
10113
+ log(`[events] ${v2.code} ${v2.taskId}: seen ${prev.count}x since last log (latest: ${v2.message})`);
10114
+ prev.count = 0;
10115
+ prev.loggedAt = now;
10116
+ }
10117
+ };
10118
+ const opencodeRequestIds = { n: 1 };
10119
+ const eventsSource = createEventsSource({
10120
+ adapters: [createOpencodeFactAdapter({ nextRequestId: () => opencodeRequestIds.n++ })],
10121
+ emit: (ev) => {
10122
+ const found = store.listAll().find((r) => r.id === ev.id);
10123
+ if (!found) return;
10124
+ void ctx.d.handle({ kind: "event", project: found.project, event: ev });
10125
+ if (ev.type === "task.approval.requested")
10126
+ ctx.schedulePromotion(ev.id, ev.requestId, "approval", ev.question);
10127
+ },
10128
+ onViolation: onEventsViolation,
10129
+ check: { stallBudgetMs: 6e4, disagreeWindowMs: 5e3 }
10130
+ });
9720
10131
  const opencodeBridge = opts.opencodeBridge ?? new OpencodeSseBridge({
9721
10132
  emit: (ev) => {
9722
10133
  const found = store.listAll().find((r) => r.id === ev.id);
@@ -9724,8 +10135,8 @@ function startSquadrantd(opts = {}) {
9724
10135
  void ctx.d.handle({ kind: "event", project: found.project, event: ev });
9725
10136
  if (ev.type === "task.approval.requested")
9726
10137
  ctx.schedulePromotion(ev.id, ev.requestId, "approval", ev.question);
9727
- opencodeControlSource.observe(ev);
9728
10138
  },
10139
+ ingest: (raw, taskId) => eventsSource.ingest("opencode-sse", raw, { taskId }),
9729
10140
  log
9730
10141
  });
9731
10142
  const cmuxEventsBridge = opts.cmuxEventsBridge ?? new CmuxEventsBridge({
@@ -9750,11 +10161,11 @@ function startSquadrantd(opts = {}) {
9750
10161
  ctx.cmuxEventsBridge = cmuxEventsBridge;
9751
10162
  const claudePeerRegistrySource = new ClaudePeerRegistrySource({ log });
9752
10163
  ctx.lifecycleSources = [
10164
+ eventsSource,
9753
10165
  cmuxStoreSource,
9754
10166
  nativeHookSource,
9755
10167
  codexAppServerSource,
9756
- claudePeerRegistrySource,
9757
- opencodeControlSource
10168
+ claudePeerRegistrySource
9758
10169
  ];
9759
10170
  const tgCfg = loadConfig().telegram;
9760
10171
  ctx.telegramBridge = opts.telegramBridge ?? (tgCfg && !process.env.VITEST ? buildTelegramBridge(
@@ -9810,6 +10221,37 @@ ${buildCompletionProtocol(rec.id, rec.project)}`;
9810
10221
  }
9811
10222
  });
9812
10223
  const h = startDaemon(ctx, { ...opts, launchHeadless }, PKG_VERSION);
10224
+ const startEventsLifecycleSource = () => {
10225
+ let eventsTaskIndex;
10226
+ let eventsTaskIndexAt = 0;
10227
+ const EVENTS_TASK_INDEX_TTL_MS = 500;
10228
+ const eventsSourceDeps = {
10229
+ resolve: (hint) => {
10230
+ if (!hint.taskId) return void 0;
10231
+ const now = Date.now();
10232
+ if (!eventsTaskIndex || now - eventsTaskIndexAt > EVENTS_TASK_INDEX_TTL_MS) {
10233
+ eventsTaskIndex = /* @__PURE__ */ new Map();
10234
+ for (const r of store.listAll()) {
10235
+ if (!TERMINAL_STATES.has(r.state)) eventsTaskIndex.set(r.id, { id: r.id });
10236
+ }
10237
+ eventsTaskIndexAt = now;
10238
+ }
10239
+ const cached = eventsTaskIndex.get(hint.taskId);
10240
+ if (cached) return cached;
10241
+ return store.listAll().find(
10242
+ (r) => r.id === hint.taskId && !TERMINAL_STATES.has(r.state)
10243
+ );
10244
+ },
10245
+ report: () => {
10246
+ },
10247
+ log
10248
+ };
10249
+ try {
10250
+ eventsSource.start(eventsSourceDeps);
10251
+ } catch (e) {
10252
+ log(`events source start failed: ${e.message}`);
10253
+ }
10254
+ };
9813
10255
  if (!process.env.VITEST) {
9814
10256
  const prevSnaps = /* @__PURE__ */ new Map();
9815
10257
  const storeDeps = {
@@ -9936,19 +10378,9 @@ ${buildCompletionProtocol(rec.id, rec.project)}`;
9936
10378
  } catch (e) {
9937
10379
  log(`claude peer registry source start failed: ${e.message}`);
9938
10380
  }
9939
- const opencodeSourceDeps = {
9940
- resolve: () => void 0,
9941
- report: () => {
9942
- },
9943
- // read-only slice: caching is internal to the source
9944
- log
9945
- };
9946
- try {
9947
- opencodeControlSource.start(opencodeSourceDeps);
9948
- } catch (e) {
9949
- log(`opencode control source start failed: ${e.message}`);
9950
- }
10381
+ startEventsLifecycleSource();
9951
10382
  }
10383
+ if (opts.forceStartEventsSource) startEventsLifecycleSource();
9952
10384
  if (!process.env.VITEST) {
9953
10385
  try {
9954
10386
  const buildMtimeMs = statSync4(SELF_PATH2).mtimeMs;
@@ -9985,7 +10417,7 @@ ${buildCompletionProtocol(rec.id, rec.project)}`;
9985
10417
  } catch {
9986
10418
  }
9987
10419
  try {
9988
- opencodeControlSource.stop();
10420
+ eventsSource.stop();
9989
10421
  } catch {
9990
10422
  }
9991
10423
  return origStop(reason);