@sideboard-ai/core 0.1.190 → 0.1.192

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 (27) hide show
  1. package/dist/{abletime-J3EGHIAV.js → abletime-53T7G667.js} +3 -1
  2. package/dist/{abletime-CAFQ4GEG.js → abletime-JFMBCSIQ.js} +3 -1
  3. package/dist/{agents-5OBZETUV.js → agents-T5VBB6X3.js} +2 -2
  4. package/dist/{agents-DTPVTDB5.js → agents-TXGQIK2E.js} +2 -2
  5. package/dist/{chunk-TEJU6UJY.js → chunk-4M2AK3O5.js} +126 -20
  6. package/dist/{chunk-XXEIF2IL.js → chunk-7NSXGL6L.js} +3 -3
  7. package/dist/{chunk-IAOXW6RI.js → chunk-FZ3G5TXM.js} +23 -16
  8. package/dist/{chunk-Z6TW3A5P.js → chunk-NF6ENGHS.js} +24 -17
  9. package/dist/{chunk-OLDZXO53.js → chunk-TWSCTJCR.js} +2 -2
  10. package/dist/{chunk-KEVY4GEK.js → chunk-UBQIELFS.js} +123 -20
  11. package/dist/{chunk-VIODP5PT.js → chunk-VV7UT3E2.js} +3 -3
  12. package/dist/{chunk-GBWKMWUQ.js → chunk-WHD4JNTN.js} +2 -2
  13. package/dist/{coordinator-prompt-LWKOL3AZ.js → coordinator-prompt-PNUDN4DW.js} +1 -1
  14. package/dist/{coordinator-prompt-MSC44OOM.js → coordinator-prompt-S4HHFSLI.js} +1 -1
  15. package/dist/{global-workspace-6G5JJH2E.js → global-workspace-2BVCJUAL.js} +1 -1
  16. package/dist/{global-workspace-XATJTV3N.js → global-workspace-JTBBH2K5.js} +1 -1
  17. package/dist/index.cjs +565 -57
  18. package/dist/index.d.cts +96 -6
  19. package/dist/index.d.ts +96 -6
  20. package/dist/index.js +422 -30
  21. package/dist/mcp/run-stdio.cjs +543 -57
  22. package/dist/mcp/run-stdio.js +407 -30
  23. package/dist/{orchestrator-BHJELEFM.js → orchestrator-4G76BOHH.js} +5 -3
  24. package/dist/{orchestrator-HWNGCDGW.js → orchestrator-LWEZ74RK.js} +5 -3
  25. package/dist/{workspaces-LMFCRXTT.js → workspaces-OY7D5RPU.js} +1 -1
  26. package/dist/{workspaces-FWKPWSXV.js → workspaces-ZYPME3YD.js} +1 -1
  27. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import {
4
+ ableTimeDependencyTarget,
4
5
  commentAbleTimeTask,
5
6
  createAbleTimeTask,
6
7
  ensureAbleTimeTask,
@@ -16,13 +17,14 @@ import {
16
17
  toAbleTimeIssueInfo,
17
18
  updateAbleTimeTask,
18
19
  verifyAbleTimeConnection
19
- } from "./chunk-KEVY4GEK.js";
20
+ } from "./chunk-UBQIELFS.js";
20
21
  import "./chunk-VKQGAWHY.js";
21
22
  import "./chunk-OKZFTOM2.js";
22
23
  import "./chunk-NSTQ6QKD.js";
23
24
  import "./chunk-KGZBYWZ3.js";
24
25
  import "./chunk-JTJHOYCU.js";
25
26
  export {
27
+ ableTimeDependencyTarget,
26
28
  commentAbleTimeTask,
27
29
  createAbleTimeTask,
28
30
  ensureAbleTimeTask,
@@ -1,4 +1,5 @@
1
1
  import {
2
+ ableTimeDependencyTarget,
2
3
  commentAbleTimeTask,
3
4
  createAbleTimeTask,
4
5
  ensureAbleTimeTask,
@@ -14,7 +15,7 @@ import {
14
15
  toAbleTimeIssueInfo,
15
16
  updateAbleTimeTask,
16
17
  verifyAbleTimeConnection
17
- } from "./chunk-TEJU6UJY.js";
18
+ } from "./chunk-4M2AK3O5.js";
18
19
  import "./chunk-N7WZ6NIN.js";
19
20
  import "./chunk-J7LQVLFQ.js";
20
21
  import "./chunk-4TR3HZFT.js";
@@ -22,6 +23,7 @@ import "./chunk-JT7R45JB.js";
22
23
  import "./chunk-77WWLBCI.js";
23
24
  import "./chunk-EFVFB2AH.js";
24
25
  export {
26
+ ableTimeDependencyTarget,
25
27
  commentAbleTimeTask,
26
28
  createAbleTimeTask,
27
29
  ensureAbleTimeTask,
@@ -38,11 +38,11 @@ import {
38
38
  resolveQuotaFallbackAgent,
39
39
  turnCostUsdFromCursorUsage,
40
40
  whichOnPath
41
- } from "./chunk-GBWKMWUQ.js";
41
+ } from "./chunk-WHD4JNTN.js";
42
42
  import "./chunk-HVIEYTUK.js";
43
43
  import "./chunk-Y4MIXGPA.js";
44
44
  import "./chunk-VKQGAWHY.js";
45
- import "./chunk-XXEIF2IL.js";
45
+ import "./chunk-7NSXGL6L.js";
46
46
  import "./chunk-XBLN52RZ.js";
47
47
  import "./chunk-D6JWYJ4P.js";
48
48
  import "./chunk-GOZ6T3AG.js";
@@ -32,8 +32,8 @@ import {
32
32
  resolveLoginCommand,
33
33
  resolveQuotaFallbackAgent,
34
34
  whichOnPath
35
- } from "./chunk-OLDZXO53.js";
36
- import "./chunk-VIODP5PT.js";
35
+ } from "./chunk-TWSCTJCR.js";
36
+ import "./chunk-VV7UT3E2.js";
37
37
  import "./chunk-WXKXSZN4.js";
