@sideboard-ai/core 0.1.318 → 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.
package/dist/index.cjs CHANGED
@@ -8685,18 +8685,18 @@ function mcpWaitForTurnTimeoutMs(requested) {
8685
8685
  if (!Number.isFinite(n)) return MCP_WAIT_FOR_TURN_MAX_MS;
8686
8686
  return Math.min(Math.max(1e3, Math.floor(n)), MCP_WAIT_FOR_TURN_MAX_MS);
8687
8687
  }
8688
- function mcpWaitTaskHint(taskState, status, blockedReason) {
8688
+ function mcpWaitTaskHint(taskState2, status, blockedReason) {
8689
8689
  const why = blockedReason?.trim();
8690
- if (why && taskState !== "failed" && taskState !== "canceled") {
8691
- if (taskState === "submitted" || taskState === "working") {
8690
+ if (why && taskState2 !== "failed" && taskState2 !== "canceled") {
8691
+ if (taskState2 === "submitted" || taskState2 === "working") {
8692
8692
  return `Child is blocked: ${why} It has not finished the turn. Do not send_to_chat a check-in.`;
8693
8693
  }
8694
- if (taskState === "input-required") {
8694
+ if (taskState2 === "input-required") {
8695
8695
  return `Child is blocked: ${why} Wait for the user in that chat \u2014 do not send_to_chat a check-in.`;
8696
8696
  }
8697
8697
  return `Child is blocked: ${why} Help if this is your child; otherwise tell the user. Do not send_to_chat a check-in.`;
8698
8698
  }
8699
- switch (taskState) {
8699
+ switch (taskState2) {
8700
8700
  case "submitted":
8701
8701
  return MCP_WAIT_QUEUED_HINT;
8702
8702
  case "working":
@@ -24886,6 +24886,7 @@ var init_abletime_projects = __esm({
24886
24886
  var abletime_exports = {};
24887
24887
  __export(abletime_exports, {
24888
24888
  ableTimeDependencyTarget: () => ableTimeDependencyTarget,
24889
+ ableTimeStateIsClosed: () => ableTimeStateIsClosed,
24889
24890
  commentAbleTimeTask: () => commentAbleTimeTask,
24890
24891
  createAbleTimeTask: () => createAbleTimeTask,
24891
24892
  downloadAbleTimeAttachment: () => downloadAbleTimeAttachment,
@@ -25039,9 +25040,12 @@ function issueAttachmentForAbleTimeTask(task) {
25039
25040
  ].filter(Boolean).join("\n")
25040
25041
  };
25041
25042
  }
25043
+ function ableTimeStateIsClosed(state) {
25044
+ const value = (state ?? "").trim().toLowerCase();
25045
+ return Boolean(value && CLOSED_STATES.has(value));
25046
+ }
25042
25047
  function isOpenTask(task) {
25043
- const state = (task.state ?? "").toLowerCase();
25044
- return !state || !CLOSED_STATES.has(state);
25048
+ return !ableTimeStateIsClosed(task.state);
25045
25049
  }
25046
25050
  async function getAbleTimeOrientation(opts) {
25047
25051
  const raw = await callAbleTimeTool("orientation", {}, opts);
@@ -28329,6 +28333,41 @@ var init_expand = __esm({
28329
28333
  }
28330
28334
  });
28331
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
+
28332
28371
  // src/composer/consume-attachments.ts
28333
28372
  function consumeComposerAttachments(thread) {
28334
28373
  return {
@@ -28386,6 +28425,142 @@ var init_consume_attachments = __esm({
28386
28425
  }
28387
28426
  });
28388
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
+
28389
28564
  // src/skills/detached-job-path.ts
28390
28565
  function compiledModuleDir() {
28391
28566
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
@@ -29756,10 +29931,6 @@ async function waitForPidExit(pid, timeoutMs) {
29756
29931
  }
29757
29932
  return true;
29758
29933
  }
29759
- function resolveSendFollowUp(thread, requested) {
29760
- if (requested === "steer" || requested === "queue") return requested;
29761
- return isOrchestratorThread(thread) ? followUpBehavior() : "queue";
29762
- }
29763
29934
  function resolveOrchChildFollowUp(requested) {
29764
29935
  if (requested === "steer" || requested === "queue") return requested;
29765
29936
  return followUpBehavior();
@@ -29909,6 +30080,7 @@ var init_orchestrator = __esm({
29909
30080
  init_read_worktree_tail();
29910
30081
  init_discover();
29911
30082
  init_expand();
30083
+ init_enqueue_thread_send();
29912
30084
  init_consume_attachments();
29913
30085
  init_stage_files();
29914
30086
  init_context_compact();
@@ -29926,6 +30098,7 @@ var init_orchestrator = __esm({
29926
30098
  init_caffeinate_hold();
29927
30099
  init_schedule_runner();
29928
30100
  init_coordinator_prompt();
30101
+ init_enqueue_thread_send();
29929
30102
  LIVE_TURN_SPAWN_GRACE_MS = 15e3;
29930
30103
  RUN_SCRIPT_DESKTOP_ADOPT_MS = 2e4;
29931
30104
  STALE_AGENT_PID_WAIT_MS = 2500;
@@ -29937,6 +30110,13 @@ var init_orchestrator = __esm({
29937
30110
  draining = /* @__PURE__ */ new Set();
29938
30111
  /** Threads past setStatus(running) but not yet in activeTurns (spawn in flight). */
29939
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();
29940
30120
  /** Last sync thread-file read for false-stop heal (throttled per thread). */
29941
30121
  lastReconcileHealAt = /* @__PURE__ */ new Map();
29942
30122
  /** Per-repo timestamp of the last `reconcile(repo, { light: true })`. */
@@ -30547,54 +30727,29 @@ var init_orchestrator = __esm({
30547
30727
  return thread;
30548
30728
  }
30549
30729
  async send(threadRef, prompt, opts) {
30550
- const thread = this.requireThread(threadRef);
30551
- if (thread.status === "archived") {
30552
- throw new Error(`Thread is archived: ${thread.id}`);
30553
- }
30554
- let shouldSteer = false;
30555
- const sent = await withThreadLock(thread.id, async () => {
30556
- const current = this.requireThread(thread.id);
30557
- if (current.status === "archived") {
30558
- throw new Error(`Thread is archived: ${thread.id}`);
30559
- }
30560
- const followUp = resolveSendFollowUp(current, opts?.followUp);
30561
- const explicit = opts?.attachments;
30562
- const consumed = explicit !== void 0 ? { attachments: current.attachments, consumed: explicit } : consumeComposerAttachments(current);
30563
- const queued = appendQueuedItem(
30564
- current.queue,
30565
- current.queueAttachments,
30566
- prompt,
30567
- consumed.consumed
30568
- );
30569
- this.crashContinued.delete(thread.id);
30570
- this.jobContinueCount.delete(thread.id);
30571
- this.jobContinueNudged.delete(thread.id);
30572
- this.haltDrain.delete(thread.id);
30573
- const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id) || current.status === "running";
30574
- shouldSteer = followUp === "steer" && (inFlight || current.queue.length > 0);
30575
- const patch = queued;
30576
- if (explicit === void 0 && consumed.consumed.length > 0) {
30577
- patch.attachments = consumed.attachments;
30578
- }
30579
- if (!inFlight) patch.status = "queued";
30580
- const pid = current.agentPid;
30581
- if (typeof pid === "number" && pid > 0 && !isPidAlive(pid)) {
30582
- patch.agentPid = null;
30583
- }
30584
- updateThread(thread.id, patch);
30585
- this.emit({ type: "queue_changed", threadId: thread.id, queue: queued.queue });
30586
- if (!inFlight) {
30587
- this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
30588
- }
30589
- if (thisProcessShouldDrainAgentQueues() && !shouldSteer) {
30590
- this.armDrain(thread.id);
30591
- }
30592
- return this.requireThread(thread.id);
30593
- });
30594
- if (shouldSteer && sent.queue.length > 0) {
30595
- 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);
30596
30751
  }
30597
- return sent;
30752
+ return sent.thread;
30598
30753
  }
30599
30754
  async fanOut(threadRefs, prompt, opts) {
30600
30755
  const results = [];
@@ -30751,6 +30906,10 @@ var init_orchestrator = __esm({
30751
30906
  this.emit({ type: "status_changed", threadId, status: "running" });
30752
30907
  this.emit({ type: "turn_started", threadId, prompt });
30753
30908
  try {
30909
+ this.openSteerPrompt.set(threadId, {
30910
+ text: typeof prompt === "string" ? prompt : "",
30911
+ spawned: false
30912
+ });
30754
30913
  try {
30755
30914
  const baseline = await captureTurnBaseline(thread.worktreePath);
30756
30915
  if (baseline) this.turnBaselines.set(threadId, baseline);
@@ -30776,18 +30935,21 @@ var init_orchestrator = __esm({
30776
30935
  this.emit({ type: "error", threadId, message: `context compact failed: ${message}` });
30777
30936
  thread = this.requireThread(threadId);
30778
30937
  }
30779
- const promptText = typeof prompt === "string" ? prompt : "";
30938
+ const promptText = this.openSteerPrompt.get(threadId)?.text ?? (typeof prompt === "string" ? prompt : "");
30780
30939
  const autoContinue = isJobContinuePrompt(promptText) || promptText.startsWith("The previous agent process ended before it finished.");
30781
30940
  const parked = takePendingTurnAttachments(thread);
30782
30941
  const sentAttachments = !autoContinue && parked.length > 0 ? parked : void 0;
30783
30942
  if (!autoContinue) clearAgentBlock(threadId);
30784
- appendMessage(threadId, {
30785
- role: "user",
30786
- text: promptText,
30787
- ...sentAttachments ? { attachments: sentAttachments } : {},
30788
- ...autoContinue ? { origin: "continue" } : {},
30789
- ts: (/* @__PURE__ */ new Date()).toISOString()
30790
- });
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
+ }
30791
30953
  if (!autoContinue) {
30792
30954
  const live = this.requireThread(threadId);
30793
30955
  const stamp = agentPurposeStamp({
@@ -30986,11 +31148,22 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
30986
31148
  ].filter(Boolean).join("\n\n---\n\n");
30987
31149
  const stderrTail = [];
30988
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
+ });
30989
31162
  const handle = await spawnAgentTurn(
30990
31163
  fresh,
30991
31164
  {
30992
31165
  cachedPrefix,
30993
- prompt: agentPrompt,
31166
+ prompt: promptForAgent,
30994
31167
  systemPrompt: turnReminders,
30995
31168
  isolateCodexPlugins: fresh.isolateCodexPlugins === true
30996
31169
  },
@@ -31118,7 +31291,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
31118
31291
  retryThread,
31119
31292
  {
31120
31293
  cachedPrefix: retryPrefix,
31121
- prompt: agentPrompt,
31294
+ prompt: promptForAgent,
31122
31295
  systemPrompt: turnReminders,
31123
31296
  isolateCodexPlugins: isolateCodexPluginRetry || retryThread.isolateCodexPlugins === true
31124
31297
  },
@@ -31278,6 +31451,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
31278
31451
  } finally {
31279
31452
  this.startingTurns.delete(threadId);
31280
31453
  this.activeTurns.delete(threadId);
31454
+ this.openSteerPrompt.delete(threadId);
31281
31455
  this.processes.delete(`${threadId}:agent`);
31282
31456
  this.stoppedTurns.delete(threadId);
31283
31457
  this.runningCount = Math.max(0, this.runningCount - 1);
@@ -31954,7 +32128,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
31954
32128
  const live = stillRunning ? readTurnLive(thread.id) : null;
31955
32129
  const liveSummary = live?.summary && !isInternalAgentStatusText(live.summary) ? live.summary : null;
31956
32130
  const queuedHint = stillRunning && thread.status === "queued" && !liveSummary ? "Queued \u2014 waiting for a concurrency slot" : null;
31957
- const taskState = deriveTaskState({
32131
+ const taskState2 = deriveTaskState({
31958
32132
  status: thread.status,
31959
32133
  stillRunning,
31960
32134
  lastAgentParts: lastAgent?.parts
@@ -31963,7 +32137,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
31963
32137
  return {
31964
32138
  text: text7,
31965
32139
  status: thread.status,
31966
- taskState,
32140
+ taskState: taskState2,
31967
32141
  sessionId: thread.sessionId,
31968
32142
  lastError,
31969
32143
  stillRunning,
@@ -32907,6 +33081,7 @@ __export(index_exports, {
32907
33081
  ableTimeOAuthLocalBounceUrl: () => ableTimeOAuthLocalBounceUrl,
32908
33082
  ableTimeOAuthRedirectUri: () => ableTimeOAuthRedirectUri,
32909
33083
  ableTimeOAuthResource: () => ableTimeOAuthResource,
33084
+ ableTimeStateIsClosed: () => ableTimeStateIsClosed,
32910
33085
  ableTimeUsesRest: () => ableTimeUsesRest,
32911
33086
  abletimeMcpRequest: () => abletimeMcpRequest,
32912
33087
  abletimeMcpUrl: () => abletimeMcpUrl,
@@ -33389,6 +33564,7 @@ __export(index_exports, {
33389
33564
  issueMatchesAssignee: () => issueMatchesAssignee,
33390
33565
  issueSourceLabel: () => issueSourceLabel,
33391
33566
  issueTicketFromThread: () => issueTicketFromThread,
33567
+ joinSteerPrompt: () => joinSteerPrompt,
33392
33568
  killDetachedRunTree: () => killDetachedRunTree,
33393
33569
  killListenersOnPorts: () => killListenersOnPorts,
33394
33570
  killStaleWorktreeListeners: () => killStaleWorktreeListeners,
@@ -33447,6 +33623,7 @@ __export(index_exports, {
33447
33623
  listWorktrees: () => listWorktrees,
33448
33624
  listenerCwd: () => listenerCwd,
33449
33625
  liveActivitySummary: () => liveActivitySummary,
33626
+ livePartsShowAgentReply: () => livePartsShowAgentReply,
33450
33627
  loadAppSettings: () => loadAppSettings,
33451
33628
  loadBrightsyConfig: () => loadBrightsyConfig,
33452
33629
  loadConductorSettings: () => loadConductorSettings,
@@ -33784,6 +33961,7 @@ __export(index_exports, {
33784
33961
  toolDetail: () => toolDetail,
33785
33962
  toolFilePath: () => toolFilePath,
33786
33963
  totalTokens: () => totalTokens,
33964
+ trailingUnansweredUserText: () => trailingUnansweredUserText,
33787
33965
  trustSystemCertificates: () => trustSystemCertificates,
33788
33966
  tryReservePort: () => tryReservePort,
33789
33967
  tryReservePorts: () => tryReservePorts,
@@ -41754,6 +41932,7 @@ init_discover();
41754
41932
  init_detached_job_path();
41755
41933
  init_long_running();
41756
41934
  init_expand();
41935
+ init_agent_has_replied();
41757
41936
 
41758
41937
  // src/composer/diff-comment.ts
41759
41938
  function lineRangeLabel(lines) {
@@ -45568,6 +45747,7 @@ init_profile();
45568
45747
  init_home_board();
45569
45748
 
45570
45749
  // src/integrations/abletime-blocked.ts
45750
+ init_abletime();
45571
45751
  init_abletime_mcp();
45572
45752
  init_abletime_rest();
45573
45753
  function asRecord18(value) {
@@ -45592,6 +45772,9 @@ function firstRecord2(record, keys) {
45592
45772
  }
45593
45773
  return null;
45594
45774
  }
45775
+ function taskState(record) {
45776
+ return firstString5(record, ["state", "board_state", "boardState", "status", "taskState"]);
45777
+ }
45595
45778
  function identifierFromUnknown(value) {
45596
45779
  if (typeof value === "string" || typeof value === "number") {
45597
45780
  const id2 = String(value).trim();
@@ -45603,6 +45786,7 @@ function identifierFromUnknown(value) {
45603
45786
  for (const key of ["nodes", "data", "tasks", "items"]) {
45604
45787
  if (Array.isArray(rec[key])) return identifierFromUnknown(rec[key]);
45605
45788
  }
45789
+ if (ableTimeStateIsClosed(taskState(rec))) return [];
45606
45790
  const id = firstString5(rec, ["identifier", "reference", "ref", "key", "code", "number", "id"]);
45607
45791
  return id ? [id] : [];
45608
45792
  }
@@ -45637,6 +45821,8 @@ async function getAbleTimeIssueBlockedBy(id, opts) {
45637
45821
  continue;
45638
45822
  }
45639
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;
45640
45826
  const ref = taskReference(parent);
45641
45827
  if (!ref || looksLikeAbleTimeUlid(ref)) {
45642
45828
  throw new Error(`AbleTime blocker has no reference: ${blockerId}`);
@@ -45661,6 +45847,8 @@ function githubBlockedByIdentifiers(payload) {
45661
45847
  const rec = node && typeof node === "object" && !Array.isArray(node) ? node : null;
45662
45848
  const number = Number(rec?.number ?? node);
45663
45849
  if (!Number.isFinite(number) || number <= 0) continue;
45850
+ const state = String(rec?.state ?? "").trim().toUpperCase();
45851
+ if (state === "CLOSED") continue;
45664
45852
  const id = `#${number}`;
45665
45853
  const key = id.toLowerCase();
45666
45854
  if (seen.has(key)) continue;
@@ -45673,7 +45861,7 @@ var GITHUB_BLOCKED_BY_QUERY = `
45673
45861
  query($owner: String!, $name: String!, $number: Int!) {
45674
45862
  repository(owner: $owner, name: $name) {
45675
45863
  issue(number: $number) {
45676
- blockedBy(first: 25) { nodes { number } }
45864
+ blockedBy(first: 25) { nodes { number state } }
45677
45865
  }
45678
45866
  }
45679
45867
  }
@@ -45721,15 +45909,21 @@ async function getGitHubIssueBlockedBy(id, opts) {
45721
45909
  }
45722
45910
 
45723
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
+ }
45724
45918
  var ISSUE_BLOCKED_BY_QUERY = `
45725
45919
  query SideboardIssueBlockedBy($id: String!) {
45726
45920
  issue(id: $id) {
45727
45921
  identifier
45728
45922
  relations(first: 25) {
45729
- nodes { id type relatedIssue { id identifier } }
45923
+ nodes { id type relatedIssue { id identifier state { type } } }
45730
45924
  }
45731
45925
  inverseRelations(first: 25) {
45732
- nodes { id type issue { id identifier } }
45926
+ nodes { id type issue { id identifier state { type } } }
45733
45927
  }
45734
45928
  }
45735
45929
  }
@@ -45740,6 +45934,7 @@ function linearBlockedByIdentifiers(relations) {
45740
45934
  for (const rel of relations) {
45741
45935
  const type = (rel.type ?? "").trim().toLowerCase().replace(/[\s_-]+/g, "");
45742
45936
  if (type !== "blockedby" && type !== "blocked") continue;
45937
+ if (!linearBlockerStillBlocks(rel.issue)) continue;
45743
45938
  const id = (rel.issue?.identifier || rel.issue?.id || "").trim();
45744
45939
  const key = id.toLowerCase();
45745
45940
  if (!id || seen.has(key)) continue;
@@ -47931,6 +48126,7 @@ async function pollSlackOutboundWatches(opts) {
47931
48126
  ableTimeOAuthLocalBounceUrl,
47932
48127
  ableTimeOAuthRedirectUri,
47933
48128
  ableTimeOAuthResource,
48129
+ ableTimeStateIsClosed,
47934
48130
  ableTimeUsesRest,
47935
48131
  abletimeMcpRequest,
47936
48132
  abletimeMcpUrl,
@@ -48413,6 +48609,7 @@ async function pollSlackOutboundWatches(opts) {
48413
48609
  issueMatchesAssignee,
48414
48610
  issueSourceLabel,
48415
48611
  issueTicketFromThread,
48612
+ joinSteerPrompt,
48416
48613
  killDetachedRunTree,
48417
48614
  killListenersOnPorts,
48418
48615
  killStaleWorktreeListeners,
@@ -48471,6 +48668,7 @@ async function pollSlackOutboundWatches(opts) {
48471
48668
  listWorktrees,
48472
48669
  listenerCwd,
48473
48670
  liveActivitySummary,
48671
+ livePartsShowAgentReply,
48474
48672
  loadAppSettings,
48475
48673
  loadBrightsyConfig,
48476
48674
  loadConductorSettings,
@@ -48808,6 +49006,7 @@ async function pollSlackOutboundWatches(opts) {
48808
49006
  toolDetail,
48809
49007
  toolFilePath,
48810
49008
  totalTokens,
49009
+ trailingUnansweredUserText,
48811
49010
  trustSystemCertificates,
48812
49011
  tryReservePort,
48813
49012
  tryReservePorts,