squadrant 0.17.0 → 0.18.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 +588 -326
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +52 -5
- package/dist/squadrantd.js.map +1 -1
- package/package.json +1 -1
- package/plugin/skills/captain-ops/SKILL.md +51 -65
- package/plugin/skills/command-ops/SKILL.md +12 -20
- package/plugin/skills/handback/SKILL.md +8 -0
- package/plugin/skills/karpathy-principles/SKILL.md +1 -1
- package/plugin/skills/takeover/SKILL.md +11 -0
- package/templates/captain.claude.md +10 -5
- package/templates/captain.generic.md +9 -3
- package/templates/command.claude.md +2 -2
- package/templates/crew.generic.md +1 -1
- package/templates/crew.opencode.md +1 -1
- package/templates/learnings.claude.md +1 -1
package/dist/squadrantd.js
CHANGED
|
@@ -507,7 +507,21 @@ function nextPendingMonitor(current, ev, now) {
|
|
|
507
507
|
}
|
|
508
508
|
function reduce(rec, ev, now) {
|
|
509
509
|
if (ev.type === "task.reopened") {
|
|
510
|
-
return { ...rec, state: "working", question: void 0, error: void 0, lastHeartbeat: now, lastEvent: ev.type };
|
|
510
|
+
return { ...rec, state: "working", question: void 0, error: void 0, lastHeartbeat: now, lastEvent: ev.type, workingStretchStartedAt: now };
|
|
511
|
+
}
|
|
512
|
+
if (ev.type === "crew.takeover.started") {
|
|
513
|
+
if (rec.operatorHold)
|
|
514
|
+
return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
|
|
515
|
+
return {
|
|
516
|
+
...rec,
|
|
517
|
+
operatorHold: { since: now, ...ev.note !== void 0 ? { note: ev.note } : {} },
|
|
518
|
+
lastHeartbeat: now,
|
|
519
|
+
lastEvent: ev.type
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
if (ev.type === "crew.takeover.ended") {
|
|
523
|
+
const { operatorHold: _dropped, ...rest } = rec;
|
|
524
|
+
return { ...rest, lastHeartbeat: now, lastEvent: ev.type };
|
|
511
525
|
}
|
|
512
526
|
if (TERMINAL_STATES.has(rec.state))
|
|
513
527
|
return rec;
|
|
@@ -523,8 +537,9 @@ function reduce(rec, ev, now) {
|
|
|
523
537
|
// resuming after a blocked→reply clears the question
|
|
524
538
|
pendingTool: void 0,
|
|
525
539
|
// #354: a new turn closes any prior tool window
|
|
526
|
-
pendingMonitor: void 0
|
|
540
|
+
pendingMonitor: void 0,
|
|
527
541
|
// #594a: same reset — a new turn moots any prior watch
|
|
542
|
+
workingStretchStartedAt: now
|
|
528
543
|
};
|
|
529
544
|
case "task.progress": {
|
|
530
545
|
const pendingTool = nextPendingTool(rec.pendingTool, ev, now);
|
|
@@ -693,6 +708,8 @@ function firePush(deps, project, prev, next, event, lastCaptainTurnAt) {
|
|
|
693
708
|
return;
|
|
694
709
|
if (!ATTENTION_STATES.has(next.state))
|
|
695
710
|
return;
|
|
711
|
+
if (next.operatorHold)
|
|
712
|
+
return;
|
|
696
713
|
if (next.state === "awaiting-input" && lastCaptainTurnAt != null && deps.now() - lastCaptainTurnAt <= IDLE_DEBOUNCE_MS) {
|
|
697
714
|
return;
|
|
698
715
|
}
|
|
@@ -744,8 +761,11 @@ var KNOWN_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
744
761
|
"task.reconcile-failed",
|
|
745
762
|
"task.cancelled",
|
|
746
763
|
"task.session.ended",
|
|
747
|
-
"task.first-turn.confirmed"
|
|
764
|
+
"task.first-turn.confirmed",
|
|
748
765
|
// #466: delivery confirmation
|
|
766
|
+
"crew.takeover.started",
|
|
767
|
+
"crew.takeover.ended"
|
|
768
|
+
// #649: operator takeover
|
|
749
769
|
]);
|
|
750
770
|
function createDaemon(deps) {
|
|
751
771
|
const { store, now } = deps;
|
|
@@ -927,9 +947,32 @@ function createDaemon(deps) {
|
|
|
927
947
|
store.delete(r.project, r.id);
|
|
928
948
|
continue;
|
|
929
949
|
}
|
|
950
|
+
if (r.operatorHold) {
|
|
951
|
+
const threshold = (deps.takeoverNudgeHours ?? 6) * 36e5;
|
|
952
|
+
const holdAge = t - r.operatorHold.since;
|
|
953
|
+
if (holdAge > threshold) {
|
|
954
|
+
const timeSinceLastNudge = t - (r.operatorHold.lastNudgeAt ?? 0);
|
|
955
|
+
if (timeSinceLastNudge > threshold) {
|
|
956
|
+
const hrs = Math.round(holdAge / 36e5);
|
|
957
|
+
const tag = crewTag(r);
|
|
958
|
+
const message = `CREW HELD-LONG ${tag} \u2014 held ${hrs}h. Ask the operator whether it is still in use. Do not release it yourself.`;
|
|
959
|
+
store.put({ ...r, operatorHold: { ...r.operatorHold, lastNudgeAt: t } });
|
|
960
|
+
if (deps.notify) {
|
|
961
|
+
try {
|
|
962
|
+
const p = deps.notify({ project: r.project, message, record: r, event: { type: "task.progress", id: r.id } });
|
|
963
|
+
if (p && typeof p.catch === "function")
|
|
964
|
+
p.catch(() => {
|
|
965
|
+
});
|
|
966
|
+
} catch {
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
}
|
|
930
972
|
if (!TERMINAL_STATES.has(r.state) && !isStickyAttention(r.state)) {
|
|
931
973
|
const ceiling = deps.taskTimeoutMs ?? DEFAULT_TASK_TIMEOUT_MS;
|
|
932
|
-
|
|
974
|
+
const refTime = r.workingStretchStartedAt ?? r.createdAt;
|
|
975
|
+
if (t - refTime > ceiling) {
|
|
933
976
|
const prevState = r.state;
|
|
934
977
|
const tag = crewTag(r);
|
|
935
978
|
const hrs = Math.round(ceiling / 36e5);
|
|
@@ -1791,7 +1834,9 @@ function buildContext(opts) {
|
|
|
1791
1834
|
const sockPath = opts.sockPath ?? join9(homedir6(), ".config", "squadrant", "squadrant.sock");
|
|
1792
1835
|
const store = createStore(stateRoot);
|
|
1793
1836
|
const bootedAt = Date.now();
|
|
1794
|
-
const
|
|
1837
|
+
const config = loadConfig();
|
|
1838
|
+
const taskTimeoutMs = config.defaults.taskTimeoutMs;
|
|
1839
|
+
const takeoverNudgeHours = config.defaults.takeoverNudgeHours;
|
|
1795
1840
|
const isPidAlive = opts.isPidAlive ?? defaultIsPidAlive;
|
|
1796
1841
|
const spawn2 = opts.spawn ?? realSpawn;
|
|
1797
1842
|
const resultsDir = join9(stateRoot, "_results");
|
|
@@ -1811,6 +1856,7 @@ function buildContext(opts) {
|
|
|
1811
1856
|
bootedAt,
|
|
1812
1857
|
lastSweepAt: { value: null },
|
|
1813
1858
|
taskTimeoutMs,
|
|
1859
|
+
takeoverNudgeHours,
|
|
1814
1860
|
isPidAlive,
|
|
1815
1861
|
spawn: spawn2,
|
|
1816
1862
|
resultsDir,
|
|
@@ -2605,6 +2651,7 @@ function startDaemon(ctx, opts, pkgVersion) {
|
|
|
2605
2651
|
isPidAlive,
|
|
2606
2652
|
notify,
|
|
2607
2653
|
taskTimeoutMs,
|
|
2654
|
+
takeoverNudgeHours: ctx.takeoverNudgeHours,
|
|
2608
2655
|
isSurfaceAlive: surfaceProbe,
|
|
2609
2656
|
resendFirstTurn: ctx.resendFirstTurn,
|
|
2610
2657
|
launchHeadless: opts.launchHeadless,
|