squadrant 0.13.2 → 0.13.4

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.
@@ -544,6 +544,9 @@ function reduce(rec, ev, now) {
544
544
  case "task.reattached":
545
545
  return stampAttempt(base, {}, now);
546
546
  case "task.first-turn.confirmed":
547
+ if (rec.firstTurnConfirmedAt) {
548
+ return { ...rec, lastEvent: "task.progress" };
549
+ }
547
550
  return { ...rec, firstTurnConfirmedAt: now, lastEvent: ev.type };
548
551
  case "task.stalled":
549
552
  case "task.idle":
@@ -860,6 +863,25 @@ function createDaemon(deps) {
860
863
  continue;
861
864
  }
862
865
  }
866
+ if (r.mode === "interactive" && r.state === "submitted" && !r.firstTurnConfirmedAt) {
867
+ const elapsed = t - r.lastHeartbeat;
868
+ if (elapsed > r.heartbeatBudgetMs) {
869
+ if (deps.notify && quietNotifiedAt.get(r.id) !== r.lastHeartbeat) {
870
+ quietNotifiedAt.set(r.id, r.lastHeartbeat);
871
+ const tag = crewTag(r);
872
+ const synthEvent = { type: "task.quiet", id: r.id, quietMs: elapsed };
873
+ const message = `\u26A0\uFE0F CREW UNDELIVERED ${tag}: first turn may not have landed (crew never started) \u2014 re-send the task or check the spawn.`;
874
+ try {
875
+ const p = deps.notify({ project: r.project, message, record: r, event: synthEvent });
876
+ if (p && typeof p.catch === "function")
877
+ p.catch(() => {
878
+ });
879
+ } catch {
880
+ }
881
+ }
882
+ continue;
883
+ }
884
+ }
863
885
  const idle = evaluateStall(r, t);
864
886
  if (idle) {
865
887
  store.put(idle);
@@ -1952,6 +1974,7 @@ var CaptainDelivery = class {
1952
1974
  // packages/core/dist/daemon/delivery-loop.js
1953
1975
  var CURSOR_SUBSCRIBER = "captain";
1954
1976
  var CAPTAIN_GONE_STREAK_K = 3;
1977
+ var TERMINAL_KINDS = /* @__PURE__ */ new Set(["task.done", "task.failed", "task.cancelled", "task.blocked"]);
1955
1978
  function discoverCaptainSurface(surfaces, captainTitle) {
1956
1979
  return surfaces.find((s) => s.title === captainTitle) ?? null;
1957
1980
  }
@@ -2044,13 +2067,19 @@ function createDelivery(ctx, daemonCmux) {
2044
2067
  }
2045
2068
  for await (const entry of readFromCursor({ stateRoot, project, fromSeq: lastAcked + 1 })) {
2046
2069
  if (new Date(entry.ts).getTime() < sessionStartMs - STALE_THRESHOLD_MS) {
2047
- await writeCursor({ stateRoot, project, subscriber: CURSOR_SUBSCRIBER, lastAckedSeq: entry.seq });
2048
- continue;
2070
+ if (!TERMINAL_KINDS.has(entry.kind)) {
2071
+ log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=stale-skipped`);
2072
+ await writeCursor({ stateRoot, project, subscriber: CURSOR_SUBSCRIBER, lastAckedSeq: entry.seq });
2073
+ continue;
2074
+ }
2075
+ log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=stale-terminal-deliver`);
2049
2076
  }
2050
2077
  const result = await d.deliver(entry, (text, sendOpts) => cmux2.send(surface, text, sendOpts));
2051
2078
  if ("delivered" in result) {
2079
+ log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=delivered`);
2052
2080
  await writeCursor({ stateRoot, project, subscriber: CURSOR_SUBSCRIBER, lastAckedSeq: entry.seq });
2053
2081
  } else {
2082
+ log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=deferred`);
2054
2083
  break;
2055
2084
  }
2056
2085
  }
@@ -4366,7 +4395,7 @@ function parseDraftFromScreen(screen) {
4366
4395
  }
4367
4396
  return "";
4368
4397
  }
