squadrant 0.16.4 → 0.16.5

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.
@@ -490,6 +490,9 @@ function stampAttempt(rec, patch, now) {
490
490
  attempts.push(last);
491
491
  return { ...rec, attempts };
492
492
  }
493
+ function isStickyAttention(state) {
494
+ return state === "blocked" || state === "review";
495
+ }
493
496
  function nextPendingTool(current, ev, now) {
494
497
  if (ev.note === "agent.hook.PreToolUse")
495
498
  return { name: ev.tool ?? "tool", since: now };
@@ -518,7 +521,7 @@ function reduce(rec, ev, now) {
518
521
  };
519
522
  case "task.progress": {
520
523
  const pendingTool = nextPendingTool(rec.pendingTool, ev, now);
521
- if (rec.state === "blocked")
524
+ if (isStickyAttention(rec.state))
522
525
  return { ...rec, lastHeartbeat: now, lastEvent: ev.type, pendingTool };
523
526
  const b = { ...base, pendingTool };
524
527
  if (rec.state === "awaiting-input" || rec.state === "stalled")
@@ -526,7 +529,7 @@ function reduce(rec, ev, now) {
526
529
  return stampAttempt(b, {}, now);
527
530
  }
528
531
  case "heartbeat":
529
- if (rec.state === "blocked")
532
+ if (isStickyAttention(rec.state))
530
533
  return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
531
534
  if (rec.state === "awaiting-input")
532
535
  return { ...base, state: "working" };
@@ -535,7 +538,12 @@ function reduce(rec, ev, now) {
535
538
  if (rec.state === "blocked")
536
539
  return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
537
540
  return { ...base, state: "blocked", question: ev.question, pendingTool: void 0 };
541
+ case "task.review":
542
+ return { ...base, state: "review", reviewNote: ev.message, pendingTool: void 0 };
538
543
  case "task.done":
544
+ if (rec.state === "review" && ev.source !== "approve") {
545
+ return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
546
+ }
539
547
  return { ...base, state: "done", resultRef: ev.resultRef, parseWarning: ev.parseWarning };
540
548
  case "task.failed":
541
549
  return { ...base, state: "failed", error: ev.error, exitCode: ev.exitCode };
@@ -548,7 +556,7 @@ function reduce(rec, ev, now) {
548
556
  case "task.turn.started":
549
557
  return { ...stampAttempt(base, {}, now), state: "working", pendingTool: void 0 };
550
558
  case "task.turn.completed":
551
- if (rec.state === "blocked")
559
+ if (isStickyAttention(rec.state))
552
560
  return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
553
561
  if (rec.pendingTool)
554
562
  return stampAttempt(base, {}, now);
@@ -606,8 +614,8 @@ var DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS = 6e4;
606
614
  var DEFAULT_TASK_TIMEOUT_MS = 8 * 60 * 60 * 1e3;
607
615
  var TERMINAL_RECORD_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
608
616
  var TERMINAL_RECORD_KEEP_PER_PROJECT = 20;
609
- var ATTENTION_STATES = /* @__PURE__ */ new Set(["done", "blocked", "failed", "stalled", "awaiting-input"]);
610
- var REAPABLE_SURFACE_STATES = /* @__PURE__ */ new Set(["working", "stalled", "awaiting-input", "blocked"]);
617
+ var ATTENTION_STATES = /* @__PURE__ */ new Set(["done", "blocked", "review", "failed", "stalled", "awaiting-input"]);
618
+ var REAPABLE_SURFACE_STATES = /* @__PURE__ */ new Set(["working", "stalled", "awaiting-input", "blocked", "review"]);
611
619
  var IDLE_DEBOUNCE_MS = 12e3;
612
620
  function shortId(id) {
613
621
  return id.slice(0, 8);
@@ -629,6 +637,10 @@ function formatMessage(rec, event) {
629
637
  }
630
638
  case "blocked":
631
639
  return `CREW BLOCKED ${tag}: ${(rec.question ?? "(no question)").trim()}`;
640
+ case "review": {
641
+ const note = (rec.reviewNote ?? "").trim();
642
+ return `CREW REVIEW ${tag}: ${note || "ready for review"} \u2014 run 'squadrant diff ${rec.project} ${rec.name ?? rec.id}' then 'squadrant crew approve' or send feedback.`;
643
+ }
632
644
  case "failed":
633
645
  return `CREW FAILED ${tag}: ${(rec.error ?? "(no error)").trim()}`;
634
646
  case "stalled": {
@@ -699,6 +711,7 @@ var KNOWN_EVENT_TYPES = /* @__PURE__ */ new Set([
699
711
  "task.progress",
700
712
  "heartbeat",
701
713
  "task.blocked",
714
+ "task.review",
702
715
  "task.done",
703
716
  "task.failed",
704
717
  "task.session",
@@ -2841,8 +2854,12 @@ var REGISTRY = {
2841
2854
  build: (a) => a[0] ? ok("crews", ["crew", "list", a[0]]) : usage("crews", "usage: /crews <project>")
2842
2855
  },
2843
2856
  launch: {
2857
+ // --headless (#586, same reason as #520 on the boot-if-down path): runCommand
2858
+ // execs this argv from the daemon, which has no CMUX_WORKSPACE_ID and no
2859
+ // terminal — a plain `launch` would open the cmux GUI app and exit 0 before
2860
+ // the workspace is ever launched.
2844
2861
  usage: "/launch <project>",
2845
- build: (a) => a[0] ? ok("launch", ["launch", a[0]]) : usage("launch", "usage: /launch <project>")
2862
+ build: (a) => a[0] ? ok("launch", ["launch", a[0], "--headless"]) : usage("launch", "usage: /launch <project>")
2846
2863
  },
2847
2864
  effort: {
2848
2865
  usage: "/effort [max|balance|low]",
@@ -3027,6 +3044,9 @@ ${ev.message}` : "");
3027
3044
  case "task.blocked":
3028
3045
  return `\u{1F6A7} [${project}] CREW BLOCKED \xB7 ${ev.id}
3029
3046
  ${ev.question}`;
3047
+ case "task.review":
3048
+ return `\u{1F440} [${project}] CREW REVIEW \xB7 ${ev.id}` + (ev.message ? `
3049
+ ${ev.message}` : "");
3030
3050
  case "task.idle":
3031
3051
  return `\u{1F4A4} [${project}] CREW IDLE \xB7 ${ev.id}`;
3032
3052
  case "task.failed":
@@ -3223,6 +3243,7 @@ var DONE_ONLY = /* @__PURE__ */ new Set(["task.done", "task.failed"]);
3223
3243
  var ALERTS = /* @__PURE__ */ new Set([
3224
3244
  ...DONE_ONLY,
3225
3245
  "task.blocked",
3246
+ "task.review",
3226
3247
  "task.approval.requested",
3227
3248
  "task.input.requested",
3228
3249
  "task.timeout"
@@ -4658,6 +4679,18 @@ function cmux(args) {
4658
4679
  );
4659
4680
  });
4660
4681
  }
4682
+ function cmuxStdin(args, input) {
4683
+ return new Promise((resolve2, reject) => {
4684
+ const child = execFile2(resolveCmuxBin(), args, { encoding: "utf-8", timeout: CMUX_TIMEOUT, env: { ...process.env, CMUX_QUIET: "1" } }, (err, stdout) => {
4685
+ if (err) {
4686
+ reject(err.code === "ETIMEDOUT" ? new CmuxTimeoutError(args.join(" ")) : err);
4687
+ return;
4688
+ }
4689
+ resolve2(stdout.trim());
4690
+ });
4691
+ child.stdin.end(input);
4692
+ });
4693
+ }
4661
4694
  function parseList(output) {
4662
4695
  let parsed;
4663
4696
  try {
@@ -5059,6 +5092,37 @@ function createCmuxDriver() {
5059
5092
  }
5060
5093
  throw new DeferDelivery(draft);
5061
5094
  },
5095
+ async showDiff(opts) {
5096
+ const source = opts.source ?? "branch";
5097
+ const args = ["diff"];
5098
+ if (source === "staged") {
5099
+ args.push("--staged");
5100
+ } else if (source === "unstaged") {
5101
+ args.push("--unstaged");
5102
+ } else {
5103
+ args.push("--branch", "--base", opts.base);
5104
+ if (opts.lastTurn)
5105
+ args.push("--last-turn");
5106
+ }
5107
+ args.push("--cwd", opts.cwd, "--workspace", opts.workspaceId, "--layout", opts.layout ?? "split");
5108
+ if (opts.title)
5109
+ args.push("--title", opts.title);
5110
+ if (opts.focus === false)
5111
+ args.push("--no-focus");
5112
+ else
5113
+ args.push("--focus", "true");
5114
+ await cmux(args);
5115
+ },
5116
+ async showPatch(opts) {
5117
+ const args = ["diff", "-", "--workspace", opts.workspaceId, "--layout", opts.layout ?? "split"];
5118
+ if (opts.title)
5119
+ args.push("--title", opts.title);
5120
+ if (opts.focus === false)
5121
+ args.push("--no-focus");
5122
+ else
5123
+ args.push("--focus", "true");
5124
+ await cmuxStdin(args, opts.patch);
5125
+ },
5062
5126
  async listSurfaces(workspaceId) {
5063
5127
  let output;
5064
5128
  try {