@sideboard-ai/core 0.1.149 → 0.1.150

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 (60) hide show
  1. package/dist/{abletime-QEVAE67D.js → abletime-223A3LY6.js} +2 -2
  2. package/dist/{abletime-DI55AJX3.js → abletime-CLC5UJC2.js} +2 -2
  3. package/dist/agents/cursor-runner.cjs +40 -35
  4. package/dist/agents/cursor-runner.js +2 -2
  5. package/dist/{agents-HSVQ2Z6Y.js → agents-KKXGTEV6.js} +10 -9
  6. package/dist/{agents-V4MTDWXS.js → agents-YWBQXOA2.js} +10 -9
  7. package/dist/{app-settings-WJDCJFSY.js → app-settings-3CHCNIMD.js} +5 -1
  8. package/dist/{app-settings-ZCN632OJ.js → app-settings-J3JBCUOT.js} +5 -1
  9. package/dist/{chunk-I77RYPOH.js → chunk-4XNSGDXE.js} +12 -0
  10. package/dist/{chunk-KBWND62T.js → chunk-7F4XE6UG.js} +5 -5
  11. package/dist/{chunk-R4JP32P5.js → chunk-AF6O55PJ.js} +4 -4
  12. package/dist/{chunk-BOD5V52W.js → chunk-B7XK37WZ.js} +1 -1
  13. package/dist/{chunk-BGQBELGJ.js → chunk-CZD4NRDZ.js} +12 -0
  14. package/dist/{chunk-ZVYLEF72.js → chunk-DMRIZR6M.js} +6 -6
  15. package/dist/{chunk-TZWO7J4W.js → chunk-DXCNW2JF.js} +113 -52
  16. package/dist/{chunk-KPIYENTF.js → chunk-FNQNJOHY.js} +89 -5
  17. package/dist/{chunk-ED4UPEJX.js → chunk-G7SIQFBQ.js} +1 -1
  18. package/dist/{chunk-3QRGCKYL.js → chunk-HAS5EPBS.js} +2 -2
  19. package/dist/{chunk-LAFA56WD.js → chunk-HEII7W3Q.js} +1 -1
  20. package/dist/{chunk-EGDRPA4B.js → chunk-NOTANBYC.js} +6 -6
  21. package/dist/{chunk-JDYKQ6OI.js → chunk-NUPITVRF.js} +134 -73
  22. package/dist/{chunk-QTUESPAW.js → chunk-PTOSP4AH.js} +1 -1
  23. package/dist/{chunk-Q62SDB53.js → chunk-QGJHIJHV.js} +2 -2
  24. package/dist/{chunk-PTI5H3RK.js → chunk-SDN4ZYQH.js} +24 -13
  25. package/dist/{chunk-DVM4ID64.js → chunk-UFHNSFAS.js} +89 -5
  26. package/dist/{chunk-N3VNOFXQ.js → chunk-VO4SPEBS.js} +4 -4
  27. package/dist/chunk-VSFGPWCQ.js +62 -0
  28. package/dist/{chunk-N5PM7HGQ.js → chunk-W6N6GQME.js} +1 -1
  29. package/dist/{chunk-FKOIHGKV.js → chunk-WHU7YGWP.js} +1 -1
  30. package/dist/{chunk-VZJVKQHH.js → chunk-WLV7TRMR.js} +1 -1
  31. package/dist/chunk-Y4NAPW5I.js +60 -0
  32. package/dist/{chunk-B3SJXYIJ.js → chunk-Y7YBWWIG.js} +1 -1
  33. package/dist/{chunk-Q5DHVMXB.js → chunk-YGGBJY5A.js} +18 -7
  34. package/dist/{chunk-JTPDWTWJ.js → chunk-Z2PVJKMN.js} +137 -114
  35. package/dist/{chunk-YP3CSOZ6.js → chunk-ZGFYFVJF.js} +142 -114
  36. package/dist/{connected-teams-IJUJQ7R6.js → connected-teams-I2WZZZFA.js} +2 -2
  37. package/dist/{connected-teams-4A2AXDNN.js → connected-teams-XO766HB4.js} +2 -2
  38. package/dist/{coordinator-prompt-ZWBMAIWA.js → coordinator-prompt-NYR5TPBD.js} +6 -5
  39. package/dist/{coordinator-prompt-5GXGBGEZ.js → coordinator-prompt-UCQJTFA7.js} +6 -5
  40. package/dist/{global-workspace-2SIPM4PW.js → global-workspace-4QHDAD2D.js} +7 -6
  41. package/dist/{global-workspace-OJRXWXEA.js → global-workspace-4SENNWBU.js} +7 -6
  42. package/dist/index.cjs +998 -574
  43. package/dist/index.d.cts +91 -10
  44. package/dist/index.d.ts +91 -10
  45. package/dist/index.js +268 -93
  46. package/dist/mcp/run-stdio.cjs +976 -579
  47. package/dist/mcp/run-stdio.js +196 -43
  48. package/dist/{orchestrator-6TXQWBXN.js → orchestrator-GZE4X3XV.js} +14 -11
  49. package/dist/{orchestrator-Z2O5OVJA.js → orchestrator-QSBRWME5.js} +16 -13
  50. package/dist/{plan-file-6O7G4VPQ.js → plan-file-5S5TUNAW.js} +2 -2
  51. package/dist/{plan-file-PHVKUAEE.js → plan-file-IBSU2PVW.js} +2 -2
  52. package/dist/{run-XQKGHQZ2.js → run-CAGKZLHA.js} +1 -1
  53. package/dist/{run-USF6FRKV.js → run-EBWL2Q4N.js} +1 -1
  54. package/dist/{workspaces-UUJNOO4T.js → workspaces-7S7QKCUE.js} +8 -7
  55. package/dist/{workspaces-3NUUZTQK.js → workspaces-RDUSB6FD.js} +8 -7
  56. package/dist/{worktree-RZRJNHYL.js → worktree-63AGPPLO.js} +5 -4
  57. package/dist/{worktree-UCWWTVLS.js → worktree-JIZ3QMZX.js} +5 -4
  58. package/dist/worktree-exclude-6MQWGWV5.js +11 -0
  59. package/dist/worktree-exclude-TV6EV6BS.js +9 -0
  60. package/package.json +1 -1
