@sideboard-ai/core 0.1.228 → 0.1.230

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 (48) hide show
  1. package/dist/{abletime-D6773P33.js → abletime-BGVOCEOU.js} +4 -4
  2. package/dist/{abletime-BF253OZT.js → abletime-WZNDTRVL.js} +4 -4
  3. package/dist/{abletime-oauth-KQPVVBKA.js → abletime-oauth-6M5GWEPX.js} +3 -3
  4. package/dist/{abletime-oauth-H4672PRH.js → abletime-oauth-ZLU2EYRK.js} +3 -3
  5. package/dist/{abletime-rest-DAFPXZ7H.js → abletime-rest-F7XG5NGY.js} +3 -3
  6. package/dist/{abletime-rest-OPEVV3XC.js → abletime-rest-WNHIATB3.js} +3 -3
  7. package/dist/{agents-DV5UROWB.js → agents-ENAQHQRZ.js} +5 -5
  8. package/dist/{agents-QQ77D7VT.js → agents-TCTEAK6M.js} +5 -5
  9. package/dist/{app-settings-5RQMQKSB.js → app-settings-KBL2G7M4.js} +7 -1
  10. package/dist/{app-settings-V2BBCX2H.js → app-settings-UXWCPDEA.js} +7 -1
  11. package/dist/{chunk-NFWPDEN4.js → chunk-4CZ25DO2.js} +2 -2
  12. package/dist/{chunk-3IMSQNTS.js → chunk-4VGCG2HV.js} +17 -0
  13. package/dist/{chunk-SQHRPJ7P.js → chunk-6L25K7BE.js} +14 -4
  14. package/dist/{chunk-HSYE5ID6.js → chunk-AS4UN7QL.js} +5 -5
  15. package/dist/{chunk-3NWUHXU3.js → chunk-C774RJJA.js} +3 -3
  16. package/dist/{chunk-VUW3VKLW.js → chunk-FA4CPYCY.js} +17 -4
  17. package/dist/{chunk-CLI5DVU4.js → chunk-GHRZNPBK.js} +2 -2
  18. package/dist/{chunk-N2VST3NL.js → chunk-HZFLKVGC.js} +3 -3
  19. package/dist/{chunk-72XUASTH.js → chunk-HZLZ34FK.js} +157 -26
  20. package/dist/{chunk-EWHJNJHV.js → chunk-I5NC7DWO.js} +38 -0
  21. package/dist/{chunk-WHYJL7CP.js → chunk-JJRRLCCS.js} +156 -25
  22. package/dist/{chunk-646I442S.js → chunk-MVLXDYN5.js} +5 -5
  23. package/dist/{chunk-KTZESHYH.js → chunk-N5TX66CD.js} +2 -2
  24. package/dist/{chunk-RRW2XWAY.js → chunk-P6ZM5QJW.js} +2 -2
  25. package/dist/{chunk-THLVCXLF.js → chunk-RGNPPRZZ.js} +3 -3
  26. package/dist/{chunk-Y5JES2VV.js → chunk-RZTRWN2V.js} +38 -0
  27. package/dist/{chunk-SOGGKYFV.js → chunk-SWNEA5OZ.js} +3 -3
  28. package/dist/{chunk-P2EAAM3H.js → chunk-UNTOZA3C.js} +3 -3
  29. package/dist/{chunk-HY2GFZ5S.js → chunk-XJETIO4K.js} +17 -0
  30. package/dist/{coordinator-prompt-JLMQVORO.js → coordinator-prompt-DFXYTM6D.js} +4 -4
  31. package/dist/{coordinator-prompt-VZ7OSRLD.js → coordinator-prompt-YKLXQTWH.js} +4 -4
  32. package/dist/{global-workspace-3FDAUNN2.js → global-workspace-4SPANNJT.js} +4 -4
  33. package/dist/{global-workspace-QPSJEXGR.js → global-workspace-EYZKYHAO.js} +4 -4
  34. package/dist/index.cjs +992 -634
  35. package/dist/index.d.cts +120 -6
  36. package/dist/index.d.ts +120 -6
  37. package/dist/index.js +207 -68
  38. package/dist/mcp/run-stdio.cjs +289 -50
  39. package/dist/mcp/run-stdio.js +87 -51
  40. package/dist/{orchestrator-6XEWQWSM.js → orchestrator-HMJPNUN5.js} +8 -6
  41. package/dist/{orchestrator-QUJORSTA.js → orchestrator-ZT3SJVKF.js} +8 -6
  42. package/dist/{thread-store-KDPPXJJ3.js → thread-store-FBK5SZ5P.js} +1 -1
  43. package/dist/{thread-store-N6WQIWZ4.js → thread-store-IKJD4EOK.js} +1 -1
  44. package/dist/{workspaces-76W7IEKR.js → workspaces-73Y7IJY2.js} +4 -4
  45. package/dist/{workspaces-TDBA64II.js → workspaces-FQCSCTNH.js} +4 -4
  46. package/dist/{worktree-7V67XTEO.js → worktree-36VEFUJN.js} +3 -3
  47. package/dist/{worktree-I573UOPD.js → worktree-DOPRKG7K.js} +3 -3
  48. package/package.json +1 -1
