@sideboard-ai/core 0.1.97 → 0.1.99
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/cursor-runner.cjs +47 -2
- package/dist/agents/cursor-runner.js +47 -2
- package/dist/{agents-WS5QV6LE.js → agents-3MWWWSMF.js} +2 -2
- package/dist/{agents-HBLA6FEV.js → agents-ESJKIQQA.js} +2 -2
- package/dist/{chunk-CLGO7TLO.js → chunk-FO67IJTY.js} +24 -25
- package/dist/{chunk-WBX46OPD.js → chunk-FYS2BULQ.js} +2 -2
- package/dist/{chunk-NR6APJLD.js → chunk-HI2OTFFR.js} +24 -25
- package/dist/{chunk-KWNUZ4LR.js → chunk-MBP3XG57.js} +2 -2
- package/dist/{chunk-6XBXVXX2.js → chunk-OANJQTVG.js} +1 -1
- package/dist/{chunk-QKYO6BHB.js → chunk-UYQYK2RY.js} +1 -1
- package/dist/{global-workspace-M3OMVDDH.js → global-workspace-RSQXRLT7.js} +3 -1
- package/dist/{global-workspace-3GNPQCLE.js → global-workspace-WMF3BJP5.js} +3 -1
- package/dist/index.cjs +541 -420
- package/dist/index.d.cts +24 -3
- package/dist/index.d.ts +24 -3
- package/dist/index.js +241 -128
- package/dist/mcp/run-stdio.cjs +225 -167
- package/dist/mcp/run-stdio.js +129 -70
- package/dist/{workspaces-ERZC7ULY.js → workspaces-4ZY4QPWQ.js} +2 -2
- package/dist/{workspaces-J4WG6UFR.js → workspaces-MZVQHRSJ.js} +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
listWorkspaces,
|
|
5
5
|
removeWorkspace,
|
|
6
6
|
syncWorkspacesFromThreads
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-UYQYK2RY.js";
|
|
8
8
|
import {
|
|
9
9
|
BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
10
10
|
CLAUDE_MODEL_CATALOG,
|
|
@@ -65,7 +65,7 @@ import {
|
|
|
65
65
|
threadRequestsBrightsyMcp,
|
|
66
66
|
totalTokens,
|
|
67
67
|
writeInjectedMcpConfig
|
|
68
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-MBP3XG57.js";
|
|
69
69
|
import {
|
|
70
70
|
CLOUD_COORDINATOR_BUSY_REPLY,
|
|
71
71
|
CLOUD_COORDINATOR_STOPPED_REPLY,
|
|
@@ -79,6 +79,7 @@ import {
|
|
|
79
79
|
createGlobalChat,
|
|
80
80
|
ensureCloudCoordinator,
|
|
81
81
|
ensureSlackCoordinator,
|
|
82
|
+
findSlackCoordinator,
|
|
82
83
|
healOrchestrationSoccerTitles,
|
|
83
84
|
isCloudCoordinatorThread,
|
|
84
85
|
isGlobalRepoPath,
|
|
@@ -92,7 +93,7 @@ import {
|
|
|
92
93
|
parseForceStopMessage,
|
|
93
94
|
slackCoordinatorSourceRef,
|
|
94
95
|
takenTeamSlugsForOrchestration
|
|
95
|
-
} from "./chunk-
|
|
96
|
+
} from "./chunk-FO67IJTY.js";
|
|
96
97
|
import {
|
|
97
98
|
COORDINATOR_TOOL_PLAYBOOK,
|
|
98
99
|
SLACK_REPLY_FORMATTING,
|
|
@@ -366,6 +367,52 @@ import {
|
|
|
366
367
|
withExportedPath
|
|
367
368
|
} from "./chunk-LGXBYZZA.js";
|
|
368
369
|
|
|
370
|
+
// src/store/desktop-host.ts
|
|
371
|
+
import { readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
372
|
+
import { join } from "path";
|
|
373
|
+
function desktopHostPidPath() {
|
|
374
|
+
return join(appDataDir(), "desktop-host.pid");
|
|
375
|
+
}
|
|
376
|
+
function claimDesktopHost(pid = process.pid) {
|
|
377
|
+
writeFileSync(desktopHostPidPath(), `${pid}
|
|
378
|
+
`, "utf8");
|
|
379
|
+
}
|
|
380
|
+
function releaseDesktopHost(pid = process.pid) {
|
|
381
|
+
if (readDesktopHostPid() !== pid) return;
|
|
382
|
+
try {
|
|
383
|
+
unlinkSync(desktopHostPidPath());
|
|
384
|
+
} catch {
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function readDesktopHostPid() {
|
|
388
|
+
try {
|
|
389
|
+
const pid = Number.parseInt(readFileSync(desktopHostPidPath(), "utf8").trim(), 10);
|
|
390
|
+
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
391
|
+
return pid;
|
|
392
|
+
} catch {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function pidAlive(pid) {
|
|
397
|
+
try {
|
|
398
|
+
process.kill(pid, 0);
|
|
399
|
+
return true;
|
|
400
|
+
} catch {
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function isDesktopHostAlive() {
|
|
405
|
+
const pid = readDesktopHostPid();
|
|
406
|
+
return pid != null && pidAlive(pid);
|
|
407
|
+
}
|
|
408
|
+
function isThisProcessDesktopHost() {
|
|
409
|
+
return readDesktopHostPid() === process.pid && pidAlive(process.pid);
|
|
410
|
+
}
|
|
411
|
+
function thisProcessShouldDrainAgentQueues() {
|
|
412
|
+
if (isThisProcessDesktopHost()) return true;
|
|
413
|
+
return !isDesktopHostAlive();
|
|
414
|
+
}
|
|
415
|
+
|
|
369
416
|
// src/git/agent-git-actions.ts
|
|
370
417
|
var AGENT_GIT_ACTIONS = [
|
|
371
418
|
"commit-push",
|
|
@@ -1338,7 +1385,7 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
1338
1385
|
`Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
|
|
1339
1386
|
);
|
|
1340
1387
|
}
|
|
1341
|
-
const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-
|
|
1388
|
+
const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-RSQXRLT7.js");
|
|
1342
1389
|
if (isGlobalThread2(thread)) {
|
|
1343
1390
|
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-AKEY4WSO.js");
|
|
1344
1391
|
ensureGlobalCoordinatorCwd2(
|
|
@@ -1451,8 +1498,8 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
1451
1498
|
}
|
|
1452
1499
|
|
|
1453
1500
|
// src/agents/instructions.ts
|
|
1454
|
-
import { existsSync, readFileSync, statSync } from "fs";
|
|
1455
|
-
import { join } from "path";
|
|
1501
|
+
import { existsSync, readFileSync as readFileSync2, statSync } from "fs";
|
|
1502
|
+
import { join as join2 } from "path";
|
|
1456
1503
|
function normPath(p) {
|
|
1457
1504
|
return p.replace(/\/+$/, "");
|
|
1458
1505
|
}
|
|
@@ -1621,11 +1668,11 @@ function loadAgentInstructions(worktreePath, agent) {
|
|
|
1621
1668
|
const out = [];
|
|
1622
1669
|
for (const rel of candidates) {
|
|
1623
1670
|
if (seenPaths.has(rel)) continue;
|
|
1624
|
-
const abs =
|
|
1671
|
+
const abs = join2(worktreePath, rel);
|
|
1625
1672
|
if (!existsSync(abs)) continue;
|
|
1626
1673
|
try {
|
|
1627
1674
|
if (!statSync(abs).isFile()) continue;
|
|
1628
|
-
let content =
|
|
1675
|
+
let content = readFileSync2(abs, "utf8");
|
|
1629
1676
|
if (!content.trim()) continue;
|
|
1630
1677
|
if (content.length > MAX_CHARS_PER_FILE) {
|
|
1631
1678
|
content = `${content.slice(0, MAX_CHARS_PER_FILE)}
|
|
@@ -1705,10 +1752,10 @@ import {
|
|
|
1705
1752
|
existsSync as existsSync2,
|
|
1706
1753
|
mkdirSync,
|
|
1707
1754
|
readdirSync,
|
|
1708
|
-
readFileSync as
|
|
1755
|
+
readFileSync as readFileSync3
|
|
1709
1756
|
} from "fs";
|
|
1710
1757
|
import { createServer as createServer2 } from "net";
|
|
1711
|
-
import { basename, dirname, join as
|
|
1758
|
+
import { basename, dirname, join as join3 } from "path";
|
|
1712
1759
|
import { execa as execa2 } from "execa";
|
|
1713
1760
|
import { createInterface as createInterface2 } from "readline";
|
|
1714
1761
|
var PORT_RANGE_SIZE = 10;
|
|
@@ -1717,9 +1764,9 @@ function matchSimpleGlob(pattern, name) {
|
|
|
1717
1764
|
return new RegExp(`^${escaped}$`).test(name);
|
|
1718
1765
|
}
|
|
1719
1766
|
function readWorktreeInclude(repoPath) {
|
|
1720
|
-
const path2 =
|
|
1767
|
+
const path2 = join3(repoPath, ".worktreeinclude");
|
|
1721
1768
|
if (!existsSync2(path2)) return [];
|
|
1722
|
-
return
|
|
1769
|
+
return readFileSync3(path2, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
1723
1770
|
}
|
|
1724
1771
|
function resolveFilesToCopy(repoPath) {
|
|
1725
1772
|
const fromInclude = readWorktreeInclude(repoPath);
|
|
@@ -1758,9 +1805,9 @@ function copyConfiguredFiles(repoPath, worktreePath) {
|
|
|
1758
1805
|
const patterns = resolveFilesToCopy(repoPath);
|
|
1759
1806
|
const copied = [];
|
|
1760
1807
|
for (const rel of patterns) {
|
|
1761
|
-
const src =
|
|
1808
|
+
const src = join3(repoPath, rel);
|
|
1762
1809
|
if (!existsSync2(src)) continue;
|
|
1763
|
-
const dest =
|
|
1810
|
+
const dest = join3(worktreePath, rel);
|
|
1764
1811
|
mkdirSync(dirname(dest), { recursive: true });
|
|
1765
1812
|
copyFileSync(src, dest);
|
|
1766
1813
|
copied.push(rel);
|
|
@@ -2055,8 +2102,8 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
|
|
|
2055
2102
|
}
|
|
2056
2103
|
|
|
2057
2104
|
// src/diff/diff.ts
|
|
2058
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as
|
|
2059
|
-
import { dirname as dirname2, join as
|
|
2105
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2106
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
2060
2107
|
async function inspectGitWorktree(worktreePath) {
|
|
2061
2108
|
if (!worktreePath || !existsSync3(worktreePath)) return "missing_worktree";
|
|
2062
2109
|
const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
|
|
@@ -2202,11 +2249,11 @@ new file mode 100644
|
|
|
2202
2249
|
};
|
|
2203
2250
|
}
|
|
2204
2251
|
async function untrackedPatch(worktreePath, path2, maxHunk) {
|
|
2205
|
-
const abs =
|
|
2252
|
+
const abs = join4(worktreePath, path2);
|
|
2206
2253
|
try {
|
|
2207
2254
|
const st = statSync2(abs);
|
|
2208
2255
|
if (st.isFile() && st.size > maxHunk) {
|
|
2209
|
-
const buf =
|
|
2256
|
+
const buf = readFileSync4(abs).subarray(0, maxHunk);
|
|
2210
2257
|
return syntheticAddPatch(path2, buf.toString("utf8"), maxHunk);
|
|
2211
2258
|
}
|
|
2212
2259
|
} catch {
|
|
@@ -2707,7 +2754,7 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
|
|
|
2707
2754
|
function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
2708
2755
|
assertSafeRelativePath(relativePath);
|
|
2709
2756
|
const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
|
|
2710
|
-
const abs =
|
|
2757
|
+
const abs = join4(worktreePath, relativePath);
|
|
2711
2758
|
const st = statSync2(abs);
|
|
2712
2759
|
if (!st.isFile()) {
|
|
2713
2760
|
throw new Error(`Not a file: ${relativePath}`);
|
|
@@ -2717,7 +2764,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
2717
2764
|
`File too large to upload (${st.size} bytes; max ${maxBytes})`
|
|
2718
2765
|
);
|
|
2719
2766
|
}
|
|
2720
|
-
const buf =
|
|
2767
|
+
const buf = readFileSync4(abs);
|
|
2721
2768
|
return {
|
|
2722
2769
|
path: relativePath,
|
|
2723
2770
|
contentBase64: buf.toString("base64"),
|
|
@@ -2727,12 +2774,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
2727
2774
|
function readWorktreeFile(worktreePath, relativePath, opts) {
|
|
2728
2775
|
assertSafeRelativePath(relativePath);
|
|
2729
2776
|
const maxBytes = opts?.maxBytes ?? 2e5;
|
|
2730
|
-
const abs =
|
|
2777
|
+
const abs = join4(worktreePath, relativePath);
|
|
2731
2778
|
const st = statSync2(abs);
|
|
2732
2779
|
if (!st.isFile()) {
|
|
2733
2780
|
throw new Error(`Not a file: ${relativePath}`);
|
|
2734
2781
|
}
|
|
2735
|
-
const buf =
|
|
2782
|
+
const buf = readFileSync4(abs);
|
|
2736
2783
|
if (isImageRelativePath(relativePath)) {
|
|
2737
2784
|
const maxImageBytes = Math.max(maxBytes, 15e6);
|
|
2738
2785
|
const truncated2 = buf.length > maxImageBytes;
|
|
@@ -2775,9 +2822,9 @@ function assertSafeRelativePath(relativePath) {
|
|
|
2775
2822
|
}
|
|
2776
2823
|
function writeWorktreeFile(worktreePath, relativePath, content) {
|
|
2777
2824
|
assertSafeRelativePath(relativePath);
|
|
2778
|
-
const abs =
|
|
2825
|
+
const abs = join4(worktreePath, relativePath);
|
|
2779
2826
|
mkdirSync2(dirname2(abs), { recursive: true });
|
|
2780
|
-
|
|
2827
|
+
writeFileSync2(abs, content, "utf8");
|
|
2781
2828
|
return { path: relativePath };
|
|
2782
2829
|
}
|
|
2783
2830
|
async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
@@ -2796,9 +2843,9 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
|
2796
2843
|
}
|
|
2797
2844
|
|
|
2798
2845
|
// src/skills/discover.ts
|
|
2799
|
-
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as
|
|
2846
|
+
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
|
|
2800
2847
|
import { homedir } from "os";
|
|
2801
|
-
import { join as
|
|
2848
|
+
import { join as join5 } from "path";
|
|
2802
2849
|
function toCommand(name) {
|
|
2803
2850
|
return name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2804
2851
|
}
|
|
@@ -2830,7 +2877,7 @@ function parseFrontmatter(content) {
|
|
|
2830
2877
|
}
|
|
2831
2878
|
function readSkill(skillMd, source) {
|
|
2832
2879
|
try {
|
|
2833
|
-
const content =
|
|
2880
|
+
const content = readFileSync5(skillMd, "utf8");
|
|
2834
2881
|
const { name: fmName, description } = parseFrontmatter(content);
|
|
2835
2882
|
const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
|
|
2836
2883
|
const name = fmName || dirName;
|
|
@@ -2858,7 +2905,7 @@ function scanSkillsDir(dir, source, out) {
|
|
|
2858
2905
|
}
|
|
2859
2906
|
for (const entry of entries) {
|
|
2860
2907
|
if (entry.startsWith(".")) continue;
|
|
2861
|
-
const skillMd =
|
|
2908
|
+
const skillMd = join5(dir, entry, "SKILL.md");
|
|
2862
2909
|
if (!existsSync4(skillMd)) continue;
|
|
2863
2910
|
try {
|
|
2864
2911
|
if (!statSync3(skillMd).isFile()) continue;
|
|
@@ -2880,12 +2927,12 @@ function scanClaudePluginSkills(pluginsRoot, out) {
|
|
|
2880
2927
|
return;
|
|
2881
2928
|
}
|
|
2882
2929
|
if (lookingForSkillsDir && entries.includes("SKILL.md")) {
|
|
2883
|
-
const skill = readSkill(
|
|
2930
|
+
const skill = readSkill(join5(dir, "SKILL.md"), "cli");
|
|
2884
2931
|
if (skill) out.push(skill);
|
|
2885
2932
|
}
|
|
2886
2933
|
for (const entry of entries) {
|
|
2887
2934
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
2888
|
-
const full =
|
|
2935
|
+
const full = join5(dir, entry);
|
|
2889
2936
|
try {
|
|
2890
2937
|
if (!statSync3(full).isDirectory()) continue;
|
|
2891
2938
|
} catch {
|
|
@@ -2905,17 +2952,17 @@ function discoverSkills(worktreePath) {
|
|
|
2905
2952
|
const home = homedir();
|
|
2906
2953
|
const collected = [];
|
|
2907
2954
|
for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
|
|
2908
|
-
scanSkillsDir(
|
|
2955
|
+
scanSkillsDir(join5(worktreePath, rel), "workspace", collected);
|
|
2909
2956
|
}
|
|
2910
2957
|
for (const abs of [
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2958
|
+
join5(home, ".claude/skills"),
|
|
2959
|
+
join5(home, ".cursor/skills"),
|
|
2960
|
+
join5(home, ".sideboard/skills"),
|
|
2961
|
+
join5(home, ".brightsy/skills")
|
|
2915
2962
|
]) {
|
|
2916
2963
|
scanSkillsDir(abs, "user", collected);
|
|
2917
2964
|
}
|
|
2918
|
-
scanClaudePluginSkills(
|
|
2965
|
+
scanClaudePluginSkills(join5(home, ".claude/plugins"), collected);
|
|
2919
2966
|
const rank = { workspace: 0, user: 1, cli: 2 };
|
|
2920
2967
|
const byCommand = /* @__PURE__ */ new Map();
|
|
2921
2968
|
for (const skill of collected) {
|
|
@@ -2927,7 +2974,7 @@ function discoverSkills(worktreePath) {
|
|
|
2927
2974
|
return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
|
|
2928
2975
|
}
|
|
2929
2976
|
function readSkillBody(skillPath, maxChars = 12e3) {
|
|
2930
|
-
const raw =
|
|
2977
|
+
const raw = readFileSync5(skillPath, "utf8");
|
|
2931
2978
|
if (raw.startsWith("---")) {
|
|
2932
2979
|
const end = raw.indexOf("\n---", 3);
|
|
2933
2980
|
if (end >= 0) {
|
|
@@ -3068,8 +3115,8 @@ function buildDiffCommentAttachment(input) {
|
|
|
3068
3115
|
}
|
|
3069
3116
|
|
|
3070
3117
|
// src/composer/stage-files.ts
|
|
3071
|
-
import { copyFileSync as copyFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as
|
|
3072
|
-
import { basename as basename2, extname, join as
|
|
3118
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync6, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
3119
|
+
import { basename as basename2, extname, join as join6 } from "path";
|
|
3073
3120
|
import { randomUUID } from "crypto";
|
|
3074
3121
|
var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
3075
3122
|
"png",
|
|
@@ -3104,22 +3151,22 @@ function imageMimeType(filePath) {
|
|
|
3104
3151
|
return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
|
|
3105
3152
|
}
|
|
3106
3153
|
function ensureAttachmentsDir(worktreePath) {
|
|
3107
|
-
const dir =
|
|
3154
|
+
const dir = join6(worktreePath, ATTACHMENTS_DIR);
|
|
3108
3155
|
mkdirSync3(dir, { recursive: true });
|
|
3109
|
-
const gi =
|
|
3156
|
+
const gi = join6(dir, ".gitignore");
|
|
3110
3157
|
if (!existsSync5(gi)) {
|
|
3111
|
-
|
|
3158
|
+
writeFileSync3(gi, attachmentsGitignoreBody(), "utf8");
|
|
3112
3159
|
}
|
|
3113
3160
|
return dir;
|
|
3114
3161
|
}
|
|
3115
3162
|
function uniqueAttachmentName(dir, originalName) {
|
|
3116
3163
|
const safe = originalName.replace(/[/\\]/g, "_") || "file";
|
|
3117
|
-
if (!existsSync5(
|
|
3164
|
+
if (!existsSync5(join6(dir, safe))) return safe;
|
|
3118
3165
|
const ext = extname(safe);
|
|
3119
3166
|
const stem = ext ? safe.slice(0, -ext.length) : safe;
|
|
3120
3167
|
for (let i = 1; i < 1e4; i++) {
|
|
3121
3168
|
const candidate = `${stem}-${i}${ext}`;
|
|
3122
|
-
if (!existsSync5(
|
|
3169
|
+
if (!existsSync5(join6(dir, candidate))) return candidate;
|
|
3123
3170
|
}
|
|
3124
3171
|
return `${stem}-${randomUUID()}${ext}`;
|
|
3125
3172
|
}
|
|
@@ -3182,7 +3229,7 @@ function attachmentFromAbsolutePath(absolutePath) {
|
|
|
3182
3229
|
content: `(not a file: ${absolutePath})`
|
|
3183
3230
|
};
|
|
3184
3231
|
}
|
|
3185
|
-
const buf =
|
|
3232
|
+
const buf = readFileSync6(absolutePath);
|
|
3186
3233
|
return attachmentFromBuffer(name, buf, { sourceLabel: absolutePath });
|
|
3187
3234
|
} catch (err) {
|
|
3188
3235
|
return {
|
|
@@ -3203,10 +3250,10 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
|
3203
3250
|
const st = statSync4(abs);
|
|
3204
3251
|
if (!st.isFile()) continue;
|
|
3205
3252
|
const name = uniqueAttachmentName(dir, originalName);
|
|
3206
|
-
const destAbs =
|
|
3253
|
+
const destAbs = join6(dir, name);
|
|
3207
3254
|
copyFileSync2(abs, destAbs);
|
|
3208
3255
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
3209
|
-
const buf =
|
|
3256
|
+
const buf = readFileSync6(destAbs);
|
|
3210
3257
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
3211
3258
|
} catch (err) {
|
|
3212
3259
|
out.push({
|
|
@@ -3228,8 +3275,8 @@ function stageBuffersAsAttachments(worktreePath, buffers) {
|
|
|
3228
3275
|
try {
|
|
3229
3276
|
const buf = Buffer.from(item.dataBase64, "base64");
|
|
3230
3277
|
const name = uniqueAttachmentName(dir, originalName);
|
|
3231
|
-
const destAbs =
|
|
3232
|
-
|
|
3278
|
+
const destAbs = join6(dir, name);
|
|
3279
|
+
writeFileSync3(destAbs, buf);
|
|
3233
3280
|
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
3234
3281
|
out.push(attachmentFromBuffer(name, buf, { path: rel }));
|
|
3235
3282
|
} catch (err) {
|
|
@@ -3273,10 +3320,10 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
3273
3320
|
}
|
|
3274
3321
|
const name = basename2(rel);
|
|
3275
3322
|
try {
|
|
3276
|
-
const abs =
|
|
3323
|
+
const abs = join6(worktreePath, rel);
|
|
3277
3324
|
const st = statSync4(abs);
|
|
3278
3325
|
if (!st.isFile()) continue;
|
|
3279
|
-
const buf =
|
|
3326
|
+
const buf = readFileSync6(abs);
|
|
3280
3327
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
3281
3328
|
} catch (err) {
|
|
3282
3329
|
out.push({
|
|
@@ -3833,7 +3880,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
3833
3880
|
return readThread(thread.id) ?? thread;
|
|
3834
3881
|
}
|
|
3835
3882
|
async function listLinearIssues(agent, repoPath) {
|
|
3836
|
-
const { getAdapter: getAdapter2 } = await import("./agents-
|
|
3883
|
+
const { getAdapter: getAdapter2 } = await import("./agents-3MWWWSMF.js");
|
|
3837
3884
|
await requireAgent(agent, { requireLinear: true });
|
|
3838
3885
|
const adapter = getAdapter2(agent);
|
|
3839
3886
|
if (!adapter.listLinearIssues) {
|
|
@@ -3913,6 +3960,7 @@ function createChatTab(input) {
|
|
|
3913
3960
|
if (isOrchestratorThread(from) || binding.sourceType === "orchestration") {
|
|
3914
3961
|
assertOrchestratorCapableAgent(nextAgent);
|
|
3915
3962
|
}
|
|
3963
|
+
const singletonInbox = isSlackCoordinatorThread(from) || isCloudCoordinatorThread(from);
|
|
3916
3964
|
const thread = createEmptyThread({
|
|
3917
3965
|
title,
|
|
3918
3966
|
// Chat-tab nicknames (soccer team or explicit) must stick. Post-turn
|
|
@@ -3920,6 +3968,10 @@ function createChatTab(input) {
|
|
|
3920
3968
|
// shared worktree folder name (e.g. fork "Arsenal" → "Monaco").
|
|
3921
3969
|
userSetTitle: true,
|
|
3922
3970
|
...binding,
|
|
3971
|
+
// Slack / Brightsy cloud identity stays on the original chat. A + tab
|
|
3972
|
+
// that copies `slack:team:user` would steal inbound DMs after you close
|
|
3973
|
+
// the connected chat.
|
|
3974
|
+
sourceRef: singletonInbox ? title : binding.sourceRef,
|
|
3923
3975
|
agent: nextAgent,
|
|
3924
3976
|
model: input.model !== void 0 ? input.model : input.agent && input.agent !== from.agent ? null : from.model,
|
|
3925
3977
|
effort: input.effort !== void 0 ? input.effort : from.effort,
|
|
@@ -4294,20 +4346,20 @@ import {
|
|
|
4294
4346
|
existsSync as existsSync8,
|
|
4295
4347
|
mkdtempSync,
|
|
4296
4348
|
readdirSync as readdirSync3,
|
|
4297
|
-
readFileSync as
|
|
4349
|
+
readFileSync as readFileSync7,
|
|
4298
4350
|
rmSync
|
|
4299
4351
|
} from "fs";
|
|
4300
4352
|
import { tmpdir } from "os";
|
|
4301
|
-
import { join as
|
|
4353
|
+
import { join as join7 } from "path";
|
|
4302
4354
|
import { createRequire } from "module";
|
|
4303
|
-
var CONDUCTOR_APP_SUPPORT =
|
|
4355
|
+
var CONDUCTOR_APP_SUPPORT = join7(
|
|
4304
4356
|
process.env.HOME ?? "",
|
|
4305
4357
|
"Library",
|
|
4306
4358
|
"Application Support",
|
|
4307
4359
|
"com.conductor.app"
|
|
4308
4360
|
);
|
|
4309
|
-
var CONDUCTOR_DB =
|
|
4310
|
-
var CURSOR_SDK_STORE =
|
|
4361
|
+
var CONDUCTOR_DB = join7(CONDUCTOR_APP_SUPPORT, "conductor.db");
|
|
4362
|
+
var CURSOR_SDK_STORE = join7(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
|
|
4311
4363
|
function openReadonlySqlite(file) {
|
|
4312
4364
|
const req = createRequire(import.meta.url);
|
|
4313
4365
|
const Database = req("better-sqlite3");
|
|
@@ -4334,11 +4386,11 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
4334
4386
|
return null;
|
|
4335
4387
|
}
|
|
4336
4388
|
for (const hash of hashes) {
|
|
4337
|
-
const agentsFile =
|
|
4389
|
+
const agentsFile = join7(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
4338
4390
|
if (!existsSync8(agentsFile)) continue;
|
|
4339
4391
|
let text3;
|
|
4340
4392
|
try {
|
|
4341
|
-
text3 =
|
|
4393
|
+
text3 = readFileSync7(agentsFile, "utf8");
|
|
4342
4394
|
} catch {
|
|
4343
4395
|
continue;
|
|
4344
4396
|
}
|
|
@@ -4381,7 +4433,7 @@ async function adoptThread(input) {
|
|
|
4381
4433
|
messages: input.messages ?? []
|
|
4382
4434
|
});
|
|
4383
4435
|
writeThread(thread);
|
|
4384
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-
|
|
4436
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-4ZY4QPWQ.js");
|
|
4385
4437
|
await ensureWorkspace2(repoPath);
|
|
4386
4438
|
return thread;
|
|
4387
4439
|
}
|
|
@@ -4392,8 +4444,8 @@ function listConductorWorkspaces() {
|
|
|
4392
4444
|
if (!existsSync8(CONDUCTOR_DB)) {
|
|
4393
4445
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
4394
4446
|
}
|
|
4395
|
-
const tmp = mkdtempSync(
|
|
4396
|
-
const snapshot =
|
|
4447
|
+
const tmp = mkdtempSync(join7(tmpdir(), "sideboard-conductor-"));
|
|
4448
|
+
const snapshot = join7(tmp, "conductor.db");
|
|
4397
4449
|
try {
|
|
4398
4450
|
copyFileSync3(CONDUCTOR_DB, snapshot);
|
|
4399
4451
|
for (const suffix of ["-wal", "-shm"]) {
|
|
@@ -4483,8 +4535,8 @@ function importConductorWorkspace(workspaceId) {
|
|
|
4483
4535
|
if (!existsSync8(CONDUCTOR_DB)) {
|
|
4484
4536
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
4485
4537
|
}
|
|
4486
|
-
const tmp = mkdtempSync(
|
|
4487
|
-
const snapshot =
|
|
4538
|
+
const tmp = mkdtempSync(join7(tmpdir(), "sideboard-conductor-"));
|
|
4539
|
+
const snapshot = join7(tmp, "conductor.db");
|
|
4488
4540
|
try {
|
|
4489
4541
|
copyFileSync3(CONDUCTOR_DB, snapshot);
|
|
4490
4542
|
for (const suffix of ["-wal", "-shm"]) {
|
|
@@ -4583,8 +4635,8 @@ async function importConductorWorkspaceAsync(workspaceId) {
|
|
|
4583
4635
|
import { EventEmitter } from "events";
|
|
4584
4636
|
|
|
4585
4637
|
// src/slack/outbound-watch.ts
|
|
4586
|
-
import { existsSync as existsSync10, readFileSync as
|
|
4587
|
-
import { join as
|
|
4638
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
4639
|
+
import { join as join10 } from "path";
|
|
4588
4640
|
|
|
4589
4641
|
// src/slack/api.ts
|
|
4590
4642
|
var SLACK_API = "https://slack.com/api";
|
|
@@ -4626,16 +4678,16 @@ async function slackAuthTest(token) {
|
|
|
4626
4678
|
}
|
|
4627
4679
|
|
|
4628
4680
|
// src/slack/reply-target.ts
|
|
4629
|
-
import { existsSync as existsSync9, readFileSync as
|
|
4630
|
-
import { join as
|
|
4681
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
|
|
4682
|
+
import { join as join8 } from "path";
|
|
4631
4683
|
function storePath() {
|
|
4632
|
-
return
|
|
4684
|
+
return join8(appDataDir(), "slack-reply-to.json");
|
|
4633
4685
|
}
|
|
4634
4686
|
function readStore() {
|
|
4635
4687
|
const path2 = storePath();
|
|
4636
4688
|
if (!existsSync9(path2)) return {};
|
|
4637
4689
|
try {
|
|
4638
|
-
const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(
|
|
4690
|
+
const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(readFileSync8(path2, "utf8"));
|
|
4639
4691
|
return parsed?.targets && typeof parsed.targets === "object" ? parsed.targets : {};
|
|
4640
4692
|
} catch {
|
|
4641
4693
|
return {};
|
|
@@ -4652,9 +4704,9 @@ function getSlackReplyTarget(threadId) {
|
|
|
4652
4704
|
}
|
|
4653
4705
|
|
|
4654
4706
|
// src/slack/workspaces.ts
|
|
4655
|
-
import { join as
|
|
4707
|
+
import { join as join9 } from "path";
|
|
4656
4708
|
function storePath2() {
|
|
4657
|
-
return
|
|
4709
|
+
return join9(appDataDir(), "slack-workspaces.json");
|
|
4658
4710
|
}
|
|
4659
4711
|
function readStore2() {
|
|
4660
4712
|
try {
|
|
@@ -4770,7 +4822,7 @@ var POLL_INTERVAL_MS = 12e3;
|
|
|
4770
4822
|
var lastPollMs = 0;
|
|
4771
4823
|
var nameCache = /* @__PURE__ */ new Map();
|
|
4772
4824
|
function storePath3() {
|
|
4773
|
-
return
|
|
4825
|
+
return join10(appDataDir(), "slack-outbound-watch.json");
|
|
4774
4826
|
}
|
|
4775
4827
|
function watchId(teamId, channelId, ts) {
|
|
4776
4828
|
return `${teamId}:${channelId}:${ts}`;
|
|
@@ -4802,7 +4854,7 @@ function readStore3() {
|
|
|
4802
4854
|
const path2 = storePath3();
|
|
4803
4855
|
if (!existsSync10(path2)) return [];
|
|
4804
4856
|
try {
|
|
4805
|
-
const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(
|
|
4857
|
+
const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(readFileSync9(path2, "utf8"));
|
|
4806
4858
|
return Array.isArray(parsed?.watches) ? parsed.watches : [];
|
|
4807
4859
|
} catch {
|
|
4808
4860
|
return [];
|
|
@@ -5165,7 +5217,7 @@ function shouldAutoArchiveOnPrMerge(opts) {
|
|
|
5165
5217
|
|
|
5166
5218
|
// src/git/orphan-cleanup.ts
|
|
5167
5219
|
import { existsSync as existsSync11, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
|
|
5168
|
-
import { join as
|
|
5220
|
+
import { join as join11 } from "path";
|
|
5169
5221
|
function isSideboardWorktreePath(path2) {
|
|
5170
5222
|
return path2.includes("/.sideboard/worktrees/") || path2.includes("/sideboard/workspaces/");
|
|
5171
5223
|
}
|
|
@@ -5212,9 +5264,9 @@ async function findOrphanWorktrees(repoPaths) {
|
|
|
5212
5264
|
if (existsSync11(root)) {
|
|
5213
5265
|
for (const entry of readdirSync4(root, { withFileTypes: true })) {
|
|
5214
5266
|
if (!entry.isDirectory()) continue;
|
|
5215
|
-
const path2 =
|
|
5267
|
+
const path2 = join11(root, entry.name).replace(/\/$/, "");
|
|
5216
5268
|
if (known.has(path2) || seen.has(path2)) continue;
|
|
5217
|
-
if (!existsSync11(
|
|
5269
|
+
if (!existsSync11(join11(path2, ".git"))) continue;
|
|
5218
5270
|
seen.add(path2);
|
|
5219
5271
|
let mtimeMs = 0;
|
|
5220
5272
|
try {
|
|
@@ -5364,7 +5416,7 @@ async function applyThreadIntoMain(thread, opts) {
|
|
|
5364
5416
|
|
|
5365
5417
|
// src/git/clone-repo.ts
|
|
5366
5418
|
import { existsSync as existsSync12 } from "fs";
|
|
5367
|
-
import { basename as basename3, join as
|
|
5419
|
+
import { basename as basename3, join as join12 } from "path";
|
|
5368
5420
|
import { execa as execa4 } from "execa";
|
|
5369
5421
|
async function cloneRepoIntoSideboard(opts) {
|
|
5370
5422
|
const url = opts.url.trim();
|
|
@@ -5375,7 +5427,7 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
5375
5427
|
name = leaf || "repo";
|
|
5376
5428
|
}
|
|
5377
5429
|
name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
|
|
5378
|
-
const dest =
|
|
5430
|
+
const dest = join12(sideboardReposDir(), name);
|
|
5379
5431
|
if (existsSync12(dest)) {
|
|
5380
5432
|
const repoPath2 = await resolveRepoRoot(dest);
|
|
5381
5433
|
const workspace2 = await ensureWorkspace(repoPath2);
|
|
@@ -5394,8 +5446,8 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
5394
5446
|
|
|
5395
5447
|
// src/review/request-review.ts
|
|
5396
5448
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
5397
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as
|
|
5398
|
-
import { dirname as dirname3, join as
|
|
5449
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
|
|
5450
|
+
import { dirname as dirname3, join as join13 } from "path";
|
|
5399
5451
|
|
|
5400
5452
|
// src/review/review-request-template.ts
|
|
5401
5453
|
var REVIEW_REQUEST_TEMPLATE = `# Review guidelines:
|
|
@@ -5542,20 +5594,20 @@ function shouldRefreshReviewRequestTemplate(content) {
|
|
|
5542
5594
|
function readTextIfPresent(abs) {
|
|
5543
5595
|
if (!existsSync13(abs)) return null;
|
|
5544
5596
|
try {
|
|
5545
|
-
const content =
|
|
5597
|
+
const content = readFileSync10(abs, "utf8");
|
|
5546
5598
|
return content.trim() ? content : null;
|
|
5547
5599
|
} catch {
|
|
5548
5600
|
return null;
|
|
5549
5601
|
}
|
|
5550
5602
|
}
|
|
5551
5603
|
function ensureAttachmentsGitignore(worktreePath) {
|
|
5552
|
-
const gitignoreAbs =
|
|
5604
|
+
const gitignoreAbs = join13(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
5553
5605
|
if (existsSync13(gitignoreAbs)) return;
|
|
5554
5606
|
mkdirSync4(dirname3(gitignoreAbs), { recursive: true });
|
|
5555
|
-
|
|
5607
|
+
writeFileSync4(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
5556
5608
|
}
|
|
5557
5609
|
function resolveReviewGuidelines(worktreePath) {
|
|
5558
|
-
const repoAbs =
|
|
5610
|
+
const repoAbs = join13(worktreePath, REPO_REVIEW_PATH);
|
|
5559
5611
|
const repoContent = readTextIfPresent(repoAbs);
|
|
5560
5612
|
if (repoContent) {
|
|
5561
5613
|
return {
|
|
@@ -5565,7 +5617,7 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
5565
5617
|
source: "repo"
|
|
5566
5618
|
};
|
|
5567
5619
|
}
|
|
5568
|
-
const localAbs =
|
|
5620
|
+
const localAbs = join13(worktreePath, REVIEW_REQUEST_PATH);
|
|
5569
5621
|
const localContent = readTextIfPresent(localAbs);
|
|
5570
5622
|
if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
|
|
5571
5623
|
return {
|
|
@@ -5575,7 +5627,7 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
5575
5627
|
source: "local"
|
|
5576
5628
|
};
|
|
5577
5629
|
}
|
|
5578
|
-
const legacyAbs =
|
|
5630
|
+
const legacyAbs = join13(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
|
|
5579
5631
|
const legacyContent = readTextIfPresent(legacyAbs);
|
|
5580
5632
|
if (legacyContent && !shouldRefreshReviewRequestTemplate(legacyContent)) {
|
|
5581
5633
|
return {
|
|
@@ -5587,7 +5639,7 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
5587
5639
|
}
|
|
5588
5640
|
ensureAttachmentsGitignore(worktreePath);
|
|
5589
5641
|
mkdirSync4(dirname3(localAbs), { recursive: true });
|
|
5590
|
-
|
|
5642
|
+
writeFileSync4(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
|
|
5591
5643
|
return {
|
|
5592
5644
|
path: REVIEW_REQUEST_PATH,
|
|
5593
5645
|
name: REVIEW_REQUEST_NAME,
|
|
@@ -5596,7 +5648,7 @@ function resolveReviewGuidelines(worktreePath) {
|
|
|
5596
5648
|
};
|
|
5597
5649
|
}
|
|
5598
5650
|
function ensureReviewRequestFile(worktreePath) {
|
|
5599
|
-
const repoAbs =
|
|
5651
|
+
const repoAbs = join13(worktreePath, REPO_REVIEW_PATH);
|
|
5600
5652
|
const repoContent = readTextIfPresent(repoAbs);
|
|
5601
5653
|
if (repoContent) {
|
|
5602
5654
|
return {
|
|
@@ -5606,8 +5658,8 @@ function ensureReviewRequestFile(worktreePath) {
|
|
|
5606
5658
|
source: "repo"
|
|
5607
5659
|
};
|
|
5608
5660
|
}
|
|
5609
|
-
const localAbs =
|
|
5610
|
-
const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent(
|
|
5661
|
+
const localAbs = join13(worktreePath, REVIEW_REQUEST_PATH);
|
|
5662
|
+
const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent(join13(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
|
|
5611
5663
|
if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
|
|
5612
5664
|
const path2 = existsSync13(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
|
|
5613
5665
|
return {
|
|
@@ -5618,7 +5670,7 @@ function ensureReviewRequestFile(worktreePath) {
|
|
|
5618
5670
|
};
|
|
5619
5671
|
}
|
|
5620
5672
|
mkdirSync4(dirname3(repoAbs), { recursive: true });
|
|
5621
|
-
|
|
5673
|
+
writeFileSync4(repoAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
|
|
5622
5674
|
return {
|
|
5623
5675
|
path: REPO_REVIEW_PATH,
|
|
5624
5676
|
name: REPO_REVIEW_NAME,
|
|
@@ -5638,7 +5690,7 @@ function buildReviewRequestAttachment(content, opts) {
|
|
|
5638
5690
|
};
|
|
5639
5691
|
}
|
|
5640
5692
|
function readExistingReviewRequestFile(worktreePath) {
|
|
5641
|
-
return readTextIfPresent(
|
|
5693
|
+
return readTextIfPresent(join13(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent(join13(worktreePath, REVIEW_REQUEST_PATH)) ?? readTextIfPresent(join13(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
|
|
5642
5694
|
}
|
|
5643
5695
|
async function requestReview(threadRef, send2) {
|
|
5644
5696
|
const from = findThreadByRef(threadRef);
|
|
@@ -6126,6 +6178,9 @@ var Orchestrator = class {
|
|
|
6126
6178
|
}
|
|
6127
6179
|
return withThreadLock(thread.id, async () => {
|
|
6128
6180
|
const current = this.requireThread(thread.id);
|
|
6181
|
+
if (current.status === "archived") {
|
|
6182
|
+
throw new Error(`Thread is archived: ${thread.id}`);
|
|
6183
|
+
}
|
|
6129
6184
|
const queue = [...current.queue, prompt];
|
|
6130
6185
|
this.haltDrain.delete(thread.id);
|
|
6131
6186
|
const patch = { queue, status: "queued" };
|
|
@@ -6136,7 +6191,9 @@ var Orchestrator = class {
|
|
|
6136
6191
|
updateThread(thread.id, patch);
|
|
6137
6192
|
this.emit({ type: "queue_changed", threadId: thread.id, queue });
|
|
6138
6193
|
this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
|
|
6139
|
-
|
|
6194
|
+
if (thisProcessShouldDrainAgentQueues()) {
|
|
6195
|
+
void this.drainQueue(thread.id);
|
|
6196
|
+
}
|
|
6140
6197
|
return this.requireThread(thread.id);
|
|
6141
6198
|
});
|
|
6142
6199
|
}
|
|
@@ -6189,10 +6246,10 @@ var Orchestrator = class {
|
|
|
6189
6246
|
async sendQueuedMessageNow(threadRef, index) {
|
|
6190
6247
|
const thread = this.requireThread(threadRef);
|
|
6191
6248
|
const promoted = await withThreadLock(thread.id, async () => {
|
|
6192
|
-
const
|
|
6193
|
-
if (index < 0 || index >=
|
|
6194
|
-
const item =
|
|
6195
|
-
const rest =
|
|
6249
|
+
const current2 = this.requireThread(thread.id);
|
|
6250
|
+
if (index < 0 || index >= current2.queue.length) return false;
|
|
6251
|
+
const item = current2.queue[index];
|
|
6252
|
+
const rest = current2.queue.filter((_, i) => i !== index);
|
|
6196
6253
|
const queue = [item, ...rest];
|
|
6197
6254
|
this.haltDrain.delete(thread.id);
|
|
6198
6255
|
updateThread(thread.id, { queue });
|
|
@@ -6200,10 +6257,16 @@ var Orchestrator = class {
|
|
|
6200
6257
|
return true;
|
|
6201
6258
|
});
|
|
6202
6259
|
if (!promoted) return this.requireThread(thread.id);
|
|
6260
|
+
const current = this.requireThread(thread.id);
|
|
6203
6261
|
const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
|
|
6262
|
+
const livePid = current.agentPid;
|
|
6263
|
+
const foreignLive = !inFlight && typeof livePid === "number" && livePid > 0 && isPidAlive(livePid);
|
|
6204
6264
|
if (inFlight) {
|
|
6205
6265
|
this.stop(thread.id, { clearQueue: false, continueQueue: true });
|
|
6206
6266
|
} else {
|
|
6267
|
+
if (foreignLive) {
|
|
6268
|
+
this.stop(thread.id, { clearQueue: false, continueQueue: true });
|
|
6269
|
+
}
|
|
6207
6270
|
void this.drainQueue(thread.id);
|
|
6208
6271
|
}
|
|
6209
6272
|
return this.requireThread(thread.id);
|
|
@@ -6638,6 +6701,13 @@ var Orchestrator = class {
|
|
|
6638
6701
|
if (handle) handle.kill();
|
|
6639
6702
|
const proc = this.processes.get(`${thread.id}:agent`);
|
|
6640
6703
|
if (proc) proc.kill();
|
|
6704
|
+
const pid = thread.agentPid;
|
|
6705
|
+
if (typeof pid === "number" && pid > 0 && isPidAlive(pid)) {
|
|
6706
|
+
try {
|
|
6707
|
+
process.kill(pid, "SIGTERM");
|
|
6708
|
+
} catch {
|
|
6709
|
+
}
|
|
6710
|
+
}
|
|
6641
6711
|
const stopped = writeLiveStatus(thread.id, "stopped") ?? readThread(thread.id) ?? thread;
|
|
6642
6712
|
if (stopped.status === "stopped") {
|
|
6643
6713
|
this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
|
|
@@ -7376,7 +7446,7 @@ var Orchestrator = class {
|
|
|
7376
7446
|
this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
|
|
7377
7447
|
if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
|
|
7378
7448
|
try {
|
|
7379
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-
|
|
7449
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-4ZY4QPWQ.js");
|
|
7380
7450
|
await ensureWorkspace2(thread.repoPath);
|
|
7381
7451
|
} catch {
|
|
7382
7452
|
}
|
|
@@ -9623,14 +9693,14 @@ async function runCloudConnect(opts) {
|
|
|
9623
9693
|
}
|
|
9624
9694
|
|
|
9625
9695
|
// src/agents/user-mcp-config.ts
|
|
9626
|
-
import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as
|
|
9696
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
|
|
9627
9697
|
import { homedir as homedir2 } from "os";
|
|
9628
|
-
import { dirname as dirname4, join as
|
|
9698
|
+
import { dirname as dirname4, join as join14 } from "path";
|
|
9629
9699
|
function userCursorMcpConfigPath() {
|
|
9630
|
-
return
|
|
9700
|
+
return join14(homedir2(), ".cursor", "mcp.json");
|
|
9631
9701
|
}
|
|
9632
9702
|
function userClaudeMcpConfigPath() {
|
|
9633
|
-
return
|
|
9703
|
+
return join14(homedir2(), ".claude.json");
|
|
9634
9704
|
}
|
|
9635
9705
|
function asObject(value) {
|
|
9636
9706
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
@@ -9659,14 +9729,14 @@ function writeMergedMcpServersJson(configPath, sideboard) {
|
|
|
9659
9729
|
let existing = {};
|
|
9660
9730
|
if (existsSync15(configPath)) {
|
|
9661
9731
|
try {
|
|
9662
|
-
existing = JSON.parse(
|
|
9732
|
+
existing = JSON.parse(readFileSync11(configPath, "utf8"));
|
|
9663
9733
|
} catch {
|
|
9664
9734
|
existing = {};
|
|
9665
9735
|
}
|
|
9666
9736
|
}
|
|
9667
9737
|
const next = mergeSideboardIntoMcpServersJson(existing, sideboard);
|
|
9668
9738
|
mkdirSync5(dirname4(configPath), { recursive: true });
|
|
9669
|
-
|
|
9739
|
+
writeFileSync5(configPath, `${JSON.stringify(next, null, 2)}
|
|
9670
9740
|
`);
|
|
9671
9741
|
}
|
|
9672
9742
|
function launchFromResolved(server) {
|
|
@@ -10480,11 +10550,16 @@ function slackInboundSuperseded(opts) {
|
|
|
10480
10550
|
}
|
|
10481
10551
|
return opts.currentInboundGeneration() !== opts.inboundGeneration;
|
|
10482
10552
|
}
|
|
10483
|
-
function
|
|
10553
|
+
function slackCoordinatorGone(err) {
|
|
10554
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
10555
|
+
return /thread not found|thread is archived/i.test(errMsg);
|
|
10556
|
+
}
|
|
10557
|
+
function interruptSlackCoordinatorForInbound(msg, _agent, log = () => void 0) {
|
|
10484
10558
|
const userId = msg.userId?.trim();
|
|
10485
10559
|
if (!userId) return false;
|
|
10486
10560
|
try {
|
|
10487
|
-
const coordinator =
|
|
10561
|
+
const coordinator = findSlackCoordinator(msg.teamId, userId);
|
|
10562
|
+
if (!coordinator) return false;
|
|
10488
10563
|
const fresh = readThread(coordinator.id) ?? coordinator;
|
|
10489
10564
|
if (fresh.status !== "running" && fresh.status !== "queued") return false;
|
|
10490
10565
|
getOrchestrator().stop(fresh.id, { clearQueue: true });
|
|
@@ -10650,15 +10725,16 @@ async function handleSlackInbound(msg, opts) {
|
|
|
10650
10725
|
log(`skip superseded ${msg.kind} ${msg.ts}`);
|
|
10651
10726
|
return;
|
|
10652
10727
|
}
|
|
10653
|
-
const coordinator = ensureSlackCoordinator(msg.teamId, userId, agent);
|
|
10654
|
-
let fresh = readThread(coordinator.id) ?? coordinator;
|
|
10655
10728
|
if (isSlackStopCommand(msg.text)) {
|
|
10656
|
-
|
|
10657
|
-
|
|
10658
|
-
|
|
10659
|
-
|
|
10660
|
-
|
|
10661
|
-
|
|
10729
|
+
const live = findSlackCoordinator(msg.teamId, userId);
|
|
10730
|
+
if (live) {
|
|
10731
|
+
try {
|
|
10732
|
+
getOrchestrator().stop(live.id, { clearQueue: true });
|
|
10733
|
+
log(`stop ${msg.kind} ${msg.ts} \u2192 coordinator ${live.id.slice(0, 8)}`);
|
|
10734
|
+
} catch (err) {
|
|
10735
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
10736
|
+
log(`stop ${msg.ts}: ${errMsg}`);
|
|
10737
|
+
}
|
|
10662
10738
|
}
|
|
10663
10739
|
if (slackInboundSuperseded(opts)) {
|
|
10664
10740
|
log(`skip superseded stop reply ${msg.ts}`);
|
|
@@ -10668,34 +10744,65 @@ async function handleSlackInbound(msg, opts) {
|
|
|
10668
10744
|
log(`replied stopped ${msg.ts}`);
|
|
10669
10745
|
return;
|
|
10670
10746
|
}
|
|
10747
|
+
const opened = ensureSlackCoordinator(msg.teamId, userId, agent);
|
|
10748
|
+
let fresh = readThread(opened.id) ?? opened;
|
|
10671
10749
|
if (fresh.status === "running" || fresh.status === "queued") {
|
|
10672
10750
|
interruptSlackCoordinatorForInbound(msg, agent, log);
|
|
10673
|
-
fresh = readThread(
|
|
10751
|
+
fresh = readThread(fresh.id) ?? fresh;
|
|
10674
10752
|
}
|
|
10675
10753
|
log(
|
|
10676
10754
|
`run ${msg.kind} ${msg.ts} user ${userId} \u2192 coordinator ${fresh.id.slice(0, 8)} (${inventory.length} workspace${inventory.length === 1 ? "" : "s"})`
|
|
10677
10755
|
);
|
|
10678
10756
|
const prompt = formatSlackInboundPrompt(msg);
|
|
10679
|
-
setSlackReplyTarget({
|
|
10680
|
-
threadId: fresh.id,
|
|
10681
|
-
teamId: msg.teamId,
|
|
10682
|
-
channelId: msg.channelId,
|
|
10683
|
-
threadTs: slackReplyThreadTs(msg)
|
|
10684
|
-
});
|
|
10685
10757
|
const orch = getOrchestrator();
|
|
10758
|
+
const bindReplyTarget = (threadId) => {
|
|
10759
|
+
setSlackReplyTarget({
|
|
10760
|
+
threadId,
|
|
10761
|
+
teamId: msg.teamId,
|
|
10762
|
+
channelId: msg.channelId,
|
|
10763
|
+
threadTs: slackReplyThreadTs(msg)
|
|
10764
|
+
});
|
|
10765
|
+
};
|
|
10766
|
+
bindReplyTarget(fresh.id);
|
|
10767
|
+
const runTurn = async (threadId) => {
|
|
10768
|
+
await orch.send(threadId, prompt);
|
|
10769
|
+
await orch.waitForTurn(threadId, 14 * 60 * 1e3);
|
|
10770
|
+
};
|
|
10686
10771
|
let reply;
|
|
10687
10772
|
try {
|
|
10688
|
-
|
|
10689
|
-
|
|
10773
|
+
try {
|
|
10774
|
+
await runTurn(fresh.id);
|
|
10775
|
+
} catch (err) {
|
|
10776
|
+
if (!slackCoordinatorGone(err)) throw err;
|
|
10777
|
+
log(`coordinator ${fresh.id.slice(0, 8)} gone, opening a new chat`);
|
|
10778
|
+
fresh = ensureSlackCoordinator(msg.teamId, userId, agent, { forceNew: true });
|
|
10779
|
+
bindReplyTarget(fresh.id);
|
|
10780
|
+
await runTurn(fresh.id);
|
|
10781
|
+
}
|
|
10690
10782
|
if (slackInboundSuperseded(opts)) {
|
|
10691
10783
|
log(`turn finished ${msg.ts} (superseded)`);
|
|
10692
10784
|
return;
|
|
10693
10785
|
}
|
|
10694
|
-
|
|
10695
|
-
if (
|
|
10786
|
+
let after = readThread(fresh.id);
|
|
10787
|
+
if (after?.status === "stopped") {
|
|
10696
10788
|
log(`turn finished ${msg.ts} (interrupted, skip post)`);
|
|
10697
10789
|
return;
|
|
10698
10790
|
}
|
|
10791
|
+
if (!after || after.status === "archived") {
|
|
10792
|
+
log(`coordinator ${fresh.id.slice(0, 8)} closed, opening a new chat`);
|
|
10793
|
+
fresh = ensureSlackCoordinator(msg.teamId, userId, agent, { forceNew: true });
|
|
10794
|
+
bindReplyTarget(fresh.id);
|
|
10795
|
+
await runTurn(fresh.id);
|
|
10796
|
+
if (slackInboundSuperseded(opts)) {
|
|
10797
|
+
log(`turn finished ${msg.ts} (superseded)`);
|
|
10798
|
+
return;
|
|
10799
|
+
}
|
|
10800
|
+
after = readThread(fresh.id);
|
|
10801
|
+
if (!after || after.status === "stopped" || after.status === "archived") {
|
|
10802
|
+
log(`turn finished ${msg.ts} (interrupted, skip post)`);
|
|
10803
|
+
return;
|
|
10804
|
+
}
|
|
10805
|
+
}
|
|
10699
10806
|
reply = orch.getTurnResult(fresh.id).text.trim();
|
|
10700
10807
|
} catch (err) {
|
|
10701
10808
|
if (slackInboundSuperseded(opts)) {
|
|
@@ -10703,8 +10810,7 @@ async function handleSlackInbound(msg, opts) {
|
|
|
10703
10810
|
return;
|
|
10704
10811
|
}
|
|
10705
10812
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
10706
|
-
|
|
10707
|
-
if (gone) {
|
|
10813
|
+
if (slackCoordinatorGone(err)) {
|
|
10708
10814
|
log(`turn finished ${msg.ts} (thread gone, skip post)`);
|
|
10709
10815
|
return;
|
|
10710
10816
|
}
|
|
@@ -11447,6 +11553,7 @@ export {
|
|
|
11447
11553
|
captureTurnBaseline,
|
|
11448
11554
|
checkoutPrStackLayer,
|
|
11449
11555
|
childEnvWithAppSettings,
|
|
11556
|
+
claimDesktopHost,
|
|
11450
11557
|
claudeAdapter,
|
|
11451
11558
|
claudeChromeEnabled,
|
|
11452
11559
|
claudeUserSettingsPath,
|
|
@@ -11485,6 +11592,7 @@ export {
|
|
|
11485
11592
|
decodeBrightsyTarget,
|
|
11486
11593
|
deleteBranchOnPurgeEnabled,
|
|
11487
11594
|
deleteThreadRecord,
|
|
11595
|
+
desktopHostPidPath,
|
|
11488
11596
|
detectAgents,
|
|
11489
11597
|
detectGhStack,
|
|
11490
11598
|
detectLocalMergeConflicts,
|
|
@@ -11518,6 +11626,7 @@ export {
|
|
|
11518
11626
|
findConventionSetup,
|
|
11519
11627
|
findInvalidCacheControlTtlOrder,
|
|
11520
11628
|
findOrphanWorktrees,
|
|
11629
|
+
findSlackCoordinator,
|
|
11521
11630
|
findThreadByRef,
|
|
11522
11631
|
findThreadForStackLayer,
|
|
11523
11632
|
flattenTurnInput,
|
|
@@ -11609,6 +11718,7 @@ export {
|
|
|
11609
11718
|
isConductorBundledCli,
|
|
11610
11719
|
isCursorAutoModel,
|
|
11611
11720
|
isDefaultishSourceRef,
|
|
11721
|
+
isDesktopHostAlive,
|
|
11612
11722
|
isDirty,
|
|
11613
11723
|
isGhRateLimitError,
|
|
11614
11724
|
isGlobalRepoPath,
|
|
@@ -11630,6 +11740,7 @@ export {
|
|
|
11630
11740
|
isSlackExternalReplyPrompt,
|
|
11631
11741
|
isSlackOAuthCancelled,
|
|
11632
11742
|
isThinkingEffort,
|
|
11743
|
+
isThisProcessDesktopHost,
|
|
11633
11744
|
isThreadCaffeinated,
|
|
11634
11745
|
isWorkspaceScratchPath,
|
|
11635
11746
|
linearAuthorizationHeader,
|
|
@@ -11731,6 +11842,7 @@ export {
|
|
|
11731
11842
|
refreshSlackReplyBadges,
|
|
11732
11843
|
registerPackagedUserMcpClients,
|
|
11733
11844
|
releaseCaffeinateHoldForThread,
|
|
11845
|
+
releaseDesktopHost,
|
|
11734
11846
|
removeWorkspace,
|
|
11735
11847
|
removeWorktree,
|
|
11736
11848
|
repoSlug,
|
|
@@ -11831,6 +11943,7 @@ export {
|
|
|
11831
11943
|
taskMessageText,
|
|
11832
11944
|
thinkingEffortBars,
|
|
11833
11945
|
thinkingEffortLabel,
|
|
11946
|
+
thisProcessShouldDrainAgentQueues,
|
|
11834
11947
|
threadDisplayLabel,
|
|
11835
11948
|
threadFilePath,
|
|
11836
11949
|
threadLockPath,
|