sisyphi 1.2.19 → 1.2.21
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/cli.js +528 -283
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +193 -287
- package/dist/daemon.js.map +1 -1
- package/dist/templates/agent-plugin/agents/spec.md +18 -7
- package/dist/tui.js +98 -79
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
- package/templates/agent-plugin/agents/spec.md +18 -7
package/dist/daemon.js
CHANGED
|
@@ -2504,7 +2504,7 @@ var init_history = __esm({
|
|
|
2504
2504
|
});
|
|
2505
2505
|
|
|
2506
2506
|
// src/shared/platform.ts
|
|
2507
|
-
import { execSync as execSync4 } from "child_process";
|
|
2507
|
+
import { execFileSync as execFileSync2, execSync as execSync4 } from "child_process";
|
|
2508
2508
|
import { existsSync as existsSync9, readFileSync as readFileSync11 } from "fs";
|
|
2509
2509
|
function detectPlatform() {
|
|
2510
2510
|
if (cachedPlatform) return cachedPlatform;
|
|
@@ -2694,13 +2694,14 @@ var init_notify = __esm({
|
|
|
2694
2694
|
});
|
|
2695
2695
|
|
|
2696
2696
|
// src/daemon/ask-store.ts
|
|
2697
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync12, readdirSync as readdirSync7 } from "fs";
|
|
2697
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync12, readdirSync as readdirSync7, unlinkSync } from "fs";
|
|
2698
2698
|
import { basename as basename4 } from "path";
|
|
2699
|
-
function maybeNotifyOnAskCreated(cwd, sessionId, meta) {
|
|
2699
|
+
function maybeNotifyOnAskCreated(cwd, sessionId, meta, suppress = false) {
|
|
2700
2700
|
if (process.env.NODE_ENV === "test" || process.env.SISYPHUS_DISABLE_NOTIFY === "1") return;
|
|
2701
2701
|
const isActionable = meta.kind !== void 0 && ACTIONABLE_KINDS.has(meta.kind);
|
|
2702
2702
|
const isHeartbeat = meta.askedBy === HEARTBEAT_ASKED_BY;
|
|
2703
2703
|
if (!isActionable && !isHeartbeat) return;
|
|
2704
|
+
if (suppress) return;
|
|
2704
2705
|
try {
|
|
2705
2706
|
const config = loadConfig(cwd);
|
|
2706
2707
|
if (config.notifications?.enabled === false) return;
|
|
@@ -2726,8 +2727,7 @@ function createAsk(cwd, sessionId, params) {
|
|
|
2726
2727
|
...params.title !== void 0 ? { title: params.title } : {},
|
|
2727
2728
|
...params.subtitle !== void 0 ? { subtitle: params.subtitle } : {},
|
|
2728
2729
|
...params.kind !== void 0 ? { kind: params.kind } : {},
|
|
2729
|
-
...params.orphanTarget !== void 0 ? { orphanTarget: params.orphanTarget } : {}
|
|
2730
|
-
...params.modeTransition !== void 0 ? { modeTransition: params.modeTransition } : {}
|
|
2730
|
+
...params.orphanTarget !== void 0 ? { orphanTarget: params.orphanTarget } : {}
|
|
2731
2731
|
};
|
|
2732
2732
|
atomicWrite(askMetaPath(cwd, sessionId, params.askId), JSON.stringify(meta, null, 2));
|
|
2733
2733
|
emitHistoryEvent(sessionId, "ask-issued", {
|
|
@@ -2736,7 +2736,7 @@ function createAsk(cwd, sessionId, params) {
|
|
|
2736
2736
|
blocking: params.blocking,
|
|
2737
2737
|
askedAt
|
|
2738
2738
|
});
|
|
2739
|
-
maybeNotifyOnAskCreated(cwd, sessionId, meta);
|
|
2739
|
+
maybeNotifyOnAskCreated(cwd, sessionId, meta, params.suppressTerminalNotification === true);
|
|
2740
2740
|
return meta;
|
|
2741
2741
|
}
|
|
2742
2742
|
function writeDecisions(cwd, sessionId, askId, deck) {
|
|
@@ -2871,6 +2871,26 @@ async function maybeAutoResolveAsk(cwd, sessionId, askId, deck) {
|
|
|
2871
2871
|
} catch {
|
|
2872
2872
|
}
|
|
2873
2873
|
}
|
|
2874
|
+
async function clearStaleAskClaims(cwd, sessionId) {
|
|
2875
|
+
let cleared = 0;
|
|
2876
|
+
for (const askId of listAsks(cwd, sessionId)) {
|
|
2877
|
+
const progressPath = askProgressPath(cwd, sessionId, askId);
|
|
2878
|
+
if (!existsSync11(progressPath)) continue;
|
|
2879
|
+
if (existsSync11(askOutputPath(cwd, sessionId, askId))) continue;
|
|
2880
|
+
try {
|
|
2881
|
+
unlinkSync(progressPath);
|
|
2882
|
+
} catch {
|
|
2883
|
+
continue;
|
|
2884
|
+
}
|
|
2885
|
+
cleared++;
|
|
2886
|
+
const meta = readMeta(cwd, sessionId, askId);
|
|
2887
|
+
if (meta?.status === "in-progress") {
|
|
2888
|
+
await updateMeta(cwd, sessionId, askId, { status: "pending", startedAt: void 0 }).catch(() => {
|
|
2889
|
+
});
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
return cleared;
|
|
2893
|
+
}
|
|
2874
2894
|
function listOpenAsksFor(cwd, sessionId, askedBy) {
|
|
2875
2895
|
const out = [];
|
|
2876
2896
|
for (const askId of listAsks(cwd, sessionId)) {
|
|
@@ -3293,7 +3313,7 @@ var init_orphan_sweep = __esm({
|
|
|
3293
3313
|
});
|
|
3294
3314
|
|
|
3295
3315
|
// src/daemon/agent.ts
|
|
3296
|
-
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, readdirSync as readdirSync8, existsSync as existsSync13, unlinkSync } from "fs";
|
|
3316
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, readdirSync as readdirSync8, existsSync as existsSync13, unlinkSync as unlinkSync2 } from "fs";
|
|
3297
3317
|
import { execSync as execSync5 } from "child_process";
|
|
3298
3318
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3299
3319
|
import { resolve as resolve5, relative as relative2, dirname as dirname3, join as join12 } from "path";
|
|
@@ -3660,7 +3680,7 @@ function gcBgTasks(cwd, sessionId, agentId) {
|
|
|
3660
3680
|
console.warn(`[bg-tasks] ${agentId} exited with ${leftover.length} untracked background task(s): ${leftover.join(", ")}`);
|
|
3661
3681
|
emitHistoryEvent(sessionId, "bg-tasks-leftover", { agentId, leftover });
|
|
3662
3682
|
}
|
|
3663
|
-
|
|
3683
|
+
unlinkSync2(file);
|
|
3664
3684
|
} catch (err) {
|
|
3665
3685
|
console.warn(`[bg-tasks] ${agentId} cleanup failed:`, err instanceof Error ? err.message : err);
|
|
3666
3686
|
}
|
|
@@ -5140,13 +5160,12 @@ function buildPersonality(stats) {
|
|
|
5140
5160
|
function shouldGenerateCommentary(event) {
|
|
5141
5161
|
switch (event) {
|
|
5142
5162
|
case "session-start":
|
|
5163
|
+
case "mode-transition":
|
|
5143
5164
|
case "session-complete":
|
|
5144
5165
|
case "level-up":
|
|
5145
5166
|
case "achievement":
|
|
5146
5167
|
case "late-night":
|
|
5147
5168
|
return true;
|
|
5148
|
-
case "cycle-boundary":
|
|
5149
|
-
return Math.random() < 0.5;
|
|
5150
5169
|
case "idle-wake":
|
|
5151
5170
|
return Math.random() < 0.5;
|
|
5152
5171
|
case "agent-crash":
|
|
@@ -5338,18 +5357,17 @@ var init_companion_commentary = __esm({
|
|
|
5338
5357
|
{ event: "session-start", mood: "grinding", context: "migrate database schema", output: "Database migration. The kind of task that sounds simple until you're three hours in." },
|
|
5339
5358
|
{ event: "agent-crash", mood: "frustrated", context: "agent-003 (reviewer) crashed. 2/5 agents still running", output: "Down to two out of five. The reviewer went first, which is either ironic or fitting." },
|
|
5340
5359
|
{ event: "late-night", mood: "existential", context: "3:14am, 2 sessions active", output: "Something about 3am makes every function look like a confession." },
|
|
5341
|
-
{ event: "
|
|
5360
|
+
{ event: "mode-transition", mood: "grinding", context: "Entering Implementation mode. Discovery: 3 cycles \xB7 24m active.", output: "Discovery is done pretending it was the hard part. Now we actually build the thing." },
|
|
5361
|
+
{ event: "mode-transition", mood: "zen", context: "Entering Validation mode. Implementation: 5 cycles \xB7 1h active.", output: "Code is written. Now the part where we find out which of my assumptions were lies." },
|
|
5342
5362
|
{ event: "level-up", mood: "excited", context: "Level 7 (Boulder Artisan) \u2192 8 (Crag Whisperer)", output: 'Crag Whisperer. Could be worse. Could be "guy who pushes rocks forever."' },
|
|
5343
5363
|
{ event: "idle-wake", mood: "sleepy", context: "Idle for 45 minutes", output: "Forty-five minutes of nothing, and the workspace looks exactly the same. Comforting or depressing." },
|
|
5344
5364
|
{ event: "session-complete", mood: "zen", context: "Task: fix CI pipeline. 1 agent, 1 cycle, 4min", output: "One agent, one cycle, four minutes. If they were all like this, what would I complain about." },
|
|
5345
5365
|
{ event: "session-start", mood: "existential", context: "rewrite the entire test suite", output: "Rewriting every test. Each one a small promise that the code does what someone thinks it does." },
|
|
5346
|
-
{ event: "cycle-boundary", mood: "grinding", context: "Cycle 7. 3 agents running, 1 crashed, 2 completed", output: "Cycle seven and the survivors are doing the work of five. Nobody asked them to." },
|
|
5347
5366
|
{ event: "late-night", mood: "sleepy", context: "1:30am, 1 session active", output: "Past one. Everything takes twice as long and matters half as much at this hour." },
|
|
5348
5367
|
{ event: "session-complete", mood: "excited", context: "Task: implement search. 8 agents, 3 cycles", output: "Search works. Eight agents and none of them stepped on each other. That never happens." },
|
|
5349
5368
|
{ event: "agent-crash", mood: "zen", context: "agent-001 crashed during linting", output: "Lost one to the linter. Not the worst way to go." },
|
|
5350
5369
|
{ event: "idle-wake", mood: "grinding", context: "Idle for 2 hours", output: "Two hours away and nothing changed. Exactly as expected, exactly as disappointing." },
|
|
5351
5370
|
{ event: "session-start", mood: "happy", context: "add dark mode", output: "Dark mode. Finally a task that matches the terminal aesthetic." },
|
|
5352
|
-
{ event: "cycle-boundary", mood: "frustrated", context: "Cycle 3. 2 agents crashed, 1 completed with errors", output: "Two crashed, one limped back. If this were a heist movie, we'd be in the part where the plan falls apart." },
|
|
5353
5371
|
{ event: "level-up", mood: "zen", context: "Level 12 (Slope Philosopher) \u2192 13 (Gradient Monk)", output: "Gradient Monk. A title for finding calm in repetition. Accurate enough." },
|
|
5354
5372
|
{ event: "session-complete", mood: "grinding", context: "Task: dependency upgrades. 6 agents, 5 cycles, 40min", output: "Forty minutes on dependencies. The kind of work that feels like running in place until suddenly you're done." },
|
|
5355
5373
|
{ event: "late-night", mood: "grinding", context: "4:22am, 3 sessions running", output: "Three sessions at four in the morning. Either dedication or the absence of better judgment." },
|
|
@@ -6175,7 +6193,7 @@ var init_companion_render = __esm({
|
|
|
6175
6193
|
});
|
|
6176
6194
|
|
|
6177
6195
|
// src/daemon/companion-popup.ts
|
|
6178
|
-
import { writeFileSync as writeFileSync12, readFileSync as readFileSync16, unlinkSync as
|
|
6196
|
+
import { writeFileSync as writeFileSync12, readFileSync as readFileSync16, unlinkSync as unlinkSync3, existsSync as existsSync16 } from "fs";
|
|
6179
6197
|
import { tmpdir } from "os";
|
|
6180
6198
|
import { join as join15, resolve as resolve6 } from "path";
|
|
6181
6199
|
function wrapText(text, width) {
|
|
@@ -6268,7 +6286,7 @@ fi
|
|
|
6268
6286
|
`;
|
|
6269
6287
|
writeFileSync12(POPUP_SCRIPT, script, { mode: 493 });
|
|
6270
6288
|
try {
|
|
6271
|
-
|
|
6289
|
+
unlinkSync3(POPUP_RESULT_PREFIX);
|
|
6272
6290
|
} catch {
|
|
6273
6291
|
}
|
|
6274
6292
|
const clientsRaw = execSafe('tmux list-clients -F "#{client_name} #{client_width}"');
|
|
@@ -6278,14 +6296,14 @@ fi
|
|
|
6278
6296
|
const client = line.slice(0, lastSpace);
|
|
6279
6297
|
const clientWidth = parseInt(line.slice(lastSpace + 1), 10);
|
|
6280
6298
|
if (!clientWidth) continue;
|
|
6281
|
-
const x = Math.max(0, clientWidth - POPUP_WIDTH);
|
|
6299
|
+
const x = Math.max(0, clientWidth - POPUP_WIDTH - 1);
|
|
6282
6300
|
const args = [
|
|
6283
6301
|
`-c ${shellQuote(client)}`,
|
|
6284
6302
|
"-E -b rounded",
|
|
6285
6303
|
`-T ${shellQuote(initialTitle)}`,
|
|
6286
6304
|
`-S "fg=${moodColor}"`,
|
|
6287
6305
|
`-s "fg=${moodColor}"`,
|
|
6288
|
-
`-x ${x} -y
|
|
6306
|
+
`-x ${x} -y 2`,
|
|
6289
6307
|
`-w ${POPUP_WIDTH} -h ${maxContentHeight}`,
|
|
6290
6308
|
shellQuote(POPUP_SCRIPT)
|
|
6291
6309
|
].join(" ");
|
|
@@ -6298,7 +6316,7 @@ fi
|
|
|
6298
6316
|
return null;
|
|
6299
6317
|
} finally {
|
|
6300
6318
|
try {
|
|
6301
|
-
|
|
6319
|
+
unlinkSync3(POPUP_RESULT_PREFIX);
|
|
6302
6320
|
} catch {
|
|
6303
6321
|
}
|
|
6304
6322
|
}
|
|
@@ -6769,152 +6787,15 @@ var init_pane_monitor = __esm({
|
|
|
6769
6787
|
}
|
|
6770
6788
|
});
|
|
6771
6789
|
|
|
6772
|
-
// src/daemon/mode-notify.ts
|
|
6773
|
-
import { existsSync as existsSync17 } from "fs";
|
|
6774
|
-
import { ulid as ulid2 } from "ulid";
|
|
6775
|
-
function capitalize(s) {
|
|
6776
|
-
return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
|
|
6777
|
-
}
|
|
6778
|
-
function formatDuration(ms) {
|
|
6779
|
-
const sec = Math.round(ms / 1e3);
|
|
6780
|
-
if (sec < 60) return `${sec}s`;
|
|
6781
|
-
const min = Math.round(sec / 60);
|
|
6782
|
-
if (min < 60) return `${min}m`;
|
|
6783
|
-
const h = Math.floor(min / 60);
|
|
6784
|
-
const remM = min % 60;
|
|
6785
|
-
return remM ? `${h}h ${remM}m` : `${h}h`;
|
|
6786
|
-
}
|
|
6787
|
-
function findOpenModeTransitionAsk(cwd, sessionId) {
|
|
6788
|
-
for (const askId of listAsks(cwd, sessionId)) {
|
|
6789
|
-
const meta = readMeta(cwd, sessionId, askId);
|
|
6790
|
-
if (!meta) continue;
|
|
6791
|
-
if (meta.askedBy !== ORCHESTRATOR_ASKED_BY) continue;
|
|
6792
|
-
if (meta.modeTransition !== true) continue;
|
|
6793
|
-
if (meta.status === "answered") continue;
|
|
6794
|
-
if (meta.orphaned === true) continue;
|
|
6795
|
-
if (existsSync17(askOutputPath(cwd, sessionId, askId))) continue;
|
|
6796
|
-
return askId;
|
|
6797
|
-
}
|
|
6798
|
-
return null;
|
|
6799
|
-
}
|
|
6800
|
-
function buildNextChain(prevChain, prevMode, nextMode, prevModeStats) {
|
|
6801
|
-
const stats = prevModeStats ? { cycles: prevModeStats.cycles, activeMs: prevModeStats.activeMs } : {};
|
|
6802
|
-
if (prevChain && prevChain.length > 0) {
|
|
6803
|
-
const updated = prevChain.map(
|
|
6804
|
-
(e, i) => i === prevChain.length - 1 && prevModeStats ? { ...e, ...stats } : e
|
|
6805
|
-
);
|
|
6806
|
-
updated.push({ mode: nextMode });
|
|
6807
|
-
return updated;
|
|
6808
|
-
}
|
|
6809
|
-
if (prevMode !== void 0) {
|
|
6810
|
-
return [{ mode: prevMode, ...stats }, { mode: nextMode }];
|
|
6811
|
-
}
|
|
6812
|
-
return [{ mode: "unknown" }, { mode: nextMode }];
|
|
6813
|
-
}
|
|
6814
|
-
function renderBody(chain, cwd) {
|
|
6815
|
-
const current = chain[chain.length - 1];
|
|
6816
|
-
const description = discoverOrchestratorModes(cwd).find((m) => m.name === current.mode)?.description?.trim();
|
|
6817
|
-
const lines = [];
|
|
6818
|
-
if (description) {
|
|
6819
|
-
lines.push(`**${capitalize(current.mode)}** \u2014 ${description}`);
|
|
6820
|
-
} else {
|
|
6821
|
-
lines.push(`Now in **${capitalize(current.mode)}** mode.`);
|
|
6822
|
-
}
|
|
6823
|
-
for (let i = 0; i < chain.length - 1; i++) {
|
|
6824
|
-
const e = chain[i];
|
|
6825
|
-
if (e.cycles === void 0) continue;
|
|
6826
|
-
const label = e.cycles === 1 ? "cycle" : "cycles";
|
|
6827
|
-
lines.push(
|
|
6828
|
-
`${capitalize(e.mode)}: ${e.cycles} ${label} \xB7 ${formatDuration(e.activeMs ?? 0)} active`
|
|
6829
|
-
);
|
|
6830
|
-
}
|
|
6831
|
-
return lines.join("\n\n");
|
|
6832
|
-
}
|
|
6833
|
-
async function emitModeTransitionNotify(cwd, sessionId, prevMode, nextMode, prevModeStats) {
|
|
6834
|
-
let sessionName;
|
|
6835
|
-
try {
|
|
6836
|
-
sessionName = getSession(cwd, sessionId).name;
|
|
6837
|
-
} catch {
|
|
6838
|
-
}
|
|
6839
|
-
const existingAskId = findOpenModeTransitionAsk(cwd, sessionId);
|
|
6840
|
-
const existingDeck = existingAskId ? readDecisions(cwd, sessionId, existingAskId) : null;
|
|
6841
|
-
const chain = buildNextChain(
|
|
6842
|
-
existingDeck?.source?.modeChain,
|
|
6843
|
-
prevMode,
|
|
6844
|
-
nextMode,
|
|
6845
|
-
prevModeStats
|
|
6846
|
-
);
|
|
6847
|
-
const subtitle = chain.map((e) => e.mode).join(" \u2192 ");
|
|
6848
|
-
const title = "Mode change";
|
|
6849
|
-
const deckTitle = `Mode: ${subtitle}`;
|
|
6850
|
-
const body = renderBody(chain, cwd);
|
|
6851
|
-
const interaction = {
|
|
6852
|
-
id: "mode-transition",
|
|
6853
|
-
title,
|
|
6854
|
-
subtitle,
|
|
6855
|
-
body,
|
|
6856
|
-
kind: "notify",
|
|
6857
|
-
options: [{ id: "ack", label: "Acknowledged" }]
|
|
6858
|
-
};
|
|
6859
|
-
const deckSource = {
|
|
6860
|
-
...sessionName !== void 0 ? { sessionName } : {},
|
|
6861
|
-
askedBy: ORCHESTRATOR_ASKED_BY,
|
|
6862
|
-
modeChain: chain
|
|
6863
|
-
};
|
|
6864
|
-
const deck = {
|
|
6865
|
-
title: deckTitle,
|
|
6866
|
-
source: deckSource,
|
|
6867
|
-
interactions: [interaction]
|
|
6868
|
-
};
|
|
6869
|
-
try {
|
|
6870
|
-
if (existingAskId) {
|
|
6871
|
-
writeDecisions(cwd, sessionId, existingAskId, deck);
|
|
6872
|
-
await updateMeta(cwd, sessionId, existingAskId, {
|
|
6873
|
-
title,
|
|
6874
|
-
subtitle,
|
|
6875
|
-
askedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6876
|
-
});
|
|
6877
|
-
return;
|
|
6878
|
-
}
|
|
6879
|
-
const askId = ulid2();
|
|
6880
|
-
createAsk(cwd, sessionId, {
|
|
6881
|
-
askId,
|
|
6882
|
-
askedBy: ORCHESTRATOR_ASKED_BY,
|
|
6883
|
-
blocking: false,
|
|
6884
|
-
cwd,
|
|
6885
|
-
title,
|
|
6886
|
-
subtitle,
|
|
6887
|
-
kind: "notify",
|
|
6888
|
-
modeTransition: true
|
|
6889
|
-
});
|
|
6890
|
-
writeDecisions(cwd, sessionId, askId, deck);
|
|
6891
|
-
} catch (err) {
|
|
6892
|
-
console.warn(
|
|
6893
|
-
`[sisyphus] mode-notify: failed to emit mode transition ask for ${sessionId}:`,
|
|
6894
|
-
err instanceof Error ? err.message : err
|
|
6895
|
-
);
|
|
6896
|
-
}
|
|
6897
|
-
}
|
|
6898
|
-
var init_mode_notify = __esm({
|
|
6899
|
-
"src/daemon/mode-notify.ts"() {
|
|
6900
|
-
"use strict";
|
|
6901
|
-
init_ask_store();
|
|
6902
|
-
init_state();
|
|
6903
|
-
init_orchestrator_modes();
|
|
6904
|
-
init_paths();
|
|
6905
|
-
init_types();
|
|
6906
|
-
}
|
|
6907
|
-
});
|
|
6908
|
-
|
|
6909
6790
|
// src/daemon/orchestrator.ts
|
|
6910
|
-
import { existsSync as
|
|
6791
|
+
import { existsSync as existsSync17, readdirSync as readdirSync10, readFileSync as readFileSync17, writeFileSync as writeFileSync13 } from "fs";
|
|
6911
6792
|
import { execSync as execSync6 } from "child_process";
|
|
6912
6793
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
6913
6794
|
import { resolve as resolve7, join as join16, relative as relative3 } from "path";
|
|
6914
6795
|
function detectRepos(cwd) {
|
|
6915
6796
|
const config = loadConfig(cwd);
|
|
6916
6797
|
const repos = [];
|
|
6917
|
-
if (
|
|
6798
|
+
if (existsSync17(join16(cwd, ".git"))) {
|
|
6918
6799
|
try {
|
|
6919
6800
|
repos.push(getRepoInfo(cwd, "."));
|
|
6920
6801
|
} catch {
|
|
@@ -6926,7 +6807,7 @@ function detectRepos(cwd) {
|
|
|
6926
6807
|
if (!entry.isDirectory()) continue;
|
|
6927
6808
|
if (entry.name.startsWith(".")) continue;
|
|
6928
6809
|
const childPath = join16(cwd, entry.name);
|
|
6929
|
-
if (
|
|
6810
|
+
if (existsSync17(join16(childPath, ".git"))) {
|
|
6930
6811
|
try {
|
|
6931
6812
|
repos.push(getRepoInfo(childPath, entry.name));
|
|
6932
6813
|
} catch {
|
|
@@ -6964,13 +6845,13 @@ function resolveOrchestratorSettings(cwd, sessionId) {
|
|
|
6964
6845
|
const bundled = resolve7(import.meta.dirname, "../templates/orchestrator-settings.json");
|
|
6965
6846
|
const projectSettings = projectOrchestratorSettingsPath(cwd);
|
|
6966
6847
|
const userSettings = userOrchestratorSettingsPath();
|
|
6967
|
-
const hasProject =
|
|
6968
|
-
const hasUser =
|
|
6848
|
+
const hasProject = existsSync17(projectSettings);
|
|
6849
|
+
const hasUser = existsSync17(userSettings);
|
|
6969
6850
|
const digestVerbs = digestSpinnerVerbs(cwd, sessionId);
|
|
6970
6851
|
if (!hasProject && !hasUser && !digestVerbs) return bundled;
|
|
6971
6852
|
let merged = {};
|
|
6972
6853
|
for (const path of [bundled, hasUser ? userSettings : null, hasProject ? projectSettings : null]) {
|
|
6973
|
-
if (!path || !
|
|
6854
|
+
if (!path || !existsSync17(path)) continue;
|
|
6974
6855
|
try {
|
|
6975
6856
|
const parsed = JSON.parse(readFileSync17(path, "utf-8"));
|
|
6976
6857
|
merged = { ...merged, ...parsed };
|
|
@@ -6987,11 +6868,11 @@ function resolveOrchestratorSettings(cwd, sessionId) {
|
|
|
6987
6868
|
}
|
|
6988
6869
|
function loadOrchestratorPrompt(cwd, sessionId, mode) {
|
|
6989
6870
|
const projectPath = projectOrchestratorPromptPath(cwd);
|
|
6990
|
-
if (
|
|
6871
|
+
if (existsSync17(projectPath)) {
|
|
6991
6872
|
return readFileSync17(projectPath, "utf-8");
|
|
6992
6873
|
}
|
|
6993
6874
|
const userPath = userOrchestratorPromptPath();
|
|
6994
|
-
if (
|
|
6875
|
+
if (existsSync17(userPath)) {
|
|
6995
6876
|
return readFileSync17(userPath, "utf-8");
|
|
6996
6877
|
}
|
|
6997
6878
|
const basePath = resolve7(import.meta.dirname, "../templates/orchestrator-base.md");
|
|
@@ -7019,7 +6900,7 @@ function buildCompletionContent(session) {
|
|
|
7019
6900
|
lines.push("");
|
|
7020
6901
|
}
|
|
7021
6902
|
const logsDirPath = logsDir(session.cwd, session.id);
|
|
7022
|
-
if (
|
|
6903
|
+
if (existsSync17(logsDirPath)) {
|
|
7023
6904
|
const logFiles = readdirSync10(logsDirPath).filter((f) => f.startsWith("cycle-") && f.endsWith(".md")).sort();
|
|
7024
6905
|
if (logFiles.length > 0) {
|
|
7025
6906
|
lines.push("### Cycle Logs\n");
|
|
@@ -7071,7 +6952,7 @@ function buildCompletionContent(session) {
|
|
|
7071
6952
|
}
|
|
7072
6953
|
}
|
|
7073
6954
|
const reportsDirPath = reportsDir(session.cwd, session.id);
|
|
7074
|
-
if (
|
|
6955
|
+
if (existsSync17(reportsDirPath)) {
|
|
7075
6956
|
const reportFiles = readdirSync10(reportsDirPath).filter((f) => f.endsWith(".md"));
|
|
7076
6957
|
if (reportFiles.length > 0) {
|
|
7077
6958
|
lines.push("### Detailed Reports\n");
|
|
@@ -7097,7 +6978,7 @@ ${session.context}
|
|
|
7097
6978
|
}
|
|
7098
6979
|
} else {
|
|
7099
6980
|
let ctxFiles = [];
|
|
7100
|
-
if (
|
|
6981
|
+
if (existsSync17(ctxDir)) {
|
|
7101
6982
|
ctxFiles = readdirSync10(ctxDir).filter((f) => f !== "CLAUDE.md");
|
|
7102
6983
|
}
|
|
7103
6984
|
if (ctxFiles.length > 0) {
|
|
@@ -7139,10 +7020,10 @@ ${agentLines}
|
|
|
7139
7020
|
}
|
|
7140
7021
|
}
|
|
7141
7022
|
const strategyFile = strategyPath(session.cwd, session.id);
|
|
7142
|
-
const strategyRef =
|
|
7143
|
-
const roadmapRef =
|
|
7023
|
+
const strategyRef = existsSync17(strategyFile) ? `@${relative3(session.cwd, strategyFile)}` : "(empty)";
|
|
7024
|
+
const roadmapRef = existsSync17(roadmapFile) ? `@${relative3(session.cwd, roadmapFile)}` : "(empty)";
|
|
7144
7025
|
const digestFile = digestPath(session.cwd, session.id);
|
|
7145
|
-
const digestRef =
|
|
7026
|
+
const digestRef = existsSync17(digestFile) ? `@${relative3(session.cwd, digestFile)}` : "(not yet created)";
|
|
7146
7027
|
const repos = detectRepos(session.cwd);
|
|
7147
7028
|
let repositoriesSection = "\n\n## Repositories\n";
|
|
7148
7029
|
if (repos.length === 0) {
|
|
@@ -7169,7 +7050,7 @@ ${agentLines}
|
|
|
7169
7050
|
}
|
|
7170
7051
|
}
|
|
7171
7052
|
const goalFile = goalPath(session.cwd, session.id);
|
|
7172
|
-
const goalContent =
|
|
7053
|
+
const goalContent = existsSync17(goalFile) ? readFileSync17(goalFile, "utf-8").trim() : session.task;
|
|
7173
7054
|
const modeContent = modeContentBuilders[mode]?.(session) ?? "";
|
|
7174
7055
|
return `## Goal
|
|
7175
7056
|
|
|
@@ -7362,14 +7243,12 @@ async function handleOrchestratorYield(sessionId, cwd, nextPrompt, mode) {
|
|
|
7362
7243
|
prevModeStats = { cycles, activeMs };
|
|
7363
7244
|
}
|
|
7364
7245
|
await completeOrchestratorCycle(cwd, sessionId, nextPrompt, mode, cycleActiveMs);
|
|
7365
|
-
if (mode && mode !== prevMode) {
|
|
7366
|
-
await emitModeTransitionNotify(cwd, sessionId, prevMode, mode, prevModeStats);
|
|
7367
|
-
}
|
|
7368
7246
|
const freshSession = getSession(cwd, sessionId);
|
|
7369
7247
|
const runningAgents = freshSession.agents.filter((a) => a.status === "running");
|
|
7370
7248
|
if (runningAgents.length === 0) {
|
|
7371
7249
|
console.log(`[sisyphus] Orchestrator yielded with no running agents for session ${sessionId}`);
|
|
7372
7250
|
}
|
|
7251
|
+
return prevModeStats ? { prevMode, nextMode: mode, prevModeStats } : void 0;
|
|
7373
7252
|
}
|
|
7374
7253
|
async function handleOrchestratorComplete(sessionId, cwd, report) {
|
|
7375
7254
|
const session = getSession(cwd, sessionId);
|
|
@@ -7405,7 +7284,6 @@ var init_orchestrator = __esm({
|
|
|
7405
7284
|
init_tmux();
|
|
7406
7285
|
init_pane_registry();
|
|
7407
7286
|
init_pane_monitor();
|
|
7408
|
-
init_mode_notify();
|
|
7409
7287
|
init_plugins();
|
|
7410
7288
|
sessionWindowMap = /* @__PURE__ */ new Map();
|
|
7411
7289
|
sessionOrchestratorPane = /* @__PURE__ */ new Map();
|
|
@@ -7573,6 +7451,36 @@ var init_status_dots = __esm({
|
|
|
7573
7451
|
}
|
|
7574
7452
|
});
|
|
7575
7453
|
|
|
7454
|
+
// src/daemon/mode-transition.ts
|
|
7455
|
+
function capitalize(s) {
|
|
7456
|
+
return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
|
|
7457
|
+
}
|
|
7458
|
+
function formatDuration(ms) {
|
|
7459
|
+
const sec = Math.round(ms / 1e3);
|
|
7460
|
+
if (sec < 60) return `${sec}s`;
|
|
7461
|
+
const min = Math.round(sec / 60);
|
|
7462
|
+
if (min < 60) return `${min}m`;
|
|
7463
|
+
const h = Math.floor(min / 60);
|
|
7464
|
+
const remM = min % 60;
|
|
7465
|
+
return remM ? `${h}h ${remM}m` : `${h}h`;
|
|
7466
|
+
}
|
|
7467
|
+
function buildModeTransitionCommentary(cwd, prevMode, nextMode, prevModeStats) {
|
|
7468
|
+
const popupTitle = ` Starting ${capitalize(nextMode)} `;
|
|
7469
|
+
const description = discoverOrchestratorModes(cwd).find((m) => m.name === nextMode)?.description?.trim();
|
|
7470
|
+
const label = prevModeStats.cycles === 1 ? "cycle" : "cycles";
|
|
7471
|
+
const context = [
|
|
7472
|
+
description ? `Entering ${capitalize(nextMode)} mode \u2014 ${description}` : `Entering ${capitalize(nextMode)} mode.`,
|
|
7473
|
+
`${capitalize(prevMode)}: ${prevModeStats.cycles} ${label} \xB7 ${formatDuration(prevModeStats.activeMs)} active.`
|
|
7474
|
+
].join("\n");
|
|
7475
|
+
return { context, popupTitle };
|
|
7476
|
+
}
|
|
7477
|
+
var init_mode_transition = __esm({
|
|
7478
|
+
"src/daemon/mode-transition.ts"() {
|
|
7479
|
+
"use strict";
|
|
7480
|
+
init_orchestrator_modes();
|
|
7481
|
+
}
|
|
7482
|
+
});
|
|
7483
|
+
|
|
7576
7484
|
// src/shared/manifest.ts
|
|
7577
7485
|
import os from "os";
|
|
7578
7486
|
function mapEffortFallback(level) {
|
|
@@ -7613,7 +7521,7 @@ var init_manifest = __esm({
|
|
|
7613
7521
|
// src/shared/session-export.ts
|
|
7614
7522
|
import { execFile as execFile3 } from "child_process";
|
|
7615
7523
|
import { promisify as promisify2 } from "util";
|
|
7616
|
-
import { existsSync as
|
|
7524
|
+
import { existsSync as existsSync18, readFileSync as readFileSync19, mkdirSync as mkdirSync10, symlinkSync, rmSync as rmSync5, writeFileSync as writeFileSync14 } from "fs";
|
|
7617
7525
|
import { homedir as homedir6 } from "os";
|
|
7618
7526
|
import { join as join17 } from "path";
|
|
7619
7527
|
function sanitizeName(name) {
|
|
@@ -7625,7 +7533,7 @@ function buildOutputPath(label, dir) {
|
|
|
7625
7533
|
const base = `sisyphus-${label}-${date}`;
|
|
7626
7534
|
let candidate = join17(dir, `${base}.zip`);
|
|
7627
7535
|
let counter = 1;
|
|
7628
|
-
while (
|
|
7536
|
+
while (existsSync18(candidate)) {
|
|
7629
7537
|
counter++;
|
|
7630
7538
|
candidate = join17(dir, `${base}-${counter}.zip`);
|
|
7631
7539
|
}
|
|
@@ -7688,14 +7596,14 @@ async function exportSessionToZip(sessionId, cwd, options) {
|
|
|
7688
7596
|
const reveal = options?.reveal ?? true;
|
|
7689
7597
|
const sessDir = sessionDir(cwd, sessionId);
|
|
7690
7598
|
const histDir = historySessionDir(sessionId);
|
|
7691
|
-
const sessExists =
|
|
7692
|
-
const histExists =
|
|
7599
|
+
const sessExists = existsSync18(sessDir);
|
|
7600
|
+
const histExists = existsSync18(histDir);
|
|
7693
7601
|
if (!sessExists && !histExists) {
|
|
7694
7602
|
throw new Error(`No data found for session ${sessionId}`);
|
|
7695
7603
|
}
|
|
7696
7604
|
let label = sessionId.slice(0, 8);
|
|
7697
7605
|
const stPath = statePath(cwd, sessionId);
|
|
7698
|
-
if (
|
|
7606
|
+
if (existsSync18(stPath)) {
|
|
7699
7607
|
try {
|
|
7700
7608
|
const state = JSON.parse(readFileSync19(stPath, "utf-8"));
|
|
7701
7609
|
if (state.name) {
|
|
@@ -7887,7 +7795,7 @@ var init_format = __esm({
|
|
|
7887
7795
|
});
|
|
7888
7796
|
|
|
7889
7797
|
// src/cli/deploy/creds.ts
|
|
7890
|
-
import { chmodSync, existsSync as
|
|
7798
|
+
import { chmodSync, existsSync as existsSync19, mkdirSync as mkdirSync11, readFileSync as readFileSync21 } from "fs";
|
|
7891
7799
|
import { createInterface } from "readline";
|
|
7892
7800
|
function isValidProvider(value) {
|
|
7893
7801
|
return PROVIDERS.includes(value);
|
|
@@ -7928,10 +7836,10 @@ var init_pricing = __esm({
|
|
|
7928
7836
|
});
|
|
7929
7837
|
|
|
7930
7838
|
// src/cli/deploy/runtime.ts
|
|
7931
|
-
import { existsSync as
|
|
7839
|
+
import { existsSync as existsSync20, readFileSync as readFileSync22, unlinkSync as unlinkSync4 } from "fs";
|
|
7932
7840
|
function readRuntimeState(provider) {
|
|
7933
7841
|
const path = deployRuntimePath(provider);
|
|
7934
|
-
if (!
|
|
7842
|
+
if (!existsSync20(path)) return null;
|
|
7935
7843
|
try {
|
|
7936
7844
|
return JSON.parse(readFileSync22(path, "utf-8"));
|
|
7937
7845
|
} catch {
|
|
@@ -7955,15 +7863,15 @@ var init_tailnet = __esm({
|
|
|
7955
7863
|
});
|
|
7956
7864
|
|
|
7957
7865
|
// src/cli/deploy/templates.ts
|
|
7958
|
-
import { existsSync as
|
|
7866
|
+
import { existsSync as existsSync21 } from "fs";
|
|
7959
7867
|
import { dirname as dirname6, resolve as resolve9 } from "path";
|
|
7960
7868
|
import { fileURLToPath } from "url";
|
|
7961
7869
|
function deployRoot() {
|
|
7962
7870
|
const here = dirname6(fileURLToPath(import.meta.url));
|
|
7963
7871
|
const bundled = resolve9(here, "..", "deploy");
|
|
7964
|
-
if (
|
|
7872
|
+
if (existsSync21(bundled)) return bundled;
|
|
7965
7873
|
const sourceRoot = resolve9(here, "..", "..", "..", "deploy");
|
|
7966
|
-
if (
|
|
7874
|
+
if (existsSync21(sourceRoot)) return sourceRoot;
|
|
7967
7875
|
throw new Error(
|
|
7968
7876
|
`Could not locate deploy/ templates. Looked at:
|
|
7969
7877
|
${bundled}
|
|
@@ -7992,7 +7900,7 @@ var init_tailscale = __esm({
|
|
|
7992
7900
|
|
|
7993
7901
|
// src/cli/deploy/runner.ts
|
|
7994
7902
|
import { spawn as spawn2, spawnSync } from "child_process";
|
|
7995
|
-
import { copyFileSync as copyFileSync5, existsSync as
|
|
7903
|
+
import { copyFileSync as copyFileSync5, existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync23 } from "fs";
|
|
7996
7904
|
function readOutputs(provider) {
|
|
7997
7905
|
const result = spawnSync("terraform", ["output", "-json", `-state=${deployStatePath(provider)}`], {
|
|
7998
7906
|
cwd: providerModuleDir(provider),
|
|
@@ -8018,7 +7926,7 @@ function readOutputs(provider) {
|
|
|
8018
7926
|
}
|
|
8019
7927
|
}
|
|
8020
7928
|
function isProvisioned(provider) {
|
|
8021
|
-
if (!
|
|
7929
|
+
if (!existsSync22(deployStatePath(provider))) return false;
|
|
8022
7930
|
return readOutputs(provider) !== null;
|
|
8023
7931
|
}
|
|
8024
7932
|
function effectiveSshTarget(provider) {
|
|
@@ -8137,7 +8045,7 @@ var init_grove = __esm({
|
|
|
8137
8045
|
|
|
8138
8046
|
// src/cli/cloud/repo.ts
|
|
8139
8047
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
8140
|
-
import { existsSync as
|
|
8048
|
+
import { existsSync as existsSync23 } from "fs";
|
|
8141
8049
|
import { basename as basename6, join as join18 } from "path";
|
|
8142
8050
|
function captureGit(args, cwd) {
|
|
8143
8051
|
const result = spawnSync3("git", args, {
|
|
@@ -8178,10 +8086,10 @@ function buildRsyncArgs(localDir, remoteTarget) {
|
|
|
8178
8086
|
];
|
|
8179
8087
|
}
|
|
8180
8088
|
function detectPackageManager(toplevel) {
|
|
8181
|
-
if (
|
|
8182
|
-
if (
|
|
8183
|
-
if (
|
|
8184
|
-
if (
|
|
8089
|
+
if (existsSync23(join18(toplevel, "pnpm-lock.yaml"))) return "pnpm";
|
|
8090
|
+
if (existsSync23(join18(toplevel, "bun.lockb"))) return "bun";
|
|
8091
|
+
if (existsSync23(join18(toplevel, "yarn.lock"))) return "yarn";
|
|
8092
|
+
if (existsSync23(join18(toplevel, "package-lock.json"))) return "npm";
|
|
8185
8093
|
return null;
|
|
8186
8094
|
}
|
|
8187
8095
|
function packageManagerInstallCmd(pm) {
|
|
@@ -8382,7 +8290,7 @@ __export(cloud_handoff_exports, {
|
|
|
8382
8290
|
triggerForceHandoff: () => triggerForceHandoff
|
|
8383
8291
|
});
|
|
8384
8292
|
import { spawn as spawn5 } from "child_process";
|
|
8385
|
-
import { existsSync as
|
|
8293
|
+
import { existsSync as existsSync24 } from "fs";
|
|
8386
8294
|
import { join as join19 } from "path";
|
|
8387
8295
|
function runRsync2(args) {
|
|
8388
8296
|
return new Promise((resolve13) => {
|
|
@@ -8422,7 +8330,7 @@ async function syncSessionState(cwd, sessionId, repo, target, provider) {
|
|
|
8422
8330
|
const candidates = ["config.json", "orchestrator.md", "orchestrator-settings.json"];
|
|
8423
8331
|
for (const name of candidates) {
|
|
8424
8332
|
const localPath = join19(localProject, name);
|
|
8425
|
-
if (!
|
|
8333
|
+
if (!existsSync24(localPath)) continue;
|
|
8426
8334
|
const remotePath = `${boxRepoPath(repo)}/.sisyphus/${name}`;
|
|
8427
8335
|
const args = [
|
|
8428
8336
|
"-avz",
|
|
@@ -8589,14 +8497,14 @@ var init_cloud_handoff = __esm({
|
|
|
8589
8497
|
|
|
8590
8498
|
// src/daemon/session-manager.ts
|
|
8591
8499
|
import { v4 as uuidv4 } from "uuid";
|
|
8592
|
-
import { existsSync as
|
|
8500
|
+
import { existsSync as existsSync25, readFileSync as readFileSync24, readdirSync as readdirSync11, rmSync as rmSync6 } from "fs";
|
|
8593
8501
|
function truncate(s, max) {
|
|
8594
8502
|
return s.length <= max ? s : s.slice(0, max) + "...";
|
|
8595
8503
|
}
|
|
8596
8504
|
function readGoal(cwd, sessionId, fallback) {
|
|
8597
8505
|
try {
|
|
8598
8506
|
const p = goalPath(cwd, sessionId);
|
|
8599
|
-
if (
|
|
8507
|
+
if (existsSync25(p)) return readFileSync24(p, "utf-8").trim();
|
|
8600
8508
|
} catch {
|
|
8601
8509
|
}
|
|
8602
8510
|
return fallback;
|
|
@@ -8650,7 +8558,7 @@ function fireHaikuNaming(sessionId, cwd, fallbackTmuxName, task) {
|
|
|
8650
8558
|
console.error(`[sisyphus] Name generation failed for session ${sessionId}:`, err);
|
|
8651
8559
|
});
|
|
8652
8560
|
}
|
|
8653
|
-
function fireCommentary(event, companion, context, flash = false, repo, sessionId) {
|
|
8561
|
+
function fireCommentary(event, companion, context, flash = false, repo, sessionId, popupTitle) {
|
|
8654
8562
|
const memoryCtx = buildMemoryContext(repo);
|
|
8655
8563
|
generateCommentary(event, companion, context, memoryCtx).then((text) => {
|
|
8656
8564
|
if (text) {
|
|
@@ -8658,7 +8566,7 @@ function fireCommentary(event, companion, context, flash = false, repo, sessionI
|
|
|
8658
8566
|
const c = loadCompanion();
|
|
8659
8567
|
recordCommentary(c, text, event);
|
|
8660
8568
|
if (flash) {
|
|
8661
|
-
const feedback =
|
|
8569
|
+
const feedback = showCommentaryPopupQueue([{ text, title: popupTitle }]);
|
|
8662
8570
|
if (feedback) {
|
|
8663
8571
|
recordFeedback(c, text, feedback.rating, event, feedback.comment);
|
|
8664
8572
|
if (sessionId) {
|
|
@@ -8835,7 +8743,7 @@ It is the other session's responsibility. You do not need to monitor it.
|
|
|
8835
8743
|
function pruneOldSessions(cwd) {
|
|
8836
8744
|
try {
|
|
8837
8745
|
const dir = sessionsDir(cwd);
|
|
8838
|
-
if (!
|
|
8746
|
+
if (!existsSync25(dir)) return;
|
|
8839
8747
|
const entries = readdirSync11(dir, { withFileTypes: true });
|
|
8840
8748
|
const candidates = [];
|
|
8841
8749
|
for (const entry of entries) {
|
|
@@ -8973,7 +8881,7 @@ function getSessionStatus(cwd, sessionId) {
|
|
|
8973
8881
|
}
|
|
8974
8882
|
function countSessions(cwd) {
|
|
8975
8883
|
const dir = sessionsDir(cwd);
|
|
8976
|
-
if (!
|
|
8884
|
+
if (!existsSync25(dir)) return 0;
|
|
8977
8885
|
let n = 0;
|
|
8978
8886
|
for (const entry of readdirSync11(dir, { withFileTypes: true })) {
|
|
8979
8887
|
if (entry.isDirectory()) n++;
|
|
@@ -8982,7 +8890,7 @@ function countSessions(cwd) {
|
|
|
8982
8890
|
}
|
|
8983
8891
|
function listSessions(cwd) {
|
|
8984
8892
|
const dir = sessionsDir(cwd);
|
|
8985
|
-
if (!
|
|
8893
|
+
if (!existsSync25(dir)) return [];
|
|
8986
8894
|
const entries = readdirSync11(dir, { withFileTypes: true });
|
|
8987
8895
|
const sessions = [];
|
|
8988
8896
|
for (const entry of entries) {
|
|
@@ -9046,23 +8954,6 @@ function onAllAgentsDone2(sessionId, cwd, windowId) {
|
|
|
9046
8954
|
const companion = loadCompanion();
|
|
9047
8955
|
companion.spinnerVerbIndex = (companion.spinnerVerbIndex + 1) % SPINNER_VERBS.length;
|
|
9048
8956
|
saveCompanion(companion);
|
|
9049
|
-
const goal = readGoal(cwd, sessionId, session.task);
|
|
9050
|
-
const modeLabel = lastCycle?.mode ? ` (${lastCycle.mode})` : "";
|
|
9051
|
-
const agentMap = new Map(session.agents.map((a) => [a.id, a]));
|
|
9052
|
-
const spawnedThisCycle = (lastCycle?.agentsSpawned ?? []).map((id) => agentMap.get(id)).filter(Boolean).map((a) => `${a.name} (${a.agentType.replace(/^sisyphus:/, "")}, ${a.status})`).join(", ");
|
|
9053
|
-
let cycleCtx = `Cycle ${cycleNumber}${modeLabel} complete. Goal: ${truncate(goal, 80)}`;
|
|
9054
|
-
if (spawnedThisCycle) cycleCtx += `
|
|
9055
|
-
Agents: ${truncate(spawnedThisCycle, 200)}`;
|
|
9056
|
-
try {
|
|
9057
|
-
const logPath2 = cycleLogPath(cwd, sessionId, cycleNumber);
|
|
9058
|
-
if (existsSync26(logPath2)) {
|
|
9059
|
-
const log = readFileSync24(logPath2, "utf-8").trim();
|
|
9060
|
-
if (log) cycleCtx += `
|
|
9061
|
-
Cycle log: ${truncate(log, 200)}`;
|
|
9062
|
-
}
|
|
9063
|
-
} catch {
|
|
9064
|
-
}
|
|
9065
|
-
fireCommentary("cycle-boundary", companion, cycleCtx, true, session.cwd, sessionId);
|
|
9066
8957
|
} catch {
|
|
9067
8958
|
}
|
|
9068
8959
|
setImmediate(async () => {
|
|
@@ -9235,8 +9126,23 @@ async function handleYield(sessionId, cwd, nextPrompt, mode) {
|
|
|
9235
9126
|
await updateSessionStatus(cwd, sessionId, "active");
|
|
9236
9127
|
}
|
|
9237
9128
|
respawningSessions.add(sessionId);
|
|
9238
|
-
await handleOrchestratorYield(sessionId, cwd, nextPrompt, mode);
|
|
9129
|
+
const transition = await handleOrchestratorYield(sessionId, cwd, nextPrompt, mode);
|
|
9239
9130
|
orchestratorDone.add(sessionId);
|
|
9131
|
+
if (transition) {
|
|
9132
|
+
try {
|
|
9133
|
+
const companion = loadCompanion();
|
|
9134
|
+
const goal = readGoal(cwd, sessionId, getSession(cwd, sessionId).task);
|
|
9135
|
+
const { context, popupTitle } = buildModeTransitionCommentary(
|
|
9136
|
+
cwd,
|
|
9137
|
+
transition.prevMode,
|
|
9138
|
+
transition.nextMode,
|
|
9139
|
+
transition.prevModeStats
|
|
9140
|
+
);
|
|
9141
|
+
fireCommentary("mode-transition", companion, `Goal: ${truncate(goal, 100)}
|
|
9142
|
+
${context}`, true, cwd, sessionId, popupTitle);
|
|
9143
|
+
} catch {
|
|
9144
|
+
}
|
|
9145
|
+
}
|
|
9240
9146
|
try {
|
|
9241
9147
|
recomputeDots();
|
|
9242
9148
|
} catch {
|
|
@@ -9291,6 +9197,7 @@ async function handleComplete(sessionId, cwd, report) {
|
|
|
9291
9197
|
console.warn("[sisyphus] Sentiment generation failed:", err instanceof Error ? err.message : err);
|
|
9292
9198
|
});
|
|
9293
9199
|
const config = loadConfig(cwd);
|
|
9200
|
+
const uploadCfg = config.upload;
|
|
9294
9201
|
if (isUploadConfigured(config.upload)) {
|
|
9295
9202
|
runSessionUploadAndPersist({
|
|
9296
9203
|
sessionId,
|
|
@@ -9302,11 +9209,10 @@ async function handleComplete(sessionId, cwd, report) {
|
|
|
9302
9209
|
}).catch(() => {
|
|
9303
9210
|
console.warn("[sisyphus] upload pipeline crashed; check uploadError on session state");
|
|
9304
9211
|
});
|
|
9305
|
-
} else if (
|
|
9306
|
-
const partialUpload = config.upload;
|
|
9212
|
+
} else if (uploadCfg) {
|
|
9307
9213
|
const missing = [];
|
|
9308
|
-
if (!
|
|
9309
|
-
if (!
|
|
9214
|
+
if (!uploadCfg.url) missing.push("upload.url");
|
|
9215
|
+
if (!uploadCfg.token) missing.push("upload.token");
|
|
9310
9216
|
const error = `upload skipped: missing ${missing.join(", ")}`;
|
|
9311
9217
|
console.warn(`[sisyphus] ${error}`);
|
|
9312
9218
|
updateSession(cwd, sessionId, { uploadStatus: "failed", uploadError: error }).catch(() => console.warn("[sisyphus] failed to persist upload skip status"));
|
|
@@ -9580,6 +9486,7 @@ var init_session_manager = __esm({
|
|
|
9580
9486
|
init_companion_render();
|
|
9581
9487
|
init_companion_commentary();
|
|
9582
9488
|
init_companion_popup();
|
|
9489
|
+
init_mode_transition();
|
|
9583
9490
|
init_history();
|
|
9584
9491
|
init_uploader();
|
|
9585
9492
|
init_upload();
|
|
@@ -9701,7 +9608,7 @@ var init_transcript_digest = __esm({
|
|
|
9701
9608
|
import {
|
|
9702
9609
|
closeSync as closeSync2,
|
|
9703
9610
|
constants,
|
|
9704
|
-
existsSync as
|
|
9611
|
+
existsSync as existsSync26,
|
|
9705
9612
|
fstatSync as fstatSync2,
|
|
9706
9613
|
lstatSync,
|
|
9707
9614
|
openSync as openSync2,
|
|
@@ -9723,7 +9630,7 @@ async function generateVisualForQuestion(opts) {
|
|
|
9723
9630
|
if (!question) return { ok: false, error: `qid ${opts.qid} not found in decisions` };
|
|
9724
9631
|
const mdPath = askVisualMarkdownPath(opts.cwd, opts.sessionId, opts.askId, opts.qid);
|
|
9725
9632
|
const ansiPath = askVisualAnsiPath(opts.cwd, opts.sessionId, opts.askId, opts.qid);
|
|
9726
|
-
if (!opts.force &&
|
|
9633
|
+
if (!opts.force && existsSync26(mdPath) && existsSync26(ansiPath)) {
|
|
9727
9634
|
return { ok: true, markdownPath: mdPath, ansiPath, turns: 0 };
|
|
9728
9635
|
}
|
|
9729
9636
|
if (opts.force) {
|
|
@@ -9797,7 +9704,7 @@ function buildUserPrompt(q, askedBy, ctx) {
|
|
|
9797
9704
|
}
|
|
9798
9705
|
function readSystemPrompt() {
|
|
9799
9706
|
if (cachedSystemPrompt !== void 0) return cachedSystemPrompt;
|
|
9800
|
-
const found = SYSTEM_PROMPT_CANDIDATES.find((p) =>
|
|
9707
|
+
const found = SYSTEM_PROMPT_CANDIDATES.find((p) => existsSync26(p));
|
|
9801
9708
|
if (found === void 0) {
|
|
9802
9709
|
throw new Error(
|
|
9803
9710
|
`termrender-haiku-system.md not found in any candidate location: ${SYSTEM_PROMPT_CANDIDATES.join(", ")}`
|
|
@@ -9944,7 +9851,7 @@ var init_ask_visual = __esm({
|
|
|
9944
9851
|
|
|
9945
9852
|
// src/daemon/server.ts
|
|
9946
9853
|
import { createServer } from "net";
|
|
9947
|
-
import { unlinkSync as
|
|
9854
|
+
import { unlinkSync as unlinkSync5, existsSync as existsSync27, writeFileSync as writeFileSync15, readFileSync as readFileSync26, mkdirSync as mkdirSync13, readdirSync as readdirSync12, rmSync as rmSync8, chmodSync as chmodSync2 } from "fs";
|
|
9948
9855
|
import { join as join21, basename as basename7, dirname as dirname8 } from "path";
|
|
9949
9856
|
import { scanInbox } from "@crouton-kit/humanloop";
|
|
9950
9857
|
function setCompositor(c) {
|
|
@@ -9964,7 +9871,7 @@ function persistSessionRegistry() {
|
|
|
9964
9871
|
}
|
|
9965
9872
|
function loadSessionRegistry() {
|
|
9966
9873
|
const p = registryPath();
|
|
9967
|
-
if (!
|
|
9874
|
+
if (!existsSync27(p)) return {};
|
|
9968
9875
|
try {
|
|
9969
9876
|
return JSON.parse(readFileSync26(p, "utf-8"));
|
|
9970
9877
|
} catch (err) {
|
|
@@ -10020,7 +9927,7 @@ function collectAllSessionIds() {
|
|
|
10020
9927
|
scannedCwds.add(cwd);
|
|
10021
9928
|
try {
|
|
10022
9929
|
const dir = sessionsDir(cwd);
|
|
10023
|
-
if (!
|
|
9930
|
+
if (!existsSync27(dir)) continue;
|
|
10024
9931
|
for (const entry of readdirSync12(dir, { withFileTypes: true })) {
|
|
10025
9932
|
if (entry.isDirectory() && !idToCwd.has(entry.name)) {
|
|
10026
9933
|
idToCwd.set(entry.name, cwd);
|
|
@@ -10252,7 +10159,7 @@ async function handleRequest(req) {
|
|
|
10252
10159
|
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
10253
10160
|
if (!tracking) {
|
|
10254
10161
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
10255
|
-
if (
|
|
10162
|
+
if (existsSync27(stateFile)) {
|
|
10256
10163
|
tracking = { cwd: req.cwd, messageCounter: 0 };
|
|
10257
10164
|
sessionTrackingMap.set(req.sessionId, tracking);
|
|
10258
10165
|
persistSessionRegistry();
|
|
@@ -10274,7 +10181,7 @@ async function handleRequest(req) {
|
|
|
10274
10181
|
}
|
|
10275
10182
|
case "clear-orphan": {
|
|
10276
10183
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
10277
|
-
if (!
|
|
10184
|
+
if (!existsSync27(stateFile)) {
|
|
10278
10185
|
return {
|
|
10279
10186
|
ok: false,
|
|
10280
10187
|
error: errNotFound("unknown_session", {
|
|
@@ -10332,7 +10239,7 @@ async function handleRequest(req) {
|
|
|
10332
10239
|
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
10333
10240
|
if (!tracking) {
|
|
10334
10241
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
10335
|
-
if (
|
|
10242
|
+
if (existsSync27(stateFile)) {
|
|
10336
10243
|
registerSessionCwd(req.sessionId, req.cwd);
|
|
10337
10244
|
tracking = sessionTrackingMap.get(req.sessionId);
|
|
10338
10245
|
} else {
|
|
@@ -10346,7 +10253,7 @@ async function handleRequest(req) {
|
|
|
10346
10253
|
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
10347
10254
|
if (!tracking) {
|
|
10348
10255
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
10349
|
-
if (
|
|
10256
|
+
if (existsSync27(stateFile)) {
|
|
10350
10257
|
registerSessionCwd(req.sessionId, req.cwd);
|
|
10351
10258
|
tracking = sessionTrackingMap.get(req.sessionId);
|
|
10352
10259
|
} else {
|
|
@@ -10363,7 +10270,7 @@ async function handleRequest(req) {
|
|
|
10363
10270
|
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
10364
10271
|
if (!tracking) {
|
|
10365
10272
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
10366
|
-
if (
|
|
10273
|
+
if (existsSync27(stateFile)) {
|
|
10367
10274
|
tracking = { cwd: req.cwd, messageCounter: 0 };
|
|
10368
10275
|
sessionTrackingMap.set(req.sessionId, tracking);
|
|
10369
10276
|
persistSessionRegistry();
|
|
@@ -10433,7 +10340,7 @@ async function handleRequest(req) {
|
|
|
10433
10340
|
}
|
|
10434
10341
|
case "set-upload-status": {
|
|
10435
10342
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
10436
|
-
if (!
|
|
10343
|
+
if (!existsSync27(stateFile)) {
|
|
10437
10344
|
return unknownSessionError(req.sessionId);
|
|
10438
10345
|
}
|
|
10439
10346
|
try {
|
|
@@ -10632,6 +10539,7 @@ async function handleRequest(req) {
|
|
|
10632
10539
|
const reviewItems = [];
|
|
10633
10540
|
for (const [sessionId, tracking] of sessionTrackingMap) {
|
|
10634
10541
|
if (!tracking.cwd) continue;
|
|
10542
|
+
if (tracking.cwd !== req.cwd) continue;
|
|
10635
10543
|
askDirs.push(askDir(tracking.cwd, sessionId));
|
|
10636
10544
|
reviewItems.push(...listReviewInboxItems(tracking.cwd, sessionId));
|
|
10637
10545
|
}
|
|
@@ -10655,6 +10563,26 @@ async function handleRequest(req) {
|
|
|
10655
10563
|
});
|
|
10656
10564
|
return { ok: true, data: { items: itemsWithName } };
|
|
10657
10565
|
}
|
|
10566
|
+
case "focus-set": {
|
|
10567
|
+
const ID_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
10568
|
+
if (!ID_RE.test(req.askId)) return {
|
|
10569
|
+
ok: false,
|
|
10570
|
+
error: errUsage("invalid_ask_id", { message: `Invalid askId: ${req.askId}`, received: req.askId })
|
|
10571
|
+
};
|
|
10572
|
+
pendingFocus = { cwd: req.cwd, sessionId: req.sessionId, askId: req.askId, requestedAt: Date.now() };
|
|
10573
|
+
return { ok: true };
|
|
10574
|
+
}
|
|
10575
|
+
case "focus-get": {
|
|
10576
|
+
if (pendingFocus && pendingFocus.cwd === req.cwd) {
|
|
10577
|
+
const captured = pendingFocus;
|
|
10578
|
+
pendingFocus = null;
|
|
10579
|
+
if (Date.now() - captured.requestedAt > FOCUS_TTL_MS) {
|
|
10580
|
+
return { ok: true, data: { focus: null } };
|
|
10581
|
+
}
|
|
10582
|
+
return { ok: true, data: { focus: captured } };
|
|
10583
|
+
}
|
|
10584
|
+
return { ok: true, data: { focus: null } };
|
|
10585
|
+
}
|
|
10658
10586
|
case "cloud-handoff": {
|
|
10659
10587
|
const tracking = sessionTrackingMap.get(req.sessionId);
|
|
10660
10588
|
if (!tracking) return unknownSessionError(req.sessionId);
|
|
@@ -10802,8 +10730,8 @@ async function handleRequest(req) {
|
|
|
10802
10730
|
function startServer() {
|
|
10803
10731
|
return new Promise((resolve13, reject) => {
|
|
10804
10732
|
const sock = socketPath();
|
|
10805
|
-
if (
|
|
10806
|
-
|
|
10733
|
+
if (existsSync27(sock)) {
|
|
10734
|
+
unlinkSync5(sock);
|
|
10807
10735
|
}
|
|
10808
10736
|
server = createServer((conn) => {
|
|
10809
10737
|
let buffer = "";
|
|
@@ -10857,15 +10785,15 @@ function stopServer() {
|
|
|
10857
10785
|
}
|
|
10858
10786
|
server.close(() => {
|
|
10859
10787
|
const sock = socketPath();
|
|
10860
|
-
if (
|
|
10861
|
-
|
|
10788
|
+
if (existsSync27(sock)) {
|
|
10789
|
+
unlinkSync5(sock);
|
|
10862
10790
|
}
|
|
10863
10791
|
server = null;
|
|
10864
10792
|
resolve13();
|
|
10865
10793
|
});
|
|
10866
10794
|
});
|
|
10867
10795
|
}
|
|
10868
|
-
var server, compositor, sessionTrackingMap;
|
|
10796
|
+
var server, compositor, sessionTrackingMap, pendingFocus, FOCUS_TTL_MS;
|
|
10869
10797
|
var init_server = __esm({
|
|
10870
10798
|
"src/daemon/server.ts"() {
|
|
10871
10799
|
"use strict";
|
|
@@ -10889,6 +10817,8 @@ var init_server = __esm({
|
|
|
10889
10817
|
server = null;
|
|
10890
10818
|
compositor = null;
|
|
10891
10819
|
sessionTrackingMap = /* @__PURE__ */ new Map();
|
|
10820
|
+
pendingFocus = null;
|
|
10821
|
+
FOCUS_TTL_MS = 3e4;
|
|
10892
10822
|
}
|
|
10893
10823
|
});
|
|
10894
10824
|
|
|
@@ -10897,7 +10827,7 @@ init_paths();
|
|
|
10897
10827
|
init_config();
|
|
10898
10828
|
init_server();
|
|
10899
10829
|
init_orphan_sweep();
|
|
10900
|
-
import { mkdirSync as mkdirSync15, readFileSync as readFileSync30, writeFileSync as writeFileSync17, unlinkSync as
|
|
10830
|
+
import { mkdirSync as mkdirSync15, readFileSync as readFileSync30, writeFileSync as writeFileSync17, unlinkSync as unlinkSync7, existsSync as existsSync32 } from "fs";
|
|
10901
10831
|
import { execSync as execSync8 } from "child_process";
|
|
10902
10832
|
import { setTimeout as sleep } from "timers/promises";
|
|
10903
10833
|
import { Command } from "commander";
|
|
@@ -10907,8 +10837,8 @@ init_ask_store();
|
|
|
10907
10837
|
init_state();
|
|
10908
10838
|
init_server();
|
|
10909
10839
|
init_paths();
|
|
10910
|
-
import { ulid as
|
|
10911
|
-
import { existsSync as
|
|
10840
|
+
import { ulid as ulid2 } from "ulid";
|
|
10841
|
+
import { existsSync as existsSync28 } from "fs";
|
|
10912
10842
|
var HEARTBEAT_ASKED_BY2 = "system:heartbeat";
|
|
10913
10843
|
var HEARTBEAT_THRESHOLD_MS = 60 * 60 * 1e3;
|
|
10914
10844
|
var HEARTBEAT_SCAN_INTERVAL_MS = 15 * 60 * 1e3;
|
|
@@ -10951,7 +10881,7 @@ async function emitHeartbeatAsk(cwd, sessionId, original) {
|
|
|
10951
10881
|
},
|
|
10952
10882
|
interactions: [interaction]
|
|
10953
10883
|
};
|
|
10954
|
-
const askId =
|
|
10884
|
+
const askId = ulid2();
|
|
10955
10885
|
createAsk(cwd, sessionId, {
|
|
10956
10886
|
askId,
|
|
10957
10887
|
askedBy: HEARTBEAT_ASKED_BY2,
|
|
@@ -10967,43 +10897,14 @@ async function emitHeartbeatAsk(cwd, sessionId, original) {
|
|
|
10967
10897
|
heartbeatAskId: askId
|
|
10968
10898
|
});
|
|
10969
10899
|
}
|
|
10970
|
-
function isModeGateStale(deck, currentMode) {
|
|
10971
|
-
const source = deck.source;
|
|
10972
|
-
const chain = source?.modeChain;
|
|
10973
|
-
if (!chain || chain.length === 0) return false;
|
|
10974
|
-
if (!currentMode) return false;
|
|
10975
|
-
return !chain.some((e) => e.mode === currentMode);
|
|
10976
|
-
}
|
|
10977
10900
|
async function scanSessionForStaleAsks(cwd, sessionId) {
|
|
10978
10901
|
const now = Date.now();
|
|
10979
|
-
let currentMode;
|
|
10980
|
-
try {
|
|
10981
|
-
const session = getSession(cwd, sessionId);
|
|
10982
|
-
currentMode = session.orchestratorCycles[session.orchestratorCycles.length - 1]?.mode;
|
|
10983
|
-
} catch {
|
|
10984
|
-
}
|
|
10985
10902
|
for (const askId of listAsks(cwd, sessionId)) {
|
|
10986
10903
|
try {
|
|
10987
10904
|
const meta = readMeta(cwd, sessionId, askId);
|
|
10988
10905
|
if (!meta) continue;
|
|
10989
10906
|
if (meta.status === "answered") continue;
|
|
10990
10907
|
if (meta.orphaned) continue;
|
|
10991
|
-
if (meta.modeTransition === true) {
|
|
10992
|
-
const deck = readDecisions(cwd, sessionId, askId);
|
|
10993
|
-
if (deck && isModeGateStale(deck, currentMode)) {
|
|
10994
|
-
writeOutput(cwd, sessionId, askId, [{
|
|
10995
|
-
id: "mode-transition",
|
|
10996
|
-
selectedOptionId: "ack",
|
|
10997
|
-
freetext: "auto-resolved: session advanced past mode-transition"
|
|
10998
|
-
}]);
|
|
10999
|
-
await updateMeta(cwd, sessionId, askId, {
|
|
11000
|
-
status: "answered",
|
|
11001
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11002
|
-
});
|
|
11003
|
-
console.log(`[sisyphus] mode-gate auto-resolved ask ${askId} for session ${sessionId} (currentMode: ${currentMode})`);
|
|
11004
|
-
}
|
|
11005
|
-
continue;
|
|
11006
|
-
}
|
|
11007
10908
|
if (meta.heartbeatNotifiedAt) continue;
|
|
11008
10909
|
if (meta.askedBy === HEARTBEAT_ASKED_BY2) continue;
|
|
11009
10910
|
const askedAtMs = new Date(meta.askedAt).getTime();
|
|
@@ -11021,7 +10922,7 @@ async function scanSessionForStaleAsks(cwd, sessionId) {
|
|
|
11021
10922
|
async function scanAllSessionsForStaleAsks() {
|
|
11022
10923
|
const reg = loadSessionRegistry();
|
|
11023
10924
|
for (const [sessionId, cwd] of Object.entries(reg)) {
|
|
11024
|
-
if (!
|
|
10925
|
+
if (!existsSync28(statePath(cwd, sessionId))) continue;
|
|
11025
10926
|
try {
|
|
11026
10927
|
await scanSessionForStaleAsks(cwd, sessionId);
|
|
11027
10928
|
} catch (err) {
|
|
@@ -11084,14 +10985,14 @@ var DEFAULT_STATUS_BAR_CONFIG = {
|
|
|
11084
10985
|
init_tmux();
|
|
11085
10986
|
init_status_dots();
|
|
11086
10987
|
init_companion();
|
|
11087
|
-
import { readFileSync as readFileSync27, existsSync as
|
|
10988
|
+
import { readFileSync as readFileSync27, existsSync as existsSync29 } from "fs";
|
|
11088
10989
|
import { homedir as homedir8 } from "os";
|
|
11089
10990
|
import { join as join22 } from "path";
|
|
11090
10991
|
var STATUS_BAR_BG = "#1d1e21";
|
|
11091
10992
|
var SESSION_ORDER_PATH = join22(homedir8(), ".config", "tmux", "session-order");
|
|
11092
10993
|
function getSessionOrder() {
|
|
11093
10994
|
try {
|
|
11094
|
-
if (!
|
|
10995
|
+
if (!existsSync29(SESSION_ORDER_PATH)) return [];
|
|
11095
10996
|
return readFileSync27(SESSION_ORDER_PATH, "utf-8").split("\n").filter(Boolean);
|
|
11096
10997
|
} catch {
|
|
11097
10998
|
return [];
|
|
@@ -11644,6 +11545,7 @@ function writeEmptyManifest() {
|
|
|
11644
11545
|
|
|
11645
11546
|
// src/daemon/index.ts
|
|
11646
11547
|
init_agent();
|
|
11548
|
+
init_ask_store();
|
|
11647
11549
|
init_orphan_asks();
|
|
11648
11550
|
init_process();
|
|
11649
11551
|
init_orchestrator();
|
|
@@ -11655,7 +11557,7 @@ init_state();
|
|
|
11655
11557
|
init_paths();
|
|
11656
11558
|
init_version();
|
|
11657
11559
|
import { execSync as execSync7 } from "child_process";
|
|
11658
|
-
import { writeFileSync as writeFileSync16, unlinkSync as
|
|
11560
|
+
import { writeFileSync as writeFileSync16, unlinkSync as unlinkSync6, lstatSync as lstatSync2 } from "fs";
|
|
11659
11561
|
import { resolve as resolve11 } from "path";
|
|
11660
11562
|
import { get } from "https";
|
|
11661
11563
|
function isNewer(latest, current) {
|
|
@@ -11732,7 +11634,7 @@ function markUpdating(version) {
|
|
|
11732
11634
|
}
|
|
11733
11635
|
function clearUpdating() {
|
|
11734
11636
|
try {
|
|
11735
|
-
|
|
11637
|
+
unlinkSync6(daemonUpdatingPath());
|
|
11736
11638
|
} catch {
|
|
11737
11639
|
}
|
|
11738
11640
|
}
|
|
@@ -11782,7 +11684,7 @@ function stopPeriodicUpdateCheck() {
|
|
|
11782
11684
|
}
|
|
11783
11685
|
|
|
11784
11686
|
// src/daemon/plugin-install.ts
|
|
11785
|
-
import { copyFileSync as copyFileSync6, mkdirSync as mkdirSync14, readdirSync as readdirSync13, statSync as statSync4, existsSync as
|
|
11687
|
+
import { copyFileSync as copyFileSync6, mkdirSync as mkdirSync14, readdirSync as readdirSync13, statSync as statSync4, existsSync as existsSync30, readFileSync as readFileSync28, chmodSync as chmodSync3 } from "fs";
|
|
11786
11688
|
import { join as join23, resolve as resolve12 } from "path";
|
|
11787
11689
|
import { homedir as homedir9 } from "os";
|
|
11788
11690
|
var PLUGIN_NAME = "sisyphus-tmux";
|
|
@@ -11796,7 +11698,7 @@ function copyDir(src, dest) {
|
|
|
11796
11698
|
copyDir(srcPath, destPath);
|
|
11797
11699
|
} else {
|
|
11798
11700
|
const srcMtime = statSync4(srcPath).mtimeMs;
|
|
11799
|
-
const destMtime =
|
|
11701
|
+
const destMtime = existsSync30(destPath) ? statSync4(destPath).mtimeMs : 0;
|
|
11800
11702
|
if (srcMtime > destMtime) {
|
|
11801
11703
|
copyFileSync6(srcPath, destPath);
|
|
11802
11704
|
}
|
|
@@ -11806,7 +11708,7 @@ function copyDir(src, dest) {
|
|
|
11806
11708
|
function pluginNeedsUpdate(sourceDir) {
|
|
11807
11709
|
const srcHooks = join23(sourceDir, "hooks", "hooks.json");
|
|
11808
11710
|
const destHooks = join23(INSTALL_DIR, "hooks", "hooks.json");
|
|
11809
|
-
if (!
|
|
11711
|
+
if (!existsSync30(destHooks)) return true;
|
|
11810
11712
|
try {
|
|
11811
11713
|
return readFileSync28(srcHooks, "utf-8") !== readFileSync28(destHooks, "utf-8");
|
|
11812
11714
|
} catch {
|
|
@@ -11815,7 +11717,7 @@ function pluginNeedsUpdate(sourceDir) {
|
|
|
11815
11717
|
}
|
|
11816
11718
|
function installPlugin() {
|
|
11817
11719
|
const sourceDir = resolve12(import.meta.dirname, "../templates/sisyphus-tmux-plugin");
|
|
11818
|
-
if (!
|
|
11720
|
+
if (!existsSync30(sourceDir)) {
|
|
11819
11721
|
console.error(`[plugin-install] Source dir not found: ${sourceDir}`);
|
|
11820
11722
|
return;
|
|
11821
11723
|
}
|
|
@@ -11823,7 +11725,7 @@ function installPlugin() {
|
|
|
11823
11725
|
try {
|
|
11824
11726
|
copyDir(sourceDir, INSTALL_DIR);
|
|
11825
11727
|
const hookScript = join23(INSTALL_DIR, "hooks", "tmux-state.sh");
|
|
11826
|
-
if (
|
|
11728
|
+
if (existsSync30(hookScript)) {
|
|
11827
11729
|
try {
|
|
11828
11730
|
chmodSync3(hookScript, 493);
|
|
11829
11731
|
} catch {
|
|
@@ -11879,7 +11781,7 @@ init_upload();
|
|
|
11879
11781
|
init_version();
|
|
11880
11782
|
init_state();
|
|
11881
11783
|
init_uploader();
|
|
11882
|
-
import { existsSync as
|
|
11784
|
+
import { existsSync as existsSync31, readdirSync as readdirSync14, readFileSync as readFileSync29 } from "fs";
|
|
11883
11785
|
var backfillStarted = false;
|
|
11884
11786
|
async function backfillUploads() {
|
|
11885
11787
|
if (backfillStarted) return;
|
|
@@ -11912,7 +11814,7 @@ async function backfillUploads() {
|
|
|
11912
11814
|
let failed = 0;
|
|
11913
11815
|
let unexportable = 0;
|
|
11914
11816
|
for (const { sessionId, cwd } of candidates) {
|
|
11915
|
-
if (!
|
|
11817
|
+
if (!existsSync31(statePath(cwd, sessionId))) {
|
|
11916
11818
|
unexportable++;
|
|
11917
11819
|
continue;
|
|
11918
11820
|
}
|
|
@@ -11990,7 +11892,7 @@ function isLaunchdManaged() {
|
|
|
11990
11892
|
}
|
|
11991
11893
|
function releasePidLock() {
|
|
11992
11894
|
try {
|
|
11993
|
-
|
|
11895
|
+
unlinkSync7(daemonPidPath());
|
|
11994
11896
|
} catch {
|
|
11995
11897
|
}
|
|
11996
11898
|
}
|
|
@@ -12029,7 +11931,7 @@ function stopDaemon() {
|
|
|
12029
11931
|
}
|
|
12030
11932
|
async function recoverOneSession(sessionId, cwd, tmuxIdSet, tmuxNameToId, panesByWindow) {
|
|
12031
11933
|
const stateFile = statePath(cwd, sessionId);
|
|
12032
|
-
if (!
|
|
11934
|
+
if (!existsSync32(stateFile)) return false;
|
|
12033
11935
|
let session;
|
|
12034
11936
|
try {
|
|
12035
11937
|
session = JSON.parse(readFileSync30(stateFile, "utf-8"));
|
|
@@ -12040,6 +11942,10 @@ async function recoverOneSession(sessionId, cwd, tmuxIdSet, tmuxNameToId, panesB
|
|
|
12040
11942
|
if (session.status !== "active" && session.status !== "paused") return false;
|
|
12041
11943
|
registerSessionCwd(sessionId, cwd);
|
|
12042
11944
|
resetAgentCounterFromState(sessionId, session.agents ?? []);
|
|
11945
|
+
const clearedClaims = await clearStaleAskClaims(cwd, sessionId);
|
|
11946
|
+
if (clearedClaims > 0) {
|
|
11947
|
+
console.log(`[sisyphus] Cleared ${clearedClaims} stale ask claim(s) for session ${sessionId} on recovery`);
|
|
11948
|
+
}
|
|
12043
11949
|
if (!session.tmuxSessionName) return true;
|
|
12044
11950
|
let sessionAlive = false;
|
|
12045
11951
|
let currentTmuxId = session.tmuxSessionId;
|