@@ -12,6 +12,7 @@ import {
12
12
  formatTurnExitError,
13
13
  getAdapter,
14
14
  isBrightsyNdjsonLine,
15
+ isInternalAgentStatusText,
15
16
  isPollWrapperToolName,
16
17
  isSessionQuotaLimit,
17
18
  liveActivitySummary,
@@ -32,21 +33,13 @@ import {
32
33
  summarizeTurnStderr,
33
34
  toolDescription,
34
35
  turnFailChatText
35
- } from "./chunk-Q5DHVMXB.js";
36
+ } from "./chunk-YGGBJY5A.js";
36
37
  import {
37
38
  addWorkspace,
38
39
  ensureWorkspace,
39
40
  removeWorkspace,
40
41
  syncWorkspacesFromThreads
41
- } from "./chunk-Q62SDB53.js";
42
- import {
43
- extractPresentedPlan,
44
- readPlanFile,
45
- writePlanFile
46
- } from "./chunk-N5PM7HGQ.js";
47
- import {
48
- releaseCaffeinateHoldForThread
49
- } from "./chunk-KLBOWJ74.js";
42
+ } from "./chunk-QGJHIJHV.js";
50
43
  import {
51
44
  assertOrchestratorCapableAgent,
52
45
  attachmentsFromWorktreePaths,
@@ -63,14 +56,22 @@ import {
63
56
  persistPendingFileAttachments,
64
57
  stageAbsolutePathsAsAttachments,
65
58
  stageBuffersAsAttachments
66
- } from "./chunk-N3VNOFXQ.js";
59
+ } from "./chunk-VO4SPEBS.js";
60
+ import {
61
+ extractPresentedPlan,
62
+ readPlanFile,
63
+ writePlanFile
64
+ } from "./chunk-W6N6GQME.js";
65
+ import {
66
+ releaseCaffeinateHoldForThread
67
+ } from "./chunk-KLBOWJ74.js";
67
68
  import {
68
69
  SLACK_REPLY_FORMATTING,
69
70
  coordinatorSystemPrompt,
70
71
  coordinatorTurnReminder,
71
72
  enrichWorkspacesWithGithub,
72
73
  ensureGlobalCoordinatorCwd
73
- } from "./chunk-ZVYLEF72.js";
74
+ } from "./chunk-DMRIZR6M.js";
74
75
  import {
75
76
  addPrStackLayer,
76
77
  allocateTeamName,
@@ -118,11 +119,11 @@ import {
118
119
  withRepoGitLock,
119
120
  worktreeDisplayLabelForGroup,
120
121
  worktreeNameFromPath
121
- } from "./chunk-JTPDWTWJ.js";
122
+ } from "./chunk-Z2PVJKMN.js";
122
123
  import {
123
124
  ATTACHMENTS_DIR,
124
125
  LEGACY_ATTACHMENTS_DIR
125
- } from "./chunk-B3SJXYIJ.js";
126
+ } from "./chunk-Y7YBWWIG.js";
126
127
  import {
127
128
  appendMessage,
128
129
  createEmptyThread,
@@ -135,6 +136,9 @@ import {
135
136
  withThreadLock,
136
137
  writeThread
137
138
  } from "./chunk-EUXOHTUK.js";
139
+ import {
140
+ ensureWorktreeSideboardIgnored
141
+ } from "./chunk-VSFGPWCQ.js";
138
142
  import {
139
143
  clearStaleIndexLocks,
140
144
  enrichPathWithNpmGlobalBin,
@@ -142,7 +146,7 @@ import {
142
146
  git,
143
147
  resolveGitDirsForLockRecovery,
144
148
  run
145
- } from "./chunk-KPIYENTF.js";
149
+ } from "./chunk-FNQNJOHY.js";
146
150
  import {
147
151
  childEnvWithAppSettings,
148
152
  getIssueSource,
@@ -158,7 +162,7 @@ import {
158
162
  stripNestedElectronEnv,
159
163
  updateAdvancedSettings,
160
164
  writeSecureJson
161
- } from "./chunk-BGQBELGJ.js";
165
+ } from "./chunk-CZD4NRDZ.js";
162
166
  import {
163
167
  writePrivateFile
164
168
  } from "./chunk-NSTQ6QKD.js";
@@ -391,7 +395,7 @@ async function continueSourceThread(threadId, prompt) {
391
395
  await continueOnReply(threadId, prompt);
392
396
  return;
393
397
  }
394
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-6TXQWBXN.js");
398
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-GZE4X3XV.js");
395
399
  await getOrchestrator2().send(threadId, prompt);
396
400
  } catch {
397
401
  }
