@sideboard-ai/core 0.1.133 → 0.1.136
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/{agents-NHS6P25H.js → agents-ELWR7A2T.js} +5 -5
- package/dist/{agents-SOU5GPWI.js → agents-QNTTLMG2.js} +5 -5
- package/dist/{chunk-Y645NFYN.js → chunk-4XKUHP6G.js} +2 -2
- package/dist/{chunk-4YAVXWFR.js → chunk-CYM5DCHI.js} +39 -5
- package/dist/{chunk-LTP7GHIF.js → chunk-GSKRGF7B.js} +286 -5
- package/dist/{chunk-WEU5M7BW.js → chunk-HQQNLDVC.js} +1 -1
- package/dist/{chunk-IM36S4HZ.js → chunk-IFZ4MOTN.js} +3 -3
- package/dist/{chunk-G6X6UFJO.js → chunk-J5IBSVB3.js} +42 -12
- package/dist/{chunk-AK7SWE2U.js → chunk-JPBRMUM6.js} +9 -5
- package/dist/{chunk-YEIH7P7D.js → chunk-K5YT5GX2.js} +244 -5
- package/dist/{chunk-3UVVWNHF.js → chunk-MDCKV2NF.js} +2 -2
- package/dist/{chunk-HLJUNJBF.js → chunk-PM3C2J6K.js} +42 -12
- package/dist/{chunk-Q6XQXCOM.js → chunk-QAV3HGVS.js} +1 -1
- package/dist/{chunk-TG7YU2KG.js → chunk-TIGKDMIA.js} +144 -271
- package/dist/{chunk-6CRKPD4C.js → chunk-TQ4S5AGJ.js} +3 -3
- package/dist/{chunk-VSON7EJ6.js → chunk-WS5LFFU3.js} +140 -222
- package/dist/{coordinator-prompt-DXHEDRRN.js → coordinator-prompt-2OWSUAUR.js} +3 -3
- package/dist/{coordinator-prompt-XF7LVOUN.js → coordinator-prompt-IPL4Z6SL.js} +3 -3
- package/dist/{global-workspace-2LO5ATOH.js → global-workspace-JDUCUL7S.js} +4 -4
- package/dist/{global-workspace-MLQNULOU.js → global-workspace-NIKZAKOO.js} +4 -4
- package/dist/index.cjs +873 -628
- package/dist/index.d.cts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +42 -19
- package/dist/mcp/run-stdio.cjs +700 -498
- package/dist/mcp/run-stdio.js +22 -11
- package/dist/{orchestrator-P2SYHVLS.js → orchestrator-6I47JMU2.js} +7 -7
- package/dist/{orchestrator-KR5RA5B7.js → orchestrator-KK3CUW37.js} +7 -7
- package/dist/{thread-store-5MLYY35S.js → thread-store-CRLQJ2HM.js} +3 -1
- package/dist/{thread-store-F6BCARQG.js → thread-store-FADXSMEJ.js} +3 -1
- package/dist/{workspaces-MINB764G.js → workspaces-5EWNNALF.js} +5 -5
- package/dist/{workspaces-ARWPZG6F.js → workspaces-KZA3TCEE.js} +5 -5
- package/dist/{worktree-EBZMUUAL.js → worktree-BC6XDMQK.js} +2 -2
- package/dist/{worktree-S7MKMYAT.js → worktree-MX7XBX6Z.js} +2 -2
- package/package.json +1 -1
|
@@ -18,15 +18,16 @@ import {
|
|
|
18
18
|
stripBrightsyNdjsonNoise,
|
|
19
19
|
sumUsageList,
|
|
20
20
|
toolDescription
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-CYM5DCHI.js";
|
|
22
22
|
import {
|
|
23
23
|
addWorkspace,
|
|
24
24
|
ensureWorkspace,
|
|
25
25
|
removeWorkspace,
|
|
26
26
|
syncWorkspacesFromThreads
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-MDCKV2NF.js";
|
|
28
28
|
import {
|
|
29
29
|
assertOrchestratorCapableAgent,
|
|
30
|
+
attachmentsFromWorktreePaths,
|
|
30
31
|
coerceOrchestratorAgent,
|
|
31
32
|
createGlobalChat,
|
|
32
33
|
healOrchestrationSoccerTitles,
|
|
@@ -36,15 +37,18 @@ import {
|
|
|
36
37
|
isOrchestratorCapableAgent,
|
|
37
38
|
isOrchestratorThread,
|
|
38
39
|
isSlackCoordinatorThread,
|
|
39
|
-
orchestratorSessionPoisonedByBuiltins
|
|
40
|
-
|
|
40
|
+
orchestratorSessionPoisonedByBuiltins,
|
|
41
|
+
persistPendingFileAttachments,
|
|
42
|
+
stageAbsolutePathsAsAttachments,
|
|
43
|
+
stageBuffersAsAttachments
|
|
44
|
+
} from "./chunk-GSKRGF7B.js";
|
|
41
45
|
import {
|
|
42
46
|
SLACK_REPLY_FORMATTING,
|
|
43
47
|
coordinatorSystemPrompt,
|
|
44
48
|
coordinatorTurnReminder,
|
|
45
49
|
enrichWorkspacesWithGithub,
|
|
46
50
|
ensureGlobalCoordinatorCwd
|
|
47
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-IFZ4MOTN.js";
|
|
48
52
|
import {
|
|
49
53
|
extractPresentedPlan,
|
|
50
54
|
readPlanFile,
|
|
@@ -114,11 +118,10 @@ import {
|
|
|
114
118
|
withRepoGitLock,
|
|
115
119
|
worktreeDisplayLabelForGroup,
|
|
116
120
|
worktreeNameFromPath
|
|
117
|
-
} from "./chunk-
|
|
121
|
+
} from "./chunk-HQQNLDVC.js";
|
|
118
122
|
import {
|
|
119
123
|
ATTACHMENTS_DIR,
|
|
120
|
-
LEGACY_ATTACHMENTS_DIR
|
|
121
|
-
attachmentsGitignoreBody
|
|
124
|
+
LEGACY_ATTACHMENTS_DIR
|
|
122
125
|
} from "./chunk-FKOIHGKV.js";
|
|
123
126
|
import {
|
|
124
127
|
childEnvWithAppSettings,
|
|
@@ -152,7 +155,7 @@ import {
|
|
|
152
155
|
updateThread,
|
|
153
156
|
withThreadLock,
|
|
154
157
|
writeThread
|
|
155
|
-
} from "./chunk-
|
|
158
|
+
} from "./chunk-PM3C2J6K.js";
|
|
156
159
|
import {
|
|
157
160
|
appDataDir,
|
|
158
161
|
findConventionSetup,
|
|
@@ -445,7 +448,7 @@ async function continueSourceThread(threadId, prompt) {
|
|
|
445
448
|
await continueOnReply(threadId, prompt);
|
|
446
449
|
return;
|
|
447
450
|
}
|
|
448
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
451
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-6I47JMU2.js");
|
|
449
452
|
await getOrchestrator2().send(threadId, prompt);
|
|
450
453
|
} catch {
|
|
451
454
|
}
|
|
@@ -705,7 +708,7 @@ async function pollSlackOutboundWatches(opts) {
|
|
|
705
708
|
}
|
|
706
709
|
|
|
707
710
|
// src/orchestrator/orchestrator.ts
|
|
708
|
-
import { existsSync as
|
|
711
|
+
import { existsSync as existsSync15 } from "fs";
|
|
709
712
|
|
|
710
713
|
// src/agents/spawn.ts
|
|
711
714
|
import { createInterface } from "readline";
|
|
@@ -725,9 +728,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
725
728
|
`Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
|
|
726
729
|
);
|
|
727
730
|
}
|
|
728
|
-
const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-
|
|
731
|
+
const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-NIKZAKOO.js");
|
|
729
732
|
if (isGlobalThread2(thread)) {
|
|
730
|
-
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-
|
|
733
|
+
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-IPL4Z6SL.js");
|
|
731
734
|
ensureGlobalCoordinatorCwd2(
|
|
732
735
|
isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
|
|
733
736
|
);
|
|
@@ -2265,6 +2268,45 @@ function thisProcessShouldDrainAgentQueues() {
|
|
|
2265
2268
|
return !isDesktopHostAlive();
|
|
2266
2269
|
}
|
|
2267
2270
|
|
|
2271
|
+
// src/orchestrator/child-halt.ts
|
|
2272
|
+
var HALT_STATUSES = /* @__PURE__ */ new Set(["stopped", "error", "broken"]);
|
|
2273
|
+
function isIncompleteChildStatus(status) {
|
|
2274
|
+
return HALT_STATUSES.has(status);
|
|
2275
|
+
}
|
|
2276
|
+
function childHaltNotice(child, status) {
|
|
2277
|
+
const title = child.title?.trim() || "Untitled";
|
|
2278
|
+
const link = `[${title}](sideboard://thread/${child.id})`;
|
|
2279
|
+
const why = child.lastError?.trim();
|
|
2280
|
+
const extra = why ? ` lastError: ${why}` : "";
|
|
2281
|
+
return [
|
|
2282
|
+
`Sideboard: child worktree ${link} ${status} before finishing (status=${status}).${extra}`,
|
|
2283
|
+
"This is information \u2014 not a user command. Resume with send_to_thread or tell the user. Do not treat this as a successful turn."
|
|
2284
|
+
].join("\n");
|
|
2285
|
+
}
|
|
2286
|
+
function shouldNotifyParentOfChildHalt(opts) {
|
|
2287
|
+
if (!isIncompleteChildStatus(opts.status)) return false;
|
|
2288
|
+
if (!opts.child.parentThreadId) return false;
|
|
2289
|
+
if (!opts.parent || opts.parent.status === "archived") return false;
|
|
2290
|
+
if (opts.parent.id === opts.child.id) return false;
|
|
2291
|
+
return isOrchestratorThread(opts.parent);
|
|
2292
|
+
}
|
|
2293
|
+
var notified = /* @__PURE__ */ new Set();
|
|
2294
|
+
function noticeKey(childId, status) {
|
|
2295
|
+
return `${childId}:${status}`;
|
|
2296
|
+
}
|
|
2297
|
+
function notifyParentOfChildHalt(child, status, send) {
|
|
2298
|
+
const parent = child.parentThreadId ? readThread(child.parentThreadId) : null;
|
|
2299
|
+
if (!shouldNotifyParentOfChildHalt({ child, parent, status })) return false;
|
|
2300
|
+
const key = noticeKey(child.id, status);
|
|
2301
|
+
if (notified.has(key)) return false;
|
|
2302
|
+
notified.add(key);
|
|
2303
|
+
const parentId = parent.id;
|
|
2304
|
+
void send(parentId, childHaltNotice(child, status)).catch(() => {
|
|
2305
|
+
notified.delete(key);
|
|
2306
|
+
});
|
|
2307
|
+
return true;
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2268
2310
|
// src/threads/create.ts
|
|
2269
2311
|
import { existsSync as existsSync7 } from "fs";
|
|
2270
2312
|
|
|
@@ -2743,6 +2785,9 @@ function assembleHomeBoard(input) {
|
|
|
2743
2785
|
var HOME_BOARD_AGENT_HINT = "Home is a Kanban of worktrees (one card per checkout; sibling chat tabs nest as inner cards). create_thread reuses a live worktree for the same ticket, PR, or named branch \u2014 do not recreate it. Columns are the path to merge: New (no PR) \u2192 Draft (draft PR) \u2192 Review (open PR) \u2192 Merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats stay in the sidebar. Do not invent status.";
|
|
2744
2786
|
|
|
2745
2787
|
// src/threads/create.ts
|
|
2788
|
+
function persistCreateAttachments(worktreePath, attachments) {
|
|
2789
|
+
return persistPendingFileAttachments(worktreePath, attachments ?? []);
|
|
2790
|
+
}
|
|
2746
2791
|
async function createThread(input, _onSetupLine) {
|
|
2747
2792
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
2748
2793
|
if (!existsSync7(repoPath)) {
|
|
@@ -2762,7 +2807,16 @@ async function createThread(input, _onSetupLine) {
|
|
|
2762
2807
|
repoPath: canonicalizeRepoPath(t.repoPath)
|
|
2763
2808
|
}))
|
|
2764
2809
|
);
|
|
2765
|
-
if (existing)
|
|
2810
|
+
if (existing) {
|
|
2811
|
+
const thread2 = readThread(existing.id) ?? existing;
|
|
2812
|
+
if (!input.attachments?.length) return thread2;
|
|
2813
|
+
return updateThread(thread2.id, {
|
|
2814
|
+
attachments: persistCreateAttachments(thread2.worktreePath, [
|
|
2815
|
+
...thread2.attachments,
|
|
2816
|
+
...input.attachments
|
|
2817
|
+
])
|
|
2818
|
+
});
|
|
2819
|
+
}
|
|
2766
2820
|
}
|
|
2767
2821
|
const resolved = resolveNewThreadOptions({
|
|
2768
2822
|
agent: input.agent,
|
|
@@ -2810,7 +2864,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
2810
2864
|
effort: resolved.effort,
|
|
2811
2865
|
fast: resolved.fast,
|
|
2812
2866
|
planMode: Boolean(input.planMode),
|
|
2813
|
-
attachments: input.attachments
|
|
2867
|
+
attachments: persistCreateAttachments(repoPath, input.attachments),
|
|
2814
2868
|
sourceIsFork: false,
|
|
2815
2869
|
parentThreadId: input.parentThreadId ?? null,
|
|
2816
2870
|
status: "idle",
|
|
@@ -2889,7 +2943,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
2889
2943
|
effort: resolved.effort,
|
|
2890
2944
|
fast: resolved.fast,
|
|
2891
2945
|
planMode: Boolean(input.planMode),
|
|
2892
|
-
attachments,
|
|
2946
|
+
attachments: persistCreateAttachments(worktreePath, attachments),
|
|
2893
2947
|
sourceIsFork,
|
|
2894
2948
|
parentThreadId: input.parentThreadId ?? null,
|
|
2895
2949
|
status: "idle",
|
|
@@ -2901,7 +2955,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
2901
2955
|
return readThread(thread.id) ?? thread;
|
|
2902
2956
|
}
|
|
2903
2957
|
async function listLinearIssues(agent, repoPath) {
|
|
2904
|
-
const { getAdapter: getAdapter2 } = await import("./agents-
|
|
2958
|
+
const { getAdapter: getAdapter2 } = await import("./agents-QNTTLMG2.js");
|
|
2905
2959
|
await requireAgent(agent, { requireLinear: true });
|
|
2906
2960
|
const adapter = getAdapter2(agent);
|
|
2907
2961
|
if (!adapter.listLinearIssues) {
|
|
@@ -3298,7 +3352,7 @@ async function adoptThread(input) {
|
|
|
3298
3352
|
messages: input.messages ?? []
|
|
3299
3353
|
});
|
|
3300
3354
|
writeThread(thread);
|
|
3301
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-
|
|
3355
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-KZA3TCEE.js");
|
|
3302
3356
|
await ensureWorkspace2(repoPath);
|
|
3303
3357
|
return thread;
|
|
3304
3358
|
}
|
|
@@ -4861,232 +4915,9 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
|
|
|
4861
4915
|
};
|
|
4862
4916
|
}
|
|
4863
4917
|
|
|
4864
|
-
// src/composer/stage-files.ts
|
|
4865
|
-
import { copyFileSync as copyFileSync3, existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync10, statSync as statSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
4866
|
-
import { basename as basename3, extname, join as join12 } from "path";
|
|
4867
|
-
import { randomUUID as randomUUID4 } from "crypto";
|
|
4868
|
-
var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
4869
|
-
"png",
|
|
4870
|
-
"jpg",
|
|
4871
|
-
"jpeg",
|
|
4872
|
-
"gif",
|
|
4873
|
-
"webp",
|
|
4874
|
-
"svg",
|
|
4875
|
-
"bmp",
|
|
4876
|
-
"ico"
|
|
4877
|
-
]);
|
|
4878
|
-
var IMAGE_MIME_BY_EXT = {
|
|
4879
|
-
png: "image/png",
|
|
4880
|
-
jpg: "image/jpeg",
|
|
4881
|
-
jpeg: "image/jpeg",
|
|
4882
|
-
gif: "image/gif",
|
|
4883
|
-
webp: "image/webp",
|
|
4884
|
-
svg: "image/svg+xml",
|
|
4885
|
-
bmp: "image/bmp",
|
|
4886
|
-
ico: "image/x-icon"
|
|
4887
|
-
};
|
|
4888
|
-
var MAX_INLINE_BYTES = 4e5;
|
|
4889
|
-
var MAX_PREVIEW_BYTES = 5e6;
|
|
4890
|
-
function fileExtension(filePath) {
|
|
4891
|
-
const base = basename3(filePath).toLowerCase();
|
|
4892
|
-
return base.includes(".") ? base.split(".").pop() || "" : "";
|
|
4893
|
-
}
|
|
4894
|
-
function isImageFilePath(filePath) {
|
|
4895
|
-
return IMAGE_EXTENSIONS2.has(fileExtension(filePath));
|
|
4896
|
-
}
|
|
4897
|
-
function imageMimeType(filePath) {
|
|
4898
|
-
return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
|
|
4899
|
-
}
|
|
4900
|
-
function ensureAttachmentsDir(worktreePath) {
|
|
4901
|
-
const dir = join12(worktreePath, ATTACHMENTS_DIR);
|
|
4902
|
-
mkdirSync4(dir, { recursive: true });
|
|
4903
|
-
const gi = join12(dir, ".gitignore");
|
|
4904
|
-
if (!existsSync13(gi)) {
|
|
4905
|
-
writeFileSync5(gi, attachmentsGitignoreBody(), "utf8");
|
|
4906
|
-
}
|
|
4907
|
-
return dir;
|
|
4908
|
-
}
|
|
4909
|
-
function uniqueAttachmentName(dir, originalName) {
|
|
4910
|
-
const safe = originalName.replace(/[/\\]/g, "_") || "file";
|
|
4911
|
-
if (!existsSync13(join12(dir, safe))) return safe;
|
|
4912
|
-
const ext = extname(safe);
|
|
4913
|
-
const stem = ext ? safe.slice(0, -ext.length) : safe;
|
|
4914
|
-
for (let i = 1; i < 1e4; i++) {
|
|
4915
|
-
const candidate = `${stem}-${i}${ext}`;
|
|
4916
|
-
if (!existsSync13(join12(dir, candidate))) return candidate;
|
|
4917
|
-
}
|
|
4918
|
-
return `${stem}-${randomUUID4()}${ext}`;
|
|
4919
|
-
}
|
|
4920
|
-
function previewDataUrlFromBuf(filePath, buf) {
|
|
4921
|
-
if (!isImageFilePath(filePath)) return void 0;
|
|
4922
|
-
if (buf.length > MAX_PREVIEW_BYTES) return void 0;
|
|
4923
|
-
return `data:${imageMimeType(filePath)};base64,${buf.toString("base64")}`;
|
|
4924
|
-
}
|
|
4925
|
-
function attachmentFromBuffer(name, buf, opts) {
|
|
4926
|
-
const previewDataUrl = previewDataUrlFromBuf(name, buf);
|
|
4927
|
-
if (isImageFilePath(name)) {
|
|
4928
|
-
const pathHint = opts.path ? `\`${opts.path}\`` : opts.sourceLabel || name;
|
|
4929
|
-
return {
|
|
4930
|
-
id: randomUUID4(),
|
|
4931
|
-
name,
|
|
4932
|
-
kind: "file",
|
|
4933
|
-
path: opts.path,
|
|
4934
|
-
previewDataUrl,
|
|
4935
|
-
content: [
|
|
4936
|
-
`Image attached: ${pathHint}`,
|
|
4937
|
-
opts.path ? `Use the Read tool on \`${opts.path}\` to view this image.` : "The image is shown in the composer; copy it into the worktree if you need to inspect pixels."
|
|
4938
|
-
].join("\n")
|
|
4939
|
-
};
|
|
4940
|
-
}
|
|
4941
|
-
if (buf.length > MAX_INLINE_BYTES) {
|
|
4942
|
-
return {
|
|
4943
|
-
id: randomUUID4(),
|
|
4944
|
-
name,
|
|
4945
|
-
kind: "file",
|
|
4946
|
-
path: opts.path,
|
|
4947
|
-
content: opts.path ? `(file too large to attach inline: \`${opts.path}\`, ${buf.length} bytes \u2014 use the Read tool)` : `(file too large to attach inline: ${opts.sourceLabel || name}, ${buf.length} bytes)`
|
|
4948
|
-
};
|
|
4949
|
-
}
|
|
4950
|
-
if (buf.includes(0)) {
|
|
4951
|
-
return {
|
|
4952
|
-
id: randomUUID4(),
|
|
4953
|
-
name,
|
|
4954
|
-
kind: "file",
|
|
4955
|
-
path: opts.path,
|
|
4956
|
-
content: opts.path ? `(binary file at \`${opts.path}\` \u2014 use tools to inspect)` : `(binary file attached by path only: ${opts.sourceLabel || name})`
|
|
4957
|
-
};
|
|
4958
|
-
}
|
|
4959
|
-
return {
|
|
4960
|
-
id: randomUUID4(),
|
|
4961
|
-
name,
|
|
4962
|
-
kind: "file",
|
|
4963
|
-
path: opts.path,
|
|
4964
|
-
content: buf.toString("utf8")
|
|
4965
|
-
};
|
|
4966
|
-
}
|
|
4967
|
-
function attachmentFromAbsolutePath(absolutePath) {
|
|
4968
|
-
const name = basename3(absolutePath);
|
|
4969
|
-
try {
|
|
4970
|
-
const st = statSync4(absolutePath);
|
|
4971
|
-
if (!st.isFile()) {
|
|
4972
|
-
return {
|
|
4973
|
-
id: randomUUID4(),
|
|
4974
|
-
name,
|
|
4975
|
-
kind: "file",
|
|
4976
|
-
content: `(not a file: ${absolutePath})`
|
|
4977
|
-
};
|
|
4978
|
-
}
|
|
4979
|
-
const buf = readFileSync10(absolutePath);
|
|
4980
|
-
return attachmentFromBuffer(name, buf, { sourceLabel: absolutePath });
|
|
4981
|
-
} catch (err) {
|
|
4982
|
-
return {
|
|
4983
|
-
id: randomUUID4(),
|
|
4984
|
-
name,
|
|
4985
|
-
kind: "file",
|
|
4986
|
-
content: `(could not read ${absolutePath}: ${err instanceof Error ? err.message : String(err)})`
|
|
4987
|
-
};
|
|
4988
|
-
}
|
|
4989
|
-
}
|
|
4990
|
-
function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
4991
|
-
if (absolutePaths.length === 0) return [];
|
|
4992
|
-
const dir = ensureAttachmentsDir(worktreePath);
|
|
4993
|
-
const out = [];
|
|
4994
|
-
for (const abs of absolutePaths) {
|
|
4995
|
-
const originalName = basename3(abs);
|
|
4996
|
-
try {
|
|
4997
|
-
const st = statSync4(abs);
|
|
4998
|
-
if (!st.isFile()) continue;
|
|
4999
|
-
const name = uniqueAttachmentName(dir, originalName);
|
|
5000
|
-
const destAbs = join12(dir, name);
|
|
5001
|
-
copyFileSync3(abs, destAbs);
|
|
5002
|
-
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
5003
|
-
const buf = readFileSync10(destAbs);
|
|
5004
|
-
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
5005
|
-
} catch (err) {
|
|
5006
|
-
out.push({
|
|
5007
|
-
id: randomUUID4(),
|
|
5008
|
-
name: originalName,
|
|
5009
|
-
kind: "file",
|
|
5010
|
-
content: `(could not attach ${abs}: ${err instanceof Error ? err.message : String(err)})`
|
|
5011
|
-
});
|
|
5012
|
-
}
|
|
5013
|
-
}
|
|
5014
|
-
return out;
|
|
5015
|
-
}
|
|
5016
|
-
function stageBuffersAsAttachments(worktreePath, buffers2) {
|
|
5017
|
-
if (buffers2.length === 0) return [];
|
|
5018
|
-
const dir = ensureAttachmentsDir(worktreePath);
|
|
5019
|
-
const out = [];
|
|
5020
|
-
for (const item of buffers2) {
|
|
5021
|
-
const originalName = (item.name || "file").replace(/[/\\]/g, "_") || "file";
|
|
5022
|
-
try {
|
|
5023
|
-
const buf = Buffer.from(item.dataBase64, "base64");
|
|
5024
|
-
const name = uniqueAttachmentName(dir, originalName);
|
|
5025
|
-
const destAbs = join12(dir, name);
|
|
5026
|
-
writeFileSync5(destAbs, buf);
|
|
5027
|
-
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
5028
|
-
out.push(attachmentFromBuffer(name, buf, { path: rel }));
|
|
5029
|
-
} catch (err) {
|
|
5030
|
-
out.push({
|
|
5031
|
-
id: randomUUID4(),
|
|
5032
|
-
name: originalName,
|
|
5033
|
-
kind: "file",
|
|
5034
|
-
content: `(could not attach ${originalName}: ${err instanceof Error ? err.message : String(err)})`
|
|
5035
|
-
});
|
|
5036
|
-
}
|
|
5037
|
-
}
|
|
5038
|
-
return out;
|
|
5039
|
-
}
|
|
5040
|
-
function attachmentsFromBuffers(buffers2) {
|
|
5041
|
-
return buffers2.map((item) => {
|
|
5042
|
-
const name = (item.name || "file").replace(/[/\\]/g, "_") || "file";
|
|
5043
|
-
try {
|
|
5044
|
-
const buf = Buffer.from(item.dataBase64, "base64");
|
|
5045
|
-
return attachmentFromBuffer(name, buf, { sourceLabel: name });
|
|
5046
|
-
} catch (err) {
|
|
5047
|
-
return {
|
|
5048
|
-
id: randomUUID4(),
|
|
5049
|
-
name,
|
|
5050
|
-
kind: "file",
|
|
5051
|
-
content: `(could not attach ${name}: ${err instanceof Error ? err.message : String(err)})`
|
|
5052
|
-
};
|
|
5053
|
-
}
|
|
5054
|
-
});
|
|
5055
|
-
}
|
|
5056
|
-
function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
5057
|
-
const out = [];
|
|
5058
|
-
for (const rel of relativePaths) {
|
|
5059
|
-
if (!rel || rel.includes("..") || rel.startsWith("/")) {
|
|
5060
|
-
out.push({
|
|
5061
|
-
id: randomUUID4(),
|
|
5062
|
-
name: basename3(rel) || "file",
|
|
5063
|
-
kind: "file",
|
|
5064
|
-
content: `(invalid path: ${rel})`
|
|
5065
|
-
});
|
|
5066
|
-
continue;
|
|
5067
|
-
}
|
|
5068
|
-
const name = basename3(rel);
|
|
5069
|
-
try {
|
|
5070
|
-
const abs = join12(worktreePath, rel);
|
|
5071
|
-
const st = statSync4(abs);
|
|
5072
|
-
if (!st.isFile()) continue;
|
|
5073
|
-
const buf = readFileSync10(abs);
|
|
5074
|
-
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
5075
|
-
} catch (err) {
|
|
5076
|
-
out.push({
|
|
5077
|
-
id: randomUUID4(),
|
|
5078
|
-
name,
|
|
5079
|
-
kind: "file",
|
|
5080
|
-
content: `(could not read ${rel}: ${err instanceof Error ? err.message : String(err)})`
|
|
5081
|
-
});
|
|
5082
|
-
}
|
|
5083
|
-
}
|
|
5084
|
-
return out;
|
|
5085
|
-
}
|
|
5086
|
-
|
|
5087
4918
|
// src/agents/instructions.ts
|
|
5088
|
-
import { existsSync as
|
|
5089
|
-
import { join as
|
|
4919
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10, statSync as statSync4 } from "fs";
|
|
4920
|
+
import { join as join12 } from "path";
|
|
5090
4921
|
function normPath(p) {
|
|
5091
4922
|
return p.replace(/\/+$/, "");
|
|
5092
4923
|
}
|
|
@@ -5250,11 +5081,11 @@ function loadAgentInstructions(worktreePath, agent) {
|
|
|
5250
5081
|
const out = [];
|
|
5251
5082
|
for (const rel of candidates) {
|
|
5252
5083
|
if (seenPaths.has(rel)) continue;
|
|
5253
|
-
const abs =
|
|
5254
|
-
if (!
|
|
5084
|
+
const abs = join12(worktreePath, rel);
|
|
5085
|
+
if (!existsSync13(abs)) continue;
|
|
5255
5086
|
try {
|
|
5256
|
-
if (!
|
|
5257
|
-
let content =
|
|
5087
|
+
if (!statSync4(abs).isFile()) continue;
|
|
5088
|
+
let content = readFileSync10(abs, "utf8");
|
|
5258
5089
|
if (!content.trim()) continue;
|
|
5259
5090
|
if (content.length > MAX_CHARS_PER_FILE) {
|
|
5260
5091
|
content = `${content.slice(0, MAX_CHARS_PER_FILE)}
|
|
@@ -5326,9 +5157,9 @@ async function syncThreadBranchFromGit(threadId) {
|
|
|
5326
5157
|
}
|
|
5327
5158
|
|
|
5328
5159
|
// src/store/schedules.ts
|
|
5329
|
-
import { randomUUID as
|
|
5330
|
-
import { existsSync as
|
|
5331
|
-
import { join as
|
|
5160
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
5161
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
|
|
5162
|
+
import { join as join13 } from "path";
|
|
5332
5163
|
import { Cron } from "croner";
|
|
5333
5164
|
var DURATION_RE = /^(\d+)(s|m|h|d)$/i;
|
|
5334
5165
|
var UNIT_MS = {
|
|
@@ -5338,7 +5169,7 @@ var UNIT_MS = {
|
|
|
5338
5169
|
d: 864e5
|
|
5339
5170
|
};
|
|
5340
5171
|
function schedulesPath() {
|
|
5341
|
-
return
|
|
5172
|
+
return join13(appDataDir(), "schedules.json");
|
|
5342
5173
|
}
|
|
5343
5174
|
function parseDurationMs(every) {
|
|
5344
5175
|
const m = every.trim().match(DURATION_RE);
|
|
@@ -5444,9 +5275,9 @@ function normalizeTask(raw) {
|
|
|
5444
5275
|
}
|
|
5445
5276
|
function readAll() {
|
|
5446
5277
|
const path = schedulesPath();
|
|
5447
|
-
if (!
|
|
5278
|
+
if (!existsSync14(path)) return [];
|
|
5448
5279
|
try {
|
|
5449
|
-
const parsed = JSON.parse(
|
|
5280
|
+
const parsed = JSON.parse(readFileSync11(path, "utf8"));
|
|
5450
5281
|
if (!Array.isArray(parsed)) return [];
|
|
5451
5282
|
return parsed.map((row) => {
|
|
5452
5283
|
try {
|
|
@@ -5471,7 +5302,7 @@ function buildTask(input, now) {
|
|
|
5471
5302
|
const ts = nowIso(now);
|
|
5472
5303
|
const agent = input.agent ? coerceOrchestratorAgent(input.agent) : null;
|
|
5473
5304
|
return {
|
|
5474
|
-
id:
|
|
5305
|
+
id: randomUUID4(),
|
|
5475
5306
|
name: input.name?.trim() || defaultScheduleName(prompt),
|
|
5476
5307
|
prompt,
|
|
5477
5308
|
enabled: input.enabled !== false,
|
|
@@ -5572,7 +5403,7 @@ function formatScheduledPrompt(name, prompt) {
|
|
|
5572
5403
|
${prompt}`;
|
|
5573
5404
|
}
|
|
5574
5405
|
async function defaultDeps() {
|
|
5575
|
-
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-
|
|
5406
|
+
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-6I47JMU2.js");
|
|
5576
5407
|
const orch = getOrchestrator2();
|
|
5577
5408
|
return {
|
|
5578
5409
|
findThread: (id) => findThreadByRef(id),
|
|
@@ -5778,7 +5609,7 @@ var Orchestrator = class {
|
|
|
5778
5609
|
}
|
|
5779
5610
|
continue;
|
|
5780
5611
|
}
|
|
5781
|
-
if (!
|
|
5612
|
+
if (!existsSync15(thread.worktreePath)) {
|
|
5782
5613
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
5783
5614
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
5784
5615
|
continue;
|
|
@@ -5815,6 +5646,9 @@ var Orchestrator = class {
|
|
|
5815
5646
|
* MCP-created review threads don't stay `queued` after the MCP child exits.
|
|
5816
5647
|
*/
|
|
5817
5648
|
adoptPersistedQueues() {
|
|
5649
|
+
if (thisProcessShouldDrainAgentQueues()) {
|
|
5650
|
+
this.healStaleRunningTurns();
|
|
5651
|
+
}
|
|
5818
5652
|
for (const thread of listThreads()) {
|
|
5819
5653
|
if (thread.status === "stopped" || thread.status === "archived") continue;
|
|
5820
5654
|
const pid = thread.agentPid;
|
|
@@ -5835,6 +5669,32 @@ var Orchestrator = class {
|
|
|
5835
5669
|
}
|
|
5836
5670
|
}
|
|
5837
5671
|
}
|
|
5672
|
+
/**
|
|
5673
|
+
* Mid-session: a worktree can sit at `running` after the agent process dies
|
|
5674
|
+
* (Cursor/CLI crash, OOM) while wait_for_turn still reports stillRunning.
|
|
5675
|
+
* Reclaim those and wake the parent orchestration chat.
|
|
5676
|
+
*/
|
|
5677
|
+
healStaleRunningTurns() {
|
|
5678
|
+
for (const thread of listThreads()) {
|
|
5679
|
+
if (thread.status === "archived") continue;
|
|
5680
|
+
const handle = this.activeTurns.get(thread.id);
|
|
5681
|
+
if (handle) {
|
|
5682
|
+
const pid = thread.agentPid;
|
|
5683
|
+
if (typeof pid === "number" && pid > 0 && !isPidAlive(pid)) {
|
|
5684
|
+
handle.kill();
|
|
5685
|
+
}
|
|
5686
|
+
continue;
|
|
5687
|
+
}
|
|
5688
|
+
if (!this.shouldReclaimRunningThread(thread)) continue;
|
|
5689
|
+
setStatus(thread.id, "stopped", "Process died (agent exited)");
|
|
5690
|
+
this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
|
|
5691
|
+
this.emit({ type: "turn_finished", threadId: thread.id, exitCode: 1 });
|
|
5692
|
+
const latest = readThread(thread.id);
|
|
5693
|
+
if (latest) {
|
|
5694
|
+
notifyParentOfChildHalt(latest, "stopped", (id, prompt) => this.send(id, prompt));
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
5697
|
+
}
|
|
5838
5698
|
clearQuotaResumeTimer(threadId) {
|
|
5839
5699
|
const timer = this.quotaResumeTimers.get(threadId);
|
|
5840
5700
|
if (timer) clearTimeout(timer);
|
|
@@ -6552,6 +6412,12 @@ var Orchestrator = class {
|
|
|
6552
6412
|
assistantText: chatText,
|
|
6553
6413
|
partsCount: parts.length
|
|
6554
6414
|
});
|
|
6415
|
+
if (!this.crashContinued.has(threadId)) {
|
|
6416
|
+
const failed = readThread(threadId);
|
|
6417
|
+
if (failed) {
|
|
6418
|
+
notifyParentOfChildHalt(failed, "error", (id, prompt2) => this.send(id, prompt2));
|
|
6419
|
+
}
|
|
6420
|
+
}
|
|
6555
6421
|
}
|
|
6556
6422
|
}
|
|
6557
6423
|
} catch (err) {
|
|
@@ -6576,6 +6442,12 @@ var Orchestrator = class {
|
|
|
6576
6442
|
assistantText: "",
|
|
6577
6443
|
partsCount: 0
|
|
6578
6444
|
});
|
|
6445
|
+
if (!this.crashContinued.has(threadId)) {
|
|
6446
|
+
const failed = readThread(threadId);
|
|
6447
|
+
if (failed) {
|
|
6448
|
+
notifyParentOfChildHalt(failed, "error", (id, prompt2) => this.send(id, prompt2));
|
|
6449
|
+
}
|
|
6450
|
+
}
|
|
6579
6451
|
}
|
|
6580
6452
|
} finally {
|
|
6581
6453
|
this.startingTurns.delete(threadId);
|
|
@@ -6631,6 +6503,9 @@ var Orchestrator = class {
|
|
|
6631
6503
|
const stopped = writeLiveStatus(thread.id, "stopped") ?? readThread(thread.id) ?? thread;
|
|
6632
6504
|
if (stopped.status === "stopped") {
|
|
6633
6505
|
this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
|
|
6506
|
+
if (opts?.notifyParent !== false) {
|
|
6507
|
+
notifyParentOfChildHalt(stopped, "stopped", (id, prompt) => this.send(id, prompt));
|
|
6508
|
+
}
|
|
6634
6509
|
}
|
|
6635
6510
|
return stopped;
|
|
6636
6511
|
}
|
|
@@ -6875,21 +6750,25 @@ var Orchestrator = class {
|
|
|
6875
6750
|
fn();
|
|
6876
6751
|
};
|
|
6877
6752
|
const off = this.on((event) => {
|
|
6878
|
-
if (
|
|
6753
|
+
if (!("threadId" in event) || event.threadId !== thread.id) return;
|
|
6754
|
+
if (event.type === "turn_finished" || event.type === "error") {
|
|
6879
6755
|
const latest = readThread(thread.id);
|
|
6880
6756
|
if (!latest) {
|
|
6881
6757
|
finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
|
|
6882
6758
|
return;
|
|
6883
6759
|
}
|
|
6884
6760
|
finish(() => resolve(latest));
|
|
6761
|
+
return;
|
|
6885
6762
|
}
|
|
6886
|
-
if (event.type === "
|
|
6763
|
+
if (event.type === "status_changed") {
|
|
6887
6764
|
const latest = readThread(thread.id);
|
|
6888
6765
|
if (!latest) {
|
|
6889
6766
|
finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
|
|
6890
6767
|
return;
|
|
6891
6768
|
}
|
|
6892
|
-
|
|
6769
|
+
if (!["running", "queued"].includes(latest.status)) {
|
|
6770
|
+
finish(() => resolve(latest));
|
|
6771
|
+
}
|
|
6893
6772
|
}
|
|
6894
6773
|
});
|
|
6895
6774
|
timer = setInterval(() => {
|
|
@@ -6924,7 +6803,7 @@ var Orchestrator = class {
|
|
|
6924
6803
|
const thread = this.requireThread(threadRef);
|
|
6925
6804
|
const lastAgent = [...thread.messages].reverse().find((m) => m.role === "agent");
|
|
6926
6805
|
const lastError = thread.lastError ?? null;
|
|
6927
|
-
const text = (lastAgent?.text ?? "").trim() || (thread.status === "error" ? lastError ?? "" : "");
|
|
6806
|
+
const text = (lastAgent?.text ?? "").trim() || (thread.status === "error" || thread.status === "stopped" || thread.status === "broken" ? lastError ?? "" : "");
|
|
6928
6807
|
const stillRunning = thread.status === "running" || thread.status === "queued";
|
|
6929
6808
|
const live = stillRunning ? readTurnLive(thread.id) : null;
|
|
6930
6809
|
const queuedHint = thread.status === "queued" && !live?.summary ? "Queued \u2014 waiting for a concurrency slot" : null;
|
|
@@ -7411,7 +7290,7 @@ var Orchestrator = class {
|
|
|
7411
7290
|
this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
|
|
7412
7291
|
if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
|
|
7413
7292
|
try {
|
|
7414
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-
|
|
7293
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-KZA3TCEE.js");
|
|
7415
7294
|
await ensureWorkspace2(thread.repoPath);
|
|
7416
7295
|
} catch {
|
|
7417
7296
|
}
|
|
@@ -7459,13 +7338,13 @@ var Orchestrator = class {
|
|
|
7459
7338
|
this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
|
|
7460
7339
|
return restored2;
|
|
7461
7340
|
}
|
|
7462
|
-
if (!
|
|
7341
|
+
if (!existsSync15(thread.worktreePath)) {
|
|
7463
7342
|
if (isCowboyThread(thread) || isPrimaryCheckoutThread(thread)) {
|
|
7464
7343
|
throw new Error(
|
|
7465
7344
|
`Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
|
|
7466
7345
|
);
|
|
7467
7346
|
}
|
|
7468
|
-
const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-
|
|
7347
|
+
const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-MX7XBX6Z.js");
|
|
7469
7348
|
const slug = thread.worktreePath.split("/").pop();
|
|
7470
7349
|
const dest = thread.worktreePath;
|
|
7471
7350
|
await withRepoGitLock(thread.repoPath, async () => {
|
|
@@ -7573,7 +7452,7 @@ async function startOrchestration(opts) {
|
|
|
7573
7452
|
sourceRef: "default",
|
|
7574
7453
|
...createOpts
|
|
7575
7454
|
}).catch(async () => {
|
|
7576
|
-
const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-
|
|
7455
|
+
const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-MX7XBX6Z.js");
|
|
7577
7456
|
const repo = await resolveRepoRoot2(repoPath);
|
|
7578
7457
|
const def = await resolveDefaultBranch2(repo);
|
|
7579
7458
|
return orch.createThread({
|
|
@@ -7583,7 +7462,7 @@ async function startOrchestration(opts) {
|
|
|
7583
7462
|
repoPath: repo
|
|
7584
7463
|
});
|
|
7585
7464
|
});
|
|
7586
|
-
const { updateThread: upd } = await import("./thread-store-
|
|
7465
|
+
const { updateThread: upd } = await import("./thread-store-CRLQJ2HM.js");
|
|
7587
7466
|
const updated = upd(thread.id, {
|
|
7588
7467
|
sourceType: "orchestration",
|
|
7589
7468
|
sourceRef: goal
|
|
@@ -7760,12 +7639,6 @@ export {
|
|
|
7760
7639
|
discoverSkills,
|
|
7761
7640
|
readSkillBody,
|
|
7762
7641
|
expandComposerPrompt,
|
|
7763
|
-
isImageFilePath,
|
|
7764
|
-
attachmentFromAbsolutePath,
|
|
7765
|
-
stageAbsolutePathsAsAttachments,
|
|
7766
|
-
stageBuffersAsAttachments,
|
|
7767
|
-
attachmentsFromBuffers,
|
|
7768
|
-
attachmentsFromWorktreePaths,
|
|
7769
7642
|
formatRenameBranchDirective,
|
|
7770
7643
|
formatWorktreeDirective,
|
|
7771
7644
|
formatWorktreeReminder,
|