@indigoai-us/hq-cli 5.264.0 → 5.266.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/CHANGELOG.md +27 -0
- package/dist/command-catalog.generated.d.ts +19 -0
- package/dist/command-catalog.generated.js +25 -0
- package/dist/commands/daemon.d.ts +3 -0
- package/dist/commands/daemon.js +11 -0
- package/dist/commands/lanes-kpis.d.ts +11 -0
- package/dist/commands/lanes-kpis.js +34 -0
- package/dist/commands/lanes-watch.js +2 -1
- package/dist/commands/lanes.js +12 -0
- package/dist/lib/daemon/handoff.d.ts +2 -0
- package/dist/lib/daemon/handoff.js +11 -2
- package/dist/lib/daemon/run.d.ts +19 -0
- package/dist/lib/daemon/run.js +23 -4
- package/dist/lib/daemon/supervisor.d.ts +5 -0
- package/dist/lib/daemon/supervisor.js +1 -0
- package/dist/lib/lanes/config.d.ts +2 -0
- package/dist/lib/lanes/config.js +4 -0
- package/dist/lib/lanes/keep-alive.d.ts +2 -2
- package/dist/lib/lanes/keep-alive.js +9 -6
- package/dist/lib/lanes/kr-freshness.d.ts +76 -0
- package/dist/lib/lanes/kr-freshness.js +412 -0
- package/dist/lib/lanes/lane-company.d.ts +9 -0
- package/dist/lib/lanes/lane-company.js +24 -0
- package/dist/lib/lanes/reconcile.js +57 -6
- package/dist/lib/lanes/types.d.ts +20 -1
- package/dist/lib/lanes/types.js +1 -1
- package/dist/lib/lanes/watcher.d.ts +2 -0
- package/dist/lib/lanes/watcher.js +70 -1
- package/package.json +1 -1
|
@@ -49,6 +49,7 @@ import { createLaneMesh, emitLaneDecisionEvents, emitLaneProgressEvent, emitLane
|
|
|
49
49
|
import { envelopeQuestionId } from "./questions.js";
|
|
50
50
|
import { readLanesRegistryFlag } from "./registry-flag.js";
|
|
51
51
|
import { blockerFreshnessFingerprint, resolveBlockerFreshnessForLane, } from "./blocker-freshness.js";
|
|
52
|
+
import { krFreshnessFingerprint, resolveKrFreshnessForLane, } from "./kr-freshness.js";
|
|
52
53
|
import { MANAGER_STATUS_GATE_FLAG, managerStatusWasWritten, readStatusSnapshot, resolveManagerStatusTarget, managerStatusGateEnabledForLane, } from "./manager-status-gate.js";
|
|
53
54
|
import { queueLaneTerminalResume, queueManagerStatusGateResume } from "./keep-alive.js";
|
|
54
55
|
import { monitorSessionDir } from "../monitor.js";
|
|
@@ -863,6 +864,72 @@ async function settleBlockerFreshness(hqRoot, lane, manifestTurnId, at, deps, me
|
|
|
863
864
|
}
|
|
864
865
|
return updated;
|
|
865
866
|
}
|
|
867
|
+
async function settleKrFreshness(hqRoot, lane, manifestTurnId, at, deps, logDecision) {
|
|
868
|
+
if (manifestTurnId !== undefined && lane.status_gate !== undefined && lane.status_gate.turn_id !== manifestTurnId) {
|
|
869
|
+
logDecision({ event: "kr_freshness_baseline_mismatch", at, turn_id: lane.status_gate.turn_id });
|
|
870
|
+
return lane;
|
|
871
|
+
}
|
|
872
|
+
let errorLogged = false;
|
|
873
|
+
const resolution = await resolveKrFreshnessForLane(hqRoot, lane, {
|
|
874
|
+
readFlag: deps.readKrFreshnessFlag ?? readLanesRegistryFlag,
|
|
875
|
+
now: Date.parse(at),
|
|
876
|
+
onError: (error) => {
|
|
877
|
+
if (errorLogged)
|
|
878
|
+
return;
|
|
879
|
+
errorLogged = true;
|
|
880
|
+
const code = error instanceof LanesError ? error.error : "kr_freshness_unavailable";
|
|
881
|
+
process.stderr.write(`hq lanes watcher: KR freshness disabled for ${lane.lane_id}: ${code}\n`);
|
|
882
|
+
logDecision({ event: "kr_freshness_disabled", at, error: code });
|
|
883
|
+
},
|
|
884
|
+
});
|
|
885
|
+
if (!resolution.enabled || resolution.statusPath === undefined || resolution.error !== undefined)
|
|
886
|
+
return lane;
|
|
887
|
+
const stalled = resolution.kpis.filter((kpi) => kpi.verdict === "stalled");
|
|
888
|
+
if (stalled.length === 0) {
|
|
889
|
+
if (lane.kr_freshness_state === undefined)
|
|
890
|
+
return lane;
|
|
891
|
+
return retryUpdateLane(hqRoot, lane.lane_id, "watcher", (current) => current.kr_freshness_state === undefined
|
|
892
|
+
? current
|
|
893
|
+
: { ...current, kr_freshness_state: undefined });
|
|
894
|
+
}
|
|
895
|
+
const fingerprint = krFreshnessFingerprint(resolution.stalledKeys);
|
|
896
|
+
const names = stalled.map((kpi) => kpi.name);
|
|
897
|
+
const turnId = manifestTurnId ?? lane.status_gate?.turn_id ?? `unknown-${at}`;
|
|
898
|
+
const resumeText = `These KPIs in ${resolution.statusPath} were not re-measured in the last ${resolution.windowHours}h: ${names.join(", ")}. Measure each one now, update value and measured_at, then end the turn.`;
|
|
899
|
+
return retryUpdateLane(hqRoot, lane.lane_id, "watcher", (current) => {
|
|
900
|
+
const history = current.status_gate_history ?? [];
|
|
901
|
+
if (history.some((entry) => entry.event === "kr_stalled" && entry.turn_id === turnId))
|
|
902
|
+
return current;
|
|
903
|
+
const sameStalledSet = current.kr_freshness_state?.stalled_fingerprint === fingerprint;
|
|
904
|
+
let nextLane = current;
|
|
905
|
+
let resumeQueued = false;
|
|
906
|
+
let resumeSkippedReason;
|
|
907
|
+
if (!sameStalledSet) {
|
|
908
|
+
const mergeExisting = current.pending_resume !== undefined &&
|
|
909
|
+
current.state !== "blocked" && current.state !== "awaiting_input";
|
|
910
|
+
const resume = queueLaneTerminalResume(current, at, resumeText, { mergeExisting });
|
|
911
|
+
nextLane = resume.lane;
|
|
912
|
+
resumeQueued = resume.resumeQueued;
|
|
913
|
+
resumeSkippedReason = resume.resumeSkippedReason;
|
|
914
|
+
}
|
|
915
|
+
const entry = {
|
|
916
|
+
event: "kr_stalled",
|
|
917
|
+
turn_id: turnId,
|
|
918
|
+
path: resolution.statusPath,
|
|
919
|
+
at,
|
|
920
|
+
names,
|
|
921
|
+
count: stalled.length,
|
|
922
|
+
fingerprint,
|
|
923
|
+
...(resumeQueued ? { resume_queued: true } : {}),
|
|
924
|
+
...(resumeSkippedReason !== undefined ? { resume_skipped_reason: resumeSkippedReason } : {}),
|
|
925
|
+
};
|
|
926
|
+
return {
|
|
927
|
+
...nextLane,
|
|
928
|
+
kr_freshness_state: { stalled_fingerprint: fingerprint },
|
|
929
|
+
status_gate_history: appendCappedHistory(history, entry),
|
|
930
|
+
};
|
|
931
|
+
});
|
|
932
|
+
}
|
|
866
933
|
function resolveBlockerFreshnessSessionInbox(hqRoot, sessionId) {
|
|
867
934
|
let candidates;
|
|
868
935
|
try {
|
|
@@ -1452,11 +1519,13 @@ async function runWatcherLoopOwned(hqRoot, laneId, deps) {
|
|
|
1452
1519
|
if (terminalWrite.changed) {
|
|
1453
1520
|
updated = await settleManagerStatusGate(hqRoot, updated, manifest.turn_id, tick.emit.at, deps, logDecision);
|
|
1454
1521
|
updated = await settleBlockerFreshness(hqRoot, updated, manifest.turn_id, tick.emit.at, deps, mesh, logDecision);
|
|
1522
|
+
updated = await settleKrFreshness(hqRoot, updated, manifest.turn_id, tick.emit.at, deps, logDecision);
|
|
1455
1523
|
}
|
|
1456
1524
|
const managerStatusFollowupQueued = manifest.turn_id !== undefined &&
|
|
1457
1525
|
updated.status_gate_history?.some((entry) => entry.turn_id === manifest.turn_id &&
|
|
1458
1526
|
((entry.event === "status_missed" && entry.resume_queued === true) ||
|
|
1459
|
-
(entry.event === "blocker_stale" && entry.resume_queued === true)
|
|
1527
|
+
(entry.event === "blocker_stale" && entry.resume_queued === true) ||
|
|
1528
|
+
(entry.event === "kr_stalled" && entry.resume_queued === true))) === true;
|
|
1460
1529
|
if (terminalWrite.changed && deps.recoverQueuedMessages && !managerStatusFollowupQueued) {
|
|
1461
1530
|
try {
|
|
1462
1531
|
if (dropboxInboxHasMessages(hqRoot, laneId)) {
|