@sideboard-ai/core 0.1.318 → 0.1.320

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 (61) hide show
  1. package/dist/{abletime-TKAFNXCM.js → abletime-CRRLEYTE.js} +6 -4
  2. package/dist/{abletime-KWOBUL46.js → abletime-ICW6Q2AB.js} +6 -4
  3. package/dist/{abletime-oauth-S4AXA4WR.js → abletime-oauth-P3KPPJE4.js} +3 -3
  4. package/dist/{abletime-oauth-IUIAGLFJ.js → abletime-oauth-Q76XBSPG.js} +3 -3
  5. package/dist/{abletime-rest-RWN4EPY3.js → abletime-rest-DNFAYYLP.js} +3 -3
  6. package/dist/{abletime-rest-ORBELZQO.js → abletime-rest-DQ3PNKXP.js} +3 -3
  7. package/dist/{agents-KABJUDPI.js → agents-CJZYJOWP.js} +7 -7
  8. package/dist/{agents-2AR7I6B6.js → agents-F2E2HACB.js} +7 -7
  9. package/dist/{app-settings-CZVFMOI5.js → app-settings-KMPVI2B7.js} +1 -1
  10. package/dist/{app-settings-V6YPYY57.js → app-settings-P3H4SEIK.js} +1 -1
  11. package/dist/{chunk-YEIHCSZS.js → chunk-4GZC6DDH.js} +2 -0
  12. package/dist/{chunk-XOZNEAWF.js → chunk-56M6VEXI.js} +3 -3
  13. package/dist/{chunk-7SCVGGF6.js → chunk-5OQONGHO.js} +5 -5
  14. package/dist/{chunk-A6C67DY5.js → chunk-5S3YP5KH.js} +4 -4
  15. package/dist/{chunk-I5CLCWUK.js → chunk-72PUPPV2.js} +9 -5
  16. package/dist/{chunk-BXTISNKW.js → chunk-B7UL36WH.js} +428 -99
  17. package/dist/{chunk-W5QZWAUB.js → chunk-BTIXTH4N.js} +9 -5
  18. package/dist/{chunk-AESO2KJS.js → chunk-CB5Z5C3J.js} +2 -2
  19. package/dist/{chunk-2HOZMSKQ.js → chunk-DFUXW2IT.js} +2 -2
  20. package/dist/{chunk-ST2UQBJT.js → chunk-DJZRDTZY.js} +6 -6
  21. package/dist/{chunk-TUJFCJJU.js → chunk-EDXTP2VR.js} +2 -2
  22. package/dist/{chunk-I5TESHDN.js → chunk-EQ52JR3Q.js} +5 -5
  23. package/dist/{chunk-FDXHNROD.js → chunk-EQ5JY3MJ.js} +2 -2
  24. package/dist/{chunk-E52SQENB.js → chunk-FZKAAAEB.js} +3 -1
  25. package/dist/{chunk-VZAJVX36.js → chunk-HHPTGIKS.js} +2 -2
  26. package/dist/{chunk-23ZUL2EX.js → chunk-IDPFLXRY.js} +429 -100
  27. package/dist/{chunk-GCHSJCCX.js → chunk-IFU4W4C4.js} +3 -1
  28. package/dist/{chunk-IAPO7ZFG.js → chunk-LLIEU2ZB.js} +5 -5
  29. package/dist/{chunk-MBYGWNLQ.js → chunk-OAFBHCSM.js} +1 -1
  30. package/dist/{chunk-NRSC4N5X.js → chunk-P74GXA5V.js} +6 -6
  31. package/dist/{chunk-A46CIARU.js → chunk-PNLGWAYE.js} +2 -2
  32. package/dist/{chunk-53OCOGND.js → chunk-QGY3UNNL.js} +2 -2
  33. package/dist/{chunk-7Q327QF2.js → chunk-QIC2JXHW.js} +5 -5
  34. package/dist/{chunk-YALFZKSC.js → chunk-RHJPP26T.js} +11 -11
  35. package/dist/{chunk-AZ4GEEGL.js → chunk-UK2KESBN.js} +2 -2
  36. package/dist/{chunk-7ADEYEMH.js → chunk-V76MQ7MH.js} +5 -5
  37. package/dist/{chunk-HOLB6ZFZ.js → chunk-XMHMVEIY.js} +2 -0
  38. package/dist/{chunk-GH7UMULC.js → chunk-ZYZBCLJR.js} +6 -6
  39. package/dist/{conductor-MG2RAQNT.js → conductor-FPFY6Z5S.js} +6 -6
  40. package/dist/{conductor-NIUBJLTS.js → conductor-JA5W3L7C.js} +6 -6
  41. package/dist/{coordinator-prompt-3CT3D5UM.js → coordinator-prompt-M75TAN43.js} +4 -4
  42. package/dist/{coordinator-prompt-X74F3JIC.js → coordinator-prompt-NUC2ZXMJ.js} +4 -4
  43. package/dist/{global-workspace-BUWMRNW5.js → global-workspace-5A4H34AF.js} +4 -4
  44. package/dist/{global-workspace-FTNJEML3.js → global-workspace-SCLQUNUI.js} +4 -4
  45. package/dist/index.cjs +861 -475
  46. package/dist/index.d.cts +56 -13
  47. package/dist/index.d.ts +56 -13
  48. package/dist/index.js +54 -31
  49. package/dist/mcp/run-stdio.cjs +459 -95
  50. package/dist/mcp/run-stdio.js +23 -23
  51. package/dist/{optional-cli-EMSZBYDE.js → optional-cli-2V3SLME5.js} +3 -3
  52. package/dist/{optional-cli-6GGESWGE.js → optional-cli-MRNQKVIR.js} +4 -4
  53. package/dist/{orchestrator-N54V3B76.js → orchestrator-7BDZ5GWD.js} +10 -10
  54. package/dist/{orchestrator-2SXK4U65.js → orchestrator-NPFWU3XU.js} +10 -10
  55. package/dist/{thread-store-PLNIWFZ4.js → thread-store-JWHVXLDX.js} +1 -1
  56. package/dist/{thread-store-7BBA63KV.js → thread-store-QHYN2ZMQ.js} +1 -1
  57. package/dist/{workspaces-2V5UYPYC.js → workspaces-5QA3YM4L.js} +4 -4
  58. package/dist/{workspaces-MGKEAYNM.js → workspaces-KWMBOTQD.js} +4 -4
  59. package/dist/{worktree-THAXW5U7.js → worktree-7EATHVYQ.js} +3 -3
  60. package/dist/{worktree-XJKLDYOL.js → worktree-O6BANO2I.js} +3 -3
  61. package/package.json +1 -1
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  formatOptionalServicesDirectiveForTurn,
8
8
  formatOptionalServicesReminderForTurn
