@sideboard-ai/core 0.1.156 → 0.1.158
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-JSTJLXAW.js → abletime-BEIDJG7W.js} +5 -1
- package/dist/{abletime-H7LPGIP2.js → abletime-GWZE6OYM.js} +5 -1
- package/dist/{agents-IY4CSPWV.js → agents-E3ZPWZRS.js} +2 -2
- package/dist/{agents-2FTZAXO3.js → agents-RGE7PSUY.js} +4 -4
- package/dist/{chunk-TI6UMRWK.js → chunk-3SUZMPGL.js} +5 -4
- package/dist/{chunk-NVXVEGGM.js → chunk-5U4NZLBH.js} +4 -0
- package/dist/{chunk-CJQLPPEJ.js → chunk-A7QXLYBN.js} +1 -1
- package/dist/{chunk-XLU7DXHH.js → chunk-BLRHO7XQ.js} +54 -5
- package/dist/{chunk-MSFPM5VK.js → chunk-CVR4RJ6G.js} +3 -2
- package/dist/{chunk-JOK4XWBG.js → chunk-DVJBO3M7.js} +87 -3
- package/dist/{chunk-UA3YZKRX.js → chunk-EPQIOAAY.js} +25 -5
- package/dist/{chunk-OLE2BLIX.js → chunk-FBFIOVDH.js} +457 -106
- package/dist/{chunk-2SDMENAL.js → chunk-JV4VNFMS.js} +1 -1
- package/dist/{chunk-7EIPEPAH.js → chunk-KYKNFJK5.js} +484 -105
- package/dist/{chunk-FDCFXMDG.js → chunk-N27GVFZY.js} +87 -3
- package/dist/{coordinator-prompt-D2FHU5IZ.js → coordinator-prompt-26IJU2AV.js} +1 -1
- package/dist/{coordinator-prompt-UZDNIVP7.js → coordinator-prompt-GZESIQNH.js} +3 -3
- package/dist/{global-workspace-5WZVSWNT.js → global-workspace-5BIW26YR.js} +1 -1
- package/dist/{global-workspace-MR75BDBY.js → global-workspace-REI55FZ6.js} +3 -3
- package/dist/index.cjs +1405 -560
- package/dist/index.d.cts +138 -4
- package/dist/index.d.ts +138 -4
- package/dist/index.js +609 -274
- package/dist/mcp/run-stdio.cjs +1253 -500
- package/dist/mcp/run-stdio.js +525 -226
- package/dist/{orchestrator-LXO2CDOG.js → orchestrator-J727WIJY.js} +6 -6
- package/dist/{orchestrator-G4WKJG5Y.js → orchestrator-ZVW2AC6B.js} +3 -3
- package/dist/{plan-file-OZEBFSGQ.js → plan-file-I24VJVP2.js} +2 -2
- package/dist/{workspaces-RP32AW6X.js → workspaces-NYSM2EPX.js} +1 -1
- package/dist/{workspaces-HO4EQNNM.js → workspaces-OCEPZXFD.js} +3 -3
- package/dist/{worktree-PEWDAX5O.js → worktree-VTMBZKY6.js} +2 -2
- package/package.json +1 -1
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
stripBrightsyNdjsonNoise,
|
|
25
25
|
sumUsageList,
|
|
26
26
|
toolDescription
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-BLRHO7XQ.js";
|
|
28
28
|
import {
|
|
29
29
|
SLACK_REPLY_FORMATTING,
|
|
30
30
|
addWorkspace,
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
stageAbsolutePathsAsAttachments,
|
|
51
51
|
stageBuffersAsAttachments,
|
|
52
52
|
syncWorkspacesFromThreads
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-CVR4RJ6G.js";
|
|
54
54
|
import {
|
|
55
55
|
addPrStackLayer,
|
|
56
56
|
allocateTeamName,
|
|
@@ -101,7 +101,9 @@ import {
|
|
|
101
101
|
} from "./chunk-LQMFCS54.js";
|
|
102
102
|
import {
|
|
103
103
|
ATTACHMENTS_DIR,
|
|
104
|
-
|
|
104
|
+
DETACHED_JOBS_DIR,
|
|
105
|
+
LEGACY_ATTACHMENTS_DIR,
|
|
106
|
+
LEGACY_DETACHED_JOBS_DIR
|
|
105
107
|
} from "./chunk-JRH62XC4.js";
|
|
106
108
|
import {
|
|
107
109
|
httpFetch
|
|
@@ -128,11 +130,13 @@ import {
|
|
|
128
130
|
childEnvWithAppSettings,
|
|
129
131
|
getIssueSource,
|
|
130
132
|
isAbleTimeConnected,
|
|
133
|
+
isLinearConnected,
|
|
131
134
|
isSecureFileEncrypted,
|
|
132
135
|
loadAppSettings,
|
|
133
136
|
orchestrationQuotaFallbackAgent,
|
|
134
137
|
orchestrationQuotaOnLimit,
|
|
135
138
|
readSecureJson,
|
|
139
|
+
resolveEffectiveIssueSource,
|
|
136
140
|
resolveNewThreadOptions,
|
|
137
141
|
resolveThreadDefaults,
|
|
138
142
|
resolveVaultKey,
|
|
@@ -457,7 +461,7 @@ async function continueSourceThread(threadId, prompt) {
|
|
|
457
461
|
await continueOnReply(threadId, prompt);
|
|
458
462
|
return;
|
|
459
463
|
}
|
|
460
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
464
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-ZVW2AC6B.js");
|
|
461
465
|
await getOrchestrator2().send(threadId, prompt);
|
|
462
466
|
} catch {
|
|
463
467
|
}
|
|
@@ -717,7 +721,7 @@ async function pollSlackOutboundWatches(opts) {
|
|
|
717
721
|
}
|
|
718
722
|
|
|
719
723
|
// src/orchestrator/orchestrator.ts
|
|
720
|
-
import { existsSync as
|
|
724
|
+
import { existsSync as existsSync18 } from "fs";
|
|
721
725
|
|
|
722
726
|
// src/agents/spawn.ts
|
|
723
727
|
import { createInterface } from "readline";
|
|
@@ -737,9 +741,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
737
741
|
`Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
|
|
738
742
|
);
|
|
739
743
|
}
|
|
740
|
-
const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-
|
|
744
|
+
const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-5BIW26YR.js");
|
|
741
745
|
if (isGlobalThread2(thread)) {
|
|
742
|
-
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-
|
|
746
|
+
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-26IJU2AV.js");
|
|
743
747
|
ensureGlobalCoordinatorCwd2(
|
|
744
748
|
isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
|
|
745
749
|
);
|
|
@@ -2562,8 +2566,232 @@ function notifyParentOfChildHalt(child, status, send) {
|
|
|
2562
2566
|
return true;
|
|
2563
2567
|
}
|
|
2564
2568
|
|
|
2569
|
+
// src/mcp/wait-for-job.ts
|
|
2570
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, readdirSync as readdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
2571
|
+
import { dirname as dirname3, join as join10 } from "path";
|
|
2572
|
+
|
|
2573
|
+
// src/mcp/wait-for-turn.ts
|
|
2574
|
+
var MCP_WAIT_FOR_TURN_MAX_MS = 45e3;
|
|
2575
|
+
function mcpWaitForTurnTimeoutMs(requested) {
|
|
2576
|
+
const n = requested ?? MCP_WAIT_FOR_TURN_MAX_MS;
|
|
2577
|
+
if (!Number.isFinite(n)) return MCP_WAIT_FOR_TURN_MAX_MS;
|
|
2578
|
+
return Math.min(Math.max(1e3, Math.floor(n)), MCP_WAIT_FOR_TURN_MAX_MS);
|
|
2579
|
+
}
|
|
2580
|
+
var MCP_WAIT_STILL_RUNNING_HINT = "Child is still working. Call wait_for_turn again. Do not send a check-in prompt or assume a hang while progress is updating.";
|
|
2581
|
+
var MCP_WAIT_QUEUED_HINT = "Child is queued waiting for a concurrency slot \u2014 it has not started yet. Call wait_for_turn again. Do not send a check-in prompt, force_stop, or assume it failed to start.";
|
|
2582
|
+
function mcpWaitStillRunningHint(status) {
|
|
2583
|
+
return status === "queued" ? MCP_WAIT_QUEUED_HINT : MCP_WAIT_STILL_RUNNING_HINT;
|
|
2584
|
+
}
|
|
2585
|
+
var MCP_WAIT_STOPPED_HINT = "Child was stopped before the turn finished. Do not treat this as success. send_to_thread to resume, or tell the user.";
|
|
2586
|
+
var MCP_WAIT_BROKEN_HINT = "Child worktree is broken (missing on disk). Tell the user \u2014 do not treat this as success.";
|
|
2587
|
+
var MCP_WAIT_ERROR_HINT = "Child turn failed. lastError/text is the failure \u2014 switch agent, tell the user, or retry. Do not treat empty text as success.";
|
|
2588
|
+
function mcpWaitFinishedHint(status) {
|
|
2589
|
+
if (status === "stopped") return MCP_WAIT_STOPPED_HINT;
|
|
2590
|
+
if (status === "broken") return MCP_WAIT_BROKEN_HINT;
|
|
2591
|
+
if (status === "error") return MCP_WAIT_ERROR_HINT;
|
|
2592
|
+
return void 0;
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
// src/mcp/wait-for-job.ts
|
|
2596
|
+
var MAX_JOB_CONTINUES = 8;
|
|
2597
|
+
var MCP_WAIT_JOB_STILL_RUNNING_HINT = "Job is still running. present_artifact type=log with the same artifact_id and content=delta (new lines only). Then call wait_for_job again. Do not end the turn or tell the user you will let them know later.";
|
|
2598
|
+
var JOB_ID_RE = /^[a-zA-Z0-9._-]{1,64}$/;
|
|
2599
|
+
function mcpWaitForJobTimeoutMs(requested) {
|
|
2600
|
+
return mcpWaitForTurnTimeoutMs(requested);
|
|
2601
|
+
}
|
|
2602
|
+
function sanitizeDetachedJobId(id) {
|
|
2603
|
+
const s = id.trim();
|
|
2604
|
+
if (!JOB_ID_RE.test(s)) {
|
|
2605
|
+
throw new Error(`detached-job id must be 1\u201364 chars [A-Za-z0-9._-], got ${JSON.stringify(id)}`);
|
|
2606
|
+
}
|
|
2607
|
+
return s;
|
|
2608
|
+
}
|
|
2609
|
+
function jobAlive(pid) {
|
|
2610
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
2611
|
+
try {
|
|
2612
|
+
process.kill(pid, 0);
|
|
2613
|
+
return true;
|
|
2614
|
+
} catch {
|
|
2615
|
+
return false;
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
function readIntFile(file) {
|
|
2619
|
+
if (!existsSync8(file)) return null;
|
|
2620
|
+
const n = Number.parseInt(readFileSync7(file, "utf8").trim(), 10);
|
|
2621
|
+
return Number.isInteger(n) ? n : null;
|
|
2622
|
+
}
|
|
2623
|
+
function jobDir(root, id, legacy = false) {
|
|
2624
|
+
return join10(root, legacy ? LEGACY_DETACHED_JOBS_DIR : DETACHED_JOBS_DIR, id);
|
|
2625
|
+
}
|
|
2626
|
+
function resolveJobDir(root, id) {
|
|
2627
|
+
const modern = jobDir(root, id, false);
|
|
2628
|
+
if (existsSync8(modern)) return modern;
|
|
2629
|
+
const legacy = jobDir(root, id, true);
|
|
2630
|
+
if (existsSync8(legacy)) return legacy;
|
|
2631
|
+
return modern;
|
|
2632
|
+
}
|
|
2633
|
+
function listJobIdsIn(root, rel) {
|
|
2634
|
+
const dir = join10(root, rel);
|
|
2635
|
+
if (!existsSync8(dir)) return [];
|
|
2636
|
+
return readdirSync3(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && JOB_ID_RE.test(e.name)).map((e) => e.name);
|
|
2637
|
+
}
|
|
2638
|
+
function listRunningDetachedJobs(worktreePath) {
|
|
2639
|
+
const root = worktreePath.trim();
|
|
2640
|
+
if (!root) return [];
|
|
2641
|
+
const ids = /* @__PURE__ */ new Set([
|
|
2642
|
+
...listJobIdsIn(root, DETACHED_JOBS_DIR),
|
|
2643
|
+
...listJobIdsIn(root, LEGACY_DETACHED_JOBS_DIR)
|
|
2644
|
+
]);
|
|
2645
|
+
const running = [];
|
|
2646
|
+
for (const id of ids) {
|
|
2647
|
+
const dir = resolveJobDir(root, id);
|
|
2648
|
+
const pid = readIntFile(join10(dir, "pid"));
|
|
2649
|
+
if (pid != null && jobAlive(pid)) running.push(id);
|
|
2650
|
+
}
|
|
2651
|
+
return running.sort();
|
|
2652
|
+
}
|
|
2653
|
+
function looksLikeDeferredDonePromise(text) {
|
|
2654
|
+
const t = (text ?? "").trim();
|
|
2655
|
+
if (!t) return false;
|
|
2656
|
+
return /\b(i['’]?ll|i will)\s+let you know\b/i.test(t) || /\blet you know when\b/i.test(t) || /\b(check back|ping me)\s+when\b/i.test(t) || /\bi(?:['’]ll| will)\s+(report|update you)\s+when\b/i.test(t);
|
|
2657
|
+
}
|
|
2658
|
+
function formatJobStillRunningContinuePrompt(jobIds) {
|
|
2659
|
+
const ids = jobIds.join(", ");
|
|
2660
|
+
return [
|
|
2661
|
+
`Detached job still running: ${ids}.`,
|
|
2662
|
+
"Do not end this turn. Loop wait_for_job (same id) and present_artifact type=log with content=delta until stillRunning is false.",
|
|
2663
|
+
"Then report the result. Do not tell the user you will let them know later."
|
|
2664
|
+
].join(" ");
|
|
2665
|
+
}
|
|
2666
|
+
function formatDeferredDoneContinuePrompt() {
|
|
2667
|
+
return [
|
|
2668
|
+
"You ended the turn after promising to report later, but no detached job is running.",
|
|
2669
|
+
"If tests/pack/deploy still need to run: start once with detached-job.js, present_artifact type=log, then loop wait_for_job until stillRunning is false.",
|
|
2670
|
+
"Do not say you will let the user know later."
|
|
2671
|
+
].join(" ");
|
|
2672
|
+
}
|
|
2673
|
+
function turnWatchedDetachedJob(parts) {
|
|
2674
|
+
return parts.some((p) => {
|
|
2675
|
+
if (p.type !== "tool") return false;
|
|
2676
|
+
if (/wait_for_job$/i.test(p.name ?? "")) return true;
|
|
2677
|
+
const blob = [p.name, p.detail, p.description, p.input ? JSON.stringify(p.input) : ""].filter(Boolean).join(" ");
|
|
2678
|
+
return /detached-job\.js\b/i.test(blob);
|
|
2679
|
+
});
|
|
2680
|
+
}
|
|
2681
|
+
function planJobContinue(opts) {
|
|
2682
|
+
if (opts.isOrchestrator) return { action: "none" };
|
|
2683
|
+
if (opts.agent === "brightsy") return { action: "none" };
|
|
2684
|
+
if (opts.queueLength > 0) return { action: "none" };
|
|
2685
|
+
if (opts.continueCount >= MAX_JOB_CONTINUES) return { action: "none" };
|
|
2686
|
+
const farewell = looksLikeDeferredDonePromise(opts.chatText);
|
|
2687
|
+
if (opts.runningJobIds.length > 0 && (farewell || opts.watchedJob)) {
|
|
2688
|
+
return {
|
|
2689
|
+
action: "wait",
|
|
2690
|
+
jobIds: opts.runningJobIds,
|
|
2691
|
+
prompt: formatJobStillRunningContinuePrompt(opts.runningJobIds)
|
|
2692
|
+
};
|
|
2693
|
+
}
|
|
2694
|
+
if (looksLikeDeferredDonePromise(opts.chatText) && !opts.alreadyNudged) {
|
|
2695
|
+
return { action: "nudge", prompt: formatDeferredDoneContinuePrompt() };
|
|
2696
|
+
}
|
|
2697
|
+
return { action: "none" };
|
|
2698
|
+
}
|
|
2699
|
+
function tailProgress(logFile, maxLines = 12) {
|
|
2700
|
+
if (!existsSync8(logFile)) return "(no log yet)";
|
|
2701
|
+
const lines = readFileSync7(logFile, "utf8").split("\n");
|
|
2702
|
+
return lines.slice(-maxLines).join("\n");
|
|
2703
|
+
}
|
|
2704
|
+
function readLogLines(file) {
|
|
2705
|
+
if (!existsSync8(file)) return [];
|
|
2706
|
+
const lines = readFileSync7(file, "utf8").split("\n");
|
|
2707
|
+
if (lines.length && lines[lines.length - 1] === "") lines.pop();
|
|
2708
|
+
return lines;
|
|
2709
|
+
}
|
|
2710
|
+
function takeDelta(logFile, cursorFile) {
|
|
2711
|
+
const lines = readLogLines(logFile);
|
|
2712
|
+
const cursor = readIntFile(cursorFile) ?? 0;
|
|
2713
|
+
const start = Math.min(Math.max(0, cursor), lines.length);
|
|
2714
|
+
return { delta: lines.slice(start).join("\n"), nextCursor: lines.length };
|
|
2715
|
+
}
|
|
2716
|
+
function snapshotJob(dir) {
|
|
2717
|
+
const pid = readIntFile(join10(dir, "pid"));
|
|
2718
|
+
const running = pid != null && jobAlive(pid);
|
|
2719
|
+
return {
|
|
2720
|
+
pid,
|
|
2721
|
+
running,
|
|
2722
|
+
exitCode: readIntFile(join10(dir, "exit")),
|
|
2723
|
+
log: join10(dir, "log"),
|
|
2724
|
+
cursor: join10(dir, "present.cursor"),
|
|
2725
|
+
progress: tailProgress(join10(dir, "log"))
|
|
2726
|
+
};
|
|
2727
|
+
}
|
|
2728
|
+
function toResult(id, snap, extra) {
|
|
2729
|
+
const failed = extra?.failed === true || !snap.running && snap.exitCode != null && snap.exitCode !== 0;
|
|
2730
|
+
const ok = !snap.running && snap.exitCode === 0;
|
|
2731
|
+
const stillRunning = snap.running && !ok;
|
|
2732
|
+
const { delta, nextCursor } = takeDelta(snap.log, snap.cursor);
|
|
2733
|
+
try {
|
|
2734
|
+
mkdirSync3(dirname3(snap.cursor), { recursive: true });
|
|
2735
|
+
writeFileSync4(snap.cursor, `${nextCursor}
|
|
2736
|
+
`);
|
|
2737
|
+
} catch {
|
|
2738
|
+
}
|
|
2739
|
+
const status = ok ? "ok" : failed && !stillRunning ? "failed" : stillRunning ? "running" : "idle";
|
|
2740
|
+
return {
|
|
2741
|
+
stillRunning,
|
|
2742
|
+
ok,
|
|
2743
|
+
failed: Boolean(failed && !stillRunning && !ok),
|
|
2744
|
+
status,
|
|
2745
|
+
id,
|
|
2746
|
+
pid: snap.pid,
|
|
2747
|
+
exitCode: snap.exitCode ?? void 0,
|
|
2748
|
+
delta,
|
|
2749
|
+
progress: extra?.progress ?? snap.progress,
|
|
2750
|
+
hint: stillRunning ? MCP_WAIT_JOB_STILL_RUNNING_HINT : void 0
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2753
|
+
async function sleepMs(ms) {
|
|
2754
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
2755
|
+
}
|
|
2756
|
+
async function waitForDetachedJob(cwd, id, opts) {
|
|
2757
|
+
const jobId = sanitizeDetachedJobId(id);
|
|
2758
|
+
const root = cwd.trim() || process.cwd();
|
|
2759
|
+
const dir = resolveJobDir(root, jobId);
|
|
2760
|
+
const timeoutMs = mcpWaitForJobTimeoutMs(opts?.timeoutMs);
|
|
2761
|
+
const sleep = opts?.sleep ?? sleepMs;
|
|
2762
|
+
if (!existsSync8(dir)) {
|
|
2763
|
+
return {
|
|
2764
|
+
stillRunning: false,
|
|
2765
|
+
ok: false,
|
|
2766
|
+
failed: true,
|
|
2767
|
+
status: "failed",
|
|
2768
|
+
id: jobId,
|
|
2769
|
+
delta: "",
|
|
2770
|
+
progress: "No detached job. Start one first.",
|
|
2771
|
+
hint: "Start with detached-job.js start <id> -- <command>, then call wait_for_job again."
|
|
2772
|
+
};
|
|
2773
|
+
}
|
|
2774
|
+
const deadline = Date.now() + timeoutMs;
|
|
2775
|
+
let snap = snapshotJob(dir);
|
|
2776
|
+
if (snap.exitCode === 0 && !snap.running) return toResult(jobId, snap);
|
|
2777
|
+
if (!snap.running && snap.pid == null && snap.progress === "(no log yet)") {
|
|
2778
|
+
return toResult(jobId, snap, {
|
|
2779
|
+
failed: true,
|
|
2780
|
+
progress: "No detached job. Start one first."
|
|
2781
|
+
});
|
|
2782
|
+
}
|
|
2783
|
+
while (Date.now() < deadline) {
|
|
2784
|
+
snap = snapshotJob(dir);
|
|
2785
|
+
if (snap.exitCode === 0 && !snap.running) return toResult(jobId, snap);
|
|
2786
|
+
if (!snap.running) return toResult(jobId, snap, { failed: true });
|
|
2787
|
+
await sleep(Math.min(2e3, Math.max(50, deadline - Date.now())));
|
|
2788
|
+
}
|
|
2789
|
+
snap = snapshotJob(dir);
|
|
2790
|
+
return toResult(jobId, snap);
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2565
2793
|
// src/threads/create.ts
|
|
2566
|
-
import { existsSync as
|
|
2794
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2567
2795
|
|
|
2568
2796
|
// src/detect/detect.ts
|
|
2569
2797
|
async function detectAgents() {
|
|
@@ -3125,7 +3353,7 @@ function reuseLiveThread(input, repoPath, match) {
|
|
|
3125
3353
|
}
|
|
3126
3354
|
async function createThread(input, _onSetupLine) {
|
|
3127
3355
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
3128
|
-
if (!
|
|
3356
|
+
if (!existsSync9(repoPath)) {
|
|
3129
3357
|
throw new Error(`Repo not found: ${repoPath}`);
|
|
3130
3358
|
}
|
|
3131
3359
|
const reused = reuseLiveThread(input, repoPath, {
|
|
@@ -3200,7 +3428,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
3200
3428
|
let sourceIsFork = false;
|
|
3201
3429
|
if (!input.cowboy && sourceType === "branch" && (!sourceRef || sourceRef === "HEAD" || sourceRef === "default")) {
|
|
3202
3430
|
if (isAbleTimeConnected() && getIssueSource() === "abletime") {
|
|
3203
|
-
const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-
|
|
3431
|
+
const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-GWZE6OYM.js");
|
|
3204
3432
|
const task = await ensureAbleTimeTask({
|
|
3205
3433
|
title: title?.trim() || "Untitled work",
|
|
3206
3434
|
description: input.prompt?.trim() || void 0
|
|
@@ -3297,7 +3525,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
3297
3525
|
return readThread(thread.id) ?? thread;
|
|
3298
3526
|
}
|
|
3299
3527
|
async function listLinearIssues(agent, repoPath) {
|
|
3300
|
-
const { getAdapter: getAdapter2 } = await import("./agents-
|
|
3528
|
+
const { getAdapter: getAdapter2 } = await import("./agents-E3ZPWZRS.js");
|
|
3301
3529
|
await requireAgent(agent, { requireLinear: true });
|
|
3302
3530
|
const adapter = getAdapter2(agent);
|
|
3303
3531
|
if (!adapter.listLinearIssues) {
|
|
@@ -3320,11 +3548,11 @@ function shouldRemoveWorktreeOnTeardown(thread) {
|
|
|
3320
3548
|
|
|
3321
3549
|
// src/store/turn-live.ts
|
|
3322
3550
|
import {
|
|
3323
|
-
existsSync as
|
|
3551
|
+
existsSync as existsSync10,
|
|
3324
3552
|
renameSync,
|
|
3325
3553
|
unlinkSync as unlinkSync2,
|
|
3326
|
-
writeFileSync as
|
|
3327
|
-
readFileSync as
|
|
3554
|
+
writeFileSync as writeFileSync5,
|
|
3555
|
+
readFileSync as readFileSync8
|
|
3328
3556
|
} from "fs";
|
|
3329
3557
|
var buffers = /* @__PURE__ */ new Map();
|
|
3330
3558
|
var FLUSH_MS = 800;
|
|
@@ -3401,7 +3629,7 @@ function writeTurnLive(threadId, progress) {
|
|
|
3401
3629
|
const path = threadLivePath(threadId);
|
|
3402
3630
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
3403
3631
|
try {
|
|
3404
|
-
|
|
3632
|
+
writeFileSync5(tmp, JSON.stringify(progress), "utf8");
|
|
3405
3633
|
renameSync(tmp, path);
|
|
3406
3634
|
} catch {
|
|
3407
3635
|
try {
|
|
@@ -3412,9 +3640,9 @@ function writeTurnLive(threadId, progress) {
|
|
|
3412
3640
|
}
|
|
3413
3641
|
function readTurnLive(threadId) {
|
|
3414
3642
|
const path = threadLivePath(threadId);
|
|
3415
|
-
if (!
|
|
3643
|
+
if (!existsSync10(path)) return null;
|
|
3416
3644
|
try {
|
|
3417
|
-
const raw = JSON.parse(
|
|
3645
|
+
const raw = JSON.parse(readFileSync8(path, "utf8"));
|
|
3418
3646
|
if (!raw || typeof raw.summary !== "string") return null;
|
|
3419
3647
|
return raw;
|
|
3420
3648
|
} catch {
|
|
@@ -3426,7 +3654,7 @@ function clearTurnLive(threadId) {
|
|
|
3426
3654
|
if (buf?.timer) clearTimeout(buf.timer);
|
|
3427
3655
|
buffers.delete(threadId);
|
|
3428
3656
|
const path = threadLivePath(threadId);
|
|
3429
|
-
if (!
|
|
3657
|
+
if (!existsSync10(path)) return;
|
|
3430
3658
|
try {
|
|
3431
3659
|
unlinkSync2(path);
|
|
3432
3660
|
} catch {
|
|
@@ -3614,26 +3842,26 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
|
3614
3842
|
import { execFileSync } from "child_process";
|
|
3615
3843
|
import {
|
|
3616
3844
|
copyFileSync as copyFileSync2,
|
|
3617
|
-
existsSync as
|
|
3845
|
+
existsSync as existsSync11,
|
|
3618
3846
|
mkdtempSync,
|
|
3619
|
-
readdirSync as
|
|
3620
|
-
readFileSync as
|
|
3847
|
+
readdirSync as readdirSync4,
|
|
3848
|
+
readFileSync as readFileSync9,
|
|
3621
3849
|
rmSync
|
|
3622
3850
|
} from "fs";
|
|
3623
3851
|
import { tmpdir } from "os";
|
|
3624
|
-
import { join as
|
|
3852
|
+
import { join as join11 } from "path";
|
|
3625
3853
|
import { createRequire } from "module";
|
|
3626
|
-
var CONDUCTOR_APP_SUPPORT =
|
|
3854
|
+
var CONDUCTOR_APP_SUPPORT = join11(
|
|
3627
3855
|
process.env.HOME ?? "",
|
|
3628
3856
|
"Library",
|
|
3629
3857
|
"Application Support",
|
|
3630
3858
|
"com.conductor.app"
|
|
3631
3859
|
);
|
|
3632
|
-
var CONDUCTOR_DB =
|
|
3633
|
-
var CURSOR_SDK_STORE =
|
|
3860
|
+
var CONDUCTOR_DB = join11(CONDUCTOR_APP_SUPPORT, "conductor.db");
|
|
3861
|
+
var CURSOR_SDK_STORE = join11(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
|
|
3634
3862
|
function thisModuleFile() {
|
|
3635
3863
|
const cjsFile = typeof __filename !== "undefined" ? __filename : "";
|
|
3636
|
-
return cjsFile || process.argv[1] ||
|
|
3864
|
+
return cjsFile || process.argv[1] || join11(process.cwd(), "package.json");
|
|
3637
3865
|
}
|
|
3638
3866
|
function openReadonlySqlite(file) {
|
|
3639
3867
|
const req = createRequire(thisModuleFile());
|
|
@@ -3651,21 +3879,21 @@ function mapAgentType(raw) {
|
|
|
3651
3879
|
return null;
|
|
3652
3880
|
}
|
|
3653
3881
|
function resolveConductorCursorAgentId(workspacePath) {
|
|
3654
|
-
if (!workspacePath || !
|
|
3882
|
+
if (!workspacePath || !existsSync11(CURSOR_SDK_STORE)) return null;
|
|
3655
3883
|
const normalized = workspacePath.replace(/\/$/, "");
|
|
3656
3884
|
let best = null;
|
|
3657
3885
|
let hashes;
|
|
3658
3886
|
try {
|
|
3659
|
-
hashes =
|
|
3887
|
+
hashes = readdirSync4(CURSOR_SDK_STORE);
|
|
3660
3888
|
} catch {
|
|
3661
3889
|
return null;
|
|
3662
3890
|
}
|
|
3663
3891
|
for (const hash of hashes) {
|
|
3664
|
-
const agentsFile =
|
|
3665
|
-
if (!
|
|
3892
|
+
const agentsFile = join11(CURSOR_SDK_STORE, hash, "agents.ndjson");
|
|
3893
|
+
if (!existsSync11(agentsFile)) continue;
|
|
3666
3894
|
let text;
|
|
3667
3895
|
try {
|
|
3668
|
-
text =
|
|
3896
|
+
text = readFileSync9(agentsFile, "utf8");
|
|
3669
3897
|
} catch {
|
|
3670
3898
|
continue;
|
|
3671
3899
|
}
|
|
@@ -3689,7 +3917,7 @@ function resolveConductorCursorAgentId(workspacePath) {
|
|
|
3689
3917
|
return best?.agentId ?? null;
|
|
3690
3918
|
}
|
|
3691
3919
|
async function adoptThread(input) {
|
|
3692
|
-
if (!
|
|
3920
|
+
if (!existsSync11(input.worktreePath)) {
|
|
3693
3921
|
throw new Error(`Worktree not found: ${input.worktreePath}`);
|
|
3694
3922
|
}
|
|
3695
3923
|
const repoPath = await resolveRepoRoot(input.worktreePath);
|
|
@@ -3708,7 +3936,7 @@ async function adoptThread(input) {
|
|
|
3708
3936
|
messages: input.messages ?? []
|
|
3709
3937
|
});
|
|
3710
3938
|
writeThread(thread);
|
|
3711
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-
|
|
3939
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-NYSM2EPX.js");
|
|
3712
3940
|
await ensureWorkspace2(repoPath);
|
|
3713
3941
|
const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-X54WSOXM.js");
|
|
3714
3942
|
await ensureWorktreeSideboardIgnored2(input.worktreePath);
|
|
@@ -3718,16 +3946,16 @@ function conductorDbPath() {
|
|
|
3718
3946
|
return CONDUCTOR_DB;
|
|
3719
3947
|
}
|
|
3720
3948
|
function listConductorWorkspaces() {
|
|
3721
|
-
if (!
|
|
3949
|
+
if (!existsSync11(CONDUCTOR_DB)) {
|
|
3722
3950
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
3723
3951
|
}
|
|
3724
|
-
const tmp = mkdtempSync(
|
|
3725
|
-
const snapshot =
|
|
3952
|
+
const tmp = mkdtempSync(join11(tmpdir(), "sideboard-conductor-"));
|
|
3953
|
+
const snapshot = join11(tmp, "conductor.db");
|
|
3726
3954
|
try {
|
|
3727
3955
|
copyFileSync2(CONDUCTOR_DB, snapshot);
|
|
3728
3956
|
for (const suffix of ["-wal", "-shm"]) {
|
|
3729
3957
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
3730
|
-
if (
|
|
3958
|
+
if (existsSync11(src)) {
|
|
3731
3959
|
try {
|
|
3732
3960
|
copyFileSync2(src, `${snapshot}${suffix}`);
|
|
3733
3961
|
} catch {
|
|
@@ -3809,16 +4037,16 @@ function listConductorWorkspaces() {
|
|
|
3809
4037
|
}
|
|
3810
4038
|
}
|
|
3811
4039
|
function importConductorWorkspace(workspaceId) {
|
|
3812
|
-
if (!
|
|
4040
|
+
if (!existsSync11(CONDUCTOR_DB)) {
|
|
3813
4041
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
3814
4042
|
}
|
|
3815
|
-
const tmp = mkdtempSync(
|
|
3816
|
-
const snapshot =
|
|
4043
|
+
const tmp = mkdtempSync(join11(tmpdir(), "sideboard-conductor-"));
|
|
4044
|
+
const snapshot = join11(tmp, "conductor.db");
|
|
3817
4045
|
try {
|
|
3818
4046
|
copyFileSync2(CONDUCTOR_DB, snapshot);
|
|
3819
4047
|
for (const suffix of ["-wal", "-shm"]) {
|
|
3820
4048
|
const src = `${CONDUCTOR_DB}${suffix}`;
|
|
3821
|
-
if (
|
|
4049
|
+
if (existsSync11(src)) {
|
|
3822
4050
|
try {
|
|
3823
4051
|
copyFileSync2(src, `${snapshot}${suffix}`);
|
|
3824
4052
|
} catch {
|
|
@@ -3838,7 +4066,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
3838
4066
|
).get(workspaceId);
|
|
3839
4067
|
if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
|
|
3840
4068
|
const worktreePath = String(row.workspacePath);
|
|
3841
|
-
if (!
|
|
4069
|
+
if (!existsSync11(worktreePath)) {
|
|
3842
4070
|
throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
|
|
3843
4071
|
}
|
|
3844
4072
|
let sessionId = null;
|
|
@@ -3909,7 +4137,7 @@ async function importConductorWorkspaceAsync(workspaceId) {
|
|
|
3909
4137
|
}
|
|
3910
4138
|
|
|
3911
4139
|
// src/threads/stack-layers.ts
|
|
3912
|
-
import { existsSync as
|
|
4140
|
+
import { existsSync as existsSync12 } from "fs";
|
|
3913
4141
|
function stackIdFrom(stack) {
|
|
3914
4142
|
if (stack.stackNumber != null) return `gh-stack-${stack.stackNumber}`;
|
|
3915
4143
|
const key = stack.layers.map((l) => l.branchName).join("|");
|
|
@@ -3972,7 +4200,7 @@ async function openStackLayer(input, _onSetupLine) {
|
|
|
3972
4200
|
let createdWorktree = false;
|
|
3973
4201
|
const trees = await listWorktrees(repoPath);
|
|
3974
4202
|
const checkedOut = trees.find((w) => w.branch === branchName);
|
|
3975
|
-
if (checkedOut?.path &&
|
|
4203
|
+
if (checkedOut?.path && existsSync12(checkedOut.path)) {
|
|
3976
4204
|
if (input.reuseExistingWorktree !== false) {
|
|
3977
4205
|
worktreePath = checkedOut.path;
|
|
3978
4206
|
} else {
|
|
@@ -4114,7 +4342,7 @@ async function initStackFromThread(input, onSetupLine) {
|
|
|
4114
4342
|
async function createPrStack(input, onSetupLine) {
|
|
4115
4343
|
await requireAgent(input.agent);
|
|
4116
4344
|
const repoPath = await resolveRepoRoot(input.repoPath);
|
|
4117
|
-
if (!
|
|
4345
|
+
if (!existsSync12(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
|
|
4118
4346
|
if (!input.branches.length) throw new Error("At least one branch name required");
|
|
4119
4347
|
const status = await detectGhStack(repoPath);
|
|
4120
4348
|
if (!status.available) throw new Error(status.reason);
|
|
@@ -4181,7 +4409,7 @@ async function createPrStack(input, onSetupLine) {
|
|
|
4181
4409
|
}
|
|
4182
4410
|
}
|
|
4183
4411
|
const claimed = new Set(threads.map((t) => t.worktreePath));
|
|
4184
|
-
if (!claimed.has(bootstrap.worktreePath) &&
|
|
4412
|
+
if (!claimed.has(bootstrap.worktreePath) && existsSync12(bootstrap.worktreePath)) {
|
|
4185
4413
|
try {
|
|
4186
4414
|
await removeWorktree(repoPath, bootstrap.worktreePath, {
|
|
4187
4415
|
deleteBranch: bootstrap.branchName
|
|
@@ -4203,10 +4431,10 @@ function stackAgentDefaultsFrom(input) {
|
|
|
4203
4431
|
}
|
|
4204
4432
|
|
|
4205
4433
|
// src/diff/diff.ts
|
|
4206
|
-
import { existsSync as
|
|
4207
|
-
import { dirname as
|
|
4434
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync10, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
4435
|
+
import { dirname as dirname4, join as join12 } from "path";
|
|
4208
4436
|
async function inspectGitWorktree(worktreePath) {
|
|
4209
|
-
if (!worktreePath || !
|
|
4437
|
+
if (!worktreePath || !existsSync13(worktreePath)) return "missing_worktree";
|
|
4210
4438
|
const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
|
|
4211
4439
|
reject: false
|
|
4212
4440
|
});
|
|
@@ -4214,7 +4442,7 @@ async function inspectGitWorktree(worktreePath) {
|
|
|
4214
4442
|
return "ok";
|
|
4215
4443
|
}
|
|
4216
4444
|
async function initializeGitRepository(worktreePath) {
|
|
4217
|
-
if (!worktreePath || !
|
|
4445
|
+
if (!worktreePath || !existsSync13(worktreePath)) {
|
|
4218
4446
|
throw new Error("Worktree not found");
|
|
4219
4447
|
}
|
|
4220
4448
|
const status = await inspectGitWorktree(worktreePath);
|
|
@@ -4350,11 +4578,11 @@ new file mode 100644
|
|
|
4350
4578
|
};
|
|
4351
4579
|
}
|
|
4352
4580
|
async function untrackedPatch(worktreePath, path, maxHunk) {
|
|
4353
|
-
const abs =
|
|
4581
|
+
const abs = join12(worktreePath, path);
|
|
4354
4582
|
try {
|
|
4355
4583
|
const st = statSync2(abs);
|
|
4356
4584
|
if (st.isFile() && st.size > maxHunk) {
|
|
4357
|
-
const buf =
|
|
4585
|
+
const buf = readFileSync10(abs).subarray(0, maxHunk);
|
|
4358
4586
|
return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
|
|
4359
4587
|
}
|
|
4360
4588
|
} catch {
|
|
@@ -4855,7 +5083,7 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
|
|
|
4855
5083
|
function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
4856
5084
|
assertSafeRelativePath(relativePath);
|
|
4857
5085
|
const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
|
|
4858
|
-
const abs =
|
|
5086
|
+
const abs = join12(worktreePath, relativePath);
|
|
4859
5087
|
const st = statSync2(abs);
|
|
4860
5088
|
if (!st.isFile()) {
|
|
4861
5089
|
throw new Error(`Not a file: ${relativePath}`);
|
|
@@ -4865,7 +5093,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
4865
5093
|
`File too large to upload (${st.size} bytes; max ${maxBytes})`
|
|
4866
5094
|
);
|
|
4867
5095
|
}
|
|
4868
|
-
const buf =
|
|
5096
|
+
const buf = readFileSync10(abs);
|
|
4869
5097
|
return {
|
|
4870
5098
|
path: relativePath,
|
|
4871
5099
|
contentBase64: buf.toString("base64"),
|
|
@@ -4875,12 +5103,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
4875
5103
|
function readWorktreeFile(worktreePath, relativePath, opts) {
|
|
4876
5104
|
assertSafeRelativePath(relativePath);
|
|
4877
5105
|
const maxBytes = opts?.maxBytes ?? 2e5;
|
|
4878
|
-
const abs =
|
|
5106
|
+
const abs = join12(worktreePath, relativePath);
|
|
4879
5107
|
const st = statSync2(abs);
|
|
4880
5108
|
if (!st.isFile()) {
|
|
4881
5109
|
throw new Error(`Not a file: ${relativePath}`);
|
|
4882
5110
|
}
|
|
4883
|
-
const buf =
|
|
5111
|
+
const buf = readFileSync10(abs);
|
|
4884
5112
|
if (isImageRelativePath(relativePath)) {
|
|
4885
5113
|
const maxImageBytes = Math.max(maxBytes, 15e6);
|
|
4886
5114
|
const truncated2 = buf.length > maxImageBytes;
|
|
@@ -4923,9 +5151,9 @@ function assertSafeRelativePath(relativePath) {
|
|
|
4923
5151
|
}
|
|
4924
5152
|
function writeWorktreeFile(worktreePath, relativePath, content) {
|
|
4925
5153
|
assertSafeRelativePath(relativePath);
|
|
4926
|
-
const abs =
|
|
4927
|
-
|
|
4928
|
-
|
|
5154
|
+
const abs = join12(worktreePath, relativePath);
|
|
5155
|
+
mkdirSync4(dirname4(abs), { recursive: true });
|
|
5156
|
+
writeFileSync6(abs, content, "utf8");
|
|
4929
5157
|
return { path: relativePath };
|
|
4930
5158
|
}
|
|
4931
5159
|
async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
@@ -5050,9 +5278,9 @@ async function confirmLand(thread, opts) {
|
|
|
5050
5278
|
}
|
|
5051
5279
|
|
|
5052
5280
|
// src/skills/discover.ts
|
|
5053
|
-
import { existsSync as
|
|
5281
|
+
import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync3 } from "fs";
|
|
5054
5282
|
import { homedir } from "os";
|
|
5055
|
-
import { join as
|
|
5283
|
+
import { join as join13 } from "path";
|
|
5056
5284
|
|
|
5057
5285
|
// src/skills/bundled/long-running.ts
|
|
5058
5286
|
var LONG_RUNNING_SKILL_COMMAND = "long-running";
|
|
@@ -5063,7 +5291,7 @@ var LONG_RUNNING_SKILL_BODY = `# Long-running jobs
|
|
|
5063
5291
|
|
|
5064
5292
|
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.
|
|
5065
5293
|
|
|
5066
|
-
Do **not** ask the human to check back. Detach, then **wait** in 45s slices (same idea as
|
|
5294
|
+
Do **not** ask the human to check back. Detach, then **wait** in 45s slices (MCP \`wait_for_job\`, same idea as \`wait_for_turn\`) until \`stillRunning\` is false. Do not end the turn with \u201CI\u2019ll let you know.\u201D
|
|
5067
5295
|
|
|
5068
5296
|
## Tool
|
|
5069
5297
|
|
|
@@ -5073,7 +5301,8 @@ Use the helper from the Sideboard playbook \u2014 an absolute \`node "\u2026" st
|
|
|
5073
5301
|
# Start (exits in ~1s; job survives this turn)
|
|
5074
5302
|
node <detached-job.js> start <id> -- <command> [args...]
|
|
5075
5303
|
|
|
5076
|
-
# Wait \u2014
|
|
5304
|
+
# Wait \u2014 prefer MCP wait_for_job (same 45s / stillRunning contract).
|
|
5305
|
+
# Shell fallback:
|
|
5077
5306
|
node <detached-job.js> wait <id>
|
|
5078
5307
|
|
|
5079
5308
|
# Block until the process exits (humans / a turn that will not be interrupted)
|
|
@@ -5086,7 +5315,7 @@ node <detached-job.js> status <id>
|
|
|
5086
5315
|
|
|
5087
5316
|
Wait JSON:
|
|
5088
5317
|
|
|
5089
|
-
- \`stillRunning: true\` \u2192
|
|
5318
|
+
- \`stillRunning: true\` \u2192 **call wait_for_job again** (or shell wait). Progress is in \`progress\` / \`phase\`. Do not start a second job. Do not ping the user.
|
|
5090
5319
|
- \`ok: true\` \u2192 exit 0 \u2192 continue the rest of the task.
|
|
5091
5320
|
- \`failed: true\` \u2192 exit 1 \u2192 read \`progress\`, fix, start **once**.
|
|
5092
5321
|
|
|
@@ -5116,7 +5345,7 @@ node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
|
|
|
5116
5345
|
|
|
5117
5346
|
1. \`start\` once. If JSON says \`already-running\`, do not start again.
|
|
5118
5347
|
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.
|
|
5119
|
-
3. Loop \`
|
|
5348
|
+
3. Loop \`wait_for_job\` (or shell \`wait\`). After each slice, \`present_artifact\` the same id with \`content=delta\` only.
|
|
5120
5349
|
4. On \`ok\`, present once more (\`status=ok\`, last \`delta\`) and finish the task. On \`failed\`, fix from the log.
|
|
5121
5350
|
|
|
5122
5351
|
Never tell the user \u201Csay status when it\u2019s done.\u201D You wait.
|
|
@@ -5181,7 +5410,7 @@ function parseFrontmatter(content) {
|
|
|
5181
5410
|
}
|
|
5182
5411
|
function readSkill(skillMd, source) {
|
|
5183
5412
|
try {
|
|
5184
|
-
const content =
|
|
5413
|
+
const content = readFileSync11(skillMd, "utf8");
|
|
5185
5414
|
const { name: fmName, description } = parseFrontmatter(content);
|
|
5186
5415
|
const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
|
|
5187
5416
|
const name = fmName || dirName;
|
|
@@ -5200,17 +5429,17 @@ function readSkill(skillMd, source) {
|
|
|
5200
5429
|
}
|
|
5201
5430
|
}
|
|
5202
5431
|
function scanSkillsDir(dir, source, out) {
|
|
5203
|
-
if (!
|
|
5432
|
+
if (!existsSync14(dir)) return;
|
|
5204
5433
|
let entries;
|
|
5205
5434
|
try {
|
|
5206
|
-
entries =
|
|
5435
|
+
entries = readdirSync5(dir);
|
|
5207
5436
|
} catch {
|
|
5208
5437
|
return;
|
|
5209
5438
|
}
|
|
5210
5439
|
for (const entry of entries) {
|
|
5211
5440
|
if (entry.startsWith(".")) continue;
|
|
5212
|
-
const skillMd =
|
|
5213
|
-
if (!
|
|
5441
|
+
const skillMd = join13(dir, entry, "SKILL.md");
|
|
5442
|
+
if (!existsSync14(skillMd)) continue;
|
|
5214
5443
|
try {
|
|
5215
5444
|
if (!statSync3(skillMd).isFile()) continue;
|
|
5216
5445
|
} catch {
|
|
@@ -5221,22 +5450,22 @@ function scanSkillsDir(dir, source, out) {
|
|
|
5221
5450
|
}
|
|
5222
5451
|
}
|
|
5223
5452
|
function scanClaudePluginSkills(pluginsRoot, out) {
|
|
5224
|
-
if (!
|
|
5453
|
+
if (!existsSync14(pluginsRoot)) return;
|
|
5225
5454
|
const walk = (dir, depth, lookingForSkillsDir) => {
|
|
5226
5455
|
if (depth > 7) return;
|
|
5227
5456
|
let entries;
|
|
5228
5457
|
try {
|
|
5229
|
-
entries =
|
|
5458
|
+
entries = readdirSync5(dir);
|
|
5230
5459
|
} catch {
|
|
5231
5460
|
return;
|
|
5232
5461
|
}
|
|
5233
5462
|
if (lookingForSkillsDir && entries.includes("SKILL.md")) {
|
|
5234
|
-
const skill = readSkill(
|
|
5463
|
+
const skill = readSkill(join13(dir, "SKILL.md"), "cli");
|
|
5235
5464
|
if (skill) out.push(skill);
|
|
5236
5465
|
}
|
|
5237
5466
|
for (const entry of entries) {
|
|
5238
5467
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
5239
|
-
const full =
|
|
5468
|
+
const full = join13(dir, entry);
|
|
5240
5469
|
try {
|
|
5241
5470
|
if (!statSync3(full).isDirectory()) continue;
|
|
5242
5471
|
} catch {
|
|
@@ -5256,17 +5485,17 @@ function discoverSkills(worktreePath) {
|
|
|
5256
5485
|
const home = homedir();
|
|
5257
5486
|
const collected = [];
|
|
5258
5487
|
for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
|
|
5259
|
-
scanSkillsDir(
|
|
5488
|
+
scanSkillsDir(join13(worktreePath, rel), "workspace", collected);
|
|
5260
5489
|
}
|
|
5261
5490
|
for (const abs of [
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5491
|
+
join13(home, ".claude/skills"),
|
|
5492
|
+
join13(home, ".cursor/skills"),
|
|
5493
|
+
join13(home, ".sideboard/skills"),
|
|
5494
|
+
join13(home, ".brightsy/skills")
|
|
5266
5495
|
]) {
|
|
5267
5496
|
scanSkillsDir(abs, "user", collected);
|
|
5268
5497
|
}
|
|
5269
|
-
scanClaudePluginSkills(
|
|
5498
|
+
scanClaudePluginSkills(join13(home, ".claude/plugins"), collected);
|
|
5270
5499
|
collected.push(...bundledSkills());
|
|
5271
5500
|
const rank = {
|
|
5272
5501
|
workspace: 0,
|
|
@@ -5292,7 +5521,7 @@ function readSkillBody(skillPath, maxChars = 12e3) {
|
|
|
5292
5521
|
\u2026(truncated)` : body;
|
|
5293
5522
|
}
|
|
5294
5523
|
}
|
|
5295
|
-
const raw =
|
|
5524
|
+
const raw = readFileSync11(skillPath, "utf8");
|
|
5296
5525
|
if (raw.startsWith("---")) {
|
|
5297
5526
|
const end = raw.indexOf("\n---", 3);
|
|
5298
5527
|
if (end >= 0) {
|
|
@@ -5385,27 +5614,27 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
|
|
|
5385
5614
|
}
|
|
5386
5615
|
|
|
5387
5616
|
// src/agents/instructions.ts
|
|
5388
|
-
import { existsSync as
|
|
5389
|
-
import { join as
|
|
5617
|
+
import { existsSync as existsSync16, readFileSync as readFileSync12, statSync as statSync4 } from "fs";
|
|
5618
|
+
import { join as join15 } from "path";
|
|
5390
5619
|
|
|
5391
5620
|
// src/skills/detached-job-path.ts
|
|
5392
|
-
import { existsSync as
|
|
5393
|
-
import { dirname as
|
|
5621
|
+
import { existsSync as existsSync15 } from "fs";
|
|
5622
|
+
import { dirname as dirname5, join as join14 } from "path";
|
|
5394
5623
|
import { fileURLToPath } from "url";
|
|
5395
5624
|
function packagedDetachedJobPath() {
|
|
5396
5625
|
const dir = packagedMcpDir();
|
|
5397
5626
|
if (!dir) return null;
|
|
5398
|
-
const script =
|
|
5399
|
-
return
|
|
5627
|
+
const script = join14(dir, "scripts", "detached-job.js");
|
|
5628
|
+
return existsSync15(script) ? script : null;
|
|
5400
5629
|
}
|
|
5401
5630
|
function resolveDetachedJobScript() {
|
|
5402
5631
|
const packaged = packagedDetachedJobPath();
|
|
5403
5632
|
if (packaged) return packaged;
|
|
5404
|
-
let dir =
|
|
5633
|
+
let dir = dirname5(fileURLToPath(import.meta.url));
|
|
5405
5634
|
for (let i = 0; i < 8; i++) {
|
|
5406
|
-
const candidate =
|
|
5407
|
-
if (
|
|
5408
|
-
const parent =
|
|
5635
|
+
const candidate = join14(dir, "scripts", "detached-job.js");
|
|
5636
|
+
if (existsSync15(candidate)) return candidate;
|
|
5637
|
+
const parent = dirname5(dir);
|
|
5409
5638
|
if (parent === dir) break;
|
|
5410
5639
|
dir = parent;
|
|
5411
5640
|
}
|
|
@@ -5524,6 +5753,88 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
5524
5753
|
function formatWorktreeReminder() {
|
|
5525
5754
|
return "Sideboard worktree: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push.";
|
|
5526
5755
|
}
|
|
5756
|
+
var GITHUB_TICKET_REF = /^(?:#?\d+|gh-\d+)$/i;
|
|
5757
|
+
var KEYED_TICKET_REF = /^(?:[A-Z][A-Z0-9]{0,9}-\d+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i;
|
|
5758
|
+
function issueTicketFromThread(thread, preferredSource = "github") {
|
|
5759
|
+
if (thread?.sourceType !== "ticket") return null;
|
|
5760
|
+
const ref = thread.sourceRef?.trim() ?? "";
|
|
5761
|
+
if (!ref) return null;
|
|
5762
|
+
if (/github\.com\/[^/]+\/[^/]+\/issues\/\d+/i.test(ref) || GITHUB_TICKET_REF.test(ref)) {
|
|
5763
|
+
return { id: ref, provider: "github" };
|
|
5764
|
+
}
|
|
5765
|
+
if (KEYED_TICKET_REF.test(ref)) {
|
|
5766
|
+
return { id: ref, provider: preferredSource === "github" ? "linear" : preferredSource };
|
|
5767
|
+
}
|
|
5768
|
+
return { id: ref, provider: preferredSource };
|
|
5769
|
+
}
|
|
5770
|
+
function linearTicketIdFromThread(thread) {
|
|
5771
|
+
const ticket = issueTicketFromThread(thread, "linear");
|
|
5772
|
+
return ticket?.provider === "linear" ? ticket.id : null;
|
|
5773
|
+
}
|
|
5774
|
+
function formatIssueToolsDirective(opts) {
|
|
5775
|
+
const github = opts.github !== false;
|
|
5776
|
+
if (!opts.linear && !opts.abletime && !github) return null;
|
|
5777
|
+
const lines = [
|
|
5778
|
+
"Issue tracking (Settings \u2192 Issues / Git \u2014 already signed in):",
|
|
5779
|
+
"- Use Sideboard `linear_*` / `github_*` / `abletime_*` tools. Do not call Claude Linear MCP, vendor GitHub MCP, or any other vendor issue MCP \u2014 those need a separate login and hang. If a vendor namespace shows needsAuth, ignore it and keep going with Sideboard tools."
|
|
5780
|
+
];
|
|
5781
|
+
if (opts.linear) {
|
|
5782
|
+
lines.push(
|
|
5783
|
+
"- Linear: `linear_get_issue` (comments), `linear_comment`, `linear_update_issue` (state), `linear_create_issue` (pass `parent` for spin-offs; call `linear_list_teams` first). Scope errors: reconnect Linear in Settings \u2192 Issues."
|
|
5784
|
+
);
|
|
5785
|
+
}
|
|
5786
|
+
if (github) {
|
|
5787
|
+
lines.push(
|
|
5788
|
+
"- GitHub: `github_get_issue` (comments), `github_comment`, `github_update_issue` (state open|closed), `github_create_issue` (pass `parent` for spin-offs). Uses Account `gh`."
|
|
5789
|
+
);
|
|
5790
|
+
}
|
|
5791
|
+
if (opts.abletime) {
|
|
5792
|
+
lines.push(
|
|
5793
|
+
"- AbleTime: `abletime_get_task` (comments), `abletime_comment`, `abletime_update_task` (state), `abletime_create_task` (pass `parent` for spin-offs; `abletime_list_projects` if needed)."
|
|
5794
|
+
);
|
|
5795
|
+
}
|
|
5796
|
+
lines.push(
|
|
5797
|
+
"- Do not ask the user to `claude mcp login` for tickets. Reconnect the Account source in Settings \u2192 Issues (or Git for `gh`)."
|
|
5798
|
+
);
|
|
5799
|
+
const ticket = opts.ticketId?.trim();
|
|
5800
|
+
if (ticket) {
|
|
5801
|
+
const provider = opts.ticketProvider ?? "linear";
|
|
5802
|
+
lines.push(
|
|
5803
|
+
`- This thread's ticket is \`${ticket}\` (${provider}). Use that id for get/comment/update; pass it as \`parent\` on spin-offs.`
|
|
5804
|
+
);
|
|
5805
|
+
}
|
|
5806
|
+
return lines.join("\n");
|
|
5807
|
+
}
|
|
5808
|
+
function formatLinearDirective(opts) {
|
|
5809
|
+
return formatIssueToolsDirective({
|
|
5810
|
+
linear: opts.connected,
|
|
5811
|
+
abletime: false,
|
|
5812
|
+
github: false,
|
|
5813
|
+
ticketId: opts.ticketId,
|
|
5814
|
+
ticketProvider: "linear"
|
|
5815
|
+
});
|
|
5816
|
+
}
|
|
5817
|
+
function formatIssueToolsReminder(opts) {
|
|
5818
|
+
const github = opts.github !== false;
|
|
5819
|
+
if (!opts.linear && !opts.abletime && !github) return null;
|
|
5820
|
+
const names = [
|
|
5821
|
+
opts.linear ? "linear_*" : null,
|
|
5822
|
+
github ? "github_*" : null,
|
|
5823
|
+
opts.abletime ? "abletime_*" : null
|
|
5824
|
+
].filter(Boolean);
|
|
5825
|
+
const ticket = opts.ticketId?.trim();
|
|
5826
|
+
const ticketBit = ticket ? ` This ticket: ${ticket}${opts.ticketProvider ? ` (${opts.ticketProvider})` : ""} \u2014 get/comment/update; create with parent for spin-offs.` : " get/comment/update/create (parent= for spin-offs).";
|
|
5827
|
+
return `Issues: Sideboard ${names.join(" / ")} (Account). Ignore vendor issue MCP auth.${ticketBit}`;
|
|
5828
|
+
}
|
|
5829
|
+
function formatLinearReminder(opts) {
|
|
5830
|
+
return formatIssueToolsReminder({
|
|
5831
|
+
linear: opts.connected,
|
|
5832
|
+
abletime: false,
|
|
5833
|
+
github: false,
|
|
5834
|
+
ticketId: opts.ticketId,
|
|
5835
|
+
ticketProvider: "linear"
|
|
5836
|
+
});
|
|
5837
|
+
}
|
|
5527
5838
|
function formatPrGateDirective() {
|
|
5528
5839
|
return [
|
|
5529
5840
|
"If a goal is given (not after every push):",
|
|
@@ -5551,14 +5862,15 @@ function formatLongRunningDirective(opts) {
|
|
|
5551
5862
|
`Helper (same tool as \`scripts/detached-job.js\` when that file exists in the worktree): \`${invoke}\``,
|
|
5552
5863
|
`- Start once: \`${invoke} start <id> -- <command> [args...]\` (cwd = this worktree). If JSON says already-running, do not start again.`,
|
|
5553
5864
|
"- Immediately `present_artifact` `type=log` with `artifact_id=<id>` and `status=running` \u2014 the side column is the live view.",
|
|
5554
|
-
`- Loop
|
|
5865
|
+
`- Loop Sideboard MCP \`wait_for_job\` with the same id (returns in ~45s). stillRunning \u2192 present the same id with \`content=delta\` only \u2192 wait_for_job again. Shell fallback: \`${invoke} wait <id>\`. Do not resend the full log or HTML.`,
|
|
5866
|
+
"- Do not end the turn with \u201CI\u2019ll let you know when it\u2019s done.\u201D Stay in the loop until stillRunning is false.",
|
|
5555
5867
|
"- ok \u2192 finish the task. failed \u2192 read the log, fix, start once.",
|
|
5556
5868
|
"State: `.context/.sideboard/detached-jobs/<id>/` (local scratch). Full guide: `/long-running` (always available)."
|
|
5557
5869
|
].join("\n");
|
|
5558
5870
|
}
|
|
5559
5871
|
function formatLongRunningReminder(opts) {
|
|
5560
5872
|
const invoke = formatDetachedJobInvoke(opts?.scriptPath);
|
|
5561
|
-
return `Long jobs: \`${invoke} start <id> -- <cmd>\`, loop wait, present_artifact type=log (delta). Do not
|
|
5873
|
+
return `Long jobs: \`${invoke} start <id> -- <cmd>\`, loop wait_for_job (or detached-job wait), present_artifact type=log (delta). Do not say you will let the user know later \u2014 stay in the turn.`;
|
|
5562
5874
|
}
|
|
5563
5875
|
function formatArtifactDirective() {
|
|
5564
5876
|
return [
|
|
@@ -5609,11 +5921,11 @@ function loadAgentInstructions(worktreePath, agent) {
|
|
|
5609
5921
|
const out = [];
|
|
5610
5922
|
for (const rel of candidates) {
|
|
5611
5923
|
if (seenPaths.has(rel)) continue;
|
|
5612
|
-
const abs =
|
|
5613
|
-
if (!
|
|
5924
|
+
const abs = join15(worktreePath, rel);
|
|
5925
|
+
if (!existsSync16(abs)) continue;
|
|
5614
5926
|
try {
|
|
5615
5927
|
if (!statSync4(abs).isFile()) continue;
|
|
5616
|
-
let content =
|
|
5928
|
+
let content = readFileSync12(abs, "utf8");
|
|
5617
5929
|
if (!content.trim()) continue;
|
|
5618
5930
|
if (content.length > MAX_CHARS_PER_FILE) {
|
|
5619
5931
|
content = `${content.slice(0, MAX_CHARS_PER_FILE)}
|
|
@@ -5933,8 +6245,8 @@ async function syncThreadBranchFromGit(threadId) {
|
|
|
5933
6245
|
|
|
5934
6246
|
// src/store/schedules.ts
|
|
5935
6247
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
5936
|
-
import { existsSync as
|
|
5937
|
-
import { join as
|
|
6248
|
+
import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
|
|
6249
|
+
import { join as join16 } from "path";
|
|
5938
6250
|
import { Cron } from "croner";
|
|
5939
6251
|
var DURATION_RE = /^(\d+)(s|m|h|d)$/i;
|
|
5940
6252
|
var UNIT_MS = {
|
|
@@ -5944,7 +6256,7 @@ var UNIT_MS = {
|
|
|
5944
6256
|
d: 864e5
|
|
5945
6257
|
};
|
|
5946
6258
|
function schedulesPath() {
|
|
5947
|
-
return
|
|
6259
|
+
return join16(appDataDir(), "schedules.json");
|
|
5948
6260
|
}
|
|
5949
6261
|
function parseDurationMs(every) {
|
|
5950
6262
|
const m = every.trim().match(DURATION_RE);
|
|
@@ -6050,9 +6362,9 @@ function normalizeTask(raw) {
|
|
|
6050
6362
|
}
|
|
6051
6363
|
function readAll() {
|
|
6052
6364
|
const path = schedulesPath();
|
|
6053
|
-
if (!
|
|
6365
|
+
if (!existsSync17(path)) return [];
|
|
6054
6366
|
try {
|
|
6055
|
-
const parsed = JSON.parse(
|
|
6367
|
+
const parsed = JSON.parse(readFileSync13(path, "utf8"));
|
|
6056
6368
|
if (!Array.isArray(parsed)) return [];
|
|
6057
6369
|
return parsed.map((row) => {
|
|
6058
6370
|
try {
|
|
@@ -6178,7 +6490,7 @@ function formatScheduledPrompt(name, prompt) {
|
|
|
6178
6490
|
${prompt}`;
|
|
6179
6491
|
}
|
|
6180
6492
|
async function defaultDeps() {
|
|
6181
|
-
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-
|
|
6493
|
+
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-ZVW2AC6B.js");
|
|
6182
6494
|
const orch = getOrchestrator2();
|
|
6183
6495
|
return {
|
|
6184
6496
|
findThread: (id) => findThreadByRef(id),
|
|
@@ -6335,6 +6647,9 @@ var Orchestrator = class {
|
|
|
6335
6647
|
* finish or a user send so a later crash can recover again.
|
|
6336
6648
|
*/
|
|
6337
6649
|
crashContinued = /* @__PURE__ */ new Set();
|
|
6650
|
+
/** Auto-continues after a worktree turn ended while a detached job still runs. */
|
|
6651
|
+
jobContinueCount = /* @__PURE__ */ new Map();
|
|
6652
|
+
jobContinueNudged = /* @__PURE__ */ new Set();
|
|
6338
6653
|
maxConcurrent;
|
|
6339
6654
|
runningCount = 0;
|
|
6340
6655
|
constructor(opts) {
|
|
@@ -6418,7 +6733,7 @@ var Orchestrator = class {
|
|
|
6418
6733
|
}
|
|
6419
6734
|
continue;
|
|
6420
6735
|
}
|
|
6421
|
-
if (!
|
|
6736
|
+
if (!existsSync18(thread.worktreePath)) {
|
|
6422
6737
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
6423
6738
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
6424
6739
|
continue;
|
|
@@ -6633,6 +6948,37 @@ var Orchestrator = class {
|
|
|
6633
6948
|
this.emit({ type: "queue_changed", threadId, queue });
|
|
6634
6949
|
this.haltDrain.delete(threadId);
|
|
6635
6950
|
}
|
|
6951
|
+
/**
|
|
6952
|
+
* Worktree agent ended the turn after “I’ll let you know” (or left a
|
|
6953
|
+
* detached test/pack job running). Queue a continue so the chat does not
|
|
6954
|
+
* go idle with nobody watching the log.
|
|
6955
|
+
*/
|
|
6956
|
+
maybeEnqueueJobContinue(threadId, chatText, parts = []) {
|
|
6957
|
+
if (this.haltDrain.has(threadId)) return;
|
|
6958
|
+
const thread = readThread(threadId);
|
|
6959
|
+
if (!thread || thread.status === "archived") return;
|
|
6960
|
+
const runningJobIds = listRunningDetachedJobs(thread.worktreePath ?? "");
|
|
6961
|
+
const decision = planJobContinue({
|
|
6962
|
+
runningJobIds,
|
|
6963
|
+
chatText,
|
|
6964
|
+
queueLength: thread.queue.length,
|
|
6965
|
+
continueCount: this.jobContinueCount.get(threadId) ?? 0,
|
|
6966
|
+
alreadyNudged: this.jobContinueNudged.has(threadId),
|
|
6967
|
+
isOrchestrator: isOrchestratorThread(thread),
|
|
6968
|
+
agent: thread.agent,
|
|
6969
|
+
watchedJob: turnWatchedDetachedJob(parts)
|
|
6970
|
+
});
|
|
6971
|
+
if (decision.action === "none") {
|
|
6972
|
+
if (runningJobIds.length === 0) this.jobContinueCount.delete(threadId);
|
|
6973
|
+
return;
|
|
6974
|
+
}
|
|
6975
|
+
if (decision.action === "nudge") this.jobContinueNudged.add(threadId);
|
|
6976
|
+
else this.jobContinueCount.set(threadId, (this.jobContinueCount.get(threadId) ?? 0) + 1);
|
|
6977
|
+
const queue = [decision.prompt, ...thread.queue];
|
|
6978
|
+
updateThread(threadId, { queue });
|
|
6979
|
+
this.emit({ type: "queue_changed", threadId, queue });
|
|
6980
|
+
this.haltDrain.delete(threadId);
|
|
6981
|
+
}
|
|
6636
6982
|
getThreads(includeArchived = false) {
|
|
6637
6983
|
return listThreads({ includeArchived });
|
|
6638
6984
|
}
|
|
@@ -6732,6 +7078,8 @@ var Orchestrator = class {
|
|
|
6732
7078
|
}
|
|
6733
7079
|
const queue = [...current.queue, prompt];
|
|
6734
7080
|
this.crashContinued.delete(thread.id);
|
|
7081
|
+
this.jobContinueCount.delete(thread.id);
|
|
7082
|
+
this.jobContinueNudged.delete(thread.id);
|
|
6735
7083
|
this.haltDrain.delete(thread.id);
|
|
6736
7084
|
const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id) || current.status === "running";
|
|
6737
7085
|
shouldSteer = followUp === "steer" && (inFlight || current.queue.length > 0);
|
|
@@ -6946,6 +7294,14 @@ var Orchestrator = class {
|
|
|
6946
7294
|
const longRunningReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatLongRunningReminder() : null;
|
|
6947
7295
|
const worktreeReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatWorktreeReminder() : null;
|
|
6948
7296
|
const optionalServicesReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatOptionalServicesReminder(loadAppSettings().integrations) : null;
|
|
7297
|
+
const issueTicket = issueTicketFromThread(thread, resolveEffectiveIssueSource());
|
|
7298
|
+
const issueToolsReminder = thread.agent !== "brightsy" && !isOrchestratorThread(thread) ? formatIssueToolsReminder({
|
|
7299
|
+
linear: isLinearConnected(),
|
|
7300
|
+
abletime: isAbleTimeConnected(),
|
|
7301
|
+
github: true,
|
|
7302
|
+
ticketId: issueTicket?.id,
|
|
7303
|
+
ticketProvider: issueTicket?.provider
|
|
7304
|
+
}) : null;
|
|
6949
7305
|
const slackReplyContext = formatSlackRepliesForTurn(
|
|
6950
7306
|
pendingSlackExternalReplies(thread.messages)
|
|
6951
7307
|
);
|
|
@@ -6954,6 +7310,7 @@ var Orchestrator = class {
|
|
|
6954
7310
|
orchestrationReminder,
|
|
6955
7311
|
worktreeReminder,
|
|
6956
7312
|
optionalServicesReminder,
|
|
7313
|
+
issueToolsReminder,
|
|
6957
7314
|
artifactReminder,
|
|
6958
7315
|
longRunningReminder,
|
|
6959
7316
|
slackReplyContext,
|
|
@@ -6986,6 +7343,14 @@ var Orchestrator = class {
|
|
|
6986
7343
|
const artifactDirective = isBrightsy ? null : formatArtifactDirective();
|
|
6987
7344
|
const longRunningDirective = isBrightsy || isOrchestration ? null : formatLongRunningDirective();
|
|
6988
7345
|
const optionalServicesDirective = isBrightsy || isOrchestration ? null : formatOptionalServicesDirective(loadAppSettings().integrations);
|
|
7346
|
+
const freshIssueTicket = issueTicketFromThread(fresh, resolveEffectiveIssueSource());
|
|
7347
|
+
const issueToolsDirective = isBrightsy || isOrchestration ? null : formatIssueToolsDirective({
|
|
7348
|
+
linear: isLinearConnected(),
|
|
7349
|
+
abletime: isAbleTimeConnected(),
|
|
7350
|
+
github: true,
|
|
7351
|
+
ticketId: freshIssueTicket?.id,
|
|
7352
|
+
ticketProvider: freshIssueTicket?.provider
|
|
7353
|
+
});
|
|
6989
7354
|
const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
|
|
6990
7355
|
const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled() ? formatRenameBranchDirective(fresh, {
|
|
6991
7356
|
customPrompt: settings?.prompts?.renameBranch
|
|
@@ -7016,6 +7381,7 @@ var Orchestrator = class {
|
|
|
7016
7381
|
coordinatorDirective,
|
|
7017
7382
|
worktreeDirective,
|
|
7018
7383
|
optionalServicesDirective,
|
|
7384
|
+
issueToolsDirective,
|
|
7019
7385
|
artifactDirective,
|
|
7020
7386
|
longRunningDirective,
|
|
7021
7387
|
renameBranchDirective,
|
|
@@ -7125,6 +7491,7 @@ var Orchestrator = class {
|
|
|
7125
7491
|
coordinatorDirective,
|
|
7126
7492
|
worktreeDirective,
|
|
7127
7493
|
optionalServicesDirective,
|
|
7494
|
+
issueToolsDirective,
|
|
7128
7495
|
artifactDirective,
|
|
7129
7496
|
longRunningDirective,
|
|
7130
7497
|
renameBranchDirective,
|
|
@@ -7227,6 +7594,7 @@ var Orchestrator = class {
|
|
|
7227
7594
|
this.emit({ type: "turn_finished", threadId, exitCode });
|
|
7228
7595
|
if (exitCode === 0) {
|
|
7229
7596
|
this.crashContinued.delete(threadId);
|
|
7597
|
+
this.maybeEnqueueJobContinue(threadId, chatText, parts);
|
|
7230
7598
|
} else {
|
|
7231
7599
|
const blob = [chatText, detail].filter(Boolean).join("\n");
|
|
7232
7600
|
void this.maybeHandleOrchestrationQuotaFailover(threadId, blob);
|
|
@@ -8179,7 +8547,7 @@ var Orchestrator = class {
|
|
|
8179
8547
|
this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
|
|
8180
8548
|
if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
|
|
8181
8549
|
try {
|
|
8182
|
-
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-
|
|
8550
|
+
const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-NYSM2EPX.js");
|
|
8183
8551
|
await ensureWorkspace2(thread.repoPath);
|
|
8184
8552
|
} catch {
|
|
8185
8553
|
}
|
|
@@ -8227,7 +8595,7 @@ var Orchestrator = class {
|
|
|
8227
8595
|
this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
|
|
8228
8596
|
return restored2;
|
|
8229
8597
|
}
|
|
8230
|
-
if (!
|
|
8598
|
+
if (!existsSync18(thread.worktreePath)) {
|
|
8231
8599
|
if (isCowboyThread(thread) || isPrimaryCheckoutThread(thread)) {
|
|
8232
8600
|
throw new Error(
|
|
8233
8601
|
`Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
|
|
@@ -8489,6 +8857,11 @@ export {
|
|
|
8489
8857
|
worktreeCleanupSettings,
|
|
8490
8858
|
applyThreadIntoMain,
|
|
8491
8859
|
cloneRepoIntoSideboard,
|
|
8860
|
+
mcpWaitForTurnTimeoutMs,
|
|
8861
|
+
mcpWaitStillRunningHint,
|
|
8862
|
+
mcpWaitFinishedHint,
|
|
8863
|
+
mcpWaitForJobTimeoutMs,
|
|
8864
|
+
waitForDetachedJob,
|
|
8492
8865
|
detectAgents,
|
|
8493
8866
|
requireAgent,
|
|
8494
8867
|
isHomeBoardThread,
|
|
@@ -8559,6 +8932,12 @@ export {
|
|
|
8559
8932
|
formatRenameBranchDirective,
|
|
8560
8933
|
formatWorktreeDirective,
|
|
8561
8934
|
formatWorktreeReminder,
|
|
8935
|
+
issueTicketFromThread,
|
|
8936
|
+
linearTicketIdFromThread,
|
|
8937
|
+
formatIssueToolsDirective,
|
|
8938
|
+
formatLinearDirective,
|
|
8939
|
+
formatIssueToolsReminder,
|
|
8940
|
+
formatLinearReminder,
|
|
8562
8941
|
formatPrGateDirective,
|
|
8563
8942
|
formatProcessGuideDirective,
|
|
8564
8943
|
formatLongRunningDirective,
|