squadrant 0.16.5 → 0.17.0
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/index.js +1619 -482
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +202 -137
- package/dist/squadrantd.js.map +1 -1
- package/package.json +1 -1
- package/plugin/skills/captain-ops/SKILL.md +35 -10
- package/scripts/read-handoff.sh +16 -4
package/dist/squadrantd.js
CHANGED
|
@@ -60,10 +60,10 @@ var init_snapshot = __esm({
|
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
// packages/cli/src/squadrantd.ts
|
|
63
|
-
import { join as
|
|
64
|
-
import { homedir as
|
|
63
|
+
import { join as join19, dirname as dirname5 } from "path";
|
|
64
|
+
import { homedir as homedir14 } from "os";
|
|
65
65
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
66
|
-
import { readFileSync as
|
|
66
|
+
import { readFileSync as readFileSync14, statSync as statSync4 } from "fs";
|
|
67
67
|
|
|
68
68
|
// packages/shared/dist/config.js
|
|
69
69
|
import fs from "fs";
|
|
@@ -500,6 +500,11 @@ function nextPendingTool(current, ev, now) {
|
|
|
500
500
|
return void 0;
|
|
501
501
|
return current;
|
|
502
502
|
}
|
|
503
|
+
function nextPendingMonitor(current, ev, now) {
|
|
504
|
+
if (ev.note === "agent.hook.PreToolUse" && ev.tool === "Monitor")
|
|
505
|
+
return { since: now };
|
|
506
|
+
return current;
|
|
507
|
+
}
|
|
503
508
|
function reduce(rec, ev, now) {
|
|
504
509
|
if (ev.type === "task.reopened") {
|
|
505
510
|
return { ...rec, state: "working", question: void 0, error: void 0, lastHeartbeat: now, lastEvent: ev.type };
|
|
@@ -516,14 +521,17 @@ function reduce(rec, ev, now) {
|
|
|
516
521
|
sessionId: ev.sessionId ?? rec.sessionId,
|
|
517
522
|
question: void 0,
|
|
518
523
|
// resuming after a blocked→reply clears the question
|
|
519
|
-
pendingTool: void 0
|
|
524
|
+
pendingTool: void 0,
|
|
520
525
|
// #354: a new turn closes any prior tool window
|
|
526
|
+
pendingMonitor: void 0
|
|
527
|
+
// #594a: same reset — a new turn moots any prior watch
|
|
521
528
|
};
|
|
522
529
|
case "task.progress": {
|
|
523
530
|
const pendingTool = nextPendingTool(rec.pendingTool, ev, now);
|
|
531
|
+
const pendingMonitor = nextPendingMonitor(rec.pendingMonitor, ev, now);
|
|
524
532
|
if (isStickyAttention(rec.state))
|
|
525
|
-
return { ...rec, lastHeartbeat: now, lastEvent: ev.type, pendingTool };
|
|
526
|
-
const b = { ...base, pendingTool };
|
|
533
|
+
return { ...rec, lastHeartbeat: now, lastEvent: ev.type, pendingTool, pendingMonitor };
|
|
534
|
+
const b = { ...base, pendingTool, pendingMonitor };
|
|
527
535
|
if (rec.state === "awaiting-input" || rec.state === "stalled")
|
|
528
536
|
return { ...stampAttempt(b, {}, now), state: "working" };
|
|
529
537
|
return stampAttempt(b, {}, now);
|
|
@@ -537,9 +545,9 @@ function reduce(rec, ev, now) {
|
|
|
537
545
|
case "task.blocked":
|
|
538
546
|
if (rec.state === "blocked")
|
|
539
547
|
return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
|
|
540
|
-
return { ...base, state: "blocked", question: ev.question, pendingTool: void 0 };
|
|
548
|
+
return { ...base, state: "blocked", question: ev.question, pendingTool: void 0, pendingMonitor: void 0 };
|
|
541
549
|
case "task.review":
|
|
542
|
-
return { ...base, state: "review", reviewNote: ev.message, pendingTool: void 0 };
|
|
550
|
+
return { ...base, state: "review", reviewNote: ev.message, pendingTool: void 0, pendingMonitor: void 0 };
|
|
543
551
|
case "task.done":
|
|
544
552
|
if (rec.state === "review" && ev.source !== "approve") {
|
|
545
553
|
return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
|
|
@@ -554,19 +562,19 @@ function reduce(rec, ev, now) {
|
|
|
554
562
|
case "task.session":
|
|
555
563
|
return stampAttempt(base, { resumeRef: ev.resumeRef }, now);
|
|
556
564
|
case "task.turn.started":
|
|
557
|
-
return { ...stampAttempt(base, {}, now), state: "working", pendingTool: void 0 };
|
|
565
|
+
return { ...stampAttempt(base, {}, now), state: "working", pendingTool: void 0, pendingMonitor: void 0 };
|
|
558
566
|
case "task.turn.completed":
|
|
559
567
|
if (isStickyAttention(rec.state))
|
|
560
568
|
return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
|
|
561
|
-
if (rec.pendingTool)
|
|
569
|
+
if (rec.pendingTool || rec.pendingMonitor)
|
|
562
570
|
return stampAttempt(base, {}, now);
|
|
563
|
-
return { ...stampAttempt(base, {}, now), state: "awaiting-input", pendingTool: void 0 };
|
|
571
|
+
return { ...stampAttempt(base, {}, now), state: "awaiting-input", pendingTool: void 0, pendingMonitor: void 0 };
|
|
564
572
|
case "task.delta":
|
|
565
573
|
return stampAttempt(base, {}, now);
|
|
566
574
|
// heartbeat-only
|
|
567
575
|
case "task.input.requested":
|
|
568
576
|
case "task.approval.requested":
|
|
569
|
-
return { ...stampAttempt(base, {}, now), state: "blocked", question: ev.question, pendingTool: void 0 };
|
|
577
|
+
return { ...stampAttempt(base, {}, now), state: "blocked", question: ev.question, pendingTool: void 0, pendingMonitor: void 0 };
|
|
570
578
|
case "task.reattached":
|
|
571
579
|
return stampAttempt(base, {}, now);
|
|
572
580
|
case "task.first-turn.confirmed":
|
|
@@ -587,15 +595,22 @@ function reduce(rec, ev, now) {
|
|
|
587
595
|
|
|
588
596
|
// packages/core/dist/watchdog.js
|
|
589
597
|
var TOOL_STALL_BUDGET_MS = 10 * 60 * 1e3;
|
|
590
|
-
|
|
598
|
+
var MONITOR_STALL_BUDGET_MS = 60 * 60 * 1e3;
|
|
599
|
+
function evaluateStall(rec, now, toolStallMs = TOOL_STALL_BUDGET_MS, monitorStallMs = MONITOR_STALL_BUDGET_MS) {
|
|
591
600
|
if (rec.state !== "working")
|
|
592
601
|
return null;
|
|
593
602
|
if (rec.mode === "interactive") {
|
|
594
|
-
if (
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
return
|
|
598
|
-
|
|
603
|
+
if (rec.pendingTool) {
|
|
604
|
+
if (now - rec.pendingTool.since <= toolStallMs)
|
|
605
|
+
return null;
|
|
606
|
+
return { ...rec, state: "stalled", lastEvent: "watchdog.tool-stall" };
|
|
607
|
+
}
|
|
608
|
+
if (rec.pendingMonitor) {
|
|
609
|
+
if (now - rec.pendingMonitor.since <= monitorStallMs)
|
|
610
|
+
return null;
|
|
611
|
+
return { ...rec, state: "stalled", lastEvent: "watchdog.monitor-stall" };
|
|
612
|
+
}
|
|
613
|
+
return null;
|
|
599
614
|
}
|
|
600
615
|
const liveness = rec.attempts.at(-1)?.lastHeartbeatAt ?? rec.lastHeartbeat;
|
|
601
616
|
if (now - liveness <= rec.heartbeatBudgetMs)
|
|
@@ -605,7 +620,7 @@ function evaluateStall(rec, now, toolStallMs = TOOL_STALL_BUDGET_MS) {
|
|
|
605
620
|
function recoverStall(rec, now) {
|
|
606
621
|
if (rec.state !== "stalled")
|
|
607
622
|
return null;
|
|
608
|
-
return { ...rec, state: "working", lastHeartbeat: now, lastEvent: "watchdog.recover", pendingTool: void 0 };
|
|
623
|
+
return { ...rec, state: "working", lastHeartbeat: now, lastEvent: "watchdog.recover", pendingTool: void 0, pendingMonitor: void 0 };
|
|
609
624
|
}
|
|
610
625
|
|
|
611
626
|
// packages/core/dist/daemon/reduce.js
|
|
@@ -912,7 +927,7 @@ function createDaemon(deps) {
|
|
|
912
927
|
store.delete(r.project, r.id);
|
|
913
928
|
continue;
|
|
914
929
|
}
|
|
915
|
-
if (!TERMINAL_STATES.has(r.state)) {
|
|
930
|
+
if (!TERMINAL_STATES.has(r.state) && !isStickyAttention(r.state)) {
|
|
916
931
|
const ceiling = deps.taskTimeoutMs ?? DEFAULT_TASK_TIMEOUT_MS;
|
|
917
932
|
if (t - r.createdAt > ceiling) {
|
|
918
933
|
const prevState = r.state;
|
|
@@ -957,7 +972,7 @@ function createDaemon(deps) {
|
|
|
957
972
|
const idle = evaluateStall(r, t);
|
|
958
973
|
if (idle) {
|
|
959
974
|
store.put(idle);
|
|
960
|
-
const synthEvent = idle.pendingTool ? { type: "task.stalled", id: r.id, heartbeatBudgetMs: r.heartbeatBudgetMs, tool: idle.pendingTool.name, elapsedMs: t - idle.pendingTool.since } : { type: "task.stalled", id: r.id, heartbeatBudgetMs: r.heartbeatBudgetMs };
|
|
975
|
+
const synthEvent = idle.pendingTool ? { type: "task.stalled", id: r.id, heartbeatBudgetMs: r.heartbeatBudgetMs, tool: idle.pendingTool.name, elapsedMs: t - idle.pendingTool.since } : idle.pendingMonitor ? { type: "task.stalled", id: r.id, heartbeatBudgetMs: r.heartbeatBudgetMs, tool: "Monitor", elapsedMs: t - idle.pendingMonitor.since } : { type: "task.stalled", id: r.id, heartbeatBudgetMs: r.heartbeatBudgetMs };
|
|
961
976
|
firePush(deps, r.project, r.state, idle, synthEvent, lastCaptainTurnAt.get(r.id));
|
|
962
977
|
continue;
|
|
963
978
|
}
|
|
@@ -1394,9 +1409,9 @@ function startServer(sockPath, handlerOrCallbacks, onListenError = defaultListen
|
|
|
1394
1409
|
return server;
|
|
1395
1410
|
}
|
|
1396
1411
|
function isDaemonSocketLive(sockPath, timeoutMs = 500) {
|
|
1397
|
-
return new Promise((
|
|
1412
|
+
return new Promise((resolve3) => {
|
|
1398
1413
|
if (!existsSync5(sockPath)) {
|
|
1399
|
-
|
|
1414
|
+
resolve3(false);
|
|
1400
1415
|
return;
|
|
1401
1416
|
}
|
|
1402
1417
|
const conn = createConnection(sockPath);
|
|
@@ -1405,7 +1420,7 @@ function isDaemonSocketLive(sockPath, timeoutMs = 500) {
|
|
|
1405
1420
|
conn.destroy();
|
|
1406
1421
|
} catch {
|
|
1407
1422
|
}
|
|
1408
|
-
|
|
1423
|
+
resolve3(v);
|
|
1409
1424
|
};
|
|
1410
1425
|
const timer = setTimeout(() => finish(false), timeoutMs);
|
|
1411
1426
|
conn.on("connect", () => {
|
|
@@ -1419,7 +1434,7 @@ function isDaemonSocketLive(sockPath, timeoutMs = 500) {
|
|
|
1419
1434
|
});
|
|
1420
1435
|
}
|
|
1421
1436
|
function sendRequest(sockPath, msg, timeoutMs = 5e3) {
|
|
1422
|
-
return new Promise((
|
|
1437
|
+
return new Promise((resolve3, reject) => {
|
|
1423
1438
|
const conn = createConnection(sockPath);
|
|
1424
1439
|
const dec = createDecoder();
|
|
1425
1440
|
const timer = setTimeout(() => {
|
|
@@ -1435,7 +1450,7 @@ function sendRequest(sockPath, msg, timeoutMs = 5e3) {
|
|
|
1435
1450
|
if (m._v !== void 0 && m._v !== PROTOCOL_VERSION) {
|
|
1436
1451
|
reject(new Error(`squadrantd protocol v${m._v}, this client expects v${PROTOCOL_VERSION} \u2014 upgrade squadrantd or this CLI`));
|
|
1437
1452
|
} else if (m.ok) {
|
|
1438
|
-
|
|
1453
|
+
resolve3(m.reply);
|
|
1439
1454
|
} else {
|
|
1440
1455
|
reject(new Error(m.error));
|
|
1441
1456
|
}
|
|
@@ -1611,14 +1626,21 @@ function createStore(root) {
|
|
|
1611
1626
|
};
|
|
1612
1627
|
}
|
|
1613
1628
|
|
|
1629
|
+
// packages/core/dist/work-store.js
|
|
1630
|
+
import { homedir as homedir4 } from "os";
|
|
1631
|
+
import { join as join7, resolve as resolve2, sep as sep2 } from "path";
|
|
1632
|
+
import { randomBytes } from "crypto";
|
|
1633
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, readdirSync as readdirSync2, renameSync as renameSync2, writeFileSync as writeFileSync5, existsSync as existsSync7, rmSync as rmSync4, statSync as statSync2 } from "fs";
|
|
1634
|
+
var WORK_ITEM_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
1635
|
+
|
|
1614
1636
|
// packages/core/dist/index.js
|
|
1615
1637
|
init_snapshot();
|
|
1616
1638
|
|
|
1617
1639
|
// packages/core/dist/launchd.js
|
|
1618
1640
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
1619
|
-
import { mkdirSync as
|
|
1620
|
-
import { homedir as
|
|
1621
|
-
import { dirname as dirname3, join as
|
|
1641
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6, readFileSync as readFileSync7, existsSync as existsSync8, openSync, writeSync, closeSync, unlinkSync as unlinkSync2, constants } from "fs";
|
|
1642
|
+
import { homedir as homedir5 } from "os";
|
|
1643
|
+
import { dirname as dirname3, join as join8 } from "path";
|
|
1622
1644
|
import { fileURLToPath } from "url";
|
|
1623
1645
|
|
|
1624
1646
|
// packages/core/dist/crew-pane-reader.js
|
|
@@ -1686,13 +1708,13 @@ function makeGate(opts) {
|
|
|
1686
1708
|
}
|
|
1687
1709
|
|
|
1688
1710
|
// packages/core/dist/daemon/context.js
|
|
1689
|
-
import { homedir as
|
|
1690
|
-
import { join as
|
|
1711
|
+
import { homedir as homedir6 } from "os";
|
|
1712
|
+
import { join as join9 } from "path";
|
|
1691
1713
|
import { spawn as realSpawn } from "child_process";
|
|
1692
|
-
import { writeFileSync as
|
|
1714
|
+
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6 } from "fs";
|
|
1693
1715
|
|
|
1694
1716
|
// packages/core/dist/daemon/liveness-registry.js
|
|
1695
|
-
import { writeFileSync as
|
|
1717
|
+
import { writeFileSync as writeFileSync7, readFileSync as readFileSync8, renameSync as renameSync3 } from "fs";
|
|
1696
1718
|
var LivenessRegistry = class {
|
|
1697
1719
|
path;
|
|
1698
1720
|
readFile;
|
|
@@ -1702,14 +1724,14 @@ var LivenessRegistry = class {
|
|
|
1702
1724
|
this.path = opts.path;
|
|
1703
1725
|
this.readFile = opts.readFile ?? ((p) => {
|
|
1704
1726
|
try {
|
|
1705
|
-
return
|
|
1727
|
+
return readFileSync8(p, "utf-8");
|
|
1706
1728
|
} catch {
|
|
1707
1729
|
return void 0;
|
|
1708
1730
|
}
|
|
1709
1731
|
});
|
|
1710
1732
|
this.writeFile = opts.writeFile ?? ((p, c) => {
|
|
1711
|
-
|
|
1712
|
-
|
|
1733
|
+
writeFileSync7(`${p}.tmp`, c);
|
|
1734
|
+
renameSync3(`${p}.tmp`, p);
|
|
1713
1735
|
});
|
|
1714
1736
|
}
|
|
1715
1737
|
load() {
|
|
@@ -1765,18 +1787,18 @@ function defaultIsPidAlive(pid) {
|
|
|
1765
1787
|
}
|
|
1766
1788
|
}
|
|
1767
1789
|
function buildContext(opts) {
|
|
1768
|
-
const stateRoot = opts.stateRoot ??
|
|
1769
|
-
const sockPath = opts.sockPath ??
|
|
1790
|
+
const stateRoot = opts.stateRoot ?? join9(homedir6(), ".config", "squadrant", "state");
|
|
1791
|
+
const sockPath = opts.sockPath ?? join9(homedir6(), ".config", "squadrant", "squadrant.sock");
|
|
1770
1792
|
const store = createStore(stateRoot);
|
|
1771
1793
|
const bootedAt = Date.now();
|
|
1772
1794
|
const taskTimeoutMs = loadConfig().defaults.taskTimeoutMs;
|
|
1773
1795
|
const isPidAlive = opts.isPidAlive ?? defaultIsPidAlive;
|
|
1774
1796
|
const spawn2 = opts.spawn ?? realSpawn;
|
|
1775
|
-
const resultsDir =
|
|
1776
|
-
|
|
1797
|
+
const resultsDir = join9(stateRoot, "_results");
|
|
1798
|
+
mkdirSync6(resultsDir, { recursive: true });
|
|
1777
1799
|
const writeResult = (id, payload) => {
|
|
1778
|
-
const p =
|
|
1779
|
-
|
|
1800
|
+
const p = join9(resultsDir, `${id}.txt`);
|
|
1801
|
+
writeFileSync8(p, payload);
|
|
1780
1802
|
return p;
|
|
1781
1803
|
};
|
|
1782
1804
|
const log = (m) => process.stderr.write(`[squadrantd] ${(/* @__PURE__ */ new Date()).toISOString()} ${m}
|
|
@@ -1798,7 +1820,7 @@ function buildContext(opts) {
|
|
|
1798
1820
|
inFlightHeadlessIds: /* @__PURE__ */ new Set(),
|
|
1799
1821
|
activeHeadlessKills: /* @__PURE__ */ new Set(),
|
|
1800
1822
|
livenessRegistry: (() => {
|
|
1801
|
-
const r = new LivenessRegistry({ path:
|
|
1823
|
+
const r = new LivenessRegistry({ path: join9(stateRoot, "liveness.json") });
|
|
1802
1824
|
r.load();
|
|
1803
1825
|
return r;
|
|
1804
1826
|
})(),
|
|
@@ -1876,7 +1898,7 @@ function createAttach(ctx) {
|
|
|
1876
1898
|
}
|
|
1877
1899
|
|
|
1878
1900
|
// packages/core/dist/daemon/start.js
|
|
1879
|
-
import { join as
|
|
1901
|
+
import { join as join11, dirname as dirname4 } from "path";
|
|
1880
1902
|
import { readdir } from "fs/promises";
|
|
1881
1903
|
|
|
1882
1904
|
// packages/core/dist/daemon/interactive-probe.js
|
|
@@ -2087,9 +2109,11 @@ function buildSurfaceProbe(ctx, probes, daemonCmux) {
|
|
|
2087
2109
|
// packages/core/dist/delivery/defer-delivery.js
|
|
2088
2110
|
var DeferDelivery = class extends Error {
|
|
2089
2111
|
draft;
|
|
2090
|
-
|
|
2112
|
+
reason;
|
|
2113
|
+
constructor(draft = null, reason = "draft") {
|
|
2091
2114
|
super("deferred: captain composing");
|
|
2092
2115
|
this.draft = draft;
|
|
2116
|
+
this.reason = reason;
|
|
2093
2117
|
this.name = "DeferDelivery";
|
|
2094
2118
|
}
|
|
2095
2119
|
};
|
|
@@ -2107,6 +2131,7 @@ var CaptainDelivery = class {
|
|
|
2107
2131
|
deferCounts = /* @__PURE__ */ new Map();
|
|
2108
2132
|
lastContent = /* @__PURE__ */ new Map();
|
|
2109
2133
|
stableCounts = /* @__PURE__ */ new Map();
|
|
2134
|
+
lastReason = /* @__PURE__ */ new Map();
|
|
2110
2135
|
constructor(opts) {
|
|
2111
2136
|
this.opts = opts;
|
|
2112
2137
|
}
|
|
@@ -2129,29 +2154,40 @@ var CaptainDelivery = class {
|
|
|
2129
2154
|
this.deferCounts.delete(seq);
|
|
2130
2155
|
this.stableCounts.delete(seq);
|
|
2131
2156
|
this.lastContent.delete(seq);
|
|
2157
|
+
this.lastReason.delete(seq);
|
|
2132
2158
|
return { delivered: true };
|
|
2133
2159
|
} catch (e) {
|
|
2134
2160
|
if (e instanceof DeferDelivery) {
|
|
2135
2161
|
this.deferCounts.set(seq, deferCount + 1);
|
|
2136
2162
|
const content = e.draft;
|
|
2163
|
+
let stableCount;
|
|
2137
2164
|
if (content && content === this.lastContent.get(seq)) {
|
|
2138
|
-
|
|
2165
|
+
stableCount = (this.stableCounts.get(seq) ?? 0) + 1;
|
|
2166
|
+
this.stableCounts.set(seq, stableCount);
|
|
2139
2167
|
} else {
|
|
2168
|
+
stableCount = 0;
|
|
2140
2169
|
this.stableCounts.set(seq, 0);
|
|
2141
2170
|
}
|
|
2142
2171
|
this.lastContent.set(seq, content);
|
|
2143
|
-
|
|
2172
|
+
const reason = e.reason !== "draft" ? e.reason : stableCount >= this.opts.stableProbePolls ? "stable" : "draft";
|
|
2173
|
+
this.lastReason.set(seq, reason);
|
|
2174
|
+
return { deferred: true, reason };
|
|
2144
2175
|
}
|
|
2145
|
-
|
|
2176
|
+
this.lastReason.set(seq, "unknown");
|
|
2177
|
+
return { deferred: true, reason: "unknown" };
|
|
2146
2178
|
}
|
|
2147
2179
|
}
|
|
2148
2180
|
/** Read-only. Never mutates — safe to poll from the snapshot assembler every tick. */
|
|
2149
2181
|
stats() {
|
|
2150
2182
|
let maxDeferCount = 0;
|
|
2151
|
-
|
|
2152
|
-
|
|
2183
|
+
let reason;
|
|
2184
|
+
for (const [seq, c] of this.deferCounts) {
|
|
2185
|
+
if (c > maxDeferCount) {
|
|
2153
2186
|
maxDeferCount = c;
|
|
2154
|
-
|
|
2187
|
+
reason = this.lastReason.get(seq);
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
return { maxDeferCount, stuck: maxDeferCount >= this.opts.maxDefers, reason };
|
|
2155
2191
|
}
|
|
2156
2192
|
};
|
|
2157
2193
|
|
|
@@ -2251,6 +2287,10 @@ function createDelivery(ctx, daemonCmux) {
|
|
|
2251
2287
|
const notifyFault = ctx.notifyFault ?? (() => {
|
|
2252
2288
|
});
|
|
2253
2289
|
const defaultNotify = async (args) => {
|
|
2290
|
+
const fresh = store.get(args.project, args.record.id);
|
|
2291
|
+
if (fresh && TERMINAL_STATES.has(fresh.state) && fresh.state !== args.record.state) {
|
|
2292
|
+
return;
|
|
2293
|
+
}
|
|
2254
2294
|
try {
|
|
2255
2295
|
await appendToMailbox({
|
|
2256
2296
|
stateRoot,
|
|
@@ -2340,16 +2380,19 @@ function createDelivery(ctx, daemonCmux) {
|
|
|
2340
2380
|
log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=delivered`);
|
|
2341
2381
|
await writeCursor({ stateRoot, project, subscriber: CURSOR_SUBSCRIBER, lastAckedSeq: entry.seq });
|
|
2342
2382
|
} else {
|
|
2343
|
-
|
|
2383
|
+
const { maxDeferCount } = d.stats();
|
|
2384
|
+
if (maxDeferCount === 1 || maxDeferCount % 30 === 0) {
|
|
2385
|
+
log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=deferred project=${project} reason=${result.reason}`);
|
|
2386
|
+
}
|
|
2344
2387
|
break;
|
|
2345
2388
|
}
|
|
2346
2389
|
}
|
|
2347
2390
|
const stuck = d.stats().stuck;
|
|
2348
2391
|
if (stuck && !stuckNotified.has(project)) {
|
|
2349
2392
|
stuckNotified.add(project);
|
|
2350
|
-
const { maxDeferCount } = d.stats();
|
|
2351
|
-
log(`delivery stuck project=${project} deferCount=${maxDeferCount}`);
|
|
2352
|
-
const text = `\u26A0\uFE0F DELIVERY STUCK: an in-progress draft (or ghost text) in your input box has blocked pending notification(s) for ${maxDeferCount}+ retries. Your input is never touched \u2014 this keeps retrying safely and will deliver automatically once you submit or clear it.`;
|
|
2393
|
+
const { maxDeferCount, reason } = d.stats();
|
|
2394
|
+
log(`delivery stuck project=${project} deferCount=${maxDeferCount} reason=${reason ?? "unknown"}`);
|
|
2395
|
+
const text = reason === "modal" ? `\u26A0\uFE0F DELIVERY STUCK: a modal question is open in your captain pane and has blocked pending notification(s) for ${maxDeferCount}+ retries. This keeps retrying safely and will deliver automatically once you answer or dismiss it.` : `\u26A0\uFE0F DELIVERY STUCK: an in-progress draft (or ghost text) in your input box has blocked pending notification(s) for ${maxDeferCount}+ retries. Your input is never touched \u2014 this keeps retrying safely and will deliver automatically once you submit or clear it.`;
|
|
2353
2396
|
appendCaptainMessage({ stateRoot, project, text, source: "daemon" }).catch((e) => log(`delivery stuck alert failed project=${project}: ${e.message}`));
|
|
2354
2397
|
Promise.resolve(notifyFault(project, text)).catch((e) => log(`delivery stuck fault-notify failed project=${project}: ${e.message}`));
|
|
2355
2398
|
telegramBridge?.pushRaw(project, text);
|
|
@@ -2448,12 +2491,12 @@ function createServer2(ctx, handlers) {
|
|
|
2448
2491
|
|
|
2449
2492
|
// packages/core/dist/daemon/snapshot-gather.js
|
|
2450
2493
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2451
|
-
import { join as
|
|
2452
|
-
import { statSync as
|
|
2494
|
+
import { join as join10 } from "path";
|
|
2495
|
+
import { statSync as statSync3, openSync as openSync2, readSync, closeSync as closeSync2, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
|
|
2453
2496
|
var SELF_PATH = fileURLToPath2(import.meta.url);
|
|
2454
2497
|
function distBuiltAt() {
|
|
2455
2498
|
try {
|
|
2456
|
-
return
|
|
2499
|
+
return statSync3(SELF_PATH).mtimeMs;
|
|
2457
2500
|
} catch {
|
|
2458
2501
|
return 0;
|
|
2459
2502
|
}
|
|
@@ -2461,7 +2504,7 @@ function distBuiltAt() {
|
|
|
2461
2504
|
function gatherLogStats(path19, now, windowMs) {
|
|
2462
2505
|
let sizeBytes = 0;
|
|
2463
2506
|
try {
|
|
2464
|
-
sizeBytes =
|
|
2507
|
+
sizeBytes = statSync3(path19).size;
|
|
2465
2508
|
} catch {
|
|
2466
2509
|
return { errorCount: 0, sizeBytes: 0, windowMs };
|
|
2467
2510
|
}
|
|
@@ -2503,9 +2546,9 @@ function gatherStoreStats(store, stateRoot, project) {
|
|
|
2503
2546
|
for (const r of store.list(project))
|
|
2504
2547
|
byState[r.state] = (byState[r.state] ?? 0) + 1;
|
|
2505
2548
|
let corruptCount = 0;
|
|
2506
|
-
const dir =
|
|
2549
|
+
const dir = join10(stateRoot, project);
|
|
2507
2550
|
try {
|
|
2508
|
-
for (const n of
|
|
2551
|
+
for (const n of readdirSync3(dir)) {
|
|
2509
2552
|
if (n.includes(".corrupt.")) {
|
|
2510
2553
|
corruptCount++;
|
|
2511
2554
|
continue;
|
|
@@ -2513,7 +2556,7 @@ function gatherStoreStats(store, stateRoot, project) {
|
|
|
2513
2556
|
if (!n.endsWith(".json"))
|
|
2514
2557
|
continue;
|
|
2515
2558
|
try {
|
|
2516
|
-
JSON.parse(
|
|
2559
|
+
JSON.parse(readFileSync9(join10(dir, n), "utf-8"));
|
|
2517
2560
|
} catch {
|
|
2518
2561
|
corruptCount++;
|
|
2519
2562
|
}
|
|
@@ -2526,9 +2569,9 @@ function gatherResults(resultsDir) {
|
|
|
2526
2569
|
let fileCount = 0;
|
|
2527
2570
|
let totalBytes = 0;
|
|
2528
2571
|
try {
|
|
2529
|
-
for (const n of
|
|
2572
|
+
for (const n of readdirSync3(resultsDir)) {
|
|
2530
2573
|
try {
|
|
2531
|
-
const s =
|
|
2574
|
+
const s = statSync3(join10(resultsDir, n));
|
|
2532
2575
|
if (s.isFile()) {
|
|
2533
2576
|
fileCount++;
|
|
2534
2577
|
totalBytes += s.size;
|
|
@@ -2617,7 +2660,7 @@ function startDaemon(ctx, opts, pkgVersion) {
|
|
|
2617
2660
|
return out;
|
|
2618
2661
|
}
|
|
2619
2662
|
async function gatherSnapshotInputs(now) {
|
|
2620
|
-
const logPath2 =
|
|
2663
|
+
const logPath2 = join11(dirname4(stateRoot), "squadrantd.log");
|
|
2621
2664
|
const tier2Projects = opts.registeredProjects ?? Object.keys(loadConfig().projects);
|
|
2622
2665
|
const projects = await Promise.all(tier2Projects.map(async (project) => {
|
|
2623
2666
|
const cursor = await readCursor({ stateRoot, project, subscriber: CURSOR_SUBSCRIBER2 });
|
|
@@ -2751,7 +2794,7 @@ function startDaemon(ctx, opts, pkgVersion) {
|
|
|
2751
2794
|
};
|
|
2752
2795
|
let rotationTimer;
|
|
2753
2796
|
if (rotationInterval > 0) {
|
|
2754
|
-
const inboxPath =
|
|
2797
|
+
const inboxPath = join11(stateRoot, "inbox");
|
|
2755
2798
|
rotationTimer = setInterval(async () => {
|
|
2756
2799
|
try {
|
|
2757
2800
|
let entries;
|
|
@@ -2794,9 +2837,9 @@ function startDaemon(ctx, opts, pkgVersion) {
|
|
|
2794
2837
|
}
|
|
2795
2838
|
for (const kill of ctx.activeHeadlessKills)
|
|
2796
2839
|
kill();
|
|
2797
|
-
return new Promise((
|
|
2840
|
+
return new Promise((resolve3) => server.close(() => {
|
|
2798
2841
|
log(`exit-complete pid=${process.pid}`);
|
|
2799
|
-
|
|
2842
|
+
resolve3();
|
|
2800
2843
|
}));
|
|
2801
2844
|
},
|
|
2802
2845
|
tickDelivery: deliveryTick,
|
|
@@ -2985,7 +3028,7 @@ function createIsCaptainAlive(sock) {
|
|
|
2985
3028
|
};
|
|
2986
3029
|
}
|
|
2987
3030
|
function createLaunch(cliBin, log) {
|
|
2988
|
-
return (project) => new Promise((
|
|
3031
|
+
return (project) => new Promise((resolve3, reject) => {
|
|
2989
3032
|
execFile(process.execPath, [cliBin, "launch", project, "--headless"], { timeout: 3e4 }, (err, stdout, stderr) => {
|
|
2990
3033
|
const output = capOutput(stdout ?? "", stderr ?? "");
|
|
2991
3034
|
if (err) {
|
|
@@ -2995,7 +3038,7 @@ function createLaunch(cliBin, log) {
|
|
|
2995
3038
|
}
|
|
2996
3039
|
if (output !== "(no output)")
|
|
2997
3040
|
log?.(`launch ${project}: ${output}`);
|
|
2998
|
-
|
|
3041
|
+
resolve3();
|
|
2999
3042
|
});
|
|
3000
3043
|
});
|
|
3001
3044
|
}
|
|
@@ -3117,10 +3160,10 @@ function findProjectByThread(stateRoot, threadId) {
|
|
|
3117
3160
|
for (const [key, id] of Object.entries(s.topics)) {
|
|
3118
3161
|
if (id !== threadId)
|
|
3119
3162
|
continue;
|
|
3120
|
-
const
|
|
3121
|
-
if (
|
|
3163
|
+
const sep3 = key.indexOf("::");
|
|
3164
|
+
if (sep3 === -1)
|
|
3122
3165
|
continue;
|
|
3123
|
-
return { project: key.slice(0,
|
|
3166
|
+
return { project: key.slice(0, sep3), scope: key.slice(sep3 + 2) };
|
|
3124
3167
|
}
|
|
3125
3168
|
return null;
|
|
3126
3169
|
}
|
|
@@ -3646,16 +3689,16 @@ import fs12 from "fs";
|
|
|
3646
3689
|
|
|
3647
3690
|
// packages/core/dist/restart-daemon.js
|
|
3648
3691
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
3649
|
-
import { existsSync as
|
|
3650
|
-
import { homedir as
|
|
3651
|
-
import { join as
|
|
3652
|
-
var DEFAULT_SOCK_PATH =
|
|
3692
|
+
import { existsSync as existsSync9 } from "fs";
|
|
3693
|
+
import { homedir as homedir7 } from "os";
|
|
3694
|
+
import { join as join12 } from "path";
|
|
3695
|
+
var DEFAULT_SOCK_PATH = join12(homedir7(), ".config", "squadrant", "squadrant.sock");
|
|
3653
3696
|
|
|
3654
3697
|
// packages/core/dist/group-dispatch.js
|
|
3655
3698
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3656
|
-
import { homedir as
|
|
3657
|
-
import { join as
|
|
3658
|
-
var DEFAULT_SOCK_PATH2 =
|
|
3699
|
+
import { homedir as homedir8 } from "os";
|
|
3700
|
+
import { join as join13 } from "path";
|
|
3701
|
+
var DEFAULT_SOCK_PATH2 = join13(homedir8(), ".config", "squadrant", "squadrant.sock");
|
|
3659
3702
|
|
|
3660
3703
|
// packages/core/dist/side-session.js
|
|
3661
3704
|
import fs13 from "fs";
|
|
@@ -3772,8 +3815,8 @@ var AppServerClient = class extends EventEmitter {
|
|
|
3772
3815
|
const info = this.opts.clientInfo ?? { name: "squadrant", version: "0" };
|
|
3773
3816
|
const id = this.nextId++;
|
|
3774
3817
|
const env = { jsonrpc: "2.0", id, method: "initialize", params: { clientInfo: info } };
|
|
3775
|
-
const res = await new Promise((
|
|
3776
|
-
this.pending.set(id, { resolve:
|
|
3818
|
+
const res = await new Promise((resolve3, reject) => {
|
|
3819
|
+
this.pending.set(id, { resolve: resolve3, reject });
|
|
3777
3820
|
this.proc.stdin.write(JSON.stringify(env) + "\n");
|
|
3778
3821
|
});
|
|
3779
3822
|
this.proc.stdin.write(JSON.stringify({ jsonrpc: "2.0", method: "initialized" }) + "\n");
|
|
@@ -3806,13 +3849,13 @@ var AppServerClient = class extends EventEmitter {
|
|
|
3806
3849
|
const turnId = ack?.turn?.id;
|
|
3807
3850
|
if (typeof turnId !== "string")
|
|
3808
3851
|
throw new Error(`turn/start: unexpected ack shape (no turn.id): ${JSON.stringify(ack).slice(0, 200)}`);
|
|
3809
|
-
return new Promise((
|
|
3852
|
+
return new Promise((resolve3, reject) => {
|
|
3810
3853
|
const onNote = (n) => {
|
|
3811
3854
|
if (n.params?.turn?.id !== turnId)
|
|
3812
3855
|
return;
|
|
3813
3856
|
if (n.method === "turn/completed") {
|
|
3814
3857
|
cleanup();
|
|
3815
|
-
|
|
3858
|
+
resolve3({ turnId });
|
|
3816
3859
|
}
|
|
3817
3860
|
if (n.method === "turn/failed") {
|
|
3818
3861
|
cleanup();
|
|
@@ -3863,8 +3906,8 @@ var AppServerClient = class extends EventEmitter {
|
|
|
3863
3906
|
throw new Error("AppServerClient not started");
|
|
3864
3907
|
const id = this.nextId++;
|
|
3865
3908
|
const env = { jsonrpc: "2.0", id, method, params: params ?? {} };
|
|
3866
|
-
return new Promise((
|
|
3867
|
-
this.pending.set(id, { resolve:
|
|
3909
|
+
return new Promise((resolve3, reject) => {
|
|
3910
|
+
this.pending.set(id, { resolve: resolve3, reject });
|
|
3868
3911
|
this.proc.stdin.write(JSON.stringify(env) + "\n");
|
|
3869
3912
|
});
|
|
3870
3913
|
}
|
|
@@ -3992,11 +4035,11 @@ function toSnapshot(ev) {
|
|
|
3992
4035
|
|
|
3993
4036
|
// packages/agents/dist/codex/config.js
|
|
3994
4037
|
import { readFile as readFile5 } from "fs/promises";
|
|
3995
|
-
import { homedir as
|
|
3996
|
-
import { join as
|
|
4038
|
+
import { homedir as homedir9 } from "os";
|
|
4039
|
+
import { join as join14 } from "path";
|
|
3997
4040
|
async function resolveCodexModel() {
|
|
3998
|
-
const home = process.env["CODEX_HOME"] ??
|
|
3999
|
-
const configPath =
|
|
4041
|
+
const home = process.env["CODEX_HOME"] ?? join14(homedir9(), ".codex");
|
|
4042
|
+
const configPath = join14(home, "config.toml");
|
|
4000
4043
|
let text;
|
|
4001
4044
|
try {
|
|
4002
4045
|
text = await readFile5(configPath, "utf8");
|
|
@@ -4309,11 +4352,11 @@ function buildCodexDeveloperInstructions(rec) {
|
|
|
4309
4352
|
${directive}` : directive;
|
|
4310
4353
|
}
|
|
4311
4354
|
function withTimeout(p, ms, msg) {
|
|
4312
|
-
return new Promise((
|
|
4355
|
+
return new Promise((resolve3, reject) => {
|
|
4313
4356
|
const t = setTimeout(() => reject(new Error(msg)), ms);
|
|
4314
4357
|
p.then((v) => {
|
|
4315
4358
|
clearTimeout(t);
|
|
4316
|
-
|
|
4359
|
+
resolve3(v);
|
|
4317
4360
|
}, (e) => {
|
|
4318
4361
|
clearTimeout(t);
|
|
4319
4362
|
reject(e);
|
|
@@ -4485,9 +4528,9 @@ var OpencodeSseBridge = class {
|
|
|
4485
4528
|
|
|
4486
4529
|
// packages/agents/dist/interactive/claude.js
|
|
4487
4530
|
import { execSync as execSync6 } from "child_process";
|
|
4488
|
-
import { readFileSync as
|
|
4489
|
-
import { homedir as
|
|
4490
|
-
import { join as
|
|
4531
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
4532
|
+
import { homedir as homedir10 } from "os";
|
|
4533
|
+
import { join as join15 } from "path";
|
|
4491
4534
|
var nextAskUserQuestionRequestId = Date.now();
|
|
4492
4535
|
|
|
4493
4536
|
// packages/agents/dist/headless/types.js
|
|
@@ -4620,14 +4663,14 @@ function runHeadless(opts) {
|
|
|
4620
4663
|
if (err.length > ERR_CAP)
|
|
4621
4664
|
err = err.slice(err.length - ERR_CAP);
|
|
4622
4665
|
});
|
|
4623
|
-
const result = new Promise((
|
|
4666
|
+
const result = new Promise((resolve3) => {
|
|
4624
4667
|
child.once("error", (e) => {
|
|
4625
4668
|
if (debounceTimer) {
|
|
4626
4669
|
clearTimeout(debounceTimer);
|
|
4627
4670
|
debounceTimer = null;
|
|
4628
4671
|
}
|
|
4629
4672
|
opts.emit({ type: "task.failed", id: opts.id, error: `spawn error: ${e.message}`, exitCode: void 0 });
|
|
4630
|
-
|
|
4673
|
+
resolve3();
|
|
4631
4674
|
});
|
|
4632
4675
|
child.on("close", (code) => {
|
|
4633
4676
|
if (chunksSinceProgress > 0)
|
|
@@ -4644,7 +4687,7 @@ function runHeadless(opts) {
|
|
|
4644
4687
|
const ref = opts.writeResult ? opts.writeResult(opts.id, res.payload ?? "") : "";
|
|
4645
4688
|
opts.emit({ type: "task.done", id: opts.id, resultRef: ref, parseWarning: res.parseWarning });
|
|
4646
4689
|
}
|
|
4647
|
-
|
|
4690
|
+
resolve3();
|
|
4648
4691
|
});
|
|
4649
4692
|
});
|
|
4650
4693
|
return { result, kill: () => child.kill("SIGTERM") };
|
|
@@ -4660,7 +4703,7 @@ var CmuxTimeoutError = class extends Error {
|
|
|
4660
4703
|
}
|
|
4661
4704
|
};
|
|
4662
4705
|
function cmux(args) {
|
|
4663
|
-
return new Promise((
|
|
4706
|
+
return new Promise((resolve3, reject) => {
|
|
4664
4707
|
execFile2(
|
|
4665
4708
|
resolveCmuxBin(),
|
|
4666
4709
|
args,
|
|
@@ -4674,19 +4717,19 @@ function cmux(args) {
|
|
|
4674
4717
|
reject(err.code === "ETIMEDOUT" ? new CmuxTimeoutError(args.join(" ")) : err);
|
|
4675
4718
|
return;
|
|
4676
4719
|
}
|
|
4677
|
-
|
|
4720
|
+
resolve3(stdout.trim());
|
|
4678
4721
|
}
|
|
4679
4722
|
);
|
|
4680
4723
|
});
|
|
4681
4724
|
}
|
|
4682
4725
|
function cmuxStdin(args, input) {
|
|
4683
|
-
return new Promise((
|
|
4726
|
+
return new Promise((resolve3, reject) => {
|
|
4684
4727
|
const child = execFile2(resolveCmuxBin(), args, { encoding: "utf-8", timeout: CMUX_TIMEOUT, env: { ...process.env, CMUX_QUIET: "1" } }, (err, stdout) => {
|
|
4685
4728
|
if (err) {
|
|
4686
4729
|
reject(err.code === "ETIMEDOUT" ? new CmuxTimeoutError(args.join(" ")) : err);
|
|
4687
4730
|
return;
|
|
4688
4731
|
}
|
|
4689
|
-
|
|
4732
|
+
resolve3(stdout.trim());
|
|
4690
4733
|
});
|
|
4691
4734
|
child.stdin.end(input);
|
|
4692
4735
|
});
|
|
@@ -5049,9 +5092,9 @@ function createCmuxDriver() {
|
|
|
5049
5092
|
}
|
|
5050
5093
|
const draft = parseDraftFromScreen(screen);
|
|
5051
5094
|
if (draft === null)
|
|
5052
|
-
throw new DeferDelivery(null);
|
|
5095
|
+
throw new DeferDelivery(null, "no-box");
|
|
5053
5096
|
if (hasModalOptionList(screen))
|
|
5054
|
-
throw new DeferDelivery(null);
|
|
5097
|
+
throw new DeferDelivery(null, "modal");
|
|
5055
5098
|
if (draft === "") {
|
|
5056
5099
|
await deliver();
|
|
5057
5100
|
return;
|
|
@@ -5245,7 +5288,7 @@ var NotifierRegistry = class {
|
|
|
5245
5288
|
|
|
5246
5289
|
// packages/workspaces/dist/workspaces/obsidian.js
|
|
5247
5290
|
import fs16 from "fs/promises";
|
|
5248
|
-
import { existsSync as
|
|
5291
|
+
import { existsSync as existsSync10 } from "fs";
|
|
5249
5292
|
import path17 from "path";
|
|
5250
5293
|
|
|
5251
5294
|
// packages/workspaces/dist/cmux-daemon/events-bridge.js
|
|
@@ -5314,12 +5357,12 @@ var CmuxEventsBridge = class {
|
|
|
5314
5357
|
continue;
|
|
5315
5358
|
}
|
|
5316
5359
|
this.child = child;
|
|
5317
|
-
await new Promise((
|
|
5360
|
+
await new Promise((resolve3) => {
|
|
5318
5361
|
let settled = false;
|
|
5319
5362
|
const done = () => {
|
|
5320
5363
|
if (!settled) {
|
|
5321
5364
|
settled = true;
|
|
5322
|
-
|
|
5365
|
+
resolve3();
|
|
5323
5366
|
}
|
|
5324
5367
|
};
|
|
5325
5368
|
child.stdout?.on("data", (b) => this.onData(b));
|
|
@@ -5383,9 +5426,9 @@ var CmuxEventsBridge = class {
|
|
|
5383
5426
|
};
|
|
5384
5427
|
|
|
5385
5428
|
// packages/workspaces/dist/cmux-daemon/daemon-cmux.js
|
|
5386
|
-
import { readdirSync as
|
|
5387
|
-
import { join as
|
|
5388
|
-
import { homedir as
|
|
5429
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync11 } from "fs";
|
|
5430
|
+
import { join as join16 } from "path";
|
|
5431
|
+
import { homedir as homedir11 } from "os";
|
|
5389
5432
|
|
|
5390
5433
|
// packages/workspaces/dist/cmux-daemon/store-fingerprint.js
|
|
5391
5434
|
function roleFromTemplate(args) {
|
|
@@ -5504,22 +5547,22 @@ var DaemonCmux = class {
|
|
|
5504
5547
|
* file failed to read/parse — see the class doc above.
|
|
5505
5548
|
*/
|
|
5506
5549
|
async liveness() {
|
|
5507
|
-
const dir = process.env.CMUX_AGENT_HOOK_STATE_DIR ??
|
|
5550
|
+
const dir = process.env.CMUX_AGENT_HOOK_STATE_DIR ?? join16(homedir11(), ".cmuxterm");
|
|
5508
5551
|
const projects = loadConfig().projects;
|
|
5509
5552
|
let files;
|
|
5510
5553
|
try {
|
|
5511
|
-
files =
|
|
5554
|
+
files = readdirSync4(dir).filter((f) => f.endsWith("-hook-sessions.json") && !f.endsWith(".lock"));
|
|
5512
5555
|
} catch (e) {
|
|
5513
5556
|
throw new Error(`liveness: could not read cmux state dir ${dir}: ${e.message}`);
|
|
5514
5557
|
}
|
|
5515
|
-
return readLivenessSnapshot(files, (f) =>
|
|
5558
|
+
return readLivenessSnapshot(files, (f) => readFileSync11(join16(dir, f), "utf-8"), projects);
|
|
5516
5559
|
}
|
|
5517
5560
|
};
|
|
5518
5561
|
|
|
5519
5562
|
// packages/workspaces/dist/cmux-daemon/cmux-store-source.js
|
|
5520
|
-
import { join as
|
|
5521
|
-
import { homedir as
|
|
5522
|
-
import { watch, readdirSync as
|
|
5563
|
+
import { join as join17 } from "path";
|
|
5564
|
+
import { homedir as homedir12 } from "os";
|
|
5565
|
+
import { watch, readdirSync as readdirSync5, readFileSync as readFileSync12, existsSync as existsSync11 } from "fs";
|
|
5523
5566
|
var CmuxStoreSource = class {
|
|
5524
5567
|
name = "cmux-store";
|
|
5525
5568
|
stateDir;
|
|
@@ -5540,12 +5583,12 @@ var CmuxStoreSource = class {
|
|
|
5540
5583
|
active = false;
|
|
5541
5584
|
lastError = null;
|
|
5542
5585
|
constructor(opts = {}) {
|
|
5543
|
-
this.stateDir = opts.stateDir ?? process.env.CMUX_AGENT_HOOK_STATE_DIR ??
|
|
5586
|
+
this.stateDir = opts.stateDir ?? process.env.CMUX_AGENT_HOOK_STATE_DIR ?? join17(homedir12(), ".cmuxterm");
|
|
5544
5587
|
this.debounceMs = opts.debounceMs ?? 50;
|
|
5545
5588
|
this.isPidAlive = opts.isPidAlive ?? defaultIsPidAlive2;
|
|
5546
5589
|
this.listFiles = opts.listFiles ?? defaultListFiles;
|
|
5547
5590
|
this.readFile = opts.readFile ?? defaultReadFile;
|
|
5548
|
-
this.fileExists = opts.fileExists ??
|
|
5591
|
+
this.fileExists = opts.fileExists ?? existsSync11;
|
|
5549
5592
|
this.watchDir = opts.watchDir ?? defaultWatchDir;
|
|
5550
5593
|
this.scheduleTimer = opts.scheduleTimer ?? setTimeout;
|
|
5551
5594
|
this.cancelTimer = opts.cancelTimer ?? clearTimeout;
|
|
@@ -5602,7 +5645,7 @@ var CmuxStoreSource = class {
|
|
|
5602
5645
|
}
|
|
5603
5646
|
scanFile(filename) {
|
|
5604
5647
|
const deps = this.deps;
|
|
5605
|
-
const filePath =
|
|
5648
|
+
const filePath = join17(this.stateDir, filename);
|
|
5606
5649
|
const lockPath = `${filePath}.lock`;
|
|
5607
5650
|
if (this.fileExists(lockPath)) {
|
|
5608
5651
|
this.log(`cmux-store: skipping ${filename} (locked)`);
|
|
@@ -5668,14 +5711,14 @@ function defaultIsPidAlive2(pid) {
|
|
|
5668
5711
|
}
|
|
5669
5712
|
function defaultListFiles(dir) {
|
|
5670
5713
|
try {
|
|
5671
|
-
return
|
|
5714
|
+
return readdirSync5(dir).filter((f) => f.endsWith("-hook-sessions.json") && !f.endsWith(".lock"));
|
|
5672
5715
|
} catch {
|
|
5673
5716
|
return [];
|
|
5674
5717
|
}
|
|
5675
5718
|
}
|
|
5676
5719
|
function defaultReadFile(path19) {
|
|
5677
5720
|
try {
|
|
5678
|
-
return
|
|
5721
|
+
return readFileSync12(path19, "utf-8");
|
|
5679
5722
|
} catch {
|
|
5680
5723
|
return void 0;
|
|
5681
5724
|
}
|
|
@@ -5690,9 +5733,9 @@ function defaultWatchDir(dir, cb) {
|
|
|
5690
5733
|
}
|
|
5691
5734
|
|
|
5692
5735
|
// packages/workspaces/dist/native-hooks/native-hook-source.js
|
|
5693
|
-
import { join as
|
|
5694
|
-
import { homedir as
|
|
5695
|
-
import { mkdirSync as
|
|
5736
|
+
import { join as join18 } from "path";
|
|
5737
|
+
import { homedir as homedir13 } from "os";
|
|
5738
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "fs";
|
|
5696
5739
|
var CLAUDE_HOOK_EVENTS = [
|
|
5697
5740
|
["SessionStart", "session-start"],
|
|
5698
5741
|
["UserPromptSubmit", "prompt-submit"],
|
|
@@ -5704,7 +5747,7 @@ var CLAUDE_HOOK_EVENTS = [
|
|
|
5704
5747
|
];
|
|
5705
5748
|
var DEFAULT_HOOK_CMD = "squadrant hooks";
|
|
5706
5749
|
function installClaudeHooks(opts = {}) {
|
|
5707
|
-
const settingsPath = opts.settingsPath ??
|
|
5750
|
+
const settingsPath = opts.settingsPath ?? join18(homedir13(), ".claude", "settings.json");
|
|
5708
5751
|
const hookCmd = opts.hookCmd ?? DEFAULT_HOOK_CMD;
|
|
5709
5752
|
const readFile6 = opts.readFile ?? defaultReadFile2;
|
|
5710
5753
|
const writeFile5 = opts.writeFile ?? defaultWriteFile;
|
|
@@ -5712,6 +5755,7 @@ function installClaudeHooks(opts = {}) {
|
|
|
5712
5755
|
});
|
|
5713
5756
|
let settings = {};
|
|
5714
5757
|
const raw = readFile6(settingsPath);
|
|
5758
|
+
const hadExistingSettings = raw !== void 0;
|
|
5715
5759
|
if (raw) {
|
|
5716
5760
|
try {
|
|
5717
5761
|
settings = JSON.parse(raw);
|
|
@@ -5724,6 +5768,7 @@ function installClaudeHooks(opts = {}) {
|
|
|
5724
5768
|
}
|
|
5725
5769
|
const hooks = settings.hooks;
|
|
5726
5770
|
let changed = false;
|
|
5771
|
+
const repaired = [];
|
|
5727
5772
|
for (const [eventName, sub, matcher] of CLAUDE_HOOK_EVENTS) {
|
|
5728
5773
|
if (!Array.isArray(hooks[eventName])) {
|
|
5729
5774
|
hooks[eventName] = [];
|
|
@@ -5735,6 +5780,26 @@ function installClaudeHooks(opts = {}) {
|
|
|
5735
5780
|
if (!alreadyPresent) {
|
|
5736
5781
|
entries.push({ matcher: hookMatcher, hooks: [{ type: "command", command, timeout: 10 }] });
|
|
5737
5782
|
changed = true;
|
|
5783
|
+
repaired.push(`${eventName}/${sub}`);
|
|
5784
|
+
}
|
|
5785
|
+
}
|
|
5786
|
+
if (repaired.length > 0 && hadExistingSettings) {
|
|
5787
|
+
log(`native-hook: repaired ${repaired.length} missing squadrant hook(s) in ${settingsPath} [${repaired.join(", ")}] \u2014 WARNING: blocked-signalling or lifecycle tracking may have been broken until this run`);
|
|
5788
|
+
}
|
|
5789
|
+
if (opts.claudeEnv && Object.keys(opts.claudeEnv).length > 0) {
|
|
5790
|
+
if (typeof settings.env !== "object" || settings.env === null || Array.isArray(settings.env)) {
|
|
5791
|
+
settings.env = {};
|
|
5792
|
+
}
|
|
5793
|
+
const env = settings.env;
|
|
5794
|
+
for (const [key, value] of Object.entries(opts.claudeEnv)) {
|
|
5795
|
+
if (key in env) {
|
|
5796
|
+
if (env[key] !== value) {
|
|
5797
|
+
log(`native-hook: claudeEnv key '${key}' already set to '${String(env[key])}' in ${settingsPath} \u2014 not overwriting with '${value}'`);
|
|
5798
|
+
}
|
|
5799
|
+
continue;
|
|
5800
|
+
}
|
|
5801
|
+
env[key] = value;
|
|
5802
|
+
changed = true;
|
|
5738
5803
|
}
|
|
5739
5804
|
}
|
|
5740
5805
|
if (changed) {
|
|
@@ -5771,9 +5836,9 @@ var NativeHookSource = class {
|
|
|
5771
5836
|
cache = /* @__PURE__ */ new Map();
|
|
5772
5837
|
active = false;
|
|
5773
5838
|
constructor(opts = {}) {
|
|
5774
|
-
this.hookInstall = opts.hookInstall ?? {};
|
|
5775
5839
|
this.log = opts.log ?? (() => {
|
|
5776
5840
|
});
|
|
5841
|
+
this.hookInstall = { log: this.log, ...opts.hookInstall };
|
|
5777
5842
|
}
|
|
5778
5843
|
start(deps) {
|
|
5779
5844
|
this.deps = deps;
|
|
@@ -5852,14 +5917,14 @@ function extractDetail(sub, payload) {
|
|
|
5852
5917
|
}
|
|
5853
5918
|
function defaultReadFile2(path19) {
|
|
5854
5919
|
try {
|
|
5855
|
-
return
|
|
5920
|
+
return readFileSync13(path19, "utf-8");
|
|
5856
5921
|
} catch {
|
|
5857
5922
|
return void 0;
|
|
5858
5923
|
}
|
|
5859
5924
|
}
|
|
5860
5925
|
function defaultWriteFile(path19, content) {
|
|
5861
|
-
|
|
5862
|
-
|
|
5926
|
+
mkdirSync7(path19.replace(/\/[^/]+$/, ""), { recursive: true });
|
|
5927
|
+
writeFileSync9(path19, content, "utf-8");
|
|
5863
5928
|
}
|
|
5864
5929
|
|
|
5865
5930
|
// packages/workspaces/dist/crew-pane.js
|
|
@@ -5983,12 +6048,12 @@ async function maybeBroadcastDaemonRestart(opts) {
|
|
|
5983
6048
|
|
|
5984
6049
|
// packages/cli/src/squadrantd.ts
|
|
5985
6050
|
var SELF_PATH2 = fileURLToPath3(import.meta.url);
|
|
5986
|
-
var CLI_BIN =
|
|
5987
|
-
var DAEMON_SOCK =
|
|
6051
|
+
var CLI_BIN = join19(dirname5(SELF_PATH2), "index.js");
|
|
6052
|
+
var DAEMON_SOCK = join19(homedir14(), ".config", "squadrant", "squadrant.sock");
|
|
5988
6053
|
function readPkgVersion() {
|
|
5989
6054
|
try {
|
|
5990
|
-
const pkgPath =
|
|
5991
|
-
return JSON.parse(
|
|
6055
|
+
const pkgPath = join19(dirname5(SELF_PATH2), "..", "package.json");
|
|
6056
|
+
return JSON.parse(readFileSync14(pkgPath, "utf-8")).version ?? "unknown";
|
|
5992
6057
|
} catch {
|
|
5993
6058
|
return "unknown";
|
|
5994
6059
|
}
|
|
@@ -6081,11 +6146,11 @@ function startSquadrantd(opts = {}) {
|
|
|
6081
6146
|
(r) => r.mode === "interactive" && !TERMINAL_STATES.has(r.state) && r.cwd === hook.cwd
|
|
6082
6147
|
);
|
|
6083
6148
|
},
|
|
6084
|
-
cursorFile:
|
|
6149
|
+
cursorFile: join19(stateRoot, "cmux-events.seq"),
|
|
6085
6150
|
log
|
|
6086
6151
|
});
|
|
6087
6152
|
const cmuxStoreSource = new CmuxStoreSource({ log });
|
|
6088
|
-
const nativeHookSource = new NativeHookSource({ log });
|
|
6153
|
+
const nativeHookSource = new NativeHookSource({ log, hookInstall: { claudeEnv: loadConfig().defaults.claudeEnv } });
|
|
6089
6154
|
ctx.codexDriver = codexDriver;
|
|
6090
6155
|
ctx.opencodeBridge = opencodeBridge;
|
|
6091
6156
|
ctx.cmuxEventsBridge = cmuxEventsBridge;
|
|
@@ -6240,7 +6305,7 @@ ${buildCompletionProtocol(rec.id, rec.project)}`;
|
|
|
6240
6305
|
}
|
|
6241
6306
|
if (!process.env.VITEST) {
|
|
6242
6307
|
try {
|
|
6243
|
-
const buildMtimeMs =
|
|
6308
|
+
const buildMtimeMs = statSync4(SELF_PATH2).mtimeMs;
|
|
6244
6309
|
const restartConfig = loadConfig();
|
|
6245
6310
|
const registry = new RuntimeRegistry({ cmux: createCmuxDriver() });
|
|
6246
6311
|
void maybeBroadcastDaemonRestart({
|
|
@@ -6293,7 +6358,7 @@ if (process.argv[1] && process.argv[1].endsWith("squadrantd.js")) {
|
|
|
6293
6358
|
process.stdout.write("squadrantd: launchd-managed daemon entry (no CLI args). Use `squadrant` for commands.\n");
|
|
6294
6359
|
process.exit(0);
|
|
6295
6360
|
}
|
|
6296
|
-
const sock =
|
|
6361
|
+
const sock = join19(homedir14(), ".config", "squadrant", "squadrant.sock");
|
|
6297
6362
|
if (await isDaemonSocketLive(sock)) {
|
|
6298
6363
|
process.stderr.write(`[squadrantd] refusing to start: a live daemon already owns ${sock}
|
|
6299
6364
|
`);
|