@sideboard-ai/core 0.1.139 → 0.1.142

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 (63) hide show
  1. package/dist/{abletime-VTP4JR63.js → abletime-DI55AJX3.js} +2 -2
  2. package/dist/{abletime-DDTV7WO3.js → abletime-QEVAE67D.js} +2 -2
  3. package/dist/agents/cursor-runner.cjs +0 -0
  4. package/dist/agents/cursor-runner.js +1 -1
  5. package/dist/{agents-665S7Z3R.js → agents-E5AAMHDY.js} +11 -7
  6. package/dist/{agents-OC3XM7UE.js → agents-OEEOIKUB.js} +10 -6
  7. package/dist/{app-settings-U5RZKUXG.js → app-settings-WJDCJFSY.js} +3 -1
  8. package/dist/{app-settings-YQ7YX2CC.js → app-settings-ZCN632OJ.js} +3 -1
  9. package/dist/caffeinate-hold-NBRUWYXO.js +0 -0
  10. package/dist/{chunk-HLIHBGVO.js → chunk-4D4PEBGB.js} +2 -2
  11. package/dist/{chunk-DJJ3DTT4.js → chunk-4Q45TLZ5.js} +7 -7
  12. package/dist/chunk-6GN5WPYZ.js +0 -0
  13. package/dist/chunk-B3SJXYIJ.js +0 -0
  14. package/dist/chunk-BD2ICC4D.js +0 -0
  15. package/dist/{chunk-GKKHXZCT.js → chunk-BGQBELGJ.js} +201 -2
  16. package/dist/{chunk-2XXAAIN3.js → chunk-BOD5V52W.js} +1 -1
  17. package/dist/{chunk-23KCPND2.js → chunk-CG25RYQQ.js} +47 -40
  18. package/dist/chunk-ED4UPEJX.js +0 -0
  19. package/dist/chunk-EUXOHTUK.js +0 -0
  20. package/dist/{chunk-5XH5M6RA.js → chunk-FADNFPZO.js} +3 -3
  21. package/dist/{chunk-ZCLHAOFR.js → chunk-FGOD5MQ4.js} +479 -37
  22. package/dist/{chunk-SSBM4GZX.js → chunk-HC5N3BDL.js} +2 -2
  23. package/dist/{chunk-GJBHOJCZ.js → chunk-I77RYPOH.js} +201 -2
  24. package/dist/{chunk-YZQEJOAU.js → chunk-JUWVAMRS.js} +280 -34
  25. package/dist/chunk-KGZBYWZ3.js +0 -0
  26. package/dist/chunk-KLBOWJ74.js +0 -0
  27. package/dist/chunk-KPIYENTF.js +0 -0
  28. package/dist/chunk-N5PM7HGQ.js +0 -0
  29. package/dist/chunk-NSTQ6QKD.js +0 -0
  30. package/dist/{chunk-DKXZCIB2.js → chunk-O3JEC2UJ.js} +7 -7
  31. package/dist/{chunk-RTX3AY42.js → chunk-OHWN4JEL.js} +5 -3
  32. package/dist/{chunk-E2RE7P2S.js → chunk-R2W7A2UO.js} +3 -3
  33. package/dist/chunk-S42XV45P.js +0 -0
  34. package/dist/{chunk-7SYFWPOZ.js → chunk-UG5N7ET3.js} +5 -3
  35. package/dist/{chunk-CDJISVKN.js → chunk-VHTIHOHE.js} +47 -41
  36. package/dist/{chunk-AT346U23.js → chunk-VZJVKQHH.js} +1 -1
  37. package/dist/{chunk-FSCQXMGI.js → chunk-WT5HSFOU.js} +1 -0
  38. package/dist/connected-teams-IJUJQ7R6.js +0 -0
  39. package/dist/{coordinator-prompt-43O6EPA2.js → coordinator-prompt-2LD3C74O.js} +3 -3
  40. package/dist/{coordinator-prompt-4TXU6Q3R.js → coordinator-prompt-FBX7Y4EM.js} +3 -3
  41. package/dist/cursor-recover-NOQYOFRF.js +0 -0
  42. package/dist/{global-workspace-ZDYKHQ4O.js → global-workspace-NG7SU3GV.js} +4 -4
  43. package/dist/{global-workspace-VIU57E3Y.js → global-workspace-OCV77UYF.js} +4 -4
  44. package/dist/index.cjs +867 -126
  45. package/dist/index.d.cts +222 -60
  46. package/dist/index.d.ts +222 -60
  47. package/dist/index.js +100 -14
  48. package/dist/mcp/run-stdio.cjs +583 -109
  49. package/dist/mcp/run-stdio.js +12 -12
  50. package/dist/oauth-YJBAURIJ.js +0 -0
  51. package/dist/{orchestrator-ZCMYSDTC.js → orchestrator-4BEZJ2BU.js} +7 -7
  52. package/dist/{orchestrator-WEFXUHFX.js → orchestrator-I2T44CRP.js} +8 -8
  53. package/dist/paths-QCI3MTO5.js +0 -0
  54. package/dist/plan-file-PHVKUAEE.js +0 -0
  55. package/dist/run-USF6FRKV.js +0 -0
  56. package/dist/thread-store-57ZLHR3A.js +0 -0
  57. package/dist/title-4WAKWZRF.js +0 -0
  58. package/dist/{workspaces-4HYGNH4B.js → workspaces-4FUQW5LD.js} +5 -5
  59. package/dist/{workspaces-ALCTB65T.js → workspaces-PU5YHQ7Z.js} +5 -5
  60. package/dist/{worktree-GXD2NGOZ.js → worktree-GKLPPNWR.js} +2 -2
  61. package/dist/{worktree-NSNZODAM.js → worktree-Z22HTSCU.js} +2 -2
  62. package/package.json +9 -8
  63. package/LICENSE +0 -190