@@ -732,9 +736,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
732
736
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
733
737
  );
734
738
  }
735
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-2SIPM4PW.js");
739
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-4QHDAD2D.js");
736
740
  if (isGlobalThread2(thread)) {
737
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-5GXGBGEZ.js");
741
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-UCQJTFA7.js");
738
742
  ensureGlobalCoordinatorCwd2(
739
743
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
740
744
  );
@@ -1966,6 +1970,7 @@ async function runConventionSetup(repoPath, worktreePath, onLine, opts) {
1966
1970
  };
1967
1971
  }
1968
1972
  async function runWorkspaceSetup(repoPath, worktreePath, onLine, opts) {
1973
+ await ensureWorktreeSideboardIgnored(worktreePath);
1969
1974
  const reviewSkill = ensureReviewSkillFile(worktreePath);
1970
1975
  if (reviewSkill.wrote) {
1971
1976
  onLine?.(
@@ -2874,7 +2879,7 @@ async function createThread(input, _onSetupLine) {
2874
2879
  });
2875
2880
  await requireAgent(resolved.agent);
2876
2881
  if (input.cowboy) {
2877
- const { cowboyModeEnabled } = await import("./app-settings-ZCN632OJ.js");
2882
+ const { cowboyModeEnabled } = await import("./app-settings-J3JBCUOT.js");
2878
2883
  if (!cowboyModeEnabled()) {
2879
2884
  throw new Error(
2880
2885
  "Cowboy mode is off. Enable it in Settings \u2192 Advanced."
@@ -2932,7 +2937,7 @@ async function createThread(input, _onSetupLine) {
2932
2937
  let sourceIsFork = false;
2933
2938
  if (!input.cowboy && sourceType === "branch" && (!sourceRef || sourceRef === "HEAD" || sourceRef === "default")) {
2934
2939
  if (isAbleTimeConnected() && getIssueSource() === "abletime") {
2935
- const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-DI55AJX3.js");
2940
+ const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-CLC5UJC2.js");
2936
2941
  const task = await ensureAbleTimeTask({
2937
2942
  title: title?.trim() || "Untitled work",
2938
2943
  description: input.prompt?.trim() || void 0
@@ -3005,7 +3010,7 @@ async function createThread(input, _onSetupLine) {
3005
3010
  return readThread(thread.id) ?? thread;
3006
3011
  }
3007
3012
  async function listLinearIssues(agent, repoPath) {
3008
- const { getAdapter: getAdapter2 } = await import("./agents-V4MTDWXS.js");
3013
+ const { getAdapter: getAdapter2 } = await import("./agents-YWBQXOA2.js");
3009
3014
  await requireAgent(agent, { requireLinear: true });
3010
3015
  const adapter = getAdapter2(agent);
3011
3016
  if (!adapter.listLinearIssues) {
@@ -3397,7 +3402,7 @@ async function adoptThread(input) {
3397
3402
  throw new Error(`Worktree not found: ${input.worktreePath}`);
3398
3403
  }
3399
3404
  const repoPath = await resolveRepoRoot(input.worktreePath);
3400
- const { stdout: branch } = await import("./run-USF6FRKV.js").then(
3405
+ const { stdout: branch } = await import("./run-EBWL2Q4N.js").then(
3401
3406
  ({ git: git2 }) => git2(["rev-parse", "--abbrev-ref", "HEAD"], input.worktreePath)
3402
3407
  );
3403
3408
  const thread = createEmptyThread({
@@ -3412,8 +3417,10 @@ async function adoptThread(input) {
3412
3417
  messages: input.messages ?? []
3413
3418
  });
3414
3419
  writeThread(thread);
3415
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-UUJNOO4T.js");
3420
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-7S7QKCUE.js");
3416
3421
  await ensureWorkspace2(repoPath);
3422
+ const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-6MQWGWV5.js");
3423
+ await ensureWorktreeSideboardIgnored2(input.worktreePath);
3417
3424
  return thread;
3418
3425
  }
3419
3426
  function listConductorWorkspaces() {
@@ -4709,7 +4716,7 @@ async function confirmLand(thread, opts) {
4709
4716
  if (preview.dirty) {
4710
4717
  committed = await commitAll(thread.worktreePath, meta.commitMessage);
4711
4718
  }
4712
- const { git: git2 } = await import("./run-USF6FRKV.js");
4719
+ const { git: git2 } = await import("./run-EBWL2Q4N.js");
4713
4720
  const { stdout: headOut } = await git2(
4714
4721
  ["rev-parse", "--abbrev-ref", "HEAD"],
4715
4722
  thread.worktreePath,
@@ -5162,11 +5169,11 @@ function formatWorktreeDirective(thread, opts) {
5162
5169
  );
5163
5170
  } else if (opts?.githubSlug) {
5164
5171
  lines.push(
5165
- `- Prefer a draft PR first: \`gh pr create --draft -R ${opts.githubSlug}\` (or update via \`gh pr edit -R ${opts.githubSlug}\`) once the change set is coherent. Always pass \`-R ${opts.githubSlug}\` (this worktree's origin). Bare \`gh pr create\` may target upstream instead of origin on dual-remote checkouts. Mark ready for review only when asked. Title/body must reflect the change purpose, not the worktree name.`
5172
+ `- Prefer a draft PR first: \`gh pr create --draft -R ${opts.githubSlug}\` (or update via \`gh pr edit -R ${opts.githubSlug}\`) once the change set is coherent. Always pass \`-R ${opts.githubSlug}\` (this worktree's origin). Bare \`gh pr create\` may target upstream instead of origin on dual-remote checkouts. Mark ready for review only when asked. Title/body must reflect the change purpose, not the worktree name. If GitHub rejects the GraphQL body as too long, retry with a short \`--body-file\` (limit 65,536 characters) \u2014 do not paste a changelog or diff into \`--body\`.`
5166
5173
  );
5167
5174
  } else {
5168
5175
  lines.push(
5169
- "- Prefer a draft PR first: `gh pr create --draft -R <origin-owner/name>` (or update via `gh pr edit -R \u2026`) once the change set is coherent. Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree \u2014 never from `upstream`. Mark ready for review only when asked. Title/body must reflect the change purpose, not the worktree name."
5176
+ "- Prefer a draft PR first: `gh pr create --draft -R <origin-owner/name>` (or update via `gh pr edit -R \u2026`) once the change set is coherent. Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree \u2014 never from `upstream`. Mark ready for review only when asked. Title/body must reflect the change purpose, not the worktree name. If GitHub rejects the GraphQL body as too long, retry with a short `--body-file` (limit 65,536 characters) \u2014 do not paste a changelog or diff into `--body`."
5170
5177
  );
5171
5178
  }
5172
5179
  lines.push("");
@@ -5611,7 +5618,7 @@ function formatScheduledPrompt(name, prompt) {
5611
5618
  ${prompt}`;
5612
5619
  }
5613
5620
  async function defaultDeps() {
5614
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-6TXQWBXN.js");
5621
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-GZE4X3XV.js");
5615
5622
  const orch = getOrchestrator2();
5616
5623
  return {
5617
5624
  findThread: (id) => findThreadByRef(id),
@@ -5715,6 +5722,7 @@ function armSchedules() {
5715
5722
  }
5716
5723
 
5717
5724
  // src/orchestrator/orchestrator.ts
5725
+ var LIVE_TURN_SPAWN_GRACE_MS = 15e3;
5718
5726
  function isPidAlive(pid) {
5719
5727
  if (!Number.isFinite(pid) || pid <= 0) return false;
5720
5728
  try {
@@ -5793,6 +5801,39 @@ var Orchestrator = class {
5793
5801
  if (typeof pid === "number" && pid > 0 && isPidAlive(pid)) return false;
5794
5802
  return true;
5795
5803
  }
5804
+ /**
5805
+ * Whether a child is actually turning — not leftover disk `running` after
5806
+ * the process died. Coordinators were telling users “Agent is running.
5807
+ * Waiting for gate to pass” from stale stillRunning.
5808
+ */
5809
+ threadLooksLive(thread) {
5810
+ if (thread.status === "queued") {
5811
+ if (this.draining.has(thread.id) || this.startingTurns.has(thread.id)) return true;
5812
+ return thread.queue.length > 0;
5813
+ }
5814
+ if (thread.status !== "running") return false;
5815
+ if (this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id)) return true;
5816
+ const pid = thread.agentPid;
5817
+ if (typeof pid === "number" && pid > 0 && isPidAlive(pid)) return true;
5818
+ const updated = Date.parse(thread.updatedAt ?? "");
5819
+ if (Number.isFinite(updated) && Date.now() - updated < LIVE_TURN_SPAWN_GRACE_MS) return true;
5820
+ return false;
5821
+ }
5822
+ /** Desktop-only: persist idle/stopped when MCP would otherwise keep reporting live. */
5823
+ healStaleReportedActivity(thread) {
5824
+ if (!thisProcessShouldDrainAgentQueues()) return thread;
5825
+ if (this.threadLooksLive(thread)) return thread;
5826
+ if (thread.status === "running") {
5827
+ this.healStaleRunningTurns();
5828
+ return readThread(thread.id) ?? thread;
5829
+ }
5830
+ if (thread.status === "queued" && thread.queue.length === 0 && !this.activeTurns.has(thread.id) && !this.startingTurns.has(thread.id) && !this.draining.has(thread.id)) {
5831
+ setStatus(thread.id, "idle");
5832
+ this.emit({ type: "status_changed", threadId: thread.id, status: "idle" });
5833
+ return readThread(thread.id) ?? thread;
5834
+ }
5835
+ return thread;
5836
+ }
5796
5837
  async reconcile(repoPath, opts) {
5797
5838
  const reclaimStaleTurns = opts?.reclaimStaleTurns === true;
5798
5839
  const drainQueues = opts?.drainQueues !== false;
@@ -5836,7 +5877,7 @@ var Orchestrator = class {
5836
5877
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
5837
5878
  });
5838
5879
  }
5839
- const { autoCleanupOrphansEnabled } = await import("./app-settings-ZCN632OJ.js");
5880
+ const { autoCleanupOrphansEnabled } = await import("./app-settings-J3JBCUOT.js");
5840
5881
  if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
5841
5882
  await cleanupOrphanWorktrees({ repoPaths });
5842
5883
  }
@@ -6076,7 +6117,7 @@ var Orchestrator = class {
6076
6117
  }
6077
6118
  await setup;
6078
6119
  if (skipSetup) return;
6079
- const { autoRunAfterSetupEnabled } = await import("./app-settings-ZCN632OJ.js");
6120
+ const { autoRunAfterSetupEnabled } = await import("./app-settings-J3JBCUOT.js");
6080
6121
  if (autoRunAfterSetupEnabled()) {
6081
6122
  try {
6082
6123
  await this.startDev(threadId);
@@ -6127,12 +6168,14 @@ var Orchestrator = class {
6127
6168
  this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
6128
6169
  return thread;
6129
6170
  }
6130
- async send(threadRef, prompt) {
6171
+ async send(threadRef, prompt, opts) {
6131
6172
  const thread = this.requireThread(threadRef);
6132
6173
  if (thread.status === "archived") {
6133
6174
  throw new Error(`Thread is archived: ${thread.id}`);
6134
6175
  }
6135
- return withThreadLock(thread.id, async () => {
6176
+ const followUp = opts?.followUp === "steer" ? "steer" : "queue";
6177
+ let shouldSteer = false;
6178
+ const sent = await withThreadLock(thread.id, async () => {
6136
6179
  const current = this.requireThread(thread.id);
6137
6180
  if (current.status === "archived") {
6138
6181
  throw new Error(`Thread is archived: ${thread.id}`);
@@ -6141,6 +6184,7 @@ var Orchestrator = class {
6141
6184
  this.crashContinued.delete(thread.id);
6142
6185
  this.haltDrain.delete(thread.id);
6143
6186
  const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id) || current.status === "running";
6187
+ shouldSteer = followUp === "steer" && (inFlight || current.queue.length > 0);
6144
6188
  const patch = { queue };
6145
6189
  if (!inFlight) patch.status = "queued";
6146
6190
  const pid = current.agentPid;
@@ -6152,16 +6196,20 @@ var Orchestrator = class {
6152
6196
  if (!inFlight) {
6153
6197
  this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
6154
6198
  }
6155
- if (thisProcessShouldDrainAgentQueues()) {
6199
+ if (thisProcessShouldDrainAgentQueues() && !shouldSteer) {
6156
6200
  void this.drainQueue(thread.id);
6157
6201
  }
6158
6202
  return this.requireThread(thread.id);
6159
6203
  });
6204
+ if (shouldSteer && sent.queue.length > 0) {
6205
+ return this.sendQueuedMessageNow(sent.id, sent.queue.length - 1);
6206
+ }
6207
+ return sent;
6160
6208
  }
6161
- async fanOut(threadRefs, prompt) {
6209
+ async fanOut(threadRefs, prompt, opts) {
6162
6210
  const results = [];
6163
6211
  for (const ref of threadRefs) {
6164
- results.push(await this.send(ref, prompt));
6212
+ results.push(await this.send(ref, prompt, opts));
6165
6213
  }
6166
6214
  return results;
6167
6215
  }
@@ -6377,7 +6425,7 @@ var Orchestrator = class {
6377
6425
  }
6378
6426
  const isBrightsy = fresh.agent === "brightsy";
6379
6427
  const isOrchestration = isOrchestratorThread(fresh);
6380
- const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-ZCN632OJ.js");
6428
+ const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-J3JBCUOT.js");
6381
6429
  const gitAuthMode = getGithubGitAuthMode();
6382
6430
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
6383
6431
  githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
@@ -6965,8 +7013,8 @@ var Orchestrator = class {
6965
7013
  return created;
6966
7014
  }
6967
7015
  async waitForTurn(threadRef, timeoutMs = 6e5, opts) {
6968
- const thread = this.requireThread(threadRef);
6969
- if (!["running", "queued"].includes(thread.status)) {
7016
+ const thread = this.healStaleReportedActivity(this.requireThread(threadRef));
7017
+ if (!this.threadLooksLive(thread)) {
6970
7018
  return thread;
6971
7019
  }
6972
7020
  const start = Date.now();
@@ -7003,12 +7051,13 @@ var Orchestrator = class {
7003
7051
  }
7004
7052
  });
7005
7053
  timer = setInterval(() => {
7006
- const current = readThread(thread.id);
7007
- if (!current) {
7054
+ const raw = readThread(thread.id);
7055
+ if (!raw) {
7008
7056
  finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
7009
7057
  return;
7010
7058
  }
7011
- if (!["running", "queued"].includes(current.status)) {
7059
+ const current = this.healStaleReportedActivity(raw);
7060
+ if (!this.threadLooksLive(current)) {
7012
7061
  finish(() => resolve(current));
7013
7062
  return;
7014
7063
  }
@@ -7031,20 +7080,22 @@ var Orchestrator = class {
7031
7080
  };
7032
7081
  }
7033
7082
  getTurnResult(threadRef) {
7034
- const thread = this.requireThread(threadRef);
7083
+ const thread = this.healStaleReportedActivity(this.requireThread(threadRef));
7035
7084
  const lastAgent = [...thread.messages].reverse().find((m) => m.role === "agent");
7036
7085
  const lastError = thread.lastError ?? null;
7037
- const text = (lastAgent?.text ?? "").trim() || (thread.status === "error" || thread.status === "stopped" || thread.status === "broken" ? lastError ?? "" : "");
7038
- const stillRunning = thread.status === "running" || thread.status === "queued";
7086
+ const rawText = (lastAgent?.text ?? "").trim();
7087
+ const text = (rawText && !isInternalAgentStatusText(rawText) ? rawText : "") || (thread.status === "error" || thread.status === "stopped" || thread.status === "broken" ? lastError ?? "" : "");
7088
+ const stillRunning = this.threadLooksLive(thread);
7039
7089
  const live = stillRunning ? readTurnLive(thread.id) : null;
7040
- const queuedHint = thread.status === "queued" && !live?.summary ? "Queued \u2014 waiting for a concurrency slot" : null;
7090
+ const liveSummary = live?.summary && !isInternalAgentStatusText(live.summary) ? live.summary : null;
7091
+ const queuedHint = stillRunning && thread.status === "queued" && !liveSummary ? "Queued \u2014 waiting for a concurrency slot" : null;
7041
7092
  return {
7042
7093
  text,
7043
7094
  status: thread.status,
7044
7095
  sessionId: thread.sessionId,
7045
7096
  lastError,
7046
7097
  stillRunning,
7047
- progress: live?.summary ?? queuedHint,
7098
+ progress: liveSummary ?? queuedHint,
7048
7099
  lastActivityAt: live?.updatedAt ?? null,
7049
7100
  usage: lastAgent?.usage ?? null
7050
7101
  };
@@ -7237,7 +7288,7 @@ var Orchestrator = class {
7237
7288
  updateThread(thread.id, { title: meta.title });
7238
7289
  }
7239
7290
  }
7240
- const { autoArchiveOnMergeEnabled } = await import("./app-settings-ZCN632OJ.js");
7291
+ const { autoArchiveOnMergeEnabled } = await import("./app-settings-J3JBCUOT.js");
7241
7292
  const latest = this.requireThread(thread.id);
7242
7293
  if (!shouldAutoArchiveOnPrMerge({
7243
7294
  previousPrState: prevState || null,
@@ -7392,7 +7443,14 @@ var Orchestrator = class {
7392
7443
  );
7393
7444
  }
7394
7445
  if ((action === "commit-push" || action === "create-draft" || action === "create-web") && thread.worktreePath?.trim() && !await isDirty(thread.worktreePath)) {
7395
- await this.pushAndMaybeOpenPr(thread, action);
7446
+ try {
7447
+ await this.pushAndMaybeOpenPr(thread, action);
7448
+ } catch (err) {
7449
+ const message = err instanceof Error ? err.message : String(err);
7450
+ updateThread(thread.id, { lastError: message });
7451
+ this.emit({ type: "error", threadId: thread.id, message });
7452
+ throw err;
7453
+ }
7396
7454
  return this.requireThread(threadRef);
7397
7455
  }
7398
7456
  let prBase;
@@ -7547,7 +7605,7 @@ var Orchestrator = class {
7547
7605
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
7548
7606
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
7549
7607
  try {
7550
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-UUJNOO4T.js");
7608
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-7S7QKCUE.js");
7551
7609
  await ensureWorkspace2(thread.repoPath);
7552
7610
  } catch {
7553
7611
  }
@@ -7574,7 +7632,7 @@ var Orchestrator = class {
7574
7632
  await runArchiveScript(thread.repoPath, thread.worktreePath);
7575
7633
  } catch {
7576
7634
  }
7577
- const { deleteBranchOnPurgeEnabled } = await import("./app-settings-ZCN632OJ.js");
7635
+ const { deleteBranchOnPurgeEnabled } = await import("./app-settings-J3JBCUOT.js");
7578
7636
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
7579
7637
  await removeWorktree(thread.repoPath, thread.worktreePath, {
7580
7638
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -7601,12 +7659,14 @@ var Orchestrator = class {
7601
7659
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
7602
7660
  );
7603
7661
  }
7604
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-UCWWTVLS.js");
7662
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-JIZ3QMZX.js");
7605
7663
  const slug = thread.worktreePath.split("/").pop();
7606
7664
  const dest = thread.worktreePath;
7607
7665
  await withRepoGitLock(thread.repoPath, async () => {
7608
7666
  await git(["worktree", "add", dest, thread.branchName], thread.repoPath);
7609
7667
  });
7668
+ const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-6MQWGWV5.js");
7669
+ await ensureWorktreeSideboardIgnored2(dest);
7610
7670
  void createThreadWorktree2;
7611
7671
  void slug;
7612
7672
  }
@@ -7709,7 +7769,7 @@ async function startOrchestration(opts) {
7709
7769
  sourceRef: "default",
7710
7770
  ...createOpts
7711
7771
  }).catch(async () => {
7712
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-UCWWTVLS.js");
7772
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-JIZ3QMZX.js");
7713
7773
  const repo = await resolveRepoRoot2(repoPath);
7714
7774
  const def = await resolveDefaultBranch2(repo);
7715
7775
  return orch.createThread({
@@ -7761,6 +7821,7 @@ export {
7761
7821
  updateSchedule,
7762
7822
  deleteSchedule,
7763
7823
  fireSchedule,
7824
+ LIVE_TURN_SPAWN_GRACE_MS,
7764
7825
  isPidAlive,
7765
7826
  waitForPidExit,
7766
7827
  Orchestrator,
@@ -99,17 +99,93 @@ function withExportedPath(command, pathValue) {
99
99
  return `export PATH=${posixShellSingleQuote(pathValue)} && ${trimmed}`;
100
100
  }
101
101
 
102
+ // src/git/github-agent-auth.ts
103
+ import { chmodSync, existsSync as existsSync2, mkdirSync, writeFileSync } from "fs";
104
+ import { homedir as homedir2 } from "os";
105
+ import { dirname, join as join2 } from "path";
106
+ function githubAgentAuthDir() {
107
+ const override = process.env.SIDEBOARD_GIT_AUTH_DIR?.trim();
108
+ if (override) return override;
109
+ return join2(homedir2(), ".sideboard-git-auth");
110
+ }
111
+ function githubCredentialStorePath() {
112
+ return join2(githubAgentAuthDir(), "git-credentials");
113
+ }
114
+ function githubGhConfigDir() {
115
+ return join2(githubAgentAuthDir(), "gh");
116
+ }
117
+ function writePrivateFile(file, body) {
118
+ mkdirSync(dirname(file), { recursive: true, mode: 448 });
119
+ try {
120
+ chmodSync(dirname(file), 448);
121
+ } catch {
122
+ }
123
+ writeFileSync(file, body, { encoding: "utf8", mode: 384 });
124
+ try {
125
+ chmodSync(file, 384);
126
+ } catch {
127
+ }
128
+ }
129
+ function gitCredentialStoreContents(token) {
130
+ return `https://x-access-token:${encodeURIComponent(token)}@github.com
131
+ `;
132
+ }
133
+ function ghHostsYml(token, user = "x-access-token") {
134
+ const u = user.replace(/[^A-Za-z0-9._-]/g, "") || "x-access-token";
135
+ return [
136
+ "github.com:",
137
+ " git_protocol: https",
138
+ ` user: ${u}`,
139
+ ` oauth_token: ${token}`,
140
+ " users:",
141
+ ` ${u}:`,
142
+ ` oauth_token: ${token}`,
143
+ ""
144
+ ].join("\n");
145
+ }
146
+ function materializeGithubAgentAuth(token, user) {
147
+ const trimmed = token.trim();
148
+ if (!trimmed) return;
149
+ const root = githubAgentAuthDir();
150
+ mkdirSync(root, { recursive: true, mode: 448 });
151
+ try {
152
+ chmodSync(root, 448);
153
+ } catch {
154
+ }
155
+ writePrivateFile(githubCredentialStorePath(), gitCredentialStoreContents(trimmed));
156
+ const ghDir = githubGhConfigDir();
157
+ mkdirSync(ghDir, { recursive: true, mode: 448 });
158
+ writePrivateFile(join2(ghDir, "hosts.yml"), ghHostsYml(trimmed, user));
159
+ writePrivateFile(join2(ghDir, "config.yml"), "git_protocol: https\nprompt: disabled\n");
160
+ }
161
+ function githubAgentAuthReady() {
162
+ return existsSync2(githubCredentialStorePath()) && existsSync2(join2(githubGhConfigDir(), "hosts.yml"));
163
+ }
164
+ function githubCredentialHelperGitConfig() {
165
+ const file = githubCredentialStorePath();
166
+ return [
167
+ { key: "credential.helper", value: "" },
168
+ { key: "credential.helper", value: `store --file=${file}` }
169
+ ];
170
+ }
171
+ function githubGhConfigEnv() {
172
+ return {
173
+ GH_CONFIG_DIR: githubGhConfigDir(),
174
+ GH_PROMPT_DISABLED: "1"
175
+ };
176
+ }
177
+
102
178
  // src/git/stale-lock.ts
103
- import { existsSync as existsSync2, statSync, unlinkSync } from "fs";
104
- import { join as join2 } from "path";
179
+ import { existsSync as existsSync3, statSync, unlinkSync } from "fs";
180
+ import { join as join3 } from "path";
105
181
  var STALE_INDEX_LOCK_MS = 2e4;
106
182
  function isIndexLockError(text) {
107
183
  return /Unable to create ['"][^'"]*index\.lock['"]: File exists/i.test(text);
108
184
  }
109
185
  function clearStaleIndexLock(gitDir, maxAgeMs = STALE_INDEX_LOCK_MS, now = Date.now()) {
110
- const lockPath = join2(gitDir, "index.lock");
186
+ const lockPath = join3(gitDir, "index.lock");
111
187
  try {
112
- if (!existsSync2(lockPath)) return null;
188
+ if (!existsSync3(lockPath)) return null;
113
189
  if (now - statSync(lockPath).mtimeMs < maxAgeMs) return null;
114
190
  unlinkSync(lockPath);
115
191
  return lockPath;
@@ -211,7 +287,10 @@ async function gh(args, cwd, opts) {
211
287
  timeoutMs: opts?.timeoutMs,
212
288
  env: {
213
289
  GH_PROMPT_DISABLED: "1",
214
- GIT_TERMINAL_PROMPT: "0"
290
+ GIT_TERMINAL_PROMPT: "0",
291
+ // Isolated hosts.yml uses git_protocol: https so `gh pr create` does not
292
+ // shell out over SSH when Settings → Git is SSH (ask_git in Electron).
293
+ ...githubAgentAuthReady() ? githubGhConfigEnv() : {}
215
294
  }
216
295
  });
217
296
  }
@@ -230,6 +309,11 @@ export {
230
309
  posixShellSingleQuote,
231
310
  resolveCommandBinarySync,
232
311
  withExportedPath,
312
+ githubAgentAuthDir,
313
+ materializeGithubAgentAuth,
314
+ githubAgentAuthReady,
315
+ githubCredentialHelperGitConfig,
316
+ githubGhConfigEnv,
233
317
  clearStaleIndexLocks,
234
318
  run,
235
319
  resolveGitDirsForLockRecovery,
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-S42XV45P.js";
10
10
  import {
11
11
  run
12
- } from "./chunk-KPIYENTF.js";
12
+ } from "./chunk-FNQNJOHY.js";
13
13
  import {
14
14
  appDataDir
15
15
  } from "./chunk-BD2ICC4D.js";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  isGlobalRepoPath
3
- } from "./chunk-R4JP32P5.js";
3
+ } from "./chunk-AF6O55PJ.js";
4
4
  import {
5
5
  ensureGhPreferOrigin,
6
6
  resolveRepoRoot
7
- } from "./chunk-YP3CSOZ6.js";
7
+ } from "./chunk-ZGFYFVJF.js";
8
8
  import {
9
9
  appDataDir
10
10
  } from "./chunk-QZQEXME2.js";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-4TR3HZFT.js";
5
5
  import {
6
6
  run
7
- } from "./chunk-DVM4ID64.js";
7
+ } from "./chunk-UFHNSFAS.js";
8
8
 
9
9
  // src/agents/error-detail.ts
10
10
  function formatUnknownDetail(err) {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  resolveGithubRepoSlug
3
- } from "./chunk-YP3CSOZ6.js";
3
+ } from "./chunk-ZGFYFVJF.js";
4
4
  import {
5
5
  resolveThreadDefaults
6
- } from "./chunk-I77RYPOH.js";
6
+ } from "./chunk-4XNSGDXE.js";
7
7
  import {
8
8
  globalAgentCwd,
9
9
  sideboardReposDir
@@ -45,8 +45,8 @@ var COORDINATOR_TOOL_PLAYBOOK = [
45
45
  "Discover:",
46
46
  "- list_workspaces \u2014 registered repos (path + github slug when known)",
47
47
  "- list_board \u2014 Home Kanban of worktrees (New / Draft / Review / Merged; one card per checkout). Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind, column, limit.",
48
- "- list_branches / list_prs / list_issues \u2014 pass repoPath from list_workspaces (issues: Linear, AbleTime MCP, or GitHub Issues)",
49
- "- linear_list_teams / linear_get_issue / linear_create_issue / linear_update_issue / linear_comment \u2014 Linear Account connection; call linear_list_teams for team key and workflow states; pass ENG-123 or uuid. linear_get_issue returns comments, relations, parent/children, and the rest of the issue metadata. If mutations fail with a scope error, Disconnect and Connect Linear in Account settings.",
48
+ "- list_branches / list_prs / list_issues \u2014 pass repoPath from list_workspaces (issues: Linear, AbleTime MCP, or GitHub Issues). list_issues accepts query and assignee (me / unassigned / all / user id) so Linear is not limited to tickets assigned to you.",
49
+ "- linear_list_teams / linear_search_issues / linear_get_issue / linear_create_issue / linear_update_issue / linear_comment \u2014 Linear Account connection; call linear_list_teams for team key and workflow states; pass ENG-123 or uuid. linear_search_issues finds unassigned and other people's open issues (assignee=unassigned|all|me|user). linear_get_issue returns comments, relations, parent/children, and the rest of the issue metadata. If mutations fail with a scope error, Disconnect and Connect Linear in Account settings.",
50
50
  "- abletime_orientation / abletime_list_projects / abletime_list_tasks / abletime_search_tasks / abletime_get_task / abletime_create_task / abletime_ensure_task \u2014 AbleTime Account personal access token (hosted MCP). Call orientation first. If work has no task yet, abletime_ensure_task creates one to track against (or create_thread from the default branch does that automatically when AbleTime is the preferred issue source).",
51
51
  "- list_teams / slack_list_channels / slack_list_users / slack_search / slack_read / slack_post / slack_replies \u2014 Slack workspaces from Settings \u2192 Remote; pass team_id from list_teams",
52
52
  "- Optional connectors (Vercel, Supabase, PostHog, Sentry) in Settings \u2192 Connectors inject tokens into worktree agent env when connected. Prefer official CLIs (`vercel`, `supabase`, `sentry-cli`) with those env vars. PostHog has no first-class CLI \u2014 use the HTTP API (`POSTHOG_PERSONAL_API_KEY`). If a CLI is missing, the user can Install CLI on that row (not auto-installed on Connect). Do not add vendor MCPs or ask the user to paste tokens again. Git (`gh`) stays Settings \u2192 Git; issue tracking stays Settings \u2192 Issues; Slack stays Settings \u2192 Remote (Sideboard MCP).",
@@ -65,7 +65,7 @@ var COORDINATOR_TOOL_PLAYBOOK = [
65
65
  "- 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.",
66
66
  "- 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.",
67
67
  "- 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)",
68
- "- 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). 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. 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.",
68
+ "- 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.",
69
69
  "- stop_thread \u2014 force-stop: kill in-flight turn AND clear queued prompts (do not leave stale queue after an interrupt)",
70
70
  "- archive_thread / restore_thread \u2014 archive (tears down worktree when last tab) or restore",
71
71
  "Setup / run:",
@@ -74,7 +74,7 @@ var COORDINATOR_TOOL_PLAYBOOK = [
74
74
  "Inspect / review / PRs:",
75
75
  "- get_diff \u2014 compact diff summary",
76
76
  '- request_review \u2014 open a Review chat tab on a worktree thread (attaches .claude/skills/review/SKILL.md when present, else seeds that skill / local guidelines; sends "Review changes in this workspace."); then wait_for_turn (loop while stillRunning) / get_turn_result on the returned id',
77
- "- ask_git \u2014 commit & push, open a draft PR, 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.",
77
+ "- ask_git \u2014 commit & push, open a draft PR, 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.",
78
78
  '- 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.',
79
79
  '- Or send_to_thread with those exact phrases: "Commit and push.", "Commit, push, and open a draft PR.", "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.',
80
80
  "Process guides:",