@sideboard-ai/core 0.1.317 → 0.1.319

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.
Files changed (40) hide show
  1. package/dist/{abletime-NZ2ABWAL.js → abletime-DVYO4IVS.js} +6 -4
  2. package/dist/{abletime-TKAFNXCM.js → abletime-L3ODNTBT.js} +5 -3
  3. package/dist/{abletime-oauth-S4AXA4WR.js → abletime-oauth-2VWQCXGT.js} +2 -2
  4. package/dist/{abletime-oauth-CBFFBINA.js → abletime-oauth-6DTX7LB7.js} +3 -3
  5. package/dist/{abletime-rest-ORBELZQO.js → abletime-rest-7NICZYOC.js} +2 -2
  6. package/dist/{abletime-rest-NSOP72AI.js → abletime-rest-PYQM234B.js} +3 -3
  7. package/dist/{agents-BJVSYC6Y.js → agents-2AR7I6B6.js} +5 -5
  8. package/dist/{app-settings-RQAFJZCD.js → app-settings-V6YPYY57.js} +1 -1
  9. package/dist/{chunk-CCUGIVYN.js → chunk-5OM7UCTQ.js} +3 -3
  10. package/dist/{chunk-NXNNYY3Q.js → chunk-AESO2KJS.js} +2 -2
  11. package/dist/{chunk-VVV7EI6T.js → chunk-AUJ4XB6H.js} +2 -2
  12. package/dist/{chunk-S3U4HR5F.js → chunk-AZ4GEEGL.js} +2 -2
  13. package/dist/{chunk-U7PNCYNX.js → chunk-E52SQENB.js} +1 -0
  14. package/dist/{chunk-OLYNM52B.js → chunk-FDXHNROD.js} +2 -2
  15. package/dist/{chunk-XWUDFLBX.js → chunk-IAPO7ZFG.js} +4 -4
  16. package/dist/{chunk-VZAJVX36.js → chunk-JD7CC6SW.js} +1 -1
  17. package/dist/{chunk-ELBK34TY.js → chunk-LXBZQP2Q.js} +5 -5
  18. package/dist/{chunk-BXTISNKW.js → chunk-OO3R6CS2.js} +216 -64
  19. package/dist/{chunk-I5CLCWUK.js → chunk-QPLQXWND.js} +8 -4
  20. package/dist/{chunk-4XZJYBO2.js → chunk-ST2UQBJT.js} +4 -4
  21. package/dist/{chunk-YN6XONV5.js → chunk-TUJFCJJU.js} +1 -1
  22. package/dist/{chunk-U53JICJ2.js → chunk-U2N32EA6.js} +233 -81
  23. package/dist/{chunk-GXSV3X3L.js → chunk-WLZHRNE6.js} +9 -5
  24. package/dist/{chunk-PX324N7P.js → chunk-YALFZKSC.js} +2 -2
  25. package/dist/{chunk-7SCVGGF6.js → chunk-ZM43NTJJ.js} +4 -4
  26. package/dist/{conductor-TP2NA44I.js → conductor-MG2RAQNT.js} +4 -4
  27. package/dist/{coordinator-prompt-FQC2SZPP.js → coordinator-prompt-X74F3JIC.js} +3 -3
  28. package/dist/{global-workspace-72K6D354.js → global-workspace-FTNJEML3.js} +3 -3
  29. package/dist/index.cjs +301 -79
  30. package/dist/index.d.cts +29 -9
  31. package/dist/index.d.ts +29 -9
  32. package/dist/index.js +72 -27
  33. package/dist/mcp/run-stdio.cjs +240 -63
  34. package/dist/mcp/run-stdio.js +4 -4
  35. package/dist/{optional-cli-LMSRZNH4.js → optional-cli-6GGESWGE.js} +4 -4
  36. package/dist/{orchestrator-N54V3B76.js → orchestrator-H3DSZ5RM.js} +1 -1
  37. package/dist/{orchestrator-E72HYGTB.js → orchestrator-NCA4LSCC.js} +8 -8
  38. package/dist/{workspaces-GAUHXBUL.js → workspaces-MGKEAYNM.js} +3 -3
  39. package/dist/{worktree-6FUQ7FEF.js → worktree-THAXW5U7.js} +2 -2
  40. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -1272,6 +1272,7 @@ function parseRemoteClientMessage(raw) {
1272
1272
  const msg = parsed;
1273
1273
  if (msg.type === "ping") return { type: "ping" };
1274
1274
  if (msg.type === "host_pair") return { type: "host_pair" };
1275
+ if (msg.type === "pairing_done") return { type: "pairing_done" };
1275
1276
  if (msg.type === "e2ee_fail") return { type: "e2ee_fail" };
1276
1277
  if (msg.type === "stop") return { type: "stop" };
1277
1278
  if (msg.type === "host_register") {
@@ -8684,18 +8685,18 @@ function mcpWaitForTurnTimeoutMs(requested) {
8684
8685
  if (!Number.isFinite(n)) return MCP_WAIT_FOR_TURN_MAX_MS;
8685
8686
  return Math.min(Math.max(1e3, Math.floor(n)), MCP_WAIT_FOR_TURN_MAX_MS);
8686
8687
  }
8687
- function mcpWaitTaskHint(taskState, status, blockedReason) {
8688
+ function mcpWaitTaskHint(taskState2, status, blockedReason) {
8688
8689
  const why = blockedReason?.trim();
8689
- if (why && taskState !== "failed" && taskState !== "canceled") {
8690
- if (taskState === "submitted" || taskState === "working") {
8690
+ if (why && taskState2 !== "failed" && taskState2 !== "canceled") {
8691
+ if (taskState2 === "submitted" || taskState2 === "working") {
8691
8692
  return `Child is blocked: ${why} It has not finished the turn. Do not send_to_chat a check-in.`;
8692
8693
  }
8693
- if (taskState === "input-required") {
8694
+ if (taskState2 === "input-required") {
8694
8695
  return `Child is blocked: ${why} Wait for the user in that chat \u2014 do not send_to_chat a check-in.`;
8695
8696
  }
8696
8697
  return `Child is blocked: ${why} Help if this is your child; otherwise tell the user. Do not send_to_chat a check-in.`;
8697
8698
  }
8698
- switch (taskState) {
8699
+ switch (taskState2) {
8699
8700
  case "submitted":
8700
8701
  return MCP_WAIT_QUEUED_HINT;
8701
8702
  case "working":
@@ -24885,6 +24886,7 @@ var init_abletime_projects = __esm({
24885
24886
  var abletime_exports = {};
24886
24887
  __export(abletime_exports, {
24887
24888
  ableTimeDependencyTarget: () => ableTimeDependencyTarget,
24889
+ ableTimeStateIsClosed: () => ableTimeStateIsClosed,
24888
24890
  commentAbleTimeTask: () => commentAbleTimeTask,
24889
24891
  createAbleTimeTask: () => createAbleTimeTask,
24890
24892
  downloadAbleTimeAttachment: () => downloadAbleTimeAttachment,
@@ -25038,9 +25040,12 @@ function issueAttachmentForAbleTimeTask(task) {
25038
25040
  ].filter(Boolean).join("\n")
25039
25041
  };
25040
25042
  }
25043
+ function ableTimeStateIsClosed(state) {
25044
+ const value = (state ?? "").trim().toLowerCase();
25045
+ return Boolean(value && CLOSED_STATES.has(value));
25046
+ }
25041
25047
  function isOpenTask(task) {
25042
- const state = (task.state ?? "").toLowerCase();
25043
- return !state || !CLOSED_STATES.has(state);
25048
+ return !ableTimeStateIsClosed(task.state);
25044
25049
  }
25045
25050
  async function getAbleTimeOrientation(opts) {
25046
25051
  const raw = await callAbleTimeTool("orientation", {}, opts);
@@ -28328,6 +28333,41 @@ var init_expand = __esm({
28328
28333
  }
28329
28334
  });
28330
28335
 
28336
+ // src/composer/agent-has-replied.ts
28337
+ function joinSteerPrompt(base, addition) {
28338
+ const next = addition.trim();
28339
+ if (!next) return base;
28340
+ const head = base.replace(/\s+$/, "");
28341
+ if (!head) return next;
28342
+ return `${head}
28343
+ ${next}`;
28344
+ }
28345
+ function trailingUnansweredUserText(messages) {
28346
+ for (let i = messages.length - 1; i >= 0; i--) {
28347
+ const message = messages[i];
28348
+ if (!message || message.origin === "continue" || message.role === "summary") continue;
28349
+ if (message.role === "agent") {
28350
+ const text7 = (message.text ?? "").trim();
28351
+ if (text7 || (message.parts?.length ?? 0) > 0) return null;
28352
+ continue;
28353
+ }
28354
+ if (message.role === "user") return message.text;
28355
+ }
28356
+ return null;
28357
+ }
28358
+ function livePartsShowAgentReply(parts) {
28359
+ for (const part of parts) {
28360
+ if (part.type === "tool") return true;
28361
+ if ((part.type === "text" || part.type === "thinking") && part.text.trim()) return true;
28362
+ }
28363
+ return false;
28364
+ }
28365
+ var init_agent_has_replied = __esm({
28366
+ "src/composer/agent-has-replied.ts"() {
28367
+ "use strict";
28368
+ }
28369
+ });
28370
+
28331
28371
  // src/composer/consume-attachments.ts
28332
28372
  function consumeComposerAttachments(thread) {
28333
28373
  return {
@@ -28385,6 +28425,142 @@ var init_consume_attachments = __esm({
28385
28425
  }
28386
28426
  });
28387
28427
 
28428
+ // src/orchestrator/enqueue-thread-send.ts
28429
+ function resolveSendFollowUp(thread, requested) {
28430
+ if (requested === "steer" || requested === "queue") return requested;
28431
+ return isOrchestratorThread(thread) ? followUpBehavior() : "queue";
28432
+ }
28433
+ function rewriteTrailingUserPrompt(threadId, from, to) {
28434
+ if (from === to) return;
28435
+ const thread = readThread(threadId);
28436
+ if (!thread) return;
28437
+ const messages = thread.messages.slice();
28438
+ for (let i = messages.length - 1; i >= 0; i--) {
28439
+ const message = messages[i];
28440
+ if (!message || message.origin === "continue" || message.role === "summary") continue;
28441
+ if (message.role === "user" && message.text === from) {
28442
+ messages[i] = { ...message, text: to };
28443
+ updateThread(threadId, { messages });
28444
+ }
28445
+ return;
28446
+ }
28447
+ }
28448
+ function reuseJoinedUserBubble(reused, threadId, last, promptText) {
28449
+ return reused.delete(threadId) && last?.role === "user" && last.origin !== "continue" && last.text === promptText;
28450
+ }
28451
+ function promptForJoinedSteer(input) {
28452
+ if (input.latestPrompt === input.promptText) return input.agentPrompt;
28453
+ rewriteTrailingUserPrompt(input.threadId, input.promptText, input.latestPrompt);
28454
+ const { agentPrompt: expandedLatest } = expandComposerPrompt(
28455
+ input.worktreePath,
28456
+ expandCanonicalGitRequest(input.latestPrompt),
28457
+ { attachments: input.attachments }
28458
+ );
28459
+ return input.expandedPrompt && input.agentPrompt.endsWith(input.expandedPrompt) ? input.agentPrompt.slice(0, -input.expandedPrompt.length) + expandedLatest : `${input.agentPrompt}
28460
+
28461
+ ${expandedLatest}`;
28462
+ }
28463
+ function clearSendBookkeeping(host, threadId) {
28464
+ host.crashContinued.delete(threadId);
28465
+ host.jobContinueCount.delete(threadId);
28466
+ host.jobContinueNudged.delete(threadId);
28467
+ host.haltDrain.delete(threadId);
28468
+ }
28469
+ async function enqueueThreadSend(host, threadRef, prompt, opts) {
28470
+ const thread = host.requireThread(threadRef);
28471
+ if (thread.status === "archived") {
28472
+ throw new Error(`Thread is archived: ${thread.id}`);
28473
+ }
28474
+ let shouldSteer = false;
28475
+ const sent = await withThreadLock(thread.id, async () => {
28476
+ const current = host.requireThread(thread.id);
28477
+ if (current.status === "archived") {
28478
+ throw new Error(`Thread is archived: ${thread.id}`);
28479
+ }
28480
+ const followUp = resolveSendFollowUp(current, opts?.followUp);
28481
+ const explicit = opts?.attachments;
28482
+ const consumed = explicit !== void 0 ? { attachments: current.attachments, consumed: explicit } : consumeComposerAttachments(current);
28483
+ const inFlightEarly = host.activeTurns.has(thread.id) || host.startingTurns.has(thread.id) || current.status === "running";
28484
+ const agentHasRepliedEarly = livePartsShowAgentReply(readTurnLiveParts(current.id));
28485
+ const draft = host.openSteerPrompt.get(current.id);
28486
+ const queuedHead = !inFlightEarly && current.status === "queued" && current.queue.length > 0 ? current.queue[0] : null;
28487
+ const unanswered = followUp === "steer" && !agentHasRepliedEarly ? draft?.text ?? (inFlightEarly ? trailingUnansweredUserText(current.messages) : queuedHead) : null;
28488
+ if (unanswered != null && (draft ? !draft.spawned : queuedHead != null)) {
28489
+ clearSendBookkeeping(host, thread.id);
28490
+ const combined = joinSteerPrompt(unanswered, prompt);
28491
+ if (draft && !draft.spawned) {
28492
+ draft.text = combined;
28493
+ rewriteTrailingUserPrompt(current.id, unanswered, combined);
28494
+ } else {
28495
+ const queue = current.queue.slice();
28496
+ queue[0] = combined;
28497
+ const queueAttachments = alignQueueAttachments(queue.length, current.queueAttachments);
28498
+ if (consumed.consumed.length > 0) {
28499
+ queueAttachments[0] = [...queueAttachments[0] ?? [], ...consumed.consumed];
28500
+ }
28501
+ const patch2 = { queue, queueAttachments };
28502
+ if (explicit === void 0 && consumed.consumed.length > 0) {
28503
+ patch2.attachments = consumed.attachments;
28504
+ }
28505
+ updateThread(current.id, patch2);
28506
+ host.emit({ type: "queue_changed", threadId: current.id, queue });
28507
+ }
28508
+ if (thisProcessShouldDrainAgentQueues() && !inFlightEarly) {
28509
+ host.armDrain(current.id);
28510
+ }
28511
+ return host.requireThread(current.id);
28512
+ }
28513
+ const promptToQueue = unanswered != null ? joinSteerPrompt(unanswered, prompt) : prompt;
28514
+ if (unanswered != null) {
28515
+ if (draft) draft.text = promptToQueue;
28516
+ rewriteTrailingUserPrompt(current.id, unanswered, promptToQueue);
28517
+ host.reusedUserPrompt.add(current.id);
28518
+ }
28519
+ const queued = appendQueuedItem(
28520
+ current.queue,
28521
+ current.queueAttachments,
28522
+ promptToQueue,
28523
+ consumed.consumed
28524
+ );
28525
+ clearSendBookkeeping(host, thread.id);
28526
+ const inFlight = host.activeTurns.has(thread.id) || host.startingTurns.has(thread.id) || current.status === "running";
28527
+ shouldSteer = followUp === "steer" && (inFlight || current.queue.length > 0);
28528
+ const patch = queued;
28529
+ if (explicit === void 0 && consumed.consumed.length > 0) {
28530
+ patch.attachments = consumed.attachments;
28531
+ }
28532
+ if (!inFlight) patch.status = "queued";
28533
+ const pid = current.agentPid;
28534
+ if (typeof pid === "number" && pid > 0 && !host.pidAlive(pid)) {
28535
+ patch.agentPid = null;
28536
+ }
28537
+ updateThread(thread.id, patch);
28538
+ host.emit({ type: "queue_changed", threadId: thread.id, queue: queued.queue });
28539
+ if (!inFlight) {
28540
+ host.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
28541
+ }
28542
+ if (thisProcessShouldDrainAgentQueues() && !shouldSteer) {
28543
+ host.armDrain(thread.id);
28544
+ }
28545
+ return host.requireThread(thread.id);
28546
+ });
28547
+ return { thread: sent, shouldSteer };
28548
+ }
28549
+ var init_enqueue_thread_send = __esm({
28550
+ "src/orchestrator/enqueue-thread-send.ts"() {
28551
+ "use strict";
28552
+ init_agent_git_actions();
28553
+ init_agent_has_replied();
28554
+ init_expand();
28555
+ init_consume_attachments();
28556
+ init_desktop_host();
28557
+ init_thread_store();
28558
+ init_turn_live();
28559
+ init_app_settings();
28560
+ init_global_workspace();
28561
+ }
28562
+ });
28563
+
28388
28564
  // src/skills/detached-job-path.ts
28389
28565
  function compiledModuleDir() {
28390
28566
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
@@ -29755,10 +29931,6 @@ async function waitForPidExit(pid, timeoutMs) {
29755
29931
  }
29756
29932
  return true;
29757
29933
  }
29758
- function resolveSendFollowUp(thread, requested) {
29759
- if (requested === "steer" || requested === "queue") return requested;
29760
- return isOrchestratorThread(thread) ? followUpBehavior() : "queue";
29761
- }
29762
29934
  function resolveOrchChildFollowUp(requested) {
29763
29935
  if (requested === "steer" || requested === "queue") return requested;
29764
29936
  return followUpBehavior();
@@ -29908,6 +30080,7 @@ var init_orchestrator = __esm({
29908
30080
  init_read_worktree_tail();
29909
30081
  init_discover();
29910
30082
  init_expand();
30083
+ init_enqueue_thread_send();
29911
30084
  init_consume_attachments();
29912
30085
  init_stage_files();
29913
30086
  init_context_compact();
@@ -29925,6 +30098,7 @@ var init_orchestrator = __esm({
29925
30098
  init_caffeinate_hold();
29926
30099
  init_schedule_runner();
29927
30100
  init_coordinator_prompt();
30101
+ init_enqueue_thread_send();
29928
30102
  LIVE_TURN_SPAWN_GRACE_MS = 15e3;
29929
30103
  RUN_SCRIPT_DESKTOP_ADOPT_MS = 2e4;
29930
30104
  STALE_AGENT_PID_WAIT_MS = 2500;
@@ -29936,6 +30110,13 @@ var init_orchestrator = __esm({
29936
30110
  draining = /* @__PURE__ */ new Set();
29937
30111
  /** Threads past setStatus(running) but not yet in activeTurns (spawn in flight). */
29938
30112
  startingTurns = /* @__PURE__ */ new Set();
30113
+ /**
30114
+ * Prompt for the turn that has not been answered yet. Steer follow-ups before
30115
+ * any agent output are joined onto this text instead of starting a second bubble.
30116
+ */
30117
+ openSteerPrompt = /* @__PURE__ */ new Map();
30118
+ /** Next runTurn should keep the user bubble already rewritten to this prompt. */
30119
+ reusedUserPrompt = /* @__PURE__ */ new Set();
29939
30120
  /** Last sync thread-file read for false-stop heal (throttled per thread). */
29940
30121
  lastReconcileHealAt = /* @__PURE__ */ new Map();
29941
30122
  /** Per-repo timestamp of the last `reconcile(repo, { light: true })`. */
@@ -30546,54 +30727,29 @@ var init_orchestrator = __esm({
30546
30727
  return thread;
30547
30728
  }
30548
30729
  async send(threadRef, prompt, opts) {
30549
- const thread = this.requireThread(threadRef);
30550
- if (thread.status === "archived") {
30551
- throw new Error(`Thread is archived: ${thread.id}`);
30552
- }
30553
- let shouldSteer = false;
30554
- const sent = await withThreadLock(thread.id, async () => {
30555
- const current = this.requireThread(thread.id);
30556
- if (current.status === "archived") {
30557
- throw new Error(`Thread is archived: ${thread.id}`);
30558
- }
30559
- const followUp = resolveSendFollowUp(current, opts?.followUp);
30560
- const explicit = opts?.attachments;
30561
- const consumed = explicit !== void 0 ? { attachments: current.attachments, consumed: explicit } : consumeComposerAttachments(current);
30562
- const queued = appendQueuedItem(
30563
- current.queue,
30564
- current.queueAttachments,
30565
- prompt,
30566
- consumed.consumed
30567
- );
30568
- this.crashContinued.delete(thread.id);
30569
- this.jobContinueCount.delete(thread.id);
30570
- this.jobContinueNudged.delete(thread.id);
30571
- this.haltDrain.delete(thread.id);
30572
- const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id) || current.status === "running";
30573
- shouldSteer = followUp === "steer" && (inFlight || current.queue.length > 0);
30574
- const patch = queued;
30575
- if (explicit === void 0 && consumed.consumed.length > 0) {
30576
- patch.attachments = consumed.attachments;
30577
- }
30578
- if (!inFlight) patch.status = "queued";
30579
- const pid = current.agentPid;
30580
- if (typeof pid === "number" && pid > 0 && !isPidAlive(pid)) {
30581
- patch.agentPid = null;
30582
- }
30583
- updateThread(thread.id, patch);
30584
- this.emit({ type: "queue_changed", threadId: thread.id, queue: queued.queue });
30585
- if (!inFlight) {
30586
- this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
30587
- }
30588
- if (thisProcessShouldDrainAgentQueues() && !shouldSteer) {
30589
- this.armDrain(thread.id);
30590
- }
30591
- return this.requireThread(thread.id);
30592
- });
30593
- if (shouldSteer && sent.queue.length > 0) {
30594
- return this.sendQueuedMessageNow(sent.id, sent.queue.length - 1);
30730
+ const sent = await enqueueThreadSend(
30731
+ {
30732
+ activeTurns: this.activeTurns,
30733
+ startingTurns: this.startingTurns,
30734
+ openSteerPrompt: this.openSteerPrompt,
30735
+ reusedUserPrompt: this.reusedUserPrompt,
30736
+ crashContinued: this.crashContinued,
30737
+ jobContinueCount: this.jobContinueCount,
30738
+ jobContinueNudged: this.jobContinueNudged,
30739
+ haltDrain: this.haltDrain,
30740
+ emit: (event) => this.emit(event),
30741
+ armDrain: (id) => this.armDrain(id),
30742
+ requireThread: (ref) => this.requireThread(ref),
30743
+ pidAlive: (pid) => isPidAlive(pid)
30744
+ },
30745
+ threadRef,
30746
+ prompt,
30747
+ opts
30748
+ );
30749
+ if (sent.shouldSteer && sent.thread.queue.length > 0) {
30750
+ return this.sendQueuedMessageNow(sent.thread.id, sent.thread.queue.length - 1);
30595
30751
  }
30596
- return sent;
30752
+ return sent.thread;
30597
30753
  }
30598
30754
  async fanOut(threadRefs, prompt, opts) {
30599
30755
  const results = [];
@@ -30750,6 +30906,10 @@ var init_orchestrator = __esm({
30750
30906
  this.emit({ type: "status_changed", threadId, status: "running" });
30751
30907
  this.emit({ type: "turn_started", threadId, prompt });
30752
30908
  try {
30909
+ this.openSteerPrompt.set(threadId, {
30910
+ text: typeof prompt === "string" ? prompt : "",
30911
+ spawned: false
30912
+ });
30753
30913
  try {
30754
30914
  const baseline = await captureTurnBaseline(thread.worktreePath);
30755
30915
  if (baseline) this.turnBaselines.set(threadId, baseline);
@@ -30775,18 +30935,21 @@ var init_orchestrator = __esm({
30775
30935
  this.emit({ type: "error", threadId, message: `context compact failed: ${message}` });
30776
30936
  thread = this.requireThread(threadId);
30777
30937
  }
30778
- const promptText = typeof prompt === "string" ? prompt : "";
30938
+ const promptText = this.openSteerPrompt.get(threadId)?.text ?? (typeof prompt === "string" ? prompt : "");
30779
30939
  const autoContinue = isJobContinuePrompt(promptText) || promptText.startsWith("The previous agent process ended before it finished.");
30780
30940
  const parked = takePendingTurnAttachments(thread);
30781
30941
  const sentAttachments = !autoContinue && parked.length > 0 ? parked : void 0;
30782
30942
  if (!autoContinue) clearAgentBlock(threadId);
30783
- appendMessage(threadId, {
30784
- role: "user",
30785
- text: promptText,
30786
- ...sentAttachments ? { attachments: sentAttachments } : {},
30787
- ...autoContinue ? { origin: "continue" } : {},
30788
- ts: (/* @__PURE__ */ new Date()).toISOString()
30789
- });
30943
+ const lastShown = this.requireThread(threadId).messages.at(-1);
30944
+ if (!reuseJoinedUserBubble(this.reusedUserPrompt, threadId, lastShown, promptText)) {
30945
+ appendMessage(threadId, {
30946
+ role: "user",
30947
+ text: promptText,
30948
+ ...sentAttachments ? { attachments: sentAttachments } : {},
30949
+ ...autoContinue ? { origin: "continue" } : {},
30950
+ ts: (/* @__PURE__ */ new Date()).toISOString()
30951
+ });
30952
+ }
30790
30953
  if (!autoContinue) {
30791
30954
  const live = this.requireThread(threadId);
30792
30955
  const stamp = agentPurposeStamp({
@@ -30985,11 +31148,22 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
30985
31148
  ].filter(Boolean).join("\n\n---\n\n");
30986
31149
  const stderrTail = [];
30987
31150
  traceTurn("runTurn.beforeSpawn", { threadId, agent: fresh.agent });
31151
+ const steerDraft = this.openSteerPrompt.get(threadId);
31152
+ if (steerDraft) steerDraft.spawned = true;
31153
+ const promptForAgent = promptForJoinedSteer({
31154
+ threadId,
31155
+ promptText,
31156
+ latestPrompt: steerDraft?.text ?? promptText,
31157
+ agentPrompt,
31158
+ expandedPrompt,
31159
+ worktreePath: fresh.worktreePath,
31160
+ attachments: autoContinue ? [] : sentAttachments ?? []
31161
+ });
30988
31162
  const handle = await spawnAgentTurn(
30989
31163
  fresh,
30990
31164
  {
30991
31165
  cachedPrefix,
30992
- prompt: agentPrompt,
31166
+ prompt: promptForAgent,
30993
31167
  systemPrompt: turnReminders,
30994
31168
  isolateCodexPlugins: fresh.isolateCodexPlugins === true
30995
31169
  },
@@ -31117,7 +31291,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
31117
31291
  retryThread,
31118
31292
  {
31119
31293
  cachedPrefix: retryPrefix,
31120
- prompt: agentPrompt,
31294
+ prompt: promptForAgent,
31121
31295
  systemPrompt: turnReminders,
31122
31296
  isolateCodexPlugins: isolateCodexPluginRetry || retryThread.isolateCodexPlugins === true
31123
31297
  },
@@ -31277,6 +31451,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
31277
31451
  } finally {
31278
31452
  this.startingTurns.delete(threadId);
31279
31453
  this.activeTurns.delete(threadId);
31454
+ this.openSteerPrompt.delete(threadId);
31280
31455
  this.processes.delete(`${threadId}:agent`);
31281
31456
  this.stoppedTurns.delete(threadId);
31282
31457
  this.runningCount = Math.max(0, this.runningCount - 1);
@@ -31953,7 +32128,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
31953
32128
  const live = stillRunning ? readTurnLive(thread.id) : null;
31954
32129
  const liveSummary = live?.summary && !isInternalAgentStatusText(live.summary) ? live.summary : null;
31955
32130
  const queuedHint = stillRunning && thread.status === "queued" && !liveSummary ? "Queued \u2014 waiting for a concurrency slot" : null;
31956
- const taskState = deriveTaskState({
32131
+ const taskState2 = deriveTaskState({
31957
32132
  status: thread.status,
31958
32133
  stillRunning,
31959
32134
  lastAgentParts: lastAgent?.parts
@@ -31962,7 +32137,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
31962
32137
  return {
31963
32138
  text: text7,
31964
32139
  status: thread.status,
31965
- taskState,
32140
+ taskState: taskState2,
31966
32141
  sessionId: thread.sessionId,
31967
32142
  lastError,
31968
32143
  stillRunning,
@@ -32906,6 +33081,7 @@ __export(index_exports, {
32906
33081
  ableTimeOAuthLocalBounceUrl: () => ableTimeOAuthLocalBounceUrl,
32907
33082
  ableTimeOAuthRedirectUri: () => ableTimeOAuthRedirectUri,
32908
33083
  ableTimeOAuthResource: () => ableTimeOAuthResource,
33084
+ ableTimeStateIsClosed: () => ableTimeStateIsClosed,
32909
33085
  ableTimeUsesRest: () => ableTimeUsesRest,
32910
33086
  abletimeMcpRequest: () => abletimeMcpRequest,
32911
33087
  abletimeMcpUrl: () => abletimeMcpUrl,
@@ -33388,6 +33564,7 @@ __export(index_exports, {
33388
33564
  issueMatchesAssignee: () => issueMatchesAssignee,
33389
33565
  issueSourceLabel: () => issueSourceLabel,
33390
33566
  issueTicketFromThread: () => issueTicketFromThread,
33567
+ joinSteerPrompt: () => joinSteerPrompt,
33391
33568
  killDetachedRunTree: () => killDetachedRunTree,
33392
33569
  killListenersOnPorts: () => killListenersOnPorts,
33393
33570
  killStaleWorktreeListeners: () => killStaleWorktreeListeners,
@@ -33446,6 +33623,7 @@ __export(index_exports, {
33446
33623
  listWorktrees: () => listWorktrees,
33447
33624
  listenerCwd: () => listenerCwd,
33448
33625
  liveActivitySummary: () => liveActivitySummary,
33626
+ livePartsShowAgentReply: () => livePartsShowAgentReply,
33449
33627
  loadAppSettings: () => loadAppSettings,
33450
33628
  loadBrightsyConfig: () => loadBrightsyConfig,
33451
33629
  loadConductorSettings: () => loadConductorSettings,
@@ -33783,6 +33961,7 @@ __export(index_exports, {
33783
33961
  toolDetail: () => toolDetail,
33784
33962
  toolFilePath: () => toolFilePath,
33785
33963
  totalTokens: () => totalTokens,
33964
+ trailingUnansweredUserText: () => trailingUnansweredUserText,
33786
33965
  trustSystemCertificates: () => trustSystemCertificates,
33787
33966
  tryReservePort: () => tryReservePort,
33788
33967
  tryReservePorts: () => tryReservePorts,
@@ -33962,8 +34141,11 @@ var DirectionalSeal = class {
33962
34141
  recvCounter = 0;
33963
34142
  transcript = "";
33964
34143
  sendDirection;
34144
+ matchesClientNonce(clientNonce) {
34145
+ return clientNonce === this.hello.clientNonce;
34146
+ }
33965
34147
  acceptReady(ready, desktopNonceBytes) {
33966
- if (ready.v !== 1 || ready.clientNonce !== this.hello.clientNonce) return false;
34148
+ if (ready.v !== 1 || !this.matchesClientNonce(ready.clientNonce)) return false;
33967
34149
  const desktopNonce = desktopNonceBytes ?? b64urlToBytes(ready.desktopNonce);
33968
34150
  if (desktopNonce.length !== NONCE_BYTES) return false;
33969
34151
  let shared;
@@ -39270,6 +39452,7 @@ function runRemoteHost(opts) {
39270
39452
  clientNonce: msg.clientNonce
39271
39453
  });
39272
39454
  if (ready) send3({ type: "e2ee_ready", ...ready });
39455
+ else send3({ type: "e2ee_fail" });
39273
39456
  return;
39274
39457
  }
39275
39458
  if (msg.type === "e2ee") {
@@ -39282,8 +39465,13 @@ function runRemoteHost(opts) {
39282
39465
  }
39283
39466
  if (opened.okFrame) {
39284
39467
  send3({ type: "e2ee", frame: opened.okFrame });
39285
- startBoardSync();
39468
+ try {
39469
+ startBoardSync();
39470
+ } catch (err) {
39471
+ log(`remote error: ${err instanceof Error ? err.message : err}`);
39472
+ }
39286
39473
  opts.onPairingCode?.(null);
39474
+ send3({ type: "pairing_done" });
39287
39475
  opts.onStatus?.({ connected: true, phoneConnected: true });
39288
39476
  log("phone joined");
39289
39477
  return;
@@ -40369,6 +40557,10 @@ var RemoteHub = class {
40369
40557
  this.issueCode(socket);
40370
40558
  return;
40371
40559
  }
40560
+ if (msg.type === "pairing_done") {
40561
+ this.finishPairing(socket);
40562
+ return;
40563
+ }
40372
40564
  if (msg.type === "pair") {
40373
40565
  this.pairPhone(socket, msg.code);
40374
40566
  return;
@@ -40448,7 +40640,8 @@ var RemoteHub = class {
40448
40640
  hostSecret,
40449
40641
  socket,
40450
40642
  sessionToken,
40451
- phone: existing?.socket === socket ? existing.phone : null
40643
+ phone: existing?.socket === socket ? existing.phone : null,
40644
+ pairingCode: existing && existing.socket === socket ? existing.pairingCode : null
40452
40645
  };
40453
40646
  this.hosts.set(deviceId, host);
40454
40647
  this.remembered.set(deviceId, { hostSecret, sessionToken, deviceLabel });
@@ -40484,8 +40677,15 @@ var RemoteHub = class {
40484
40677
  send(socket, { type: "error", message: "Mac is offline" });
40485
40678
  return;
40486
40679
  }
40487
- this.codes.delete(code);
40488
- this.attachPhone(socket, host);
40680
+ this.attachPhone(socket, host, void 0, code);
40681
+ }
40682
+ /** The Mac accepted the phone. That QR must not replace the saved session. */
40683
+ finishPairing(socket) {
40684
+ const bound = this.bySocket.get(socket);
40685
+ const host = bound?.role === "host" ? this.hosts.get(bound.deviceId) : null;
40686
+ if (!host || host.socket !== socket) return;
40687
+ if (host.pairingCode) this.codes.delete(host.pairingCode);
40688
+ host.pairingCode = null;
40489
40689
  }
40490
40690
  /** In-memory session, with the live Mac's label when that socket is up. */
40491
40691
  memorySession(sessionToken) {
@@ -40558,7 +40758,7 @@ var RemoteHub = class {
40558
40758
  send(prev.socket, { type: "phone_left" });
40559
40759
  }
40560
40760
  }
40561
- attachPhone(socket, host, sessionToken) {
40761
+ attachPhone(socket, host, sessionToken, pairingCode) {
40562
40762
  const bound = this.bySocket.get(socket);
40563
40763
  if (bound?.role === "phone" && bound.deviceId !== host.deviceId) {
40564
40764
  this.releasePhoneSocket(socket);
@@ -40579,6 +40779,7 @@ var RemoteHub = class {
40579
40779
  }
40580
40780
  host.sessionToken = token;
40581
40781
  host.phone = socket;
40782
+ if (pairingCode) host.pairingCode = pairingCode;
40582
40783
  this.phonesByToken.set(token, { deviceId: host.deviceId, deviceLabel: host.deviceLabel });
40583
40784
  this.remembered.set(host.deviceId, {
40584
40785
  hostSecret: host.hostSecret,
@@ -41731,6 +41932,7 @@ init_discover();
41731
41932
  init_detached_job_path();
41732
41933
  init_long_running();
41733
41934
  init_expand();
41935
+ init_agent_has_replied();
41734
41936
 
41735
41937
  // src/composer/diff-comment.ts
41736
41938
  function lineRangeLabel(lines) {
@@ -45545,6 +45747,7 @@ init_profile();
45545
45747
  init_home_board();
45546
45748
 
45547
45749
  // src/integrations/abletime-blocked.ts
45750
+ init_abletime();
45548
45751
  init_abletime_mcp();
45549
45752
  init_abletime_rest();
45550
45753
  function asRecord18(value) {
@@ -45569,6 +45772,9 @@ function firstRecord2(record, keys) {
45569
45772
  }
45570
45773
  return null;
45571
45774
  }
45775
+ function taskState(record) {
45776
+ return firstString5(record, ["state", "board_state", "boardState", "status", "taskState"]);
45777
+ }
45572
45778
  function identifierFromUnknown(value) {
45573
45779
  if (typeof value === "string" || typeof value === "number") {
45574
45780
  const id2 = String(value).trim();
@@ -45580,6 +45786,7 @@ function identifierFromUnknown(value) {
45580
45786
  for (const key of ["nodes", "data", "tasks", "items"]) {
45581
45787
  if (Array.isArray(rec[key])) return identifierFromUnknown(rec[key]);
45582
45788
  }
45789
+ if (ableTimeStateIsClosed(taskState(rec))) return [];
45583
45790
  const id = firstString5(rec, ["identifier", "reference", "ref", "key", "code", "number", "id"]);
45584
45791
  return id ? [id] : [];
45585
45792
  }
@@ -45614,6 +45821,8 @@ async function getAbleTimeIssueBlockedBy(id, opts) {
45614
45821
  continue;
45615
45822
  }
45616
45823
  const parent = await callAbleTimeTool("get_task", { id: blockerId, task_id: blockerId }, opts);
45824
+ const parentRecord = firstRecord2(asRecord18(parent), ["task", "data"]) ?? asRecord18(parent);
45825
+ if (ableTimeStateIsClosed(taskState(parentRecord))) continue;
45617
45826
  const ref = taskReference(parent);
45618
45827
  if (!ref || looksLikeAbleTimeUlid(ref)) {
45619
45828
  throw new Error(`AbleTime blocker has no reference: ${blockerId}`);
@@ -45638,6 +45847,8 @@ function githubBlockedByIdentifiers(payload) {
45638
45847
  const rec = node && typeof node === "object" && !Array.isArray(node) ? node : null;
45639
45848
  const number = Number(rec?.number ?? node);
45640
45849
  if (!Number.isFinite(number) || number <= 0) continue;
45850
+ const state = String(rec?.state ?? "").trim().toUpperCase();
45851
+ if (state === "CLOSED") continue;
45641
45852
  const id = `#${number}`;
45642
45853
  const key = id.toLowerCase();
45643
45854
  if (seen.has(key)) continue;
@@ -45650,7 +45861,7 @@ var GITHUB_BLOCKED_BY_QUERY = `
45650
45861
  query($owner: String!, $name: String!, $number: Int!) {
45651
45862
  repository(owner: $owner, name: $name) {
45652
45863
  issue(number: $number) {
45653
- blockedBy(first: 25) { nodes { number } }
45864
+ blockedBy(first: 25) { nodes { number state } }
45654
45865
  }
45655
45866
  }
45656
45867
  }
@@ -45698,15 +45909,21 @@ async function getGitHubIssueBlockedBy(id, opts) {
45698
45909
  }
45699
45910
 
45700
45911
  // src/integrations/linear-blocked.ts
45912
+ var RESOLVED_LINEAR_STATE = /* @__PURE__ */ new Set(["completed", "canceled", "cancelled", "duplicate"]);
45913
+ function linearBlockerStillBlocks(issue) {
45914
+ const type = (issue?.state?.type ?? "").trim().toLowerCase();
45915
+ if (!type) return true;
45916
+ return !RESOLVED_LINEAR_STATE.has(type);
45917
+ }
45701
45918
  var ISSUE_BLOCKED_BY_QUERY = `
45702
45919
  query SideboardIssueBlockedBy($id: String!) {
45703
45920
  issue(id: $id) {
45704
45921
  identifier
45705
45922
  relations(first: 25) {
45706
- nodes { id type relatedIssue { id identifier } }
45923
+ nodes { id type relatedIssue { id identifier state { type } } }
45707
45924
  }
45708
45925
  inverseRelations(first: 25) {
45709
- nodes { id type issue { id identifier } }
45926
+ nodes { id type issue { id identifier state { type } } }
45710
45927
  }
45711
45928
  }
45712
45929
  }
@@ -45717,6 +45934,7 @@ function linearBlockedByIdentifiers(relations) {
45717
45934
  for (const rel of relations) {
45718
45935
  const type = (rel.type ?? "").trim().toLowerCase().replace(/[\s_-]+/g, "");
45719
45936
  if (type !== "blockedby" && type !== "blocked") continue;
45937
+ if (!linearBlockerStillBlocks(rel.issue)) continue;
45720
45938
  const id = (rel.issue?.identifier || rel.issue?.id || "").trim();
45721
45939
  const key = id.toLowerCase();
45722
45940
  if (!id || seen.has(key)) continue;
@@ -47908,6 +48126,7 @@ async function pollSlackOutboundWatches(opts) {
47908
48126
  ableTimeOAuthLocalBounceUrl,
47909
48127
  ableTimeOAuthRedirectUri,
47910
48128
  ableTimeOAuthResource,
48129
+ ableTimeStateIsClosed,
47911
48130
  ableTimeUsesRest,
47912
48131
  abletimeMcpRequest,
47913
48132
  abletimeMcpUrl,
@@ -48390,6 +48609,7 @@ async function pollSlackOutboundWatches(opts) {
48390
48609
  issueMatchesAssignee,
48391
48610
  issueSourceLabel,
48392
48611
  issueTicketFromThread,
48612
+ joinSteerPrompt,
48393
48613
  killDetachedRunTree,
48394
48614
  killListenersOnPorts,
48395
48615
  killStaleWorktreeListeners,
@@ -48448,6 +48668,7 @@ async function pollSlackOutboundWatches(opts) {
48448
48668
  listWorktrees,
48449
48669
  listenerCwd,
48450
48670
  liveActivitySummary,
48671
+ livePartsShowAgentReply,
48451
48672
  loadAppSettings,
48452
48673
  loadBrightsyConfig,
48453
48674
  loadConductorSettings,
@@ -48785,6 +49006,7 @@ async function pollSlackOutboundWatches(opts) {
48785
49006
  toolDetail,
48786
49007
  toolFilePath,
48787
49008
  totalTokens,
49009
+ trailingUnansweredUserText,
48788
49010
  trustSystemCertificates,
48789
49011
  tryReservePort,
48790
49012
  tryReservePorts,