@@ -24,7 +24,7 @@ import {
24
24
  resolveQuotaFallbackAgent,
25
25
  stripBrightsyNdjsonNoise,
26
26
  toolDescription
27
- } from "./chunk-VUW3VKLW.js";
27
+ } from "./chunk-FA4CPYCY.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-N2VST3NL.js";
50
+ } from "./chunk-HZFLKVGC.js";
51
51
  import {
52
52
  addPrStackLayer,
53
53
  allocateTeamName,
@@ -96,7 +96,7 @@ import {
96
96
  worktreeDisplayLabelForGroup,
97
97
  worktreeNameFromPath,
98
98
  worktreeSlugForTicket
99
- } from "./chunk-KTZESHYH.js";
99
+ } from "./chunk-N5TX66CD.js";
100
100
  import {
101
101
  ATTACHMENTS_DIR,
102
102
  DETACHED_JOBS_DIR,
@@ -163,7 +163,7 @@ import {
163
163
  updateAdvancedSettings,
164
164
  usageOnLimit,
165
165
  writeSecureJson
166
- } from "./chunk-EWHJNJHV.js";
166
+ } from "./chunk-I5NC7DWO.js";
167
167
  import {
168
168
  examplePrefixedBranch,
169
169
  sanitizeGitBranchPrefix
@@ -185,7 +185,7 @@ import {
185
185
  updateThread,
186
186
  withThreadLock,
187
187
  writeThread
188
- } from "./chunk-3IMSQNTS.js";
188
+ } from "./chunk-4VGCG2HV.js";
189
189
  import {
190
190
  appDataDir,
191
191
  findConventionSetup,
@@ -482,7 +482,7 @@ async function continueSourceThread(threadId, prompt) {
482
482
  await continueOnReply(threadId, prompt);
483
483
  return;
484
484
  }
485
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-QUJORSTA.js");
485
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-ZT3SJVKF.js");
486
486
  await getOrchestrator2().send(threadId, prompt);
487
487
  } catch {
488
488
  }
@@ -742,6 +742,7 @@ async function pollSlackOutboundWatches(opts) {
742
742
  }
743
743
 
744
744
  // src/orchestrator/orchestrator.ts
745
+ import { randomUUID as randomUUID5 } from "crypto";
745
746
  import { existsSync as existsSync19 } from "fs";
746
747
 
747
748
  // src/agents/spawn.ts
@@ -900,9 +901,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
900
901
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
901
902
  );
902
903
  }
