@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
@@ -42,7 +42,7 @@ import {
42
42
  summarizeTurnStderr,
43
43
  toolDescription,
44
44
  turnFailChatText
45
- } from "./chunk-SQHRPJ7P.js";
45
+ } from "./chunk-6L25K7BE.js";
46
46
  import {
47
47
  SLACK_REPLY_FORMATTING,
48
48
  addWorkspace,
@@ -65,7 +65,7 @@ import {
65
65
  stageAbsolutePathsAsAttachments,
66
66
  stageBuffersAsAttachments,
67
67
  syncWorkspacesFromThreads
68
- } from "./chunk-P2EAAM3H.js";
68
+ } from "./chunk-UNTOZA3C.js";
69
69
  import {
70
70
  addPrStackLayer,
71
71
  allocateTeamName,
@@ -114,7 +114,7 @@ import {
114
114
  worktreeDisplayLabelForGroup,
115
115
  worktreeNameFromPath,
116
116
  worktreeSlugForTicket
117
- } from "./chunk-NFWPDEN4.js";
117
+ } from "./chunk-4CZ25DO2.js";
118
118
  import {
119
119
  ATTACHMENTS_DIR,
120
120
  DETACHED_JOBS_DIR,
@@ -132,7 +132,7 @@ import {
132
132
  updateThread,
133
133
  withThreadLock,
134
134
  writeThread
135
- } from "./chunk-HY2GFZ5S.js";
135
+ } from "./chunk-XJETIO4K.js";
136
136
  import {
137
137
  ensureWorktreeSideboardIgnored
138
138
  } from "./chunk-SZJZEFTY.js";
@@ -177,7 +177,7 @@ import {
177
177
  updateAdvancedSettings,
178
178
  usageOnLimit,
179
179
  writeSecureJson
180
- } from "./chunk-Y5JES2VV.js";
180
+ } from "./chunk-RZTRWN2V.js";
181
181
  import {
182
182
  writePrivateFile
183
183
  } from "./chunk-NSTQ6QKD.js";
@@ -418,7 +418,7 @@ async function continueSourceThread(threadId, prompt) {
418
418
  await continueOnReply(threadId, prompt);
419
419
  return;
420
420
  }
421
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-6XEWQWSM.js");
421
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-HMJPNUN5.js");
422
422
  await getOrchestrator2().send(threadId, prompt);
423
423
  } catch {
424
424
  }
@@ -678,6 +678,7 @@ async function pollSlackOutboundWatches(opts) {
678
678
  }
679
679
 
680
680
  // src/orchestrator/orchestrator.ts
681
+ import { randomUUID as randomUUID5 } from "crypto";
681
682
  import { existsSync as existsSync19 } from "fs";
682
683
 
683
684
  // src/agents/spawn.ts
@@ -895,9 +896,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
895
896
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
896
897
  );
897
898
  }
