harnery 0.45.0 → 0.45.2
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/commander.js +0 -1
- package/dist/commands/files.d.ts +2 -2
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +4 -7
- package/dist/commands/servers.d.ts.map +1 -1
- package/dist/commands/servers.js +17 -11
- package/dist/commands/tunnel.d.ts +0 -15
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +153 -453
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +6 -45
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +0 -18
- package/dist/core/agents/live-authority-v3.d.ts +0 -1
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +0 -11
- package/dist/core/agents/live-lifecycle-v3.d.ts +2 -4
- package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
- package/dist/core/agents/live-lifecycle-v3.js +7 -28
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +14 -25
- package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-writer.js +0 -5
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +2 -7
- package/dist/core/agents/state/proc-start.d.ts +20 -31
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +37 -70
- package/dist/core/agents/state/service-process.d.ts +61 -0
- package/dist/core/agents/state/service-process.d.ts.map +1 -0
- package/dist/core/agents/state/service-process.js +143 -0
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +70 -180
- package/dist/core/artifacts/activity.d.ts +4 -1
- package/dist/core/artifacts/activity.d.ts.map +1 -1
- package/dist/core/artifacts/activity.js +8 -2
- package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
- package/dist/core/artifacts/delivery-card.js +1 -2
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +38 -12
- package/dist/core/context/index.d.ts +6 -4
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +194 -256
- package/dist/core/context/observation.d.ts +9 -9
- package/dist/core/context/observation.d.ts.map +1 -1
- package/dist/core/context/observation.js +37 -6
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
- package/dist/core/events/v3/authority-outbox.js +0 -29
- package/dist/core/events/v3/producers/coordination-recorder.d.ts +0 -1
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +0 -7
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +64 -77
- package/dist/core/events/v3/runtime-build.d.ts +14 -0
- package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
- package/dist/core/events/v3/runtime-build.js +44 -0
- package/dist/core/governor/index.d.ts +2 -1
- package/dist/core/governor/index.d.ts.map +1 -1
- package/dist/core/governor/index.js +2 -1
- package/dist/core/governor/service-read.d.ts +3 -10
- package/dist/core/governor/service-read.d.ts.map +1 -1
- package/dist/core/governor/service-read.js +10 -94
- package/dist/core/governor/service.d.ts +3 -7
- package/dist/core/governor/service.d.ts.map +1 -1
- package/dist/core/governor/service.js +17 -182
- package/dist/core/hooks/cli.js +78 -48
- package/dist/core/hooks/resolve/owner.js +2 -2
- package/dist/core/resources/sampler.js +2 -2
- package/dist/core/resources/service-status.d.ts +2 -14
- package/dist/core/resources/service-status.d.ts.map +1 -1
- package/dist/core/resources/service-status.js +3 -23
- package/dist/core/resources/service.d.ts +3 -7
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +12 -101
- package/dist/core/resources/status.d.ts.map +1 -1
- package/dist/core/resources/status.js +3 -2
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +9 -2
- package/dist/core/servers/index.d.ts +11 -10
- package/dist/core/servers/index.d.ts.map +1 -1
- package/dist/core/servers/index.js +82 -192
- 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 +2 -45
- package/dist/core/storage/durable-history.js +56 -22
- package/dist/core/storage/segments.d.ts.map +1 -1
- package/dist/core/storage/segments.js +73 -17
- package/dist/core/supervisor/service.d.ts +4 -7
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +23 -122
- package/dist/core/supervisor/services.js +2 -2
- package/dist/core/supervisor/status.d.ts +3 -17
- package/dist/core/supervisor/status.d.ts.map +1 -1
- package/dist/core/supervisor/status.js +7 -70
- package/dist/core/work/state.js +7 -7
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +23 -66
- package/dist/core/workflow/index.d.ts +3 -4
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +2 -2
- package/dist/core/workflow/live-session-v3.d.ts +0 -6
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +5 -126
- package/dist/core/workflow/proof.d.ts +4 -4
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +34 -86
- package/dist/core/workflow/resume-lock.d.ts +17 -0
- package/dist/core/workflow/resume-lock.d.ts.map +1 -0
- package/dist/core/workflow/resume-lock.js +158 -0
- package/dist/core/workflow/run-state.d.ts +0 -21
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +3 -174
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +0 -19
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +0 -13
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +0 -19
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
- package/dist/core/workflow/spawn-opencode.js +0 -14
- package/dist/core/workflow/types.d.ts +11 -54
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.d.ts +4 -0
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +1 -1
- package/dist/core/workflow/workspaces/local-git.js +91 -210
- package/dist/core/workflow/workspaces/ownership.d.ts +0 -7
- package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/ownership.js +3 -130
- package/dist/core/workflow/workspaces/reclaim.d.ts +4 -7
- package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/reclaim.js +0 -4
- package/dist/core/workflow/workspaces/recovery.d.ts +1 -1
- package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/recovery.js +1 -1
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +1 -4
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +0 -4
- package/dist/lib/tunnel/state.d.ts +6 -13
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +12 -58
- package/package.json +1 -6
- package/src/commander.ts +0 -10
- package/src/commands/files.ts +5 -13
- package/src/commands/servers.ts +23 -12
- package/src/commands/tunnel.ts +166 -533
- package/src/commands/workflow.ts +8 -58
- package/src/core/agents/coord-client.ts +0 -20
- package/src/core/agents/live-authority-v3.ts +0 -17
- package/src/core/agents/live-lifecycle-v3.ts +14 -52
- package/src/core/agents/state/heartbeat-writer.ts +12 -29
- package/src/core/agents/state/live-coordination-writer.ts +0 -29
- package/src/core/agents/state/pidmap.ts +2 -15
- package/src/core/agents/state/proc-start.ts +41 -69
- package/src/core/agents/state/service-process.ts +217 -0
- package/src/core/agents/state/stale-sweep.ts +82 -247
- package/src/core/artifacts/activity.ts +9 -2
- package/src/core/artifacts/delivery-card.ts +1 -7
- package/src/core/artifacts/index.ts +45 -12
- package/src/core/context/index.ts +207 -272
- package/src/core/context/observation.ts +40 -9
- package/src/core/events/v3/authority-outbox.ts +0 -56
- package/src/core/events/v3/producers/coordination-recorder.ts +0 -15
- package/src/core/events/v3/producers/recorder.ts +68 -94
- package/src/core/events/v3/runtime-build.ts +62 -0
- package/src/core/governor/index.ts +1 -2
- package/src/core/governor/service-read.ts +15 -114
- package/src/core/governor/service.ts +36 -220
- package/src/core/hooks/cli.ts +85 -47
- package/src/core/hooks/resolve/owner.ts +2 -2
- package/src/core/resources/sampler.ts +3 -2
- package/src/core/resources/service-status.ts +6 -35
- package/src/core/resources/service.ts +23 -133
- package/src/core/resources/status.ts +3 -2
- package/src/core/semantic/service.ts +8 -2
- package/src/core/servers/index.ts +86 -228
- package/src/core/servers/tunnels.ts +3 -55
- package/src/core/storage/durable-history.ts +52 -20
- package/src/core/storage/segments.ts +79 -17
- package/src/core/supervisor/service.ts +33 -155
- package/src/core/supervisor/services.ts +2 -2
- package/src/core/supervisor/status.ts +10 -85
- package/src/core/work/state.ts +7 -7
- package/src/core/workflow/engine.ts +22 -74
- package/src/core/workflow/index.ts +2 -13
- package/src/core/workflow/live-session-v3.ts +5 -146
- package/src/core/workflow/proof.ts +40 -104
- package/src/core/workflow/resume-lock.ts +179 -0
- package/src/core/workflow/run-state.ts +3 -207
- package/src/core/workflow/spawn-claude.ts +0 -18
- package/src/core/workflow/spawn-codex.ts +0 -13
- package/src/core/workflow/spawn-cursor.ts +0 -18
- package/src/core/workflow/spawn-opencode.ts +0 -14
- package/src/core/workflow/types.ts +11 -41
- package/src/core/workflow/workspaces/leases.ts +1 -1
- package/src/core/workflow/workspaces/local-git.ts +120 -239
- package/src/core/workflow/workspaces/ownership.ts +3 -158
- package/src/core/workflow/workspaces/reclaim.ts +3 -9
- package/src/core/workflow/workspaces/recovery.ts +1 -1
- package/src/core/workflow/workspaces/state.ts +1 -5
- package/src/lib/identities/assume.ts +0 -4
- package/src/lib/tunnel/state.ts +15 -70
- package/dist/commands/turn-rollback.d.ts +0 -5
- package/dist/commands/turn-rollback.d.ts.map +0 -1
- package/dist/commands/turn-rollback.js +0 -76
- package/dist/core/agents/state/process-handle.d.ts +0 -26
- package/dist/core/agents/state/process-handle.d.ts.map +0 -1
- package/dist/core/agents/state/process-handle.js +0 -121
- package/dist/core/agents/state/unchanged-file.d.ts +0 -44
- package/dist/core/agents/state/unchanged-file.d.ts.map +0 -1
- package/dist/core/agents/state/unchanged-file.js +0 -390
- package/dist/core/events/v3/generation-custody.d.ts +0 -5
- package/dist/core/events/v3/generation-custody.d.ts.map +0 -1
- package/dist/core/events/v3/generation-custody.js +0 -50
- package/dist/core/turn-rollback/content.d.ts +0 -53
- package/dist/core/turn-rollback/content.d.ts.map +0 -1
- package/dist/core/turn-rollback/content.js +0 -396
- package/dist/core/turn-rollback/index.d.ts +0 -72
- package/dist/core/turn-rollback/index.d.ts.map +0 -1
- package/dist/core/turn-rollback/index.js +0 -443
- package/dist/core/workflow/failure-evidence.d.ts +0 -16
- package/dist/core/workflow/failure-evidence.d.ts.map +0 -1
- package/dist/core/workflow/failure-evidence.js +0 -114
- package/dist/core/workflow/recovery-audit.d.ts +0 -30
- package/dist/core/workflow/recovery-audit.d.ts.map +0 -1
- package/dist/core/workflow/recovery-audit.js +0 -116
- package/src/commands/turn-rollback.ts +0 -111
- package/src/core/agents/state/process-handle.ts +0 -165
- package/src/core/agents/state/unchanged-file.ts +0 -456
- package/src/core/events/v3/generation-custody.ts +0 -59
- package/src/core/turn-rollback/content.ts +0 -499
- package/src/core/turn-rollback/index.ts +0 -580
- package/src/core/workflow/failure-evidence.ts +0 -141
- package/src/core/workflow/recovery-audit.ts +0 -181
|
@@ -1,101 +1,15 @@
|
|
|
1
1
|
import { closeSync, existsSync, openSync, readFileSync, readSync, statSync } from "node:fs";
|
|
2
|
-
import { hostname } from "node:os";
|
|
3
2
|
import { join, resolve } from "node:path";
|
|
4
3
|
import { gunzipSync } from "node:zlib";
|
|
5
4
|
import { coordEnv } from "../../lib/env.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { observeServiceProcess, } from "../agents/state/service-process.js";
|
|
6
|
+
import { serviceBuildStatus } from "../events/v3/runtime-build.js";
|
|
8
7
|
import { createStorageCatalog } from "../storage/catalog.js";
|
|
9
8
|
import { parseLogRecord } from "../storage/jsonl.js";
|
|
10
9
|
import { familyLogDirectory, readSegmentManifest } from "../storage/segments.js";
|
|
11
10
|
const MAX_FILE_BYTES = 512 * 1024;
|
|
12
11
|
const MAX_LOG_RECORDS = 10_000;
|
|
13
12
|
const MAX_GOALS = 100;
|
|
14
|
-
export function currentGovernorServiceBuild() {
|
|
15
|
-
let version = "unknown";
|
|
16
|
-
let revision = "unknown";
|
|
17
|
-
try {
|
|
18
|
-
const manifest = JSON.parse(readFileSync(new URL("../../../package.json", import.meta.url), "utf8"));
|
|
19
|
-
if (typeof manifest.version === "string" && manifest.version.length <= 100) {
|
|
20
|
-
version = manifest.version;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
// Missing package metadata cannot establish that the running build is current.
|
|
25
|
-
}
|
|
26
|
-
try {
|
|
27
|
-
revision = currentHarneryRuntimeBuild();
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
// Status still reports the unavailable build instead of guessing a match.
|
|
31
|
-
}
|
|
32
|
-
return { version, revision };
|
|
33
|
-
}
|
|
34
|
-
export function governorServiceBuildStatus(record) {
|
|
35
|
-
const installed_build = currentGovernorServiceBuild();
|
|
36
|
-
const candidate = record?.running_build;
|
|
37
|
-
const running_build = candidate &&
|
|
38
|
-
typeof candidate.version === "string" &&
|
|
39
|
-
candidate.version.length > 0 &&
|
|
40
|
-
candidate.version.length <= 100 &&
|
|
41
|
-
typeof candidate.revision === "string" &&
|
|
42
|
-
candidate.revision.length > 0 &&
|
|
43
|
-
candidate.revision.length <= 100 &&
|
|
44
|
-
candidate.version !== "unknown" &&
|
|
45
|
-
candidate.revision !== "unknown"
|
|
46
|
-
? candidate
|
|
47
|
-
: null;
|
|
48
|
-
const restart_needed = running_build === null ||
|
|
49
|
-
installed_build.version === "unknown" ||
|
|
50
|
-
installed_build.revision === "unknown" ||
|
|
51
|
-
running_build.version !== installed_build.version ||
|
|
52
|
-
running_build.revision !== installed_build.revision;
|
|
53
|
-
return {
|
|
54
|
-
running_build,
|
|
55
|
-
installed_build,
|
|
56
|
-
restart_needed,
|
|
57
|
-
build_status: running_build === null
|
|
58
|
-
? "running build unknown; restart needed"
|
|
59
|
-
: restart_needed
|
|
60
|
-
? "restart needed"
|
|
61
|
-
: "current",
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
/** An unknown token or process probe cannot establish a running or stopped owner. */
|
|
65
|
-
export function observeGovernorServiceProcess(record, dependencies = {}) {
|
|
66
|
-
if (!record)
|
|
67
|
-
return { generation: "unknown", liveness: "stopped" };
|
|
68
|
-
const generation = record.host === hostname()
|
|
69
|
-
? verifyProcessGeneration(record.pid, record.start_token, dependencies.readToken)
|
|
70
|
-
: "unknown";
|
|
71
|
-
if (record.host !== hostname())
|
|
72
|
-
return { generation, liveness: "unknown" };
|
|
73
|
-
try {
|
|
74
|
-
const present = (dependencies.probePid ?? probePid)(record.pid);
|
|
75
|
-
if (!present)
|
|
76
|
-
return { generation, liveness: "stopped" };
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
return {
|
|
80
|
-
generation,
|
|
81
|
-
liveness: error.code === "ESRCH" ? "stopped" : "unknown",
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
generation,
|
|
86
|
-
liveness: generation === "same"
|
|
87
|
-
? record.state === "stopped"
|
|
88
|
-
? "stopped"
|
|
89
|
-
: "running"
|
|
90
|
-
: generation === "different"
|
|
91
|
-
? "stopped"
|
|
92
|
-
: "unknown",
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
function probePid(pid) {
|
|
96
|
-
process.kill(pid, 0);
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
13
|
export function readGovernorServiceConfig(coordRootRaw) {
|
|
100
14
|
const coordRoot = resolve(coordRootRaw);
|
|
101
15
|
const config = readBoundedJson(join(serviceDir(coordRoot), "config.json"), "governor service configuration");
|
|
@@ -151,15 +65,17 @@ export function readGovernorServiceStatus(coordRootRaw, dependencies = {}) {
|
|
|
151
65
|
catch {
|
|
152
66
|
// Runtime is recoverable and must not make the dashboard unreadable.
|
|
153
67
|
}
|
|
154
|
-
const observation =
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
68
|
+
const observation = record
|
|
69
|
+
? observeServiceProcess(record, dependencies)
|
|
70
|
+
: {
|
|
71
|
+
generation: "unknown",
|
|
72
|
+
liveness: existsSync(join(serviceDir(coordRoot), "status.json")) ? "unknown" : "stopped",
|
|
73
|
+
};
|
|
158
74
|
return {
|
|
159
|
-
running: observation.liveness === "running"
|
|
75
|
+
running: observation.liveness === "running",
|
|
160
76
|
stale: observation.liveness === "stopped" && record !== undefined && record.state !== "stopped",
|
|
161
77
|
...observation,
|
|
162
|
-
...
|
|
78
|
+
...serviceBuildStatus(record?.running_build),
|
|
163
79
|
record,
|
|
164
80
|
config,
|
|
165
81
|
runtime,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ProcessGenerationVerdict } from "../agents/state/proc-start.js";
|
|
2
|
+
import { type ServiceProcessDependencies } from "../agents/state/service-process.js";
|
|
2
3
|
import { type NormalizedPolicy, type PolicyIsolation } from "../policy/index.js";
|
|
3
4
|
import type { RunWorkItemInput } from "../work/index.js";
|
|
4
5
|
import { type GovernorRunReport, type GovernorStopReason } from "./runner.js";
|
|
@@ -55,9 +56,6 @@ export interface GovernorServiceBuild {
|
|
|
55
56
|
version: string;
|
|
56
57
|
revision: string;
|
|
57
58
|
}
|
|
58
|
-
export interface GovernorServiceProcessDependencies extends ProcessGenerationSignalDependencies {
|
|
59
|
-
probePid?: (pid: number) => boolean;
|
|
60
|
-
}
|
|
61
59
|
export interface GovernorServiceStatusRecord {
|
|
62
60
|
schema_version: typeof GOVERNOR_SERVICE_STATUS_SCHEMA_VERSION;
|
|
63
61
|
pid: number;
|
|
@@ -122,10 +120,8 @@ export interface RunGovernorServiceDaemonInput {
|
|
|
122
120
|
}
|
|
123
121
|
export declare function configureGovernorService(input: ConfigureGovernorServiceInput): GovernorServiceConfig;
|
|
124
122
|
export declare function readGovernorServiceConfig(coordRootRaw: string): GovernorServiceConfig;
|
|
125
|
-
export declare function readGovernorServiceRuntime(coordRootRaw: string): GovernorServiceRuntime | undefined;
|
|
126
|
-
export declare function readGovernorServiceStatus(coordRootRaw: string, dependencies?: GovernorServiceProcessDependencies): GovernorServiceStatus;
|
|
127
123
|
export declare function spawnGovernorService(coordRootRaw: string): Promise<GovernorServiceStatus>;
|
|
128
|
-
export declare function requestGovernorServiceStop(coordRootRaw: string, dependencies?:
|
|
124
|
+
export declare function requestGovernorServiceStop(coordRootRaw: string, dependencies?: ServiceProcessDependencies): GovernorServiceStatus;
|
|
129
125
|
export declare function runGovernorServiceSweep(input: RunGovernorServiceSweepInput): Promise<GovernorServiceSweepReport>;
|
|
130
126
|
export declare function runGovernorServiceDaemon(input: RunGovernorServiceDaemonInput): Promise<GovernorServiceStatusRecord>;
|
|
131
127
|
export declare function governorServiceLogPath(coordRoot: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/core/governor/service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/core/governor/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
3
|
-
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync,
|
|
3
|
+
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
4
4
|
import { hostname } from "node:os";
|
|
5
5
|
import { dirname, join, resolve } from "node:path";
|
|
6
6
|
import { coordEnv } from "../../lib/env.js";
|
|
7
|
-
import { processStartToken
|
|
7
|
+
import { processStartToken } from "../agents/state/proc-start.js";
|
|
8
|
+
import { acquireServiceLease, requestServiceStop, serviceStartRefusal, } from "../agents/state/service-process.js";
|
|
8
9
|
import { resolveBinName } from "../config.js";
|
|
10
|
+
import { installedHarneryBuild } from "../events/v3/runtime-build.js";
|
|
9
11
|
import { normalizePolicy } from "../policy/index.js";
|
|
10
12
|
import { closeProcessLoggers, legacyLogFields, processLogger } from "../storage/logger.js";
|
|
11
13
|
import { stateDirMode, stateFileMode } from "../storage/modes.js";
|
|
12
14
|
import { RotatingTextSink } from "../storage/process-log.js";
|
|
13
15
|
import { runGovernor } from "./runner.js";
|
|
14
|
-
import {
|
|
16
|
+
import { readGovernorServiceRuntime, readGovernorServiceStatus } from "./service-read.js";
|
|
15
17
|
import { readGovernor } from "./state.js";
|
|
16
18
|
export const GOVERNOR_SERVICE_CONFIG_SCHEMA_VERSION = 1;
|
|
17
19
|
export const GOVERNOR_SERVICE_RUNTIME_SCHEMA_VERSION = 1;
|
|
@@ -21,15 +23,9 @@ const MAX_GOALS = 100;
|
|
|
21
23
|
const MAX_ERROR = 2_000;
|
|
22
24
|
export function configureGovernorService(input) {
|
|
23
25
|
const coordRoot = resolve(input.coordRoot);
|
|
24
|
-
const
|
|
25
|
-
if (
|
|
26
|
-
throw
|
|
27
|
-
}
|
|
28
|
-
if (active) {
|
|
29
|
-
const liveness = statusOwnerIsLive(active);
|
|
30
|
-
if (liveness !== "stopped")
|
|
31
|
-
throw serviceStartRefusal(coordRoot, active.pid, liveness);
|
|
32
|
-
}
|
|
26
|
+
const current = readGovernorServiceStatus(coordRoot);
|
|
27
|
+
if (current.liveness !== "stopped")
|
|
28
|
+
throw startRefusal(coordRoot, current.record?.pid, current.liveness);
|
|
33
29
|
const goalIds = unique(input.goalIds.map((value) => value.trim()).filter(Boolean));
|
|
34
30
|
if (goalIds.length < 1 || goalIds.length > MAX_GOALS) {
|
|
35
31
|
throw new Error(`governor service requires from 1 to ${MAX_GOALS} unique goal ids`);
|
|
@@ -65,57 +61,12 @@ export function readGovernorServiceConfig(coordRootRaw) {
|
|
|
65
61
|
validateConfig(coordRoot, config);
|
|
66
62
|
return config;
|
|
67
63
|
}
|
|
68
|
-
export function readGovernorServiceRuntime(coordRootRaw) {
|
|
69
|
-
const coordRoot = resolve(coordRootRaw);
|
|
70
|
-
if (!existsSync(serviceRuntimePath(coordRoot)))
|
|
71
|
-
return undefined;
|
|
72
|
-
const runtime = readBoundedJson(serviceRuntimePath(coordRoot), "governor service runtime");
|
|
73
|
-
if (runtime.schema_version !== GOVERNOR_SERVICE_RUNTIME_SCHEMA_VERSION ||
|
|
74
|
-
!validTimestamp(runtime.config_created_at) ||
|
|
75
|
-
!validTimestamp(runtime.updated_at) ||
|
|
76
|
-
!runtime.goals ||
|
|
77
|
-
typeof runtime.goals !== "object" ||
|
|
78
|
-
Array.isArray(runtime.goals)) {
|
|
79
|
-
throw new Error("governor service runtime has an unsupported schema");
|
|
80
|
-
}
|
|
81
|
-
return runtime;
|
|
82
|
-
}
|
|
83
|
-
export function readGovernorServiceStatus(coordRootRaw, dependencies = {}) {
|
|
84
|
-
const coordRoot = resolve(coordRootRaw);
|
|
85
|
-
const record = readStatusRecord(coordRoot);
|
|
86
|
-
let config;
|
|
87
|
-
let runtime;
|
|
88
|
-
try {
|
|
89
|
-
config = readGovernorServiceConfig(coordRoot);
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
// A service has no usable configuration until explicitly configured.
|
|
93
|
-
}
|
|
94
|
-
try {
|
|
95
|
-
runtime = readGovernorServiceRuntime(coordRoot);
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
// Status remains readable when recoverable runtime state is corrupt.
|
|
99
|
-
}
|
|
100
|
-
const observation = observeGovernorServiceProcess(record, dependencies);
|
|
101
|
-
if (!record && existsSync(serviceStatusPath(coordRoot)))
|
|
102
|
-
observation.liveness = "unknown";
|
|
103
|
-
return {
|
|
104
|
-
running: observation.liveness === "running" && record?.state !== "stopped",
|
|
105
|
-
stale: observation.liveness === "stopped" && record !== undefined && record.state !== "stopped",
|
|
106
|
-
...observation,
|
|
107
|
-
...governorServiceBuildStatus(record),
|
|
108
|
-
record,
|
|
109
|
-
config,
|
|
110
|
-
runtime,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
64
|
export async function spawnGovernorService(coordRootRaw) {
|
|
114
65
|
const coordRoot = resolve(coordRootRaw);
|
|
115
66
|
readGovernorServiceConfig(coordRoot);
|
|
116
67
|
const current = readGovernorServiceStatus(coordRoot);
|
|
117
68
|
if (current.liveness !== "stopped") {
|
|
118
|
-
throw
|
|
69
|
+
throw startRefusal(coordRoot, current.record?.pid, current.liveness);
|
|
119
70
|
}
|
|
120
71
|
mkdirSync(serviceDir(coordRoot), { recursive: true, mode: stateDirMode() });
|
|
121
72
|
const sharedLogs = coordEnv("SHARED_LOGS") !== "0";
|
|
@@ -164,18 +115,7 @@ export function requestGovernorServiceStop(coordRootRaw, dependencies = {}) {
|
|
|
164
115
|
if ((status.record?.state === "stopped" || !status.record) && status.liveness === "stopped") {
|
|
165
116
|
return { ...status, signal_sent: false, signal_verdict: status.generation };
|
|
166
117
|
}
|
|
167
|
-
|
|
168
|
-
requested_at: new Date().toISOString(),
|
|
169
|
-
requested_by_pid: process.pid,
|
|
170
|
-
});
|
|
171
|
-
const signal = status.record?.host === hostname() && status.record.state !== "stopped"
|
|
172
|
-
? signalIfSameGeneration(status.record.pid, status.record.start_token, "SIGTERM", dependencies)
|
|
173
|
-
: { sent: false, verdict: status.generation };
|
|
174
|
-
return {
|
|
175
|
-
...readGovernorServiceStatus(coordRoot, dependencies),
|
|
176
|
-
signal_sent: signal.sent,
|
|
177
|
-
signal_verdict: signal.verdict,
|
|
178
|
-
};
|
|
118
|
+
return requestServiceStop(status, () => readGovernorServiceStatus(coordRoot, dependencies), serviceStopPath(coordRoot), dependencies);
|
|
179
119
|
}
|
|
180
120
|
export async function runGovernorServiceSweep(input) {
|
|
181
121
|
const coordRoot = resolve(input.coordRoot);
|
|
@@ -313,23 +253,17 @@ export async function runGovernorServiceSweep(input) {
|
|
|
313
253
|
export async function runGovernorServiceDaemon(input) {
|
|
314
254
|
const coordRoot = resolve(input.coordRoot);
|
|
315
255
|
const config = readGovernorServiceConfig(coordRoot);
|
|
316
|
-
const
|
|
317
|
-
if (
|
|
318
|
-
throw
|
|
319
|
-
}
|
|
320
|
-
if (existing) {
|
|
321
|
-
const liveness = statusOwnerIsLive(existing);
|
|
322
|
-
if (liveness !== "stopped")
|
|
323
|
-
throw serviceStartRefusal(coordRoot, existing.pid, liveness);
|
|
324
|
-
}
|
|
325
|
-
const release = acquireServiceLease(coordRoot);
|
|
256
|
+
const current = readGovernorServiceStatus(coordRoot);
|
|
257
|
+
if (current.liveness !== "stopped")
|
|
258
|
+
throw startRefusal(coordRoot, current.record?.pid, current.liveness);
|
|
259
|
+
const { release } = acquireServiceLease(serviceLeasePath(coordRoot), (pid, liveness) => startRefusal(coordRoot, pid, liveness), { running_build: installedHarneryBuild() });
|
|
326
260
|
rmSync(serviceStopPath(coordRoot), { force: true });
|
|
327
261
|
const now = new Date().toISOString();
|
|
328
262
|
const status = {
|
|
329
263
|
schema_version: GOVERNOR_SERVICE_STATUS_SCHEMA_VERSION,
|
|
330
264
|
pid: process.pid,
|
|
331
265
|
start_token: processStartToken(process.pid) ?? undefined,
|
|
332
|
-
running_build:
|
|
266
|
+
running_build: installedHarneryBuild(),
|
|
333
267
|
host: hostname(),
|
|
334
268
|
nonce: randomUUID(),
|
|
335
269
|
state: "starting",
|
|
@@ -537,107 +471,8 @@ function normalizeIsolation(value) {
|
|
|
537
471
|
}
|
|
538
472
|
throw new Error("service isolation must be shared, worktree, sandbox, or remote");
|
|
539
473
|
}
|
|
540
|
-
function
|
|
541
|
-
|
|
542
|
-
mkdirSync(dirname(path), { recursive: true, mode: stateDirMode() });
|
|
543
|
-
const owner = {
|
|
544
|
-
pid: process.pid,
|
|
545
|
-
start_token: processStartToken(process.pid) ?? undefined,
|
|
546
|
-
running_build: currentGovernorServiceBuild(),
|
|
547
|
-
host: hostname(),
|
|
548
|
-
nonce: randomUUID(),
|
|
549
|
-
created_at: new Date().toISOString(),
|
|
550
|
-
};
|
|
551
|
-
const acquire = () => {
|
|
552
|
-
try {
|
|
553
|
-
const fd = openSync(path, "wx", stateFileMode());
|
|
554
|
-
try {
|
|
555
|
-
writeFileSync(fd, `${JSON.stringify(owner)}\n`, "utf8");
|
|
556
|
-
}
|
|
557
|
-
finally {
|
|
558
|
-
closeSync(fd);
|
|
559
|
-
}
|
|
560
|
-
return true;
|
|
561
|
-
}
|
|
562
|
-
catch (error) {
|
|
563
|
-
if (error.code !== "EEXIST")
|
|
564
|
-
throw error;
|
|
565
|
-
return false;
|
|
566
|
-
}
|
|
567
|
-
};
|
|
568
|
-
if (!acquire()) {
|
|
569
|
-
const existing = readLease(path);
|
|
570
|
-
const liveness = existing ? leaseOwnerIsLive(existing) : "unknown";
|
|
571
|
-
if (liveness !== "stopped") {
|
|
572
|
-
throw serviceStartRefusal(coordRoot, existing?.pid, liveness);
|
|
573
|
-
}
|
|
574
|
-
unlinkSync(path);
|
|
575
|
-
if (!acquire())
|
|
576
|
-
throw new Error("governor service lease raced with another process");
|
|
577
|
-
}
|
|
578
|
-
return () => {
|
|
579
|
-
try {
|
|
580
|
-
const existing = readLease(path);
|
|
581
|
-
if (existing?.nonce === owner.nonce)
|
|
582
|
-
unlinkSync(path);
|
|
583
|
-
}
|
|
584
|
-
catch {
|
|
585
|
-
// A stale lease is recoverable by the next explicit service start.
|
|
586
|
-
}
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
function readLease(path) {
|
|
590
|
-
try {
|
|
591
|
-
const value = JSON.parse(readFileSync(path, "utf8"));
|
|
592
|
-
return Number.isSafeInteger(value.pid) &&
|
|
593
|
-
(value.pid ?? 0) > 0 &&
|
|
594
|
-
typeof value.host === "string" &&
|
|
595
|
-
typeof value.nonce === "string" &&
|
|
596
|
-
typeof value.created_at === "string"
|
|
597
|
-
? {
|
|
598
|
-
...value,
|
|
599
|
-
start_token: typeof value.start_token === "string" ? value.start_token : undefined,
|
|
600
|
-
}
|
|
601
|
-
: null;
|
|
602
|
-
}
|
|
603
|
-
catch {
|
|
604
|
-
return null;
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
function leaseOwnerIsLive(lease) {
|
|
608
|
-
return observeGovernorServiceProcess({ ...lease, state: "running" }).liveness;
|
|
609
|
-
}
|
|
610
|
-
function readStatusRecord(coordRoot) {
|
|
611
|
-
if (!existsSync(serviceStatusPath(coordRoot)))
|
|
612
|
-
return undefined;
|
|
613
|
-
try {
|
|
614
|
-
const value = readBoundedJson(serviceStatusPath(coordRoot), "governor service status");
|
|
615
|
-
if (value.schema_version !== GOVERNOR_SERVICE_STATUS_SCHEMA_VERSION ||
|
|
616
|
-
!Number.isSafeInteger(value.pid) ||
|
|
617
|
-
value.pid < 1 ||
|
|
618
|
-
typeof value.host !== "string" ||
|
|
619
|
-
typeof value.nonce !== "string" ||
|
|
620
|
-
!validTimestamp(value.started_at) ||
|
|
621
|
-
!validTimestamp(value.heartbeat_at)) {
|
|
622
|
-
return undefined;
|
|
623
|
-
}
|
|
624
|
-
return {
|
|
625
|
-
...value,
|
|
626
|
-
start_token: typeof value.start_token === "string" ? value.start_token : undefined,
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
catch {
|
|
630
|
-
return undefined;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
function statusOwnerIsLive(record) {
|
|
634
|
-
return observeGovernorServiceProcess(record).liveness;
|
|
635
|
-
}
|
|
636
|
-
function serviceStartRefusal(coordRoot, pid, liveness) {
|
|
637
|
-
const reason = liveness === "unknown"
|
|
638
|
-
? `governor service process generation is unknown${pid ? ` for pid ${pid}` : ""}`
|
|
639
|
-
: `governor service process is still running under pid ${pid}`;
|
|
640
|
-
return new Error(`${reason}; run \`${resolveBinName(coordRoot)} governor service stop\` to write a durable stop request before starting or reconfiguring it`);
|
|
474
|
+
function startRefusal(coordRoot, pid, liveness) {
|
|
475
|
+
return serviceStartRefusal("governor service", `${resolveBinName(coordRoot)} governor service stop`, pid, liveness);
|
|
641
476
|
}
|
|
642
477
|
export function appendGovernorServiceDiagnostic(coordRoot, data) {
|
|
643
478
|
if (coordEnv("SHARED_LOGS") !== "0") {
|
package/dist/core/hooks/cli.js
CHANGED
|
@@ -631,6 +631,9 @@ async function main() {
|
|
|
631
631
|
if (eventName === "runtime-context-retry") {
|
|
632
632
|
return runRuntimeContextRetryWorker(coordRoot, adapter, extra);
|
|
633
633
|
}
|
|
634
|
+
if (eventName === SESSION_MAINTENANCE_EVENT) {
|
|
635
|
+
return runSessionMaintenanceWorker(coordRoot, adapter);
|
|
636
|
+
}
|
|
634
637
|
const ledgerRoute = resolveLiveEventLedgerRouteV3(coordRoot);
|
|
635
638
|
// Always log a breadcrumb, useful when an event_type maps to null or owner
|
|
636
639
|
// resolution fails. Stays cheap (one append) and self-prunes via repo log
|
|
@@ -1007,7 +1010,7 @@ function recordContextTelemetry(run) {
|
|
|
1007
1010
|
}
|
|
1008
1011
|
}
|
|
1009
1012
|
async function handleSessionStarted(run) {
|
|
1010
|
-
const { coordRoot, adapter,
|
|
1013
|
+
const { coordRoot, adapter, payload, owner, sessionId, data } = run;
|
|
1011
1014
|
// A configured backup runs out of process and is freshness-gated per host.
|
|
1012
1015
|
// Startup never waits for restic or a remote provider; the child owns no
|
|
1013
1016
|
// hook stdio, so the harness is not held open until it finishes. A failed
|
|
@@ -1019,53 +1022,10 @@ async function handleSessionStarted(run) {
|
|
|
1019
1022
|
catch (err) {
|
|
1020
1023
|
logError(coordRoot, err, { phase: "backup-schedule" });
|
|
1021
1024
|
}
|
|
1022
|
-
//
|
|
1023
|
-
//
|
|
1024
|
-
//
|
|
1025
|
-
|
|
1026
|
-
journalJanitor(coordRoot);
|
|
1027
|
-
imageJanitor(coordRoot);
|
|
1028
|
-
// Session start is the only caller of the artifact janitor and the storage
|
|
1029
|
-
// maintenance pass, so both load here instead of on every tool call.
|
|
1030
|
-
const { autoCleanArtifacts } = await import("../artifacts/index.js");
|
|
1031
|
-
const { autoCleanEventV3Archives } = await import("../events/v3/archive-retention.js");
|
|
1032
|
-
const { createAutomaticMaintenanceComposition, runAutomaticMaintenancePass } = await import("../storage/maintenance-providers.js");
|
|
1033
|
-
// Effect: throttled hourly artifact sweep (see autoCleanArtifacts). It
|
|
1034
|
-
// deletes expired workspaces, plus unheld workspaces over the per-bundle
|
|
1035
|
-
// ceiling or repository budget once they have been idle past
|
|
1036
|
-
// artifacts.idle_grace_hours. Each deletion is rechecked first and recorded
|
|
1037
|
-
// in .harnery/artifact-deletions.jsonl. Best-effort like every janitor
|
|
1038
|
-
// here: a failure logs and never blocks session start.
|
|
1039
|
-
try {
|
|
1040
|
-
autoCleanArtifacts(coordRoot);
|
|
1041
|
-
}
|
|
1042
|
-
catch (err) {
|
|
1043
|
-
logError(coordRoot, err, { phase: "artifact-auto-clean" });
|
|
1044
|
-
}
|
|
1045
|
-
// Effect: apply the same daily throttle to closed V3 epochs. The newest
|
|
1046
|
-
// recovery epochs remain protected by the archive retention policy.
|
|
1047
|
-
try {
|
|
1048
|
-
autoCleanEventV3Archives(coordRoot);
|
|
1049
|
-
}
|
|
1050
|
-
catch (err) {
|
|
1051
|
-
logError(coordRoot, err, { phase: "event-v3-archive-auto-clean" });
|
|
1052
|
-
}
|
|
1053
|
-
// Plan one bounded, claim-first global slice from cached pressure only.
|
|
1054
|
-
// Existing owner janitors above remain the only active executors until
|
|
1055
|
-
// each provider receives a separate production activation.
|
|
1056
|
-
try {
|
|
1057
|
-
await runAutomaticMaintenancePass(createAutomaticMaintenanceComposition(coordRoot, {
|
|
1058
|
-
journal: () => {
|
|
1059
|
-
if (behavior.journalMaintenance)
|
|
1060
|
-
journalJanitor(coordRoot);
|
|
1061
|
-
},
|
|
1062
|
-
images: () => imageJanitor(coordRoot),
|
|
1063
|
-
artifacts: () => autoCleanArtifacts(coordRoot),
|
|
1064
|
-
}));
|
|
1065
|
-
}
|
|
1066
|
-
catch (err) {
|
|
1067
|
-
logError(coordRoot, err, { phase: "storage-maintenance-auto" });
|
|
1068
|
-
}
|
|
1025
|
+
// Storage maintenance can walk large stores. It runs in a detached worker so
|
|
1026
|
+
// session start emits its context without waiting for it; an adapter that
|
|
1027
|
+
// stops a slow hook would otherwise drop that context entirely.
|
|
1028
|
+
scheduleSessionMaintenance(coordRoot, adapter);
|
|
1069
1029
|
let recovery = null;
|
|
1070
1030
|
if (payload?.source === "compact") {
|
|
1071
1031
|
try {
|
|
@@ -1518,6 +1478,76 @@ async function handleCompletedResponse(run) {
|
|
|
1518
1478
|
});
|
|
1519
1479
|
return 0;
|
|
1520
1480
|
}
|
|
1481
|
+
const SESSION_MAINTENANCE_EVENT = "session-maintenance";
|
|
1482
|
+
function scheduleSessionMaintenance(coordRoot, adapter) {
|
|
1483
|
+
const modulePath = process.argv[1];
|
|
1484
|
+
if (!modulePath)
|
|
1485
|
+
return;
|
|
1486
|
+
try {
|
|
1487
|
+
const child = spawn(process.execPath, [modulePath, SESSION_MAINTENANCE_EVENT, "--adapter", adapter], {
|
|
1488
|
+
cwd: coordRoot,
|
|
1489
|
+
detached: true,
|
|
1490
|
+
stdio: "ignore",
|
|
1491
|
+
env: childEnv(coordRoot),
|
|
1492
|
+
});
|
|
1493
|
+
child.unref();
|
|
1494
|
+
}
|
|
1495
|
+
catch (err) {
|
|
1496
|
+
logError(coordRoot, err, { phase: "session-maintenance-spawn" });
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Session-start storage maintenance, run out of the hook's process. Every step
|
|
1501
|
+
* is best-effort and throttled by its own stamp: a failure logs and never
|
|
1502
|
+
* reaches the session.
|
|
1503
|
+
*/
|
|
1504
|
+
async function runSessionMaintenanceWorker(coordRoot, adapter) {
|
|
1505
|
+
const journalMaintenance = adapter ? adapterBehavior(adapter).journalMaintenance : false;
|
|
1506
|
+
// Effect (journal-maintaining adapters): prune stale journal archives +
|
|
1507
|
+
// sweep orphans. The recovery cue is read separately at session start.
|
|
1508
|
+
if (journalMaintenance)
|
|
1509
|
+
journalJanitor(coordRoot);
|
|
1510
|
+
imageJanitor(coordRoot);
|
|
1511
|
+
const { autoCleanArtifacts } = await import("../artifacts/index.js");
|
|
1512
|
+
const { autoCleanEventV3Archives } = await import("../events/v3/archive-retention.js");
|
|
1513
|
+
const { createAutomaticMaintenanceComposition, runAutomaticMaintenancePass } = await import("../storage/maintenance-providers.js");
|
|
1514
|
+
// Effect: throttled hourly artifact sweep (see autoCleanArtifacts). It
|
|
1515
|
+
// deletes expired workspaces, plus unheld workspaces over the per-bundle
|
|
1516
|
+
// ceiling or repository budget once they have been idle past
|
|
1517
|
+
// artifacts.idle_grace_hours. Each deletion is rechecked first and recorded
|
|
1518
|
+
// in .harnery/artifact-deletions.jsonl.
|
|
1519
|
+
try {
|
|
1520
|
+
autoCleanArtifacts(coordRoot);
|
|
1521
|
+
}
|
|
1522
|
+
catch (err) {
|
|
1523
|
+
logError(coordRoot, err, { phase: "artifact-auto-clean" });
|
|
1524
|
+
}
|
|
1525
|
+
// Effect: apply the same daily throttle to closed V3 epochs. The newest
|
|
1526
|
+
// recovery epochs remain protected by the archive retention policy.
|
|
1527
|
+
try {
|
|
1528
|
+
autoCleanEventV3Archives(coordRoot);
|
|
1529
|
+
}
|
|
1530
|
+
catch (err) {
|
|
1531
|
+
logError(coordRoot, err, { phase: "event-v3-archive-auto-clean" });
|
|
1532
|
+
}
|
|
1533
|
+
// Plan one bounded, claim-first global slice from cached pressure only.
|
|
1534
|
+
// Existing owner janitors above remain the only active executors until
|
|
1535
|
+
// each provider receives a separate production activation.
|
|
1536
|
+
try {
|
|
1537
|
+
await runAutomaticMaintenancePass(createAutomaticMaintenanceComposition(coordRoot, {
|
|
1538
|
+
journal: () => {
|
|
1539
|
+
if (journalMaintenance)
|
|
1540
|
+
journalJanitor(coordRoot);
|
|
1541
|
+
},
|
|
1542
|
+
images: () => imageJanitor(coordRoot),
|
|
1543
|
+
artifacts: () => autoCleanArtifacts(coordRoot),
|
|
1544
|
+
}));
|
|
1545
|
+
}
|
|
1546
|
+
catch (err) {
|
|
1547
|
+
logError(coordRoot, err, { phase: "storage-maintenance-auto" });
|
|
1548
|
+
}
|
|
1549
|
+
return 0;
|
|
1550
|
+
}
|
|
1521
1551
|
function scheduleRuntimeContextRetry(coordRoot, adapter, nativeSessionId) {
|
|
1522
1552
|
const modulePath = process.argv[1];
|
|
1523
1553
|
if (!modulePath)
|
|
@@ -4,7 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import { coordEnv } from "../../../lib/env.js";
|
|
5
5
|
import { resolveOwnerBySessionEnv } from "../../agents/coord-client.js";
|
|
6
6
|
import { readLiveCoordinationRow } from "../../agents/state/live-coordination-view.js";
|
|
7
|
-
import {
|
|
7
|
+
import { verifyProcessGeneration } from "../../agents/state/proc-start.js";
|
|
8
8
|
/**
|
|
9
9
|
* Resolve the canonical `instance_id` for the current hook invocation.
|
|
10
10
|
*
|
|
@@ -91,7 +91,7 @@ export function resolveOwner(opts) {
|
|
|
91
91
|
// A row whose start token disagrees with the process now holding this
|
|
92
92
|
// pid is about a different, earlier process. Believing it hands this
|
|
93
93
|
// session another agent's identity, so walk past it.
|
|
94
|
-
const recycled =
|
|
94
|
+
const recycled = verifyProcessGeneration(pid, startToken) === "different";
|
|
95
95
|
if (owner && owner.length > 0 && !recycled) {
|
|
96
96
|
return {
|
|
97
97
|
instance_id: owner,
|
|
@@ -5,7 +5,7 @@ import { basename, join } from "node:path";
|
|
|
5
5
|
import { performance } from "node:perf_hooks";
|
|
6
6
|
import { readLiveCoordinationRows } from "../agents/state/live-coordination-view.js";
|
|
7
7
|
import { parsePidmapRow } from "../agents/state/pidmap.js";
|
|
8
|
-
import {
|
|
8
|
+
import { verifyProcessGeneration } from "../agents/state/proc-start.js";
|
|
9
9
|
import { RESOURCE_SNAPSHOT_SCHEMA_VERSION, } from "./contract.js";
|
|
10
10
|
import { collectWindowsHost, collectWindowsHostInBackground, sampleNativeResources, } from "./native.js";
|
|
11
11
|
import { collectSystemResources } from "./system.js";
|
|
@@ -266,7 +266,7 @@ function readOwnershipAnchors(coordRoot, procRoot, readings, services, liveSessi
|
|
|
266
266
|
continue;
|
|
267
267
|
try {
|
|
268
268
|
const row = parsePidmapRow(readFileSync(join(directory, entry), "utf8"));
|
|
269
|
-
if (!row.instanceId ||
|
|
269
|
+
if (!row.instanceId || verifyProcessGeneration(pid, row.startToken) === "different")
|
|
270
270
|
continue;
|
|
271
271
|
anchors.set(pid, { kind: "agent", id: row.instanceId, source: "pid-map" });
|
|
272
272
|
}
|
|
@@ -1,16 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ServiceProcessDependencies } from "../agents/state/service-process.js";
|
|
2
2
|
import { type ResourceServiceStatus } from "./contract.js";
|
|
3
|
-
export
|
|
4
|
-
readToken?: (pid: number) => string | null;
|
|
5
|
-
probePid?: (pid: number) => unknown;
|
|
6
|
-
}
|
|
7
|
-
export declare function readResourceServiceStatus(coordRoot: string, nowMs?: number, dependencies?: ResourceProcessDependencies): ResourceServiceStatus;
|
|
8
|
-
export declare function resourceProcessLiveness(owner: {
|
|
9
|
-
pid: number;
|
|
10
|
-
host: string;
|
|
11
|
-
start_token?: string;
|
|
12
|
-
}, dependencies?: ResourceProcessDependencies): {
|
|
13
|
-
generation: ProcessGenerationVerdict;
|
|
14
|
-
liveness: ResourceServiceStatus["liveness"];
|
|
15
|
-
};
|
|
3
|
+
export declare function readResourceServiceStatus(coordRoot: string, nowMs?: number, dependencies?: ServiceProcessDependencies): ResourceServiceStatus;
|
|
16
4
|
//# sourceMappingURL=service-status.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service-status.d.ts","sourceRoot":"","sources":["../../../src/core/resources/service-status.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"service-status.d.ts","sourceRoot":"","sources":["../../../src/core/resources/service-status.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAEL,KAAK,qBAAqB,EAE3B,MAAM,eAAe,CAAC;AAKvB,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,KAAK,SAAa,EAClB,YAAY,GAAE,0BAA+B,GAC5C,qBAAqB,CAyBvB"}
|