@sideboard-ai/core 0.1.136 → 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.
- package/dist/{abletime-VTP4JR63.js → abletime-DI55AJX3.js} +2 -2
- package/dist/{abletime-DDTV7WO3.js → abletime-QEVAE67D.js} +2 -2
- package/dist/agents/cursor-runner.cjs +0 -0
- package/dist/agents/cursor-runner.js +1 -1
- package/dist/{agents-QNTTLMG2.js → agents-E5AAMHDY.js} +12 -8
- package/dist/{agents-ELWR7A2T.js → agents-OEEOIKUB.js} +11 -7
- package/dist/{app-settings-U5RZKUXG.js → app-settings-WJDCJFSY.js} +3 -1
- package/dist/{app-settings-YQ7YX2CC.js → app-settings-ZCN632OJ.js} +3 -1
- package/dist/caffeinate-hold-NBRUWYXO.js +0 -0
- package/dist/{chunk-4XKUHP6G.js → chunk-4D4PEBGB.js} +2 -2
- package/dist/{chunk-QAV3HGVS.js → chunk-4Q45TLZ5.js} +8 -8
- package/dist/chunk-6GN5WPYZ.js +0 -0
- package/dist/chunk-B3SJXYIJ.js +0 -0
- package/dist/chunk-BD2ICC4D.js +0 -0
- package/dist/{chunk-GKKHXZCT.js → chunk-BGQBELGJ.js} +201 -2
- package/dist/{chunk-2XXAAIN3.js → chunk-BOD5V52W.js} +1 -1
- package/dist/{chunk-JPBRMUM6.js → chunk-CG25RYQQ.js} +87 -41
- package/dist/chunk-ED4UPEJX.js +0 -0
- package/dist/{chunk-J5IBSVB3.js → chunk-EUXOHTUK.js} +42 -25
- package/dist/{chunk-K5YT5GX2.js → chunk-FADNFPZO.js} +4 -4
- package/dist/{chunk-TIGKDMIA.js → chunk-FGOD5MQ4.js} +550 -69
- package/dist/{chunk-MDCKV2NF.js → chunk-HC5N3BDL.js} +2 -2
- package/dist/{chunk-GJBHOJCZ.js → chunk-I77RYPOH.js} +201 -2
- package/dist/{chunk-WS5LFFU3.js → chunk-JUWVAMRS.js} +333 -64
- package/dist/chunk-KGZBYWZ3.js +0 -0
- package/dist/chunk-KLBOWJ74.js +0 -0
- package/dist/chunk-KPIYENTF.js +0 -0
- package/dist/chunk-N5PM7HGQ.js +0 -0
- package/dist/chunk-NSTQ6QKD.js +0 -0
- package/dist/{chunk-HQQNLDVC.js → chunk-O3JEC2UJ.js} +8 -8
- package/dist/{chunk-TQ4S5AGJ.js → chunk-OHWN4JEL.js} +7 -5
- package/dist/{chunk-GSKRGF7B.js → chunk-R2W7A2UO.js} +4 -4
- package/dist/chunk-S42XV45P.js +0 -0
- package/dist/{chunk-PM3C2J6K.js → chunk-TUGKX5BD.js} +42 -25
- package/dist/{chunk-IFZ4MOTN.js → chunk-UG5N7ET3.js} +7 -5
- package/dist/{chunk-CYM5DCHI.js → chunk-VHTIHOHE.js} +87 -42
- package/dist/{chunk-AT346U23.js → chunk-VZJVKQHH.js} +1 -1
- package/dist/{chunk-FSCQXMGI.js → chunk-WT5HSFOU.js} +1 -0
- package/dist/connected-teams-IJUJQ7R6.js +0 -0
- package/dist/{coordinator-prompt-IPL4Z6SL.js → coordinator-prompt-2LD3C74O.js} +4 -4
- package/dist/{coordinator-prompt-2OWSUAUR.js → coordinator-prompt-FBX7Y4EM.js} +4 -4
- package/dist/cursor-recover-NOQYOFRF.js +0 -0
- package/dist/{global-workspace-NIKZAKOO.js → global-workspace-NG7SU3GV.js} +5 -5
- package/dist/{global-workspace-JDUCUL7S.js → global-workspace-OCV77UYF.js} +5 -5
- package/dist/index.cjs +1064 -187
- package/dist/index.d.cts +239 -62
- package/dist/index.d.ts +239 -62
- package/dist/index.js +144 -20
- package/dist/mcp/run-stdio.cjs +759 -170
- package/dist/mcp/run-stdio.js +46 -18
- package/dist/oauth-YJBAURIJ.js +0 -0
- package/dist/{orchestrator-KK3CUW37.js → orchestrator-4BEZJ2BU.js} +8 -8
- package/dist/{orchestrator-6I47JMU2.js → orchestrator-I2T44CRP.js} +9 -9
- package/dist/paths-QCI3MTO5.js +0 -0
- package/dist/plan-file-PHVKUAEE.js +0 -0
- package/dist/run-USF6FRKV.js +0 -0
- package/dist/{thread-store-FADXSMEJ.js → thread-store-57ZLHR3A.js} +1 -1
- package/dist/{thread-store-CRLQJ2HM.js → thread-store-WGASXXXR.js} +1 -1
- package/dist/title-4WAKWZRF.js +0 -0
- package/dist/{workspaces-5EWNNALF.js → workspaces-4FUQW5LD.js} +6 -6
- package/dist/{workspaces-KZA3TCEE.js → workspaces-PU5YHQ7Z.js} +6 -6
- package/dist/{worktree-MX7XBX6Z.js → worktree-GKLPPNWR.js} +3 -3
- package/dist/{worktree-BC6XDMQK.js → worktree-Z22HTSCU.js} +3 -3
- package/package.json +9 -8
- package/LICENSE +0 -190
|
@@ -18,13 +18,13 @@ import {
|
|
|
18
18
|
stripBrightsyNdjsonNoise,
|
|
19
19
|
sumUsageList,
|
|
20
20
|
toolDescription
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-VHTIHOHE.js";
|
|
22
22
|
import {
|
|
23
23
|
addWorkspace,
|
|
24
24
|
ensureWorkspace,
|
|
25
25
|
removeWorkspace,
|
|
26
26
|
syncWorkspacesFromThreads
|
|
27
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
145
|
+
} from "./chunk-I77RYPOH.js";
|
|
141
146
|
import {
|
|
142
147
|
stripNestedElectronEnv
|
|
143
148
|
} from "./chunk-4TR3HZFT.js";
|
|
@@ -155,7 +160,7 @@ import {
|
|
|
155
160
|
updateThread,
|
|
156
161
|
withThreadLock,
|
|
157
162
|
writeThread
|
|
158
|
-
} from "./chunk-
|
|
163
|
+
} from "./chunk-TUGKX5BD.js";
|
|
159
164
|
import {
|
|
160
165
|
appDataDir,
|
|
161
166
|
findConventionSetup,
|
|
@@ -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
|
|
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
|
|
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-
|
|
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
|
|
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-
|
|
736
|
+
const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-NG7SU3GV.js");
|
|
732
737
|
if (isGlobalThread2(thread)) {
|
|
733
|
-
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-
|
|
738
|
+
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-2LD3C74O.js");
|
|
734
739
|
ensureGlobalCoordinatorCwd2(
|
|
735
740
|
isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
|
|
736
741
|
);
|
|
@@ -977,6 +982,69 @@ import { dirname, join as join4 } from "path";
|
|
|
977
982
|
// src/threads/chat-tabs.ts
|
|
978
983
|
import { randomUUID } from "crypto";
|
|
979
984
|
|
|
985
|
+
// src/composer/context-estimate.ts
|
|
986
|
+
var CHARS_PER_CONTEXT_TOKEN = 4;
|
|
987
|
+
function estimateMessageChars(message) {
|
|
988
|
+
let n = message.text.length + 16;
|
|
989
|
+
for (const part of message.parts ?? []) {
|
|
990
|
+
n += estimatePartChars(part);
|
|
991
|
+
}
|
|
992
|
+
return n;
|
|
993
|
+
}
|
|
994
|
+
function estimatePartChars(part) {
|
|
995
|
+
switch (part.type) {
|
|
996
|
+
case "text":
|
|
997
|
+
case "thinking":
|
|
998
|
+
return part.text.length;
|
|
999
|
+
case "tool": {
|
|
1000
|
+
const input = part.input ? JSON.stringify(part.input) : "";
|
|
1001
|
+
return part.name.length + (part.description?.length ?? 0) + (part.detail?.length ?? 0) + (part.result?.length ?? 0) + input.length + 32;
|
|
1002
|
+
}
|
|
1003
|
+
default:
|
|
1004
|
+
return 0;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
function estimateThreadChars(messages) {
|
|
1008
|
+
return messages.reduce((sum, m) => sum + estimateMessageChars(m), 0);
|
|
1009
|
+
}
|
|
1010
|
+
function estimateOccupancyTokens(messages) {
|
|
1011
|
+
return Math.ceil(estimateThreadChars(messages) / CHARS_PER_CONTEXT_TOKEN);
|
|
1012
|
+
}
|
|
1013
|
+
function threadHasCompactedContext(messages) {
|
|
1014
|
+
return messages.some((m) => m.role === "summary");
|
|
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
|
+
}
|
|
1025
|
+
function forwardContextUsage(usage, messages) {
|
|
1026
|
+
if (!usage) return null;
|
|
1027
|
+
if (!threadHasCompactedContext(messages)) return usage;
|
|
1028
|
+
const remaining = estimateOccupancyTokens(messages);
|
|
1029
|
+
const current = contextTokens(usage);
|
|
1030
|
+
if (remaining <= 0 || remaining >= current) return usage;
|
|
1031
|
+
return { ...usage, lastRequestTokens: remaining };
|
|
1032
|
+
}
|
|
1033
|
+
function applyForwardOccupancy(messages) {
|
|
1034
|
+
const tokens = estimateOccupancyTokens(messages);
|
|
1035
|
+
if (tokens <= 0) return messages;
|
|
1036
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1037
|
+
const m = messages[i];
|
|
1038
|
+
if (m?.role !== "agent" || !m.usage) continue;
|
|
1039
|
+
const current = contextTokens(m.usage);
|
|
1040
|
+
if (tokens >= current) return messages;
|
|
1041
|
+
const next = messages.slice();
|
|
1042
|
+
next[i] = { ...m, usage: { ...m.usage, lastRequestTokens: tokens } };
|
|
1043
|
+
return next;
|
|
1044
|
+
}
|
|
1045
|
+
return messages;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
980
1048
|
// src/composer/summarize.ts
|
|
981
1049
|
var MAX_TRANSCRIPT_CHARS = 12e4;
|
|
982
1050
|
var MAX_SUMMARY_CHARS = 6e3;
|
|
@@ -1085,29 +1153,6 @@ var CONTEXT_KEEP_RECENT_CHARS = 24e3;
|
|
|
1085
1153
|
var CONTEXT_KEEP_RECENT_MESSAGES = 12;
|
|
1086
1154
|
var CONTEXT_MIN_MESSAGES = 10;
|
|
1087
1155
|
var SESSION_RESET_OCCUPANCY_TOKENS = 75e4;
|
|
1088
|
-
function estimateMessageChars(message) {
|
|
1089
|
-
let n = message.text.length + 16;
|
|
1090
|
-
for (const part of message.parts ?? []) {
|
|
1091
|
-
n += estimatePartChars(part);
|
|
1092
|
-
}
|
|
1093
|
-
return n;
|
|
1094
|
-
}
|
|
1095
|
-
function estimatePartChars(part) {
|
|
1096
|
-
switch (part.type) {
|
|
1097
|
-
case "text":
|
|
1098
|
-
case "thinking":
|
|
1099
|
-
return part.text.length;
|
|
1100
|
-
case "tool": {
|
|
1101
|
-
const input = part.input ? JSON.stringify(part.input) : "";
|
|
1102
|
-
return part.name.length + (part.description?.length ?? 0) + (part.detail?.length ?? 0) + (part.result?.length ?? 0) + input.length + 32;
|
|
1103
|
-
}
|
|
1104
|
-
default:
|
|
1105
|
-
return 0;
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
function estimateThreadChars(messages) {
|
|
1109
|
-
return messages.reduce((sum, m) => sum + estimateMessageChars(m), 0);
|
|
1110
|
-
}
|
|
1111
1156
|
function shouldCompactContext(messages, thresholds = {}) {
|
|
1112
1157
|
const maxChars = thresholds.maxChars ?? CONTEXT_COMPACT_CHARS;
|
|
1113
1158
|
const minMessages = thresholds.minMessages ?? CONTEXT_MIN_MESSAGES;
|
|
@@ -1244,8 +1289,11 @@ async function maybeCompactContext(thread, thresholds = {}, summarize = summariz
|
|
|
1244
1289
|
const { summary, method } = await summarize(transcript, {
|
|
1245
1290
|
cwd: thread.worktreePath
|
|
1246
1291
|
});
|
|
1247
|
-
|
|
1292
|
+
let messages = applyCompaction(thread.messages, summary, thresholds);
|
|
1248
1293
|
const resetSession = shouldResetSessionForOccupancy({ messages: thread.messages });
|
|
1294
|
+
if (resetSession) {
|
|
1295
|
+
messages = applyForwardOccupancy(messages);
|
|
1296
|
+
}
|
|
1249
1297
|
const next = {
|
|
1250
1298
|
...thread,
|
|
1251
1299
|
messages,
|
|
@@ -2826,7 +2874,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
2826
2874
|
});
|
|
2827
2875
|
await requireAgent(resolved.agent);
|
|
2828
2876
|
if (input.cowboy) {
|
|
2829
|
-
const { cowboyModeEnabled } = await import("./app-settings-
|
|
2877
|
+
const { cowboyModeEnabled } = await import("./app-settings-WJDCJFSY.js");
|
|
2830
2878
|
if (!cowboyModeEnabled()) {
|
|
2831
2879
|
throw new Error(
|
|
2832
2880
|
"Cowboy mode is off. Enable it in Settings \u2192 Advanced."
|
|
@@ -2882,7 +2930,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
2882
2930
|
let sourceIsFork = false;
|
|
2883
2931
|
if (!input.cowboy && sourceType === "branch" && (!sourceRef || sourceRef === "HEAD" || sourceRef === "default")) {
|
|
2884
2932
|
if (isAbleTimeConnected() && getIssueSource() === "abletime") {
|
|
2885
|
-
const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-
|
|
2933
|
+
const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-QEVAE67D.js");
|
|
2886
2934
|
const task = await ensureAbleTimeTask({
|
|
2887
2935
|
title: title?.trim() || "Untitled work",
|
|
2888
2936
|
description: input.prompt?.trim() || void 0
|
|
@@ -2955,7 +3003,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
2955
3003
|
return readThread(thread.id) ?? thread;
|
|
2956
3004
|
}
|
|
2957
3005
|
async function listLinearIssues(agent, repoPath) {
|
|
2958
|
-
const { getAdapter: getAdapter2 } = await import("./agents-
|
|
3006
|
+
const { getAdapter: getAdapter2 } = await import("./agents-E5AAMHDY.js");
|
|
2959
3007
|
await requireAgent(agent, { requireLinear: true });
|
|
2960
3008
|
const adapter = getAdapter2(agent);
|
|
2961
3009
|
if (!adapter.listLinearIssues) {
|
|
@@ -3352,7 +3400,7 @@ async function adoptThread(input) {
|
|
|
3352
3400
|
messages: input.messages ?? []
|
|
3353
3401
|
});
|
|
3354
3402
|
writeThread(thread);
|
|
3355
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-
|
|
3403
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-PU5YHQ7Z.js");
|
|
3356
3404
|
await ensureWorkspace2(repoPath);
|
|
3357
3405
|
return thread;
|
|
3358
3406
|
}
|
|
@@ -4695,6 +4743,93 @@ async function confirmLand(thread, opts) {
|
|
|
4695
4743
|
import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync3 } from "fs";
|
|
4696
4744
|
import { homedir } from "os";
|
|
4697
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
|
+
}
|
|
4698
4833
|
function toCommand(name) {
|
|
4699
4834
|
return name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
4700
4835
|
}
|
|
@@ -4812,7 +4947,13 @@ function discoverSkills(worktreePath) {
|
|
|
4812
4947
|
scanSkillsDir(abs, "user", collected);
|
|
4813
4948
|
}
|
|
4814
4949
|
scanClaudePluginSkills(join11(home, ".claude/plugins"), collected);
|
|
4815
|
-
|
|
4950
|
+
collected.push(...bundledSkills());
|
|
4951
|
+
const rank = {
|
|
4952
|
+
workspace: 0,
|
|
4953
|
+
user: 1,
|
|
4954
|
+
cli: 2,
|
|
4955
|
+
bundled: 3
|
|
4956
|
+
};
|
|
4816
4957
|
const byCommand = /* @__PURE__ */ new Map();
|
|
4817
4958
|
for (const skill of collected) {
|
|
4818
4959
|
const prev = byCommand.get(skill.command);
|
|
@@ -4823,6 +4964,14 @@ function discoverSkills(worktreePath) {
|
|
|
4823
4964
|
return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
|
|
4824
4965
|
}
|
|
4825
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
|
+
}
|
|
4826
4975
|
const raw = readFileSync9(skillPath, "utf8");
|
|
4827
4976
|
if (raw.startsWith("---")) {
|
|
4828
4977
|
const end = raw.indexOf("\n---", 3);
|
|
@@ -4916,8 +5065,39 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
|
|
|
4916
5065
|
}
|
|
4917
5066
|
|
|
4918
5067
|
// src/agents/instructions.ts
|
|
4919
|
-
import { existsSync as
|
|
4920
|
-
import { join as
|
|
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
|
|
4921
5101
|
function normPath(p) {
|
|
4922
5102
|
return p.replace(/\/+$/, "");
|
|
4923
5103
|
}
|
|
@@ -5025,6 +5205,7 @@ function formatWorktreeReminder() {
|
|
|
5025
5205
|
function formatProcessGuideDirective() {
|
|
5026
5206
|
return [
|
|
5027
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.",
|
|
5028
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.",
|
|
5029
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.",
|
|
5030
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).",
|
|
@@ -5032,6 +5213,23 @@ function formatProcessGuideDirective() {
|
|
|
5032
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."
|
|
5033
5214
|
].join("\n");
|
|
5034
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
|
+
}
|
|
5035
5233
|
function formatArtifactDirective() {
|
|
5036
5234
|
return [
|
|
5037
5235
|
"Sideboard side column (desktop UI):",
|
|
@@ -5081,8 +5279,8 @@ function loadAgentInstructions(worktreePath, agent) {
|
|
|
5081
5279
|
const out = [];
|
|
5082
5280
|
for (const rel of candidates) {
|
|
5083
5281
|
if (seenPaths.has(rel)) continue;
|
|
5084
|
-
const abs =
|
|
5085
|
-
if (!
|
|
5282
|
+
const abs = join13(worktreePath, rel);
|
|
5283
|
+
if (!existsSync14(abs)) continue;
|
|
5086
5284
|
try {
|
|
5087
5285
|
if (!statSync4(abs).isFile()) continue;
|
|
5088
5286
|
let content = readFileSync10(abs, "utf8");
|
|
@@ -5126,6 +5324,253 @@ function withAgentInstructions(prompt, files) {
|
|
|
5126
5324
|
${prompt}`;
|
|
5127
5325
|
}
|
|
5128
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
|
+
|
|
5129
5574
|
// src/threads/sync-branch.ts
|
|
5130
5575
|
async function syncThreadBranchFromGit(threadId) {
|
|
5131
5576
|
const thread = readThread(threadId);
|
|
@@ -5158,8 +5603,8 @@ async function syncThreadBranchFromGit(threadId) {
|
|
|
5158
5603
|
|
|
5159
5604
|
// src/store/schedules.ts
|
|
5160
5605
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
5161
|
-
import { existsSync as
|
|
5162
|
-
import { join as
|
|
5606
|
+
import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
|
|
5607
|
+
import { join as join14 } from "path";
|
|
5163
5608
|
import { Cron } from "croner";
|
|
5164
5609
|
var DURATION_RE = /^(\d+)(s|m|h|d)$/i;
|
|
5165
5610
|
var UNIT_MS = {
|
|
@@ -5169,7 +5614,7 @@ var UNIT_MS = {
|
|
|
5169
5614
|
d: 864e5
|
|
5170
5615
|
};
|
|
5171
5616
|
function schedulesPath() {
|
|
5172
|
-
return
|
|
5617
|
+
return join14(appDataDir(), "schedules.json");
|
|
5173
5618
|
}
|
|
5174
5619
|
function parseDurationMs(every) {
|
|
5175
5620
|
const m = every.trim().match(DURATION_RE);
|
|
@@ -5275,7 +5720,7 @@ function normalizeTask(raw) {
|
|
|
5275
5720
|
}
|
|
5276
5721
|
function readAll() {
|
|
5277
5722
|
const path = schedulesPath();
|
|
5278
|
-
if (!
|
|
5723
|
+
if (!existsSync15(path)) return [];
|
|
5279
5724
|
try {
|
|
5280
5725
|
const parsed = JSON.parse(readFileSync11(path, "utf8"));
|
|
5281
5726
|
if (!Array.isArray(parsed)) return [];
|
|
@@ -5403,7 +5848,7 @@ function formatScheduledPrompt(name, prompt) {
|
|
|
5403
5848
|
${prompt}`;
|
|
5404
5849
|
}
|
|
5405
5850
|
async function defaultDeps() {
|
|
5406
|
-
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-
|
|
5851
|
+
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-I2T44CRP.js");
|
|
5407
5852
|
const orch = getOrchestrator2();
|
|
5408
5853
|
return {
|
|
5409
5854
|
findThread: (id) => findThreadByRef(id),
|
|
@@ -5609,7 +6054,7 @@ var Orchestrator = class {
|
|
|
5609
6054
|
}
|
|
5610
6055
|
continue;
|
|
5611
6056
|
}
|
|
5612
|
-
if (!
|
|
6057
|
+
if (!existsSync16(thread.worktreePath)) {
|
|
5613
6058
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
5614
6059
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
5615
6060
|
continue;
|
|
@@ -5628,7 +6073,7 @@ var Orchestrator = class {
|
|
|
5628
6073
|
orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
|
|
5629
6074
|
});
|
|
5630
6075
|
}
|
|
5631
|
-
const { autoCleanupOrphansEnabled } = await import("./app-settings-
|
|
6076
|
+
const { autoCleanupOrphansEnabled } = await import("./app-settings-WJDCJFSY.js");
|
|
5632
6077
|
if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
|
|
5633
6078
|
await cleanupOrphanWorktrees({ repoPaths });
|
|
5634
6079
|
}
|
|
@@ -5679,7 +6124,7 @@ var Orchestrator = class {
|
|
|
5679
6124
|
if (thread.status === "archived") continue;
|
|
5680
6125
|
const handle = this.activeTurns.get(thread.id);
|
|
5681
6126
|
if (handle) {
|
|
5682
|
-
const pid =
|
|
6127
|
+
const pid = handle.pid;
|
|
5683
6128
|
if (typeof pid === "number" && pid > 0 && !isPidAlive(pid)) {
|
|
5684
6129
|
handle.kill();
|
|
5685
6130
|
}
|
|
@@ -5868,7 +6313,7 @@ var Orchestrator = class {
|
|
|
5868
6313
|
}
|
|
5869
6314
|
await setup;
|
|
5870
6315
|
if (skipSetup) return;
|
|
5871
|
-
const { autoRunAfterSetupEnabled } = await import("./app-settings-
|
|
6316
|
+
const { autoRunAfterSetupEnabled } = await import("./app-settings-WJDCJFSY.js");
|
|
5872
6317
|
if (autoRunAfterSetupEnabled()) {
|
|
5873
6318
|
try {
|
|
5874
6319
|
await this.startDev(threadId);
|
|
@@ -6130,7 +6575,9 @@ var Orchestrator = class {
|
|
|
6130
6575
|
].join("\n") : null
|
|
6131
6576
|
].filter(Boolean).join("\n") : null;
|
|
6132
6577
|
const artifactReminder = thread.agent !== "brightsy" ? formatUiReminder() : null;
|
|
6578
|
+
const longRunningReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatLongRunningReminder() : null;
|
|
6133
6579
|
const worktreeReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatWorktreeReminder() : null;
|
|
6580
|
+
const optionalServicesReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatOptionalServicesReminder(loadAppSettings().integrations) : null;
|
|
6134
6581
|
const slackReplyContext = formatSlackRepliesForTurn(
|
|
6135
6582
|
pendingSlackExternalReplies(thread.messages)
|
|
6136
6583
|
);
|
|
@@ -6138,7 +6585,9 @@ var Orchestrator = class {
|
|
|
6138
6585
|
thread.planMode ? PLAN_MODE_INSTRUCTION : null,
|
|
6139
6586
|
orchestrationReminder,
|
|
6140
6587
|
worktreeReminder,
|
|
6588
|
+
optionalServicesReminder,
|
|
6141
6589
|
artifactReminder,
|
|
6590
|
+
longRunningReminder,
|
|
6142
6591
|
slackReplyContext,
|
|
6143
6592
|
expandedPrompt
|
|
6144
6593
|
].filter(Boolean).join("\n\n");
|
|
@@ -6158,7 +6607,7 @@ var Orchestrator = class {
|
|
|
6158
6607
|
}
|
|
6159
6608
|
const isBrightsy = fresh.agent === "brightsy";
|
|
6160
6609
|
const isOrchestration = isOrchestratorThread(fresh);
|
|
6161
|
-
const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-
|
|
6610
|
+
const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-WJDCJFSY.js");
|
|
6162
6611
|
const gitAuthMode = getGithubGitAuthMode();
|
|
6163
6612
|
const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
|
|
6164
6613
|
githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
|
|
@@ -6167,6 +6616,8 @@ var Orchestrator = class {
|
|
|
6167
6616
|
gitAuthMode
|
|
6168
6617
|
});
|
|
6169
6618
|
const artifactDirective = isBrightsy ? null : formatArtifactDirective();
|
|
6619
|
+
const longRunningDirective = isBrightsy || isOrchestration ? null : formatLongRunningDirective();
|
|
6620
|
+
const optionalServicesDirective = isBrightsy || isOrchestration ? null : formatOptionalServicesDirective(loadAppSettings().integrations);
|
|
6170
6621
|
const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
|
|
6171
6622
|
const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled() ? formatRenameBranchDirective(fresh, {
|
|
6172
6623
|
customPrompt: settings?.prompts?.renameBranch
|
|
@@ -6196,7 +6647,9 @@ var Orchestrator = class {
|
|
|
6196
6647
|
const cachedPrefix = fresh.sessionId ? "" : [
|
|
6197
6648
|
coordinatorDirective,
|
|
6198
6649
|
worktreeDirective,
|
|
6650
|
+
optionalServicesDirective,
|
|
6199
6651
|
artifactDirective,
|
|
6652
|
+
longRunningDirective,
|
|
6200
6653
|
renameBranchDirective,
|
|
6201
6654
|
seed
|
|
6202
6655
|
].filter(Boolean).join("\n\n---\n\n");
|
|
@@ -6303,7 +6756,9 @@ var Orchestrator = class {
|
|
|
6303
6756
|
const retryPrefix = [
|
|
6304
6757
|
coordinatorDirective,
|
|
6305
6758
|
worktreeDirective,
|
|
6759
|
+
optionalServicesDirective,
|
|
6306
6760
|
artifactDirective,
|
|
6761
|
+
longRunningDirective,
|
|
6307
6762
|
renameBranchDirective,
|
|
6308
6763
|
retrySeed
|
|
6309
6764
|
].filter(Boolean).join("\n\n---\n\n");
|
|
@@ -6503,7 +6958,7 @@ var Orchestrator = class {
|
|
|
6503
6958
|
const stopped = writeLiveStatus(thread.id, "stopped") ?? readThread(thread.id) ?? thread;
|
|
6504
6959
|
if (stopped.status === "stopped") {
|
|
6505
6960
|
this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
|
|
6506
|
-
if (opts?.notifyParent !== false) {
|
|
6961
|
+
if (inFlight && opts?.notifyParent !== false) {
|
|
6507
6962
|
notifyParentOfChildHalt(stopped, "stopped", (id, prompt) => this.send(id, prompt));
|
|
6508
6963
|
}
|
|
6509
6964
|
}
|
|
@@ -6980,7 +7435,7 @@ var Orchestrator = class {
|
|
|
6980
7435
|
updateThread(thread.id, { title: meta.title });
|
|
6981
7436
|
}
|
|
6982
7437
|
}
|
|
6983
|
-
const { autoArchiveOnMergeEnabled } = await import("./app-settings-
|
|
7438
|
+
const { autoArchiveOnMergeEnabled } = await import("./app-settings-WJDCJFSY.js");
|
|
6984
7439
|
const latest = this.requireThread(thread.id);
|
|
6985
7440
|
if (!shouldAutoArchiveOnPrMerge({
|
|
6986
7441
|
previousPrState: prevState || null,
|
|
@@ -7262,7 +7717,7 @@ var Orchestrator = class {
|
|
|
7262
7717
|
}
|
|
7263
7718
|
async archiveUnlocked(threadRef) {
|
|
7264
7719
|
const thread = this.requireThread(threadRef);
|
|
7265
|
-
this.stop(thread.id);
|
|
7720
|
+
this.stop(thread.id, { notifyParent: false });
|
|
7266
7721
|
this.releaseOrchestratorCaffeinate(thread);
|
|
7267
7722
|
if (isGlobalThread(thread)) {
|
|
7268
7723
|
const archived2 = setStatus(thread.id, "archived");
|
|
@@ -7290,7 +7745,7 @@ var Orchestrator = class {
|
|
|
7290
7745
|
this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
|
|
7291
7746
|
if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
|
|
7292
7747
|
try {
|
|
7293
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-
|
|
7748
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-PU5YHQ7Z.js");
|
|
7294
7749
|
await ensureWorkspace2(thread.repoPath);
|
|
7295
7750
|
} catch {
|
|
7296
7751
|
}
|
|
@@ -7303,7 +7758,7 @@ var Orchestrator = class {
|
|
|
7303
7758
|
}
|
|
7304
7759
|
async purgeUnlocked(threadRef, opts) {
|
|
7305
7760
|
const thread = this.requireThread(threadRef);
|
|
7306
|
-
this.stop(thread.id);
|
|
7761
|
+
this.stop(thread.id, { notifyParent: false });
|
|
7307
7762
|
this.releaseOrchestratorCaffeinate(thread);
|
|
7308
7763
|
if (isGlobalThread(thread)) {
|
|
7309
7764
|
deleteThreadRecord(thread.id);
|
|
@@ -7317,7 +7772,7 @@ var Orchestrator = class {
|
|
|
7317
7772
|
await runArchiveScript(thread.repoPath, thread.worktreePath);
|
|
7318
7773
|
} catch {
|
|
7319
7774
|
}
|
|
7320
|
-
const { deleteBranchOnPurgeEnabled } = await import("./app-settings-
|
|
7775
|
+
const { deleteBranchOnPurgeEnabled } = await import("./app-settings-WJDCJFSY.js");
|
|
7321
7776
|
const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
|
|
7322
7777
|
await removeWorktree(thread.repoPath, thread.worktreePath, {
|
|
7323
7778
|
deleteBranch: deleteBranch ? thread.branchName : void 0
|
|
@@ -7338,13 +7793,13 @@ var Orchestrator = class {
|
|
|
7338
7793
|
this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
|
|
7339
7794
|
return restored2;
|
|
7340
7795
|
}
|
|
7341
|
-
if (!
|
|
7796
|
+
if (!existsSync16(thread.worktreePath)) {
|
|
7342
7797
|
if (isCowboyThread(thread) || isPrimaryCheckoutThread(thread)) {
|
|
7343
7798
|
throw new Error(
|
|
7344
7799
|
`Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
|
|
7345
7800
|
);
|
|
7346
7801
|
}
|
|
7347
|
-
const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-
|
|
7802
|
+
const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-GKLPPNWR.js");
|
|
7348
7803
|
const slug = thread.worktreePath.split("/").pop();
|
|
7349
7804
|
const dest = thread.worktreePath;
|
|
7350
7805
|
await withRepoGitLock(thread.repoPath, async () => {
|
|
@@ -7452,7 +7907,7 @@ async function startOrchestration(opts) {
|
|
|
7452
7907
|
sourceRef: "default",
|
|
7453
7908
|
...createOpts
|
|
7454
7909
|
}).catch(async () => {
|
|
7455
|
-
const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-
|
|
7910
|
+
const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-GKLPPNWR.js");
|
|
7456
7911
|
const repo = await resolveRepoRoot2(repoPath);
|
|
7457
7912
|
const def = await resolveDefaultBranch2(repo);
|
|
7458
7913
|
return orch.createThread({
|
|
@@ -7462,7 +7917,7 @@ async function startOrchestration(opts) {
|
|
|
7462
7917
|
repoPath: repo
|
|
7463
7918
|
});
|
|
7464
7919
|
});
|
|
7465
|
-
const { updateThread: upd } = await import("./thread-store-
|
|
7920
|
+
const { updateThread: upd } = await import("./thread-store-WGASXXXR.js");
|
|
7466
7921
|
const updated = upd(thread.id, {
|
|
7467
7922
|
sourceType: "orchestration",
|
|
7468
7923
|
sourceRef: goal
|
|
@@ -7518,6 +7973,14 @@ export {
|
|
|
7518
7973
|
spawnAgentTurn,
|
|
7519
7974
|
AGENT_GIT_ACTIONS,
|
|
7520
7975
|
agentGitPrompt,
|
|
7976
|
+
CHARS_PER_CONTEXT_TOKEN,
|
|
7977
|
+
estimateMessageChars,
|
|
7978
|
+
estimateThreadChars,
|
|
7979
|
+
estimateOccupancyTokens,
|
|
7980
|
+
threadHasCompactedContext,
|
|
7981
|
+
forwardOccupancyTokens,
|
|
7982
|
+
forwardContextUsage,
|
|
7983
|
+
applyForwardOccupancy,
|
|
7521
7984
|
summarizeConversation,
|
|
7522
7985
|
extractiveSummary,
|
|
7523
7986
|
CONTEXT_COMPACT_CHARS,
|
|
@@ -7525,8 +7988,6 @@ export {
|
|
|
7525
7988
|
CONTEXT_KEEP_RECENT_MESSAGES,
|
|
7526
7989
|
CONTEXT_MIN_MESSAGES,
|
|
7527
7990
|
SESSION_RESET_OCCUPANCY_TOKENS,
|
|
7528
|
-
estimateMessageChars,
|
|
7529
|
-
estimateThreadChars,
|
|
7530
7991
|
shouldCompactContext,
|
|
7531
7992
|
splitForCompaction,
|
|
7532
7993
|
formatMessagesAsTranscript,
|
|
@@ -7636,18 +8097,38 @@ export {
|
|
|
7636
8097
|
getDiffSummary,
|
|
7637
8098
|
previewLand,
|
|
7638
8099
|
confirmLand,
|
|
8100
|
+
LONG_RUNNING_SKILL_COMMAND,
|
|
8101
|
+
BUNDLED_LONG_RUNNING_PATH,
|
|
8102
|
+
BUNDLED_SKILL_PREFIX,
|
|
7639
8103
|
discoverSkills,
|
|
7640
8104
|
readSkillBody,
|
|
7641
8105
|
expandComposerPrompt,
|
|
8106
|
+
packagedDetachedJobPath,
|
|
8107
|
+
resolveDetachedJobScript,
|
|
8108
|
+
formatDetachedJobInvoke,
|
|
7642
8109
|
formatRenameBranchDirective,
|
|
7643
8110
|
formatWorktreeDirective,
|
|
7644
8111
|
formatWorktreeReminder,
|
|
7645
8112
|
formatProcessGuideDirective,
|
|
8113
|
+
formatLongRunningDirective,
|
|
8114
|
+
formatLongRunningReminder,
|
|
7646
8115
|
formatArtifactDirective,
|
|
7647
8116
|
formatUiReminder,
|
|
7648
8117
|
loadAgentInstructions,
|
|
7649
8118
|
formatAgentInstructions,
|
|
7650
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,
|
|
7651
8132
|
isPidAlive,
|
|
7652
8133
|
waitForPidExit,
|
|
7653
8134
|
Orchestrator,
|