9
- } from "./chunk-MBYGWNLQ.js";
9
+ } from "./chunk-OAFBHCSM.js";
10
10
  import {
11
11
  extractPresentedPlan,
12
12
  isPresentPlanToolName,
@@ -27,7 +27,7 @@ import {
27
27
  parseSessionQuotaResetAt,
28
28
  planModeTurnInstruction,
29
29
  resolveQuotaFallbackAgent
30
- } from "./chunk-GH7UMULC.js";
30
+ } from "./chunk-ZYZBCLJR.js";
31
31
  import {
32
32
  applyWorktreePkgCacheEnv,
33
33
  askBlockStillWaiting,
@@ -65,7 +65,7 @@ import {
65
65
  worktreeDevProcessKey,
66
66
  worktreeRunProcessKey,
67
67
  worktreeSetupProcessKey
68
- } from "./chunk-NRSC4N5X.js";
68
+ } from "./chunk-P74GXA5V.js";
69
69
  import {
70
70
  applyAgentEvent,
71
71
  clipAgentEventForPaint,
@@ -92,7 +92,7 @@ import {
92
92
  summarizeTurnStderr,
93
93
  toolDescription,
94
94
  turnFailChatText
95
- } from "./chunk-I5TESHDN.js";
95
+ } from "./chunk-EQ52JR3Q.js";
96
96
  import {
97
97
  SLACK_REPLY_FORMATTING,
98
98
  addWorkspace,
@@ -117,7 +117,7 @@ import {
117
117
  stageAbsolutePathsAsAttachments,
118
118
  stageBuffersAsAttachments,
119
119
  syncWorkspacesFromThreads
120
- } from "./chunk-A6C67DY5.js";
120
+ } from "./chunk-5S3YP5KH.js";
121
121
  import {
122
122
  addPrStackLayer,
123
123
  allocateTeamSlug,
@@ -159,7 +159,7 @@ import {
159
159
  resolveRepoRoot,
160
160
  switchWorktreeBranch,
161
161
  withRepoGitLock
162
- } from "./chunk-A46CIARU.js";
162
+ } from "./chunk-PNLGWAYE.js";
163
163
  import {
164
164
  clearStaleIndexLocks,
165
165
  enrichPathWithNpmGlobalBin,
@@ -201,7 +201,7 @@ import {
201
201
  usageOnLimit,
202
202
  withTurnScopedCost,
203
203
  writeViewerContext
204
- } from "./chunk-GCHSJCCX.js";
204
+ } from "./chunk-IFU4W4C4.js";
205
205
  import {
206
206
  writePrivateFile
207
207
  } from "./chunk-NSTQ6QKD.js";
@@ -242,7 +242,10 @@ import {
242
242
  worktreeNameFromPath,
243
243
  worktreeSlugForTicket,
244
244
  writeThread
245
- } from "./chunk-HOLB6ZFZ.js";
245
+ } from "./chunk-XMHMVEIY.js";
246
+ import {
247
+ normalizeThinkingEffort
248
+ } from "./chunk-KGZBYWZ3.js";
246
249
  import {
247
250
  annotateDevAccessRuns,
248
251
  appDataDir,
@@ -507,9 +510,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
507
510
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
508
511
  );
509
512
  }
510
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-BUWMRNW5.js");
513
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-5A4H34AF.js");
511
514
  if (isGlobalThread2(thread)) {
512
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-3CT3D5UM.js");
515
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-M75TAN43.js");
513
516
  ensureGlobalCoordinatorCwd2(
514
517
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
515
518
  );
@@ -1955,6 +1958,71 @@ function claimSteerRequest(threadId) {
1955
1958
  });
1956
1959
  }
1957
1960
 
1961
+ // src/orchestrator/ephemeral-automation.ts
1962
+ function ephemeralRunReadyToArchive(thread) {
1963
+ if (!thread.ephemeralAutomationId?.trim()) return false;
1964
+ if (thread.status !== "idle" && thread.status !== "error") return false;
1965
+ if (thread.queue.length > 0) return false;
1966
+ if (thread.agentBlock?.reason?.trim()) return false;
1967
+ if (thread.quotaResumeAt) return false;
1968
+ return true;
1969
+ }
1970
+ function markEphemeralAutomation(threadId, opts) {
1971
+ const id = opts.ephemeralAutomationId?.trim();
1972
+ const current = readThread(threadId);
1973
+ if (!current) throw new Error(`Thread not found: ${threadId}`);
1974
+ if (!id) return current;
1975
+ const title = opts.ephemeralTitle?.trim();
1976
+ return updateThread(threadId, {
1977
+ ephemeralAutomationId: id,
1978
+ ...title ? { title, userSetTitle: true } : {}
1979
+ });
1980
+ }
1981
+ async function archiveEphemeralAutomationIfReady(threadId, archive) {
1982
+ const keep = await withThreadLock(threadId, async () => {
1983
+ const thread = readThread(threadId);
1984
+ if (!thread || !ephemeralRunReadyToArchive(thread)) return null;
1985
+ const automationId = thread.ephemeralAutomationId?.trim();
1986
+ if (!automationId) return null;
1987
+ await archive(thread.id);
1988
+ return {
1989
+ automationId,
1990
+ keepId: archivedContinuationId(automationId, threadId) ?? threadId
1991
+ };
1992
+ });
1993
+ if (keep) forgetOlderEphemeralRuns(keep.automationId, keep.keepId);
1994
+ }
1995
+ function archivedContinuationId(automationId, sourceId) {
1996
+ for (const thread of listThreads({ includeArchived: true })) {
1997
+ if (thread.id === sourceId || thread.status !== "archived") continue;
1998
+ if (thread.ephemeralAutomationId !== automationId) continue;
1999
+ if (thread.quotaContinuedFromId !== sourceId) continue;
2000
+ return thread.id;
2001
+ }
2002
+ return null;
2003
+ }
2004
+ function clearEphemeralAutomation(threadId) {
2005
+ const thread = readThread(threadId);
2006
+ if (!thread?.ephemeralAutomationId?.trim()) return;
2007
+ updateThread(threadId, { ephemeralAutomationId: void 0 });
2008
+ }
2009
+ function forgetOlderEphemeralRuns(automationId, keepId) {
2010
+ const id = automationId.trim();
2011
+ if (!id || !keepId) return [];
2012
+ const removed = [];
2013
+ for (const thread of listThreads({ includeArchived: true })) {
2014
+ if (thread.id === keepId) continue;
2015
+ if (thread.status !== "archived") continue;
2016
+ if (thread.ephemeralAutomationId !== id) continue;
2017
+ try {
2018
+ deleteThreadRecord(thread.id);
2019
+ removed.push(thread.id);
2020
+ } catch {
2021
+ }
2022
+ }
2023
+ return removed;
2024
+ }
2025
+
1958
2026
  // src/threads/agent-purpose-title.ts
1959
2027
  function agentPurposeStamp(input) {
1960
2028
  if (input.autoContinue) return null;
@@ -2632,7 +2700,7 @@ async function createThread(input, _onSetupLine) {
2632
2700
  });
2633
2701
  await requireAgent(resolved.agent);
