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.
package/dist/index.js CHANGED
@@ -2569,7 +2569,12 @@ async function runCrewSpawn(input, config, deps) {
2569
2569
  task: input.task,
2570
2570
  name
2571
2571
  });
2572
- deps.writeSettingsLocal(spawnCwd);
2572
+ let hooksInstalled = false;
2573
+ try {
2574
+ deps.writeSettingsLocal(spawnCwd);
2575
+ hooksInstalled = true;
2576
+ } catch {
2577
+ }
2573
2578
  const cliCommand2 = agent.buildCommand({
2574
2579
  prompt: input.task,
2575
2580
  workdir: spawnCwd,
@@ -2593,7 +2598,7 @@ ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
2593
2598
  if (!claudeResult.delivered) {
2594
2599
  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.
2595
2600
  `);
2596
- } else {
2601
+ } else if (!hooksInstalled) {
2597
2602
  await deps.emitEvent?.(input.project, { type: "task.first-turn.confirmed", id: rec.id });
2598
2603
  }
2599
2604
  return { ...pane2, title: title2 };
@@ -2900,7 +2905,28 @@ function parseDraftFromScreen(screen) {
2900
2905
  }
2901
2906
  return "";
2902
2907
  }
2903
- function readInputBoxRaw(screen) {
2908
+ function hasCCInputBox(screen) {
2909
+ if (!screen)
2910
+ return false;
2911
+ const lines = screen.split(/\r?\n/);
2912
+ const HR_RE = /^\s*─{10,}\s*$/;
2913
+ let bottomHR = -1;
2914
+ let topHR = -1;
2915
+ for (let i = lines.length - 1; i >= 0; i--) {
2916
+ if (HR_RE.test(lines[i])) {
2917
+ if (bottomHR === -1)
2918
+ bottomHR = i;
2919
+ else {
2920
+ topHR = i;
2921
+ break;
2922
+ }
2923
+ }
2924
+ }
2925
+ if (topHR === -1)
2926
+ return false;
2927
+ return lines.slice(topHR + 1, bottomHR).some((l) => /[>❯]/.test(l));
2928
+ }
2929
+ function readInputBoxRaw(screen, opts) {
2904
2930
  if (!screen)
2905
2931
  return null;
2906
2932
  const lines = screen.split(/\r?\n/);
@@ -2926,7 +2952,8 @@ function readInputBoxRaw(screen) {
2926
2952
  s = s.replace(/\s*[▌█▔▎▏]+\s*$/, "");
2927
2953
  parts.push(s);
2928
2954
  }
2929
- return parts.join("").replace(/\s+$/, "");
2955
+ const joined = parts.join("");
2956
+ return opts?.trim === false ? joined : joined.replace(/\s+$/, "");
2930
2957
  }
2931
2958
  function classifyStartupSurface(screen) {
2932
2959
  if (CC_WORKING_RE.test(screen))
@@ -2947,6 +2974,19 @@ function classifySendOutcome(payload, postBox) {
2947
2974
  return "stuck";
2948
2975
  return "unknown";
2949
2976
  }
2977
+ function classifyDraftLiveness(before, after) {
2978
+ if (before === null || after === null)
2979
+ return "inconclusive";
2980
+ if (after === "")
2981
+ return "no-draft";
2982
+ if (before.length > 0) {
2983
+ const segs = [...new Intl.Segmenter().segment(before)];
2984
+ const expected = segs.slice(0, -1).map((s) => s.segment).join("").replace(/\s+$/, "");
2985
+ if (after === expected)
2986
+ return "real-draft";
2987
+ }
2988
+ return "inconclusive";
2989
+ }
2950
2990
  function createCmuxDriver() {
2951
2991
  return {
2952
2992
  name: "cmux",
@@ -3144,18 +3184,38 @@ function createCmuxDriver() {
3144
3184
  if (!opts?.probe)
3145
3185
  throw new DeferDelivery(draft);
3146
3186
  const before = readInputBoxRaw(screen);
3187
+ const rawBefore = readInputBoxRaw(screen, { trim: false });
3147
3188
  await cmux(["send-key", "--workspace", ws, "--surface", sf, "backspace"]);
3189
+ await new Promise((r) => setTimeout(r, 50));
3148
3190
  let afterScreen = "";
3149
3191
  try {
3150
3192
  afterScreen = await cmux(["read-screen", "--workspace", ws, "--surface", sf]);
3151
3193
  } catch {
3152
3194
  }
3153
3195
  const after = readInputBoxRaw(afterScreen);
3154
- if (before !== null && after !== null && after.length > 0 && after === before.slice(0, -1)) {
3155
- await cmux(["send", "--workspace", ws, "--surface", sf, before.slice(-1)]);
3196
+ const rawAfter = readInputBoxRaw(afterScreen, { trim: false });
3197
+ const liveness = classifyDraftLiveness(before, after);
3198
+ if (liveness === "real-draft") {
3199
+ const segs = before ? [...new Intl.Segmenter().segment(before)] : [];
3200
+ const lastGrapheme = segs.length > 0 ? segs[segs.length - 1].segment : before.slice(-1);
3201
+ await cmux(["send", "--workspace", ws, "--surface", sf, lastGrapheme]);
3156
3202
  throw new DeferDelivery(draft);
3157
3203
  }
3158
- await deliver();
3204
+ if (liveness === "no-draft") {
3205
+ await deliver();
3206
+ return;
3207
+ }
3208
+ if (rawBefore !== null && rawAfter !== null) {
3209
+ if (rawBefore !== rawAfter) {
3210
+ const segs = [...new Intl.Segmenter().segment(rawBefore)];
3211
+ const lastGrapheme = segs.length > 0 ? segs[segs.length - 1].segment : rawBefore.slice(-1);
3212
+ await cmux(["send", "--workspace", ws, "--surface", sf, lastGrapheme]);
3213
+ throw new DeferDelivery(draft);
3214
+ }
3215
+ await deliver();
3216
+ return;
3217
+ }
3218
+ throw new DeferDelivery(draft);
3159
3219
  },
3160
3220
  async listSurfaces(workspaceId) {
3161
3221
  let output;
@@ -4033,7 +4093,7 @@ async function confirmedSendToPane(runtime, pane, message) {
4033
4093
  sawDraft = true;
4034
4094
  if (draft === "" && sawDraft)
4035
4095
  return { delivered: true };
4036
- if (draft === null && afterScreen !== preSendScreen)
4096
+ if (draft === null && afterScreen !== preSendScreen && sawDraft)
4037
4097
  return { delivered: true };
4038
4098
  const settled = await settleInputBox(runtime, pane);
4039
4099
  if (settled)
@@ -4053,8 +4113,8 @@ async function sendFirstTurnWhenReady(runtime, pane, task, preLaunchScreen, acce
4053
4113
  let stable = false;
4054
4114
  for (let i = 0; i < maxPolls && !stable; i++) {
4055
4115
  const screen = await runtime.readPaneScreen(pane) ?? "";
4056
- const hasInputBox = !!acceptanceConfig?.splashMarker || parseDraftFromScreen(screen) !== null;
4057
- if (screen.length > 0 && screen === previousScreen && screen !== preLaunchScreen && hasInputBox) {
4116
+ const ready = acceptanceConfig?.splashMarker ? true : hasCCInputBox(screen) && classifyStartupSurface(screen) === "idle";
4117
+ if (screen.length > 0 && screen === previousScreen && screen !== preLaunchScreen && ready) {
4058
4118
  stable = true;
4059
4119
  } else {
4060
4120
  previousScreen = screen;
@@ -4092,7 +4152,7 @@ async function sendFirstTurnWhenReady(runtime, pane, task, preLaunchScreen, acce
4092
4152
  sawDraft = true;
4093
4153
  if (draft === "" && sawDraft)
4094
4154
  return { delivered: true };
4095
- if (draft === null && afterScreen !== preSendScreen)
4155
+ if (draft === null && afterScreen !== preSendScreen && sawDraft)
4096
4156
  return { delivered: true };
4097
4157
  const settled = await settleInputBox(runtime, pane);
4098
4158
  if (settled)
@@ -4117,7 +4177,7 @@ var init_crew_pane = __esm({
4117
4177
  init_dist2();
4118
4178
  SEND_FIRST_TURN_FLOOR_MS = 1500;
4119
4179
  POLL_INTERVAL_MS = 750;
4120
- SEND_FIRST_TURN_TIMEOUT_MS = 2e4;
4180
+ SEND_FIRST_TURN_TIMEOUT_MS = 9e4;
4121
4181
  POST_SEND_CHECK_MS = 750;
4122
4182
  SPLASH_MAX_CHECKS = 20;
4123
4183
  SPLASH_RESEND_EVERY_N = 4;
@@ -5833,6 +5893,8 @@ function mapClaudeHookToEvent(event, payload, taskId) {
5833
5893
  case "SubagentStop":
5834
5894
  case "PostToolUse":
5835
5895
  return { type: "task.progress", id: taskId, note: event.toLowerCase() };
5896
+ case "UserPromptSubmit":
5897
+ return { type: "task.first-turn.confirmed", id: taskId };
5836
5898
  default:
5837
5899
  return null;
5838
5900
  }
@@ -5840,7 +5902,7 @@ function mapClaudeHookToEvent(event, payload, taskId) {
5840
5902
  var EVENTS, claudeInteractive;
5841
5903
  var init_claude2 = __esm({
5842
5904
  "packages/agents/dist/interactive/claude.js"() {
5843
- EVENTS = ["Stop", "SubagentStop", "SessionEnd", "PostToolUse", "Notification"];
5905
+ EVENTS = ["Stop", "SubagentStop", "SessionEnd", "PostToolUse", "Notification", "UserPromptSubmit"];
5844
5906
  claudeInteractive = {
5845
5907
  provider: "claude",
5846
5908
  tier: "strong",
@@ -11028,9 +11090,10 @@ async function sendToSock(req) {
11028
11090
  function mapHookSub(sub, payload, taskId) {
11029
11091
  switch (sub) {
11030
11092
  case "session-start":
11031
- case "prompt-submit":
11032
11093
  case "pre-tool-use":
11033
11094
  return { type: "task.progress", id: taskId, note: sub };
11095
+ case "prompt-submit":
11096
+ return { type: "task.first-turn.confirmed", id: taskId };
11034
11097
  case "stop":
11035
11098
  return mapClaudeHookToEvent("Stop", payload, taskId);
11036
11099
  case "notification":