harnery 0.43.0 → 0.44.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/README.md +3 -3
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +41 -48
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +38 -11
- package/dist/commands/diagnostics.d.ts.map +1 -1
- package/dist/commands/diagnostics.js +10 -0
- package/dist/commands/doctor.d.ts +12 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +22 -7
- package/dist/commands/governor.js +22 -10
- package/dist/commands/inbox.d.ts +1 -1
- package/dist/commands/inbox.d.ts.map +1 -1
- package/dist/commands/inbox.js +55 -2
- package/dist/commands/rm.d.ts.map +1 -1
- package/dist/commands/rm.js +28 -4
- package/dist/commands/servers.d.ts.map +1 -1
- package/dist/commands/servers.js +16 -1
- package/dist/commands/supervisor.d.ts.map +1 -1
- package/dist/commands/supervisor.js +7 -2
- package/dist/commands/toc.d.ts +14 -0
- package/dist/commands/toc.d.ts.map +1 -1
- package/dist/commands/toc.js +7 -4
- package/dist/commands/workflow.js +24 -30
- package/dist/core/agents/live-authority-v3.d.ts +1 -0
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +11 -0
- package/dist/core/agents/live-lifecycle-v3.d.ts +4 -2
- package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
- package/dist/core/agents/live-lifecycle-v3.js +28 -7
- package/dist/core/agents/mailbox.d.ts +9 -8
- package/dist/core/agents/mailbox.d.ts.map +1 -1
- package/dist/core/agents/mailbox.js +24 -17
- package/dist/core/agents/render/prompt-context.d.ts +2 -0
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +8 -12
- package/dist/core/agents/render/session-context.d.ts +2 -8
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +7 -30
- package/dist/core/agents/rules/stop-hook.js +3 -3
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +25 -14
- package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-writer.js +5 -0
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +5 -0
- package/dist/core/agents/state/proc-start.d.ts +19 -0
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +41 -0
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +180 -70
- package/dist/core/agents/state/unchanged-file.d.ts +44 -0
- package/dist/core/agents/state/unchanged-file.d.ts.map +1 -0
- package/dist/core/agents/state/unchanged-file.js +390 -0
- package/dist/core/context/index.d.ts +22 -10
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +381 -119
- package/dist/core/context/observation.d.ts +26 -0
- package/dist/core/context/observation.d.ts.map +1 -0
- package/dist/core/context/observation.js +11 -0
- package/dist/core/diagnostics/bundle.d.ts.map +1 -1
- package/dist/core/diagnostics/bundle.js +25 -14
- package/dist/core/diagnostics/path-identity.d.ts +3 -0
- package/dist/core/diagnostics/path-identity.d.ts.map +1 -0
- package/dist/core/diagnostics/path-identity.js +12 -0
- package/dist/core/diagnostics/sanitize.d.ts.map +1 -1
- package/dist/core/diagnostics/sanitize.js +4 -3
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
- package/dist/core/events/v3/authority-outbox.js +29 -0
- package/dist/core/events/v3/generation-custody.d.ts +5 -0
- package/dist/core/events/v3/generation-custody.d.ts.map +1 -0
- package/dist/core/events/v3/generation-custody.js +50 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts +1 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +7 -0
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +77 -64
- package/dist/core/governor/planning.d.ts.map +1 -1
- package/dist/core/governor/planning.js +14 -1
- package/dist/core/governor/service-read.d.ts +10 -2
- package/dist/core/governor/service-read.d.ts.map +1 -1
- package/dist/core/governor/service-read.js +105 -22
- package/dist/core/governor/service.d.ts +20 -2
- package/dist/core/governor/service.d.ts.map +1 -1
- package/dist/core/governor/service.js +72 -47
- package/dist/core/hooks/cli.js +33 -5
- package/dist/core/inbox/contract.d.ts +2 -1
- package/dist/core/inbox/contract.d.ts.map +1 -1
- package/dist/core/inbox/index.d.ts +2 -0
- package/dist/core/inbox/index.d.ts.map +1 -1
- package/dist/core/inbox/index.js +2 -0
- package/dist/core/inbox/peer-messages.d.ts +37 -0
- package/dist/core/inbox/peer-messages.d.ts.map +1 -0
- package/dist/core/inbox/peer-messages.js +72 -0
- package/dist/core/inbox/service.d.ts +8 -1
- package/dist/core/inbox/service.d.ts.map +1 -1
- package/dist/core/inbox/service.js +67 -21
- package/dist/core/inbox/targets.d.ts +22 -0
- package/dist/core/inbox/targets.d.ts.map +1 -0
- package/dist/core/inbox/targets.js +108 -0
- package/dist/core/inbox/watcher.js +7 -3
- package/dist/core/journal/index.d.ts.map +1 -1
- package/dist/core/journal/index.js +17 -2
- package/dist/core/resources/contract.d.ts +3 -0
- package/dist/core/resources/contract.d.ts.map +1 -1
- package/dist/core/resources/service-status.d.ts +14 -1
- package/dist/core/resources/service-status.d.ts.map +1 -1
- package/dist/core/resources/service-status.js +23 -13
- package/dist/core/resources/service.d.ts +9 -1
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +47 -27
- package/dist/core/resources/status.d.ts +2 -5
- package/dist/core/resources/status.d.ts.map +1 -1
- package/dist/core/resources/status.js +36 -7
- package/dist/core/servers/index.d.ts +24 -13
- package/dist/core/servers/index.d.ts.map +1 -1
- package/dist/core/servers/index.js +132 -92
- package/dist/core/servers/tunnels.d.ts +3 -5
- package/dist/core/servers/tunnels.d.ts.map +1 -1
- package/dist/core/servers/tunnels.js +3 -9
- package/dist/core/supervisor/contract.d.ts +12 -0
- package/dist/core/supervisor/contract.d.ts.map +1 -1
- package/dist/core/supervisor/explanations.d.ts.map +1 -1
- package/dist/core/supervisor/explanations.js +2 -1
- package/dist/core/supervisor/findings.d.ts.map +1 -1
- package/dist/core/supervisor/findings.js +54 -9
- package/dist/core/supervisor/service.d.ts +9 -1
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +59 -29
- package/dist/core/supervisor/services.d.ts.map +1 -1
- package/dist/core/supervisor/services.js +22 -1
- package/dist/core/supervisor/status.d.ts +17 -2
- package/dist/core/supervisor/status.d.ts.map +1 -1
- package/dist/core/supervisor/status.js +70 -12
- package/dist/core/supervisor/timeline.d.ts.map +1 -1
- package/dist/core/supervisor/timeline.js +2 -1
- package/dist/core/work/state.js +7 -7
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +105 -32
- package/dist/core/workflow/failure-evidence.d.ts +16 -0
- package/dist/core/workflow/failure-evidence.d.ts.map +1 -0
- package/dist/core/workflow/failure-evidence.js +114 -0
- package/dist/core/workflow/index.d.ts +1 -1
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +1 -1
- package/dist/core/workflow/live-session-v3.d.ts +6 -0
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +126 -5
- package/dist/core/workflow/proof.d.ts +5 -5
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +115 -20
- package/dist/core/workflow/run-state.d.ts +3 -1
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +31 -27
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +19 -0
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +13 -0
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +19 -0
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
- package/dist/core/workflow/spawn-opencode.js +14 -0
- package/dist/core/workflow/types.d.ts +64 -17
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.d.ts +0 -1
- package/dist/core/workflow/workspaces/index.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.js +0 -1
- package/dist/core/workflow/workspaces/leases.d.ts +3 -1
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +30 -16
- package/dist/core/workflow/workspaces/local-git.d.ts +6 -2
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +270 -6
- package/dist/core/workflow/workspaces/ownership.d.ts +11 -0
- package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -0
- package/dist/core/workflow/workspaces/ownership.js +194 -0
- package/dist/core/workflow/workspaces/reclaim.d.ts +35 -0
- package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/reclaim.js +14 -0
- package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/recovery.js +21 -13
- package/dist/core/workflow/workspaces/state.d.ts +1 -0
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +21 -1
- package/dist/lib/browser/windows-chrome.d.ts +100 -12
- package/dist/lib/browser/windows-chrome.d.ts.map +1 -1
- package/dist/lib/browser/windows-chrome.js +262 -73
- package/dist/lib/docs-links.d.ts.map +1 -1
- package/dist/lib/docs-links.js +45 -2
- package/dist/lib/guarded-remove.d.ts +7 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -1
- package/dist/lib/guarded-remove.js +106 -12
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +4 -0
- package/dist/lib/zip/index.d.ts +41 -0
- package/dist/lib/zip/index.d.ts.map +1 -0
- package/dist/lib/zip/index.js +398 -0
- package/package.json +7 -1
- package/src/commands/agents.ts +54 -63
- package/src/commands/browse.ts +50 -14
- package/src/commands/diagnostics.ts +10 -0
- package/src/commands/doctor.ts +26 -7
- package/src/commands/governor.ts +23 -10
- package/src/commands/inbox.ts +52 -2
- package/src/commands/rm.ts +43 -4
- package/src/commands/servers.ts +16 -4
- package/src/commands/supervisor.ts +7 -2
- package/src/commands/toc.ts +14 -6
- package/src/commands/workflow.ts +30 -30
- package/src/core/agents/live-authority-v3.ts +17 -0
- package/src/core/agents/live-lifecycle-v3.ts +52 -14
- package/src/core/agents/mailbox.ts +26 -17
- package/src/core/agents/render/prompt-context.ts +10 -12
- package/src/core/agents/render/session-context.ts +9 -32
- package/src/core/agents/rules/stop-hook.ts +3 -3
- package/src/core/agents/state/heartbeat-writer.ts +29 -12
- package/src/core/agents/state/live-coordination-writer.ts +29 -0
- package/src/core/agents/state/pidmap.ts +13 -0
- package/src/core/agents/state/proc-start.ts +57 -0
- package/src/core/agents/state/stale-sweep.ts +247 -82
- package/src/core/agents/state/unchanged-file.ts +456 -0
- package/src/core/context/index.ts +446 -136
- package/src/core/context/observation.ts +29 -0
- package/src/core/diagnostics/bundle.ts +23 -18
- package/src/core/diagnostics/path-identity.ts +15 -0
- package/src/core/diagnostics/sanitize.ts +4 -3
- package/src/core/events/v3/authority-outbox.ts +56 -0
- package/src/core/events/v3/generation-custody.ts +59 -0
- package/src/core/events/v3/producers/coordination-recorder.ts +15 -0
- package/src/core/events/v3/producers/recorder.ts +94 -68
- package/src/core/governor/planning.ts +20 -8
- package/src/core/governor/service-read.ts +128 -20
- package/src/core/governor/service.ts +119 -42
- package/src/core/hooks/cli.ts +33 -5
- package/src/core/inbox/contract.ts +2 -1
- package/src/core/inbox/index.ts +2 -0
- package/src/core/inbox/peer-messages.ts +94 -0
- package/src/core/inbox/service.ts +88 -22
- package/src/core/inbox/targets.ts +148 -0
- package/src/core/inbox/watcher.ts +8 -9
- package/src/core/journal/index.ts +19 -2
- package/src/core/resources/contract.ts +4 -0
- package/src/core/resources/service-status.ts +35 -12
- package/src/core/resources/service.ts +71 -24
- package/src/core/resources/status.ts +57 -8
- package/src/core/servers/index.ts +177 -91
- package/src/core/servers/tunnels.ts +3 -11
- package/src/core/supervisor/contract.ts +13 -0
- package/src/core/supervisor/explanations.ts +4 -1
- package/src/core/supervisor/findings.ts +68 -10
- package/src/core/supervisor/service.ts +88 -26
- package/src/core/supervisor/services.ts +26 -8
- package/src/core/supervisor/status.ts +89 -12
- package/src/core/supervisor/timeline.ts +2 -1
- package/src/core/work/state.ts +7 -7
- package/src/core/workflow/engine.ts +131 -49
- package/src/core/workflow/failure-evidence.ts +141 -0
- package/src/core/workflow/index.ts +0 -1
- package/src/core/workflow/live-session-v3.ts +146 -5
- package/src/core/workflow/proof.ts +134 -26
- package/src/core/workflow/run-state.ts +31 -29
- package/src/core/workflow/spawn-claude.ts +18 -0
- package/src/core/workflow/spawn-codex.ts +13 -0
- package/src/core/workflow/spawn-cursor.ts +18 -0
- package/src/core/workflow/spawn-opencode.ts +14 -0
- package/src/core/workflow/types.ts +51 -17
- package/src/core/workflow/workspaces/index.ts +0 -1
- package/src/core/workflow/workspaces/leases.ts +48 -17
- package/src/core/workflow/workspaces/local-git.ts +315 -5
- package/src/core/workflow/workspaces/ownership.ts +232 -0
- package/src/core/workflow/workspaces/reclaim.ts +58 -0
- package/src/core/workflow/workspaces/recovery.ts +25 -13
- package/src/core/workflow/workspaces/state.ts +20 -0
- package/src/lib/browser/windows-chrome.ts +346 -86
- package/src/lib/docs-links.ts +49 -2
- package/src/lib/guarded-remove.ts +132 -14
- package/src/lib/identities/assume.ts +4 -0
- package/src/lib/zip/index.ts +483 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { canonicalizeDiagnosticIdentity } from "../diagnostics/path-identity.js";
|
|
2
3
|
import { SUPERVISOR_DIAGNOSTIC_LIMITS, SUPERVISOR_FINDING_POLICY, SUPERVISOR_FINDING_SCHEMA_VERSION, } from "./contract.js";
|
|
3
4
|
import { evaluateHookHealthAlerts } from "./hook-health-alerts.js";
|
|
4
5
|
import { ledgerControlStateFindingCandidateV1, } from "./ledger-state.js";
|
|
@@ -31,14 +32,16 @@ export function updateSupervisorFindings(input) {
|
|
|
31
32
|
const candidates = evaluateCandidates(input)
|
|
32
33
|
.sort((left, right) => severityRank(right.severity) - severityRank(left.severity))
|
|
33
34
|
.slice(0, SUPERVISOR_DIAGNOSTIC_LIMITS.max_active_findings);
|
|
34
|
-
const priorActive = new Map(previous.active.map((entry) => [entry
|
|
35
|
+
const priorActive = new Map(previous.active.map((entry) => [findingFingerprint(entry), entry]));
|
|
35
36
|
const current = new Set();
|
|
36
37
|
const active = [];
|
|
37
38
|
const transitions = new Map(previous.transitions.map((entry) => [entry.id, entry]));
|
|
38
|
-
for (const
|
|
39
|
-
const
|
|
40
|
-
current.add(
|
|
41
|
-
const prior = priorActive.get(
|
|
39
|
+
for (const observed of candidates) {
|
|
40
|
+
const identity = findingFingerprint(observed);
|
|
41
|
+
current.add(identity);
|
|
42
|
+
const prior = priorActive.get(identity) ?? recentResolved(previous, identity, observedAt);
|
|
43
|
+
const candidate = preserveEpisodeReferences(observed, prior);
|
|
44
|
+
const fingerprint = prior?.fingerprint ?? identity;
|
|
42
45
|
const openedAt = prior?.opened_at ?? candidate.primary_source.observed_at;
|
|
43
46
|
const occurrenceCount = prior
|
|
44
47
|
? prior.state === "resolved"
|
|
@@ -48,7 +51,7 @@ export function updateSupervisorFindings(input) {
|
|
|
48
51
|
const peak = peakEvidence(prior, candidate.evidence, observedAt);
|
|
49
52
|
const finding = {
|
|
50
53
|
schema_version: SUPERVISOR_FINDING_SCHEMA_VERSION,
|
|
51
|
-
id: deterministicId("find", `${fingerprint}\u0000${openedAt}`),
|
|
54
|
+
id: prior?.id ?? deterministicId("find", `${fingerprint}\u0000${openedAt}`),
|
|
52
55
|
fingerprint,
|
|
53
56
|
source_kind: candidate.source_kind,
|
|
54
57
|
finding_kind: candidate.finding_kind,
|
|
@@ -72,7 +75,7 @@ export function updateSupervisorFindings(input) {
|
|
|
72
75
|
transitions.set(finding.id, finding);
|
|
73
76
|
}
|
|
74
77
|
for (const prior of priorActive.values()) {
|
|
75
|
-
if (current.has(prior
|
|
78
|
+
if (current.has(findingFingerprint(prior)))
|
|
76
79
|
continue;
|
|
77
80
|
transitions.set(prior.id, {
|
|
78
81
|
...prior,
|
|
@@ -420,7 +423,7 @@ function stableSource(sourceKind, sourceId, observedAt, capability, recordId, sc
|
|
|
420
423
|
function recentResolved(findings, fingerprint, observedAt) {
|
|
421
424
|
const observedMs = Date.parse(observedAt);
|
|
422
425
|
return findings.transitions
|
|
423
|
-
.filter((entry) => entry
|
|
426
|
+
.filter((entry) => findingFingerprint(entry) === fingerprint &&
|
|
424
427
|
entry.state === "resolved" &&
|
|
425
428
|
entry.resolved_at !== undefined &&
|
|
426
429
|
observedMs - Date.parse(entry.resolved_at) >= 0 &&
|
|
@@ -460,7 +463,49 @@ function deterministicId(prefix, value) {
|
|
|
460
463
|
return `${prefix}_${digest(value).slice(0, 24)}`;
|
|
461
464
|
}
|
|
462
465
|
function digest(value) {
|
|
463
|
-
return createHash("sha256").update(value).digest("hex");
|
|
466
|
+
return createHash("sha256").update(canonicalizeDiagnosticIdentity(value)).digest("hex");
|
|
467
|
+
}
|
|
468
|
+
/** Keep opaque episode links when the same source observation is replayed privately. */
|
|
469
|
+
function preserveEpisodeReferences(candidate, prior) {
|
|
470
|
+
if (!prior)
|
|
471
|
+
return candidate;
|
|
472
|
+
const sources = [
|
|
473
|
+
prior.primary_source,
|
|
474
|
+
...prior.evidence.map((entry) => entry.source),
|
|
475
|
+
...(prior.workload_context ? [prior.workload_context.source] : []),
|
|
476
|
+
];
|
|
477
|
+
const byIdentity = new Map(sources.map((source) => [sourceIdentity(source), source]));
|
|
478
|
+
const retainSource = (source) => {
|
|
479
|
+
const captured = byIdentity.get(sourceIdentity(source));
|
|
480
|
+
return captured ? { ...source, id: captured.id } : source;
|
|
481
|
+
};
|
|
482
|
+
return {
|
|
483
|
+
...candidate,
|
|
484
|
+
primary_source: retainSource(candidate.primary_source),
|
|
485
|
+
evidence: candidate.evidence.map((entry) => {
|
|
486
|
+
const captured = prior.evidence.find((previous) => sourceIdentity(previous.source) === sourceIdentity(entry.source) &&
|
|
487
|
+
previous.unit === entry.unit &&
|
|
488
|
+
previous.observed_value === entry.observed_value &&
|
|
489
|
+
canonicalizeDiagnosticIdentity(previous.summary) ===
|
|
490
|
+
canonicalizeDiagnosticIdentity(entry.summary));
|
|
491
|
+
return {
|
|
492
|
+
...entry,
|
|
493
|
+
...(captured ? { id: captured.id } : {}),
|
|
494
|
+
source: retainSource(entry.source),
|
|
495
|
+
};
|
|
496
|
+
}),
|
|
497
|
+
...(candidate.workload_context
|
|
498
|
+
? {
|
|
499
|
+
workload_context: {
|
|
500
|
+
...candidate.workload_context,
|
|
501
|
+
source: retainSource(candidate.workload_context.source),
|
|
502
|
+
},
|
|
503
|
+
}
|
|
504
|
+
: {}),
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function sourceIdentity(source) {
|
|
508
|
+
return canonicalizeDiagnosticIdentity([source.source_kind, source.source_id, source.record_id ?? "", source.observed_at].join("\u0000"));
|
|
464
509
|
}
|
|
465
510
|
function validFindings(value) {
|
|
466
511
|
return (value?.schema_version === SUPERVISOR_FINDING_SCHEMA_VERSION &&
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type ProcessGenerationVerdict } from "../agents/state/proc-start.js";
|
|
1
2
|
import { type SupervisorServiceStatusRecord, type SupervisorStatus } from "./contract.js";
|
|
3
|
+
import { type SupervisorProcessDependencies } from "./status.js";
|
|
2
4
|
export declare const SUPERVISOR_DEFAULT_INTERVAL_MS = 2000;
|
|
3
5
|
export declare const SUPERVISOR_DEFAULT_IDLE_EXIT_MS: number;
|
|
4
6
|
export interface RunSupervisorInput {
|
|
@@ -24,6 +26,12 @@ export declare function spawnSupervisor(coordRootRaw: string, options?: {
|
|
|
24
26
|
keepAlive?: boolean;
|
|
25
27
|
}): Promise<SupervisorStatus>;
|
|
26
28
|
export declare function ensureSupervisorRunning(coordRoot: string): Promise<EnsureSupervisorResult>;
|
|
27
|
-
export
|
|
29
|
+
export interface SupervisorStopResult extends SupervisorStatus {
|
|
30
|
+
signal_sent: boolean;
|
|
31
|
+
signal_verdict: ProcessGenerationVerdict;
|
|
32
|
+
}
|
|
33
|
+
export declare function requestSupervisorStop(coordRootRaw: string, dependencies?: SupervisorProcessDependencies & {
|
|
34
|
+
kill?: (pid: number, signal: NodeJS.Signals | number) => unknown;
|
|
35
|
+
}): SupervisorStopResult;
|
|
28
36
|
export declare function runSupervisor(input: RunSupervisorInput): Promise<SupervisorServiceStatusRecord>;
|
|
29
37
|
//# sourceMappingURL=service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/service.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,KAAK,wBAAwB,EAG9B,MAAM,+BAA+B,CAAC;AAevC,OAAO,EAML,KAAK,6BAA6B,EAElC,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAC;AAavB,OAAO,EAGL,KAAK,6BAA6B,EAEnC,MAAM,aAAa,CAAC;AASrB,eAAO,MAAM,8BAA8B,OAAQ,CAAC;AACpD,eAAO,MAAM,+BAA+B,QAAa,CAAC;AAe1D,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,IAAI,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,GAC1D;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9E,OAAO,CAAC,gBAAgB,CAAC,CAyD3B;AAED,wBAAsB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAahG;AAED,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAC5D,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,wBAAwB,CAAC;CAC1C;AAED,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,YAAY,GAAE,6BAA6B,GAAG;IAC5C,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,KAAK,OAAO,CAAC;CAC7D,GACL,oBAAoB,CA8BtB;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,6BAA6B,CAAC,CA+QxC"}
|
|
@@ -6,7 +6,8 @@ import { resolve } from "node:path";
|
|
|
6
6
|
import { performance } from "node:perf_hooks";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { collectCoordinationHealthSnapshot } from "../agents/health.js";
|
|
9
|
-
import {
|
|
9
|
+
import { processStartToken, signalIfSameGeneration, } from "../agents/state/proc-start.js";
|
|
10
|
+
import { resolveBinName } from "../config.js";
|
|
10
11
|
import { pressureHistoryFromSupervisor } from "../diagnostics/advice.js";
|
|
11
12
|
import { assessPressure } from "../diagnostics/pressure.js";
|
|
12
13
|
import { readEventV3ControlState } from "../events/v3/control.js";
|
|
@@ -28,7 +29,7 @@ import { writeSupervisorHookHealth } from "./hook-health-storage.js";
|
|
|
28
29
|
import { collectHookHealth } from "./hooks.js";
|
|
29
30
|
import { SupervisorLogCollector } from "./log-feed.js";
|
|
30
31
|
import { collectServiceHealth } from "./services.js";
|
|
31
|
-
import { readSupervisorStatus } from "./status.js";
|
|
32
|
+
import { installedSupervisorBuild, readSupervisorStatus, supervisorProcessLiveness, } from "./status.js";
|
|
32
33
|
import { readSupervisorFindings, readSupervisorHistory, readSupervisorPressure, supervisorPaths, } from "./storage.js";
|
|
33
34
|
import { buildSupervisorTimeline } from "./timeline.js";
|
|
34
35
|
export const SUPERVISOR_DEFAULT_INTERVAL_MS = 2_000;
|
|
@@ -41,15 +42,17 @@ const LIVE_DISPLAY_SWEEP_INTERVAL_MS = 5 * 60_000;
|
|
|
41
42
|
export async function spawnSupervisor(coordRootRaw, options = {}) {
|
|
42
43
|
const coordRoot = resolve(coordRootRaw);
|
|
43
44
|
const current = readSupervisorStatus(coordRoot);
|
|
44
|
-
if (current.
|
|
45
|
+
if (current.liveness === "unknown")
|
|
46
|
+
throw unknownOwnerError();
|
|
47
|
+
if (current.liveness === "running") {
|
|
45
48
|
throw new Error(`local supervisor is already running under pid ${current.record?.pid}`);
|
|
46
49
|
}
|
|
50
|
+
const paths = supervisorPaths(coordRoot);
|
|
51
|
+
assertLeaseAvailable(paths.lease);
|
|
47
52
|
await stopLegacyResourceObserver(coordRoot);
|
|
48
53
|
const intervalMs = normalizeInterval(options.intervalMs);
|
|
49
54
|
const idleExitMs = normalizeIdleExit(options.idleExitMs);
|
|
50
|
-
const paths = supervisorPaths(coordRoot);
|
|
51
55
|
mkdirSync(paths.root, { recursive: true, mode: stateDirMode() });
|
|
52
|
-
rmSync(paths.stop, { force: true });
|
|
53
56
|
const cliPath = fileURLToPath(new URL(import.meta.url.endsWith(".ts") ? "../../cli.ts" : "../../cli.js", import.meta.url));
|
|
54
57
|
if (!existsSync(cliPath))
|
|
55
58
|
throw new Error(`cannot find harn entrypoint at ${cliPath}`);
|
|
@@ -109,27 +112,38 @@ export async function ensureSupervisorRunning(coordRoot) {
|
|
|
109
112
|
};
|
|
110
113
|
}
|
|
111
114
|
}
|
|
112
|
-
export function requestSupervisorStop(coordRootRaw) {
|
|
115
|
+
export function requestSupervisorStop(coordRootRaw, dependencies = {}) {
|
|
113
116
|
const coordRoot = resolve(coordRootRaw);
|
|
114
117
|
const paths = supervisorPaths(coordRoot);
|
|
115
|
-
const status = readSupervisorStatus(coordRoot);
|
|
118
|
+
const status = readSupervisorStatus(coordRoot, Date.now(), dependencies);
|
|
116
119
|
mkdirSync(paths.root, { recursive: true, mode: stateDirMode() });
|
|
117
120
|
writePrivateJsonAtomic(paths.stop, {
|
|
118
121
|
requested_at: new Date().toISOString(),
|
|
119
122
|
requested_by_pid: process.pid,
|
|
120
123
|
});
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
const legacyPaths = resourcePaths(coordRoot);
|
|
125
|
+
if (existsSync(legacyPaths.service) || existsSync(legacyPaths.lease))
|
|
126
|
+
requestResourceServiceStop(coordRoot, dependencies);
|
|
127
|
+
let signal = { sent: false, verdict: status.generation };
|
|
128
|
+
if (status.liveness === "running" &&
|
|
129
|
+
status.record?.host === hostname() &&
|
|
130
|
+
process.platform !== "win32") {
|
|
131
|
+
signal = signalIfSameGeneration(status.record.pid, status.record.start_token, "SIGTERM", dependencies);
|
|
128
132
|
}
|
|
129
|
-
return
|
|
133
|
+
return {
|
|
134
|
+
...readSupervisorStatus(coordRoot, Date.now(), dependencies),
|
|
135
|
+
signal_sent: signal.sent,
|
|
136
|
+
signal_verdict: signal.verdict,
|
|
137
|
+
};
|
|
130
138
|
}
|
|
131
139
|
export async function runSupervisor(input) {
|
|
132
140
|
const coordRoot = resolve(input.coordRoot);
|
|
141
|
+
const current = readSupervisorStatus(coordRoot);
|
|
142
|
+
if (current.liveness === "unknown")
|
|
143
|
+
throw unknownOwnerError();
|
|
144
|
+
if (current.liveness === "running") {
|
|
145
|
+
throw new Error(`local supervisor is already running under pid ${current.record?.pid}`);
|
|
146
|
+
}
|
|
133
147
|
const paths = supervisorPaths(coordRoot);
|
|
134
148
|
const intervalMs = normalizeInterval(input.intervalMs);
|
|
135
149
|
const idleExitMs = normalizeIdleExit(input.idleExitMs);
|
|
@@ -144,6 +158,7 @@ export async function runSupervisor(input) {
|
|
|
144
158
|
schema_version: SUPERVISOR_STATUS_SCHEMA_VERSION,
|
|
145
159
|
pid: process.pid,
|
|
146
160
|
...(startToken ? { start_token: startToken } : {}),
|
|
161
|
+
running_build: installedSupervisorBuild(),
|
|
147
162
|
host: hostname(),
|
|
148
163
|
nonce: lease.nonce,
|
|
149
164
|
state: "starting",
|
|
@@ -451,7 +466,10 @@ function acquireLease(coordRoot, now) {
|
|
|
451
466
|
if (error.code !== "EEXIST")
|
|
452
467
|
throw error;
|
|
453
468
|
const existing = readLease(paths.lease);
|
|
454
|
-
|
|
469
|
+
const liveness = existing ? leaseIsLive(existing) : "unknown";
|
|
470
|
+
if (liveness === "unknown")
|
|
471
|
+
throw unknownOwnerError();
|
|
472
|
+
if (existing && liveness === "running") {
|
|
455
473
|
throw new Error(`local supervisor lease is held by pid ${existing.pid}`);
|
|
456
474
|
}
|
|
457
475
|
try {
|
|
@@ -487,7 +505,13 @@ function releaseLease(path, nonce) {
|
|
|
487
505
|
function readLease(path) {
|
|
488
506
|
try {
|
|
489
507
|
const value = JSON.parse(readFileSync(path, "utf8"));
|
|
490
|
-
return value &&
|
|
508
|
+
return value &&
|
|
509
|
+
Number.isSafeInteger(value.pid) &&
|
|
510
|
+
value.pid > 0 &&
|
|
511
|
+
typeof value.host === "string" &&
|
|
512
|
+
value.host.length > 0 &&
|
|
513
|
+
typeof value.nonce === "string" &&
|
|
514
|
+
value.nonce.length > 0
|
|
491
515
|
? value
|
|
492
516
|
: undefined;
|
|
493
517
|
}
|
|
@@ -496,25 +520,31 @@ function readLease(path) {
|
|
|
496
520
|
}
|
|
497
521
|
}
|
|
498
522
|
function leaseIsLive(lease) {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
523
|
+
return supervisorProcessLiveness(lease).liveness;
|
|
524
|
+
}
|
|
525
|
+
function assertLeaseAvailable(path) {
|
|
526
|
+
if (!existsSync(path))
|
|
527
|
+
return;
|
|
528
|
+
const lease = readLease(path);
|
|
529
|
+
const liveness = lease ? leaseIsLive(lease) : "unknown";
|
|
530
|
+
if (liveness === "unknown")
|
|
531
|
+
throw unknownOwnerError();
|
|
532
|
+
if (liveness === "running")
|
|
533
|
+
throw new Error(`local supervisor lease is held by pid ${lease?.pid}`);
|
|
534
|
+
}
|
|
535
|
+
function unknownOwnerError() {
|
|
536
|
+
return new Error(`local supervisor process identity is unknown; run ${resolveBinName()} supervisor stop to write the durable stop request before starting again`);
|
|
509
537
|
}
|
|
510
538
|
async function stopLegacyResourceObserver(coordRoot) {
|
|
511
539
|
const legacy = readResourceServiceStatus(coordRoot);
|
|
512
|
-
if (
|
|
540
|
+
if (legacy.liveness === "stopped")
|
|
513
541
|
return;
|
|
514
542
|
requestResourceServiceStop(coordRoot);
|
|
515
543
|
const deadline = Date.now() + 2_000;
|
|
516
|
-
while (readResourceServiceStatus(coordRoot).
|
|
544
|
+
while (readResourceServiceStatus(coordRoot).liveness !== "stopped" && Date.now() < deadline)
|
|
517
545
|
await delay(50);
|
|
546
|
+
if (readResourceServiceStatus(coordRoot).liveness !== "stopped")
|
|
547
|
+
throw new Error("legacy resource observer has not stopped after its durable stop request; its process identity may be unknown");
|
|
518
548
|
}
|
|
519
549
|
function log(coordRoot, event, fields, error) {
|
|
520
550
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/services.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/services.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,KAAK,qBAAqB,EAE1B,KAAK,wBAAwB,EAC7B,KAAK,6BAA6B,EACnC,MAAM,eAAe,CAAC;AAGvB,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAClC,wBAAwB,CAY1B;AAED,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAEhF;AAED,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,6BAA6B,GACxC;IAAE,QAAQ,EAAE,qBAAqB,EAAE,CAAC;IAAC,SAAS,EAAE,wBAAwB,EAAE,CAAA;CAAE,CAoC9E"}
|
|
@@ -2,6 +2,7 @@ import { existsSync, readdirSync, readFileSync, rmSync, statSync } from "node:fs
|
|
|
2
2
|
import { hostname } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { checkPidToken, processStartToken } from "../agents/state/proc-start.js";
|
|
5
|
+
import { readGovernorServiceStatus } from "../governor/service-read.js";
|
|
5
6
|
import { writePrivateJsonAtomic } from "../storage/atomic-json.js";
|
|
6
7
|
import { SUPERVISOR_CONSUMER_SCHEMA_VERSION, } from "./contract.js";
|
|
7
8
|
import { readSupervisorConsumer, supervisorConsumerPath, supervisorPaths } from "./storage.js";
|
|
@@ -24,7 +25,7 @@ export function unregisterSupervisorConsumer(coordRoot, id) {
|
|
|
24
25
|
export function collectServiceHealth(coordRoot, supervisor) {
|
|
25
26
|
const consumers = readLiveConsumers(coordRoot);
|
|
26
27
|
const semantic = readDetachedService(join(coordRoot, ".harnery", "semantic", "v2", "service.json"), "semantic-reader");
|
|
27
|
-
const governor =
|
|
28
|
+
const governor = readGovernorHealth(coordRoot);
|
|
28
29
|
const relay = readPresenceRelay(join(coordRoot, ".harnery", "presence", "relay-daemon.json"));
|
|
29
30
|
const dashboard = consumers.find((consumer) => consumer.id === "dashboard");
|
|
30
31
|
const services = [
|
|
@@ -106,6 +107,26 @@ function readDetachedService(path, id) {
|
|
|
106
107
|
const state = pidAlive(record.pid) ? "running" : "stale";
|
|
107
108
|
return projection(id, state, record, state === "stale" ? "recorded-running-process-missing" : undefined);
|
|
108
109
|
}
|
|
110
|
+
function readGovernorHealth(coordRoot) {
|
|
111
|
+
const status = readGovernorServiceStatus(coordRoot);
|
|
112
|
+
const state = status.liveness === "unknown"
|
|
113
|
+
? "unknown"
|
|
114
|
+
: status.running
|
|
115
|
+
? "running"
|
|
116
|
+
: status.stale
|
|
117
|
+
? "stale"
|
|
118
|
+
: "stopped";
|
|
119
|
+
const reason = state === "unknown"
|
|
120
|
+
? "governor-process-generation-unknown"
|
|
121
|
+
: status.generation === "different"
|
|
122
|
+
? "recorded-process-generation-different"
|
|
123
|
+
: status.stale
|
|
124
|
+
? "recorded-running-process-missing"
|
|
125
|
+
: undefined;
|
|
126
|
+
return status.record
|
|
127
|
+
? projection("governor", state, status.record, reason)
|
|
128
|
+
: { id: "governor", state, ...(reason ? { reason } : {}) };
|
|
129
|
+
}
|
|
109
130
|
function readPresenceRelay(path) {
|
|
110
131
|
const record = readBoundedJson(path);
|
|
111
132
|
if (!record || !Number.isSafeInteger(record.pid) || record.pid < 1) {
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
|
|
1
|
+
import { type ProcessGenerationVerdict } from "../agents/state/proc-start.js";
|
|
2
|
+
import { type SupervisorBuild, type SupervisorStatus } from "./contract.js";
|
|
3
|
+
export interface SupervisorProcessDependencies {
|
|
4
|
+
readToken?: (pid: number) => string | null;
|
|
5
|
+
probePid?: (pid: number) => unknown;
|
|
6
|
+
}
|
|
7
|
+
export declare function installedSupervisorBuild(): SupervisorBuild;
|
|
8
|
+
export declare function readSupervisorStatus(coordRoot: string, nowMs?: number, dependencies?: SupervisorProcessDependencies): SupervisorStatus;
|
|
9
|
+
/** A stale heartbeat does not prove that a process has exited. */
|
|
10
|
+
export declare function supervisorProcessLiveness(owner: {
|
|
11
|
+
pid: number;
|
|
12
|
+
host: string;
|
|
13
|
+
start_token?: string;
|
|
14
|
+
}, dependencies?: SupervisorProcessDependencies): {
|
|
15
|
+
liveness: SupervisorStatus["liveness"];
|
|
16
|
+
generation: ProcessGenerationVerdict;
|
|
17
|
+
};
|
|
3
18
|
//# sourceMappingURL=status.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/status.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/status.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,wBAAwB,EAE9B,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAEL,KAAK,eAAe,EAEpB,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAC;AAKvB,MAAM,WAAW,6BAA6B;IAC5C,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CACrC;AAED,wBAAgB,wBAAwB,IAAI,eAAe,CAW1D;AAED,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,KAAK,SAAa,EAClB,YAAY,GAAE,6BAAkC,GAC/C,gBAAgB,CA+ClB;AAeD,kEAAkE;AAClE,wBAAgB,yBAAyB,CACvC,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EAC1D,YAAY,GAAE,6BAAkC,GAC/C;IAAE,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAAC,UAAU,EAAE,wBAAwB,CAAA;CAAE,CAgBlF"}
|
|
@@ -1,28 +1,66 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
1
2
|
import { hostname } from "node:os";
|
|
2
|
-
import {
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { verifyProcessGeneration, } from "../agents/state/proc-start.js";
|
|
5
|
+
import { currentHarneryRuntimeBuild, harneryPackageRoot } from "../events/v3/runtime-build.js";
|
|
3
6
|
import { SUPERVISOR_STATUS_SCHEMA_VERSION, } from "./contract.js";
|
|
4
7
|
import { readSupervisorServiceRecord, supervisorPaths } from "./storage.js";
|
|
5
8
|
const STALE_HEARTBEAT_MS = 15_000;
|
|
6
|
-
export function
|
|
9
|
+
export function installedSupervisorBuild() {
|
|
10
|
+
let version = "unknown";
|
|
11
|
+
let revision = "unknown";
|
|
12
|
+
try {
|
|
13
|
+
const pkg = JSON.parse(readFileSync(join(harneryPackageRoot(), "package.json"), "utf8"));
|
|
14
|
+
if (typeof pkg.version === "string")
|
|
15
|
+
version = pkg.version;
|
|
16
|
+
revision = currentHarneryRuntimeBuild();
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// Missing build evidence must still produce an inspectable status.
|
|
20
|
+
}
|
|
21
|
+
return { version, revision };
|
|
22
|
+
}
|
|
23
|
+
export function readSupervisorStatus(coordRoot, nowMs = Date.now(), dependencies = {}) {
|
|
7
24
|
const paths = supervisorPaths(coordRoot);
|
|
8
25
|
const record = readSupervisorServiceRecord(coordRoot);
|
|
26
|
+
const installedBuild = installedSupervisorBuild();
|
|
27
|
+
const runningBuild = validBuild(record?.running_build) ? record.running_build : null;
|
|
28
|
+
const restartNeeded = !runningBuild ||
|
|
29
|
+
installedBuild.version === "unknown" ||
|
|
30
|
+
installedBuild.revision === "unknown" ||
|
|
31
|
+
runningBuild.version !== installedBuild.version ||
|
|
32
|
+
runningBuild.revision !== installedBuild.revision;
|
|
33
|
+
const build = {
|
|
34
|
+
running_build: runningBuild,
|
|
35
|
+
installed_build: installedBuild,
|
|
36
|
+
restart_needed: restartNeeded,
|
|
37
|
+
build_status: !runningBuild
|
|
38
|
+
? "running build unknown; restart needed"
|
|
39
|
+
: restartNeeded
|
|
40
|
+
? "restart needed"
|
|
41
|
+
: "current",
|
|
42
|
+
};
|
|
9
43
|
if (!validRecord(record)) {
|
|
10
44
|
return {
|
|
11
45
|
running: false,
|
|
12
46
|
stale: false,
|
|
47
|
+
generation: "unknown",
|
|
48
|
+
liveness: existsSync(paths.service) ? "unknown" : "stopped",
|
|
49
|
+
...build,
|
|
13
50
|
status_path: paths.service,
|
|
14
51
|
snapshot_path: paths.snapshot,
|
|
15
52
|
};
|
|
16
53
|
}
|
|
17
54
|
const heartbeatAt = Date.parse(record.heartbeat_at);
|
|
18
55
|
const stale = !Number.isFinite(heartbeatAt) || nowMs - heartbeatAt > STALE_HEARTBEAT_MS;
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
(record.state === "starting" || record.state === "running" || record.state === "stopping") &&
|
|
22
|
-
pidStillMatches(record.pid, record.start_token);
|
|
56
|
+
const owner = supervisorProcessLiveness(record, dependencies);
|
|
57
|
+
const liveness = owner.generation === "same" && record.state === "stopped" ? "stopped" : owner.liveness;
|
|
23
58
|
return {
|
|
24
|
-
running,
|
|
59
|
+
running: liveness === "running" && !stale && record.state !== "stopped",
|
|
25
60
|
stale,
|
|
61
|
+
generation: owner.generation,
|
|
62
|
+
liveness,
|
|
63
|
+
...build,
|
|
26
64
|
record,
|
|
27
65
|
status_path: paths.service,
|
|
28
66
|
snapshot_path: paths.snapshot,
|
|
@@ -36,13 +74,33 @@ function validRecord(value) {
|
|
|
36
74
|
typeof value.heartbeat_at === "string" &&
|
|
37
75
|
typeof value.keep_alive === "boolean");
|
|
38
76
|
}
|
|
39
|
-
|
|
77
|
+
/** A stale heartbeat does not prove that a process has exited. */
|
|
78
|
+
export function supervisorProcessLiveness(owner, dependencies = {}) {
|
|
79
|
+
if (owner.host !== hostname())
|
|
80
|
+
return { liveness: "unknown", generation: "unknown" };
|
|
40
81
|
try {
|
|
41
|
-
process.kill(pid, 0);
|
|
82
|
+
(dependencies.probePid ?? ((pid) => process.kill(pid, 0)))(owner.pid);
|
|
42
83
|
}
|
|
43
84
|
catch (error) {
|
|
44
|
-
|
|
45
|
-
|
|
85
|
+
return {
|
|
86
|
+
liveness: error.code === "ESRCH" ? "stopped" : "unknown",
|
|
87
|
+
generation: "unknown",
|
|
88
|
+
};
|
|
46
89
|
}
|
|
47
|
-
|
|
90
|
+
const generation = verifyProcessGeneration(owner.pid, owner.start_token, dependencies.readToken);
|
|
91
|
+
return {
|
|
92
|
+
generation,
|
|
93
|
+
liveness: generation === "same" ? "running" : generation === "different" ? "stopped" : "unknown",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function validBuild(value) {
|
|
97
|
+
if (!value || typeof value !== "object")
|
|
98
|
+
return false;
|
|
99
|
+
const build = value;
|
|
100
|
+
return (typeof build.version === "string" &&
|
|
101
|
+
!!build.version &&
|
|
102
|
+
build.version !== "unknown" &&
|
|
103
|
+
typeof build.revision === "string" &&
|
|
104
|
+
!!build.revision &&
|
|
105
|
+
build.revision !== "unknown");
|
|
48
106
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/timeline.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/timeline.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,kBAAkB,EAExB,MAAM,eAAe,CAAC;AAEvB,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,iBAAiB,EAC1B,cAAc,GAAE,SAAS,yBAAyB,EAAO,GACxD,kBAAkB,CAoDpB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { canonicalizeDiagnosticIdentity } from "../diagnostics/path-identity.js";
|
|
2
3
|
import { SUPERVISOR_DIAGNOSTIC_LIMITS, SUPERVISOR_TIMELINE_SCHEMA_VERSION, } from "./contract.js";
|
|
3
4
|
export function buildSupervisorTimeline(finding, relatedSources = []) {
|
|
4
5
|
const relatedEntries = clusterRelatedSources(relatedSources);
|
|
@@ -83,5 +84,5 @@ function clusterRelatedSources(sources) {
|
|
|
83
84
|
});
|
|
84
85
|
}
|
|
85
86
|
function stableId(prefix, value) {
|
|
86
|
-
return `${prefix}_${createHash("sha256").update(value).digest("hex").slice(0, 24)}`;
|
|
87
|
+
return `${prefix}_${createHash("sha256").update(canonicalizeDiagnosticIdentity(value)).digest("hex").slice(0, 24)}`;
|
|
87
88
|
}
|
package/dist/core/work/state.js
CHANGED
|
@@ -6,7 +6,7 @@ import { appendSegmentedJsonlFile, readSegmentedJsonlFileSync, } from "../storag
|
|
|
6
6
|
import { stateDirMode, stateFileMode } from "../storage/modes.js";
|
|
7
7
|
import { readWorkflowApproval } from "../workflow/approvals.js";
|
|
8
8
|
import { stableDigest } from "../workflow/durable-record.js";
|
|
9
|
-
import { readWorkflowProof } from "../workflow/proof.js";
|
|
9
|
+
import { deriveRunFailureClass, readWorkflowProof } from "../workflow/proof.js";
|
|
10
10
|
import { readWorkflowRunManifest, workflowScriptDigest } from "../workflow/run-state.js";
|
|
11
11
|
import { WORKFLOW_TRANSCRIPT_EVENT_BYTES, workflowTranscriptPath } from "../workflow/transcript.js";
|
|
12
12
|
import { isWorkspaceBoundExecutionEvidence } from "../workflow/workspaces/validate.js";
|
|
@@ -464,13 +464,13 @@ function inspectAttempt(coordRoot, event, intent, priorRunId) {
|
|
|
464
464
|
proof.acceptance.summary.unknown === 0
|
|
465
465
|
? "succeeded"
|
|
466
466
|
: "failed";
|
|
467
|
-
//
|
|
468
|
-
//
|
|
469
|
-
//
|
|
467
|
+
// Re-derive no-effect eligibility from every participant rather than
|
|
468
|
+
// trusting a persisted transport class. Missing evidence stays charged.
|
|
469
|
+
// The script's explicit decision authority remains a separate path.
|
|
470
470
|
if (attempt.status === "failed" &&
|
|
471
|
-
(proof.run.class === "
|
|
472
|
-
proof.run.class === "upstream"
|
|
473
|
-
|
|
471
|
+
(proof.run.class === "decision" ||
|
|
472
|
+
((proof.run.class === "environment" || proof.run.class === "upstream") &&
|
|
473
|
+
deriveRunFailureClass(proof.run.status, proof.agents) === proof.run.class))) {
|
|
474
474
|
attempt.uncharged = proof.run.class;
|
|
475
475
|
if (proof.run.class === "decision" && typeof proof.run.decision_id === "string") {
|
|
476
476
|
attempt.blocked_on = proof.run.decision_id;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AA0DH,OAAO,KAAK,EAIV,UAAU,EAGV,SAAS,EAeV,MAAM,YAAY,CAAC;AAuDpB,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAM/E;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;gBAEpB,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM;CAOvF;AAED;;;;6EAI6E;AAC7E,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;gBAEjB,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CAUhD;AAED,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CA6E1F"}
|