2634
2702
  if (input.cowboy) {
2635
- const { cowboyModeEnabled } = await import("./app-settings-CZVFMOI5.js");
2703
+ const { cowboyModeEnabled } = await import("./app-settings-KMPVI2B7.js");
2636
2704
  if (!cowboyModeEnabled()) {
2637
2705
  throw new Error(
2638
2706
  "Cowboy mode is off. Enable it in Settings \u2192 Advanced."
@@ -2691,7 +2759,7 @@ async function createThread(input, _onSetupLine) {
2691
2759
  let sourceIsFork = false;
2692
2760
  if (!input.cowboy && sourceType === "branch" && (!sourceRef || sourceRef === "HEAD" || sourceRef === "default")) {
2693
2761
  if (isAbleTimeConnected() && getIssueSource() === "abletime") {
2694
- const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-TKAFNXCM.js");
2762
+ const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-CRRLEYTE.js");
2695
2763
  const task = await ensureAbleTimeTask({
2696
2764
  title: title?.trim() || "Untitled work",
2697
2765
  description: input.prompt?.trim() || void 0
@@ -3210,6 +3278,9 @@ function writeTurnLive(threadId, progress) {
3210
3278
  }
3211
3279
  }
3212
3280
  }
3281
+ function readTurnLiveParts(threadId) {
3282
+ return buffers.get(threadId)?.parts.slice() ?? [];
3283
+ }
3213
3284
  function readTurnLive(threadId) {
3214
3285
  const path = threadLivePath(threadId);
3215
3286
  if (!existsSync6(path)) return null;
@@ -3447,17 +3518,23 @@ async function isolateQuotaFailover(run) {
3447
3518
  }
3448
3519
  }
3449
3520
  function createQuotaFailoverChat(from, fallbackAgent, limitText, opts) {
3521
+ const ephemeralAutomationId = from.ephemeralAutomationId?.trim();
3522
+ const ephemeralTitle = ephemeralAutomationId ? from.title.trim() : "";
3450
3523
  const handoff = buildQuotaHandoffAttachment(from, limitText, fallbackAgent);
3451
3524
  const tab = createChatTab({
3452
3525
  fromThreadId: from.id,
3453
3526
  agent: fallbackAgent,
3454
3527
  model: null,
3455
3528
  attachments: [handoff],
3529
+ ...ephemeralTitle ? { title: ephemeralTitle } : {},
3456
3530
  ...opts && "accountId" in opts ? { accountId: opts.accountId } : {}
3457
3531
  });
3458
3532
  const patch = {
3459
3533
  quotaContinuedFromId: from.id,
3460
- sourceRef: from.sourceRef
3534
+ sourceRef: from.sourceRef,
3535
+ // The original drain archives as idle. This sibling has to carry the one-off
3536
+ // so it archives when the continued turn finishes and History keeps it.
3537
+ ...ephemeralAutomationId ? { ephemeralAutomationId } : {}
3461
3538
  };
3462
3539
  if (isOrchestratorThread(from)) {
3463
3540
  patch.parentThreadId = from.id;
@@ -3564,7 +3641,7 @@ async function adoptThread(input) {
3564
3641
  messages: input.messages ?? []
3565
3642
  });
3566
3643
  writeThread(thread);
3567
- const { addWorkspace: addWorkspace2 } = await import("./workspaces-2V5UYPYC.js");
3644
+ const { addWorkspace: addWorkspace2 } = await import("./workspaces-5QA3YM4L.js");
3568
3645
  await addWorkspace2(repoPath);
3569
3646
  const { ensureWorktreeSideboardIgnored } = await import("./worktree-exclude-5N2KTYIT.js");
3570
3647
  await ensureWorktreeSideboardIgnored(input.worktreePath);
@@ -5384,6 +5461,36 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
5384
5461
  };
5385
5462
  }
5386
5463
 
5464
+ // src/composer/agent-has-replied.ts
5465
+ function joinSteerPrompt(base, addition) {
5466
+ const next = addition.trim();
5467
+ if (!next) return base;
5468
+ const head = base.replace(/\s+$/, "");
5469
+ if (!head) return next;
5470
+ return `${head}
5471
+ ${next}`;
5472
+ }
5473
+ function trailingUnansweredUserText(messages) {
5474
+ for (let i = messages.length - 1; i >= 0; i--) {
5475
+ const message = messages[i];
5476
+ if (!message || message.origin === "continue" || message.role === "summary") continue;
5477
+ if (message.role === "agent") {
5478
+ const text3 = (message.text ?? "").trim();
5479
+ if (text3 || (message.parts?.length ?? 0) > 0) return null;
5480
+ continue;
5481
+ }
5482
+ if (message.role === "user") return message.text;
5483
+ }
5484
+ return null;
5485
+ }
5486
+ function livePartsShowAgentReply(parts) {
5487
+ for (const part of parts) {
5488
+ if (part.type === "tool") return true;
5489
+ if ((part.type === "text" || part.type === "thinking") && part.text.trim()) return true;
5490
+ }
5491
+ return false;
5492
+ }
5493
+
5387
5494
  // src/composer/consume-attachments.ts
5388
5495
  function consumeComposerAttachments(thread) {
5389
5496
  return {
@@ -5436,6 +5543,128 @@ function shiftQueuedItem(queue, queueAttachments) {
5436
5543
  };
5437
5544
  }
5438
5545
 
5546
+ // src/orchestrator/enqueue-thread-send.ts
5547
+ function resolveSendFollowUp(thread, requested) {
5548
+ if (requested === "steer" || requested === "queue") return requested;
5549
+ return isOrchestratorThread(thread) ? followUpBehavior() : "queue";
5550
+ }
5551
+ function rewriteTrailingUserPrompt(threadId, from, to) {
5552
+ if (from === to) return;
5553
+ const thread = readThread(threadId);
5554
+ if (!thread) return;
5555
+ const messages = thread.messages.slice();
5556
+ for (let i = messages.length - 1; i >= 0; i--) {
5557
+ const message = messages[i];
5558
+ if (!message || message.origin === "continue" || message.role === "summary") continue;
5559
+ if (message.role === "user" && message.text === from) {
5560
+ messages[i] = { ...message, text: to };
5561
+ updateThread(threadId, { messages });
5562
+ }
5563
+ return;
5564
+ }
5565
+ }
5566
+ function reuseJoinedUserBubble(reused, threadId, last, promptText) {
5567
+ return reused.delete(threadId) && last?.role === "user" && last.origin !== "continue" && last.text === promptText;
5568
+ }
5569
+ function promptForJoinedSteer(input) {
5570
+ if (input.latestPrompt === input.promptText) return input.agentPrompt;
5571
+ rewriteTrailingUserPrompt(input.threadId, input.promptText, input.latestPrompt);
5572
+ const { agentPrompt: expandedLatest } = expandComposerPrompt(
5573
+ input.worktreePath,
5574
+ expandCanonicalGitRequest(input.latestPrompt),
5575
+ { attachments: input.attachments }
5576
+ );
5577
+ return input.expandedPrompt && input.agentPrompt.endsWith(input.expandedPrompt) ? input.agentPrompt.slice(0, -input.expandedPrompt.length) + expandedLatest : `${input.agentPrompt}
5578
+
5579
+ ${expandedLatest}`;
5580
+ }
5581
+ function clearSendBookkeeping(host, threadId) {
5582
+ host.crashContinued.delete(threadId);
5583
+ host.jobContinueCount.delete(threadId);
5584
+ host.jobContinueNudged.delete(threadId);
5585
+ host.haltDrain.delete(threadId);
5586
+ }
5587
+ async function enqueueThreadSend(host, threadRef, prompt, opts) {
5588
+ const thread = host.requireThread(threadRef);
5589
+ if (thread.status === "archived") {
5590
+ throw new Error(`Thread is archived: ${thread.id}`);
5591
+ }
5592
+ let shouldSteer = false;
5593
+ const sent = await withThreadLock(thread.id, async () => {
5594
+ const current = host.requireThread(thread.id);
5595
+ if (current.status === "archived") {
5596
+ throw new Error(`Thread is archived: ${thread.id}`);
5597
+ }
5598
+ const followUp = resolveSendFollowUp(current, opts?.followUp);
5599
+ const explicit = opts?.attachments;
5600
+ const consumed = explicit !== void 0 ? { attachments: current.attachments, consumed: explicit } : consumeComposerAttachments(current);
5601
+ const inFlightEarly = host.activeTurns.has(thread.id) || host.startingTurns.has(thread.id) || current.status === "running";
5602
+ const agentHasRepliedEarly = livePartsShowAgentReply(readTurnLiveParts(current.id));
5603
+ const draft = host.openSteerPrompt.get(current.id);
5604
+ const queuedHead = !inFlightEarly && current.status === "queued" && current.queue.length > 0 ? current.queue[0] : null;
5605
+ const unanswered = followUp === "steer" && !agentHasRepliedEarly ? draft?.text ?? (inFlightEarly ? trailingUnansweredUserText(current.messages) : queuedHead) : null;
5606
+ if (unanswered != null && (draft ? !draft.spawned : queuedHead != null)) {
5607
+ clearSendBookkeeping(host, thread.id);
5608
+ const combined = joinSteerPrompt(unanswered, prompt);
5609
+ if (draft && !draft.spawned) {
5610
+ draft.text = combined;
5611
+ rewriteTrailingUserPrompt(current.id, unanswered, combined);
5612
+ } else {
5613
+ const queue = current.queue.slice();
5614
+ queue[0] = combined;
5615
+ const queueAttachments = alignQueueAttachments(queue.length, current.queueAttachments);
5616
+ if (consumed.consumed.length > 0) {
5617
+ queueAttachments[0] = [...queueAttachments[0] ?? [], ...consumed.consumed];
5618
+ }
5619
+ const patch2 = { queue, queueAttachments };
5620
+ if (explicit === void 0 && consumed.consumed.length > 0) {
5621
+ patch2.attachments = consumed.attachments;
5622
+ }
5623
+ updateThread(current.id, patch2);
5624
+ host.emit({ type: "queue_changed", threadId: current.id, queue });
5625
+ }
5626
+ if (thisProcessShouldDrainAgentQueues() && !inFlightEarly) {
5627
+ host.armDrain(current.id);
5628
+ }
5629
+ return host.requireThread(current.id);
5630
+ }
5631
+ const promptToQueue = unanswered != null ? joinSteerPrompt(unanswered, prompt) : prompt;
5632
+ if (unanswered != null) {
5633
+ if (draft) draft.text = promptToQueue;
5634
+ rewriteTrailingUserPrompt(current.id, unanswered, promptToQueue);
5635
+ host.reusedUserPrompt.add(current.id);
5636
+ }
5637
+ const queued = appendQueuedItem(
5638
+ current.queue,
5639
+ current.queueAttachments,
5640
+ promptToQueue,
5641
+ consumed.consumed
5642
+ );
5643
+ clearSendBookkeeping(host, thread.id);
5644
+ const inFlight = host.activeTurns.has(thread.id) || host.startingTurns.has(thread.id) || current.status === "running";
5645
+ shouldSteer = followUp === "steer" && (inFlight || current.queue.length > 0);
5646
+ const patch = queued;
5647
+ if (explicit === void 0 && consumed.consumed.length > 0) {
5648
+ patch.attachments = consumed.attachments;
5649
+ }
5650
+ if (!inFlight) patch.status = "queued";
5651
+ const pid = current.agentPid;
5652
+ if (typeof pid === "number" && pid > 0 && !host.pidAlive(pid)) {
5653
+ patch.agentPid = null;
5654
+ }
5655
+ updateThread(thread.id, patch);
5656
+ host.emit({ type: "queue_changed", threadId: thread.id, queue: queued.queue });
5657
+ if (!inFlight) {
5658
+ host.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
5659
+ }
5660
+ if (thisProcessShouldDrainAgentQueues() && !shouldSteer) {
5661
+ host.armDrain(thread.id);
5662
+ }
5663
+ return host.requireThread(thread.id);
5664
+ });
5665
+ return { thread: sent, shouldSteer };
5666
+ }
5667
+
5439
5668
  // src/agents/instructions.ts
5440
5669
  import { existsSync as existsSync12 } from "fs";
5441
5670
  import { join as join12 } from "path";
@@ -5594,7 +5823,7 @@ function formatWorktreeDirective(thread, opts) {
5594
5823
  lines.push(formatDevAccessHint(opts?.activeRuns));
5595
5824
  lines.push("");
5596
5825
  lines.push(
5597
- "Schedules: `create_schedule` / `list_schedules` (`at` / `every` / `cron`). `chatId=self` continues this chat; omit chatId for a new Global chat. Fires only while Sideboard.app is running. A future job turns on Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled."
5826
+ "Automations (sidebar, after Board): `create_schedule` / `list_schedules` (`at` / `every` / `cron`). `chatId=self` continues this chat; omit chatId for a new Global chat. Fires only while Sideboard.app is running. A future job turns on Settings \u2192 Advanced \u2192 Caffeinate while automations are enabled."
5598
5827
  );
5599
5828
  lines.push(
5600
5829
  "`set_workspace_tags` labels this checkout (omit ref; replace, add, or remove). Not another checkout."
@@ -5614,7 +5843,7 @@ function connectedOrchestratorTurnReminder(thread, find) {
5614
5843
  return formatConnectedOrchestratorReminder(connectedOrchestrator(thread, find));
5615
5844
  }
5616
5845
  function formatWorktreeReminder() {
5617
- return "Sideboard workspace: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push. Schedules: create_schedule (at / every / cron; chatId=self for this chat). set_workspace_tags updates labels on this workspace (omit ref).";
5846
+ return "Sideboard workspace: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push. Automations: create_schedule (at / every / cron; chatId=self for this chat). set_workspace_tags updates labels on this workspace (omit ref).";
5618
5847
  }
5619
5848
  function formatClaudeSubagentReminder() {
5620
5849
  return 'Claude subagents: if Task/Agent workers will edit files, write a short plan in this chat and continue in the same turn. Do not ask the user whether to isolate, and do not set worktree.bgIsolation to none. One writer may edit this workspace. Every other writer is an Agent with isolation "worktree" and a disjoint file list (read-only explore agents may overlap). While those agents run, leave their files alone. Merge each returned branch into this workspace, then remove the temporary worktree. You stay in this cwd. If Claude says another write-capable agent is in this directory, or that writes to the shared checkout are blocked, re-spawn that agent with isolation "worktree" or make the edit yourself. That note is another worker in this chat, or a worker Claude still has marked running. Do not mention it to the user.';
@@ -6232,7 +6461,7 @@ function parseDurationMs(every) {
6232
6461
  }
6233
6462
  function defaultScheduleName(prompt) {
6234
6463
  const line = prompt.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
6235
- if (!line) return "Untitled schedule";
6464
+ if (!line) return "Untitled automation";
6236
6465
  return line.length > 60 ? `${line.slice(0, 57).trimEnd()}\u2026` : line;
6237
6466
  }
6238
6467
  function resolveScheduleThreadId(raw, env = process.env) {
@@ -6297,6 +6526,45 @@ function normalizeWhen(raw) {
6297
6526
  }
6298
6527
  throw new Error("Schedule `when` must be once, every, or cron");
6299
6528
  }
6529
+ var ATTACHMENT_KINDS = /* @__PURE__ */ new Set([
6530
+ "transcript",
6531
+ "file",
6532
+ "issue",
6533
+ "workspace",
6534
+ "diff-comment",
6535
+ "code-ref"
6536
+ ]);
6537
+ function normalizeOptionalBool(raw) {
6538
+ if (raw === true || raw === false) return raw;
6539
+ return null;
6540
+ }
6541
+ function normalizeAutonomy(raw) {
6542
+ return raw === "default" || raw === "full" ? raw : null;
6543
+ }
6544
+ function normalizeAttachments(raw) {
6545
+ if (!Array.isArray(raw)) return [];
6546
+ const out = [];
6547
+ for (const item of raw) {
6548
+ if (!item || typeof item !== "object") continue;
6549
+ const row = item;
6550
+ if (typeof row.id !== "string" || !row.id.trim()) continue;
6551
+ if (typeof row.name !== "string" || !row.name) continue;
6552
+ if (typeof row.content !== "string") continue;
6553
+ if (!ATTACHMENT_KINDS.has(row.kind)) continue;
6554
+ const next = {
6555
+ id: row.id,
6556
+ name: row.name,
6557
+ kind: row.kind,
6558
+ content: row.content
6559
+ };
6560
+ if (typeof row.path === "string" && row.path) next.path = row.path;
6561
+ if (typeof row.previewDataUrl === "string" && row.previewDataUrl) {
6562
+ next.previewDataUrl = row.previewDataUrl;
6563
+ }
6564
+ out.push(next);
6565
+ }
6566
+ return out;
6567
+ }
6300
6568
  function normalizeTask(raw) {
6301
6569
  const when = normalizeWhen(raw.when);
6302
6570
  const agent = raw.agent && isOrchestratorCapableAgent(raw.agent) ? raw.agent : null;
@@ -6309,6 +6577,11 @@ function normalizeTask(raw) {
6309
6577
  threadId: raw.threadId?.trim() || null,
6310
6578
  agent,
6311
6579
  model: raw.model?.trim() || null,
6580
+ effort: normalizeThinkingEffort(raw.effort),
6581
+ fast: normalizeOptionalBool(raw.fast),
6582
+ planMode: normalizeOptionalBool(raw.planMode),
6583
+ autonomy: normalizeAutonomy(raw.autonomy),
6584
+ attachments: normalizeAttachments(raw.attachments),
6312
6585
  nextRunAt: raw.nextRunAt,
6313
6586
  lastRunAt: raw.lastRunAt ?? null,
6314
6587
  lastThreadId: raw.lastThreadId ?? null,
@@ -6355,6 +6628,11 @@ function buildTask(input, now) {
6355
6628
  threadId: input.threadId?.trim() || null,
6356
6629
  agent,
6357
6630
  model: input.model?.trim() || null,
6631
+ effort: input.effort != null ? normalizeThinkingEffort(input.effort) : null,
6632
+ fast: normalizeOptionalBool(input.fast),
6633
+ planMode: normalizeOptionalBool(input.planMode),
6634
+ autonomy: normalizeAutonomy(input.autonomy),
6635
+ attachments: normalizeAttachments(input.attachments),
6358
6636
  nextRunAt,
6359
6637
  lastRunAt: null,
6360
6638
  lastThreadId: null,
@@ -6396,7 +6674,7 @@ function createSchedule(input) {
6396
6674
  function updateSchedule(id, patch) {
6397
6675
  const rows = readAll();
6398
6676
  const idx = rows.findIndex((row) => row.id === id || id.length >= 8 && row.id.startsWith(id));
6399
- if (idx < 0) throw new Error(`Schedule not found: ${id}`);
6677
+ if (idx < 0) throw new Error(`Automation not found: ${id}`);
6400
6678
  const prev = rows[idx];
6401
6679
  const when = patch.when ? normalizeWhen(patch.when) : prev.when;
6402
6680
  const prompt = patch.prompt !== void 0 ? patch.prompt.trim() : prev.prompt;
@@ -6411,6 +6689,11 @@ function updateSchedule(id, patch) {
6411
6689
  threadId: patch.threadId !== void 0 ? patch.threadId?.trim() || null : prev.threadId,
6412
6690
  agent: patch.agent !== void 0 ? patch.agent ? coerceOrchestratorAgent(patch.agent) : null : prev.agent,
6413
6691
  model: patch.model !== void 0 ? patch.model?.trim() || null : prev.model,
6692
+ effort: patch.effort !== void 0 ? normalizeThinkingEffort(patch.effort) : prev.effort,
6693
+ fast: patch.fast !== void 0 ? normalizeOptionalBool(patch.fast) : prev.fast,
6694
+ planMode: patch.planMode !== void 0 ? normalizeOptionalBool(patch.planMode) : prev.planMode,
6695
+ autonomy: patch.autonomy !== void 0 ? normalizeAutonomy(patch.autonomy) : prev.autonomy,
6696
+ attachments: patch.attachments !== void 0 ? normalizeAttachments(patch.attachments) : prev.attachments,
6414
6697
  nextRunAt: whenChanged ? computeNextRunAt(when).toISOString() : prev.nextRunAt,
6415
6698
  lastError: whenChanged ? null : prev.lastError,
6416
6699
  updatedAt: nowIso()
@@ -6425,13 +6708,13 @@ function deleteSchedule(id) {
6425
6708
  const next = rows.filter(
6426
6709
  (row) => row.id !== id && !(id.length >= 8 && row.id.startsWith(id))
6427
6710
  );
6428
- if (next.length === rows.length) throw new Error(`Schedule not found: ${id}`);
6711
+ if (next.length === rows.length) throw new Error(`Automation not found: ${id}`);
6429
6712
  writeAll(next);
6430
6713
  }
6431
6714
  function recordScheduleRun(id, result) {
6432
6715
  const rows = readAll();
6433
6716
  const idx = rows.findIndex((row) => row.id === id);
6434
- if (idx < 0) throw new Error(`Schedule not found: ${id}`);
6717
+ if (idx < 0) throw new Error(`Automation not found: ${id}`);
6435
6718
  const prev = rows[idx];
6436
6719
  const firedAt = result.firedAt ?? /* @__PURE__ */ new Date();
6437
6720
  const next = {
@@ -6458,21 +6741,56 @@ function formatScheduledPrompt(name, prompt) {
6458
6741
  ${prompt}`;
6459
6742
  }
6460
6743
  async function defaultDeps() {
6461
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-N54V3B76.js");
6744
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-7BDZ5GWD.js");
6462
6745
  const orch = getOrchestrator2();
6463
6746
  return {
6464
6747
  findThread: (id) => findThreadByRef(id),
6465
- send: (id, prompt) => orch.send(id, prompt),
6748
+ send: async (id, prompt, opts) => {
6749
+ if (opts?.options && Object.keys(opts.options).length > 0) {
6750
+ try {
6751
+ orch.setThreadOptions(id, opts.options);
6752
+ } catch {
6753
+ const rest = scheduleOptionsAfterRefusedAgentSwitch(opts.options);
6754
+ if (Object.keys(rest).length > 0) {
6755
+ try {
6756
+ orch.setThreadOptions(id, rest);
6757
+ } catch {
6758
+ }
6759
+ }
6760
+ }
6761
+ }
6762
+ const thread = findThreadByRef(id);
6763
+ const attachments = thread ? persistPendingFileAttachments(thread.worktreePath, opts?.attachments ?? []) : opts?.attachments ?? [];
6764
+ return orch.send(id, prompt, { attachments });
6765
+ },
6466
6766
  startOrchestration: (opts) => startOrchestration2(opts)
6467
6767
  };
6468
6768
  }
6769
+ function scheduleOptionsAfterRefusedAgentSwitch(options) {
6770
+ const { agent: _agent, model: _model, ...rest } = options;
6771
+ return rest;
6772
+ }
6773
+ function scheduleSendOptions(schedule) {
6774
+ const patch = {};
6775
+ if (schedule.agent) {
6776
+ patch.agent = schedule.agent;
6777
+ patch.model = schedule.model;
6778
+ } else if (schedule.model) {
6779
+ patch.model = schedule.model;
6780
+ }
6781
+ if (schedule.effort) patch.effort = schedule.effort;
6782
+ if (schedule.fast != null) patch.fast = schedule.fast;
6783
+ if (schedule.planMode != null) patch.planMode = schedule.planMode;
6784
+ if (schedule.autonomy) patch.autonomy = schedule.autonomy;
6785
+ return Object.keys(patch).length > 0 ? patch : void 0;
6786
+ }
6469
6787
  function pickAgent(schedule) {
6470
6788
  if (schedule.agent) return schedule.agent;
6471
6789
  return coerceOrchestratorAgent(resolveOrchestratorDefaults().agent);
6472
6790
  }
6473
6791
  async function fireSchedule(id) {
6474
6792
  const schedule = getSchedule(id);
6475
- if (!schedule) throw new Error(`Schedule not found: ${id}`);
6793
+ if (!schedule) throw new Error(`Automation not found: ${id}`);
6476
6794
  if (firing.has(schedule.id)) return schedule;
6477
6795
  firing.add(schedule.id);
6478
6796
  try {
@@ -6480,18 +6798,28 @@ async function fireSchedule(id) {
6480
6798
  const prompt = formatScheduledPrompt(schedule.name, schedule.prompt);
6481
6799
  const target = schedule.threadId ? deps.findThread(schedule.threadId) : null;
6482
6800
  if (target && target.status !== "archived") {
6483
- const sent = await deps.send(target.id, prompt);
6801
+ const sent = await deps.send(target.id, prompt, {
6802
+ attachments: schedule.attachments,
6803
+ options: scheduleSendOptions(schedule)
6804
+ });
6484
6805
  return recordScheduleRun(schedule.id, {
6485
6806
  lastThreadId: sent.id,
6486
6807
  lastError: null
6487
6808
  });
6488
6809
  }
6810
+ const pinReplacement = Boolean(schedule.threadId) && schedule.when.kind !== "once";
6489
6811
  const created = await deps.startOrchestration({
6490
6812
  goal: prompt,
6491
6813
  agent: pickAgent(schedule),
6492
- model: schedule.model
6814
+ model: schedule.model,
6815
+ ...schedule.effort ? { effort: schedule.effort } : {},
6816
+ ...schedule.fast != null ? { fast: schedule.fast } : {},
6817
+ ...schedule.planMode != null ? { planMode: schedule.planMode } : {},
6818
+ ...schedule.autonomy ? { autonomy: schedule.autonomy } : {},
6819
+ attachments: schedule.attachments,
6820
+ ...pinReplacement ? {} : { ephemeralAutomationId: schedule.id, ephemeralTitle: schedule.name }
6493
6821
  });
6494
- if (schedule.threadId && schedule.when.kind !== "once") {
6822
+ if (pinReplacement) {
6495
6823
  updateSchedule(schedule.id, { threadId: created.id });
6496
6824
  }
6497
6825
  return recordScheduleRun(schedule.id, {
@@ -6574,10 +6902,6 @@ async function waitForPidExit(pid, timeoutMs) {
6574
6902
  }
6575
6903
  return true;
6576
6904
  }
6577
- function resolveSendFollowUp(thread, requested) {
6578
- if (requested === "steer" || requested === "queue") return requested;
6579
- return isOrchestratorThread(thread) ? followUpBehavior() : "queue";
6580
- }
6581
6905
  function resolveOrchChildFollowUp(requested) {
6582
6906
  if (requested === "steer" || requested === "queue") return requested;
6583
6907
  return followUpBehavior();
@@ -6606,6 +6930,13 @@ var Orchestrator = class {
6606
6930
  draining = /* @__PURE__ */ new Set();
6607
6931
  /** Threads past setStatus(running) but not yet in activeTurns (spawn in flight). */
6608
6932
  startingTurns = /* @__PURE__ */ new Set();
6933
+ /**
6934
+ * Prompt for the turn that has not been answered yet. Steer follow-ups before
6935
+ * any agent output are joined onto this text instead of starting a second bubble.
6936
+ */
6937
+ openSteerPrompt = /* @__PURE__ */ new Map();
6938
+ /** Next runTurn should keep the user bubble already rewritten to this prompt. */
6939
+ reusedUserPrompt = /* @__PURE__ */ new Set();
6609
6940
  /** Last sync thread-file read for false-stop heal (throttled per thread). */
6610
6941
  lastReconcileHealAt = /* @__PURE__ */ new Map();
6611
6942
  /** Per-repo timestamp of the last `reconcile(repo, { light: true })`. */
@@ -6776,7 +7107,7 @@ var Orchestrator = class {
6776
7107
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
6777
7108
  });
6778
7109
  }
6779
- const { autoCleanupOrphansEnabled } = await import("./app-settings-CZVFMOI5.js");
7110
+ const { autoCleanupOrphansEnabled } = await import("./app-settings-KMPVI2B7.js");
6780
7111
  if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
6781
7112
  await cleanupOrphanWorktrees({ repoPaths });
6782
7113
  }
@@ -7170,7 +7501,7 @@ var Orchestrator = class {
7170
7501
  }
7171
7502
  await setup;
7172
7503
  if (skipSetup) return;
7173
- const { autoRunAfterSetupEnabled } = await import("./app-settings-CZVFMOI5.js");
7504
+ const { autoRunAfterSetupEnabled } = await import("./app-settings-KMPVI2B7.js");
7174
7505
  if (autoRunAfterSetupEnabled()) {
7175
7506
  try {
7176
7507
  await this.startDev(threadId);
@@ -7216,54 +7547,29 @@ var Orchestrator = class {
7216
7547
  return thread;
7217
7548
  }
7218
7549
  async send(threadRef, prompt, opts) {
7219
- const thread = this.requireThread(threadRef);
7220
- if (thread.status === "archived") {
7221
- throw new Error(`Thread is archived: ${thread.id}`);
7222
- }
7223
- let shouldSteer = false;
7224
- const sent = await withThreadLock(thread.id, async () => {
7225
- const current = this.requireThread(thread.id);
7226
- if (current.status === "archived") {
7227
- throw new Error(`Thread is archived: ${thread.id}`);
7228
- }
7229
- const followUp = resolveSendFollowUp(current, opts?.followUp);
7230
- const explicit = opts?.attachments;
7231
- const consumed = explicit !== void 0 ? { attachments: current.attachments, consumed: explicit } : consumeComposerAttachments(current);
7232
- const queued = appendQueuedItem(
7233
- current.queue,
7234
- current.queueAttachments,
7235
- prompt,
7236
- consumed.consumed
7237
- );
7238
- this.crashContinued.delete(thread.id);
7239
- this.jobContinueCount.delete(thread.id);
7240
- this.jobContinueNudged.delete(thread.id);
7241
- this.haltDrain.delete(thread.id);
7242
- const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id) || current.status === "running";
7243
- shouldSteer = followUp === "steer" && (inFlight || current.queue.length > 0);
7244
- const patch = queued;
7245
- if (explicit === void 0 && consumed.consumed.length > 0) {
7246
- patch.attachments = consumed.attachments;
7247
- }
7248
- if (!inFlight) patch.status = "queued";
7249
- const pid = current.agentPid;
7250
- if (typeof pid === "number" && pid > 0 && !isPidAlive(pid)) {
7251
- patch.agentPid = null;
7252
- }
7253
- updateThread(thread.id, patch);
7254
- this.emit({ type: "queue_changed", threadId: thread.id, queue: queued.queue });
7255
- if (!inFlight) {
7256
- this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
7257
- }
7258
- if (thisProcessShouldDrainAgentQueues() && !shouldSteer) {
7259
- this.armDrain(thread.id);
7260
- }
7261
- return this.requireThread(thread.id);
7262
- });
7263
- if (shouldSteer && sent.queue.length > 0) {
7264
- return this.sendQueuedMessageNow(sent.id, sent.queue.length - 1);
7550
+ const sent = await enqueueThreadSend(
7551
+ {
7552
+ activeTurns: this.activeTurns,
7553
+ startingTurns: this.startingTurns,
7554
+ openSteerPrompt: this.openSteerPrompt,
7555
+ reusedUserPrompt: this.reusedUserPrompt,
7556
+ crashContinued: this.crashContinued,
7557
+ jobContinueCount: this.jobContinueCount,
7558
+ jobContinueNudged: this.jobContinueNudged,
7559
+ haltDrain: this.haltDrain,
7560
+ emit: (event) => this.emit(event),
7561
+ armDrain: (id) => this.armDrain(id),
7562
+ requireThread: (ref) => this.requireThread(ref),
7563
+ pidAlive: (pid) => isPidAlive(pid)
7564
+ },
7565
+ threadRef,
7566
+ prompt,
7567
+ opts
7568
+ );
7569
+ if (sent.shouldSteer && sent.thread.queue.length > 0) {
7570
+ return this.sendQueuedMessageNow(sent.thread.id, sent.thread.queue.length - 1);
7265
7571
  }
7266
- return sent;
7572
+ return sent.thread;
7267
7573
  }
7268
7574
  async fanOut(threadRefs, prompt, opts) {
7269
7575
  const results = [];
@@ -7404,6 +7710,7 @@ var Orchestrator = class {
7404
7710
  } finally {
7405
7711
  this.draining.delete(threadId);
7406
7712
  }
7713
+ await archiveEphemeralAutomationIfReady(threadId, (id) => this.archive(id));
7407
7714
  }
7408
7715
  async runTurn(threadId, prompt) {
7409
7716
  const existing = readThread(threadId);
@@ -7420,6 +7727,10 @@ var Orchestrator = class {
7420
7727
  this.emit({ type: "status_changed", threadId, status: "running" });
7421
7728
  this.emit({ type: "turn_started", threadId, prompt });
7422
7729
  try {
7730
+ this.openSteerPrompt.set(threadId, {
7731
+ text: typeof prompt === "string" ? prompt : "",
7732
+ spawned: false
7733
+ });
7423
7734
  try {
7424
7735
  const baseline = await captureTurnBaseline(thread.worktreePath);
7425
7736
  if (baseline) this.turnBaselines.set(threadId, baseline);
@@ -7445,18 +7756,21 @@ var Orchestrator = class {
7445
7756
  this.emit({ type: "error", threadId, message: `context compact failed: ${message}` });
7446
7757
  thread = this.requireThread(threadId);
7447
7758
  }
7448
- const promptText = typeof prompt === "string" ? prompt : "";
7759
+ const promptText = this.openSteerPrompt.get(threadId)?.text ?? (typeof prompt === "string" ? prompt : "");
7449
7760
  const autoContinue = isJobContinuePrompt(promptText) || promptText.startsWith("The previous agent process ended before it finished.");
7450
7761
  const parked = takePendingTurnAttachments(thread);
7451
7762
  const sentAttachments = !autoContinue && parked.length > 0 ? parked : void 0;
7452
7763
  if (!autoContinue) clearAgentBlock(threadId);
7453
- appendMessage(threadId, {
7454
- role: "user",
7455
- text: promptText,
7456
- ...sentAttachments ? { attachments: sentAttachments } : {},
7457
- ...autoContinue ? { origin: "continue" } : {},
7458
- ts: (/* @__PURE__ */ new Date()).toISOString()
7459
- });
7764
+ const lastShown = this.requireThread(threadId).messages.at(-1);
7765
+ if (!reuseJoinedUserBubble(this.reusedUserPrompt, threadId, lastShown, promptText)) {
7766
+ appendMessage(threadId, {
7767
+ role: "user",
7768
+ text: promptText,
7769
+ ...sentAttachments ? { attachments: sentAttachments } : {},
7770
+ ...autoContinue ? { origin: "continue" } : {},
7771
+ ts: (/* @__PURE__ */ new Date()).toISOString()
7772
+ });
7773
+ }
7460
7774
  if (!autoContinue) {
7461
7775
  const live = this.requireThread(threadId);
7462
7776
  const stamp = agentPurposeStamp({
@@ -7564,7 +7878,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
7564
7878
  }
7565
7879
  const isBrightsy = fresh.agent === "brightsy";
7566
7880
  const isOrchestration = isOrchestratorThread(fresh);
7567
- const { autoRenameBranchEnabled, getGithubGitAuthMode, gitBranchPrefixSetting } = await import("./app-settings-CZVFMOI5.js");
7881
+ const { autoRenameBranchEnabled, getGithubGitAuthMode, gitBranchPrefixSetting } = await import("./app-settings-KMPVI2B7.js");
7568
7882
  const gitAuthMode = getGithubGitAuthMode();
7569
7883
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
7570
7884
  githubSlug: await withTimeout(
@@ -7655,11 +7969,22 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
7655
7969
  ].filter(Boolean).join("\n\n---\n\n");
7656
7970
  const stderrTail = [];
7657
7971
  traceTurn("runTurn.beforeSpawn", { threadId, agent: fresh.agent });
7972
+ const steerDraft = this.openSteerPrompt.get(threadId);
7973
+ if (steerDraft) steerDraft.spawned = true;
7974
+ const promptForAgent = promptForJoinedSteer({
7975
+ threadId,
7976
+ promptText,
7977
+ latestPrompt: steerDraft?.text ?? promptText,
7978
+ agentPrompt,
7979
+ expandedPrompt,
7980
+ worktreePath: fresh.worktreePath,
7981
+ attachments: autoContinue ? [] : sentAttachments ?? []
7982
+ });
7658
7983
  const handle = await spawnAgentTurn(
7659
7984
  fresh,
7660
7985
  {
7661
7986
  cachedPrefix,
7662
- prompt: agentPrompt,
7987
+ prompt: promptForAgent,
7663
7988
  systemPrompt: turnReminders,
7664
7989
  isolateCodexPlugins: fresh.isolateCodexPlugins === true
7665
7990
  },
@@ -7787,7 +8112,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
7787
8112
  retryThread,
7788
8113
  {
7789
8114
  cachedPrefix: retryPrefix,
7790
- prompt: agentPrompt,
8115
+ prompt: promptForAgent,
7791
8116
  systemPrompt: turnReminders,
7792
8117
  isolateCodexPlugins: isolateCodexPluginRetry || retryThread.isolateCodexPlugins === true
7793
8118
  },
@@ -7947,6 +8272,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
7947
8272
  } finally {
7948
8273
  this.startingTurns.delete(threadId);
7949
8274
  this.activeTurns.delete(threadId);
8275
+ this.openSteerPrompt.delete(threadId);
7950
8276
  this.processes.delete(`${threadId}:agent`);
7951
8277
  this.stoppedTurns.delete(threadId);
7952
8278
  this.runningCount = Math.max(0, this.runningCount - 1);
@@ -8880,7 +9206,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
8880
9206
  if (Object.keys(patch).length === 0) continue;
8881
9207
  updateThread(t.id, patch);
8882
9208
  }
8883
- const { autoArchiveOnMergeEnabled } = await import("./app-settings-CZVFMOI5.js");
9209
+ const { autoArchiveOnMergeEnabled } = await import("./app-settings-KMPVI2B7.js");
8884
9210
  const latest = this.requireThread(thread.id);
8885
9211
  if (!shouldAutoArchiveOnPrMerge({
8886
9212
  previousPrState: prevState || null,
@@ -9204,7 +9530,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
9204
9530
  await runArchiveScript(thread.repoPath, thread.worktreePath);
9205
9531
  } catch {
9206
9532
  }
9207
- const { deleteBranchOnPurgeEnabled } = await import("./app-settings-CZVFMOI5.js");
9533
+ const { deleteBranchOnPurgeEnabled } = await import("./app-settings-KMPVI2B7.js");
9208
9534
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
9209
9535
  await removeWorktree(thread.repoPath, thread.worktreePath, {
9210
9536
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -9218,6 +9544,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
9218
9544
  if (thread.status !== "archived") {
9219
9545
  throw new Error("Thread is not archived");
9220
9546
  }
9547
+ clearEphemeralAutomation(thread.id);
9221
9548
  if (isGlobalThread(thread)) {
9222
9549
  const { globalAgentCwd } = await import("./paths-HAAJNTTE.js");
9223
9550
  updateThread(thread.id, { worktreePath: globalAgentCwd() });
@@ -9317,10 +9644,11 @@ async function startOrchestration(opts) {
9317
9644
  planMode: opts.planMode,
9318
9645
  attachments: opts.attachments
9319
9646
  });
9647
+ const ready2 = markEphemeralAutomation(thread2.id, opts);
9320
9648
  if (goal) {
9321
- return orch.send(thread2.id, goal);
9649
+ return orch.send(ready2.id, goal);
9322
9650
  }
9323
- return thread2;
9651
+ return ready2;
9324
9652
  }
9325
9653
  const { titleFromPrompt: titleFromPrompt2 } = await import("./title-ERD6D3C3.js");
9326
9654
  const title = titleFromPrompt2(goal) || "Orchestration";
@@ -9340,7 +9668,7 @@ async function startOrchestration(opts) {
9340
9668
  sourceRef: "default",
9341
9669
  ...createOpts
9342
9670
  }).catch(async () => {
9343
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-XJKLDYOL.js");
9671
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-O6BANO2I.js");
9344
9672
  const repo = await resolveRepoRoot2(repoPath);
9345
9673
  const def = await resolveDefaultBranch2(repo);
9346
9674
  return orch.createThread({
@@ -9350,15 +9678,16 @@ async function startOrchestration(opts) {
9350
9678
  repoPath: repo
9351
9679
  });
9352
9680
  });
9353
- const { updateThread: upd } = await import("./thread-store-PLNIWFZ4.js");
9681
+ const { updateThread: upd } = await import("./thread-store-JWHVXLDX.js");
9354
9682
  const updated = upd(thread.id, {
9355
9683
  sourceType: "orchestration",
9356
9684
  sourceRef: goal
9357
9685
  });
9686
+ const ready = markEphemeralAutomation(updated.id, opts);
9358
9687
  if (goal) {
9359
- return orch.send(updated.id, goal);
9688
+ return orch.send(ready.id, goal);
9360
9689
  }
9361
- return updated;
9690
+ return ready;
9362
9691
  }
9363
9692
 
9364
9693
  export {
@@ -9396,6 +9725,7 @@ export {
9396
9725
  listFleetArchive,
9397
9726
  formatFleetArchive,
9398
9727
  readTurnLive,
9728
+ resolveSendFollowUp,
9399
9729
  localhostPreviewUrl,
9400
9730
  parseIssueSince,
9401
9731
  formatGitHubSearchUpdatedSince,
@@ -9419,7 +9749,6 @@ export {
9419
9749
  RUN_SCRIPT_DESKTOP_ADOPT_MS,
9420
9750
  isPidAlive,
9421
9751
  waitForPidExit,
9422
- resolveSendFollowUp,
9423
9752
  resolveOrchChildFollowUp,
9424
9753
  LIGHT_RECONCILE_THROTTLE_MS,
9425
9754
  Orchestrator,