898
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-QPSJEXGR.js");
899
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-EYZKYHAO.js");
899
900
  if (isGlobalThread2(thread)) {
900
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-VZ7OSRLD.js");
901
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-YKLXQTWH.js");
901
902
  ensureGlobalCoordinatorCwd2(
902
903
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
903
904
  );
@@ -2895,6 +2896,11 @@ async function cloneRepoIntoSideboard(opts) {
2895
2896
  return { repoPath, workspace };
2896
2897
  }
2897
2898
 
2899
+ // src/types/thread.ts
2900
+ function isUnclaimedRunScriptRequest(req) {
2901
+ return Boolean(req && !req.claimedAt && !req.fulfilledAt && !req.error);
2902
+ }
2903
+
2898
2904
  // src/store/desktop-host.ts
2899
2905
  import { readFileSync as readFileSync6, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
2900
2906
  import { join as join11 } from "path";
@@ -3918,7 +3924,7 @@ async function createThread(input, _onSetupLine) {
3918
3924
  });
3919
3925
  await requireAgent(resolved.agent);
3920
3926
  if (input.cowboy) {
3921
- const { cowboyModeEnabled } = await import("./app-settings-5RQMQKSB.js");
3927
+ const { cowboyModeEnabled } = await import("./app-settings-KBL2G7M4.js");
3922
3928
  if (!cowboyModeEnabled()) {
3923
3929
  throw new Error(
3924
3930
  "Cowboy mode is off. Enable it in Settings \u2192 Advanced."
@@ -3976,7 +3982,7 @@ async function createThread(input, _onSetupLine) {
3976
3982
  let sourceIsFork = false;
3977
3983
  if (!input.cowboy && sourceType === "branch" && (!sourceRef || sourceRef === "HEAD" || sourceRef === "default")) {
3978
3984
  if (isAbleTimeConnected() && getIssueSource() === "abletime") {
3979
- const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-BF253OZT.js");
3985
+ const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-WZNDTRVL.js");
3980
3986
  const task = await ensureAbleTimeTask({
3981
3987
  title: title?.trim() || "Untitled work",
3982
3988
  description: input.prompt?.trim() || void 0
@@ -4539,7 +4545,7 @@ async function adoptThread(input) {
4539
4545
  messages: input.messages ?? []
4540
4546
  });
4541
4547
  writeThread(thread);
4542
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-TDBA64II.js");
4548
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-FQCSCTNH.js");
4543
4549
  await ensureWorkspace2(repoPath);
4544
4550
  const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-K7QK4RI5.js");
4545
4551
  await ensureWorktreeSideboardIgnored2(input.worktreePath);
@@ -6597,12 +6603,13 @@ function formatLongRunningDirective(opts) {
6597
6603
  "- 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>`.",
6598
6604
  "- 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.",
6599
6605
  "- 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.",
6600
- "State: `.context/.sideboard/detached-jobs/<id>/` (local scratch). Full guide: `/long-running` (always available)."
6606
+ "State: `.context/.sideboard/detached-jobs/<id>/` (local scratch). Full guide: `/long-running` (always available).",
6607
+ "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."
6601
6608
  ].join("\n");
6602
6609
  }
6603
6610
  function formatLongRunningReminder(opts) {
6604
6611
  const invoke = formatDetachedJobInvoke(opts?.scriptPath);
6605
- 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.`;
6612
+ 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).`;
6606
6613
  }
6607
6614
  function formatArtifactDirective() {
6608
6615
  return [
@@ -6614,7 +6621,7 @@ function formatArtifactDirective() {
6614
6621
  ].join("\n");
6615
6622
  }
6616
6623
  function formatUiReminder() {
6617
- 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.";
6624
+ 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.";
6618
6625
  }
6619
6626
 
6620
6627
  // src/review/review-write-gate.ts
@@ -7014,7 +7021,7 @@ function formatScheduledPrompt(name, prompt) {
7014
7021
  ${prompt}`;
7015
7022
  }
7016
7023
  async function defaultDeps() {
7017
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-6XEWQWSM.js");
7024
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-HMJPNUN5.js");
7018
7025
  const orch = getOrchestrator2();
7019
7026
  return {
7020
7027
  findThread: (id) => findThreadByRef(id),
@@ -7119,6 +7126,7 @@ function armSchedules() {
7119
7126
 
7120
7127
  // src/orchestrator/orchestrator.ts
7121
7128
  var LIVE_TURN_SPAWN_GRACE_MS = 15e3;
7129
+ var RUN_SCRIPT_DESKTOP_ADOPT_MS = 2e4;
7122
7130
  function isPidAlive(pid) {
7123
7131
  if (!Number.isFinite(pid) || pid <= 0) return false;
7124
7132
  try {
@@ -7197,6 +7205,11 @@ var Orchestrator = class {
7197
7205
  jobContinueNudged = /* @__PURE__ */ new Set();
7198
7206
  /** In-flight startDev per worktree run key — agent MCP + UI Start must not double-spawn. */
7199
7207
  startingDev = /* @__PURE__ */ new Map();
7208
+ /**
7209
+ * MCP/CLI poll budget while Electron main adopts `runScriptRequest`.
7210
+ * Tests shorten this so a missing desktop does not wait 20s.
7211
+ */
7212
+ runScriptAdoptTimeoutMs = RUN_SCRIPT_DESKTOP_ADOPT_MS;
7200
7213
  maxConcurrent;
7201
7214
  runningCount = 0;
7202
7215
  constructor(opts) {
@@ -7331,7 +7344,7 @@ var Orchestrator = class {
7331
7344
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
7332
7345
  });
7333
7346
  }
7334
- const { autoCleanupOrphansEnabled } = await import("./app-settings-5RQMQKSB.js");
7347
+ const { autoCleanupOrphansEnabled } = await import("./app-settings-KBL2G7M4.js");
7335
7348
  if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
7336
7349
  await cleanupOrphanWorktrees({ repoPaths });
7337
7350
  }
@@ -7366,6 +7379,58 @@ var Orchestrator = class {
7366
7379
  this.armDrain(thread.id);
7367
7380
  }
7368
7381
  }
7382
+ this.adoptPersistedRunScripts();
7383
+ }
7384
+ /**
7385
+ * Adopt run-script start/stop persisted by MCP/CLI while this process owns
7386
+ * live children (desktop host). Spawning in stdio leaves the Run tab on
7387
+ * "Starting…" with no logs — `run_output` never reaches the renderer.
7388
+ */
7389
+ adoptPersistedRunScripts() {
7390
+ if (!this.shouldOwnRunScripts()) return;
7391
+ for (const thread of listThreads()) {
7392
+ if (thread.status === "archived") continue;
7393
+ const req = thread.runScriptRequest;
7394
+ if (!isUnclaimedRunScriptRequest(req) || !req) continue;
7395
+ const claimedAt = (/* @__PURE__ */ new Date()).toISOString();
7396
+ const claimed = { ...req, claimedAt };
7397
+ updateThread(thread.id, { runScriptRequest: claimed });
7398
+ void this.applyRunScriptRequest(thread.id, claimed);
7399
+ }
7400
+ }
7401
+ async applyRunScriptRequest(threadId, req) {
7402
+ try {
7403
+ if (req.op === "start") {
7404
+ await this.startDev(threadId, req.scriptName ?? void 0);
7405
+ } else {
7406
+ await this.stopDev(threadId, req.scriptName ?? void 0);
7407
+ }
7408
+ const latest = readThread(threadId);
7409
+ const current = latest?.runScriptRequest;
7410
+ if (current?.requestId === req.requestId && !current.error) {
7411
+ updateThread(threadId, {
7412
+ runScriptRequest: {
7413
+ ...current,
7414
+ fulfilledAt: (/* @__PURE__ */ new Date()).toISOString()
7415
+ }
7416
+ });
7417
+ }
7418
+ } catch (err) {
7419
+ const message = err instanceof Error ? err.message : String(err);
7420
+ const latest = readThread(threadId);
7421
+ const current = latest?.runScriptRequest;
7422
+ if (!current || current.requestId !== req.requestId) return;
7423
+ updateThread(threadId, {
7424
+ runScriptRequest: { ...current, error: message }
7425
+ });
7426
+ }
7427
+ }
7428
+ /**
7429
+ * Same rule as agent-queue drain: MCP stdio must not own Electron children
7430
+ * while Sideboard.app is alive.
7431
+ */
7432
+ shouldOwnRunScripts() {
7433
+ return thisProcessShouldDrainAgentQueues();
7369
7434
  }
7370
7435
  /**
7371
7436
  * Mid-session: a worktree can sit at `running` after the agent process dies
@@ -7618,7 +7683,7 @@ var Orchestrator = class {
7618
7683
  }
7619
7684
  await setup;
7620
7685
  if (skipSetup) return;
7621
- const { autoRunAfterSetupEnabled } = await import("./app-settings-5RQMQKSB.js");
7686
+ const { autoRunAfterSetupEnabled } = await import("./app-settings-KBL2G7M4.js");
7622
7687
  if (autoRunAfterSetupEnabled()) {
7623
7688
  try {
7624
7689
  await this.startDev(threadId);
@@ -7974,7 +8039,7 @@ var Orchestrator = class {
7974
8039
  }
7975
8040
  const isBrightsy = fresh.agent === "brightsy";
7976
8041
  const isOrchestration = isOrchestratorThread(fresh);
7977
- const { autoRenameBranchEnabled, getGithubGitAuthMode, gitBranchPrefixSetting } = await import("./app-settings-5RQMQKSB.js");
8042
+ const { autoRenameBranchEnabled, getGithubGitAuthMode, gitBranchPrefixSetting } = await import("./app-settings-KBL2G7M4.js");
7978
8043
  const gitAuthMode = getGithubGitAuthMode();
7979
8044
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
7980
8045
  githubSlug: await withTimeout(
@@ -8414,6 +8479,13 @@ var Orchestrator = class {
8414
8479
  "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)."
8415
8480
  );
8416
8481
  }
8482
+ if (!this.shouldOwnRunScripts()) {
8483
+ const result = await this.requestDesktopRunScript(thread, "start", resolvedName);
8484
+ if (!result) {
8485
+ throw new Error("Desktop did not return a run script port.");
8486
+ }
8487
+ return result;
8488
+ }
8417
8489
  const runKey = worktreeRunProcessKey(thread.worktreePath, resolvedName);
8418
8490
  const inFlight = this.startingDev.get(runKey);
8419
8491
  if (inFlight) return inFlight;
@@ -8536,8 +8608,65 @@ var Orchestrator = class {
8536
8608
  });
8537
8609
  return { port: handle.port, scriptName: resolvedName, ports: handle.ports };
8538
8610
  }
8539
- stopDev(threadRef, scriptName) {
8611
+ /**
8612
+ * Persist a run-script intent for the desktop host (MCP/CLI while the board
8613
+ * is alive). Polls until Electron main adopts and fulfills it.
8614
+ */
8615
+ async requestDesktopRunScript(thread, op, scriptName) {
8616
+ const latest = readThread(thread.id) ?? thread;
8617
+ const pending = latest.runScriptRequest;
8618
+ const claimedAt = pending?.claimedAt;
8619
+ const staleClaim = typeof claimedAt === "string" && Date.now() - Date.parse(claimedAt) > this.runScriptAdoptTimeoutMs;
8620
+ const sameIntent = pending && pending.op === op && (pending.scriptName ?? null) === (scriptName ?? null) && !pending.error && !pending.fulfilledAt && !staleClaim;
8621
+ const request = sameIntent ? pending : {
8622
+ op,
8623
+ scriptName: scriptName ?? null,
8624
+ requestId: randomUUID5(),
8625
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString()
8626
+ };
8627
+ if (!sameIntent) {
8628
+ updateThread(thread.id, { runScriptRequest: request });
8629
+ }
8630
+ return this.waitForDesktopRunScript(thread.id, request, op, scriptName);
8631
+ }
8632
+ async waitForDesktopRunScript(threadId, request, op, scriptName) {
8633
+ const deadline = Date.now() + this.runScriptAdoptTimeoutMs;
8634
+ while (Date.now() < deadline) {
8635
+ const latest = readThread(threadId);
8636
+ if (!latest) {
8637
+ throw new Error("Thread disappeared while waiting for the desktop run script.");
8638
+ }
8639
+ const req = latest.runScriptRequest;
8640
+ if (req?.requestId === request.requestId && req.error) {
8641
+ throw new Error(req.error);
8642
+ }
8643
+ const shared = mergeWorktreeActiveRuns(threadsSharingWorktree(latest.worktreePath));
8644
+ const run2 = scriptName ? shared.activeRuns.find((r) => r.scriptName === scriptName) : shared.activeRuns[0];
8645
+ const fulfilled = req?.requestId === request.requestId && Boolean(req.fulfilledAt);
8646
+ const requestGone = !req || req.requestId !== request.requestId;
8647
+ if (op === "start") {
8648
+ if (run2 && (fulfilled || requestGone || run2.startedAt >= request.requestedAt)) {
8649
+ return { port: run2.port, scriptName: run2.scriptName, ports: run2.ports };
8650
+ }
8651
+ if (fulfilled && !run2) {
8652
+ throw new Error("Desktop reported the run script started, but it is not active.");
8653
+ }
8654
+ } else {
8655
+ const gone = scriptName ? !run2 : shared.activeRuns.length === 0;
8656
+ if (fulfilled || gone) return null;
8657
+ }
8658
+ await new Promise((r) => setTimeout(r, 40));
8659
+ }
8660
+ throw new Error(
8661
+ 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."
8662
+ );
8663
+ }
8664
+ async stopDev(threadRef, scriptName) {
8540
8665
  const thread = this.requireThread(threadRef);
8666
+ if (!this.shouldOwnRunScripts()) {
8667
+ await this.requestDesktopRunScript(thread, "stop", scriptName);
8668
+ return;
8669
+ }
8541
8670
  const siblings = threadsSharingWorktree(thread.worktreePath);
8542
8671
  const shared = mergeWorktreeActiveRuns(siblings);
8543
8672
  if (scriptName) {
@@ -9118,7 +9247,7 @@ var Orchestrator = class {
9118
9247
  if (!titled.userSetTitle && meta.title && titled.title !== meta.title) {
9119
9248
  updateThread(thread.id, { title: meta.title });
9120
9249
  }
9121
- const { autoArchiveOnMergeEnabled } = await import("./app-settings-5RQMQKSB.js");
9250
+ const { autoArchiveOnMergeEnabled } = await import("./app-settings-KBL2G7M4.js");
9122
9251
  const latest = this.requireThread(thread.id);
9123
9252
  if (!shouldAutoArchiveOnPrMerge({
9124
9253
  previousPrState: prevState || null,
@@ -9400,7 +9529,7 @@ var Orchestrator = class {
9400
9529
  this.enforceHistoryRetention();
9401
9530
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
9402
9531
  try {
9403
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-TDBA64II.js");
9532
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-FQCSCTNH.js");
9404
9533
  await ensureWorkspace2(thread.repoPath);
9405
9534
  } catch {
9406
9535
  }
@@ -9427,7 +9556,7 @@ var Orchestrator = class {
9427
9556
  await runArchiveScript(thread.repoPath, thread.worktreePath);
9428
9557
  } catch {
9429
9558
  }
9430
- const { deleteBranchOnPurgeEnabled } = await import("./app-settings-5RQMQKSB.js");
9559
+ const { deleteBranchOnPurgeEnabled } = await import("./app-settings-KBL2G7M4.js");
9431
9560
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
9432
9561
  await removeWorktree(thread.repoPath, thread.worktreePath, {
9433
9562
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -9454,7 +9583,7 @@ var Orchestrator = class {
9454
9583
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
9455
9584
  );
9456
9585
  }
9457
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-I573UOPD.js");
9586
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-DOPRKG7K.js");
9458
9587
  const slug = thread.worktreePath.split("/").pop();
9459
9588
  const dest = thread.worktreePath;
9460
9589
  await withRepoGitLock(thread.repoPath, async () => {
@@ -9564,7 +9693,7 @@ async function startOrchestration(opts) {
9564
9693
  sourceRef: "default",
9565
9694
  ...createOpts
9566
9695
  }).catch(async () => {
9567
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-I573UOPD.js");
9696
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-DOPRKG7K.js");
9568
9697
  const repo = await resolveRepoRoot2(repoPath);
9569
9698
  const def = await resolveDefaultBranch2(repo);
9570
9699
  return orch.createThread({
@@ -9574,7 +9703,7 @@ async function startOrchestration(opts) {
9574
9703
  repoPath: repo
9575
9704
  });
9576
9705
  });
9577
- const { updateThread: upd } = await import("./thread-store-KDPPXJJ3.js");
9706
+ const { updateThread: upd } = await import("./thread-store-FBK5SZ5P.js");
9578
9707
  const updated = upd(thread.id, {
9579
9708
  sourceType: "orchestration",
9580
9709
  sourceRef: goal
@@ -9594,6 +9723,7 @@ export {
9594
9723
  recordSlackOutboundWatch,
9595
9724
  pollSlackOutboundWatches,
9596
9725
  AGENT_GIT_ACTIONS,
9726
+ threadsSharingWorktree,
9597
9727
  mcpWaitForTurnTimeoutMs,
9598
9728
  mcpWaitStillRunningHint,
9599
9729
  mcpWaitFinishedHint,
@@ -9622,6 +9752,7 @@ export {
9622
9752
  deleteSchedule,
9623
9753
  fireSchedule,
9624
9754
  LIVE_TURN_SPAWN_GRACE_MS,
9755
+ RUN_SCRIPT_DESKTOP_ADOPT_MS,
9625
9756
  isPidAlive,
9626
9757
  waitForPidExit,
9627
9758
  resolveSendFollowUp,
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  getAbleTimeHost,
6
6
  isAbleTimeConnected
7
- } from "./chunk-EWHJNJHV.js";
7
+ } from "./chunk-I5NC7DWO.js";
8
8
 
9
9
  // src/integrations/abletime-mcp.ts
10
10
  var DEFAULT_ABLETIME_HOST = "https://track.abletime.com";
@@ -153,7 +153,7 @@ function textsFromUnknown(value) {
153
153
  return "";
154
154
  }
155
155
  async function abletimeMcpRequest(method, params, opts) {
156
- const rawToken = opts?.token ?? await import("./abletime-oauth-KQPVVBKA.js").then((m) => m.ensureAbleTimeAccessToken());
156
+ const rawToken = opts?.token ?? await import("./abletime-oauth-6M5GWEPX.js").then((m) => m.ensureAbleTimeAccessToken());
157
157
  if (!rawToken?.trim()) {
158
158
  throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
159
159
  }
@@ -224,9 +224,9 @@ async function callAbleTimeTool(name, args = {}, opts) {
224
224
  if (!opts?.token && !isAbleTimeConnected()) {
225
225
  throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
226
226
  }
227
- const rawToken = opts?.token ?? await import("./abletime-oauth-KQPVVBKA.js").then((m) => m.ensureAbleTimeAccessToken());
227
+ const rawToken = opts?.token ?? await import("./abletime-oauth-6M5GWEPX.js").then((m) => m.ensureAbleTimeAccessToken());
228
228
  if (rawToken && ableTimeUsesRest(rawToken)) {
229
- const { callAbleTimeRestTool } = await import("./abletime-rest-DAFPXZ7H.js");
229
+ const { callAbleTimeRestTool } = await import("./abletime-rest-F7XG5NGY.js");
230
230
  return callAbleTimeRestTool(name, args, { ...opts, token: rawToken });
231
231
  }
232
232
  const requestOpts = { ...opts, pm: opts?.pm ?? abletimeToolUsesPm(name) };
@@ -242,7 +242,7 @@ async function callAbleTimeTool(name, args = {}, opts) {
242
242
  return await call();
243
243
  }
244
244
  if (rawToken && ableTimeUsesRest(rawToken) && isAbleTimeRejectedCredential(message)) {
245
- const { callAbleTimeRestTool } = await import("./abletime-rest-DAFPXZ7H.js");
245
+ const { callAbleTimeRestTool } = await import("./abletime-rest-F7XG5NGY.js");
246
246
  return callAbleTimeRestTool(name, args, { ...opts, token: rawToken });
247
247
  }
248
248
  throw err instanceof Error ? new Error(rewriteAbleTimeError(err.message)) : err;
@@ -7,10 +7,10 @@ import {
7
7
  import {
8
8
  getGithubGitAuthMode,
9
9
  getGithubPat
10
- } from "./chunk-EWHJNJHV.js";
10
+ } from "./chunk-I5NC7DWO.js";
11
11
  import {
12
12
  listThreads
13
- } from "./chunk-3IMSQNTS.js";
13
+ } from "./chunk-4VGCG2HV.js";
14
14
  import {
15
15
  worktreesRoot
16
16
  } from "./chunk-WFUDY6PC.js";
@@ -4,14 +4,14 @@ import {
4
4
  normalizeAbleTimeCredential,
5
5
  normalizeAbleTimeHost,
6
6
  rewriteAbleTimeError
7
- } from "./chunk-646I442S.js";
7
+ } from "./chunk-MVLXDYN5.js";
8
8
  import {
9
9
  httpFetch
10
10
  } from "./chunk-N7WZ6NIN.js";
11
11
  import {
12
12
  getAbleTimeAccessToken,
13
13
  getAbleTimeHost
14
- } from "./chunk-EWHJNJHV.js";
14
+ } from "./chunk-I5NC7DWO.js";
15
15
 
16
16
  // src/integrations/abletime-rest.ts
17
17
  var ABLETIME_REST_PREFIX = "/api/public/v2";
@@ -2,17 +2,17 @@
2
2
 
3
3
  import {
4
4
  textFromAbleTimeDoc
5
- } from "./chunk-CLI5DVU4.js";
5
+ } from "./chunk-GHRZNPBK.js";
6
6
  import {
7
7
  assertAbleTimeCredential,
8
8
  callAbleTimeTool,
9
9
  normalizeAbleTimeHost,
10
10
  rewriteAbleTimeError
11
- } from "./chunk-HSYE5ID6.js";
11
+ } from "./chunk-AS4UN7QL.js";
12
12
  import {
13
13
  isAbleTimeConnected,
14
14
  saveAbleTimeConnection
15
- } from "./chunk-Y5JES2VV.js";
15
+ } from "./chunk-RZTRWN2V.js";
16
16
 
17
17
  // src/integrations/abletime.ts
18
18
  var CLOSED_STATES = /* @__PURE__ */ new Set([
@@ -550,6 +550,21 @@ var DEFAULT_AGENTS = /* @__PURE__ */ new Set([
550
550
  ]);
551
551
  var GITHUB_GIT_AUTH_MODES = ["auto", "gh", "ssh", "token"];
552
552
  var FOLLOW_UP_BEHAVIORS = ["steer", "queue"];
553
+ var AGENT_DONE_SOUNDS = [
554
+ "none",
555
+ "goal",
556
+ "bell",
557
+ "level",
558
+ "coin",
559
+ "fanfare",
560
+ "magic",
561
+ "rooster",
562
+ "whistle",
563
+ "custom"
564
+ ];
565
+ function isAgentDoneSound(value) {
566
+ return AGENT_DONE_SOUNDS.includes(value);
567
+ }
553
568
  function emptyPublicIntegrations() {
554
569
  return {
555
570
  hasLinearApiKey: false,
@@ -988,6 +1003,13 @@ function normalizeAdvanced(raw) {
988
1003
  if (source.followUpBehavior === "queue" || source.followUpBehavior === "steer") {
989
1004
  out.followUpBehavior = source.followUpBehavior;
990
1005
  }
1006
+ if (isAgentDoneSound(source.agentDoneSound)) {
1007
+ out.agentDoneSound = source.agentDoneSound;
1008
+ }
1009
+ if (typeof source.agentDoneCustomSoundName === "string") {
1010
+ const name = source.agentDoneCustomSoundName.trim();
1011
+ if (name) out.agentDoneCustomSoundName = name.slice(0, 200);
1012
+ }
991
1013
  return out;
992
1014
  }
993
1015
  function normalizeSettings(raw) {
@@ -1911,6 +1933,16 @@ function updateAdvancedSettings(patch) {
1911
1933
  if (patch.followUpBehavior === "queue" || patch.followUpBehavior === "steer") {
1912
1934
  advanced.followUpBehavior = patch.followUpBehavior;
1913
1935
  }
1936
+ if (isAgentDoneSound(patch.agentDoneSound)) {
1937
+ advanced.agentDoneSound = patch.agentDoneSound;
1938
+ }
1939
+ if (patch.agentDoneCustomSoundName === null || patch.agentDoneCustomSoundName === "") {
1940
+ delete advanced.agentDoneCustomSoundName;
1941
+ } else if (typeof patch.agentDoneCustomSoundName === "string") {
1942
+ const name = patch.agentDoneCustomSoundName.trim();
1943
+ if (name) advanced.agentDoneCustomSoundName = name.slice(0, 200);
1944
+ else delete advanced.agentDoneCustomSoundName;
1945
+ }
1914
1946
  return saveAppSettings({ ...current, advanced });
1915
1947
  }
1916
1948
  function autoRenameBranchEnabled(settings = loadAppSettings()) {
@@ -1978,6 +2010,9 @@ function orchestrationQuotaFallbackAgent(settings = loadAppSettings()) {
1978
2010
  function followUpBehavior(settings = loadAppSettings()) {
1979
2011
  return settings.advanced.followUpBehavior === "queue" ? "queue" : "steer";
1980
2012
  }
2013
+ function agentDoneSound(settings = loadAppSettings()) {
2014
+ return isAgentDoneSound(settings.advanced.agentDoneSound) ? settings.advanced.agentDoneSound : "none";
2015
+ }
1981
2016
  function maxConcurrentAgents(settings = loadAppSettings()) {
1982
2017
  const n = settings.advanced.maxConcurrent;
1983
2018
  if (typeof n === "number" && Number.isFinite(n)) {
@@ -2106,6 +2141,8 @@ export {
2106
2141
  HARNESS_ENV_KEYS,
2107
2142
  GITHUB_GIT_AUTH_MODES,
2108
2143
  FOLLOW_UP_BEHAVIORS,
2144
+ AGENT_DONE_SOUNDS,
2145
+ isAgentDoneSound,
2109
2146
  emptyPublicIntegrations,
2110
2147
  toPublicAppSettings,
2111
2148
  appSettingsPath,
@@ -2174,6 +2211,7 @@ export {
2174
2211
  orchestrationQuotaOnLimit,
2175
2212
  orchestrationQuotaFallbackAgent,
2176
2213
  followUpBehavior,
2214
+ agentDoneSound,
2177
2215
  maxConcurrentAgents,
2178
2216
  resolveClaudeExecutable,
2179
2217
  resolveAgentExecutable,
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  textFromAbleTimeDoc
3
- } from "./chunk-RRW2XWAY.js";
3
+ } from "./chunk-P6ZM5QJW.js";
4
4
  import {
5
5
  assertAbleTimeCredential,
6
6
  callAbleTimeTool,
7
7
  normalizeAbleTimeHost,
8
8
  rewriteAbleTimeError
9
- } from "./chunk-646I442S.js";
9
+ } from "./chunk-MVLXDYN5.js";
10
10
  import {
11
11
  isAbleTimeConnected,
12
12
  saveAbleTimeConnection
13
- } from "./chunk-EWHJNJHV.js";
13
+ } from "./chunk-I5NC7DWO.js";
14
14
 
15
15
  // src/integrations/abletime.ts
16
16
  var CLOSED_STATES = /* @__PURE__ */ new Set([
@@ -8,7 +8,7 @@ import {
8
8
  resolveRepoRoot,
9
9
  takenSlugsFromThread,
10
10
  teamSlugFromName
11
- } from "./chunk-NFWPDEN4.js";
11
+ } from "./chunk-4CZ25DO2.js";
12
12
  import {
13
13
  ATTACHMENTS_DIR,
14
14
  attachmentsGitignoreBody
@@ -18,7 +18,7 @@ import {
18
18
  listThreads,
19
19
  updateThread,
20
20
  writeThread
21
- } from "./chunk-HY2GFZ5S.js";
21
+ } from "./chunk-XJETIO4K.js";
22
22
  import {
23
23
  assertOrchestratorCapableAgent,
24
24
  formatAccountProfilePlaybookLine,
@@ -31,7 +31,7 @@ import {
31
31
  resolveThreadDefaults,
32
32
  resolveViewerProfile,
33
33
  resolveViewerProfileForRepo
34
- } from "./chunk-Y5JES2VV.js";
34
+ } from "./chunk-RZTRWN2V.js";
35
35
  import {
36
36
  appDataDir,
37
37
  globalAgentCwd,
@@ -35,6 +35,22 @@ function resolveThreadEffort(raw) {
35
35
  if (raw.fast) return "low";
36
36
  return "high";
37
37
  }
38
+ function normalizeRunScriptRequest(raw) {
39
+ if (!raw || typeof raw !== "object") return void 0;
40
+ const req = raw;
41
+ if (req.op !== "start" && req.op !== "stop") return void 0;
42
+ if (typeof req.requestId !== "string" || !req.requestId.trim()) return void 0;
43
+ if (typeof req.requestedAt !== "string" || !req.requestedAt.trim()) return void 0;
44
+ return {
45
+ op: req.op,
46
+ scriptName: req.scriptName ?? null,
47
+ requestId: req.requestId,
48
+ requestedAt: req.requestedAt,
49
+ claimedAt: req.claimedAt ?? null,
50
+ fulfilledAt: req.fulfilledAt ?? null,
51
+ error: req.error ?? null
52
+ };
53
+ }
38
54
  function normalizeThread(raw) {
39
55
  return {
40
56
  ...raw,
@@ -58,6 +74,7 @@ function normalizeThread(raw) {
58
74
  stackLayer: raw.stackLayer ?? null,
59
75
  userSetTitle: Boolean(raw.userSetTitle),
60
76
  activeRuns: Array.isArray(raw.activeRuns) ? raw.activeRuns : [],
77
+ runScriptRequest: normalizeRunScriptRequest(raw.runScriptRequest),
61
78
  quotaResumeAt: raw.quotaResumeAt ?? null,
62
79
  quotaContinuedFromId: raw.quotaContinuedFromId ?? null,
63
80
  archivedAt: typeof raw.archivedAt === "string" && raw.archivedAt.trim() ? raw.archivedAt.trim() : void 0
@@ -7,14 +7,14 @@ import {
7
7
  enrichWorkspacesWithGithub,
8
8
  ensureGlobalCoordinatorCwd,
9
9
  formatWorkspaceInventory
10
- } from "./chunk-N2VST3NL.js";
11
- import "./chunk-KTZESHYH.js";
10
+ } from "./chunk-HZFLKVGC.js";
11
+ import "./chunk-N5TX66CD.js";
12
12
  import "./chunk-6K64FJDD.js";
13
13
  import "./chunk-PO4M4JAJ.js";
14
- import "./chunk-EWHJNJHV.js";
14
+ import "./chunk-I5NC7DWO.js";
15
15
  import "./chunk-WDRMZKVB.js";
16
16
  import "./chunk-JT7R45JB.js";
17
- import "./chunk-3IMSQNTS.js";
17
+ import "./chunk-4VGCG2HV.js";
18
18
  import "./chunk-7J72QJOM.js";
19
19
  import "./chunk-77WWLBCI.js";
20
20
  import "./chunk-WFUDY6PC.js";