harnery 0.42.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/commander.d.ts +16 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +18 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +95 -52
- 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/tunnel.d.ts +65 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +290 -101
- package/dist/commands/web-fetch.d.ts +3 -2
- package/dist/commands/web-fetch.d.ts.map +1 -1
- package/dist/commands/web-fetch.js +25 -19
- package/dist/commands/web.d.ts +7 -1
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +9 -3
- 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 +19 -17
- 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 +64 -43
- 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/names.d.ts +43 -3
- package/dist/core/agents/state/names.d.ts.map +1 -1
- package/dist/core/agents/state/names.js +87 -3
- 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/events/v3/support-storage/pack-reader.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/pack-reader.js +42 -16
- 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 +120 -23
- package/dist/core/hooks/fork-lineage.d.ts +40 -0
- package/dist/core/hooks/fork-lineage.d.ts.map +1 -0
- package/dist/core/hooks/fork-lineage.js +62 -0
- package/dist/core/hooks/resolve/transcript.d.ts +16 -0
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
- package/dist/core/hooks/resolve/transcript.js +84 -0
- package/dist/core/hooks/shell-rm.d.ts +2 -0
- package/dist/core/hooks/shell-rm.d.ts.map +1 -0
- package/dist/core/hooks/shell-rm.js +105 -0
- package/dist/core/hooks/shell-tokens.d.ts +12 -0
- package/dist/core/hooks/shell-tokens.d.ts.map +1 -0
- package/dist/core/hooks/shell-tokens.js +97 -0
- package/dist/core/hooks/shell-waiter.d.ts.map +1 -1
- package/dist/core/hooks/shell-waiter.js +3 -93
- 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/semantic/contract.d.ts +5 -5
- 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/storage/process-log.d.ts +12 -0
- package/dist/core/storage/process-log.d.ts.map +1 -1
- package/dist/core/storage/process-log.js +10 -2
- 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/tunnel/allowlist.d.ts +44 -0
- package/dist/lib/tunnel/allowlist.d.ts.map +1 -0
- package/dist/lib/tunnel/allowlist.js +204 -0
- package/dist/lib/tunnel/current-address.d.ts +41 -0
- package/dist/lib/tunnel/current-address.d.ts.map +1 -0
- package/dist/lib/tunnel/current-address.js +123 -0
- package/dist/lib/tunnel/gate.js +13 -6
- package/dist/lib/tunnel/processes.d.ts +39 -0
- package/dist/lib/tunnel/processes.d.ts.map +1 -0
- package/dist/lib/tunnel/processes.js +121 -0
- package/dist/lib/tunnel/state.d.ts +28 -3
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +98 -27
- 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 +8 -1
- package/src/commander.ts +32 -0
- package/src/commands/agents.ts +151 -74
- 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/tunnel.ts +394 -116
- package/src/commands/web-fetch.ts +30 -19
- package/src/commands/web.ts +9 -3
- 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 +35 -20
- package/src/core/agents/render/session-context.ts +92 -48
- 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/names.ts +117 -6
- 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/events/v3/support-storage/pack-reader.ts +45 -19
- 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 +139 -22
- package/src/core/hooks/fork-lineage.ts +82 -0
- package/src/core/hooks/resolve/transcript.ts +88 -0
- package/src/core/hooks/shell-rm.ts +103 -0
- package/src/core/hooks/shell-tokens.ts +96 -0
- package/src/core/hooks/shell-waiter.ts +4 -91
- 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/storage/process-log.ts +17 -2
- 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/tunnel/allowlist.ts +208 -0
- package/src/lib/tunnel/current-address.ts +158 -0
- package/src/lib/tunnel/gate.ts +16 -8
- package/src/lib/tunnel/processes.ts +149 -0
- package/src/lib/tunnel/state.ts +123 -27
- package/src/lib/zip/index.ts +483 -0
|
@@ -20,6 +20,14 @@ import {
|
|
|
20
20
|
readLiveCoordinationRow,
|
|
21
21
|
readLiveCoordinationRows,
|
|
22
22
|
} from "../agents/state/live-coordination-view.ts";
|
|
23
|
+
import {
|
|
24
|
+
readCoordinationViewV3,
|
|
25
|
+
requireAuthoritySafeCoordinationViewV3,
|
|
26
|
+
} from "../events/v3/coordination-view.ts";
|
|
27
|
+
import {
|
|
28
|
+
liveInstanceIdV3,
|
|
29
|
+
observeLiveEventLedgerRouteV3,
|
|
30
|
+
} from "../events/v3/live-route-observer.ts";
|
|
23
31
|
|
|
24
32
|
/**
|
|
25
33
|
* Agent journal: per-agent markdown journal at `.harnery/journal/<instance_id>.md`.
|
|
@@ -421,11 +429,20 @@ export function sweepOrphanJournals(): string[] {
|
|
|
421
429
|
const dir = journalDir();
|
|
422
430
|
const root = monorepoRoot();
|
|
423
431
|
const archived: string[] = [];
|
|
432
|
+
if (!root) return archived;
|
|
433
|
+
let view: ReturnType<typeof readCoordinationViewV3>;
|
|
434
|
+
try {
|
|
435
|
+
if (observeLiveEventLedgerRouteV3(root).state === "blocked") return archived;
|
|
436
|
+
view = requireAuthoritySafeCoordinationViewV3(readCoordinationViewV3(root));
|
|
437
|
+
if (!view.source_complete) return archived;
|
|
438
|
+
} catch {
|
|
439
|
+
// A missing or unsafe read does not prove that an owner has ended.
|
|
440
|
+
return archived;
|
|
441
|
+
}
|
|
424
442
|
for (const f of readdirSync(dir)) {
|
|
425
443
|
if (!f.endsWith(".md")) continue;
|
|
426
444
|
const instanceId = f.replace(/\.md$/, "");
|
|
427
|
-
|
|
428
|
-
if (!hb) {
|
|
445
|
+
if (!view.instances[liveInstanceIdV3(instanceId)]) {
|
|
429
446
|
const dest = archiveJournal(instanceId);
|
|
430
447
|
if (dest) archived.push(dest);
|
|
431
448
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ProcessGenerationVerdict } from "../agents/state/proc-start.ts";
|
|
2
|
+
|
|
1
3
|
export const RESOURCE_SNAPSHOT_SCHEMA_VERSION = 2;
|
|
2
4
|
export const RESOURCE_SERVICE_STATUS_SCHEMA_VERSION = 1;
|
|
3
5
|
|
|
@@ -154,6 +156,8 @@ export interface ResourceServiceStatusRecord {
|
|
|
154
156
|
export interface ResourceServiceStatus {
|
|
155
157
|
running: boolean;
|
|
156
158
|
stale: boolean;
|
|
159
|
+
generation: ProcessGenerationVerdict;
|
|
160
|
+
liveness: "running" | "stopped" | "unknown";
|
|
157
161
|
record?: ResourceServiceStatusRecord;
|
|
158
162
|
status_path: string;
|
|
159
163
|
snapshot_path: string;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
1
2
|
import { hostname } from "node:os";
|
|
2
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type ProcessGenerationVerdict,
|
|
5
|
+
verifyProcessGeneration,
|
|
6
|
+
} from "../agents/state/proc-start.ts";
|
|
3
7
|
import {
|
|
4
8
|
RESOURCE_SERVICE_STATUS_SCHEMA_VERSION,
|
|
5
9
|
type ResourceServiceStatus,
|
|
@@ -9,9 +13,15 @@ import { readResourceServiceRecord, resourcePaths } from "./storage.ts";
|
|
|
9
13
|
|
|
10
14
|
const STALE_HEARTBEAT_MS = 15_000;
|
|
11
15
|
|
|
16
|
+
export interface ResourceProcessDependencies {
|
|
17
|
+
readToken?: (pid: number) => string | null;
|
|
18
|
+
probePid?: (pid: number) => unknown;
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
export function readResourceServiceStatus(
|
|
13
22
|
coordRoot: string,
|
|
14
23
|
nowMs = Date.now(),
|
|
24
|
+
dependencies: ResourceProcessDependencies = {},
|
|
15
25
|
): ResourceServiceStatus {
|
|
16
26
|
const paths = resourcePaths(coordRoot);
|
|
17
27
|
const record = readResourceServiceRecord(coordRoot);
|
|
@@ -19,21 +29,22 @@ export function readResourceServiceStatus(
|
|
|
19
29
|
return {
|
|
20
30
|
running: false,
|
|
21
31
|
stale: false,
|
|
32
|
+
generation: "unknown",
|
|
33
|
+
liveness: existsSync(paths.service) ? "unknown" : "stopped",
|
|
22
34
|
status_path: paths.service,
|
|
23
35
|
snapshot_path: paths.snapshot,
|
|
24
36
|
};
|
|
25
37
|
}
|
|
26
38
|
const heartbeatAt = Date.parse(record.heartbeat_at);
|
|
27
39
|
const stale = !Number.isFinite(heartbeatAt) || nowMs - heartbeatAt > STALE_HEARTBEAT_MS;
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
!stale &&
|
|
32
|
-
(record.state === "starting" || record.state === "running" || record.state === "stopping") &&
|
|
33
|
-
pidStillMatches(record.pid, record.start_token);
|
|
40
|
+
const owner = resourceProcessLiveness(record, dependencies);
|
|
41
|
+
const liveness =
|
|
42
|
+
owner.generation === "same" && record.state === "stopped" ? "stopped" : owner.liveness;
|
|
34
43
|
return {
|
|
35
|
-
running,
|
|
44
|
+
running: liveness === "running" && !stale,
|
|
36
45
|
stale,
|
|
46
|
+
generation: owner.generation,
|
|
47
|
+
liveness,
|
|
37
48
|
record,
|
|
38
49
|
status_path: paths.service,
|
|
39
50
|
snapshot_path: paths.snapshot,
|
|
@@ -52,11 +63,23 @@ function validRecord(
|
|
|
52
63
|
);
|
|
53
64
|
}
|
|
54
65
|
|
|
55
|
-
function
|
|
66
|
+
export function resourceProcessLiveness(
|
|
67
|
+
owner: { pid: number; host: string; start_token?: string },
|
|
68
|
+
dependencies: ResourceProcessDependencies = {},
|
|
69
|
+
): { generation: ProcessGenerationVerdict; liveness: ResourceServiceStatus["liveness"] } {
|
|
70
|
+
if (owner.host !== hostname()) return { generation: "unknown", liveness: "unknown" };
|
|
56
71
|
try {
|
|
57
|
-
process.kill(pid, 0);
|
|
72
|
+
(dependencies.probePid ?? ((pid) => process.kill(pid, 0)))(owner.pid);
|
|
58
73
|
} catch (error) {
|
|
59
|
-
|
|
74
|
+
return {
|
|
75
|
+
generation: "unknown",
|
|
76
|
+
liveness: (error as NodeJS.ErrnoException).code === "ESRCH" ? "stopped" : "unknown",
|
|
77
|
+
};
|
|
60
78
|
}
|
|
61
|
-
|
|
79
|
+
const generation = verifyProcessGeneration(owner.pid, owner.start_token, dependencies.readToken);
|
|
80
|
+
return {
|
|
81
|
+
generation,
|
|
82
|
+
liveness:
|
|
83
|
+
generation === "same" ? "running" : generation === "different" ? "stopped" : "unknown",
|
|
84
|
+
};
|
|
62
85
|
}
|
|
@@ -12,7 +12,12 @@ import {
|
|
|
12
12
|
} from "node:fs";
|
|
13
13
|
import { hostname, platform } from "node:os";
|
|
14
14
|
import { resolve } from "node:path";
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
type ProcessGenerationVerdict,
|
|
17
|
+
processStartToken,
|
|
18
|
+
signalIfSameGeneration,
|
|
19
|
+
} from "../agents/state/proc-start.ts";
|
|
20
|
+
import { resolveBinName } from "../config.ts";
|
|
16
21
|
import { closeProcessLoggers, legacyLogFields, processLogger } from "../storage/logger.ts";
|
|
17
22
|
import { stateDirMode, stateFileMode } from "../storage/modes.ts";
|
|
18
23
|
import {
|
|
@@ -22,7 +27,11 @@ import {
|
|
|
22
27
|
type ResourceServiceStatusRecord,
|
|
23
28
|
} from "./contract.ts";
|
|
24
29
|
import { sampleResources } from "./sampler.ts";
|
|
25
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
type ResourceProcessDependencies,
|
|
32
|
+
readResourceServiceStatus,
|
|
33
|
+
resourceProcessLiveness,
|
|
34
|
+
} from "./service-status.ts";
|
|
26
35
|
import { resourcePaths, writePrivateJsonAtomic } from "./storage.ts";
|
|
27
36
|
|
|
28
37
|
export const RESOURCE_SERVICE_DEFAULT_INTERVAL_MS = 2_000;
|
|
@@ -56,13 +65,14 @@ export async function spawnResourceService(
|
|
|
56
65
|
): Promise<ResourceServiceStatus> {
|
|
57
66
|
const coordRoot = resolve(coordRootRaw);
|
|
58
67
|
const current = readResourceServiceStatus(coordRoot);
|
|
59
|
-
if (current.
|
|
68
|
+
if (current.liveness === "unknown") throw unknownResourceOwnerError();
|
|
69
|
+
if (current.liveness === "running") {
|
|
60
70
|
throw new Error(`resource observer is already running under pid ${current.record?.pid}`);
|
|
61
71
|
}
|
|
62
72
|
const intervalMs = normalizeInterval(options.intervalMs);
|
|
63
73
|
const paths = resourcePaths(coordRoot);
|
|
74
|
+
assertResourceLeaseAvailable(paths.lease);
|
|
64
75
|
mkdirSync(paths.root, { recursive: true, mode: stateDirMode() });
|
|
65
|
-
rmSync(paths.stop, { force: true });
|
|
66
76
|
const harnBin = new URL("../../../bin/harn", import.meta.url).pathname;
|
|
67
77
|
if (!existsSync(harnBin)) throw new Error(`cannot find harn executable at ${harnBin}`);
|
|
68
78
|
let spawnError: Error | undefined;
|
|
@@ -119,29 +129,49 @@ export async function ensureResourceServiceRunning(
|
|
|
119
129
|
}
|
|
120
130
|
}
|
|
121
131
|
|
|
122
|
-
export
|
|
132
|
+
export interface ResourceServiceStopResult extends ResourceServiceStatus {
|
|
133
|
+
signal_sent: boolean;
|
|
134
|
+
signal_verdict: ProcessGenerationVerdict;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function requestResourceServiceStop(
|
|
138
|
+
coordRootRaw: string,
|
|
139
|
+
dependencies: ResourceProcessDependencies & {
|
|
140
|
+
kill?: (pid: number, signal: NodeJS.Signals | number) => unknown;
|
|
141
|
+
} = {},
|
|
142
|
+
): ResourceServiceStopResult {
|
|
123
143
|
const coordRoot = resolve(coordRootRaw);
|
|
124
144
|
const paths = resourcePaths(coordRoot);
|
|
125
|
-
const status = readResourceServiceStatus(coordRoot);
|
|
145
|
+
const status = readResourceServiceStatus(coordRoot, Date.now(), dependencies);
|
|
126
146
|
mkdirSync(paths.root, { recursive: true, mode: stateDirMode() });
|
|
127
147
|
writePrivateJsonAtomic(paths.stop, {
|
|
128
148
|
requested_at: new Date().toISOString(),
|
|
129
149
|
requested_by_pid: process.pid,
|
|
130
150
|
});
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
151
|
+
const signal =
|
|
152
|
+
status.liveness === "running" && status.record?.host === hostname()
|
|
153
|
+
? signalIfSameGeneration(
|
|
154
|
+
status.record.pid,
|
|
155
|
+
status.record.start_token,
|
|
156
|
+
"SIGTERM",
|
|
157
|
+
dependencies,
|
|
158
|
+
)
|
|
159
|
+
: { sent: false, verdict: status.generation };
|
|
160
|
+
return {
|
|
161
|
+
...readResourceServiceStatus(coordRoot, Date.now(), dependencies),
|
|
162
|
+
signal_sent: signal.sent,
|
|
163
|
+
signal_verdict: signal.verdict,
|
|
164
|
+
};
|
|
139
165
|
}
|
|
140
166
|
|
|
141
167
|
export async function runResourceService(
|
|
142
168
|
input: RunResourceServiceInput,
|
|
143
169
|
): Promise<ResourceServiceStatusRecord> {
|
|
144
170
|
const coordRoot = resolve(input.coordRoot);
|
|
171
|
+
const current = readResourceServiceStatus(coordRoot);
|
|
172
|
+
if (current.liveness === "unknown") throw unknownResourceOwnerError();
|
|
173
|
+
if (current.liveness === "running")
|
|
174
|
+
throw new Error(`resource observer is already running under pid ${current.record?.pid}`);
|
|
145
175
|
const paths = resourcePaths(coordRoot);
|
|
146
176
|
const intervalMs = normalizeInterval(input.intervalMs);
|
|
147
177
|
const now = input.now ?? (() => new Date());
|
|
@@ -253,7 +283,9 @@ function acquireLease(coordRoot: string, now: Date): ResourceServiceLease {
|
|
|
253
283
|
} catch (error) {
|
|
254
284
|
if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
|
|
255
285
|
const existing = readLease(paths.lease);
|
|
256
|
-
|
|
286
|
+
const liveness = existing ? leaseIsLive(existing) : "unknown";
|
|
287
|
+
if (liveness === "unknown") throw unknownResourceOwnerError();
|
|
288
|
+
if (existing && liveness === "running") {
|
|
257
289
|
throw new Error(`resource observer lease is held by pid ${existing.pid}`);
|
|
258
290
|
}
|
|
259
291
|
try {
|
|
@@ -279,7 +311,13 @@ function releaseLease(path: string, nonce: string): void {
|
|
|
279
311
|
function readLease(path: string): ResourceServiceLease | undefined {
|
|
280
312
|
try {
|
|
281
313
|
const value = JSON.parse(readFileSync(path, "utf8")) as ResourceServiceLease;
|
|
282
|
-
return value &&
|
|
314
|
+
return value &&
|
|
315
|
+
Number.isSafeInteger(value.pid) &&
|
|
316
|
+
value.pid > 0 &&
|
|
317
|
+
typeof value.host === "string" &&
|
|
318
|
+
value.host.length > 0 &&
|
|
319
|
+
typeof value.nonce === "string" &&
|
|
320
|
+
value.nonce.length > 0
|
|
283
321
|
? value
|
|
284
322
|
: undefined;
|
|
285
323
|
} catch {
|
|
@@ -287,14 +325,23 @@ function readLease(path: string): ResourceServiceLease | undefined {
|
|
|
287
325
|
}
|
|
288
326
|
}
|
|
289
327
|
|
|
290
|
-
function leaseIsLive(lease: ResourceServiceLease):
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
328
|
+
function leaseIsLive(lease: ResourceServiceLease): ResourceServiceStatus["liveness"] {
|
|
329
|
+
return resourceProcessLiveness(lease).liveness;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function assertResourceLeaseAvailable(path: string): void {
|
|
333
|
+
if (!existsSync(path)) return;
|
|
334
|
+
const lease = readLease(path);
|
|
335
|
+
const liveness = lease ? leaseIsLive(lease) : "unknown";
|
|
336
|
+
if (liveness === "unknown") throw unknownResourceOwnerError();
|
|
337
|
+
if (liveness === "running")
|
|
338
|
+
throw new Error(`resource observer lease is held by pid ${lease?.pid}`);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function unknownResourceOwnerError(): Error {
|
|
342
|
+
return new Error(
|
|
343
|
+
`resource observer process identity is unknown; run ${resolveBinName()} supervisor stop to write the durable stop request before starting again`,
|
|
344
|
+
);
|
|
298
345
|
}
|
|
299
346
|
|
|
300
347
|
function log(
|
|
@@ -10,8 +10,9 @@ import {
|
|
|
10
10
|
SUPERVISOR_PRESSURE_SCHEMA_VERSION,
|
|
11
11
|
type SupervisorCapability,
|
|
12
12
|
type SupervisorPressureRecord,
|
|
13
|
+
type SupervisorStatus,
|
|
13
14
|
} from "../supervisor/contract.ts";
|
|
14
|
-
import { readSupervisorStatus } from "../supervisor/status.ts";
|
|
15
|
+
import { installedSupervisorBuild, readSupervisorStatus } from "../supervisor/status.ts";
|
|
15
16
|
import { supervisorPaths } from "../supervisor/storage.ts";
|
|
16
17
|
import {
|
|
17
18
|
RESOURCE_SNAPSHOT_SCHEMA_VERSION,
|
|
@@ -43,7 +44,17 @@ export interface ResourceStatus {
|
|
|
43
44
|
platform: NodeJS.Platform | null;
|
|
44
45
|
namespace: ResourceSnapshot["namespace"];
|
|
45
46
|
support: ResourceSnapshot["support"] | null;
|
|
46
|
-
writer:
|
|
47
|
+
writer: Pick<
|
|
48
|
+
SupervisorStatus,
|
|
49
|
+
| "running"
|
|
50
|
+
| "stale"
|
|
51
|
+
| "generation"
|
|
52
|
+
| "liveness"
|
|
53
|
+
| "running_build"
|
|
54
|
+
| "installed_build"
|
|
55
|
+
| "restart_needed"
|
|
56
|
+
| "build_status"
|
|
57
|
+
>;
|
|
47
58
|
/**
|
|
48
59
|
* The one published pressure assessment, read from the observer's record so
|
|
49
60
|
* this surface, the prompt notice, the dashboard, and a diagnostic bundle all
|
|
@@ -222,12 +233,16 @@ export function formatResourceSummary(status: ResourceStatus): string {
|
|
|
222
233
|
? "unknown"
|
|
223
234
|
: `${formatBytes(m.memory_available_bytes)} available`;
|
|
224
235
|
const diskText = disk ? `${formatBytes(disk.available_bytes as number)} available` : "unknown";
|
|
225
|
-
return `${lead} ${status.namespace}/${status.platform}: CPU ${cpu}, RAM ${ram}, disk ${diskText}; ${Math.round((status.sample_age_ms ?? 0) / 1_000)}s old${status.support?.state === "partial" ? "; partial" : ""}${!status.writer.running ? "
|
|
236
|
+
return `${lead} ${status.namespace}/${status.platform}: CPU ${cpu}, RAM ${ram}, disk ${diskText}; ${Math.round((status.sample_age_ms ?? 0) / 1_000)}s old${status.support?.state === "partial" ? "; partial" : ""}${!status.writer.running ? `; writer ${status.writer.liveness === "unknown" ? "unknown" : status.writer.stale ? "stale" : "stopped"}` : ""}.`;
|
|
226
237
|
}
|
|
227
238
|
|
|
228
239
|
export function formatResourceStatus(status: ResourceStatus, bin: string): string {
|
|
229
240
|
const a = status.assessment;
|
|
230
241
|
const lines = [formatResourceSummary(status)];
|
|
242
|
+
const writer = status.writer;
|
|
243
|
+
lines.push(
|
|
244
|
+
`Writer: ${writer.liveness}; generation ${writer.generation}; running build ${writer.running_build ? `${writer.running_build.version} (${writer.running_build.revision})` : "unknown"}; installed build ${writer.installed_build.version} (${writer.installed_build.revision}); ${writer.build_status}.`,
|
|
245
|
+
);
|
|
231
246
|
lines.push(
|
|
232
247
|
`Assessment: ${a.state} at ${a.scope} scope; limiting resource ${a.limiting_resource}; trend ${a.trend}; evidence ${a.evidence_state}; recommended action ${a.recommended_action}.`,
|
|
233
248
|
);
|
|
@@ -247,7 +262,11 @@ export function formatResourceStatus(status: ResourceStatus, bin: string): strin
|
|
|
247
262
|
lines.push(
|
|
248
263
|
`Unattributed memory: ${Math.round(a.unattributed_memory_percent)}% of machine memory has no validated owner.`,
|
|
249
264
|
);
|
|
250
|
-
if (status.
|
|
265
|
+
if (status.writer.liveness === "unknown")
|
|
266
|
+
lines.push(
|
|
267
|
+
`Inspect the writer with ${bin} supervisor status. Run ${bin} supervisor stop to write its durable stop request before starting again.`,
|
|
268
|
+
);
|
|
269
|
+
else if (status.state !== "fresh" || !status.writer.running)
|
|
251
270
|
lines.push(
|
|
252
271
|
`Start or inspect the writer with ${bin} supervisor start --keep-alive and ${bin} supervisor status.`,
|
|
253
272
|
);
|
|
@@ -345,7 +364,15 @@ export function readPublishedPressure(
|
|
|
345
364
|
});
|
|
346
365
|
// A record can only be trusted while its writer is alive. Every surface makes
|
|
347
366
|
// the same check here so none of them reports a state the others do not.
|
|
348
|
-
|
|
367
|
+
const observer = writer ?? readWriterState(coordRoot, nowMs);
|
|
368
|
+
if (observer.liveness === "unknown") {
|
|
369
|
+
return gap(
|
|
370
|
+
"error",
|
|
371
|
+
"pressure_observer_unknown",
|
|
372
|
+
"Local resource pressure cannot be determined because the resource observer's process identity is unknown.",
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
if (!observer.running) {
|
|
349
376
|
return gap(
|
|
350
377
|
"expired",
|
|
351
378
|
"pressure_observer_not_running",
|
|
@@ -407,7 +434,7 @@ function readWriterState(coordRoot: string, nowMs: number): ResourceStatus["writ
|
|
|
407
434
|
// Keep malformed service metadata from making the read expensive. The shared
|
|
408
435
|
// status reader retains ownership of PID/start-token liveness checks.
|
|
409
436
|
if (statSync(supervisorPaths(coordRoot).service).size > 65_536) {
|
|
410
|
-
return
|
|
437
|
+
return unknownWriterState();
|
|
411
438
|
}
|
|
412
439
|
const writer = readSupervisorStatus(coordRoot, nowMs);
|
|
413
440
|
const futureHeartbeat =
|
|
@@ -415,13 +442,35 @@ function readWriterState(coordRoot: string, nowMs: number): ResourceStatus["writ
|
|
|
415
442
|
return {
|
|
416
443
|
running: writer.running && !futureHeartbeat,
|
|
417
444
|
stale: writer.stale || !!futureHeartbeat,
|
|
445
|
+
generation: writer.generation,
|
|
446
|
+
liveness: writer.liveness,
|
|
447
|
+
running_build: writer.running_build,
|
|
448
|
+
installed_build: writer.installed_build,
|
|
449
|
+
restart_needed: writer.restart_needed,
|
|
450
|
+
build_status: writer.build_status,
|
|
418
451
|
};
|
|
419
|
-
} catch {
|
|
452
|
+
} catch (error) {
|
|
420
453
|
// A missing writer must not break normal coordination.
|
|
421
|
-
return {
|
|
454
|
+
return {
|
|
455
|
+
...unknownWriterState(),
|
|
456
|
+
liveness: (error as NodeJS.ErrnoException).code === "ENOENT" ? "stopped" : "unknown",
|
|
457
|
+
};
|
|
422
458
|
}
|
|
423
459
|
}
|
|
424
460
|
|
|
461
|
+
function unknownWriterState(): ResourceStatus["writer"] {
|
|
462
|
+
return {
|
|
463
|
+
running: false,
|
|
464
|
+
stale: false,
|
|
465
|
+
generation: "unknown",
|
|
466
|
+
liveness: "unknown",
|
|
467
|
+
running_build: null,
|
|
468
|
+
installed_build: installedSupervisorBuild(),
|
|
469
|
+
restart_needed: true,
|
|
470
|
+
build_status: "running build unknown; restart needed",
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
425
474
|
function validPressureRecord(v: unknown): v is SupervisorPressureRecord {
|
|
426
475
|
if (
|
|
427
476
|
!object(v) ||
|