4369
- function readInputBoxRaw(screen) {
4398
+ function readInputBoxRaw(screen, opts) {
4370
4399
  if (!screen)
4371
4400
  return null;
4372
4401
  const lines = screen.split(/\r?\n/);
@@ -4392,7 +4421,8 @@ function readInputBoxRaw(screen) {
4392
4421
  s = s.replace(/\s*[▌█▔▎▏]+\s*$/, "");
4393
4422
  parts.push(s);
4394
4423
  }
4395
- return parts.join("").replace(/\s+$/, "");
4424
+ const joined = parts.join("");
4425
+ return opts?.trim === false ? joined : joined.replace(/\s+$/, "");
4396
4426
  }
4397
4427
  function sendDebugEnabled() {
4398
4428
  return !!process.env.SQUADRANT_DEBUG_SEND;
@@ -4406,6 +4436,19 @@ function classifySendOutcome(payload, postBox) {
4406
4436
  return "stuck";
4407
4437
  return "unknown";
4408
4438
  }
4439
+ function classifyDraftLiveness(before, after) {
4440
+ if (before === null || after === null)
4441
+ return "inconclusive";
4442
+ if (after === "")
4443
+ return "no-draft";
4444
+ if (before.length > 0) {
4445
+ const segs = [...new Intl.Segmenter().segment(before)];
4446
+ const expected = segs.slice(0, -1).map((s) => s.segment).join("").replace(/\s+$/, "");
4447
+ if (after === expected)
4448
+ return "real-draft";
4449
+ }
4450
+ return "inconclusive";
4451
+ }
4409
4452
  function createCmuxDriver() {
4410
4453
  return {
4411
4454
  name: "cmux",
@@ -4603,18 +4646,38 @@ function createCmuxDriver() {
4603
4646
  if (!opts?.probe)
4604
4647
  throw new DeferDelivery(draft);
4605
4648
  const before = readInputBoxRaw(screen);
4649
+ const rawBefore = readInputBoxRaw(screen, { trim: false });
4606
4650
  await cmux(["send-key", "--workspace", ws, "--surface", sf, "backspace"]);
4651
+ await new Promise((r) => setTimeout(r, 50));
4607
4652
  let afterScreen = "";
4608
4653
  try {
4609
4654
  afterScreen = await cmux(["read-screen", "--workspace", ws, "--surface", sf]);
4610
4655
  } catch {
4611
4656
  }
4612
4657
  const after = readInputBoxRaw(afterScreen);
4613
- if (before !== null && after !== null && after.length > 0 && after === before.slice(0, -1)) {
4614
- await cmux(["send", "--workspace", ws, "--surface", sf, before.slice(-1)]);
4658
+ const rawAfter = readInputBoxRaw(afterScreen, { trim: false });
4659
+ const liveness = classifyDraftLiveness(before, after);
4660
+ if (liveness === "real-draft") {
4661
+ const segs = before ? [...new Intl.Segmenter().segment(before)] : [];
4662
+ const lastGrapheme = segs.length > 0 ? segs[segs.length - 1].segment : before.slice(-1);
4663
+ await cmux(["send", "--workspace", ws, "--surface", sf, lastGrapheme]);
4615
4664
  throw new DeferDelivery(draft);
4616
4665
  }
4617
- await deliver();
4666
+ if (liveness === "no-draft") {
4667
+ await deliver();
4668
+ return;
4669
+ }
4670
+ if (rawBefore !== null && rawAfter !== null) {
4671
+ if (rawBefore !== rawAfter) {
4672
+ const segs = [...new Intl.Segmenter().segment(rawBefore)];
4673
+ const lastGrapheme = segs.length > 0 ? segs[segs.length - 1].segment : rawBefore.slice(-1);
4674
+ await cmux(["send", "--workspace", ws, "--surface", sf, lastGrapheme]);
4675
+ throw new DeferDelivery(draft);
4676
+ }
4677
+ await deliver();
4678
+ return;
4679
+ }
4680
+ throw new DeferDelivery(draft);
4618
4681
  },
4619
4682
  async listSurfaces(workspaceId) {
4620
4683
  let output;