@@ -18,13 +18,13 @@ import {
18
18
  stripBrightsyNdjsonNoise,
19
19
  sumUsageList,
20
20
  toolDescription
21
- } from "./chunk-CDJISVKN.js";
21
+ } from "./chunk-VHTIHOHE.js";
22
22
  import {
23
23
  addWorkspace,
24
24
  ensureWorkspace,
25
25
  removeWorkspace,
26
26
  syncWorkspacesFromThreads
27
- } from "./chunk-SSBM4GZX.js";
27
+ } from "./chunk-HC5N3BDL.js";
28
28
  import {
29
29
  assertOrchestratorCapableAgent,
30
30
  attachmentsFromWorktreePaths,
@@ -41,14 +41,17 @@ import {
41
41
  persistPendingFileAttachments,
42
42
  stageAbsolutePathsAsAttachments,
43
43
  stageBuffersAsAttachments
44
- } from "./chunk-E2RE7P2S.js";
44
+ } from "./chunk-R2W7A2UO.js";
45
45
  import {
46
46
  SLACK_REPLY_FORMATTING,
47
47
  coordinatorSystemPrompt,
48
48
  coordinatorTurnReminder,
49
49
  enrichWorkspacesWithGithub,
50
50
  ensureGlobalCoordinatorCwd
51
- } from "./chunk-7SYFWPOZ.js";
51
+ } from "./chunk-UG5N7ET3.js";
52
+ import {
53
+ httpFetch
54
+ } from "./chunk-ELBYOGVA.js";
52
55
  import {
53
56
  extractPresentedPlan,
54
57
  readPlanFile,
@@ -65,12 +68,13 @@ import {
65
68
  looksLikeAgentFailureMessage,
66
69
  looksLikeInvalidAgentSession,
67
70
  looksLikeV8Oom,
71
+ packagedMcpDir,
68
72
  pushTurnStderr,
69
73
  shouldFeedErrorBackToAgent,
70
74
  shouldRetryFailedAgentTurn,
71
75
  summarizeTurnStderr,
72
76
  turnFailChatText
73
- } from "./chunk-FSCQXMGI.js";
77
+ } from "./chunk-WT5HSFOU.js";
74
78
  import {
75
79
  releaseCaffeinateHoldForThread
76
80
  } from "./chunk-DCOZABNT.js";
@@ -118,7 +122,7 @@ import {
118
122
  withRepoGitLock,
119
123
  worktreeDisplayLabelForGroup,
120
124
  worktreeNameFromPath
121
- } from "./chunk-DKXZCIB2.js";
125
+ } from "./chunk-O3JEC2UJ.js";
122
126
  import {
123
127
  ATTACHMENTS_DIR,
124
128
  LEGACY_ATTACHMENTS_DIR
@@ -135,9 +139,10 @@ import {
135
139
  resolveNewThreadOptions,
136
140
  resolveThreadDefaults,
137
141
  resolveVaultKey,
142
+ saveAppSettings,
138
143
  updateAdvancedSettings,
139
144
  writeSecureJson
140
- } from "./chunk-GJBHOJCZ.js";
145
+ } from "./chunk-I77RYPOH.js";
141
146
  import {
142
147
  stripNestedElectronEnv
143
148
  } from "./chunk-4TR3HZFT.js";
@@ -339,7 +344,7 @@ function slackTokenFor(ws, kind = "read") {
339
344
  const token2 = ws.user_token?.trim();
340
345
  if (!token2) {
341
346
  throw new Error(
342
- `Slack search needs a user token for ${ws.team_name}. Reconnect via Account \u2192 Slack (browser) or paste an xoxp- token.`
347
+ `Slack search needs a user token for ${ws.team_name}. Reconnect via Settings \u2192 Remote \u2192 Slack (browser) or paste an xoxp- token.`
343
348
  );
344
349
  }
345
350
  return token2;
@@ -354,7 +359,7 @@ function requireSlackWorkspace(teamId) {
354
359
  const ws = getSlackWorkspace(teamId);
355
360
  if (!ws) {
356
361
  const connected = listSlackWorkspaces();
357
- const hint = connected.length === 0 ? "Connect a workspace in Account \u2192 Slack workspaces." : `Connected: ${connected.map((t) => `${t.team_name} (${t.team_id})`).join(", ")}`;
362
+ const hint = connected.length === 0 ? "Connect a workspace in Settings \u2192 Remote \u2192 Slack." : `Connected: ${connected.map((t) => `${t.team_name} (${t.team_id})`).join(", ")}`;
358
363
  throw new Error(`Unknown Slack team_id "${teamId}". ${hint}`);
359
364
  }
360
365
  return ws;
@@ -448,7 +453,7 @@ async function continueSourceThread(threadId, prompt) {
448
453
  await continueOnReply(threadId, prompt);
449
454
  return;
450
455
  }
451
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-WEFXUHFX.js");
456
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-I2T44CRP.js");
452
457
  await getOrchestrator2().send(threadId, prompt);
453
458
  } catch {
454
459
  }
@@ -708,7 +713,7 @@ async function pollSlackOutboundWatches(opts) {
708
713
  }
709
714
 
710
715
  // src/orchestrator/orchestrator.ts
711
- import { existsSync as existsSync15 } from "fs";
716
+ import { existsSync as existsSync16 } from "fs";
712
717
 
713
718
  // src/agents/spawn.ts
714
719
  import { createInterface } from "readline";
@@ -728,9 +733,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
728
733
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
729
734
  );
730
735
  }
