sisyphi 1.2.18 → 1.2.20
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 +673 -439
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +225 -287
- package/dist/daemon.js.map +1 -1
- package/dist/templates/agent-plugin/agents/review/CLAUDE.md +0 -1
- package/dist/templates/agent-plugin/agents/spec.md +18 -7
- package/dist/templates/agent-suffix.md +9 -1
- package/dist/templates/orchestrator-base.md +13 -0
- package/dist/templates/orchestrator-plugin/hooks/goal-length-guard.sh +60 -0
- package/dist/templates/orchestrator-plugin/hooks/goal-read-advisory.sh +54 -0
- package/dist/templates/orchestrator-plugin/hooks/hooks.json +20 -0
- package/dist/tui.js +98 -79
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
- package/templates/agent-plugin/agents/review/CLAUDE.md +0 -1
- package/templates/agent-plugin/agents/spec.md +18 -7
- package/templates/agent-suffix.md +9 -1
- package/templates/orchestrator-base.md +13 -0
- package/templates/orchestrator-plugin/hooks/goal-length-guard.sh +60 -0
- package/templates/orchestrator-plugin/hooks/goal-read-advisory.sh +54 -0
- package/templates/orchestrator-plugin/hooks/hooks.json +20 -0
package/dist/daemon.js
CHANGED
|
@@ -1058,6 +1058,31 @@ var init_spawn_helpers = __esm({
|
|
|
1058
1058
|
}
|
|
1059
1059
|
});
|
|
1060
1060
|
|
|
1061
|
+
// src/cli/help-rubric.ts
|
|
1062
|
+
var ROOT_AFTER_HELP;
|
|
1063
|
+
var init_help_rubric = __esm({
|
|
1064
|
+
"src/cli/help-rubric.ts"() {
|
|
1065
|
+
"use strict";
|
|
1066
|
+
ROOT_AFTER_HELP = `
|
|
1067
|
+
I/O contract: flags and positional args on input, JSON on stdout (JSONL for streams).
|
|
1068
|
+
|
|
1069
|
+
Exit codes:
|
|
1070
|
+
0 success
|
|
1071
|
+
1 permanent error (fallback)
|
|
1072
|
+
2 usage error (bad args/shape)
|
|
1073
|
+
3 not found
|
|
1074
|
+
4 ambiguous (multiple matches \u2014 see error.candidates)
|
|
1075
|
+
5 conflict (already-exists, wrong-state)
|
|
1076
|
+
60 transient (retry-safe: daemon down, timeout, lock contention)
|
|
1077
|
+
|
|
1078
|
+
Errors:
|
|
1079
|
+
{"ok": false,
|
|
1080
|
+
"error": {"code": "<stable-enum>", "kind": "<usage|not_found|ambiguous|conflict|transient|permanent>",
|
|
1081
|
+
"message": "...", "received"?: ..., "expected"?: ..., "next"?: "...", "candidates"?: [...]}}
|
|
1082
|
+
`;
|
|
1083
|
+
}
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1061
1086
|
// src/daemon/help-inject.ts
|
|
1062
1087
|
import { execSync } from "child_process";
|
|
1063
1088
|
function injectHelp(text) {
|
|
@@ -1078,6 +1103,12 @@ function injectHelp(text) {
|
|
|
1078
1103
|
};
|
|
1079
1104
|
return text.replace(/\{\{HELP:([^}]+)\}\}/g, (_m, cmd) => {
|
|
1080
1105
|
const c = cmd.trim();
|
|
1106
|
+
if (c === ".") {
|
|
1107
|
+
const root = renderHelp("").replace(ROOT_AFTER_HELP.trim(), "").trimEnd();
|
|
1108
|
+
return `<cli-guide bash="sis -h">
|
|
1109
|
+
${root}
|
|
1110
|
+
</cli-guide>`;
|
|
1111
|
+
}
|
|
1081
1112
|
return `<cli-guide bash="sis ${c} -h">
|
|
1082
1113
|
${renderHelp(c)}
|
|
1083
1114
|
</cli-guide>`;
|
|
@@ -1087,6 +1118,7 @@ var init_help_inject = __esm({
|
|
|
1087
1118
|
"src/daemon/help-inject.ts"() {
|
|
1088
1119
|
"use strict";
|
|
1089
1120
|
init_spawn_helpers();
|
|
1121
|
+
init_help_rubric();
|
|
1090
1122
|
}
|
|
1091
1123
|
});
|
|
1092
1124
|
|
|
@@ -2472,7 +2504,7 @@ var init_history = __esm({
|
|
|
2472
2504
|
});
|
|
2473
2505
|
|
|
2474
2506
|
// src/shared/platform.ts
|
|
2475
|
-
import { execSync as execSync4 } from "child_process";
|
|
2507
|
+
import { execFileSync as execFileSync2, execSync as execSync4 } from "child_process";
|
|
2476
2508
|
import { existsSync as existsSync9, readFileSync as readFileSync11 } from "fs";
|
|
2477
2509
|
function detectPlatform() {
|
|
2478
2510
|
if (cachedPlatform) return cachedPlatform;
|
|
@@ -2662,13 +2694,14 @@ var init_notify = __esm({
|
|
|
2662
2694
|
});
|
|
2663
2695
|
|
|
2664
2696
|
// src/daemon/ask-store.ts
|
|
2665
|
-
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";
|
|
2666
2698
|
import { basename as basename4 } from "path";
|
|
2667
|
-
function maybeNotifyOnAskCreated(cwd, sessionId, meta) {
|
|
2699
|
+
function maybeNotifyOnAskCreated(cwd, sessionId, meta, suppress = false) {
|
|
2668
2700
|
if (process.env.NODE_ENV === "test" || process.env.SISYPHUS_DISABLE_NOTIFY === "1") return;
|
|
2669
2701
|
const isActionable = meta.kind !== void 0 && ACTIONABLE_KINDS.has(meta.kind);
|
|
2670
2702
|
const isHeartbeat = meta.askedBy === HEARTBEAT_ASKED_BY;
|
|
2671
2703
|
if (!isActionable && !isHeartbeat) return;
|
|
2704
|
+
if (suppress) return;
|
|
2672
2705
|
try {
|
|
2673
2706
|
const config = loadConfig(cwd);
|
|
2674
2707
|
if (config.notifications?.enabled === false) return;
|
|
@@ -2694,8 +2727,7 @@ function createAsk(cwd, sessionId, params) {
|
|
|
2694
2727
|
...params.title !== void 0 ? { title: params.title } : {},
|
|
2695
2728
|
...params.subtitle !== void 0 ? { subtitle: params.subtitle } : {},
|
|
2696
2729
|
...params.kind !== void 0 ? { kind: params.kind } : {},
|
|
2697
|
-
...params.orphanTarget !== void 0 ? { orphanTarget: params.orphanTarget } : {}
|
|
2698
|
-
...params.modeTransition !== void 0 ? { modeTransition: params.modeTransition } : {}
|
|
2730
|
+
...params.orphanTarget !== void 0 ? { orphanTarget: params.orphanTarget } : {}
|
|
2699
2731
|
};
|
|
2700
2732
|
atomicWrite(askMetaPath(cwd, sessionId, params.askId), JSON.stringify(meta, null, 2));
|
|
2701
2733
|
emitHistoryEvent(sessionId, "ask-issued", {
|
|
@@ -2704,7 +2736,7 @@ function createAsk(cwd, sessionId, params) {
|
|
|
2704
2736
|
blocking: params.blocking,
|
|
2705
2737
|
askedAt
|
|
2706
2738
|
});
|
|
2707
|
-
maybeNotifyOnAskCreated(cwd, sessionId, meta);
|
|
2739
|
+
maybeNotifyOnAskCreated(cwd, sessionId, meta, params.suppressTerminalNotification === true);
|
|
2708
2740
|
return meta;
|
|
2709
2741
|
}
|
|
2710
2742
|
function writeDecisions(cwd, sessionId, askId, deck) {
|
|
@@ -2839,6 +2871,26 @@ async function maybeAutoResolveAsk(cwd, sessionId, askId, deck) {
|
|
|
2839
2871
|
} catch {
|
|
2840
2872
|
}
|
|
2841
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
|
+
}
|
|
2842
2894
|
function listOpenAsksFor(cwd, sessionId, askedBy) {
|
|
2843
2895
|
const out = [];
|
|
2844
2896
|
for (const askId of listAsks(cwd, sessionId)) {
|
|
@@ -3261,7 +3313,7 @@ var init_orphan_sweep = __esm({
|
|
|
3261
3313
|
});
|
|
3262
3314
|
|
|
3263
3315
|
// src/daemon/agent.ts
|
|
3264
|
-
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";
|
|
3265
3317
|
import { execSync as execSync5 } from "child_process";
|
|
3266
3318
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3267
3319
|
import { resolve as resolve5, relative as relative2, dirname as dirname3, join as join12 } from "path";
|
|
@@ -3628,7 +3680,7 @@ function gcBgTasks(cwd, sessionId, agentId) {
|
|
|
3628
3680
|
console.warn(`[bg-tasks] ${agentId} exited with ${leftover.length} untracked background task(s): ${leftover.join(", ")}`);
|
|
3629
3681
|
emitHistoryEvent(sessionId, "bg-tasks-leftover", { agentId, leftover });
|
|
3630
3682
|
}
|
|
3631
|
-
|
|
3683
|
+
unlinkSync2(file);
|
|
3632
3684
|
} catch (err) {
|
|
3633
3685
|
console.warn(`[bg-tasks] ${agentId} cleanup failed:`, err instanceof Error ? err.message : err);
|
|
3634
3686
|
}
|
|
@@ -5108,13 +5160,12 @@ function buildPersonality(stats) {
|
|
|
5108
5160
|
function shouldGenerateCommentary(event) {
|
|
5109
5161
|
switch (event) {
|
|
5110
5162
|
case "session-start":
|
|
5163
|
+
case "mode-transition":
|
|
5111
5164
|
case "session-complete":
|
|
5112
5165
|
case "level-up":
|
|
5113
5166
|
case "achievement":
|
|
5114
5167
|
case "late-night":
|
|
5115
5168
|
return true;
|
|
5116
|
-
case "cycle-boundary":
|
|
5117
|
-
return Math.random() < 0.5;
|
|
5118
5169
|
case "idle-wake":
|
|
5119
5170
|
return Math.random() < 0.5;
|
|
5120
5171
|
case "agent-crash":
|
|
@@ -5306,18 +5357,17 @@ var init_companion_commentary = __esm({
|
|
|
5306
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." },
|
|
5307
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." },
|
|
5308
5359
|
{ event: "late-night", mood: "existential", context: "3:14am, 2 sessions active", output: "Something about 3am makes every function look like a confession." },
|
|
5309
|
-
{ 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." },
|
|
5310
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."' },
|
|
5311
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." },
|
|
5312
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." },
|
|
5313
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." },
|
|
5314
|
-
{ 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." },
|
|
5315
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." },
|
|
5316
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." },
|
|
5317
5368
|
{ event: "agent-crash", mood: "zen", context: "agent-001 crashed during linting", output: "Lost one to the linter. Not the worst way to go." },
|
|
5318
5369
|
{ event: "idle-wake", mood: "grinding", context: "Idle for 2 hours", output: "Two hours away and nothing changed. Exactly as expected, exactly as disappointing." },
|
|
5319
5370
|
{ event: "session-start", mood: "happy", context: "add dark mode", output: "Dark mode. Finally a task that matches the terminal aesthetic." },
|
|
5320
|
-
{ 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." },
|
|
5321
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." },
|
|
5322
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." },
|
|
5323
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." },
|
|
@@ -6143,7 +6193,7 @@ var init_companion_render = __esm({
|
|
|
6143
6193
|
});
|
|
6144
6194
|
|
|
6145
6195
|
// src/daemon/companion-popup.ts
|
|
6146
|
-
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";
|
|
6147
6197
|
import { tmpdir } from "os";
|
|
6148
6198
|
import { join as join15, resolve as resolve6 } from "path";
|
|
6149
6199
|
function wrapText(text, width) {
|
|
@@ -6236,7 +6286,7 @@ fi
|
|
|
6236
6286
|
`;
|
|
6237
6287
|
writeFileSync12(POPUP_SCRIPT, script, { mode: 493 });
|
|
6238
6288
|
try {
|
|
6239
|
-
|
|
6289
|
+
unlinkSync3(POPUP_RESULT_PREFIX);
|
|
6240
6290
|
} catch {
|
|
6241
6291
|
}
|
|
6242
6292
|
const clientsRaw = execSafe('tmux list-clients -F "#{client_name} #{client_width}"');
|
|
@@ -6246,14 +6296,14 @@ fi
|
|
|
6246
6296
|
const client = line.slice(0, lastSpace);
|
|
6247
6297
|
const clientWidth = parseInt(line.slice(lastSpace + 1), 10);
|
|
6248
6298
|
if (!clientWidth) continue;
|
|
6249
|
-
const x = Math.max(0, clientWidth - POPUP_WIDTH);
|
|
6299
|
+
const x = Math.max(0, clientWidth - POPUP_WIDTH - 1);
|
|
6250
6300
|
const args = [
|
|
6251
6301
|
`-c ${shellQuote(client)}`,
|
|
6252
6302
|
"-E -b rounded",
|
|
6253
6303
|
`-T ${shellQuote(initialTitle)}`,
|
|
6254
6304
|
`-S "fg=${moodColor}"`,
|
|
6255
6305
|
`-s "fg=${moodColor}"`,
|
|
6256
|
-
`-x ${x} -y
|
|
6306
|
+
`-x ${x} -y 2`,
|
|
6257
6307
|
`-w ${POPUP_WIDTH} -h ${maxContentHeight}`,
|
|
6258
6308
|
shellQuote(POPUP_SCRIPT)
|
|
6259
6309
|
].join(" ");
|
|
@@ -6266,7 +6316,7 @@ fi
|
|
|
6266
6316
|
return null;
|
|
6267
6317
|
} finally {
|
|
6268
6318
|
try {
|
|
6269
|
-
|
|
6319
|
+
unlinkSync3(POPUP_RESULT_PREFIX);
|
|
6270
6320
|
} catch {
|
|
6271
6321
|
}
|
|
6272
6322
|
}
|
|
@@ -6737,152 +6787,15 @@ var init_pane_monitor = __esm({
|
|
|
6737
6787
|
}
|
|
6738
6788
|
});
|
|
6739
6789
|
|
|
6740
|
-
// src/daemon/mode-notify.ts
|
|
6741
|
-
import { existsSync as existsSync17 } from "fs";
|
|
6742
|
-
import { ulid as ulid2 } from "ulid";
|
|
6743
|
-
function capitalize(s) {
|
|
6744
|
-
return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
|
|
6745
|
-
}
|
|
6746
|
-
function formatDuration(ms) {
|
|
6747
|
-
const sec = Math.round(ms / 1e3);
|
|
6748
|
-
if (sec < 60) return `${sec}s`;
|
|
6749
|
-
const min = Math.round(sec / 60);
|
|
6750
|
-
if (min < 60) return `${min}m`;
|
|
6751
|
-
const h = Math.floor(min / 60);
|
|
6752
|
-
const remM = min % 60;
|
|
6753
|
-
return remM ? `${h}h ${remM}m` : `${h}h`;
|
|
6754
|
-
}
|
|
6755
|
-
function findOpenModeTransitionAsk(cwd, sessionId) {
|
|
6756
|
-
for (const askId of listAsks(cwd, sessionId)) {
|
|
6757
|
-
const meta = readMeta(cwd, sessionId, askId);
|
|
6758
|
-
if (!meta) continue;
|
|
6759
|
-
if (meta.askedBy !== ORCHESTRATOR_ASKED_BY) continue;
|
|
6760
|
-
if (meta.modeTransition !== true) continue;
|
|
6761
|
-
if (meta.status === "answered") continue;
|
|
6762
|
-
if (meta.orphaned === true) continue;
|
|
6763
|
-
if (existsSync17(askOutputPath(cwd, sessionId, askId))) continue;
|
|
6764
|
-
return askId;
|
|
6765
|
-
}
|
|
6766
|
-
return null;
|
|
6767
|
-
}
|
|
6768
|
-
function buildNextChain(prevChain, prevMode, nextMode, prevModeStats) {
|
|
6769
|
-
const stats = prevModeStats ? { cycles: prevModeStats.cycles, activeMs: prevModeStats.activeMs } : {};
|
|
6770
|
-
if (prevChain && prevChain.length > 0) {
|
|
6771
|
-
const updated = prevChain.map(
|
|
6772
|
-
(e, i) => i === prevChain.length - 1 && prevModeStats ? { ...e, ...stats } : e
|
|
6773
|
-
);
|
|
6774
|
-
updated.push({ mode: nextMode });
|
|
6775
|
-
return updated;
|
|
6776
|
-
}
|
|
6777
|
-
if (prevMode !== void 0) {
|
|
6778
|
-
return [{ mode: prevMode, ...stats }, { mode: nextMode }];
|
|
6779
|
-
}
|
|
6780
|
-
return [{ mode: "unknown" }, { mode: nextMode }];
|
|
6781
|
-
}
|
|
6782
|
-
function renderBody(chain, cwd) {
|
|
6783
|
-
const current = chain[chain.length - 1];
|
|
6784
|
-
const description = discoverOrchestratorModes(cwd).find((m) => m.name === current.mode)?.description?.trim();
|
|
6785
|
-
const lines = [];
|
|
6786
|
-
if (description) {
|
|
6787
|
-
lines.push(`**${capitalize(current.mode)}** \u2014 ${description}`);
|
|
6788
|
-
} else {
|
|
6789
|
-
lines.push(`Now in **${capitalize(current.mode)}** mode.`);
|
|
6790
|
-
}
|
|
6791
|
-
for (let i = 0; i < chain.length - 1; i++) {
|
|
6792
|
-
const e = chain[i];
|
|
6793
|
-
if (e.cycles === void 0) continue;
|
|
6794
|
-
const label = e.cycles === 1 ? "cycle" : "cycles";
|
|
6795
|
-
lines.push(
|
|
6796
|
-
`${capitalize(e.mode)}: ${e.cycles} ${label} \xB7 ${formatDuration(e.activeMs ?? 0)} active`
|
|
6797
|
-
);
|
|
6798
|
-
}
|
|
6799
|
-
return lines.join("\n\n");
|
|
6800
|
-
}
|
|
6801
|
-
async function emitModeTransitionNotify(cwd, sessionId, prevMode, nextMode, prevModeStats) {
|
|
6802
|
-
let sessionName;
|
|
6803
|
-
try {
|
|
6804
|
-
sessionName = getSession(cwd, sessionId).name;
|
|
6805
|
-
} catch {
|
|
6806
|
-
}
|
|
6807
|
-
const existingAskId = findOpenModeTransitionAsk(cwd, sessionId);
|
|
6808
|
-
const existingDeck = existingAskId ? readDecisions(cwd, sessionId, existingAskId) : null;
|
|
6809
|
-
const chain = buildNextChain(
|
|
6810
|
-
existingDeck?.source?.modeChain,
|
|
6811
|
-
prevMode,
|
|
6812
|
-
nextMode,
|
|
6813
|
-
prevModeStats
|
|
6814
|
-
);
|
|
6815
|
-
const subtitle = chain.map((e) => e.mode).join(" \u2192 ");
|
|
6816
|
-
const title = "Mode change";
|
|
6817
|
-
const deckTitle = `Mode: ${subtitle}`;
|
|
6818
|
-
const body = renderBody(chain, cwd);
|
|
6819
|
-
const interaction = {
|
|
6820
|
-
id: "mode-transition",
|
|
6821
|
-
title,
|
|
6822
|
-
subtitle,
|
|
6823
|
-
body,
|
|
6824
|
-
kind: "notify",
|
|
6825
|
-
options: [{ id: "ack", label: "Acknowledged" }]
|
|
6826
|
-
};
|
|
6827
|
-
const deckSource = {
|
|
6828
|
-
...sessionName !== void 0 ? { sessionName } : {},
|
|
6829
|
-
askedBy: ORCHESTRATOR_ASKED_BY,
|
|
6830
|
-
modeChain: chain
|
|
6831
|
-
};
|
|
6832
|
-
const deck = {
|
|
6833
|
-
title: deckTitle,
|
|
6834
|
-
source: deckSource,
|
|
6835
|
-
interactions: [interaction]
|
|
6836
|
-
};
|
|
6837
|
-
try {
|
|
6838
|
-
if (existingAskId) {
|
|
6839
|
-
writeDecisions(cwd, sessionId, existingAskId, deck);
|
|
6840
|
-
await updateMeta(cwd, sessionId, existingAskId, {
|
|
6841
|
-
title,
|
|
6842
|
-
subtitle,
|
|
6843
|
-
askedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6844
|
-
});
|
|
6845
|
-
return;
|
|
6846
|
-
}
|
|
6847
|
-
const askId = ulid2();
|
|
6848
|
-
createAsk(cwd, sessionId, {
|
|
6849
|
-
askId,
|
|
6850
|
-
askedBy: ORCHESTRATOR_ASKED_BY,
|
|
6851
|
-
blocking: false,
|
|
6852
|
-
cwd,
|
|
6853
|
-
title,
|
|
6854
|
-
subtitle,
|
|
6855
|
-
kind: "notify",
|
|
6856
|
-
modeTransition: true
|
|
6857
|
-
});
|
|
6858
|
-
writeDecisions(cwd, sessionId, askId, deck);
|
|
6859
|
-
} catch (err) {
|
|
6860
|
-
console.warn(
|
|
6861
|
-
`[sisyphus] mode-notify: failed to emit mode transition ask for ${sessionId}:`,
|
|
6862
|
-
err instanceof Error ? err.message : err
|
|
6863
|
-
);
|
|
6864
|
-
}
|
|
6865
|
-
}
|
|
6866
|
-
var init_mode_notify = __esm({
|
|
6867
|
-
"src/daemon/mode-notify.ts"() {
|
|
6868
|
-
"use strict";
|
|
6869
|
-
init_ask_store();
|
|
6870
|
-
init_state();
|
|
6871
|
-
init_orchestrator_modes();
|
|
6872
|
-
init_paths();
|
|
6873
|
-
init_types();
|
|
6874
|
-
}
|
|
6875
|
-
});
|
|
6876
|
-
|
|
6877
6790
|
// src/daemon/orchestrator.ts
|
|
6878
|
-
import { existsSync as
|
|
6791
|
+
import { existsSync as existsSync17, readdirSync as readdirSync10, readFileSync as readFileSync17, writeFileSync as writeFileSync13 } from "fs";
|
|
6879
6792
|
import { execSync as execSync6 } from "child_process";
|
|
6880
6793
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
6881
6794
|
import { resolve as resolve7, join as join16, relative as relative3 } from "path";
|
|
6882
6795
|
function detectRepos(cwd) {
|
|
6883
6796
|
const config = loadConfig(cwd);
|
|
6884
6797
|
const repos = [];
|
|
6885
|
-
if (
|
|
6798
|
+
if (existsSync17(join16(cwd, ".git"))) {
|
|
6886
6799
|
try {
|
|
6887
6800
|
repos.push(getRepoInfo(cwd, "."));
|
|
6888
6801
|
} catch {
|
|
@@ -6894,7 +6807,7 @@ function detectRepos(cwd) {
|
|
|
6894
6807
|
if (!entry.isDirectory()) continue;
|
|
6895
6808
|
if (entry.name.startsWith(".")) continue;
|
|
6896
6809
|
const childPath = join16(cwd, entry.name);
|
|
6897
|
-
if (
|
|
6810
|
+
if (existsSync17(join16(childPath, ".git"))) {
|
|
6898
6811
|
try {
|
|
6899
6812
|
repos.push(getRepoInfo(childPath, entry.name));
|
|
6900
6813
|
} catch {
|
|
@@ -6932,13 +6845,13 @@ function resolveOrchestratorSettings(cwd, sessionId) {
|
|
|
6932
6845
|
const bundled = resolve7(import.meta.dirname, "../templates/orchestrator-settings.json");
|
|
6933
6846
|
const projectSettings = projectOrchestratorSettingsPath(cwd);
|
|
6934
6847
|
const userSettings = userOrchestratorSettingsPath();
|
|
6935
|
-
const hasProject =
|
|
6936
|
-
const hasUser =
|
|
6848
|
+
const hasProject = existsSync17(projectSettings);
|
|
6849
|
+
const hasUser = existsSync17(userSettings);
|
|
6937
6850
|
const digestVerbs = digestSpinnerVerbs(cwd, sessionId);
|
|
6938
6851
|
if (!hasProject && !hasUser && !digestVerbs) return bundled;
|
|
6939
6852
|
let merged = {};
|
|
6940
6853
|
for (const path of [bundled, hasUser ? userSettings : null, hasProject ? projectSettings : null]) {
|
|
6941
|
-
if (!path || !
|
|
6854
|
+
if (!path || !existsSync17(path)) continue;
|
|
6942
6855
|
try {
|
|
6943
6856
|
const parsed = JSON.parse(readFileSync17(path, "utf-8"));
|
|
6944
6857
|
merged = { ...merged, ...parsed };
|
|
@@ -6955,11 +6868,11 @@ function resolveOrchestratorSettings(cwd, sessionId) {
|
|
|
6955
6868
|
}
|
|
6956
6869
|
function loadOrchestratorPrompt(cwd, sessionId, mode) {
|
|
6957
6870
|
const projectPath = projectOrchestratorPromptPath(cwd);
|
|
6958
|
-
if (
|
|
6871
|
+
if (existsSync17(projectPath)) {
|
|
6959
6872
|
return readFileSync17(projectPath, "utf-8");
|
|
6960
6873
|
}
|
|
6961
6874
|
const userPath = userOrchestratorPromptPath();
|
|
6962
|
-
if (
|
|
6875
|
+
if (existsSync17(userPath)) {
|
|
6963
6876
|
return readFileSync17(userPath, "utf-8");
|
|
6964
6877
|
}
|
|
6965
6878
|
const basePath = resolve7(import.meta.dirname, "../templates/orchestrator-base.md");
|
|
@@ -6987,7 +6900,7 @@ function buildCompletionContent(session) {
|
|
|
6987
6900
|
lines.push("");
|
|
6988
6901
|
}
|
|
6989
6902
|
const logsDirPath = logsDir(session.cwd, session.id);
|
|
6990
|
-
if (
|
|
6903
|
+
if (existsSync17(logsDirPath)) {
|
|
6991
6904
|
const logFiles = readdirSync10(logsDirPath).filter((f) => f.startsWith("cycle-") && f.endsWith(".md")).sort();
|
|
6992
6905
|
if (logFiles.length > 0) {
|
|
6993
6906
|
lines.push("### Cycle Logs\n");
|
|
@@ -7039,7 +6952,7 @@ function buildCompletionContent(session) {
|
|
|
7039
6952
|
}
|
|
7040
6953
|
}
|
|
7041
6954
|
const reportsDirPath = reportsDir(session.cwd, session.id);
|
|
7042
|
-
if (
|
|
6955
|
+
if (existsSync17(reportsDirPath)) {
|
|
7043
6956
|
const reportFiles = readdirSync10(reportsDirPath).filter((f) => f.endsWith(".md"));
|
|
7044
6957
|
if (reportFiles.length > 0) {
|
|
7045
6958
|
lines.push("### Detailed Reports\n");
|
|
@@ -7065,7 +6978,7 @@ ${session.context}
|
|
|
7065
6978
|
}
|
|
7066
6979
|
} else {
|
|
7067
6980
|
let ctxFiles = [];
|
|
7068
|
-
if (
|
|
6981
|
+
if (existsSync17(ctxDir)) {
|
|
7069
6982
|
ctxFiles = readdirSync10(ctxDir).filter((f) => f !== "CLAUDE.md");
|
|
7070
6983
|
}
|
|
7071
6984
|
if (ctxFiles.length > 0) {
|
|
@@ -7107,10 +7020,10 @@ ${agentLines}
|
|
|
7107
7020
|
}
|
|
7108
7021
|
}
|
|
7109
7022
|
const strategyFile = strategyPath(session.cwd, session.id);
|
|
7110
|
-
const strategyRef =
|
|
7111
|
-
const roadmapRef =
|
|
7023
|
+
const strategyRef = existsSync17(strategyFile) ? `@${relative3(session.cwd, strategyFile)}` : "(empty)";
|
|
7024
|
+
const roadmapRef = existsSync17(roadmapFile) ? `@${relative3(session.cwd, roadmapFile)}` : "(empty)";
|
|
7112
7025
|
const digestFile = digestPath(session.cwd, session.id);
|
|
7113
|
-
const digestRef =
|
|
7026
|
+
const digestRef = existsSync17(digestFile) ? `@${relative3(session.cwd, digestFile)}` : "(not yet created)";
|
|
7114
7027
|
const repos = detectRepos(session.cwd);
|
|
7115
7028
|
let repositoriesSection = "\n\n## Repositories\n";
|
|
7116
7029
|
if (repos.length === 0) {
|
|
@@ -7137,7 +7050,7 @@ ${agentLines}
|
|
|
7137
7050
|
}
|
|
7138
7051
|
}
|
|
7139
7052
|
const goalFile = goalPath(session.cwd, session.id);
|
|
7140
|
-
const goalContent =
|
|
7053
|
+
const goalContent = existsSync17(goalFile) ? readFileSync17(goalFile, "utf-8").trim() : session.task;
|
|
7141
7054
|
const modeContent = modeContentBuilders[mode]?.(session) ?? "";
|
|
7142
7055
|
return `## Goal
|
|
7143
7056
|
|
|
@@ -7330,14 +7243,12 @@ async function handleOrchestratorYield(sessionId, cwd, nextPrompt, mode) {
|
|
|
7330
7243
|
prevModeStats = { cycles, activeMs };
|
|
7331
7244
|
}
|
|
7332
7245
|
await completeOrchestratorCycle(cwd, sessionId, nextPrompt, mode, cycleActiveMs);
|
|
7333
|
-
if (mode && mode !== prevMode) {
|
|
7334
|
-
await emitModeTransitionNotify(cwd, sessionId, prevMode, mode, prevModeStats);
|
|
7335
|
-
}
|
|
7336
7246
|
const freshSession = getSession(cwd, sessionId);
|
|
7337
7247
|
const runningAgents = freshSession.agents.filter((a) => a.status === "running");
|
|
7338
7248
|
if (runningAgents.length === 0) {
|
|
7339
7249
|
console.log(`[sisyphus] Orchestrator yielded with no running agents for session ${sessionId}`);
|
|
7340
7250
|
}
|
|
7251
|
+
return prevModeStats ? { prevMode, nextMode: mode, prevModeStats } : void 0;
|
|
7341
7252
|
}
|
|
7342
7253
|
async function handleOrchestratorComplete(sessionId, cwd, report) {
|
|
7343
7254
|
const session = getSession(cwd, sessionId);
|
|
@@ -7373,7 +7284,6 @@ var init_orchestrator = __esm({
|
|
|
7373
7284
|
init_tmux();
|
|
7374
7285
|
init_pane_registry();
|
|
7375
7286
|
init_pane_monitor();
|
|
7376
|
-
init_mode_notify();
|
|
7377
7287
|
init_plugins();
|
|
7378
7288
|
sessionWindowMap = /* @__PURE__ */ new Map();
|
|
7379
7289
|
sessionOrchestratorPane = /* @__PURE__ */ new Map();
|
|
@@ -7541,6 +7451,36 @@ var init_status_dots = __esm({
|
|
|
7541
7451
|
}
|
|
7542
7452
|
});
|
|
7543
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
|
+
|
|
7544
7484
|
// src/shared/manifest.ts
|
|
7545
7485
|
import os from "os";
|
|
7546
7486
|
function mapEffortFallback(level) {
|
|
@@ -7581,7 +7521,7 @@ var init_manifest = __esm({
|
|
|
7581
7521
|
// src/shared/session-export.ts
|
|
7582
7522
|
import { execFile as execFile3 } from "child_process";
|
|
7583
7523
|
import { promisify as promisify2 } from "util";
|
|
7584
|
-
import { existsSync as
|
|
7524
|
+
import { existsSync as existsSync18, readFileSync as readFileSync19, mkdirSync as mkdirSync10, symlinkSync, rmSync as rmSync5, writeFileSync as writeFileSync14 } from "fs";
|
|
7585
7525
|
import { homedir as homedir6 } from "os";
|
|
7586
7526
|
import { join as join17 } from "path";
|
|
7587
7527
|
function sanitizeName(name) {
|
|
@@ -7593,7 +7533,7 @@ function buildOutputPath(label, dir) {
|
|
|
7593
7533
|
const base = `sisyphus-${label}-${date}`;
|
|
7594
7534
|
let candidate = join17(dir, `${base}.zip`);
|
|
7595
7535
|
let counter = 1;
|
|
7596
|
-
while (
|
|
7536
|
+
while (existsSync18(candidate)) {
|
|
7597
7537
|
counter++;
|
|
7598
7538
|
candidate = join17(dir, `${base}-${counter}.zip`);
|
|
7599
7539
|
}
|
|
@@ -7656,14 +7596,14 @@ async function exportSessionToZip(sessionId, cwd, options) {
|
|
|
7656
7596
|
const reveal = options?.reveal ?? true;
|
|
7657
7597
|
const sessDir = sessionDir(cwd, sessionId);
|
|
7658
7598
|
const histDir = historySessionDir(sessionId);
|
|
7659
|
-
const sessExists =
|
|
7660
|
-
const histExists =
|
|
7599
|
+
const sessExists = existsSync18(sessDir);
|
|
7600
|
+
const histExists = existsSync18(histDir);
|
|
7661
7601
|
if (!sessExists && !histExists) {
|
|
7662
7602
|
throw new Error(`No data found for session ${sessionId}`);
|
|
7663
7603
|
}
|
|
7664
7604
|
let label = sessionId.slice(0, 8);
|
|
7665
7605
|
const stPath = statePath(cwd, sessionId);
|
|
7666
|
-
if (
|
|
7606
|
+
if (existsSync18(stPath)) {
|
|
7667
7607
|
try {
|
|
7668
7608
|
const state = JSON.parse(readFileSync19(stPath, "utf-8"));
|
|
7669
7609
|
if (state.name) {
|
|
@@ -7855,7 +7795,7 @@ var init_format = __esm({
|
|
|
7855
7795
|
});
|
|
7856
7796
|
|
|
7857
7797
|
// src/cli/deploy/creds.ts
|
|
7858
|
-
import { chmodSync, existsSync as
|
|
7798
|
+
import { chmodSync, existsSync as existsSync19, mkdirSync as mkdirSync11, readFileSync as readFileSync21 } from "fs";
|
|
7859
7799
|
import { createInterface } from "readline";
|
|
7860
7800
|
function isValidProvider(value) {
|
|
7861
7801
|
return PROVIDERS.includes(value);
|
|
@@ -7896,10 +7836,10 @@ var init_pricing = __esm({
|
|
|
7896
7836
|
});
|
|
7897
7837
|
|
|
7898
7838
|
// src/cli/deploy/runtime.ts
|
|
7899
|
-
import { existsSync as
|
|
7839
|
+
import { existsSync as existsSync20, readFileSync as readFileSync22, unlinkSync as unlinkSync4 } from "fs";
|
|
7900
7840
|
function readRuntimeState(provider) {
|
|
7901
7841
|
const path = deployRuntimePath(provider);
|
|
7902
|
-
if (!
|
|
7842
|
+
if (!existsSync20(path)) return null;
|
|
7903
7843
|
try {
|
|
7904
7844
|
return JSON.parse(readFileSync22(path, "utf-8"));
|
|
7905
7845
|
} catch {
|
|
@@ -7923,15 +7863,15 @@ var init_tailnet = __esm({
|
|
|
7923
7863
|
});
|
|
7924
7864
|
|
|
7925
7865
|
// src/cli/deploy/templates.ts
|
|
7926
|
-
import { existsSync as
|
|
7866
|
+
import { existsSync as existsSync21 } from "fs";
|
|
7927
7867
|
import { dirname as dirname6, resolve as resolve9 } from "path";
|
|
7928
7868
|
import { fileURLToPath } from "url";
|
|
7929
7869
|
function deployRoot() {
|
|
7930
7870
|
const here = dirname6(fileURLToPath(import.meta.url));
|
|
7931
7871
|
const bundled = resolve9(here, "..", "deploy");
|
|
7932
|
-
if (
|
|
7872
|
+
if (existsSync21(bundled)) return bundled;
|
|
7933
7873
|
const sourceRoot = resolve9(here, "..", "..", "..", "deploy");
|
|
7934
|
-
if (
|
|
7874
|
+
if (existsSync21(sourceRoot)) return sourceRoot;
|
|
7935
7875
|
throw new Error(
|
|
7936
7876
|
`Could not locate deploy/ templates. Looked at:
|
|
7937
7877
|
${bundled}
|
|
@@ -7960,7 +7900,7 @@ var init_tailscale = __esm({
|
|
|
7960
7900
|
|
|
7961
7901
|
// src/cli/deploy/runner.ts
|
|
7962
7902
|
import { spawn as spawn2, spawnSync } from "child_process";
|
|
7963
|
-
import { copyFileSync as copyFileSync5, existsSync as
|
|
7903
|
+
import { copyFileSync as copyFileSync5, existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync23 } from "fs";
|
|
7964
7904
|
function readOutputs(provider) {
|
|
7965
7905
|
const result = spawnSync("terraform", ["output", "-json", `-state=${deployStatePath(provider)}`], {
|
|
7966
7906
|
cwd: providerModuleDir(provider),
|
|
@@ -7986,7 +7926,7 @@ function readOutputs(provider) {
|
|
|
7986
7926
|
}
|
|
7987
7927
|
}
|
|
7988
7928
|
function isProvisioned(provider) {
|
|
7989
|
-
if (!
|
|
7929
|
+
if (!existsSync22(deployStatePath(provider))) return false;
|
|
7990
7930
|
return readOutputs(provider) !== null;
|
|
7991
7931
|
}
|
|
7992
7932
|
function effectiveSshTarget(provider) {
|
|
@@ -8105,7 +8045,7 @@ var init_grove = __esm({
|
|
|
8105
8045
|
|
|
8106
8046
|
// src/cli/cloud/repo.ts
|
|
8107
8047
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
8108
|
-
import { existsSync as
|
|
8048
|
+
import { existsSync as existsSync23 } from "fs";
|
|
8109
8049
|
import { basename as basename6, join as join18 } from "path";
|
|
8110
8050
|
function captureGit(args, cwd) {
|
|
8111
8051
|
const result = spawnSync3("git", args, {
|
|
@@ -8146,10 +8086,10 @@ function buildRsyncArgs(localDir, remoteTarget) {
|
|
|
8146
8086
|
];
|
|
8147
8087
|
}
|
|
8148
8088
|
function detectPackageManager(toplevel) {
|
|
8149
|
-
if (
|
|
8150
|
-
if (
|
|
8151
|
-
if (
|
|
8152
|
-
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";
|
|
8153
8093
|
return null;
|
|
8154
8094
|
}
|
|
8155
8095
|
function packageManagerInstallCmd(pm) {
|
|
@@ -8350,7 +8290,7 @@ __export(cloud_handoff_exports, {
|
|
|
8350
8290
|
triggerForceHandoff: () => triggerForceHandoff
|
|
8351
8291
|
});
|
|
8352
8292
|
import { spawn as spawn5 } from "child_process";
|
|
8353
|
-
import { existsSync as
|
|
8293
|
+
import { existsSync as existsSync24 } from "fs";
|
|
8354
8294
|
import { join as join19 } from "path";
|
|
8355
8295
|
function runRsync2(args) {
|
|
8356
8296
|
return new Promise((resolve13) => {
|
|
@@ -8390,7 +8330,7 @@ async function syncSessionState(cwd, sessionId, repo, target, provider) {
|
|
|
8390
8330
|
const candidates = ["config.json", "orchestrator.md", "orchestrator-settings.json"];
|
|
8391
8331
|
for (const name of candidates) {
|
|
8392
8332
|
const localPath = join19(localProject, name);
|
|
8393
|
-
if (!
|
|
8333
|
+
if (!existsSync24(localPath)) continue;
|
|
8394
8334
|
const remotePath = `${boxRepoPath(repo)}/.sisyphus/${name}`;
|
|
8395
8335
|
const args = [
|
|
8396
8336
|
"-avz",
|
|
@@ -8557,14 +8497,14 @@ var init_cloud_handoff = __esm({
|
|
|
8557
8497
|
|
|
8558
8498
|
// src/daemon/session-manager.ts
|
|
8559
8499
|
import { v4 as uuidv4 } from "uuid";
|
|
8560
|
-
import { existsSync as
|
|
8500
|
+
import { existsSync as existsSync25, readFileSync as readFileSync24, readdirSync as readdirSync11, rmSync as rmSync6 } from "fs";
|
|
8561
8501
|
function truncate(s, max) {
|
|
8562
8502
|
return s.length <= max ? s : s.slice(0, max) + "...";
|
|
8563
8503
|
}
|
|
8564
8504
|
function readGoal(cwd, sessionId, fallback) {
|
|
8565
8505
|
try {
|
|
8566
8506
|
const p = goalPath(cwd, sessionId);
|
|
8567
|
-
if (
|
|
8507
|
+
if (existsSync25(p)) return readFileSync24(p, "utf-8").trim();
|
|
8568
8508
|
} catch {
|
|
8569
8509
|
}
|
|
8570
8510
|
return fallback;
|
|
@@ -8618,7 +8558,7 @@ function fireHaikuNaming(sessionId, cwd, fallbackTmuxName, task) {
|
|
|
8618
8558
|
console.error(`[sisyphus] Name generation failed for session ${sessionId}:`, err);
|
|
8619
8559
|
});
|
|
8620
8560
|
}
|
|
8621
|
-
function fireCommentary(event, companion, context, flash = false, repo, sessionId) {
|
|
8561
|
+
function fireCommentary(event, companion, context, flash = false, repo, sessionId, popupTitle) {
|
|
8622
8562
|
const memoryCtx = buildMemoryContext(repo);
|
|
8623
8563
|
generateCommentary(event, companion, context, memoryCtx).then((text) => {
|
|
8624
8564
|
if (text) {
|
|
@@ -8626,7 +8566,7 @@ function fireCommentary(event, companion, context, flash = false, repo, sessionI
|
|
|
8626
8566
|
const c = loadCompanion();
|
|
8627
8567
|
recordCommentary(c, text, event);
|
|
8628
8568
|
if (flash) {
|
|
8629
|
-
const feedback =
|
|
8569
|
+
const feedback = showCommentaryPopupQueue([{ text, title: popupTitle }]);
|
|
8630
8570
|
if (feedback) {
|
|
8631
8571
|
recordFeedback(c, text, feedback.rating, event, feedback.comment);
|
|
8632
8572
|
if (sessionId) {
|
|
@@ -8803,7 +8743,7 @@ It is the other session's responsibility. You do not need to monitor it.
|
|
|
8803
8743
|
function pruneOldSessions(cwd) {
|
|
8804
8744
|
try {
|
|
8805
8745
|
const dir = sessionsDir(cwd);
|
|
8806
|
-
if (!
|
|
8746
|
+
if (!existsSync25(dir)) return;
|
|
8807
8747
|
const entries = readdirSync11(dir, { withFileTypes: true });
|
|
8808
8748
|
const candidates = [];
|
|
8809
8749
|
for (const entry of entries) {
|
|
@@ -8941,7 +8881,7 @@ function getSessionStatus(cwd, sessionId) {
|
|
|
8941
8881
|
}
|
|
8942
8882
|
function countSessions(cwd) {
|
|
8943
8883
|
const dir = sessionsDir(cwd);
|
|
8944
|
-
if (!
|
|
8884
|
+
if (!existsSync25(dir)) return 0;
|
|
8945
8885
|
let n = 0;
|
|
8946
8886
|
for (const entry of readdirSync11(dir, { withFileTypes: true })) {
|
|
8947
8887
|
if (entry.isDirectory()) n++;
|
|
@@ -8950,7 +8890,7 @@ function countSessions(cwd) {
|
|
|
8950
8890
|
}
|
|
8951
8891
|
function listSessions(cwd) {
|
|
8952
8892
|
const dir = sessionsDir(cwd);
|
|
8953
|
-
if (!
|
|
8893
|
+
if (!existsSync25(dir)) return [];
|
|
8954
8894
|
const entries = readdirSync11(dir, { withFileTypes: true });
|
|
8955
8895
|
const sessions = [];
|
|
8956
8896
|
for (const entry of entries) {
|
|
@@ -9014,23 +8954,6 @@ function onAllAgentsDone2(sessionId, cwd, windowId) {
|
|
|
9014
8954
|
const companion = loadCompanion();
|
|
9015
8955
|
companion.spinnerVerbIndex = (companion.spinnerVerbIndex + 1) % SPINNER_VERBS.length;
|
|
9016
8956
|
saveCompanion(companion);
|
|
9017
|
-
const goal = readGoal(cwd, sessionId, session.task);
|
|
9018
|
-
const modeLabel = lastCycle?.mode ? ` (${lastCycle.mode})` : "";
|
|
9019
|
-
const agentMap = new Map(session.agents.map((a) => [a.id, a]));
|
|
9020
|
-
const spawnedThisCycle = (lastCycle?.agentsSpawned ?? []).map((id) => agentMap.get(id)).filter(Boolean).map((a) => `${a.name} (${a.agentType.replace(/^sisyphus:/, "")}, ${a.status})`).join(", ");
|
|
9021
|
-
let cycleCtx = `Cycle ${cycleNumber}${modeLabel} complete. Goal: ${truncate(goal, 80)}`;
|
|
9022
|
-
if (spawnedThisCycle) cycleCtx += `
|
|
9023
|
-
Agents: ${truncate(spawnedThisCycle, 200)}`;
|
|
9024
|
-
try {
|
|
9025
|
-
const logPath2 = cycleLogPath(cwd, sessionId, cycleNumber);
|
|
9026
|
-
if (existsSync26(logPath2)) {
|
|
9027
|
-
const log = readFileSync24(logPath2, "utf-8").trim();
|
|
9028
|
-
if (log) cycleCtx += `
|
|
9029
|
-
Cycle log: ${truncate(log, 200)}`;
|
|
9030
|
-
}
|
|
9031
|
-
} catch {
|
|
9032
|
-
}
|
|
9033
|
-
fireCommentary("cycle-boundary", companion, cycleCtx, true, session.cwd, sessionId);
|
|
9034
8957
|
} catch {
|
|
9035
8958
|
}
|
|
9036
8959
|
setImmediate(async () => {
|
|
@@ -9203,8 +9126,23 @@ async function handleYield(sessionId, cwd, nextPrompt, mode) {
|
|
|
9203
9126
|
await updateSessionStatus(cwd, sessionId, "active");
|
|
9204
9127
|
}
|
|
9205
9128
|
respawningSessions.add(sessionId);
|
|
9206
|
-
await handleOrchestratorYield(sessionId, cwd, nextPrompt, mode);
|
|
9129
|
+
const transition = await handleOrchestratorYield(sessionId, cwd, nextPrompt, mode);
|
|
9207
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
|
+
}
|
|
9208
9146
|
try {
|
|
9209
9147
|
recomputeDots();
|
|
9210
9148
|
} catch {
|
|
@@ -9259,6 +9197,7 @@ async function handleComplete(sessionId, cwd, report) {
|
|
|
9259
9197
|
console.warn("[sisyphus] Sentiment generation failed:", err instanceof Error ? err.message : err);
|
|
9260
9198
|
});
|
|
9261
9199
|
const config = loadConfig(cwd);
|
|
9200
|
+
const uploadCfg = config.upload;
|
|
9262
9201
|
if (isUploadConfigured(config.upload)) {
|
|
9263
9202
|
runSessionUploadAndPersist({
|
|
9264
9203
|
sessionId,
|
|
@@ -9270,11 +9209,10 @@ async function handleComplete(sessionId, cwd, report) {
|
|
|
9270
9209
|
}).catch(() => {
|
|
9271
9210
|
console.warn("[sisyphus] upload pipeline crashed; check uploadError on session state");
|
|
9272
9211
|
});
|
|
9273
|
-
} else if (
|
|
9274
|
-
const partialUpload = config.upload;
|
|
9212
|
+
} else if (uploadCfg) {
|
|
9275
9213
|
const missing = [];
|
|
9276
|
-
if (!
|
|
9277
|
-
if (!
|
|
9214
|
+
if (!uploadCfg.url) missing.push("upload.url");
|
|
9215
|
+
if (!uploadCfg.token) missing.push("upload.token");
|
|
9278
9216
|
const error = `upload skipped: missing ${missing.join(", ")}`;
|
|
9279
9217
|
console.warn(`[sisyphus] ${error}`);
|
|
9280
9218
|
updateSession(cwd, sessionId, { uploadStatus: "failed", uploadError: error }).catch(() => console.warn("[sisyphus] failed to persist upload skip status"));
|
|
@@ -9548,6 +9486,7 @@ var init_session_manager = __esm({
|
|
|
9548
9486
|
init_companion_render();
|
|
9549
9487
|
init_companion_commentary();
|
|
9550
9488
|
init_companion_popup();
|
|
9489
|
+
init_mode_transition();
|
|
9551
9490
|
init_history();
|
|
9552
9491
|
init_uploader();
|
|
9553
9492
|
init_upload();
|
|
@@ -9669,7 +9608,7 @@ var init_transcript_digest = __esm({
|
|
|
9669
9608
|
import {
|
|
9670
9609
|
closeSync as closeSync2,
|
|
9671
9610
|
constants,
|
|
9672
|
-
existsSync as
|
|
9611
|
+
existsSync as existsSync26,
|
|
9673
9612
|
fstatSync as fstatSync2,
|
|
9674
9613
|
lstatSync,
|
|
9675
9614
|
openSync as openSync2,
|
|
@@ -9691,7 +9630,7 @@ async function generateVisualForQuestion(opts) {
|
|
|
9691
9630
|
if (!question) return { ok: false, error: `qid ${opts.qid} not found in decisions` };
|
|
9692
9631
|
const mdPath = askVisualMarkdownPath(opts.cwd, opts.sessionId, opts.askId, opts.qid);
|
|
9693
9632
|
const ansiPath = askVisualAnsiPath(opts.cwd, opts.sessionId, opts.askId, opts.qid);
|
|
9694
|
-
if (!opts.force &&
|
|
9633
|
+
if (!opts.force && existsSync26(mdPath) && existsSync26(ansiPath)) {
|
|
9695
9634
|
return { ok: true, markdownPath: mdPath, ansiPath, turns: 0 };
|
|
9696
9635
|
}
|
|
9697
9636
|
if (opts.force) {
|
|
@@ -9765,7 +9704,7 @@ function buildUserPrompt(q, askedBy, ctx) {
|
|
|
9765
9704
|
}
|
|
9766
9705
|
function readSystemPrompt() {
|
|
9767
9706
|
if (cachedSystemPrompt !== void 0) return cachedSystemPrompt;
|
|
9768
|
-
const found = SYSTEM_PROMPT_CANDIDATES.find((p) =>
|
|
9707
|
+
const found = SYSTEM_PROMPT_CANDIDATES.find((p) => existsSync26(p));
|
|
9769
9708
|
if (found === void 0) {
|
|
9770
9709
|
throw new Error(
|
|
9771
9710
|
`termrender-haiku-system.md not found in any candidate location: ${SYSTEM_PROMPT_CANDIDATES.join(", ")}`
|
|
@@ -9912,7 +9851,7 @@ var init_ask_visual = __esm({
|
|
|
9912
9851
|
|
|
9913
9852
|
// src/daemon/server.ts
|
|
9914
9853
|
import { createServer } from "net";
|
|
9915
|
-
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";
|
|
9916
9855
|
import { join as join21, basename as basename7, dirname as dirname8 } from "path";
|
|
9917
9856
|
import { scanInbox } from "@crouton-kit/humanloop";
|
|
9918
9857
|
function setCompositor(c) {
|
|
@@ -9932,7 +9871,7 @@ function persistSessionRegistry() {
|
|
|
9932
9871
|
}
|
|
9933
9872
|
function loadSessionRegistry() {
|
|
9934
9873
|
const p = registryPath();
|
|
9935
|
-
if (!
|
|
9874
|
+
if (!existsSync27(p)) return {};
|
|
9936
9875
|
try {
|
|
9937
9876
|
return JSON.parse(readFileSync26(p, "utf-8"));
|
|
9938
9877
|
} catch (err) {
|
|
@@ -9988,7 +9927,7 @@ function collectAllSessionIds() {
|
|
|
9988
9927
|
scannedCwds.add(cwd);
|
|
9989
9928
|
try {
|
|
9990
9929
|
const dir = sessionsDir(cwd);
|
|
9991
|
-
if (!
|
|
9930
|
+
if (!existsSync27(dir)) continue;
|
|
9992
9931
|
for (const entry of readdirSync12(dir, { withFileTypes: true })) {
|
|
9993
9932
|
if (entry.isDirectory() && !idToCwd.has(entry.name)) {
|
|
9994
9933
|
idToCwd.set(entry.name, cwd);
|
|
@@ -10220,7 +10159,7 @@ async function handleRequest(req) {
|
|
|
10220
10159
|
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
10221
10160
|
if (!tracking) {
|
|
10222
10161
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
10223
|
-
if (
|
|
10162
|
+
if (existsSync27(stateFile)) {
|
|
10224
10163
|
tracking = { cwd: req.cwd, messageCounter: 0 };
|
|
10225
10164
|
sessionTrackingMap.set(req.sessionId, tracking);
|
|
10226
10165
|
persistSessionRegistry();
|
|
@@ -10242,7 +10181,7 @@ async function handleRequest(req) {
|
|
|
10242
10181
|
}
|
|
10243
10182
|
case "clear-orphan": {
|
|
10244
10183
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
10245
|
-
if (!
|
|
10184
|
+
if (!existsSync27(stateFile)) {
|
|
10246
10185
|
return {
|
|
10247
10186
|
ok: false,
|
|
10248
10187
|
error: errNotFound("unknown_session", {
|
|
@@ -10300,7 +10239,7 @@ async function handleRequest(req) {
|
|
|
10300
10239
|
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
10301
10240
|
if (!tracking) {
|
|
10302
10241
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
10303
|
-
if (
|
|
10242
|
+
if (existsSync27(stateFile)) {
|
|
10304
10243
|
registerSessionCwd(req.sessionId, req.cwd);
|
|
10305
10244
|
tracking = sessionTrackingMap.get(req.sessionId);
|
|
10306
10245
|
} else {
|
|
@@ -10314,7 +10253,7 @@ async function handleRequest(req) {
|
|
|
10314
10253
|
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
10315
10254
|
if (!tracking) {
|
|
10316
10255
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
10317
|
-
if (
|
|
10256
|
+
if (existsSync27(stateFile)) {
|
|
10318
10257
|
registerSessionCwd(req.sessionId, req.cwd);
|
|
10319
10258
|
tracking = sessionTrackingMap.get(req.sessionId);
|
|
10320
10259
|
} else {
|
|
@@ -10331,7 +10270,7 @@ async function handleRequest(req) {
|
|
|
10331
10270
|
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
10332
10271
|
if (!tracking) {
|
|
10333
10272
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
10334
|
-
if (
|
|
10273
|
+
if (existsSync27(stateFile)) {
|
|
10335
10274
|
tracking = { cwd: req.cwd, messageCounter: 0 };
|
|
10336
10275
|
sessionTrackingMap.set(req.sessionId, tracking);
|
|
10337
10276
|
persistSessionRegistry();
|
|
@@ -10401,7 +10340,7 @@ async function handleRequest(req) {
|
|
|
10401
10340
|
}
|
|
10402
10341
|
case "set-upload-status": {
|
|
10403
10342
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
10404
|
-
if (!
|
|
10343
|
+
if (!existsSync27(stateFile)) {
|
|
10405
10344
|
return unknownSessionError(req.sessionId);
|
|
10406
10345
|
}
|
|
10407
10346
|
try {
|
|
@@ -10600,6 +10539,7 @@ async function handleRequest(req) {
|
|
|
10600
10539
|
const reviewItems = [];
|
|
10601
10540
|
for (const [sessionId, tracking] of sessionTrackingMap) {
|
|
10602
10541
|
if (!tracking.cwd) continue;
|
|
10542
|
+
if (tracking.cwd !== req.cwd) continue;
|
|
10603
10543
|
askDirs.push(askDir(tracking.cwd, sessionId));
|
|
10604
10544
|
reviewItems.push(...listReviewInboxItems(tracking.cwd, sessionId));
|
|
10605
10545
|
}
|
|
@@ -10623,6 +10563,26 @@ async function handleRequest(req) {
|
|
|
10623
10563
|
});
|
|
10624
10564
|
return { ok: true, data: { items: itemsWithName } };
|
|
10625
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
|
+
}
|
|
10626
10586
|
case "cloud-handoff": {
|
|
10627
10587
|
const tracking = sessionTrackingMap.get(req.sessionId);
|
|
10628
10588
|
if (!tracking) return unknownSessionError(req.sessionId);
|
|
@@ -10770,8 +10730,8 @@ async function handleRequest(req) {
|
|
|
10770
10730
|
function startServer() {
|
|
10771
10731
|
return new Promise((resolve13, reject) => {
|
|
10772
10732
|
const sock = socketPath();
|
|
10773
|
-
if (
|
|
10774
|
-
|
|
10733
|
+
if (existsSync27(sock)) {
|
|
10734
|
+
unlinkSync5(sock);
|
|
10775
10735
|
}
|
|
10776
10736
|
server = createServer((conn) => {
|
|
10777
10737
|
let buffer = "";
|
|
@@ -10825,15 +10785,15 @@ function stopServer() {
|
|
|
10825
10785
|
}
|
|
10826
10786
|
server.close(() => {
|
|
10827
10787
|
const sock = socketPath();
|
|
10828
|
-
if (
|
|
10829
|
-
|
|
10788
|
+
if (existsSync27(sock)) {
|
|
10789
|
+
unlinkSync5(sock);
|
|
10830
10790
|
}
|
|
10831
10791
|
server = null;
|
|
10832
10792
|
resolve13();
|
|
10833
10793
|
});
|
|
10834
10794
|
});
|
|
10835
10795
|
}
|
|
10836
|
-
var server, compositor, sessionTrackingMap;
|
|
10796
|
+
var server, compositor, sessionTrackingMap, pendingFocus, FOCUS_TTL_MS;
|
|
10837
10797
|
var init_server = __esm({
|
|
10838
10798
|
"src/daemon/server.ts"() {
|
|
10839
10799
|
"use strict";
|
|
@@ -10857,6 +10817,8 @@ var init_server = __esm({
|
|
|
10857
10817
|
server = null;
|
|
10858
10818
|
compositor = null;
|
|
10859
10819
|
sessionTrackingMap = /* @__PURE__ */ new Map();
|
|
10820
|
+
pendingFocus = null;
|
|
10821
|
+
FOCUS_TTL_MS = 3e4;
|
|
10860
10822
|
}
|
|
10861
10823
|
});
|
|
10862
10824
|
|
|
@@ -10865,7 +10827,7 @@ init_paths();
|
|
|
10865
10827
|
init_config();
|
|
10866
10828
|
init_server();
|
|
10867
10829
|
init_orphan_sweep();
|
|
10868
|
-
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";
|
|
10869
10831
|
import { execSync as execSync8 } from "child_process";
|
|
10870
10832
|
import { setTimeout as sleep } from "timers/promises";
|
|
10871
10833
|
import { Command } from "commander";
|
|
@@ -10875,8 +10837,8 @@ init_ask_store();
|
|
|
10875
10837
|
init_state();
|
|
10876
10838
|
init_server();
|
|
10877
10839
|
init_paths();
|
|
10878
|
-
import { ulid as
|
|
10879
|
-
import { existsSync as
|
|
10840
|
+
import { ulid as ulid2 } from "ulid";
|
|
10841
|
+
import { existsSync as existsSync28 } from "fs";
|
|
10880
10842
|
var HEARTBEAT_ASKED_BY2 = "system:heartbeat";
|
|
10881
10843
|
var HEARTBEAT_THRESHOLD_MS = 60 * 60 * 1e3;
|
|
10882
10844
|
var HEARTBEAT_SCAN_INTERVAL_MS = 15 * 60 * 1e3;
|
|
@@ -10919,7 +10881,7 @@ async function emitHeartbeatAsk(cwd, sessionId, original) {
|
|
|
10919
10881
|
},
|
|
10920
10882
|
interactions: [interaction]
|
|
10921
10883
|
};
|
|
10922
|
-
const askId =
|
|
10884
|
+
const askId = ulid2();
|
|
10923
10885
|
createAsk(cwd, sessionId, {
|
|
10924
10886
|
askId,
|
|
10925
10887
|
askedBy: HEARTBEAT_ASKED_BY2,
|
|
@@ -10935,43 +10897,14 @@ async function emitHeartbeatAsk(cwd, sessionId, original) {
|
|
|
10935
10897
|
heartbeatAskId: askId
|
|
10936
10898
|
});
|
|
10937
10899
|
}
|
|
10938
|
-
function isModeGateStale(deck, currentMode) {
|
|
10939
|
-
const source = deck.source;
|
|
10940
|
-
const chain = source?.modeChain;
|
|
10941
|
-
if (!chain || chain.length === 0) return false;
|
|
10942
|
-
if (!currentMode) return false;
|
|
10943
|
-
return !chain.some((e) => e.mode === currentMode);
|
|
10944
|
-
}
|
|
10945
10900
|
async function scanSessionForStaleAsks(cwd, sessionId) {
|
|
10946
10901
|
const now = Date.now();
|
|
10947
|
-
let currentMode;
|
|
10948
|
-
try {
|
|
10949
|
-
const session = getSession(cwd, sessionId);
|
|
10950
|
-
currentMode = session.orchestratorCycles[session.orchestratorCycles.length - 1]?.mode;
|
|
10951
|
-
} catch {
|
|
10952
|
-
}
|
|
10953
10902
|
for (const askId of listAsks(cwd, sessionId)) {
|
|
10954
10903
|
try {
|
|
10955
10904
|
const meta = readMeta(cwd, sessionId, askId);
|
|
10956
10905
|
if (!meta) continue;
|
|
10957
10906
|
if (meta.status === "answered") continue;
|
|
10958
10907
|
if (meta.orphaned) continue;
|
|
10959
|
-
if (meta.modeTransition === true) {
|
|
10960
|
-
const deck = readDecisions(cwd, sessionId, askId);
|
|
10961
|
-
if (deck && isModeGateStale(deck, currentMode)) {
|
|
10962
|
-
writeOutput(cwd, sessionId, askId, [{
|
|
10963
|
-
id: "mode-transition",
|
|
10964
|
-
selectedOptionId: "ack",
|
|
10965
|
-
freetext: "auto-resolved: session advanced past mode-transition"
|
|
10966
|
-
}]);
|
|
10967
|
-
await updateMeta(cwd, sessionId, askId, {
|
|
10968
|
-
status: "answered",
|
|
10969
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10970
|
-
});
|
|
10971
|
-
console.log(`[sisyphus] mode-gate auto-resolved ask ${askId} for session ${sessionId} (currentMode: ${currentMode})`);
|
|
10972
|
-
}
|
|
10973
|
-
continue;
|
|
10974
|
-
}
|
|
10975
10908
|
if (meta.heartbeatNotifiedAt) continue;
|
|
10976
10909
|
if (meta.askedBy === HEARTBEAT_ASKED_BY2) continue;
|
|
10977
10910
|
const askedAtMs = new Date(meta.askedAt).getTime();
|
|
@@ -10989,7 +10922,7 @@ async function scanSessionForStaleAsks(cwd, sessionId) {
|
|
|
10989
10922
|
async function scanAllSessionsForStaleAsks() {
|
|
10990
10923
|
const reg = loadSessionRegistry();
|
|
10991
10924
|
for (const [sessionId, cwd] of Object.entries(reg)) {
|
|
10992
|
-
if (!
|
|
10925
|
+
if (!existsSync28(statePath(cwd, sessionId))) continue;
|
|
10993
10926
|
try {
|
|
10994
10927
|
await scanSessionForStaleAsks(cwd, sessionId);
|
|
10995
10928
|
} catch (err) {
|
|
@@ -11052,14 +10985,14 @@ var DEFAULT_STATUS_BAR_CONFIG = {
|
|
|
11052
10985
|
init_tmux();
|
|
11053
10986
|
init_status_dots();
|
|
11054
10987
|
init_companion();
|
|
11055
|
-
import { readFileSync as readFileSync27, existsSync as
|
|
10988
|
+
import { readFileSync as readFileSync27, existsSync as existsSync29 } from "fs";
|
|
11056
10989
|
import { homedir as homedir8 } from "os";
|
|
11057
10990
|
import { join as join22 } from "path";
|
|
11058
10991
|
var STATUS_BAR_BG = "#1d1e21";
|
|
11059
10992
|
var SESSION_ORDER_PATH = join22(homedir8(), ".config", "tmux", "session-order");
|
|
11060
10993
|
function getSessionOrder() {
|
|
11061
10994
|
try {
|
|
11062
|
-
if (!
|
|
10995
|
+
if (!existsSync29(SESSION_ORDER_PATH)) return [];
|
|
11063
10996
|
return readFileSync27(SESSION_ORDER_PATH, "utf-8").split("\n").filter(Boolean);
|
|
11064
10997
|
} catch {
|
|
11065
10998
|
return [];
|
|
@@ -11612,6 +11545,7 @@ function writeEmptyManifest() {
|
|
|
11612
11545
|
|
|
11613
11546
|
// src/daemon/index.ts
|
|
11614
11547
|
init_agent();
|
|
11548
|
+
init_ask_store();
|
|
11615
11549
|
init_orphan_asks();
|
|
11616
11550
|
init_process();
|
|
11617
11551
|
init_orchestrator();
|
|
@@ -11623,7 +11557,7 @@ init_state();
|
|
|
11623
11557
|
init_paths();
|
|
11624
11558
|
init_version();
|
|
11625
11559
|
import { execSync as execSync7 } from "child_process";
|
|
11626
|
-
import { writeFileSync as writeFileSync16, unlinkSync as
|
|
11560
|
+
import { writeFileSync as writeFileSync16, unlinkSync as unlinkSync6, lstatSync as lstatSync2 } from "fs";
|
|
11627
11561
|
import { resolve as resolve11 } from "path";
|
|
11628
11562
|
import { get } from "https";
|
|
11629
11563
|
function isNewer(latest, current) {
|
|
@@ -11700,7 +11634,7 @@ function markUpdating(version) {
|
|
|
11700
11634
|
}
|
|
11701
11635
|
function clearUpdating() {
|
|
11702
11636
|
try {
|
|
11703
|
-
|
|
11637
|
+
unlinkSync6(daemonUpdatingPath());
|
|
11704
11638
|
} catch {
|
|
11705
11639
|
}
|
|
11706
11640
|
}
|
|
@@ -11750,7 +11684,7 @@ function stopPeriodicUpdateCheck() {
|
|
|
11750
11684
|
}
|
|
11751
11685
|
|
|
11752
11686
|
// src/daemon/plugin-install.ts
|
|
11753
|
-
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";
|
|
11754
11688
|
import { join as join23, resolve as resolve12 } from "path";
|
|
11755
11689
|
import { homedir as homedir9 } from "os";
|
|
11756
11690
|
var PLUGIN_NAME = "sisyphus-tmux";
|
|
@@ -11764,7 +11698,7 @@ function copyDir(src, dest) {
|
|
|
11764
11698
|
copyDir(srcPath, destPath);
|
|
11765
11699
|
} else {
|
|
11766
11700
|
const srcMtime = statSync4(srcPath).mtimeMs;
|
|
11767
|
-
const destMtime =
|
|
11701
|
+
const destMtime = existsSync30(destPath) ? statSync4(destPath).mtimeMs : 0;
|
|
11768
11702
|
if (srcMtime > destMtime) {
|
|
11769
11703
|
copyFileSync6(srcPath, destPath);
|
|
11770
11704
|
}
|
|
@@ -11774,7 +11708,7 @@ function copyDir(src, dest) {
|
|
|
11774
11708
|
function pluginNeedsUpdate(sourceDir) {
|
|
11775
11709
|
const srcHooks = join23(sourceDir, "hooks", "hooks.json");
|
|
11776
11710
|
const destHooks = join23(INSTALL_DIR, "hooks", "hooks.json");
|
|
11777
|
-
if (!
|
|
11711
|
+
if (!existsSync30(destHooks)) return true;
|
|
11778
11712
|
try {
|
|
11779
11713
|
return readFileSync28(srcHooks, "utf-8") !== readFileSync28(destHooks, "utf-8");
|
|
11780
11714
|
} catch {
|
|
@@ -11783,7 +11717,7 @@ function pluginNeedsUpdate(sourceDir) {
|
|
|
11783
11717
|
}
|
|
11784
11718
|
function installPlugin() {
|
|
11785
11719
|
const sourceDir = resolve12(import.meta.dirname, "../templates/sisyphus-tmux-plugin");
|
|
11786
|
-
if (!
|
|
11720
|
+
if (!existsSync30(sourceDir)) {
|
|
11787
11721
|
console.error(`[plugin-install] Source dir not found: ${sourceDir}`);
|
|
11788
11722
|
return;
|
|
11789
11723
|
}
|
|
@@ -11791,7 +11725,7 @@ function installPlugin() {
|
|
|
11791
11725
|
try {
|
|
11792
11726
|
copyDir(sourceDir, INSTALL_DIR);
|
|
11793
11727
|
const hookScript = join23(INSTALL_DIR, "hooks", "tmux-state.sh");
|
|
11794
|
-
if (
|
|
11728
|
+
if (existsSync30(hookScript)) {
|
|
11795
11729
|
try {
|
|
11796
11730
|
chmodSync3(hookScript, 493);
|
|
11797
11731
|
} catch {
|
|
@@ -11847,7 +11781,7 @@ init_upload();
|
|
|
11847
11781
|
init_version();
|
|
11848
11782
|
init_state();
|
|
11849
11783
|
init_uploader();
|
|
11850
|
-
import { existsSync as
|
|
11784
|
+
import { existsSync as existsSync31, readdirSync as readdirSync14, readFileSync as readFileSync29 } from "fs";
|
|
11851
11785
|
var backfillStarted = false;
|
|
11852
11786
|
async function backfillUploads() {
|
|
11853
11787
|
if (backfillStarted) return;
|
|
@@ -11880,7 +11814,7 @@ async function backfillUploads() {
|
|
|
11880
11814
|
let failed = 0;
|
|
11881
11815
|
let unexportable = 0;
|
|
11882
11816
|
for (const { sessionId, cwd } of candidates) {
|
|
11883
|
-
if (!
|
|
11817
|
+
if (!existsSync31(statePath(cwd, sessionId))) {
|
|
11884
11818
|
unexportable++;
|
|
11885
11819
|
continue;
|
|
11886
11820
|
}
|
|
@@ -11958,7 +11892,7 @@ function isLaunchdManaged() {
|
|
|
11958
11892
|
}
|
|
11959
11893
|
function releasePidLock() {
|
|
11960
11894
|
try {
|
|
11961
|
-
|
|
11895
|
+
unlinkSync7(daemonPidPath());
|
|
11962
11896
|
} catch {
|
|
11963
11897
|
}
|
|
11964
11898
|
}
|
|
@@ -11997,7 +11931,7 @@ function stopDaemon() {
|
|
|
11997
11931
|
}
|
|
11998
11932
|
async function recoverOneSession(sessionId, cwd, tmuxIdSet, tmuxNameToId, panesByWindow) {
|
|
11999
11933
|
const stateFile = statePath(cwd, sessionId);
|
|
12000
|
-
if (!
|
|
11934
|
+
if (!existsSync32(stateFile)) return false;
|
|
12001
11935
|
let session;
|
|
12002
11936
|
try {
|
|
12003
11937
|
session = JSON.parse(readFileSync30(stateFile, "utf-8"));
|
|
@@ -12008,6 +11942,10 @@ async function recoverOneSession(sessionId, cwd, tmuxIdSet, tmuxNameToId, panesB
|
|
|
12008
11942
|
if (session.status !== "active" && session.status !== "paused") return false;
|
|
12009
11943
|
registerSessionCwd(sessionId, cwd);
|
|
12010
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
|
+
}
|
|
12011
11949
|
if (!session.tmuxSessionName) return true;
|
|
12012
11950
|
let sessionAlive = false;
|
|
12013
11951
|
let currentTmuxId = session.tmuxSessionId;
|