38
38
  import "./chunk-6K64FJDD.js";
39
39
  import "./chunk-IEEAVWAP.js";
@@ -11,14 +11,31 @@ import {
11
11
  // src/integrations/abletime-mcp.ts
12
12
  var DEFAULT_ABLETIME_HOST = "https://track.abletime.com";
13
13
  var ABLETIME_MCP_PATH = "/api/public/v2/mcp";
14
+ var ABLETIME_MCP_PM_PATH = "/api/public/v2/mcp/pm";
14
15
  var MCP_PROTOCOL_VERSION = "2025-03-26";
16
+ var ABLETIME_PM_TOOL_NAMES = [
17
+ "set_task_assignee",
18
+ "set_task_priority",
19
+ "set_task_stage",
20
+ "set_task_dependency",
21
+ "set_task_blocked",
22
+ "set_task_locked",
23
+ "set_task_schedule",
24
+ "clear_task_schedule",
25
+ "archive_task",
26
+ "delete_task"
27
+ ];
28
+ function abletimeToolUsesPm(name) {
29
+ return ABLETIME_PM_TOOL_NAMES.includes(name);
30
+ }
15
31
  function normalizeAbleTimeHost(raw) {
16
32
  const trimmed = raw?.trim() || DEFAULT_ABLETIME_HOST;
17
33
  const withScheme = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
18
34
  return withScheme.replace(/\/+$/, "");
19
35
  }
20
- function abletimeMcpUrl(host) {
21
- return `${normalizeAbleTimeHost(host)}${ABLETIME_MCP_PATH}`;
36
+ function abletimeMcpUrl(host, opts) {
37
+ const path = opts?.pm ? ABLETIME_MCP_PM_PATH : ABLETIME_MCP_PATH;
38
+ return `${normalizeAbleTimeHost(host)}${path}`;
22
39
  }
23
40
  function rewriteAbleTimeError(message) {
24
41
  if (/INTEGRATION_AGENT_ACCESS_DISABLED|agent access/i.test(message)) {
@@ -96,7 +113,8 @@ async function abletimeMcpRequest(method, params, opts) {
96
113
  throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
97
114
  }
98
115
  const url = abletimeMcpUrl(
99
- opts?.host ?? (opts?.token ? DEFAULT_ABLETIME_HOST : getAbleTimeHost())
116
+ opts?.host ?? (opts?.token ? DEFAULT_ABLETIME_HOST : getAbleTimeHost()),
117
+ { pm: opts?.pm }
100
118
  );
101
119
  const res = await httpFetch(url, {
102
120
  method: "POST",
@@ -160,7 +178,8 @@ async function callAbleTimeTool(name, args = {}, opts) {
160
178
  if (!opts?.token && !isAbleTimeConnected()) {
161
179
  throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
162
180
  }
163
- const call = () => abletimeMcpRequest("tools/call", { name, arguments: args }, opts).then((result) => {
181
+ const requestOpts = { ...opts, pm: opts?.pm ?? abletimeToolUsesPm(name) };
182
+ const call = () => abletimeMcpRequest("tools/call", { name, arguments: args }, requestOpts).then((result) => {
164
183
  return unwrapToolResult(result);
165
184
  });
166
185
  try {
@@ -168,7 +187,7 @@ async function callAbleTimeTool(name, args = {}, opts) {
168
187
  } catch (err) {
169
188
  const message = err instanceof Error ? err.message : String(err);
170
189
  if (/initialize|session|not initialized|-32000/i.test(message)) {
171
- await initializeIfNeeded(opts);
190
+ await initializeIfNeeded(requestOpts);
172
191
  return await call();
173
192
  }
174
193
  throw err instanceof Error ? new Error(rewriteAbleTimeError(err.message)) : err;
@@ -424,39 +443,122 @@ async function commentAbleTimeTask(input, opts) {
424
443
  body: rec ? firstString(rec, ["body", "text", "comment"]) || body : body
425
444
  };
426
445
  }
446
+ function isAbleTimeNoneToken(value) {
447
+ if (value == null) return true;
448
+ const s = value.trim();
449
+ return !s || /^(none|clear|unschedule|null)$/i.test(s);
450
+ }
451
+ function taskIdArgs(id) {
452
+ return { id, task_id: id, task: id };
453
+ }
454
+ function ableTimeDependencyTarget(thisId, type, relatedId) {
455
+ const s = type.trim().toLowerCase().replace(/[\s_-]+/g, "");
456
+ if (s === "none" || s === "clear" || s === "null") {
457
+ return { taskId: thisId, dependsOn: null };
458
+ }
459
+ if (s === "blockedby" || s === "blocked" || s === "dependson" || s === "waiton") {
460
+ return { taskId: thisId, dependsOn: relatedId };
461
+ }
462
+ if (s === "blocks" || s === "blocking") {
463
+ return { taskId: relatedId, dependsOn: thisId };
464
+ }
465
+ throw new Error(
466
+ `AbleTime relation type must be blocks or blockedBy (got ${type}). AbleTime models one wait-on dependency.`
467
+ );
468
+ }
469
+ async function setAbleTimeTaskDependency(taskId, dependsOn, opts) {
470
+ const args = {
471
+ ...taskIdArgs(taskId)
472
+ };
473
+ if (dependsOn) {
474
+ args.depends_on = dependsOn;
475
+ args.depends_on_id = dependsOn;
476
+ args.dependency = dependsOn;
477
+ args.dependency_id = dependsOn;
478
+ args.blocked_by = dependsOn;
479
+ args.wait_on = dependsOn;
480
+ args.related_task_id = dependsOn;
481
+ } else {
482
+ args.depends_on = null;
483
+ args.dependency = null;
484
+ args.blocked_by = null;
485
+ args.clear = true;
486
+ }
487
+ await callAbleTimeTool("set_task_dependency", args, opts);
488
+ }
427
489
  async function updateAbleTimeTask(input, opts) {
428
490
  const id = input.id.trim();
429
491
  if (!id) throw new Error("AbleTime task id is required");
430
492
  const title = input.title?.trim();
431
493
  const description = input.description;
432
494
  const state = input.state?.trim();
433
- if (!title && description === void 0 && !state) {
434
- throw new Error("abletime_update_task needs at least one of title, description, state");
495
+ const addRelations = input.relations?.length ? input.relations : void 0;
496
+ const removeRelations = input.removeRelations?.length ? input.removeRelations : void 0;
497
+ const updateArgs = { ...taskIdArgs(id) };
498
+ if (title) updateArgs.title = title;
499
+ if (description !== void 0) updateArgs.description = description;
500
+ if (input.labels !== void 0) {
501
+ updateArgs.tags = input.labels;
502
+ updateArgs.labels = input.labels;
503
+ updateArgs.tag_names = input.labels;
504
+ }
505
+ if (input.project !== void 0) {
506
+ const projectName = (input.project ?? "").trim();
507
+ if (isAbleTimeNoneToken(projectName)) {
508
+ throw new Error("AbleTime tasks stay in a project \u2014 pass a project name or id");
509
+ }
510
+ const project = await resolveAbleTimeProject(projectName, opts);
511
+ updateArgs.project = project.id;
512
+ updateArgs.project_id = project.id;
513
+ }
514
+ if (input.parent !== void 0) {
515
+ if (isAbleTimeNoneToken(input.parent)) {
516
+ updateArgs.parent = null;
517
+ updateArgs.parent_id = null;
518
+ updateArgs.related_task_id = null;
519
+ } else {
520
+ const parent = (input.parent ?? "").trim();
521
+ updateArgs.parent = parent;
522
+ updateArgs.parent_id = parent;
523
+ updateArgs.related_task_id = parent;
524
+ }
525
+ }
526
+ const hasUpdateFields = Boolean(title) || description !== void 0 || input.labels !== void 0 || input.project !== void 0 || input.parent !== void 0;
527
+ if (!hasUpdateFields && !state && !addRelations && !removeRelations) {
528
+ throw new Error(
529
+ "abletime_update_task needs at least one of title, description, state, labels, project, parent, relations, removeRelations"
530
+ );
435
531
  }
436
532
  if (state) {
437
533
  await callAbleTimeTool(
438
534
  "set_task_state",
439
- { id, task_id: id, task: id, state },
535
+ { ...taskIdArgs(id), state },
440
536
  opts
441
537
  ).catch(async () => {
442
538
  await callAbleTimeTool(
443
539
  "update_task",
444
- { id, task_id: id, title, description, state },
540
+ { ...updateArgs, state },
445
541
  opts
446
542
  );
447
543
  });
448
544
  }
449
- if (title || description !== void 0) {
450
- await callAbleTimeTool(
451
- "update_task",
452
- {
453
- id,
454
- task_id: id,
455
- ...title ? { title } : {},
456
- ...description !== void 0 ? { description } : {}
457
- },
458
- opts
459
- );
545
+ if (hasUpdateFields) {
546
+ await callAbleTimeTool("update_task", updateArgs, opts);
547
+ }
548
+ for (const rel of removeRelations ?? []) {
549
+ const related = rel.issue.trim() || id;
550
+ const target = ableTimeDependencyTarget(id, rel.type, related);
551
+ await setAbleTimeTaskDependency(target.taskId, null, opts);
552
+ }
553
+ for (const rel of addRelations ?? []) {
554
+ const related = rel.issue.trim();
555
+ if (!related) throw new Error("AbleTime relations.issue is required (id or CRM-232)");
556
+ const target = ableTimeDependencyTarget(id, rel.type, related);
557
+ if (!target.dependsOn) {
558
+ await setAbleTimeTaskDependency(target.taskId, null, opts);
559
+ continue;
560
+ }
561
+ await setAbleTimeTaskDependency(target.taskId, target.dependsOn, opts);
460
562
  }
461
563
  return getAbleTimeTask(id, opts);
462
564
  }
@@ -540,6 +642,9 @@ async function verifyAbleTimeConnection(input) {
540
642
  export {
541
643
  DEFAULT_ABLETIME_HOST,
542
644
  ABLETIME_MCP_PATH,
645
+ ABLETIME_MCP_PM_PATH,
646
+ ABLETIME_PM_TOOL_NAMES,
647
+ abletimeToolUsesPm,
543
648
  normalizeAbleTimeHost,
544
649
  abletimeMcpUrl,
545
650
  rewriteAbleTimeError,
@@ -556,6 +661,7 @@ export {
556
661
  getAbleTimeTask,
557
662
  createAbleTimeTask,
558
663
  commentAbleTimeTask,
664
+ ableTimeDependencyTarget,
559
665
  updateAbleTimeTask,
560
666
  ensureAbleTimeTask,
561
667
  listAbleTimeAssignedIssues,
@@ -350,8 +350,8 @@ var COORDINATOR_TOOL_PLAYBOOK = [
350
350
  "- start_board_card \u2014 same as create_thread for a ticket/PR/named branch (attaches issue text when resolvable). Then send_to_thread.",
351
351
  "- fork_worktree \u2014 fork a worktree chat into a NEW git worktree + chat (transcript attached); optional agent; leave model unset (Auto) unless you have a reason. Not for orchestration chats.",
352
352
  "- fork_chat \u2014 fork a worktree chat (same worktree tab) OR a Global orchestration chat (new orchestration tab); optional agent; leave model unset (Auto) unless you have a reason. Remote coordinators: use this to continue another orchestration chat on a different agent after session limits.",
353
- "- send_to_thread \u2014 queue a prompt (start/continue a chat turn). force_stop: true only to replace a wrong in-flight request \u2014 never to check in, resume after a halt notice, or because wait_for_turn returned stillRunning (that kills the child mid-thought)",
354
- "- wait_for_turn / get_turn_result \u2014 wait for and read the agent reply (includes last-turn usage / costUsd when the child agent reported it). wait_for_turn returns within ~45s even if the child is still working (MCP clients kill longer tool calls). stillRunning is the source of truth \u2014 if it is false, the child is not working; do not tell the user it is running or \u201Cwaiting for a gate,\u201D even if status still says running/queued. If stillRunning is true, progress is a live snapshot of tools/thinking \u2014 call wait_for_turn again. status=queued with no lastActivityAt means the child has not started yet (concurrency cap) \u2014 keep waiting; do not force_stop or send a check-in. Do not send \u201Care you stuck?\u201D or assume a hang while lastActivityAt is recent. If stillRunning stays true across many waits with the same lastActivityAt, the child is stuck \u2014 tell the user; do not invent a gate. On status error, lastError (and text) is the failure \u2014 switch agent, tell the user, or retry; do not treat empty text as success. On status stopped or broken (or incomplete=true), the child was interrupted or died \u2014 resume with send_to_thread or tell the user; never treat stopped as a finished turn. Sideboard also injects a notice into this chat when a child stops unexpectedly.",
353
+ "- send_to_thread \u2014 steer a prompt (start/continue a chat turn; Settings \u2192 Follow-up, default steer: interrupt and start now). force_stop: true only to replace a wrong in-flight request and clear the inbox \u2014 never to check in, resume after a halt notice, or because wait_for_turn returned stillRunning (that interrupts the child mid-thought)",
354
+ "- wait_for_turn / get_turn_result \u2014 wait for and read the agent reply (includes last-turn usage / costUsd when the child agent reported it). wait_for_turn returns within ~45s even if the child is still working (MCP clients kill longer tool calls). stillRunning is the source of truth \u2014 if it is false, the child is not working; do not tell the user it is running or \u201Cwaiting for a gate,\u201D even if status still says running/queued. If stillRunning is true, progress is a live snapshot of tools/thinking \u2014 call wait_for_turn again. status=queued with no lastActivityAt means the child has not started yet (concurrency cap) \u2014 keep waiting; do not force_stop or send_to_thread a check-in (that steers / interrupts). Do not send \u201Care you stuck?\u201D or assume a hang while lastActivityAt is recent. If stillRunning stays true across many waits with the same lastActivityAt, the child is stuck \u2014 tell the user; do not invent a gate. On status error, lastError (and text) is the failure \u2014 switch agent, tell the user, or retry; do not treat empty text as success. On status stopped or broken (or incomplete=true), the child was interrupted or died \u2014 resume with send_to_thread or tell the user; never treat stopped as a finished turn. Sideboard also injects a notice into this chat when a child stops unexpectedly.",
355
355
  "- stop_thread \u2014 force-stop: kill in-flight turn AND clear queued prompts (do not leave stale queue after an interrupt)",
356
356
  "- archive_thread / restore_thread \u2014 archive (tears down worktree when last tab) or restore",
357
357
  "Setup / run:",
@@ -361,7 +361,7 @@ var COORDINATOR_TOOL_PLAYBOOK = [
361
361
  "- get_diff \u2014 compact diff summary",
362
362
  "- get_pr_checks \u2014 snapshot of a worktree thread's PR checks (null = no PR). Use this to inspect status. If the user gave a goal, the worktree agent watches with `gh pr checks --watch` \u2014 do not poll for the human.",
363
363
  '- request_review \u2014 open a Review chat tab on a worktree thread (attaches .claude/skills/review/SKILL.md when present, else .context/review.md copied from .sideboard/review.md / stock; sends "Review changes in this workspace."); then wait_for_turn (loop while stillRunning) / get_turn_result on the returned id. The review stays in that chat so they can read it and type next steps \u2014 do not tell the child to comment or update the PR or ticket.',
364
- "- ask_git \u2014 commit & push, open a draft PR, mark ready for review, resolve conflicts, or merge. When the worktree is clean, Sideboard pushes / opens the PR itself. When dirty, it queues the worktree agent \u2014 then wait_for_turn (loop while stillRunning). Prefer this over paraphrasing. If ask_git / get_thread lastError says the GraphQL/PR body is too long, the branch is already pushed \u2014 send_to_thread so the worktree agent runs `gh pr create --body-file` with a short description. Do not invent SSH or auth failures from that error. If the user gave a goal (Greptile 5/5, CI green), send_to_thread that goal so the worktree agent enters the watch-fix-push loop \u2014 do not tell the human to poll.",
364
+ "- ask_git \u2014 commit & push, open a draft PR, mark ready for review, resolve conflicts, or merge. When the worktree is clean, Sideboard pushes / opens the PR itself. When dirty, it steers the worktree agent \u2014 then wait_for_turn (loop while stillRunning). Prefer this over paraphrasing. If ask_git / get_thread lastError says the GraphQL/PR body is too long, the branch is already pushed \u2014 send_to_thread so the worktree agent runs `gh pr create --body-file` with a short description. Do not invent SSH or auth failures from that error. If the user gave a goal (Greptile 5/5, CI green), send_to_thread that goal so the worktree agent enters the watch-fix-push loop \u2014 do not tell the human to poll.",
365
365
  '- Merge (`ask_git` action=merge / send_to_thread "Merge PR.") only when the user explicitly asked to merge that PR. Do not merge because the work looks done, CI is green, or a typical flow includes it.',
366
366
  '- Or send_to_thread with those exact phrases: "Commit and push.", "Commit, push, and open a draft PR.", "Ready for review.", "Merge the remote branch (main) into your branch and resolve conflicts. Then, commit and push your changes.", "Merge PR." (draft PRs: `gh pr create --draft -R <origin-owner/name>` using the workspace `github:` slug \u2014 never upstream). Never run git/gh from this orchestration cwd, and never merge the PR yourself.',
367
367
  "Process guides:",
@@ -33,7 +33,7 @@ import {
33
33
  summarizeTurnStderr,
34
34
  toolDescription,
35
35
  turnFailChatText
36
- } from "./chunk-GBWKMWUQ.js";
36
+ } from "./chunk-WHD4JNTN.js";
37
37
  import {
38
38
  extractPresentedPlan,
39
39
  readPlanFile,
@@ -64,7 +64,7 @@ import {
64
64
  stageAbsolutePathsAsAttachments,
65
65
  stageBuffersAsAttachments,
66
66
  syncWorkspacesFromThreads
67
- } from "./chunk-XXEIF2IL.js";
67
+ } from "./chunk-7NSXGL6L.js";
68
68
  import {
69
69
  addPrStackLayer,
70
70
  allocateTeamName,
@@ -402,7 +402,7 @@ async function continueSourceThread(threadId, prompt) {
402
402
  await continueOnReply(threadId, prompt);
403
403
  return;
404
404
  }
405
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-BHJELEFM.js");
405
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-4G76BOHH.js");
406
406
  await getOrchestrator2().send(threadId, prompt);
407
407
  } catch {
408
408
  }
@@ -795,9 +795,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
795
795
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
796
796
  );
797
797
  }
798
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-6G5JJH2E.js");
798
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-2BVCJUAL.js");
799
799
  if (isGlobalThread2(thread)) {
800
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-LWKOL3AZ.js");
800
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-PNUDN4DW.js");
801
801
  ensureGlobalCoordinatorCwd2(
802
802
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
803
803
  );
@@ -2658,8 +2658,8 @@ function mcpWaitForTurnTimeoutMs(requested) {
2658
2658
  if (!Number.isFinite(n)) return MCP_WAIT_FOR_TURN_MAX_MS;
2659
2659
  return Math.min(Math.max(1e3, Math.floor(n)), MCP_WAIT_FOR_TURN_MAX_MS);
2660
2660
  }
2661
- var MCP_WAIT_STILL_RUNNING_HINT = "Child is still working. Call wait_for_turn again. Do not send a check-in prompt or assume a hang while progress is updating.";
2662
- var MCP_WAIT_QUEUED_HINT = "Child is queued waiting for a concurrency slot \u2014 it has not started yet. Call wait_for_turn again. Do not send a check-in prompt, force_stop, or assume it failed to start.";
2661
+ var MCP_WAIT_STILL_RUNNING_HINT = "Child is still working. Call wait_for_turn again. Do not send_to_thread a check-in (that steers / interrupts) or assume a hang while progress is updating.";
2662
+ var MCP_WAIT_QUEUED_HINT = "Child is queued waiting for a concurrency slot \u2014 it has not started yet. Call wait_for_turn again. Do not send_to_thread a check-in (that steers / interrupts), force_stop, or assume it failed to start.";
2663
2663
  function mcpWaitStillRunningHint(status) {
2664
2664
  return status === "queued" ? MCP_WAIT_QUEUED_HINT : MCP_WAIT_STILL_RUNNING_HINT;
2665
2665
  }
@@ -3643,7 +3643,7 @@ async function createThread(input, _onSetupLine) {
3643
3643
  let sourceIsFork = false;
3644
3644
  if (!input.cowboy && sourceType === "branch" && (!sourceRef || sourceRef === "HEAD" || sourceRef === "default")) {
3645
3645
  if (isAbleTimeConnected() && getIssueSource() === "abletime") {
3646
- const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-J3EGHIAV.js");
3646
+ const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-53T7G667.js");
3647
3647
  const task = await ensureAbleTimeTask({
3648
3648
  title: title?.trim() || "Untitled work",
3649
3649
  description: input.prompt?.trim() || void 0
@@ -4155,7 +4155,7 @@ async function adoptThread(input) {
4155
4155
  messages: input.messages ?? []
4156
4156
  });
4157
4157
  writeThread(thread);
4158
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-LMFCRXTT.js");
4158
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-OY7D5RPU.js");
4159
4159
  await ensureWorkspace2(repoPath);
4160
4160
  const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-N5X5I422.js");
4161
4161
  await ensureWorktreeSideboardIgnored2(input.worktreePath);
@@ -5972,17 +5972,17 @@ function formatIssueToolsDirective(opts) {
5972
5972
  ];
5973
5973
  if (opts.linear) {
5974
5974
  lines.push(
5975
- "- Linear: `linear_search_issues` (assignee=me; pass `updatedSince` for \u201Cany updates since yesterday?\u201D \u2014 new tickets, edits, and comments), `linear_get_issue` (comments; pass `include=full` if crush.truncated), `linear_comment`, `linear_update_issue` (state, cycle), `linear_create_issue` (pass `parent` for spin-offs; call `linear_list_teams` first). Scope errors: reconnect Linear in Settings \u2192 Issues."
5975
+ "- Linear: `linear_search_issues` (assignee=me; pass `updatedSince` for \u201Cany updates since yesterday?\u201D \u2014 new tickets, edits, and comments), `linear_get_issue` (comments; pass `include=full` if crush.truncated), `linear_comment`, `linear_update_issue` (state, cycle, labels, project, parent, relations), `linear_create_issue` (pass `parent` for spin-offs; call `linear_list_teams` first). Scope errors: reconnect Linear in Settings \u2192 Issues."
5976
5976
  );
5977
5977
  }
5978
5978
  if (github) {
5979
5979
  lines.push(
5980
- "- GitHub: `github_search_issues` (assignee=me; `updatedSince` for inbox updates + comments), `github_get_issue` (comments; pass `include=full` if crush.truncated), `github_comment`, `github_update_issue` (state open|closed), `github_create_issue` (pass `parent` for spin-offs). Uses Account `gh`."
5980
+ "- GitHub: `github_search_issues` (assignee=me; `updatedSince` for inbox updates + comments), `github_get_issue` (comments; pass `include=full` if crush.truncated), `github_comment`, `github_update_issue` (state open|closed, labels, project, parent, relations), `github_create_issue` (pass `parent` for spin-offs). Uses Account `gh`."
5981
5981
  );
5982
5982
  }
5983
5983
  if (opts.abletime) {
5984
5984
  lines.push(
5985
- "- AbleTime: `abletime_list_tasks` / `abletime_search_tasks` (`updatedSince` for inbox updates + comments), `abletime_get_task` (comments; pass `include=full` if crush.truncated), `abletime_comment`, `abletime_update_task` (state), `abletime_create_task` (pass `parent` for spin-offs; `abletime_list_projects` if needed)."
5985
+ "- AbleTime: `abletime_list_tasks` / `abletime_search_tasks` (`updatedSince` for inbox updates + comments), `abletime_get_task` (comments; pass `include=full` if crush.truncated), `abletime_comment`, `abletime_update_task` (state, labels, project, parent, relations), `abletime_create_task` (pass `parent` for spin-offs; `abletime_list_projects` if needed)."
5986
5986
  );
5987
5987
  }
5988
5988
  lines.push(
@@ -6458,7 +6458,7 @@ function formatScheduledPrompt(name, prompt) {
6458
6458
  ${prompt}`;
6459
6459
  }
6460
6460
  async function defaultDeps() {
6461
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-BHJELEFM.js");
6461
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-4G76BOHH.js");
6462
6462
  const orch = getOrchestrator2();
6463
6463
  return {
6464
6464
  findThread: (id) => findThreadByRef(id),
@@ -6589,6 +6589,10 @@ function resolveSendFollowUp(thread, requested) {
6589
6589
  if (requested === "steer" || requested === "queue") return requested;
6590
6590
  return isOrchestratorThread(thread) ? followUpBehavior() : "queue";
6591
6591
  }
6592
+ function resolveOrchChildFollowUp(requested) {
6593
+ if (requested === "steer" || requested === "queue") return requested;
6594
+ return followUpBehavior();
6595
+ }
6592
6596
  var STALE_AGENT_PID_WAIT_MS = 2500;
6593
6597
  function writeLiveStatus(threadId, status, lastError) {
6594
6598
  const latest = readThread(threadId);
@@ -8401,7 +8405,7 @@ var Orchestrator = class {
8401
8405
  /**
8402
8406
  * Desktop git buttons + MCP `ask_git`.
8403
8407
  * When the worktree is clean, push / open the PR here (HTTPS via `gh` if SSH
8404
- * is missing). When dirty, queue the worktree agent to commit first.
8408
+ * is missing). When dirty, steer the worktree agent to commit first.
8405
8409
  */
8406
8410
  async askGit(threadRef, action) {
8407
8411
  if (!AGENT_GIT_ACTIONS.includes(action)) {
@@ -8438,7 +8442,9 @@ var Orchestrator = class {
8438
8442
  } catch {
8439
8443
  }
8440
8444
  }
8441
- return this.send(threadRef, agentGitPrompt(action, { prBase }));
8445
+ return this.send(threadRef, agentGitPrompt(action, { prBase }), {
8446
+ followUp: resolveOrchChildFollowUp()
8447
+ });
8442
8448
  }
8443
8449
  /** Push origin (gh HTTPS fallback) and create/update the PR when requested. */
8444
8450
  async pushAndMaybeOpenPr(thread, action) {
@@ -8582,7 +8588,7 @@ var Orchestrator = class {
8582
8588
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
8583
8589
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
8584
8590
  try {
8585
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-LMFCRXTT.js");
8591
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-OY7D5RPU.js");
8586
8592
  await ensureWorkspace2(thread.repoPath);
8587
8593
  } catch {
8588
8594
  }
@@ -8807,6 +8813,7 @@ export {
8807
8813
  isPidAlive,
8808
8814
  waitForPidExit,
8809
8815
  resolveSendFollowUp,
8816
+ resolveOrchChildFollowUp,
8810
8817
  Orchestrator,
8811
8818
  getOrchestrator,
8812
8819
  startOrchestration
@@ -24,7 +24,7 @@ import {
24
24
  stripBrightsyNdjsonNoise,
25
25
  sumUsageList,
26
26
  toolDescription
27
- } from "./chunk-OLDZXO53.js";
27
+ } from "./chunk-TWSCTJCR.js";
28
28
  import {
29
29
  SLACK_REPLY_FORMATTING,
30
30
  addWorkspace,
@@ -47,7 +47,7 @@ import {
47
47
  stageAbsolutePathsAsAttachments,
48
48
  stageBuffersAsAttachments,
49
49
  syncWorkspacesFromThreads
50
- } from "./chunk-VIODP5PT.js";
50
+ } from "./chunk-VV7UT3E2.js";
51
51
  import {
52
52
  addPrStackLayer,
53
53
  allocateTeamName,
@@ -467,7 +467,7 @@ async function continueSourceThread(threadId, prompt) {
467
467
  await continueOnReply(threadId, prompt);
468
468
  return;
469
469
  }
470
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-HWNGCDGW.js");
470
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-LWEZ74RK.js");
471
471
  await getOrchestrator2().send(threadId, prompt);
472
472
  } catch {
473
473
  }
@@ -801,9 +801,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
801
801
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
802
802
  );
803
803
  }
804
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-XATJTV3N.js");
804
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-JTBBH2K5.js");
805
805
  if (isGlobalThread2(thread)) {
806
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-MSC44OOM.js");
806
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-S4HHFSLI.js");
807
807
  ensureGlobalCoordinatorCwd2(
808
808
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
809
809
  );
@@ -2745,8 +2745,8 @@ function mcpWaitForTurnTimeoutMs(requested) {
2745
2745
  if (!Number.isFinite(n)) return MCP_WAIT_FOR_TURN_MAX_MS;
2746
2746
  return Math.min(Math.max(1e3, Math.floor(n)), MCP_WAIT_FOR_TURN_MAX_MS);
2747
2747
  }
2748
- var MCP_WAIT_STILL_RUNNING_HINT = "Child is still working. Call wait_for_turn again. Do not send a check-in prompt or assume a hang while progress is updating.";
2749
- var MCP_WAIT_QUEUED_HINT = "Child is queued waiting for a concurrency slot \u2014 it has not started yet. Call wait_for_turn again. Do not send a check-in prompt, force_stop, or assume it failed to start.";
2748
+ var MCP_WAIT_STILL_RUNNING_HINT = "Child is still working. Call wait_for_turn again. Do not send_to_thread a check-in (that steers / interrupts) or assume a hang while progress is updating.";
2749
+ var MCP_WAIT_QUEUED_HINT = "Child is queued waiting for a concurrency slot \u2014 it has not started yet. Call wait_for_turn again. Do not send_to_thread a check-in (that steers / interrupts), force_stop, or assume it failed to start.";
2750
2750
  function mcpWaitStillRunningHint(status) {
2751
2751
  return status === "queued" ? MCP_WAIT_QUEUED_HINT : MCP_WAIT_STILL_RUNNING_HINT;
2752
2752
  }
@@ -3772,7 +3772,7 @@ async function createThread(input, _onSetupLine) {
3772
3772
  let sourceIsFork = false;
3773
3773
  if (!input.cowboy && sourceType === "branch" && (!sourceRef || sourceRef === "HEAD" || sourceRef === "default")) {
3774
3774
  if (isAbleTimeConnected() && getIssueSource() === "abletime") {
3775
- const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-CAFQ4GEG.js");
3775
+ const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-JFMBCSIQ.js");
3776
3776
  const task = await ensureAbleTimeTask({
3777
3777
  title: title?.trim() || "Untitled work",
3778
3778
  description: input.prompt?.trim() || void 0
@@ -3874,7 +3874,7 @@ async function createThread(input, _onSetupLine) {
3874
3874
  return readThread(thread.id) ?? thread;
3875
3875
  }
3876
3876
  async function listLinearIssues(agent, repoPath) {
3877
- const { getAdapter: getAdapter2 } = await import("./agents-DTPVTDB5.js");
3877
+ const { getAdapter: getAdapter2 } = await import("./agents-TXGQIK2E.js");
3878
3878
  await requireAgent(agent, { requireLinear: true });
3879
3879
  const adapter = getAdapter2(agent);
3880
3880
  if (!adapter.listLinearIssues) {
@@ -4296,7 +4296,7 @@ async function adoptThread(input) {
4296
4296
  messages: input.messages ?? []
4297
4297
  });
4298
4298
  writeThread(thread);
4299
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-FWKPWSXV.js");
4299
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-ZYPME3YD.js");
4300
4300
  await ensureWorkspace2(repoPath);
4301
4301
  const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-X54WSOXM.js");
4302
4302
  await ensureWorktreeSideboardIgnored2(input.worktreePath);
@@ -6130,17 +6130,17 @@ function formatIssueToolsDirective(opts) {
6130
6130
  ];
6131
6131
  if (opts.linear) {
6132
6132
  lines.push(
6133
- "- Linear: `linear_search_issues` (assignee=me; pass `updatedSince` for \u201Cany updates since yesterday?\u201D \u2014 new tickets, edits, and comments), `linear_get_issue` (comments; pass `include=full` if crush.truncated), `linear_comment`, `linear_update_issue` (state, cycle), `linear_create_issue` (pass `parent` for spin-offs; call `linear_list_teams` first). Scope errors: reconnect Linear in Settings \u2192 Issues."
6133
+ "- Linear: `linear_search_issues` (assignee=me; pass `updatedSince` for \u201Cany updates since yesterday?\u201D \u2014 new tickets, edits, and comments), `linear_get_issue` (comments; pass `include=full` if crush.truncated), `linear_comment`, `linear_update_issue` (state, cycle, labels, project, parent, relations), `linear_create_issue` (pass `parent` for spin-offs; call `linear_list_teams` first). Scope errors: reconnect Linear in Settings \u2192 Issues."
6134
6134
  );
6135
6135
  }
6136
6136
  if (github) {
6137
6137
  lines.push(
6138
- "- GitHub: `github_search_issues` (assignee=me; `updatedSince` for inbox updates + comments), `github_get_issue` (comments; pass `include=full` if crush.truncated), `github_comment`, `github_update_issue` (state open|closed), `github_create_issue` (pass `parent` for spin-offs). Uses Account `gh`."
6138
+ "- GitHub: `github_search_issues` (assignee=me; `updatedSince` for inbox updates + comments), `github_get_issue` (comments; pass `include=full` if crush.truncated), `github_comment`, `github_update_issue` (state open|closed, labels, project, parent, relations), `github_create_issue` (pass `parent` for spin-offs). Uses Account `gh`."
6139
6139
  );
6140
6140
  }
6141
6141
  if (opts.abletime) {
6142
6142
  lines.push(
6143
- "- AbleTime: `abletime_list_tasks` / `abletime_search_tasks` (`updatedSince` for inbox updates + comments), `abletime_get_task` (comments; pass `include=full` if crush.truncated), `abletime_comment`, `abletime_update_task` (state), `abletime_create_task` (pass `parent` for spin-offs; `abletime_list_projects` if needed)."
6143
+ "- AbleTime: `abletime_list_tasks` / `abletime_search_tasks` (`updatedSince` for inbox updates + comments), `abletime_get_task` (comments; pass `include=full` if crush.truncated), `abletime_comment`, `abletime_update_task` (state, labels, project, parent, relations), `abletime_create_task` (pass `parent` for spin-offs; `abletime_list_projects` if needed)."
6144
6144
  );
6145
6145
  }
6146
6146
  lines.push(
@@ -6805,7 +6805,7 @@ function formatScheduledPrompt(name, prompt) {
6805
6805
  ${prompt}`;
6806
6806
  }
6807
6807
  async function defaultDeps() {
6808
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-HWNGCDGW.js");
6808
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-LWEZ74RK.js");
6809
6809
  const orch = getOrchestrator2();
6810
6810
  return {
6811
6811
  findThread: (id) => findThreadByRef(id),
@@ -6936,6 +6936,10 @@ function resolveSendFollowUp(thread, requested) {
6936
6936
  if (requested === "steer" || requested === "queue") return requested;
6937
6937
  return isOrchestratorThread(thread) ? followUpBehavior() : "queue";
6938
6938
  }
6939
+ function resolveOrchChildFollowUp(requested) {
6940
+ if (requested === "steer" || requested === "queue") return requested;
6941
+ return followUpBehavior();
6942
+ }
6939
6943
  var STALE_AGENT_PID_WAIT_MS = 2500;
6940
6944
  function writeLiveStatus(threadId, status, lastError) {
6941
6945
  const latest = readThread(threadId);
@@ -8748,7 +8752,7 @@ var Orchestrator = class {
8748
8752
  /**
8749
8753
  * Desktop git buttons + MCP `ask_git`.
8750
8754
  * When the worktree is clean, push / open the PR here (HTTPS via `gh` if SSH
8751
- * is missing). When dirty, queue the worktree agent to commit first.
8755
+ * is missing). When dirty, steer the worktree agent to commit first.
8752
8756
  */
8753
8757
  async askGit(threadRef, action) {
8754
8758
  if (!AGENT_GIT_ACTIONS.includes(action)) {
@@ -8785,7 +8789,9 @@ var Orchestrator = class {
8785
8789
  } catch {
8786
8790
  }
8787
8791
  }
8788
- return this.send(threadRef, agentGitPrompt(action, { prBase }));
8792
+ return this.send(threadRef, agentGitPrompt(action, { prBase }), {
8793
+ followUp: resolveOrchChildFollowUp()
8794
+ });
8789
8795
  }
8790
8796
  /** Push origin (gh HTTPS fallback) and create/update the PR when requested. */
8791
8797
  async pushAndMaybeOpenPr(thread, action) {
@@ -8929,7 +8935,7 @@ var Orchestrator = class {
8929
8935
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
8930
8936
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
8931
8937
  try {
8932
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-FWKPWSXV.js");
8938
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-ZYPME3YD.js");
8933
8939
  await ensureWorkspace2(thread.repoPath);
8934
8940
  } catch {
8935
8941
  }
@@ -9348,6 +9354,7 @@ export {
9348
9354
  isPidAlive,
9349
9355
  waitForPidExit,
9350
9356
  resolveSendFollowUp,
9357
+ resolveOrchChildFollowUp,
9351
9358
  Orchestrator,
9352
9359
  getOrchestrator,
9353
9360
  startOrchestration,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isOrchestratorThread
3
- } from "./chunk-VIODP5PT.js";
3
+ } from "./chunk-VV7UT3E2.js";
4
4
  import {
5
5
  codexUnattendedGitConfigArgs,
6
6
  mergeAgentGitAuthEnv,
@@ -1908,7 +1908,7 @@ var claudeAdapter = {
1908
1908
  );
1909
1909
  }
1910
1910
  const mode = permissionMode(thread);
1911
- const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-XATJTV3N.js");
1911
+ const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-JTBBH2K5.js");
1912
1912
  const isOrchestrator = isOrchestratorThread2(thread);
1913
1913
  const injectedServers = await buildInjectedMcpServers({
1914
1914
  includeSideboard: true,