731
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-ZDYKHQ4O.js");
736
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-NG7SU3GV.js");
732
737
  if (isGlobalThread2(thread)) {
733
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-43O6EPA2.js");
738
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-2LD3C74O.js");
734
739
  ensureGlobalCoordinatorCwd2(
735
740
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
736
741
  );
@@ -1008,6 +1013,15 @@ function estimateOccupancyTokens(messages) {
1008
1013
  function threadHasCompactedContext(messages) {
1009
1014
  return messages.some((m) => m.role === "summary");
1010
1015
  }
1016
+ function forwardOccupancyTokens(messages, usage, windowTokens = 1e6) {
1017
+ const estimated = estimateOccupancyTokens(messages);
1018
+ if (threadHasCompactedContext(messages) && estimated > 0) return estimated;
1019
+ if (usage) {
1020
+ const last = contextTokens(usage);
1021
+ if (last > 0 && last <= windowTokens) return last;
1022
+ }
1023
+ return estimated;
1024
+ }
1011
1025
  function forwardContextUsage(usage, messages) {
1012
1026
  if (!usage) return null;
1013
1027
  if (!threadHasCompactedContext(messages)) return usage;
@@ -2860,7 +2874,7 @@ async function createThread(input, _onSetupLine) {
2860
2874
  });
2861
2875
  await requireAgent(resolved.agent);
2862
2876
  if (input.cowboy) {
2863
- const { cowboyModeEnabled } = await import("./app-settings-U5RZKUXG.js");
2877
+ const { cowboyModeEnabled } = await import("./app-settings-WJDCJFSY.js");
2864
2878
  if (!cowboyModeEnabled()) {
2865
2879
  throw new Error(
2866
2880
  "Cowboy mode is off. Enable it in Settings \u2192 Advanced."
@@ -2916,7 +2930,7 @@ async function createThread(input, _onSetupLine) {
2916
2930
  let sourceIsFork = false;
2917
2931
  if (!input.cowboy && sourceType === "branch" && (!sourceRef || sourceRef === "HEAD" || sourceRef === "default")) {
2918
2932
  if (isAbleTimeConnected() && getIssueSource() === "abletime") {
2919
- const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-DDTV7WO3.js");
2933
+ const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-QEVAE67D.js");
2920
2934
  const task = await ensureAbleTimeTask({
2921
2935
  title: title?.trim() || "Untitled work",
2922
2936
  description: input.prompt?.trim() || void 0
@@ -2989,7 +3003,7 @@ async function createThread(input, _onSetupLine) {
2989
3003
  return readThread(thread.id) ?? thread;
2990
3004
  }
2991
3005
  async function listLinearIssues(agent, repoPath) {
2992
- const { getAdapter: getAdapter2 } = await import("./agents-665S7Z3R.js");
3006
+ const { getAdapter: getAdapter2 } = await import("./agents-E5AAMHDY.js");
2993
3007
  await requireAgent(agent, { requireLinear: true });
2994
3008
  const adapter = getAdapter2(agent);
2995
3009
  if (!adapter.listLinearIssues) {
@@ -3386,7 +3400,7 @@ async function adoptThread(input) {
3386
3400
  messages: input.messages ?? []
3387
3401
  });
3388
3402
  writeThread(thread);
3389
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-ALCTB65T.js");
3403
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-PU5YHQ7Z.js");
3390
3404
  await ensureWorkspace2(repoPath);
3391
3405
  return thread;
3392
3406
  }
@@ -4729,6 +4743,93 @@ async function confirmLand(thread, opts) {
4729
4743
  import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync3 } from "fs";
4730
4744
  import { homedir } from "os";
4731
4745
  import { join as join11 } from "path";
4746
+
4747
+ // src/skills/bundled/long-running.ts
4748
+ var LONG_RUNNING_SKILL_COMMAND = "long-running";
4749
+ var LONG_RUNNING_SKILL_NAME = "long-running";
4750
+ var BUNDLED_LONG_RUNNING_PATH = "bundled:long-running";
4751
+ var LONG_RUNNING_SKILL_DESCRIPTION = "Detach and wait on any long worktree job (pack, test, deploy) so a new chat turn does not SIGTERM it, and the human does not have to poll. Use when a command may run more than ~30s, or the user mentions wait / status / aborted builds.";
4752
+ var LONG_RUNNING_SKILL_BODY = `# Long-running jobs
4753
+
4754
+ A Sideboard or Cursor **worktree turn** SIGTERMs the agent shell (and its process group) when the user sends another message or the turn is interrupted. \`block_until_ms: 0\` is not enough \u2014 the child stays in that group.
4755
+
4756
+ Do **not** ask the human to check back. Detach, then **wait** in 45s slices (same idea as MCP \`wait_for_turn\`) until \`stillRunning\` is false.
4757
+
4758
+ ## Tool
4759
+
4760
+ Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" start\` path injected each turn. If this worktree has \`scripts/detached-job.js\`, that is the same tool.
4761
+
4762
+ \`\`\`bash
4763
+ # Start (exits in ~1s; job survives this turn)
4764
+ node <detached-job.js> start <id> -- <command> [args...]
4765
+
4766
+ # Wait \u2014 returns within ~45s even if the job is still going
4767
+ node <detached-job.js> wait <id>
4768
+
4769
+ # Block until the process exits (humans / a turn that will not be interrupted)
4770
+ node <detached-job.js> wait <id> --until-done
4771
+
4772
+ node <detached-job.js> status <id>
4773
+ \`\`\`
4774
+
4775
+ \`<id>\` is kebab-case (\`mac-release\`, \`core-test\`, \`fly-deploy\`). State is \`.sideboard/detached-jobs/<id>/\` (local scratch).
4776
+
4777
+ Wait JSON:
4778
+
4779
+ - \`stillRunning: true\` \u2192 exit 2 \u2192 **call wait again**. Progress is in \`progress\` / \`phase\`. Do not start a second job. Do not ping the user.
4780
+ - \`ok: true\` \u2192 exit 0 \u2192 continue the rest of the task.
4781
+ - \`failed: true\` \u2192 exit 1 \u2192 read \`progress\`, fix, start **once**.
4782
+
4783
+ ## Sideboard UI (append-only log)
4784
+
4785
+ The side column **is** the live view. Use \`present_artifact\` **\`type=log\`** with a stable \`artifact_id\` (the job id). Each call **appends** \u2014 send only \`delta\` from wait JSON, not the full log and not HTML.
4786
+
4787
+ \`\`\`
4788
+ present_artifact
4789
+ title: <short title>
4790
+ type: log
4791
+ artifact_id: <id>
4792
+ content: <wait.delta> # new lines only; empty is ok
4793
+ status: running | ok | failed # from wait.status
4794
+ phase: <wait.phase>
4795
+ \`\`\`
4796
+
4797
+ After **start**, present once (\`status=running\`, empty or first lines). After **every wait**, present the same id with \`content=delta\`. Do **not** dump the log in chat. Do **not** also fence HTML.
4798
+
4799
+ Ad-hoc pid/log (legacy or another tool\u2019s files):
4800
+
4801
+ \`\`\`bash
4802
+ node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
4803
+ \`\`\`
4804
+
4805
+ ## Agent loop
4806
+
4807
+ 1. \`start\` once. If JSON says \`already-running\`, do not start again.
4808
+ 2. Immediately \`present_artifact\` \`type=log\` (same \`artifact_id\`, \`status=running\`) \u2014 the human should see **working** in the side column, not a \u201Ccheck back later\u201D message.
4809
+ 3. Loop \`wait\` (use \`--timeout-ms 15000\` for a livelier pane). After each slice, \`present_artifact\` the same id with \`content=delta\` only.
4810
+ 4. On \`ok\`, present once more (\`status=ok\`, last \`delta\`) and finish the task. On \`failed\`, fix from the log.
4811
+
4812
+ Never tell the user \u201Csay status when it\u2019s done.\u201D You wait.
4813
+ `;
4814
+
4815
+ // src/skills/discover.ts
4816
+ var BUNDLED_SKILL_PREFIX = "bundled:";
4817
+ function bundledSkills() {
4818
+ return [
4819
+ {
4820
+ id: BUNDLED_LONG_RUNNING_PATH,
4821
+ name: LONG_RUNNING_SKILL_NAME,
4822
+ command: LONG_RUNNING_SKILL_COMMAND,
4823
+ description: LONG_RUNNING_SKILL_DESCRIPTION.slice(0, 240),
4824
+ path: BUNDLED_LONG_RUNNING_PATH,
4825
+ source: "bundled"
4826
+ }
4827
+ ];
4828
+ }
4829
+ function bundledSkillBody(id) {
4830
+ if (id === LONG_RUNNING_SKILL_COMMAND) return LONG_RUNNING_SKILL_BODY;
4831
+ return null;
4832
+ }
4732
4833
  function toCommand(name) {
4733
4834
  return name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
4734
4835
  }
@@ -4846,7 +4947,13 @@ function discoverSkills(worktreePath) {
4846
4947
  scanSkillsDir(abs, "user", collected);
4847
4948
  }
4848
4949
  scanClaudePluginSkills(join11(home, ".claude/plugins"), collected);
4849
- const rank = { workspace: 0, user: 1, cli: 2 };
4950
+ collected.push(...bundledSkills());
4951
+ const rank = {
4952
+ workspace: 0,
4953
+ user: 1,
4954
+ cli: 2,
4955
+ bundled: 3
4956
+ };
4850
4957
  const byCommand = /* @__PURE__ */ new Map();
4851
4958
  for (const skill of collected) {
4852
4959
  const prev = byCommand.get(skill.command);
@@ -4857,6 +4964,14 @@ function discoverSkills(worktreePath) {
4857
4964
  return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
4858
4965
  }
4859
4966
  function readSkillBody(skillPath, maxChars = 12e3) {
4967
+ if (skillPath.startsWith(BUNDLED_SKILL_PREFIX)) {
4968
+ const body = bundledSkillBody(skillPath.slice(BUNDLED_SKILL_PREFIX.length));
4969
+ if (body) {
4970
+ return body.length > maxChars ? `${body.slice(0, maxChars)}
4971
+
4972
+ \u2026(truncated)` : body;
4973
+ }
4974
+ }
4860
4975
  const raw = readFileSync9(skillPath, "utf8");
4861
4976
  if (raw.startsWith("---")) {
4862
4977
  const end = raw.indexOf("\n---", 3);
@@ -4950,8 +5065,39 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
4950
5065
  }
4951
5066
 
4952
5067
  // src/agents/instructions.ts
4953
- import { existsSync as existsSync13, readFileSync as readFileSync10, statSync as statSync4 } from "fs";
4954
- import { join as join12 } from "path";
5068
+ import { existsSync as existsSync14, readFileSync as readFileSync10, statSync as statSync4 } from "fs";
5069
+ import { join as join13 } from "path";
5070
+
5071
+ // src/skills/detached-job-path.ts
5072
+ import { existsSync as existsSync13 } from "fs";
5073
+ import { dirname as dirname4, join as join12 } from "path";
5074
+ import { fileURLToPath } from "url";
5075
+ function packagedDetachedJobPath() {
5076
+ const dir = packagedMcpDir();
5077
+ if (!dir) return null;
5078
+ const script = join12(dir, "scripts", "detached-job.js");
5079
+ return existsSync13(script) ? script : null;
5080
+ }
5081
+ function resolveDetachedJobScript() {
5082
+ const packaged = packagedDetachedJobPath();
5083
+ if (packaged) return packaged;
5084
+ let dir = dirname4(fileURLToPath(import.meta.url));
5085
+ for (let i = 0; i < 8; i++) {
5086
+ const candidate = join12(dir, "scripts", "detached-job.js");
5087
+ if (existsSync13(candidate)) return candidate;
5088
+ const parent = dirname4(dir);
5089
+ if (parent === dir) break;
5090
+ dir = parent;
5091
+ }
5092
+ return null;
5093
+ }
5094
+ function formatDetachedJobInvoke(scriptPath) {
5095
+ const resolved = scriptPath === void 0 ? resolveDetachedJobScript() : scriptPath;
5096
+ if (resolved) return `node ${JSON.stringify(resolved)}`;
5097
+ return "node scripts/detached-job.js";
5098
+ }
5099
+
5100
+ // src/agents/instructions.ts
4955
5101
  function normPath(p) {
4956
5102
  return p.replace(/\/+$/, "");
4957
5103
  }
@@ -5059,6 +5205,7 @@ function formatWorktreeReminder() {
5059
5205
  function formatProcessGuideDirective() {
5060
5206
  return [
5061
5207
  "Process guides (recurring work only):",
5208
+ "- Long jobs (pack, test, deploy, anything that may run more than ~30s): Sideboard always provides `/long-running`. Detach and wait \u2014 do not ask the human to poll.",
5062
5209
  "- If `.claude/skills/graph-engineering/SKILL.md` exists, follow it (`/graph-engineering`) for migrations, ports, batch fixes, and other fan-out. Judge first; state on disk; grow the rulebook; do not patch around it.",
5063
5210
  "- If this same shape of work will happen again, write `.claude/skills/<kebab-name>/SKILL.md` in this worktree (Claude Code project skill). Sideboard `/name`, Claude Code, and `attach` all load that path. Do not leave the method only in chat.",
5064
5211
  "- Merge-readiness notes: create or edit `.claude/skills/review/SKILL.md` and commit it. That file is allowed. Do not use `.sideboard/review.md` for new notes (legacy; setup copies it into the skill).",
@@ -5066,6 +5213,23 @@ function formatProcessGuideDirective() {
5066
5213
  "- Skip a guide for a one-off. If a matching skill exists, follow it. Same miss twice \u2192 edit the skill, do not patch around it."
5067
5214
  ].join("\n");
5068
5215
  }
5216
+ function formatLongRunningDirective(opts) {
5217
+ const invoke = formatDetachedJobInvoke(opts?.scriptPath);
5218
+ return [
5219
+ "Long-running jobs (mandatory when a command may run more than ~30s):",
5220
+ "A Sideboard worktree turn SIGTERMs the agent shell (and its process group) when the user sends another message or the turn is interrupted. `block_until_ms: 0` is not enough. Do not ask the human to poll.",
5221
+ `Helper (same tool as \`scripts/detached-job.js\` when that file exists in the worktree): \`${invoke}\``,
5222
+ `- Start once: \`${invoke} start <id> -- <command> [args...]\` (cwd = this worktree). If JSON says already-running, do not start again.`,
5223
+ "- Immediately `present_artifact` `type=log` with `artifact_id=<id>` and `status=running` \u2014 the side column is the live view.",
5224
+ `- Loop \`${invoke} wait <id>\` (returns in ~45s). stillRunning \u2192 present the same id with \`content=delta\` only \u2192 wait again. Do not resend the full log or HTML.`,
5225
+ "- ok \u2192 finish the task. failed \u2192 read the log, fix, start once.",
5226
+ "State: `.sideboard/detached-jobs/<id>/` (local scratch). Full guide: `/long-running` (always available)."
5227
+ ].join("\n");
5228
+ }
5229
+ function formatLongRunningReminder(opts) {
5230
+ const invoke = formatDetachedJobInvoke(opts?.scriptPath);
5231
+ return `Long jobs: \`${invoke} start <id> -- <cmd>\`, loop wait, present_artifact type=log (delta). Do not ask the human to poll.`;
5232
+ }
5069
5233
  function formatArtifactDirective() {
5070
5234
  return [
5071
5235
  "Sideboard side column (desktop UI):",
@@ -5115,8 +5279,8 @@ function loadAgentInstructions(worktreePath, agent) {
5115
5279
  const out = [];
5116
5280
  for (const rel of candidates) {
5117
5281
  if (seenPaths.has(rel)) continue;
5118
- const abs = join12(worktreePath, rel);
5119
- if (!existsSync13(abs)) continue;
5282
+ const abs = join13(worktreePath, rel);
5283
+ if (!existsSync14(abs)) continue;
5120
5284
  try {
5121
5285
  if (!statSync4(abs).isFile()) continue;
5122
5286
  let content = readFileSync10(abs, "utf8");
@@ -5160,6 +5324,253 @@ function withAgentInstructions(prompt, files) {
5160
5324
  ${prompt}`;
5161
5325
  }
5162
5326
 
5327
+ // src/integrations/optional-services.ts
5328
+ var OPTIONAL_SERVICE_IDS = ["vercel", "supabase", "posthog", "sentry"];
5329
+ var OPTIONAL_SERVICES = [
5330
+ {
5331
+ id: "vercel",
5332
+ label: "Vercel",
5333
+ hint: "Deploys and project settings. Agents use the `vercel` CLI (`VERCEL_TOKEN`).",
5334
+ tokenPlaceholder: "token from vercel.com/account/tokens",
5335
+ tokenDocs: "https://vercel.com/account/tokens",
5336
+ envKey: "VERCEL_TOKEN",
5337
+ cli: "vercel",
5338
+ npmPackage: "vercel"
5339
+ },
5340
+ {
5341
+ id: "supabase",
5342
+ label: "Supabase",
5343
+ hint: "Database and project management. Agents use the `supabase` CLI (`SUPABASE_ACCESS_TOKEN`).",
5344
+ tokenPlaceholder: "sbp_\u2026",
5345
+ tokenDocs: "https://supabase.com/dashboard/account/tokens",
5346
+ envKey: "SUPABASE_ACCESS_TOKEN",
5347
+ cli: "supabase",
5348
+ npmPackage: "supabase"
5349
+ },
5350
+ {
5351
+ id: "posthog",
5352
+ label: "PostHog",
5353
+ hint: "Product analytics. No first-class CLI \u2014 agents use the HTTP API (`POSTHOG_PERSONAL_API_KEY`).",
5354
+ tokenPlaceholder: "phx_\u2026",
5355
+ tokenDocs: "https://app.posthog.com/settings/user-api-keys",
5356
+ envKey: "POSTHOG_PERSONAL_API_KEY",
5357
+ hostEnvKey: "POSTHOG_HOST",
5358
+ defaultHost: "https://us.posthog.com",
5359
+ hostPlaceholder: "https://us.posthog.com"
5360
+ },
5361
+ {
5362
+ id: "sentry",
5363
+ label: "Sentry",
5364
+ hint: "Error tracking. Agents use `sentry-cli` (`SENTRY_AUTH_TOKEN`).",
5365
+ tokenPlaceholder: "sntrys_\u2026 or auth token",
5366
+ tokenDocs: "https://sentry.io/settings/account/api/auth-tokens/",
5367
+ envKey: "SENTRY_AUTH_TOKEN",
5368
+ cli: "sentry-cli",
5369
+ npmPackage: "@sentry/cli",
5370
+ hostEnvKey: "SENTRY_URL",
5371
+ defaultHost: "https://sentry.io",
5372
+ hostPlaceholder: "https://sentry.io"
5373
+ }
5374
+ ];
5375
+ function optionalServiceSpec(id) {
5376
+ const spec = OPTIONAL_SERVICES.find((s) => s.id === id);
5377
+ if (!spec) throw new Error(`Unknown optional service: ${id}`);
5378
+ return spec;
5379
+ }
5380
+ function isOptionalServiceId(value) {
5381
+ return typeof value === "string" && OPTIONAL_SERVICE_IDS.includes(value);
5382
+ }
5383
+ function normalizeServiceOrigin(raw, fallback) {
5384
+ const trimmed = raw?.trim() || fallback;
5385
+ try {
5386
+ const url = new URL(trimmed.includes("://") ? trimmed : `https://${trimmed}`);
5387
+ if (url.protocol !== "http:" && url.protocol !== "https:") return fallback;
5388
+ return `${url.protocol}//${url.host}`;
5389
+ } catch {
5390
+ return fallback;
5391
+ }
5392
+ }
5393
+ function asRecord(value) {
5394
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
5395
+ }
5396
+ function asString(value) {
5397
+ return typeof value === "string" ? value.trim() : "";
5398
+ }
5399
+ function firstString(record, keys) {
5400
+ if (!record) return "";
5401
+ for (const key of keys) {
5402
+ const value = asString(record[key]);
5403
+ if (value) return value;
5404
+ }
5405
+ return "";
5406
+ }
5407
+ async function readJson(res) {
5408
+ const text = await res.text();
5409
+ if (!text.trim()) return null;
5410
+ try {
5411
+ return JSON.parse(text);
5412
+ } catch {
5413
+ return text;
5414
+ }
5415
+ }
5416
+ async function authorizedGet(url, token) {
5417
+ const res = await httpFetch(url, {
5418
+ method: "GET",
5419
+ headers: {
5420
+ Authorization: `Bearer ${token}`,
5421
+ Accept: "application/json"
5422
+ }
5423
+ });
5424
+ const body = await readJson(res);
5425
+ if (!res.ok) {
5426
+ const record = asRecord(body);
5427
+ const message = firstString(record, ["message", "error", "error_description", "detail"]) || (typeof body === "string" ? body.trim().slice(0, 160) : "");
5428
+ throw new Error(
5429
+ message ? `${res.status}: ${message}` : `Request failed (${res.status})`
5430
+ );
5431
+ }
5432
+ return body;
5433
+ }
5434
+ async function verifyOptionalService(input) {
5435
+ const token = input.token.trim();
5436
+ if (!token) throw new Error("A token is required");
5437
+ const spec = optionalServiceSpec(input.id);
5438
+ try {
5439
+ if (input.id === "vercel") {
5440
+ const body2 = asRecord(await authorizedGet("https://api.vercel.com/v2/user", token));
5441
+ const user = asRecord(body2?.user) ?? body2;
5442
+ const name2 = firstString(user, ["username", "name", "email"]) || "Vercel user";
5443
+ return { viewerName: name2 };
5444
+ }
5445
+ if (input.id === "supabase") {
5446
+ const body2 = await authorizedGet("https://api.supabase.com/v1/organizations", token);
5447
+ const list2 = Array.isArray(body2) ? body2 : [];
5448
+ const first2 = asRecord(list2[0]);
5449
+ const name2 = firstString(first2, ["name", "slug"]) || "Supabase";
5450
+ return { viewerName: list2.length > 1 ? `${name2} +${list2.length - 1}` : name2 };
5451
+ }
5452
+ if (input.id === "posthog") {
5453
+ const host2 = normalizeServiceOrigin(input.host, spec.defaultHost ?? "https://us.posthog.com");
5454
+ const body2 = asRecord(await authorizedGet(`${host2}/api/users/@me/`, token));
5455
+ const name2 = firstString(body2, ["email", "distinct_id"]) || [firstString(body2, ["first_name"]), firstString(body2, ["last_name"])].filter(Boolean).join(" ") || "PostHog user";
5456
+ return { viewerName: name2, host: host2 };
5457
+ }
5458
+ const host = normalizeServiceOrigin(input.host, spec.defaultHost ?? "https://sentry.io");
5459
+ const body = await authorizedGet(`${host}/api/0/organizations/`, token);
5460
+ const list = Array.isArray(body) ? body : [];
5461
+ const first = asRecord(list[0]);
5462
+ const name = firstString(first, ["slug", "name"]) || "Sentry";
5463
+ return { viewerName: list.length > 1 ? `${name} +${list.length - 1}` : name, host };
5464
+ } catch (err) {
5465
+ const detail = err instanceof Error ? err.message : String(err);
5466
+ throw new Error(
5467
+ `${spec.label} rejected this token. ${detail} Create a token at ${spec.tokenDocs}`
5468
+ );
5469
+ }
5470
+ }
5471
+ function applyConnection(integrations, id, input) {
5472
+ if (id === "vercel") {
5473
+ integrations.vercelToken = input.token;
5474
+ if (input.viewerName) integrations.vercelViewerName = input.viewerName.slice(0, 120);
5475
+ return;
5476
+ }
5477
+ if (id === "supabase") {
5478
+ integrations.supabaseAccessToken = input.token;
5479
+ if (input.viewerName) integrations.supabaseViewerName = input.viewerName.slice(0, 120);
5480
+ return;
5481
+ }
5482
+ if (id === "posthog") {
5483
+ integrations.posthogPersonalApiKey = input.token;
5484
+ if (input.host) integrations.posthogHost = input.host;
5485
+ if (input.viewerName) integrations.posthogViewerName = input.viewerName.slice(0, 120);
5486
+ return;
5487
+ }
5488
+ integrations.sentryAuthToken = input.token;
5489
+ if (input.host) integrations.sentryHost = input.host;
5490
+ if (input.viewerName) integrations.sentryViewerName = input.viewerName.slice(0, 120);
5491
+ }
5492
+ function clearConnection(integrations, id) {
5493
+ if (id === "vercel") {
5494
+ delete integrations.vercelToken;
5495
+ delete integrations.vercelViewerName;
5496
+ return;
5497
+ }
5498
+ if (id === "supabase") {
5499
+ delete integrations.supabaseAccessToken;
5500
+ delete integrations.supabaseViewerName;
5501
+ return;
5502
+ }
5503
+ if (id === "posthog") {
5504
+ delete integrations.posthogPersonalApiKey;
5505
+ delete integrations.posthogViewerName;
5506
+ delete integrations.posthogHost;
5507
+ return;
5508
+ }
5509
+ delete integrations.sentryAuthToken;
5510
+ delete integrations.sentryViewerName;
5511
+ delete integrations.sentryHost;
5512
+ }
5513
+ async function connectOptionalService(input) {
5514
+ if (!isOptionalServiceId(input.id)) {
5515
+ throw new Error("Unknown optional service");
5516
+ }
5517
+ const verified = await verifyOptionalService(input);
5518
+ const current = loadAppSettings();
5519
+ const integrations = { ...current.integrations };
5520
+ applyConnection(integrations, input.id, {
5521
+ token: input.token.trim(),
5522
+ host: verified.host,
5523
+ viewerName: verified.viewerName
5524
+ });
5525
+ return saveAppSettings({ ...current, integrations });
5526
+ }
5527
+ function disconnectOptionalService(id) {
5528
+ if (!isOptionalServiceId(id)) {
5529
+ throw new Error("Unknown optional service");
5530
+ }
5531
+ const current = loadAppSettings();
5532
+ const integrations = { ...current.integrations };
5533
+ clearConnection(integrations, id);
5534
+ return saveAppSettings({ ...current, integrations });
5535
+ }
5536
+ function optionalServiceConnected(id, integrations) {
5537
+ if (id === "vercel") return Boolean(integrations.vercelToken?.trim());
5538
+ if (id === "supabase") return Boolean(integrations.supabaseAccessToken?.trim());
5539
+ if (id === "posthog") return Boolean(integrations.posthogPersonalApiKey?.trim());
5540
+ return Boolean(integrations.sentryAuthToken?.trim());
5541
+ }
5542
+ function connectedOptionalServices(integrations) {
5543
+ return OPTIONAL_SERVICES.filter((spec) => optionalServiceConnected(spec.id, integrations));
5544
+ }
5545
+ function formatOptionalServicesDirective(integrations) {
5546
+ const connected = connectedOptionalServices(integrations);
5547
+ if (connected.length === 0) return null;
5548
+ const lines = [
5549
+ "Optional connectors (connected \u2014 tokens are already in this process env):"
5550
+ ];
5551
+ for (const spec of connected) {
5552
+ if (spec.cli) {
5553
+ const extras = spec.hostEnvKey ? ` / \`${spec.hostEnvKey}\`` : "";
5554
+ lines.push(
5555
+ `- ${spec.label}: use the \`${spec.cli}\` CLI (\`${spec.envKey}\`${extras}). Do not add a ${spec.label} MCP.`
5556
+ );
5557
+ } else {
5558
+ const extras = spec.hostEnvKey ? ` / \`${spec.hostEnvKey}\`` : "";
5559
+ lines.push(
5560
+ `- ${spec.label}: no first-class CLI \u2014 call the HTTP API with \`${spec.envKey}\`${extras}. Do not add a ${spec.label} MCP.`
5561
+ );
5562
+ }
5563
+ }
5564
+ lines.push(
5565
+ "If a CLI is missing, say so or use the HTTP API with the same token. The user can Install CLI from Settings \u2192 Connectors. Do not ask the user to install a vendor MCP."
5566
+ );
5567
+ return lines.join("\n");
5568
+ }
5569
+ function formatOptionalServicesReminder(integrations) {
5570
+ if (connectedOptionalServices(integrations).length === 0) return null;
5571
+ return "Settings \u2192 Connectors: prefer official CLIs (`vercel`, `supabase`, `sentry-cli`) or the PostHog HTTP API with injected env. Do not add vendor MCPs.";
5572
+ }
5573
+
5163
5574
  // src/threads/sync-branch.ts
5164
5575
  async function syncThreadBranchFromGit(threadId) {
5165
5576
  const thread = readThread(threadId);
@@ -5192,8 +5603,8 @@ async function syncThreadBranchFromGit(threadId) {
5192
5603
 
5193
5604
  // src/store/schedules.ts
5194
5605
  import { randomUUID as randomUUID4 } from "crypto";
5195
- import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
5196
- import { join as join13 } from "path";
5606
+ import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
5607
+ import { join as join14 } from "path";
5197
5608
  import { Cron } from "croner";
5198
5609
  var DURATION_RE = /^(\d+)(s|m|h|d)$/i;
5199
5610
  var UNIT_MS = {
@@ -5203,7 +5614,7 @@ var UNIT_MS = {
5203
5614
  d: 864e5
5204
5615
  };
5205
5616
  function schedulesPath() {
5206
- return join13(appDataDir(), "schedules.json");
5617
+ return join14(appDataDir(), "schedules.json");
5207
5618
  }
5208
5619
  function parseDurationMs(every) {
5209
5620
  const m = every.trim().match(DURATION_RE);
@@ -5309,7 +5720,7 @@ function normalizeTask(raw) {
5309
5720
  }
5310
5721
  function readAll() {
5311
5722
  const path = schedulesPath();
5312
- if (!existsSync14(path)) return [];
5723
+ if (!existsSync15(path)) return [];
5313
5724
  try {
5314
5725
  const parsed = JSON.parse(readFileSync11(path, "utf8"));
5315
5726
  if (!Array.isArray(parsed)) return [];
@@ -5437,7 +5848,7 @@ function formatScheduledPrompt(name, prompt) {
5437
5848
  ${prompt}`;
5438
5849
  }
5439
5850
  async function defaultDeps() {
5440
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-WEFXUHFX.js");
5851
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-I2T44CRP.js");
5441
5852
  const orch = getOrchestrator2();
5442
5853
  return {
5443
5854
  findThread: (id) => findThreadByRef(id),
@@ -5643,7 +6054,7 @@ var Orchestrator = class {
5643
6054
  }
5644
6055
  continue;
5645
6056
  }
5646
- if (!existsSync15(thread.worktreePath)) {
6057
+ if (!existsSync16(thread.worktreePath)) {
5647
6058
  setStatus(thread.id, "broken", "Worktree missing on disk");
5648
6059
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
5649
6060
  continue;
@@ -5662,7 +6073,7 @@ var Orchestrator = class {
5662
6073
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
5663
6074
  });
5664
6075
  }
5665
- const { autoCleanupOrphansEnabled } = await import("./app-settings-U5RZKUXG.js");
6076
+ const { autoCleanupOrphansEnabled } = await import("./app-settings-WJDCJFSY.js");
5666
6077
  if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
5667
6078
  await cleanupOrphanWorktrees({ repoPaths });
5668
6079
  }
@@ -5902,7 +6313,7 @@ var Orchestrator = class {
5902
6313
  }
5903
6314
  await setup;
5904
6315
  if (skipSetup) return;
5905
- const { autoRunAfterSetupEnabled } = await import("./app-settings-U5RZKUXG.js");
6316
+ const { autoRunAfterSetupEnabled } = await import("./app-settings-WJDCJFSY.js");
5906
6317
  if (autoRunAfterSetupEnabled()) {
5907
6318
  try {
5908
6319
  await this.startDev(threadId);
@@ -6164,7 +6575,9 @@ var Orchestrator = class {
6164
6575
  ].join("\n") : null
6165
6576
  ].filter(Boolean).join("\n") : null;
6166
6577
  const artifactReminder = thread.agent !== "brightsy" ? formatUiReminder() : null;
6578
+ const longRunningReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatLongRunningReminder() : null;
6167
6579
  const worktreeReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatWorktreeReminder() : null;
6580
+ const optionalServicesReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatOptionalServicesReminder(loadAppSettings().integrations) : null;
6168
6581
  const slackReplyContext = formatSlackRepliesForTurn(
6169
6582
  pendingSlackExternalReplies(thread.messages)
6170
6583
  );
@@ -6172,7 +6585,9 @@ var Orchestrator = class {
6172
6585
  thread.planMode ? PLAN_MODE_INSTRUCTION : null,
6173
6586
  orchestrationReminder,
6174
6587
  worktreeReminder,
6588
+ optionalServicesReminder,
6175
6589
  artifactReminder,
6590
+ longRunningReminder,
6176
6591
  slackReplyContext,
6177
6592
  expandedPrompt
6178
6593
  ].filter(Boolean).join("\n\n");
@@ -6192,7 +6607,7 @@ var Orchestrator = class {
6192
6607
  }
6193
6608
  const isBrightsy = fresh.agent === "brightsy";
6194
6609
  const isOrchestration = isOrchestratorThread(fresh);
6195
- const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-U5RZKUXG.js");
6610
+ const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-WJDCJFSY.js");
6196
6611
  const gitAuthMode = getGithubGitAuthMode();
6197
6612
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
6198
6613
  githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
@@ -6201,6 +6616,8 @@ var Orchestrator = class {
6201
6616
  gitAuthMode
6202
6617
  });
6203
6618
  const artifactDirective = isBrightsy ? null : formatArtifactDirective();
6619
+ const longRunningDirective = isBrightsy || isOrchestration ? null : formatLongRunningDirective();
6620
+ const optionalServicesDirective = isBrightsy || isOrchestration ? null : formatOptionalServicesDirective(loadAppSettings().integrations);
6204
6621
  const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
6205
6622
  const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled() ? formatRenameBranchDirective(fresh, {
6206
6623
  customPrompt: settings?.prompts?.renameBranch
@@ -6230,7 +6647,9 @@ var Orchestrator = class {
6230
6647
  const cachedPrefix = fresh.sessionId ? "" : [
6231
6648
  coordinatorDirective,
6232
6649
  worktreeDirective,
6650
+ optionalServicesDirective,
6233
6651
  artifactDirective,
6652
+ longRunningDirective,
6234
6653
  renameBranchDirective,
6235
6654
  seed
6236
6655
  ].filter(Boolean).join("\n\n---\n\n");
@@ -6337,7 +6756,9 @@ var Orchestrator = class {
6337
6756
  const retryPrefix = [
6338
6757
  coordinatorDirective,
6339
6758
  worktreeDirective,
6759
+ optionalServicesDirective,
6340
6760
  artifactDirective,
6761
+ longRunningDirective,
6341
6762
  renameBranchDirective,
6342
6763
  retrySeed
6343
6764
  ].filter(Boolean).join("\n\n---\n\n");
@@ -7014,7 +7435,7 @@ var Orchestrator = class {
7014
7435
  updateThread(thread.id, { title: meta.title });
7015
7436
  }
7016
7437
  }
7017
- const { autoArchiveOnMergeEnabled } = await import("./app-settings-U5RZKUXG.js");
7438
+ const { autoArchiveOnMergeEnabled } = await import("./app-settings-WJDCJFSY.js");
7018
7439
  const latest = this.requireThread(thread.id);
7019
7440
  if (!shouldAutoArchiveOnPrMerge({
7020
7441
  previousPrState: prevState || null,
@@ -7324,7 +7745,7 @@ var Orchestrator = class {
7324
7745
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
7325
7746
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
7326
7747
  try {
7327
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-ALCTB65T.js");
7748
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-PU5YHQ7Z.js");
7328
7749
  await ensureWorkspace2(thread.repoPath);
7329
7750
  } catch {
7330
7751
  }
@@ -7351,7 +7772,7 @@ var Orchestrator = class {
7351
7772
  await runArchiveScript(thread.repoPath, thread.worktreePath);
7352
7773
  } catch {
7353
7774
  }
7354
- const { deleteBranchOnPurgeEnabled } = await import("./app-settings-U5RZKUXG.js");
7775
+ const { deleteBranchOnPurgeEnabled } = await import("./app-settings-WJDCJFSY.js");
7355
7776
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
7356
7777
  await removeWorktree(thread.repoPath, thread.worktreePath, {
7357
7778
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -7372,13 +7793,13 @@ var Orchestrator = class {
7372
7793
  this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
7373
7794
  return restored2;
7374
7795
  }
7375
- if (!existsSync15(thread.worktreePath)) {
7796
+ if (!existsSync16(thread.worktreePath)) {
7376
7797
  if (isCowboyThread(thread) || isPrimaryCheckoutThread(thread)) {
7377
7798
  throw new Error(
7378
7799
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
7379
7800
  );
7380
7801
  }
7381
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-GXD2NGOZ.js");
7802
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-GKLPPNWR.js");
7382
7803
  const slug = thread.worktreePath.split("/").pop();
7383
7804
  const dest = thread.worktreePath;
7384
7805
  await withRepoGitLock(thread.repoPath, async () => {
@@ -7486,7 +7907,7 @@ async function startOrchestration(opts) {
7486
7907
  sourceRef: "default",
7487
7908
  ...createOpts
7488
7909
  }).catch(async () => {
7489
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-GXD2NGOZ.js");
7910
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-GKLPPNWR.js");
7490
7911
  const repo = await resolveRepoRoot2(repoPath);
7491
7912
  const def = await resolveDefaultBranch2(repo);
7492
7913
  return orch.createThread({
@@ -7557,6 +7978,7 @@ export {
7557
7978
  estimateThreadChars,
7558
7979
  estimateOccupancyTokens,
7559
7980
  threadHasCompactedContext,
7981
+ forwardOccupancyTokens,
7560
7982
  forwardContextUsage,
7561
7983
  applyForwardOccupancy,
7562
7984
  summarizeConversation,
@@ -7675,18 +8097,38 @@ export {
7675
8097
  getDiffSummary,
7676
8098
  previewLand,
7677
8099
  confirmLand,
8100
+ LONG_RUNNING_SKILL_COMMAND,
8101
+ BUNDLED_LONG_RUNNING_PATH,
8102
+ BUNDLED_SKILL_PREFIX,
7678
8103
  discoverSkills,
7679
8104
  readSkillBody,
7680
8105
  expandComposerPrompt,
8106
+ packagedDetachedJobPath,
8107
+ resolveDetachedJobScript,
8108
+ formatDetachedJobInvoke,
7681
8109
  formatRenameBranchDirective,
7682
8110
  formatWorktreeDirective,
7683
8111
  formatWorktreeReminder,
7684
8112
  formatProcessGuideDirective,
8113
+ formatLongRunningDirective,
8114
+ formatLongRunningReminder,
7685
8115
  formatArtifactDirective,
7686
8116
  formatUiReminder,
7687
8117
  loadAgentInstructions,
7688
8118
  formatAgentInstructions,
7689
8119
  withAgentInstructions,
8120
+ OPTIONAL_SERVICE_IDS,
8121
+ OPTIONAL_SERVICES,
8122
+ optionalServiceSpec,
8123
+ isOptionalServiceId,
8124
+ normalizeServiceOrigin,
8125
+ verifyOptionalService,
8126
+ connectOptionalService,
8127
+ disconnectOptionalService,
8128
+ optionalServiceConnected,
8129
+ connectedOptionalServices,
8130
+ formatOptionalServicesDirective,
8131
+ formatOptionalServicesReminder,
7690
8132
  isPidAlive,
7691
8133
  waitForPidExit,
7692
8134
  Orchestrator,