903
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-3FDAUNN2.js");
904
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-4SPANNJT.js");
904
905
  if (isGlobalThread2(thread)) {
905
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-JLMQVORO.js");
906
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-DFXYTM6D.js");
906
907
  ensureGlobalCoordinatorCwd2(
907
908
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
908
909
  );
@@ -2981,6 +2982,11 @@ async function cloneRepoIntoSideboard(opts) {
2981
2982
  return { repoPath, workspace };
2982
2983
  }
2983
2984
 
2985
+ // src/types/thread.ts
2986
+ function isUnclaimedRunScriptRequest(req) {
2987
+ return Boolean(req && !req.claimedAt && !req.fulfilledAt && !req.error);
2988
+ }
2989
+
2984
2990
  // src/store/desktop-host.ts
2985
2991
  import { readFileSync as readFileSync6, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
2986
2992
  import { join as join11 } from "path";
@@ -4086,7 +4092,7 @@ async function createThread(input, _onSetupLine) {
4086
4092
  });
4087
4093
  await requireAgent(resolved.agent);
4088
4094
  if (input.cowboy) {
4089
- const { cowboyModeEnabled } = await import("./app-settings-V2BBCX2H.js");
4095
+ const { cowboyModeEnabled } = await import("./app-settings-UXWCPDEA.js");
4090
4096
  if (!cowboyModeEnabled()) {
4091
4097
  throw new Error(
4092
4098
  "Cowboy mode is off. Enable it in Settings \u2192 Advanced."
@@ -4144,7 +4150,7 @@ async function createThread(input, _onSetupLine) {
4144
4150
  let sourceIsFork = false;
4145
4151
  if (!input.cowboy && sourceType === "branch" && (!sourceRef || sourceRef === "HEAD" || sourceRef === "default")) {
4146
4152
  if (isAbleTimeConnected() && getIssueSource() === "abletime") {
4147
- const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-D6773P33.js");
4153
+ const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-BGVOCEOU.js");
4148
4154
  const task = await ensureAbleTimeTask({
4149
4155
  title: title?.trim() || "Untitled work",
4150
4156
  description: input.prompt?.trim() || void 0
@@ -4246,7 +4252,7 @@ async function createThread(input, _onSetupLine) {
4246
4252
  return readThread(thread.id) ?? thread;
4247
4253
  }
4248
4254
  async function listLinearIssues(agent, repoPath) {
4249
- const { getAdapter: getAdapter2 } = await import("./agents-QQ77D7VT.js");
4255
+ const { getAdapter: getAdapter2 } = await import("./agents-TCTEAK6M.js");
4250
4256
  await requireAgent(agent, { requireLinear: true });
4251
4257
  const adapter = getAdapter2(agent);
4252
4258
  if (!adapter.listLinearIssues) {
@@ -4719,7 +4725,7 @@ async function adoptThread(input) {
4719
4725
  messages: input.messages ?? []
4720
4726
  });
4721
4727
  writeThread(thread);
4722
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-76W7IEKR.js");
4728
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-73Y7IJY2.js");
4723
4729
  await ensureWorkspace2(repoPath);
4724
4730
  const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-L32WEBX6.js");
4725
4731
  await ensureWorktreeSideboardIgnored2(input.worktreePath);
@@ -6812,12 +6818,13 @@ function formatLongRunningDirective(opts) {
6812
6818
  "- Loop Sideboard MCP `wait_for_job` (returns in ~45s). The desktop opens a `type=log` pane from wait/stop (and shell detached-job JSON) automatically \u2014 you may still `present_artifact` with `artifact_id=<id>` for a custom title. stillRunning \u2192 wait again. Shell fallback: the same helper with `wait <id>`.",
6813
6819
  "- Hanging, no useful output, or the wrong thing \u2192 `stop_job` (or the helper with `stop <id>`). Do not stop a pack/test/deploy that is clearly making progress.",
6814
6820
  "- Stay in the loop until stillRunning is false (or you stopped it). ok \u2192 finish the task. failed / stopped \u2192 read the log, fix or narrow the command, start once.",
6815
- "State: `.context/.sideboard/detached-jobs/<id>/` (local scratch). Full guide: `/long-running` (always available)."
6821
+ "State: `.context/.sideboard/detached-jobs/<id>/` (local scratch). Full guide: `/long-running` (always available).",
6822
+ "Project Dev / run scripts (`.sideboard` `[scripts.run.*]`, same as the desktop Dev button): use MCP `list_run_scripts` / `run_dev_script` / `stop_dev_script` \u2014 logs and Stop live in the thread UI. Do not detached-job or shell-spawn that same command."
6816
6823
  ].join("\n");
6817
6824
  }
6818
6825
  function formatLongRunningReminder(opts) {
6819
6826
  const invoke = formatDetachedJobInvoke(opts?.scriptPath);
6820
- return `Long jobs: \`${invoke} start <id> -- <cmd>\`, loop wait_for_job (or detached-job wait). The log pane updates from wait JSON; present_artifact type=log is optional. stop_job if hanging or wrong. Do not say you will let the user know later \u2014 stay in the turn.`;
6827
+ return `Long jobs: \`${invoke} start <id> -- <cmd>\`, loop wait_for_job (or detached-job wait). The log pane updates from wait JSON; present_artifact type=log is optional. stop_job if hanging or wrong. Do not say you will let the user know later \u2014 stay in the turn. Project Dev button: list_run_scripts / run_dev_script / stop_dev_script (UI-connected \u2014 not detached-job).`;
6821
6828
  }
6822
6829
  function formatArtifactDirective() {
6823
6830
  return [
@@ -6829,7 +6836,7 @@ function formatArtifactDirective() {
6829
6836
  ].join("\n");
6830
6837
  }
6831
6838
  function formatUiReminder() {
6832
- return "Sideboard UI: markdown table is enough to read data; present_schema if they ask to edit/filter (even after markdown); present_files for the file manager. html fence or present_artifact, not both for the same document. type=log appends (same artifact_id, new lines only). ask_user only for a real multiple-choice (not hellos, \u201Cwhat next?\u201D, or after a review) \u2014 reply in chat. Do not say artifacts/CMS UI are unavailable.";
6839
+ return "Sideboard UI: markdown table is enough to read data; present_schema if they ask to edit/filter (even after markdown); present_files for the file manager. html fence or present_artifact, not both for the same document. type=log appends (same artifact_id, new lines only). ask_user only for a real multiple-choice (not hellos, \u201Cwhat next?\u201D, or after a review) \u2014 reply in chat. Do not say artifacts/CMS UI are unavailable. Dev / preview: run_dev_script (same as the Dev button); stop_dev_script to stop.";
6833
6840
  }
6834
6841
 
6835
6842
  // src/review/review-write-gate.ts
@@ -7400,7 +7407,7 @@ function formatScheduledPrompt(name, prompt) {
7400
7407
  ${prompt}`;
7401
7408
  }
7402
7409
  async function defaultDeps() {
7403
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-QUJORSTA.js");
7410
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-ZT3SJVKF.js");
7404
7411
  const orch = getOrchestrator2();
7405
7412
  return {
7406
7413
  findThread: (id) => findThreadByRef(id),
@@ -7505,6 +7512,7 @@ function armSchedules() {
7505
7512
 
7506
7513
  // src/orchestrator/orchestrator.ts
7507
7514
  var LIVE_TURN_SPAWN_GRACE_MS = 15e3;
7515
+ var RUN_SCRIPT_DESKTOP_ADOPT_MS = 2e4;
7508
7516
  function isPidAlive(pid) {
7509
7517
  if (!Number.isFinite(pid) || pid <= 0) return false;
7510
7518
  try {
@@ -7583,6 +7591,11 @@ var Orchestrator = class {
7583
7591
  jobContinueNudged = /* @__PURE__ */ new Set();
7584
7592
  /** In-flight startDev per worktree run key — agent MCP + UI Start must not double-spawn. */
7585
7593
  startingDev = /* @__PURE__ */ new Map();
7594
+ /**
7595
+ * MCP/CLI poll budget while Electron main adopts `runScriptRequest`.
7596
+ * Tests shorten this so a missing desktop does not wait 20s.
7597
+ */
7598
+ runScriptAdoptTimeoutMs = RUN_SCRIPT_DESKTOP_ADOPT_MS;
7586
7599
  maxConcurrent;
7587
7600
  runningCount = 0;
7588
7601
  constructor(opts) {
@@ -7717,7 +7730,7 @@ var Orchestrator = class {
7717
7730
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
7718
7731
  });
7719
7732
  }
7720
- const { autoCleanupOrphansEnabled } = await import("./app-settings-V2BBCX2H.js");
7733
+ const { autoCleanupOrphansEnabled } = await import("./app-settings-UXWCPDEA.js");
7721
7734
  if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
7722
7735
  await cleanupOrphanWorktrees({ repoPaths });
7723
7736
  }
@@ -7752,6 +7765,58 @@ var Orchestrator = class {
7752
7765
  this.armDrain(thread.id);
7753
7766
  }
7754
7767
  }
7768
+ this.adoptPersistedRunScripts();
7769
+ }
7770
+ /**
7771
+ * Adopt run-script start/stop persisted by MCP/CLI while this process owns
7772
+ * live children (desktop host). Spawning in stdio leaves the Run tab on
7773
+ * "Starting…" with no logs — `run_output` never reaches the renderer.
7774
+ */
7775
+ adoptPersistedRunScripts() {
7776
+ if (!this.shouldOwnRunScripts()) return;
7777
+ for (const thread of listThreads()) {
7778
+ if (thread.status === "archived") continue;
7779
+ const req = thread.runScriptRequest;
7780
+ if (!isUnclaimedRunScriptRequest(req) || !req) continue;
7781
+ const claimedAt = (/* @__PURE__ */ new Date()).toISOString();
7782
+ const claimed = { ...req, claimedAt };
7783
+ updateThread(thread.id, { runScriptRequest: claimed });
7784
+ void this.applyRunScriptRequest(thread.id, claimed);
7785
+ }
7786
+ }
7787
+ async applyRunScriptRequest(threadId, req) {
7788
+ try {
7789
+ if (req.op === "start") {
7790
+ await this.startDev(threadId, req.scriptName ?? void 0);
7791
+ } else {
7792
+ await this.stopDev(threadId, req.scriptName ?? void 0);
7793
+ }
7794
+ const latest = readThread(threadId);
7795
+ const current = latest?.runScriptRequest;
7796
+ if (current?.requestId === req.requestId && !current.error) {
7797
+ updateThread(threadId, {
7798
+ runScriptRequest: {
7799
+ ...current,
7800
+ fulfilledAt: (/* @__PURE__ */ new Date()).toISOString()
7801
+ }
7802
+ });
7803
+ }
7804
+ } catch (err) {
7805
+ const message = err instanceof Error ? err.message : String(err);
7806
+ const latest = readThread(threadId);
7807
+ const current = latest?.runScriptRequest;
7808
+ if (!current || current.requestId !== req.requestId) return;
7809
+ updateThread(threadId, {
7810
+ runScriptRequest: { ...current, error: message }
7811
+ });
7812
+ }
7813
+ }
7814
+ /**
7815
+ * Same rule as agent-queue drain: MCP stdio must not own Electron children
7816
+ * while Sideboard.app is alive.
7817
+ */
7818
+ shouldOwnRunScripts() {
7819
+ return thisProcessShouldDrainAgentQueues();
7755
7820
  }
7756
7821
  /**
7757
7822
  * Mid-session: a worktree can sit at `running` after the agent process dies
@@ -8004,7 +8069,7 @@ var Orchestrator = class {
8004
8069
  }
8005
8070
  await setup;
8006
8071
  if (skipSetup) return;
8007
- const { autoRunAfterSetupEnabled } = await import("./app-settings-V2BBCX2H.js");
8072
+ const { autoRunAfterSetupEnabled } = await import("./app-settings-UXWCPDEA.js");
8008
8073
  if (autoRunAfterSetupEnabled()) {
8009
8074
  try {
8010
8075
  await this.startDev(threadId);
@@ -8360,7 +8425,7 @@ var Orchestrator = class {
8360
8425
  }
8361
8426
  const isBrightsy = fresh.agent === "brightsy";
8362
8427
  const isOrchestration = isOrchestratorThread(fresh);
8363
- const { autoRenameBranchEnabled, getGithubGitAuthMode, gitBranchPrefixSetting } = await import("./app-settings-V2BBCX2H.js");
8428
+ const { autoRenameBranchEnabled, getGithubGitAuthMode, gitBranchPrefixSetting } = await import("./app-settings-UXWCPDEA.js");
8364
8429
  const gitAuthMode = getGithubGitAuthMode();
8365
8430
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
8366
8431
  githubSlug: await withTimeout(
@@ -8800,6 +8865,13 @@ var Orchestrator = class {
8800
8865
  "No run script found for this worktree. Add [scripts.run.*] in the worktree\u2019s .sideboard/settings.toml or .conductor/settings.toml (or settings.local.toml on the main repo)."
8801
8866
  );
8802
8867
  }
8868
+ if (!this.shouldOwnRunScripts()) {
8869
+ const result = await this.requestDesktopRunScript(thread, "start", resolvedName);
8870
+ if (!result) {
8871
+ throw new Error("Desktop did not return a run script port.");
8872
+ }
8873
+ return result;
8874
+ }
8803
8875
  const runKey = worktreeRunProcessKey(thread.worktreePath, resolvedName);
8804
8876
  const inFlight = this.startingDev.get(runKey);
8805
8877
  if (inFlight) return inFlight;
@@ -8922,8 +8994,65 @@ var Orchestrator = class {
8922
8994
  });
8923
8995
  return { port: handle.port, scriptName: resolvedName, ports: handle.ports };
8924
8996
  }
8925
- stopDev(threadRef, scriptName) {
8997
+ /**
8998
+ * Persist a run-script intent for the desktop host (MCP/CLI while the board
8999
+ * is alive). Polls until Electron main adopts and fulfills it.
9000
+ */
9001
+ async requestDesktopRunScript(thread, op, scriptName) {
9002
+ const latest = readThread(thread.id) ?? thread;
9003
+ const pending = latest.runScriptRequest;
9004
+ const claimedAt = pending?.claimedAt;
9005
+ const staleClaim = typeof claimedAt === "string" && Date.now() - Date.parse(claimedAt) > this.runScriptAdoptTimeoutMs;
9006
+ const sameIntent = pending && pending.op === op && (pending.scriptName ?? null) === (scriptName ?? null) && !pending.error && !pending.fulfilledAt && !staleClaim;
9007
+ const request = sameIntent ? pending : {
9008
+ op,
9009
+ scriptName: scriptName ?? null,
9010
+ requestId: randomUUID5(),
9011
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString()
9012
+ };
9013
+ if (!sameIntent) {
9014
+ updateThread(thread.id, { runScriptRequest: request });
9015
+ }
9016
+ return this.waitForDesktopRunScript(thread.id, request, op, scriptName);
9017
+ }
9018
+ async waitForDesktopRunScript(threadId, request, op, scriptName) {
9019
+ const deadline = Date.now() + this.runScriptAdoptTimeoutMs;
9020
+ while (Date.now() < deadline) {
9021
+ const latest = readThread(threadId);
9022
+ if (!latest) {
9023
+ throw new Error("Thread disappeared while waiting for the desktop run script.");
9024
+ }
9025
+ const req = latest.runScriptRequest;
9026
+ if (req?.requestId === request.requestId && req.error) {
9027
+ throw new Error(req.error);
9028
+ }
9029
+ const shared = mergeWorktreeActiveRuns(threadsSharingWorktree(latest.worktreePath));
9030
+ const run2 = scriptName ? shared.activeRuns.find((r) => r.scriptName === scriptName) : shared.activeRuns[0];
9031
+ const fulfilled = req?.requestId === request.requestId && Boolean(req.fulfilledAt);
9032
+ const requestGone = !req || req.requestId !== request.requestId;
9033
+ if (op === "start") {
9034
+ if (run2 && (fulfilled || requestGone || run2.startedAt >= request.requestedAt)) {
9035
+ return { port: run2.port, scriptName: run2.scriptName, ports: run2.ports };
9036
+ }
9037
+ if (fulfilled && !run2) {
9038
+ throw new Error("Desktop reported the run script started, but it is not active.");
9039
+ }
9040
+ } else {
9041
+ const gone = scriptName ? !run2 : shared.activeRuns.length === 0;
9042
+ if (fulfilled || gone) return null;
9043
+ }
9044
+ await new Promise((r) => setTimeout(r, 40));
9045
+ }
9046
+ throw new Error(
9047
+ op === "start" ? "Desktop did not start the run script in time. Is Sideboard.app running this worktree?" : "Desktop did not stop the run script in time."
9048
+ );
9049
+ }
9050
+ async stopDev(threadRef, scriptName) {
8926
9051
  const thread = this.requireThread(threadRef);
9052
+ if (!this.shouldOwnRunScripts()) {
9053
+ await this.requestDesktopRunScript(thread, "stop", scriptName);
9054
+ return;
9055
+ }
8927
9056
  const siblings = threadsSharingWorktree(thread.worktreePath);
8928
9057
  const shared = mergeWorktreeActiveRuns(siblings);
8929
9058
  if (scriptName) {
@@ -9504,7 +9633,7 @@ var Orchestrator = class {
9504
9633
  if (!titled.userSetTitle && meta.title && titled.title !== meta.title) {
9505
9634
  updateThread(thread.id, { title: meta.title });
9506
9635
  }
9507
- const { autoArchiveOnMergeEnabled } = await import("./app-settings-V2BBCX2H.js");
9636
+ const { autoArchiveOnMergeEnabled } = await import("./app-settings-UXWCPDEA.js");
9508
9637
  const latest = this.requireThread(thread.id);
9509
9638
  if (!shouldAutoArchiveOnPrMerge({
9510
9639
  previousPrState: prevState || null,
@@ -9786,7 +9915,7 @@ var Orchestrator = class {
9786
9915
  this.enforceHistoryRetention();
9787
9916
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
9788
9917
  try {
9789
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-76W7IEKR.js");
9918
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-73Y7IJY2.js");
9790
9919
  await ensureWorkspace2(thread.repoPath);
9791
9920
  } catch {
9792
9921
  }
@@ -9813,7 +9942,7 @@ var Orchestrator = class {
9813
9942
  await runArchiveScript(thread.repoPath, thread.worktreePath);
9814
9943
  } catch {
9815
9944
  }
9816
- const { deleteBranchOnPurgeEnabled } = await import("./app-settings-V2BBCX2H.js");
9945
+ const { deleteBranchOnPurgeEnabled } = await import("./app-settings-UXWCPDEA.js");
9817
9946
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
9818
9947
  await removeWorktree(thread.repoPath, thread.worktreePath, {
9819
9948
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -9840,7 +9969,7 @@ var Orchestrator = class {
9840
9969
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
9841
9970
  );
9842
9971
  }
9843
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-7V67XTEO.js");
9972
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-36VEFUJN.js");
9844
9973
  const slug = thread.worktreePath.split("/").pop();
9845
9974
  const dest = thread.worktreePath;
9846
9975
  await withRepoGitLock(thread.repoPath, async () => {
@@ -9950,7 +10079,7 @@ async function startOrchestration(opts) {
9950
10079
  sourceRef: "default",
9951
10080
  ...createOpts
9952
10081
  }).catch(async () => {
9953
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-7V67XTEO.js");
10082
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-36VEFUJN.js");
9954
10083
  const repo = await resolveRepoRoot2(repoPath);
9955
10084
  const def = await resolveDefaultBranch2(repo);
9956
10085
  return orch.createThread({
@@ -9960,7 +10089,7 @@ async function startOrchestration(opts) {
9960
10089
  repoPath: repo
9961
10090
  });
9962
10091
  });
9963
- const { updateThread: upd } = await import("./thread-store-N6WQIWZ4.js");
10092
+ const { updateThread: upd } = await import("./thread-store-IKJD4EOK.js");
9964
10093
  const updated = upd(thread.id, {
9965
10094
  sourceType: "orchestration",
9966
10095
  sourceRef: goal
@@ -9972,6 +10101,7 @@ async function startOrchestration(opts) {
9972
10101
  }
9973
10102
 
9974
10103
  export {
10104
+ isUnclaimedRunScriptRequest,
9975
10105
  isHomeBoardThread,
9976
10106
  BOARD_COLUMN_DEFS,
9977
10107
  HOME_BOARD_CACHE_TTL_MS,
@@ -10220,6 +10350,7 @@ export {
10220
10350
  formatOptionalServicesDirective,
10221
10351
  formatOptionalServicesReminder,
10222
10352
  LIVE_TURN_SPAWN_GRACE_MS,
10353
+ RUN_SCRIPT_DESKTOP_ADOPT_MS,
10223
10354
  isPidAlive,
10224
10355
  waitForPidExit,
10225
10356
  resolveSendFollowUp,
@@ -568,6 +568,21 @@ var DEFAULT_AGENTS = /* @__PURE__ */ new Set([
568
568
  ]);
569
569
  var GITHUB_GIT_AUTH_MODES = ["auto", "gh", "ssh", "token"];
570
570
  var FOLLOW_UP_BEHAVIORS = ["steer", "queue"];
571
+ var AGENT_DONE_SOUNDS = [
572
+ "none",
573
+ "goal",
574
+ "bell",
575
+ "level",
576
+ "coin",
577
+ "fanfare",
578
+ "magic",
579
+ "rooster",
580
+ "whistle",
581
+ "custom"
582
+ ];
583
+ function isAgentDoneSound(value) {
584
+ return AGENT_DONE_SOUNDS.includes(value);
585
+ }
571
586
  function emptyPublicIntegrations() {
572
587
  return {
573
588
  hasLinearApiKey: false,
@@ -1006,6 +1021,13 @@ function normalizeAdvanced(raw) {
1006
1021
  if (source.followUpBehavior === "queue" || source.followUpBehavior === "steer") {
1007
1022
  out.followUpBehavior = source.followUpBehavior;
1008
1023
  }
1024
+ if (isAgentDoneSound(source.agentDoneSound)) {
1025
+ out.agentDoneSound = source.agentDoneSound;
1026
+ }
1027
+ if (typeof source.agentDoneCustomSoundName === "string") {
1028
+ const name = source.agentDoneCustomSoundName.trim();
1029
+ if (name) out.agentDoneCustomSoundName = name.slice(0, 200);
1030
+ }
1009
1031
  return out;
1010
1032
  }
1011
1033
  function normalizeSettings(raw) {
@@ -1929,6 +1951,16 @@ function updateAdvancedSettings(patch) {
1929
1951
  if (patch.followUpBehavior === "queue" || patch.followUpBehavior === "steer") {
1930
1952
  advanced.followUpBehavior = patch.followUpBehavior;
1931
1953
  }
1954
+ if (isAgentDoneSound(patch.agentDoneSound)) {
1955
+ advanced.agentDoneSound = patch.agentDoneSound;
1956
+ }
1957
+ if (patch.agentDoneCustomSoundName === null || patch.agentDoneCustomSoundName === "") {
1958
+ delete advanced.agentDoneCustomSoundName;
1959
+ } else if (typeof patch.agentDoneCustomSoundName === "string") {
1960
+ const name = patch.agentDoneCustomSoundName.trim();
1961
+ if (name) advanced.agentDoneCustomSoundName = name.slice(0, 200);
1962
+ else delete advanced.agentDoneCustomSoundName;
1963
+ }
1932
1964
  return saveAppSettings({ ...current, advanced });
1933
1965
  }
1934
1966
  function autoRenameBranchEnabled(settings = loadAppSettings()) {
@@ -1996,6 +2028,9 @@ function orchestrationQuotaFallbackAgent(settings = loadAppSettings()) {
1996
2028
  function followUpBehavior(settings = loadAppSettings()) {
1997
2029
  return settings.advanced.followUpBehavior === "queue" ? "queue" : "steer";
1998
2030
  }
2031
+ function agentDoneSound(settings = loadAppSettings()) {
2032
+ return isAgentDoneSound(settings.advanced.agentDoneSound) ? settings.advanced.agentDoneSound : "none";
2033
+ }
1999
2034
  function maxConcurrentAgents(settings = loadAppSettings()) {
2000
2035
  const n = settings.advanced.maxConcurrent;
2001
2036
  if (typeof n === "number" && Number.isFinite(n)) {
@@ -2140,6 +2175,8 @@ export {
2140
2175
  HARNESS_ENV_KEYS,
2141
2176
  GITHUB_GIT_AUTH_MODES,
2142
2177
  FOLLOW_UP_BEHAVIORS,
2178
+ AGENT_DONE_SOUNDS,
2179
+ isAgentDoneSound,
2143
2180
  emptyPublicIntegrations,
2144
2181
  toPublicAppSettings,
2145
2182
  appSettingsPath,
@@ -2208,6 +2245,7 @@ export {
2208
2245
  orchestrationQuotaOnLimit,
2209
2246
  orchestrationQuotaFallbackAgent,
2210
2247
  followUpBehavior,
2248
+ agentDoneSound,
2211
2249
  maxConcurrentAgents,
2212
2250
  resolveClaudeExecutable,
2213
2251
  resolveAgentExecutable,