squadrant 0.19.3 → 0.19.5
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 +358 -39
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +151 -19
- package/dist/squadrantd.js.map +1 -1
- package/package.json +1 -1
package/dist/squadrantd.js
CHANGED
|
@@ -887,6 +887,11 @@ function reduce(rec, ev, now) {
|
|
|
887
887
|
return stampAttempt(base, { resumeRef: ev.resumeRef }, now);
|
|
888
888
|
case "task.turn.started":
|
|
889
889
|
return { ...stampAttempt(base, {}, now), state: "working", pendingTool: void 0, pendingMonitor: void 0 };
|
|
890
|
+
case "task.turn.failed":
|
|
891
|
+
// #763: a crew's turn died on an API error (StopFailure hook). Anti-#2576:
|
|
892
|
+
// NOT task.failed — a hook may never terminalize a task. Falls through to
|
|
893
|
+
// the exact same turn-boundary transition as task.turn.completed below;
|
|
894
|
+
// only the notify message (reduce.ts formatMessage) differs.
|
|
890
895
|
case "task.turn.completed":
|
|
891
896
|
if (isStickyAttention(rec.state))
|
|
892
897
|
return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
|
|
@@ -1002,6 +1007,9 @@ function formatMessage(rec, event) {
|
|
|
1002
1007
|
return `CREW STALLED ${tag}: no heartbeat in ${rec.heartbeatBudgetMs}ms`;
|
|
1003
1008
|
}
|
|
1004
1009
|
case "awaiting-input":
|
|
1010
|
+
if (event?.type === "task.turn.failed") {
|
|
1011
|
+
return `CREW TURN FAILED ${tag}: API error ended the turn (not a stall) \u2014 ${event.error}`;
|
|
1012
|
+
}
|
|
1005
1013
|
return `CREW IDLE ${tag}: turn ended, awaiting your reply.`;
|
|
1006
1014
|
default:
|
|
1007
1015
|
return null;
|
|
@@ -1411,6 +1419,7 @@ var init_reduce = __esm({
|
|
|
1411
1419
|
"task.session",
|
|
1412
1420
|
"task.turn.started",
|
|
1413
1421
|
"task.turn.completed",
|
|
1422
|
+
"task.turn.failed",
|
|
1414
1423
|
"task.delta",
|
|
1415
1424
|
"task.input.requested",
|
|
1416
1425
|
"task.approval.requested",
|
|
@@ -2322,6 +2331,26 @@ function parseProgramArgs(plistXml) {
|
|
|
2322
2331
|
return null;
|
|
2323
2332
|
return { nodeBin: xmlUnescape(m[1]), daemonEntry: xmlUnescape(m[2]) };
|
|
2324
2333
|
}
|
|
2334
|
+
function isVersionUpgrade(registeredEntry, thisEntry) {
|
|
2335
|
+
const a = registeredEntry.split("/");
|
|
2336
|
+
const b = thisEntry.split("/");
|
|
2337
|
+
if (a.length !== b.length)
|
|
2338
|
+
return false;
|
|
2339
|
+
let diffIndex = -1;
|
|
2340
|
+
for (let i = 0; i < a.length; i++) {
|
|
2341
|
+
if (a[i] !== b[i]) {
|
|
2342
|
+
if (diffIndex !== -1)
|
|
2343
|
+
return false;
|
|
2344
|
+
diffIndex = i;
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
if (diffIndex === -1)
|
|
2348
|
+
return false;
|
|
2349
|
+
const versionedSegment = /^(.+)@([^@]+)$/;
|
|
2350
|
+
const ma = a[diffIndex].match(versionedSegment);
|
|
2351
|
+
const mb = b[diffIndex].match(versionedSegment);
|
|
2352
|
+
return ma !== null && mb !== null && ma[1] === mb[1] && ma[2] !== mb[2];
|
|
2353
|
+
}
|
|
2325
2354
|
function detectForeignInstall(parsed, thisEntry, registeredEntryExists) {
|
|
2326
2355
|
if (!parsed)
|
|
2327
2356
|
return null;
|
|
@@ -2329,7 +2358,11 @@ function detectForeignInstall(parsed, thisEntry, registeredEntryExists) {
|
|
|
2329
2358
|
return null;
|
|
2330
2359
|
if (!registeredEntryExists)
|
|
2331
2360
|
return null;
|
|
2332
|
-
return {
|
|
2361
|
+
return {
|
|
2362
|
+
registeredEntry: parsed.daemonEntry,
|
|
2363
|
+
thisEntry,
|
|
2364
|
+
isUpgrade: isVersionUpgrade(parsed.daemonEntry, thisEntry)
|
|
2365
|
+
};
|
|
2333
2366
|
}
|
|
2334
2367
|
function sanitizePathForPlist(path21) {
|
|
2335
2368
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2496,7 +2529,7 @@ function forceKickstartAndVerify(target, opts = {}) {
|
|
|
2496
2529
|
const pollDelayMs = opts.pollDelayMs ?? 300;
|
|
2497
2530
|
const kickstartRetries = opts.kickstartRetries ?? 10;
|
|
2498
2531
|
const kickstartRetryDelayMs = opts.kickstartRetryDelayMs ?? 300;
|
|
2499
|
-
const pidBefore = getDaemonPid(target);
|
|
2532
|
+
const pidBefore = opts.pidBefore !== void 0 ? opts.pidBefore : getDaemonPid(target);
|
|
2500
2533
|
let kickstartError = null;
|
|
2501
2534
|
for (let i = 0; i < kickstartRetries; i++) {
|
|
2502
2535
|
try {
|
|
@@ -2533,6 +2566,9 @@ function forceKickstartAndVerify(target, opts = {}) {
|
|
|
2533
2566
|
function isOperatorInitiatedCommand(topLevelArg) {
|
|
2534
2567
|
return topLevelArg !== void 0 && OPERATOR_INITIATED_COMMANDS.has(topLevelArg);
|
|
2535
2568
|
}
|
|
2569
|
+
function isReadOnlyCrewCommand(argv) {
|
|
2570
|
+
return argv[2] === "crew" && argv[3] !== void 0 && READ_ONLY_CREW_SUBCOMMANDS.has(argv[3]);
|
|
2571
|
+
}
|
|
2536
2572
|
function ensureDaemon(nodeBin = process.execPath, opts = {}) {
|
|
2537
2573
|
if (restartInFlight)
|
|
2538
2574
|
return;
|
|
@@ -2553,8 +2589,10 @@ function ensureDaemon(nodeBin = process.execPath, opts = {}) {
|
|
|
2553
2589
|
try {
|
|
2554
2590
|
const drift = computeDaemonDrift(nodeBin);
|
|
2555
2591
|
if (drift.foreignInstall) {
|
|
2556
|
-
|
|
2557
|
-
|
|
2592
|
+
const decision = decideForeignInstall(drift.foreignInstall);
|
|
2593
|
+
process.stderr.write(decision.message);
|
|
2594
|
+
if (!decision.proceed)
|
|
2595
|
+
return;
|
|
2558
2596
|
}
|
|
2559
2597
|
applyDaemonDrift(drift);
|
|
2560
2598
|
} catch (e) {
|
|
@@ -2564,6 +2602,12 @@ function ensureDaemon(nodeBin = process.execPath, opts = {}) {
|
|
|
2564
2602
|
releaseDaemonLock();
|
|
2565
2603
|
}
|
|
2566
2604
|
}
|
|
2605
|
+
function decideForeignInstall(foreign) {
|
|
2606
|
+
if (foreign.isUpgrade) {
|
|
2607
|
+
return { proceed: true, message: printVersionUpgradeNotice(foreign) };
|
|
2608
|
+
}
|
|
2609
|
+
return { proceed: false, message: printForeignInstallError(foreign) };
|
|
2610
|
+
}
|
|
2567
2611
|
function printForeignInstallError(foreign) {
|
|
2568
2612
|
return `[squadrant] refusing to restart the daemon: the registered launchd config belongs to a DIFFERENT squadrant install than this one.
|
|
2569
2613
|
registered install: ${foreign.registeredEntry}
|
|
@@ -2571,14 +2615,19 @@ function printForeignInstallError(foreign) {
|
|
|
2571
2615
|
Two squadrant installs on this machine will keep fighting over the daemon (#670). Uninstall the one you don't use, then run \`squadrant heal daemon\` to reconcile.
|
|
2572
2616
|
`;
|
|
2573
2617
|
}
|
|
2618
|
+
function printVersionUpgradeNotice(foreign) {
|
|
2619
|
+
return `[squadrant] upgraded plist: ${foreign.registeredEntry} -> ${foreign.thisEntry}
|
|
2620
|
+
`;
|
|
2621
|
+
}
|
|
2574
2622
|
function reregisterDaemon(nodeBin = process.execPath, kickstartOpts = {}) {
|
|
2575
2623
|
if (!tryAcquireDaemonLock()) {
|
|
2576
2624
|
throw new Error("could not acquire the daemon lock \u2014 another squadrant process is already restarting the daemon");
|
|
2577
2625
|
}
|
|
2578
2626
|
try {
|
|
2579
2627
|
const drift = computeDaemonDrift(nodeBin);
|
|
2628
|
+
const pidBeforeReconcile = getDaemonPid(drift.target);
|
|
2580
2629
|
reconcilePlistAndService(drift);
|
|
2581
|
-
const result = forceKickstartAndVerify(drift.target, kickstartOpts);
|
|
2630
|
+
const result = forceKickstartAndVerify(drift.target, { ...kickstartOpts, pidBefore: pidBeforeReconcile });
|
|
2582
2631
|
if (!result.restarted) {
|
|
2583
2632
|
throw new Error(`daemon did not restart (pid before=${result.pidBefore ?? "none"}, after=${result.pidAfter ?? "none"}) \u2014 \`launchctl kickstart -k ${drift.target}\` ran but the pid never changed`);
|
|
2584
2633
|
}
|
|
@@ -2587,13 +2636,14 @@ function reregisterDaemon(nodeBin = process.execPath, kickstartOpts = {}) {
|
|
|
2587
2636
|
releaseDaemonLock();
|
|
2588
2637
|
}
|
|
2589
2638
|
}
|
|
2590
|
-
var LABEL, AGENT_BINS, restartInFlight, OPERATOR_INITIATED_COMMANDS;
|
|
2639
|
+
var LABEL, AGENT_BINS, restartInFlight, OPERATOR_INITIATED_COMMANDS, READ_ONLY_CREW_SUBCOMMANDS;
|
|
2591
2640
|
var init_launchd = __esm({
|
|
2592
2641
|
"packages/core/dist/launchd.js"() {
|
|
2593
2642
|
LABEL = "com.squadrant.daemon";
|
|
2594
2643
|
AGENT_BINS = ["cmux", "claude", "opencode", "codex", "gemini", "node"];
|
|
2595
2644
|
restartInFlight = false;
|
|
2596
2645
|
OPERATOR_INITIATED_COMMANDS = /* @__PURE__ */ new Set(["launch", "init"]);
|
|
2646
|
+
READ_ONLY_CREW_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "read", "tasks"]);
|
|
2597
2647
|
}
|
|
2598
2648
|
});
|
|
2599
2649
|
|
|
@@ -3290,6 +3340,51 @@ var init_captain_delivery = __esm({
|
|
|
3290
3340
|
}
|
|
3291
3341
|
});
|
|
3292
3342
|
|
|
3343
|
+
// packages/core/dist/daemon/down-alert.js
|
|
3344
|
+
function pad2(n) {
|
|
3345
|
+
return String(n).padStart(2, "0");
|
|
3346
|
+
}
|
|
3347
|
+
function formatLocalDateTime(d) {
|
|
3348
|
+
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
|
|
3349
|
+
}
|
|
3350
|
+
function formatTzOffset(d) {
|
|
3351
|
+
const offMin = -d.getTimezoneOffset();
|
|
3352
|
+
const sign = offMin >= 0 ? "+" : "-";
|
|
3353
|
+
const abs = Math.abs(offMin);
|
|
3354
|
+
const hh = pad2(Math.floor(abs / 60));
|
|
3355
|
+
const mm = abs % 60;
|
|
3356
|
+
return mm === 0 ? `${sign}${hh}` : `${sign}${hh}:${pad2(mm)}`;
|
|
3357
|
+
}
|
|
3358
|
+
function formatDownAlertText(opts) {
|
|
3359
|
+
const start = new Date(opts.startTs);
|
|
3360
|
+
const end = new Date(opts.endTs);
|
|
3361
|
+
return `\u26A0\uFE0F daemon was down ${formatLocalDateTime(start)} \u2192 ${formatLocalDateTime(end)} (${formatTzOffset(end)}), ${opts.minutes} min (last exit reason=${opts.reasonText})`;
|
|
3362
|
+
}
|
|
3363
|
+
function formatRelative(ms) {
|
|
3364
|
+
const minutes = Math.round(ms / 6e4);
|
|
3365
|
+
if (minutes < 60)
|
|
3366
|
+
return `${minutes} min`;
|
|
3367
|
+
const hours = Math.floor(minutes / 60);
|
|
3368
|
+
const remMinutes = minutes % 60;
|
|
3369
|
+
if (hours < 24)
|
|
3370
|
+
return remMinutes > 0 ? `${hours}h ${remMinutes}m` : `${hours}h`;
|
|
3371
|
+
const days = Math.floor(hours / 24);
|
|
3372
|
+
const remHours = hours % 24;
|
|
3373
|
+
return remHours > 0 ? `${days}d ${remHours}h` : `${days}d`;
|
|
3374
|
+
}
|
|
3375
|
+
function stalePrefix(generatedAtMs, deliveredAtMs) {
|
|
3376
|
+
const ageMs = deliveredAtMs - generatedAtMs;
|
|
3377
|
+
if (ageMs <= STALE_ALERT_THRESHOLD_MS)
|
|
3378
|
+
return null;
|
|
3379
|
+
return `[stale \u2014 generated ${formatRelative(ageMs)} ago] `;
|
|
3380
|
+
}
|
|
3381
|
+
var STALE_ALERT_THRESHOLD_MS;
|
|
3382
|
+
var init_down_alert = __esm({
|
|
3383
|
+
"packages/core/dist/daemon/down-alert.js"() {
|
|
3384
|
+
STALE_ALERT_THRESHOLD_MS = 60 * 60 * 1e3;
|
|
3385
|
+
}
|
|
3386
|
+
});
|
|
3387
|
+
|
|
3293
3388
|
// packages/core/dist/control-channel.js
|
|
3294
3389
|
function fallsBackToPane(o) {
|
|
3295
3390
|
return o.status === "gone" || o.status === "unsupported";
|
|
@@ -3442,12 +3537,20 @@ function ensureSocksDir(dir = CC_SOCKS_DIR) {
|
|
|
3442
3537
|
if ((fs9.statSync(dir).mode & 511) !== 448)
|
|
3443
3538
|
fs9.chmodSync(dir, 448);
|
|
3444
3539
|
}
|
|
3445
|
-
async function pollFirstTurnConfirmedAt(getTaskRecord, project, id) {
|
|
3446
|
-
|
|
3540
|
+
async function pollFirstTurnConfirmedAt(getTaskRecord, project, id, scrapeSettled, cancel) {
|
|
3541
|
+
let scrapeSettledAt;
|
|
3542
|
+
const markSettled = () => {
|
|
3543
|
+
scrapeSettledAt ??= Date.now();
|
|
3544
|
+
};
|
|
3545
|
+
scrapeSettled.then(markSettled, markSettled);
|
|
3546
|
+
const hardDeadline = Date.now() + FIRST_TURN_HOOK_CONFIRM_MAX_MS;
|
|
3447
3547
|
for (; ; ) {
|
|
3548
|
+
if (cancel.stopped)
|
|
3549
|
+
return false;
|
|
3448
3550
|
const rec = await getTaskRecord(project, id).catch(() => void 0);
|
|
3449
3551
|
if (rec?.firstTurnConfirmedAt)
|
|
3450
3552
|
return true;
|
|
3553
|
+
const deadline = scrapeSettledAt !== void 0 ? Math.min(hardDeadline, scrapeSettledAt + FIRST_TURN_HOOK_CONFIRM_GRACE_MS) : hardDeadline;
|
|
3451
3554
|
if (Date.now() >= deadline)
|
|
3452
3555
|
return false;
|
|
3453
3556
|
await new Promise((r) => setTimeout(r, FIRST_TURN_HOOK_POLL_INTERVAL_MS));
|
|
@@ -3629,7 +3732,9 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
3629
3732
|
|
|
3630
3733
|
${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
|
|
3631
3734
|
const scrapeDelivered = sendPromise.then((r) => r.delivered).catch(() => false);
|
|
3632
|
-
const
|
|
3735
|
+
const cancelHookPoll = { stopped: false };
|
|
3736
|
+
const delivered = hooksInstalled && deps.getTaskRecord ? await firstTrueOrBothFalse(scrapeDelivered, pollFirstTurnConfirmedAt(deps.getTaskRecord, input.project, rec.id, scrapeDelivered, cancelHookPoll)) : await scrapeDelivered;
|
|
3737
|
+
cancelHookPoll.stopped = true;
|
|
3633
3738
|
if (!delivered) {
|
|
3634
3739
|
process.stderr.write(`\u26A0\uFE0F First turn not delivered for crew '${name}' \u2014 use 'squadrant crew send ${input.project} ${name}' to re-send the task.
|
|
3635
3740
|
`);
|
|
@@ -3911,7 +4016,7 @@ async function runCrewList(project, runtime, workspaceId) {
|
|
|
3911
4016
|
surfaceId: c.surfaceId
|
|
3912
4017
|
}));
|
|
3913
4018
|
}
|
|
3914
|
-
var CC_SOCKS_DIR, FIRST_TURN_INLINE_MAX_BYTES, TEMPLATES_DIR, STATE_ROOT,
|
|
4019
|
+
var CC_SOCKS_DIR, FIRST_TURN_INLINE_MAX_BYTES, TEMPLATES_DIR, STATE_ROOT, FIRST_TURN_HOOK_CONFIRM_GRACE_MS, FIRST_TURN_HOOK_CONFIRM_MAX_MS, FIRST_TURN_HOOK_POLL_INTERVAL_MS, CLOSE_LOOKUP_RETRIES, CLOSE_LOOKUP_RETRY_DELAY_MS;
|
|
3915
4020
|
var init_crew_spawn = __esm({
|
|
3916
4021
|
"packages/core/dist/crew-spawn.js"() {
|
|
3917
4022
|
init_control_channel();
|
|
@@ -3923,7 +4028,8 @@ var init_crew_spawn = __esm({
|
|
|
3923
4028
|
FIRST_TURN_INLINE_MAX_BYTES = 1200;
|
|
3924
4029
|
TEMPLATES_DIR = path9.join(os4.homedir(), ".config", "squadrant", "templates");
|
|
3925
4030
|
STATE_ROOT = path9.join(os4.homedir(), ".config", "squadrant", "state");
|
|
3926
|
-
|
|
4031
|
+
FIRST_TURN_HOOK_CONFIRM_GRACE_MS = 15e3;
|
|
4032
|
+
FIRST_TURN_HOOK_CONFIRM_MAX_MS = 18e4;
|
|
3927
4033
|
FIRST_TURN_HOOK_POLL_INTERVAL_MS = 2e3;
|
|
3928
4034
|
CLOSE_LOOKUP_RETRIES = 3;
|
|
3929
4035
|
CLOSE_LOOKUP_RETRY_DELAY_MS = 150;
|
|
@@ -4169,7 +4275,13 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
4169
4275
|
log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=stale-terminal-deliver`);
|
|
4170
4276
|
}
|
|
4171
4277
|
}
|
|
4172
|
-
|
|
4278
|
+
let deliverEntry = entry;
|
|
4279
|
+
if (entry.kind === "captain.message" && entry.payload?.source === "daemon" && entry.message) {
|
|
4280
|
+
const prefix = stalePrefix(new Date(entry.ts).getTime(), Date.now());
|
|
4281
|
+
if (prefix)
|
|
4282
|
+
deliverEntry = { ...entry, message: `${prefix}${entry.message}` };
|
|
4283
|
+
}
|
|
4284
|
+
const result = await d.deliver(deliverEntry, async (text, sendOpts) => {
|
|
4173
4285
|
let handledByChannel = false;
|
|
4174
4286
|
try {
|
|
4175
4287
|
const mode = ctx.captainChannelMode?.() ?? "off";
|
|
@@ -4261,6 +4373,7 @@ var init_delivery_loop = __esm({
|
|
|
4261
4373
|
init_defer_delivery();
|
|
4262
4374
|
init_dist();
|
|
4263
4375
|
init_interactive_probe();
|
|
4376
|
+
init_down_alert();
|
|
4264
4377
|
init_liveness2();
|
|
4265
4378
|
init_captain_channel();
|
|
4266
4379
|
CURSOR_SUBSCRIBER = "captain";
|
|
@@ -4695,8 +4808,8 @@ function startDaemon(ctx, opts, pkgVersion) {
|
|
|
4695
4808
|
log(`boot pid=${process.pid} version=${pkgVersion} socket=${ctx.sockPath} stateRoot=${stateRoot}`);
|
|
4696
4809
|
const bootTs = (/* @__PURE__ */ new Date()).toISOString();
|
|
4697
4810
|
{
|
|
4698
|
-
const sendDownAlert = (minutes, reasonText) => {
|
|
4699
|
-
const text =
|
|
4811
|
+
const sendDownAlert = (startTs, endTs, minutes, reasonText) => {
|
|
4812
|
+
const text = formatDownAlertText({ startTs, endTs, minutes, reasonText });
|
|
4700
4813
|
const alertProjects = opts.registeredProjects ?? Object.keys(loadConfig().projects);
|
|
4701
4814
|
for (const project of alertProjects) {
|
|
4702
4815
|
appendCaptainMessage({ stateRoot, project, text, source: "daemon" }).catch((e) => log(`boot-gap alert failed project=${project}: ${e.message}`));
|
|
@@ -4707,13 +4820,13 @@ function startDaemon(ctx, opts, pkgVersion) {
|
|
|
4707
4820
|
if (marker) {
|
|
4708
4821
|
log(`previous exit ts=${marker.ts} reason=${marker.reason} gap=${((gapMs ?? 0) / 1e3).toFixed(1)}s`);
|
|
4709
4822
|
if ((gapMs ?? 0) > 6e4)
|
|
4710
|
-
sendDownAlert(Math.round((gapMs ?? 0) / 6e4), marker.reason);
|
|
4823
|
+
sendDownAlert(marker.ts, bootTs, Math.round((gapMs ?? 0) / 6e4), marker.reason);
|
|
4711
4824
|
} else if (prevRunning) {
|
|
4712
4825
|
const lastHeartbeatMs = new Date(prevRunning.lastHeartbeatTs).getTime();
|
|
4713
4826
|
const uncleanGapMs = Math.max(0, Date.now() - lastHeartbeatMs);
|
|
4714
4827
|
log(`previous exit: UNCLEAN (no marker; last heartbeat ${prevRunning.lastHeartbeatTs}, gap=${(uncleanGapMs / 1e3).toFixed(1)}s)`);
|
|
4715
4828
|
if (uncleanGapMs > 6e4) {
|
|
4716
|
-
sendDownAlert(Math.round(uncleanGapMs / 6e4), "unclean/unknown \u2014 no exit marker, likely SIGKILL/OOM/power-loss");
|
|
4829
|
+
sendDownAlert(prevRunning.lastHeartbeatTs, bootTs, Math.round(uncleanGapMs / 6e4), "unclean/unknown \u2014 no exit marker, likely SIGKILL/OOM/power-loss");
|
|
4717
4830
|
}
|
|
4718
4831
|
} else {
|
|
4719
4832
|
log("previous exit: none (clean or first boot)");
|
|
@@ -4835,6 +4948,7 @@ var init_start = __esm({
|
|
|
4835
4948
|
init_server();
|
|
4836
4949
|
init_mailbox();
|
|
4837
4950
|
init_exit_marker();
|
|
4951
|
+
init_down_alert();
|
|
4838
4952
|
init_liveness2();
|
|
4839
4953
|
init_dist();
|
|
4840
4954
|
init_snapshot_gather();
|
|
@@ -6804,6 +6918,7 @@ __export(dist_exports, {
|
|
|
6804
6918
|
OPERATOR_INITIATED_COMMANDS: () => OPERATOR_INITIATED_COMMANDS,
|
|
6805
6919
|
PROBE_QUIET_MS: () => PROBE_QUIET_MS,
|
|
6806
6920
|
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
6921
|
+
READ_ONLY_CREW_SUBCOMMANDS: () => READ_ONLY_CREW_SUBCOMMANDS,
|
|
6807
6922
|
STALE_THRESHOLD_MS: () => STALE_THRESHOLD_MS,
|
|
6808
6923
|
TERMINAL_RECORD_KEEP_PER_PROJECT: () => TERMINAL_RECORD_KEEP_PER_PROJECT,
|
|
6809
6924
|
TERMINAL_RECORD_TTL_MS: () => TERMINAL_RECORD_TTL_MS,
|
|
@@ -6852,6 +6967,7 @@ __export(dist_exports, {
|
|
|
6852
6967
|
crewTag: () => crewTag,
|
|
6853
6968
|
daemonEntryPath: () => daemonEntryPath,
|
|
6854
6969
|
daemonLockPath: () => daemonLockPath,
|
|
6970
|
+
decideForeignInstall: () => decideForeignInstall,
|
|
6855
6971
|
decodeFrames: () => decodeFrames,
|
|
6856
6972
|
defaultIsPidAlive: () => defaultIsPidAlive,
|
|
6857
6973
|
defaultListenError: () => defaultListenError,
|
|
@@ -6893,9 +7009,11 @@ __export(dist_exports, {
|
|
|
6893
7009
|
isDaemonSocketLive: () => isDaemonSocketLive,
|
|
6894
7010
|
isNotifyActive: () => isNotifyActive,
|
|
6895
7011
|
isOperatorInitiatedCommand: () => isOperatorInitiatedCommand,
|
|
7012
|
+
isReadOnlyCrewCommand: () => isReadOnlyCrewCommand,
|
|
6896
7013
|
isSideTitle: () => isSideTitle,
|
|
6897
7014
|
isStickyAttention: () => isStickyAttention,
|
|
6898
7015
|
isTurnAccepted: () => isTurnAccepted,
|
|
7016
|
+
isVersionUpgrade: () => isVersionUpgrade,
|
|
6899
7017
|
kickstartArgv: () => kickstartArgv,
|
|
6900
7018
|
launchOneWorkspace: () => launchOneWorkspace,
|
|
6901
7019
|
loadSessions: () => loadSessions,
|
|
@@ -6912,6 +7030,7 @@ __export(dist_exports, {
|
|
|
6912
7030
|
parseProgramArgs: () => parseProgramArgs,
|
|
6913
7031
|
plistPath: () => plistPath,
|
|
6914
7032
|
printForeignInstallError: () => printForeignInstallError,
|
|
7033
|
+
printVersionUpgradeNotice: () => printVersionUpgradeNotice,
|
|
6915
7034
|
programArgsBlock: () => programArgsBlock,
|
|
6916
7035
|
projectHealth: () => projectHealth,
|
|
6917
7036
|
purgeExpiredWorkItems: () => purgeExpiredWorkItems,
|
|
@@ -7887,7 +8006,7 @@ import { execSync as execSync6 } from "child_process";
|
|
|
7887
8006
|
import { readFileSync as readFileSync10 } from "fs";
|
|
7888
8007
|
import { homedir as homedir8 } from "os";
|
|
7889
8008
|
import { join as join15 } from "path";
|
|
7890
|
-
var
|
|
8009
|
+
var nextHookRequestId = Date.now();
|
|
7891
8010
|
|
|
7892
8011
|
// packages/agents/dist/headless/types.js
|
|
7893
8012
|
var HEADLESS_ERROR_TAIL = 2e3;
|
|
@@ -9593,7 +9712,13 @@ var CLAUDE_HOOK_EVENTS = [
|
|
|
9593
9712
|
["Stop", "stop"],
|
|
9594
9713
|
["Notification", "notification"],
|
|
9595
9714
|
["PreToolUse", "ask-question", "AskUserQuestion"],
|
|
9596
|
-
["SessionEnd", "session-end"]
|
|
9715
|
+
["SessionEnd", "session-end"],
|
|
9716
|
+
// #760: fires ~6s before the matching Notification, carrying tool_name +
|
|
9717
|
+
// tool_input directly — a strictly better task.blocked source.
|
|
9718
|
+
["PermissionRequest", "permission-request"],
|
|
9719
|
+
// #763: the turn died on an API error — today squadrant has no source for
|
|
9720
|
+
// this at all; without it the watchdog reports a plain stall (wrong story).
|
|
9721
|
+
["StopFailure", "stop-failure"]
|
|
9597
9722
|
];
|
|
9598
9723
|
var DEFAULT_HOOK_CMD = "squadrant hooks";
|
|
9599
9724
|
function installClaudeHooks(opts = {}) {
|
|
@@ -9671,6 +9796,10 @@ function mapSubToLifecycle(sub) {
|
|
|
9671
9796
|
return "needsInput";
|
|
9672
9797
|
case "ask-question":
|
|
9673
9798
|
return "needsInput";
|
|
9799
|
+
case "permission-request":
|
|
9800
|
+
return "needsInput";
|
|
9801
|
+
case "stop-failure":
|
|
9802
|
+
return "idle";
|
|
9674
9803
|
case "session-end":
|
|
9675
9804
|
return "session-end";
|
|
9676
9805
|
default:
|
|
@@ -9809,9 +9938,12 @@ async function confirmedSendToPane(runtime, pane, message) {
|
|
|
9809
9938
|
let sawDraft = await settleInputBox(runtime, pane);
|
|
9810
9939
|
await runtime.sendKeyToPane(pane, "Enter");
|
|
9811
9940
|
let repasted = false;
|
|
9941
|
+
let screenMoved = false;
|
|
9812
9942
|
for (let attempt = 0; attempt < SUBMIT_RETRY_LIMIT; attempt++) {
|
|
9813
9943
|
await new Promise((r) => setTimeout(r, POST_SEND_CHECK_MS));
|
|
9814
9944
|
const afterScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
9945
|
+
if (afterScreen !== preSendScreen)
|
|
9946
|
+
screenMoved = true;
|
|
9815
9947
|
const draft = parseDraftFromScreen(afterScreen);
|
|
9816
9948
|
if (draft !== "" && draft !== null)
|
|
9817
9949
|
sawDraft = true;
|
|
@@ -9822,7 +9954,7 @@ async function confirmedSendToPane(runtime, pane, message) {
|
|
|
9822
9954
|
const settled = await settleInputBox(runtime, pane);
|
|
9823
9955
|
if (settled)
|
|
9824
9956
|
sawDraft = true;
|
|
9825
|
-
if (!sawDraft && !repasted) {
|
|
9957
|
+
if (!sawDraft && !repasted && !screenMoved) {
|
|
9826
9958
|
repasted = true;
|
|
9827
9959
|
await runtime.pasteToPane(pane, message);
|
|
9828
9960
|
}
|