squadrant 0.26.2 → 0.26.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.
@@ -4662,9 +4662,13 @@ async function runCrewSpawn(input, config, deps) {
4662
4662
  await deps.runtime.sendToPane(pane2, `cd ${shellQuote(spawnCwd)} && ${envPrefix}${routerPrefix} ${niceCrewCommand(cliCommand2)}`);
4663
4663
  const preLaunchScreen = await deps.runtime.readPaneScreen(pane2) ?? "";
4664
4664
  const claudeFirstTurn = spillOversizedFirstTurn(firstTurnTask, rec.id);
4665
- const sendPromise = deps.sendFirstTurn(pane2, `${claudeFirstTurn}
4665
+ const peerBrief = `${firstTurnTask}
4666
4666
 
4667
- ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
4667
+ ${buildCompletionProtocol(rec.id, input.project)}`;
4668
+ const peerResult = deps.sendFirstTurnPeer ? await deps.sendFirstTurnPeer({ pane: pane2, taskId: rec.id, project: input.project, messagingSocketPath, message: peerBrief }).catch(() => ({ kind: "fallback" })) : { kind: "fallback" };
4669
+ const sendPromise = peerResult.kind === "fallback" ? deps.sendFirstTurn(pane2, `${claudeFirstTurn}
4670
+
4671
+ ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen) : Promise.resolve({ delivered: peerResult.kind === "delivered" });
4668
4672
  let agentNotStarted = false;
4669
4673
  const scrapeDelivered = sendPromise.then((r) => {
4670
4674
  agentNotStarted = !!r.agentNotStarted;
@@ -8889,6 +8893,30 @@ async function runCrewAnswer(project, name, option, runtime, workspaceId, deps,
8889
8893
  if (!crew) {
8890
8894
  throw new Error(`Crew '${name}' not found for ${project}. Run 'squadrant crew list ${project}'.`);
8891
8895
  }
8896
+ const match = runtime.findDialog && runtime.answerDialog ? await deps.dialogMatch?.() : void 0;
8897
+ if (match) {
8898
+ const dialog = await runtime.findDialog(match).catch(() => void 0);
8899
+ if (dialog) {
8900
+ const log2 = deps.log ?? (() => {
8901
+ });
8902
+ const options2 = dialog.options.map((o) => ({ ...o, highlighted: false }));
8903
+ let target2;
8904
+ try {
8905
+ target2 = resolveOption(options2, option);
8906
+ if (opts?.expect && !target2.label.toLowerCase().includes(opts.expect.toLowerCase()))
8907
+ target2 = void 0;
8908
+ } catch {
8909
+ target2 = void 0;
8910
+ }
8911
+ if (target2) {
8912
+ log2(`\u2192 answering ${target2.index}. "${target2.label}" via the runtime (no keystrokes)`);
8913
+ const ok2 = await runtime.answerDialog(dialog, opts?.text ? { text: opts.text } : { label: target2.label }).catch(() => false);
8914
+ if (ok2)
8915
+ return { selected: target2, closed: true };
8916
+ }
8917
+ log2("runtime answer not applied \u2014 falling back to keystrokes");
8918
+ }
8919
+ }
8892
8920
  const modal = await deps.readModalOptions(crew);
8893
8921
  if (!modal || modal.options.length === 0) {
8894
8922
  throw new Error(`Crew '${name}' has no interactive option prompt visible right now \u2014 nothing to answer. Read its screen with 'squadrant crew read ${project} ${name}' to check its state.`);
@@ -9268,6 +9296,58 @@ var init_source = __esm({
9268
9296
  }
9269
9297
  });
9270
9298
 
9299
+ // packages/core/dist/peer-first-turn.js
9300
+ function screenShowsTrustPanel(screen) {
9301
+ return /do you trust the files in this folder|is this a project you created or one you trust|is this a project you trust/i.test(screen);
9302
+ }
9303
+ function trustPanelAcceptKeys(screen) {
9304
+ return /❯\s*(\d\.\s*)?No\b/.test(screen) ? ["Down", "Enter"] : ["Enter"];
9305
+ }
9306
+ async function deliverFirstTurnViaPeer(message, deps) {
9307
+ const pollMs = deps.pollMs ?? DEFAULT_POLL_MS2;
9308
+ const deadline = deps.now() + (deps.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS);
9309
+ let lastTrustAnswer = -Infinity;
9310
+ for (; ; ) {
9311
+ if (deps.statusOf()?.status === "idle")
9312
+ break;
9313
+ if (deps.now() >= deadline)
9314
+ return { kind: "fallback" };
9315
+ if (!deps.statusOf() && deps.now() - lastTrustAnswer >= TRUST_ANSWER_COOLDOWN_MS) {
9316
+ const screen = await deps.readScreen().catch(() => "");
9317
+ if (screenShowsTrustPanel(screen)) {
9318
+ lastTrustAnswer = deps.now();
9319
+ for (const key of trustPanelAcceptKeys(screen))
9320
+ await deps.pressKey(key).catch(() => {
9321
+ });
9322
+ }
9323
+ }
9324
+ await deps.sleep(pollMs);
9325
+ }
9326
+ const outcome = await deps.send(message);
9327
+ if (fallsBackToPane(outcome))
9328
+ return { kind: "fallback" };
9329
+ if (outcome.status === "accepted" && outcome.confirmed)
9330
+ return { kind: "delivered" };
9331
+ const confirmDeadline = deps.now() + (deps.confirmGraceMs ?? DEFAULT_CONFIRM_GRACE_MS);
9332
+ for (; ; ) {
9333
+ if (await deps.isConfirmed().catch(() => false))
9334
+ return { kind: "delivered" };
9335
+ if (deps.now() >= confirmDeadline)
9336
+ return { kind: "unconfirmed" };
9337
+ await deps.sleep(pollMs);
9338
+ }
9339
+ }
9340
+ var DEFAULT_READY_TIMEOUT_MS, DEFAULT_POLL_MS2, DEFAULT_CONFIRM_GRACE_MS, TRUST_ANSWER_COOLDOWN_MS;
9341
+ var init_peer_first_turn = __esm({
9342
+ "packages/core/dist/peer-first-turn.js"() {
9343
+ init_control_channel();
9344
+ DEFAULT_READY_TIMEOUT_MS = 9e4;
9345
+ DEFAULT_POLL_MS2 = 250;
9346
+ DEFAULT_CONFIRM_GRACE_MS = 1e4;
9347
+ TRUST_ANSWER_COOLDOWN_MS = 5e3;
9348
+ }
9349
+ });
9350
+
9271
9351
  // packages/core/dist/knowledge/paths.js
9272
9352
  import path14 from "path";
9273
9353
  function kbDir(hubVault, kb) {
@@ -12987,6 +13067,7 @@ __export(dist_exports, {
12987
13067
  defaultIsPidAlive: () => defaultIsPidAlive,
12988
13068
  defaultListenError: () => defaultListenError,
12989
13069
  defaultWorkRoot: () => defaultWorkRoot,
13070
+ deliverFirstTurnViaPeer: () => deliverFirstTurnViaPeer,
12990
13071
  deliverStartupPrompt: () => deliverStartupPrompt,
12991
13072
  deliverToCaptain: () => deliverToCaptain,
12992
13073
  deliverable: () => deliverable,
@@ -13168,6 +13249,7 @@ __export(dist_exports, {
13168
13249
  saveState: () => saveState,
13169
13250
  screenHasSplashMarker: () => screenHasSplashMarker,
13170
13251
  screenHash: () => screenHash,
13252
+ screenShowsTrustPanel: () => screenShowsTrustPanel,
13171
13253
  searchRules: () => searchRules,
13172
13254
  selectPromptHits: () => selectPromptHits,
13173
13255
  sendRequest: () => sendRequest,
@@ -13193,6 +13275,7 @@ __export(dist_exports, {
13193
13275
  toControlEvent: () => toControlEvent,
13194
13276
  topicKey: () => topicKey,
13195
13277
  topicName: () => topicName,
13278
+ trustPanelAcceptKeys: () => trustPanelAcceptKeys,
13196
13279
  tryAcquireDaemonLock: () => tryAcquireDaemonLock,
13197
13280
  usageFromJson: () => usageFromJson,
13198
13281
  validateKb: () => validateKb,
@@ -13255,6 +13338,7 @@ var init_dist2 = __esm({
13255
13338
  init_conformance();
13256
13339
  init_source();
13257
13340
  init_control_channel();
13341
+ init_peer_first_turn();
13258
13342
  init_captain_channel();
13259
13343
  init_captain_record();
13260
13344
  init_opencode_session();
@@ -15069,6 +15153,15 @@ async function cmuxHasInputGuards() {
15069
15153
  inputGuardsCache = major > 0 || minor >= 65;
15070
15154
  return inputGuardsCache;
15071
15155
  }
15156
+ function feedSessionOf(item) {
15157
+ const parts = item.workstream_id?.split(":");
15158
+ if (parts?.[0] !== "cmux-feed-v1" || !parts[2])
15159
+ return void 0;
15160
+ return Buffer.from(parts[2], "base64").toString("utf-8");
15161
+ }
15162
+ async function feedRpc(method, params) {
15163
+ return JSON.parse(await cmux(["rpc", method, JSON.stringify(params)]));
15164
+ }
15072
15165
  function parseList(output) {
15073
15166
  let parsed;
15074
15167
  try {
@@ -15433,6 +15526,26 @@ function createCmuxDriver() {
15433
15526
  await cmux(["send-key", "--workspace", wsId, "--surface", surfaceId, "Enter"]);
15434
15527
  return { workspaceId: wsId, surfaceId, title: opts.title };
15435
15528
  },
15529
+ // #916: cmux >= 0.65 `surface.input_state` reads the render grid (incl. faint
15530
+ // spans) without touching the pane. The RPC needs a surface UUID, and an
15531
+ // unknown ref would silently fall back to the focused surface, so resolve the
15532
+ // ref through the tree first. Anything unexpected → undefined (old path).
15533
+ async inputState(pane) {
15534
+ try {
15535
+ if (!await cmuxHasInputGuards())
15536
+ return void 0;
15537
+ const tree = JSON.parse(await cmux(["tree", "--workspace", pane.workspaceId, "--json", "--id-format", "both"]));
15538
+ const id = tree.windows?.flatMap((w) => w.workspaces ?? []).flatMap((w) => w.panes ?? []).flatMap((p) => p.surfaces ?? []).find((s) => s.ref === pane.surfaceId)?.id;
15539
+ if (!id)
15540
+ return void 0;
15541
+ const r = JSON.parse(await cmux(["rpc", "surface.input_state", JSON.stringify({ surface_id: id })]));
15542
+ if (r.agent !== true)
15543
+ return "unknown";
15544
+ return r.state === "empty" || r.state === "draft" || r.state === "dialog" ? r.state : "unknown";
15545
+ } catch {
15546
+ return void 0;
15547
+ }
15548
+ },
15436
15549
  async sendToSurface(surface, text, opts) {
15437
15550
  const ws = surface.workspaceId;
15438
15551
  const sf = surface.surfaceId;
@@ -15459,6 +15572,17 @@ function createCmuxDriver() {
15459
15572
  `);
15460
15573
  }
15461
15574
  };
15575
+ if (opts?.agent !== "opencode") {
15576
+ const state = await this.inputState?.(surface);
15577
+ if (state === "draft")
15578
+ throw new DeferDelivery(null, "draft");
15579
+ if (state === "dialog")
15580
+ throw new DeferDelivery(null, "modal");
15581
+ if (state === "empty") {
15582
+ await deliver();
15583
+ return;
15584
+ }
15585
+ }
15462
15586
  let screen = "";
15463
15587
  try {
15464
15588
  screen = await cmux(["read-screen", "--workspace", ws, "--surface", sf]);
@@ -15537,6 +15661,39 @@ function createCmuxDriver() {
15537
15661
  args.push("--focus", "true");
15538
15662
  await cmux(args);
15539
15663
  },
15664
+ // Feed-backed structured answer (#918). cmux >= 0.65 only; any miss
15665
+ // (old cmux, no matching item, unsupported shape) is undefined so the
15666
+ // caller keeps the keystroke path.
15667
+ async findDialog(match) {
15668
+ if (!await cmuxHasInputGuards())
15669
+ return void 0;
15670
+ let items;
15671
+ try {
15672
+ items = (await feedRpc("feed.list", { pending_only: true })).items ?? [];
15673
+ } catch {
15674
+ return void 0;
15675
+ }
15676
+ const mine = items.filter((i) => match.agentSessionId ? feedSessionOf(i) === match.agentSessionId : !!match.cwd && i.cwd === match.cwd);
15677
+ if (mine.length !== 1 || !mine[0].request_id)
15678
+ return void 0;
15679
+ const item = mine[0];
15680
+ if (item.kind !== "question" || item.questions?.length !== 1)
15681
+ return void 0;
15682
+ const labels = (item.questions[0].options ?? []).map((o) => o.label ?? "");
15683
+ if (labels.length === 0)
15684
+ return void 0;
15685
+ return {
15686
+ id: item.request_id,
15687
+ options: labels.map((label, i) => ({ index: i + 1, label }))
15688
+ };
15689
+ },
15690
+ async answerDialog(dialog, answer) {
15691
+ const reply = answer.text ?? answer.label;
15692
+ if (!reply)
15693
+ return false;
15694
+ const res = await feedRpc("feed.question.reply", { request_id: dialog.id, selections: [reply] });
15695
+ return res.delivered === true;
15696
+ },
15540
15697
  async showPatch(opts) {
15541
15698
  const args = ["diff", "-", "--workspace", opts.workspaceId, "--layout", opts.layout ?? "split"];
15542
15699
  if (opts.title)
@@ -16637,6 +16794,11 @@ async function settleInputBox(runtime, pane) {
16637
16794
  return sawContent;
16638
16795
  }
16639
16796
  async function confirmedSendToPane(runtime, pane, message) {
16797
+ const native = await runtime.inputState?.(pane).catch(() => void 0);
16798
+ if (native === "dialog")
16799
+ return { delivered: false, blockedByModal: true };
16800
+ if (native === "draft")
16801
+ return { delivered: false };
16640
16802
  const preSendScreen = await runtime.readPaneScreen(pane) ?? "";
16641
16803
  if (hasModalOptionList2(preSendScreen)) {
16642
16804
  return { delivered: false, blockedByModal: true };