harnery 0.45.2 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commander.d.ts +13 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commands/admission.d.ts.map +1 -1
- package/dist/commands/admission.js +214 -27
- package/dist/commands/agents.d.ts +4 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +102 -25
- package/dist/commands/browse.d.ts +3 -0
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +172 -8
- package/dist/commands/fetch.js +1 -0
- package/dist/commands/ledger-v3.d.ts.map +1 -1
- package/dist/commands/ledger-v3.js +8 -2
- package/dist/commands/presence.d.ts.map +1 -1
- package/dist/commands/presence.js +40 -0
- package/dist/commands/qa-run.d.ts.map +1 -1
- package/dist/commands/qa-run.js +4 -3
- package/dist/commands/review-pack.d.ts.map +1 -1
- package/dist/commands/review-pack.js +33 -6
- package/dist/core/agents/index.d.ts +1 -0
- package/dist/core/agents/index.d.ts.map +1 -1
- package/dist/core/agents/index.js +1 -0
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +11 -4
- package/dist/core/agents/render/session-context.d.ts +0 -6
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +1 -6
- package/dist/core/agents/session-events.d.ts.map +1 -1
- package/dist/core/agents/session-events.js +12 -4
- package/dist/core/agents/state/heartbeat-reader.d.ts +3 -0
- package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.d.ts +3 -1
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.js +6 -2
- package/dist/core/agents/state/parent-reference-contract.d.ts +16 -0
- package/dist/core/agents/state/parent-reference-contract.d.ts.map +1 -0
- package/dist/core/agents/state/parent-reference-contract.js +1 -0
- package/dist/core/agents/state/parent-reference.d.ts +18 -0
- package/dist/core/agents/state/parent-reference.d.ts.map +1 -0
- package/dist/core/agents/state/parent-reference.js +119 -0
- package/dist/core/config.d.ts +39 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +63 -0
- package/dist/core/diagnostics/path-identity.d.ts +1 -1
- package/dist/core/diagnostics/path-identity.d.ts.map +1 -1
- package/dist/core/diagnostics/path-identity.js +51 -6
- package/dist/core/events/v3/canonical.js +43 -14
- package/dist/core/events/v3/index.d.ts +1 -1
- package/dist/core/events/v3/index.d.ts.map +1 -1
- package/dist/core/events/v3/index.js +1 -1
- package/dist/core/events/v3/producers/recorder.d.ts +34 -0
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +117 -13
- package/dist/core/events/v3/reader.d.ts.map +1 -1
- package/dist/core/events/v3/reader.js +53 -13
- package/dist/core/events/v3/validate.d.ts.map +1 -1
- package/dist/core/events/v3/validate.js +36 -1
- package/dist/core/hooks/adapter/native-parent.d.ts +23 -0
- package/dist/core/hooks/adapter/native-parent.d.ts.map +1 -0
- package/dist/core/hooks/adapter/native-parent.js +91 -0
- package/dist/core/hooks/cli.js +79 -4
- package/dist/core/hooks/resolve/owner.d.ts +0 -11
- package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
- package/dist/core/hooks/resolve/owner.js +0 -23
- package/dist/core/hooks/shell-admission.d.ts +84 -0
- package/dist/core/hooks/shell-admission.d.ts.map +1 -0
- package/dist/core/hooks/shell-admission.js +622 -0
- package/dist/core/hooks/shell-commit-tree.d.ts +2 -0
- package/dist/core/hooks/shell-commit-tree.d.ts.map +1 -0
- package/dist/core/hooks/shell-commit-tree.js +104 -0
- package/dist/core/inbox/contract.d.ts +12 -0
- package/dist/core/inbox/contract.d.ts.map +1 -1
- package/dist/core/inbox/contract.js +16 -0
- package/dist/core/inbox/peer-messages.d.ts +41 -16
- package/dist/core/inbox/peer-messages.d.ts.map +1 -1
- package/dist/core/inbox/peer-messages.js +372 -15
- 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 +83 -3
- package/dist/core/inbox/targets.d.ts +17 -3
- package/dist/core/inbox/targets.d.ts.map +1 -1
- package/dist/core/inbox/targets.js +77 -2
- package/dist/core/presence/git.d.ts +18 -12
- package/dist/core/presence/git.d.ts.map +1 -1
- package/dist/core/presence/git.js +32 -38
- package/dist/core/presence/index.d.ts +2 -2
- package/dist/core/presence/index.d.ts.map +1 -1
- package/dist/core/presence/index.js +8 -6
- package/dist/core/presence/mail.d.ts +133 -0
- package/dist/core/presence/mail.d.ts.map +1 -0
- package/dist/core/presence/mail.js +464 -0
- package/dist/core/semantic/contract.d.ts +5 -5
- package/dist/core/storage/modes.d.ts +4 -0
- package/dist/core/storage/modes.d.ts.map +1 -1
- package/dist/core/storage/modes.js +4 -0
- package/dist/core/storage/segments.d.ts.map +1 -1
- package/dist/core/storage/segments.js +5 -4
- package/dist/lib/admission-history.d.ts +96 -0
- package/dist/lib/admission-history.d.ts.map +1 -0
- package/dist/lib/admission-history.js +196 -0
- package/dist/lib/admission.d.ts +56 -7
- package/dist/lib/admission.d.ts.map +1 -1
- package/dist/lib/admission.js +161 -19
- package/dist/lib/browser/client.d.ts +18 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +100 -12
- package/dist/lib/browser/content-checks.d.ts +3 -1
- package/dist/lib/browser/content-checks.d.ts.map +1 -1
- package/dist/lib/browser/content-checks.js +10 -2
- package/dist/lib/browser/critique.d.ts +32 -0
- package/dist/lib/browser/critique.d.ts.map +1 -1
- package/dist/lib/browser/critique.js +72 -3
- package/dist/lib/browser/geometry.d.ts +2 -0
- package/dist/lib/browser/geometry.d.ts.map +1 -1
- package/dist/lib/browser/geometry.js +61 -15
- package/dist/lib/browser/index.d.ts +1 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +1 -1
- package/dist/lib/browser/page-review-contracts.d.ts +6 -1
- package/dist/lib/browser/page-review-contracts.d.ts.map +1 -1
- package/dist/lib/browser/page-review-contracts.js +6 -1
- package/dist/lib/browser/page-review-judge.d.ts +3 -1
- package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
- package/dist/lib/browser/page-review-judge.js +41 -11
- package/dist/lib/browser/page-review-pack.d.ts +3 -0
- package/dist/lib/browser/page-review-pack.d.ts.map +1 -1
- package/dist/lib/browser/page-review-pack.js +5 -2
- package/dist/lib/browser/qa-run-contracts.d.ts +11 -0
- package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
- package/dist/lib/browser/qa-run-contracts.js +11 -4
- package/dist/lib/browser/qa-run.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.js +73 -16
- package/dist/lib/browser/runts.d.ts +4 -2
- package/dist/lib/browser/runts.d.ts.map +1 -1
- package/dist/lib/browser/runts.js +84 -33
- package/dist/lib/browser/windows-chrome.d.ts +14 -0
- package/dist/lib/browser/windows-chrome.d.ts.map +1 -1
- package/dist/lib/browser/windows-chrome.js +42 -0
- package/dist/lib/browser/windows-login-sync.d.ts +110 -0
- package/dist/lib/browser/windows-login-sync.d.ts.map +1 -0
- package/dist/lib/browser/windows-login-sync.js +171 -0
- package/dist/lib/cookies/client.d.ts +30 -5
- package/dist/lib/cookies/client.d.ts.map +1 -1
- package/dist/lib/cookies/client.js +55 -10
- package/dist/lib/cookies/index.d.ts +1 -1
- package/dist/lib/cookies/index.d.ts.map +1 -1
- package/dist/lib/cookies/index.js +1 -1
- package/dist/lib/docs-metadata-sync.d.ts.map +1 -1
- package/dist/lib/docs-metadata-sync.js +41 -2
- package/dist/lib/durable-job.d.ts +8 -0
- package/dist/lib/durable-job.d.ts.map +1 -1
- package/dist/lib/durable-job.js +10 -0
- package/dist/lib/http/client.d.ts +9 -8
- package/dist/lib/http/client.d.ts.map +1 -1
- package/dist/lib/http/client.js +44 -2
- package/dist/lib/instructions/templates.js +4 -4
- package/package.json +2 -2
- package/src/commander.ts +13 -0
- package/src/commands/admission.ts +272 -29
- package/src/commands/agents.ts +132 -27
- package/src/commands/browse.ts +208 -6
- package/src/commands/fetch.ts +1 -0
- package/src/commands/ledger-v3.ts +8 -2
- package/src/commands/presence.ts +49 -0
- package/src/commands/qa-run.ts +4 -3
- package/src/commands/review-pack.ts +41 -5
- package/src/core/agents/index.ts +6 -0
- package/src/core/agents/live-authority-v3.ts +11 -4
- package/src/core/agents/render/session-context.ts +1 -7
- package/src/core/agents/session-events.ts +18 -3
- package/src/core/agents/state/heartbeat-reader.ts +3 -0
- package/src/core/agents/state/live-coordination-view.ts +13 -6
- package/src/core/agents/state/parent-reference-contract.ts +14 -0
- package/src/core/agents/state/parent-reference.ts +179 -0
- package/src/core/config.ts +85 -0
- package/src/core/diagnostics/path-identity.ts +52 -6
- package/src/core/events/v3/canonical.ts +46 -13
- package/src/core/events/v3/index.ts +3 -0
- package/src/core/events/v3/producers/recorder.ts +151 -13
- package/src/core/events/v3/reader.ts +55 -11
- package/src/core/events/v3/validate.ts +42 -1
- package/src/core/hooks/adapter/native-parent.ts +109 -0
- package/src/core/hooks/cli.ts +83 -4
- package/src/core/hooks/resolve/owner.ts +0 -23
- package/src/core/hooks/shell-admission.ts +634 -0
- package/src/core/hooks/shell-commit-tree.ts +102 -0
- package/src/core/inbox/contract.ts +18 -0
- package/src/core/inbox/peer-messages.ts +501 -15
- package/src/core/inbox/service.ts +108 -3
- package/src/core/inbox/targets.ts +94 -3
- package/src/core/presence/git.ts +45 -37
- package/src/core/presence/index.ts +16 -8
- package/src/core/presence/mail.ts +620 -0
- package/src/core/storage/modes.ts +6 -0
- package/src/core/storage/segments.ts +5 -4
- package/src/lib/admission-history.ts +267 -0
- package/src/lib/admission.ts +217 -19
- package/src/lib/browser/client.ts +147 -35
- package/src/lib/browser/content-checks.ts +14 -3
- package/src/lib/browser/critique.ts +113 -3
- package/src/lib/browser/geometry.ts +63 -14
- package/src/lib/browser/index.ts +5 -0
- package/src/lib/browser/page-review-contracts.ts +6 -1
- package/src/lib/browser/page-review-judge.ts +58 -13
- package/src/lib/browser/page-review-pack.ts +13 -2
- package/src/lib/browser/qa-run-contracts.ts +30 -4
- package/src/lib/browser/qa-run.ts +78 -17
- package/src/lib/browser/runts.ts +85 -37
- package/src/lib/browser/windows-chrome.ts +50 -0
- package/src/lib/browser/windows-login-sync.ts +275 -0
- package/src/lib/cookies/client.ts +62 -10
- package/src/lib/cookies/index.ts +3 -0
- package/src/lib/docs-metadata-sync.ts +44 -2
- package/src/lib/durable-job.ts +15 -0
- package/src/lib/http/client.ts +63 -1
- package/src/lib/instructions/templates.ts +4 -4
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { closeSync, constants, fstatSync, lstatSync, openSync, readSync } from "node:fs";
|
|
3
|
+
import { discoverCodexSessionTranscript } from "./runtime-telemetry.ts";
|
|
4
|
+
|
|
5
|
+
export type NativeParentObservation =
|
|
6
|
+
| { state: "observed"; parent_session_id: string; source_witness: string }
|
|
7
|
+
| { state: "none"; reason: string; source_witness: string }
|
|
8
|
+
| { state: "unknown"; reason: string };
|
|
9
|
+
|
|
10
|
+
export interface NativeParentRequest {
|
|
11
|
+
adapter: string;
|
|
12
|
+
native_session_id?: string;
|
|
13
|
+
transcript_path?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface NativeParentReaderOptions {
|
|
17
|
+
discoverTranscript?: (sessionId: string, transcriptPath?: string) => string | undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const HEADER_LIMIT = 1024 * 1024;
|
|
21
|
+
const NATIVE_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
22
|
+
const ORDINARY_SOURCES = new Set(["cli", "exec", "vscode", "app-server"]);
|
|
23
|
+
|
|
24
|
+
function object(value: unknown): Record<string, unknown> | undefined {
|
|
25
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
26
|
+
? (value as Record<string, unknown>)
|
|
27
|
+
: undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Native relationship evidence only; no prompt bodies, state writes or recipient authority. */
|
|
31
|
+
export function readNativeParentReference(
|
|
32
|
+
request: NativeParentRequest,
|
|
33
|
+
options: NativeParentReaderOptions = {},
|
|
34
|
+
): NativeParentObservation {
|
|
35
|
+
if (request.adapter !== "codex") return unknown("native_parent_adapter_unsupported");
|
|
36
|
+
const child = request.native_session_id;
|
|
37
|
+
if (!child || !NATIVE_ID.test(child)) return unknown("native_child_identity_unavailable");
|
|
38
|
+
let fd: number | undefined;
|
|
39
|
+
try {
|
|
40
|
+
const path = (options.discoverTranscript ?? discoverCodexSessionTranscript)(
|
|
41
|
+
child,
|
|
42
|
+
request.transcript_path,
|
|
43
|
+
);
|
|
44
|
+
if (!path) return unknown("native_parent_transcript_unavailable");
|
|
45
|
+
const entry = lstatSync(path);
|
|
46
|
+
if (!entry.isFile() || entry.isSymbolicLink()) return unknown("native_parent_not_regular");
|
|
47
|
+
fd = openSync(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
48
|
+
const before = fstatSync(fd);
|
|
49
|
+
if (!before.isFile() || before.dev !== entry.dev || before.ino !== entry.ino) {
|
|
50
|
+
return unknown("native_parent_file_changed");
|
|
51
|
+
}
|
|
52
|
+
const header = readHeader(fd);
|
|
53
|
+
if (!header) return unknown("native_parent_header_incomplete");
|
|
54
|
+
// Appending later rows is normal. The same header must remain unchanged.
|
|
55
|
+
const checked = readHeader(fd);
|
|
56
|
+
const after = lstatSync(path);
|
|
57
|
+
if (
|
|
58
|
+
!checked?.equals(header) ||
|
|
59
|
+
!after.isFile() ||
|
|
60
|
+
after.isSymbolicLink() ||
|
|
61
|
+
before.dev !== after.dev ||
|
|
62
|
+
before.ino !== after.ino
|
|
63
|
+
) {
|
|
64
|
+
return unknown("native_parent_file_changed");
|
|
65
|
+
}
|
|
66
|
+
const row = object(JSON.parse(header.toString("utf8")));
|
|
67
|
+
const payload = object(row?.payload);
|
|
68
|
+
if (row?.type !== "session_meta" || payload?.id !== child) {
|
|
69
|
+
return unknown("native_parent_child_mismatch");
|
|
70
|
+
}
|
|
71
|
+
const source_witness = createHash("sha256").update(header).digest("hex");
|
|
72
|
+
if (typeof payload.source === "string" && ORDINARY_SOURCES.has(payload.source)) {
|
|
73
|
+
return { state: "none", reason: "native_ordinary_session", source_witness };
|
|
74
|
+
}
|
|
75
|
+
const source = object(payload.source);
|
|
76
|
+
const spawn = object(object(source?.subagent)?.thread_spawn);
|
|
77
|
+
const parent = spawn?.parent_thread_id;
|
|
78
|
+
if (typeof parent !== "string" || !NATIVE_ID.test(parent)) {
|
|
79
|
+
return unknown("native_parent_not_reported");
|
|
80
|
+
}
|
|
81
|
+
if (parent.toLowerCase() === child.toLowerCase())
|
|
82
|
+
return unknown("native_parent_self_reference");
|
|
83
|
+
return { state: "observed", parent_session_id: parent, source_witness };
|
|
84
|
+
} catch {
|
|
85
|
+
return unknown("native_parent_metadata_unreadable");
|
|
86
|
+
} finally {
|
|
87
|
+
if (fd !== undefined) closeSync(fd);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function readHeader(fd: number): Buffer | undefined {
|
|
92
|
+
const chunks: Buffer[] = [];
|
|
93
|
+
let offset = 0;
|
|
94
|
+
while (offset < HEADER_LIMIT) {
|
|
95
|
+
const buffer = Buffer.alloc(Math.min(16 * 1024, HEADER_LIMIT - offset));
|
|
96
|
+
const bytes = readSync(fd, buffer, 0, buffer.length, offset);
|
|
97
|
+
if (bytes === 0) return undefined;
|
|
98
|
+
const data = buffer.subarray(0, bytes);
|
|
99
|
+
const newline = data.indexOf(10);
|
|
100
|
+
if (newline >= 0) return Buffer.concat([...chunks, data.subarray(0, newline)]);
|
|
101
|
+
chunks.push(data);
|
|
102
|
+
offset += bytes;
|
|
103
|
+
}
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function unknown(reason: string): NativeParentObservation {
|
|
108
|
+
return { state: "unknown", reason };
|
|
109
|
+
}
|
package/src/core/hooks/cli.ts
CHANGED
|
@@ -69,6 +69,7 @@ import {
|
|
|
69
69
|
agentsRequireGitFinalization,
|
|
70
70
|
coordFreshnessSeconds,
|
|
71
71
|
hostPromptContextConfig,
|
|
72
|
+
readAdmissionConfig,
|
|
72
73
|
resolveBinName,
|
|
73
74
|
} from "../config.ts";
|
|
74
75
|
import {
|
|
@@ -92,6 +93,7 @@ import {
|
|
|
92
93
|
import type { TurnRitualEvidenceV3 } from "../events/v3/producers/hook-base.ts";
|
|
93
94
|
import { captureSpanClockV3 } from "../events/v3/span-state.ts";
|
|
94
95
|
import { ensureRelayDaemon, fetchPresence, publishPresence } from "../presence/index.ts";
|
|
96
|
+
import { importMail, publishMail } from "../presence/mail.ts";
|
|
95
97
|
import { closeProcessLoggers, legacyLogFields, processLogger } from "../storage/logger.ts";
|
|
96
98
|
import { stableScopeId } from "../workflow/scope-id.ts";
|
|
97
99
|
import { type AdapterBehavior, adapterBehavior } from "./adapter/behaviors/index.ts";
|
|
@@ -142,6 +144,7 @@ import {
|
|
|
142
144
|
} from "./resolve/transcript.ts";
|
|
143
145
|
import { clearHookRunMarker, writeHookRunMarker } from "./run-markers.ts";
|
|
144
146
|
import { sessionNamePresence } from "./session-name-presence.ts";
|
|
147
|
+
import { shellCommitTreeReason } from "./shell-commit-tree.ts";
|
|
145
148
|
import { shellRmReason } from "./shell-rm.ts";
|
|
146
149
|
import { shellWaiterReason } from "./shell-waiter.ts";
|
|
147
150
|
|
|
@@ -343,8 +346,17 @@ function ensureSessionDisplayName(
|
|
|
343
346
|
payload: ParsedPayload | null,
|
|
344
347
|
): void {
|
|
345
348
|
if (coordEnv("WORKFLOW_CHILD") === "1") return;
|
|
346
|
-
|
|
347
|
-
|
|
349
|
+
// This runs on every tool call. The heartbeat cache is the surface this
|
|
350
|
+
// function stamps, so a cache that already carries a name, or marks a
|
|
351
|
+
// session that never takes one, means there is nothing to do. Only a cache
|
|
352
|
+
// that cannot answer pays for the authoritative read, which re-validates the
|
|
353
|
+
// whole active ledger and cost about half a second per call at 22 MB.
|
|
354
|
+
const cached = readHeartbeat(coordRoot, instanceId);
|
|
355
|
+
if (cached?.kind === "subagent" || cached?.kind === "transient" || cached?.workflow_run_id) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (cached?.name?.trim()) return;
|
|
359
|
+
const row = readLiveCoordinationRow(coordRoot, instanceId) ?? cached;
|
|
348
360
|
if (row?.kind === "subagent" || row?.kind === "transient" || row?.workflow_run_id) return;
|
|
349
361
|
if (row?.name?.trim()) return;
|
|
350
362
|
const assigned = assignSessionNameInProcess(coordRoot, instanceId, payload, adapter);
|
|
@@ -1276,8 +1288,23 @@ function recordContextTelemetry(run: HookRun): void {
|
|
|
1276
1288
|
}
|
|
1277
1289
|
}
|
|
1278
1290
|
|
|
1291
|
+
/** Cross-machine peer messages (ADR 0222) ride the presence fetch: import
|
|
1292
|
+
* whatever mail refs are already local. Reads local refs only, at most once
|
|
1293
|
+
* per import interval; never blocks on the network. */
|
|
1294
|
+
async function receiveRemoteMail(coordRoot: string): Promise<void> {
|
|
1295
|
+
const { peerInbox } = await import("../inbox/peer-messages.ts");
|
|
1296
|
+
importMail(coordRoot, peerInbox(coordRoot), { throttle: true });
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1279
1299
|
async function handleSessionStarted(run: HookRun): Promise<void> {
|
|
1280
1300
|
const { coordRoot, adapter, payload, owner, sessionId, data } = run;
|
|
1301
|
+
// Import mail from refs fetched earlier, so a message sent from another
|
|
1302
|
+
// machine while this task was away is in the inbox this context renders.
|
|
1303
|
+
try {
|
|
1304
|
+
await receiveRemoteMail(coordRoot);
|
|
1305
|
+
} catch (err) {
|
|
1306
|
+
logError(coordRoot, err, { phase: "session-start-mail" });
|
|
1307
|
+
}
|
|
1281
1308
|
// A configured backup runs out of process and is freshness-gated per host.
|
|
1282
1309
|
// Startup never waits for restic or a remote provider; the child owns no
|
|
1283
1310
|
// hook stdio, so the harness is not held open until it finishes. A failed
|
|
@@ -1402,6 +1429,7 @@ async function handleTurnStarted(run: HookRun): Promise<void> {
|
|
|
1402
1429
|
// interval; the systemMessage below reads whatever is locally known).
|
|
1403
1430
|
try {
|
|
1404
1431
|
fetchPresence(coordRoot);
|
|
1432
|
+
await receiveRemoteMail(coordRoot);
|
|
1405
1433
|
} catch (err) {
|
|
1406
1434
|
logError(coordRoot, err, { phase: "user-prompt-submit-presence" });
|
|
1407
1435
|
}
|
|
@@ -1472,6 +1500,7 @@ async function handleStop(run: HookRun): Promise<number | undefined> {
|
|
|
1472
1500
|
// its daemon intentionally exits when a machine has no live sessions.
|
|
1473
1501
|
try {
|
|
1474
1502
|
publishPresence(coordRoot);
|
|
1503
|
+
publishMail(coordRoot);
|
|
1475
1504
|
ensureRelayDaemon(coordRoot);
|
|
1476
1505
|
} catch (err) {
|
|
1477
1506
|
logError(coordRoot, err, { phase: "stop-presence" });
|
|
@@ -1609,6 +1638,17 @@ async function handleToolRequested(run: HookRun): Promise<number | undefined> {
|
|
|
1609
1638
|
return 0;
|
|
1610
1639
|
}
|
|
1611
1640
|
|
|
1641
|
+
const commitTreeReason = shellCommitTreeReason(
|
|
1642
|
+
payload?.tool_name,
|
|
1643
|
+
payload?.tool_input,
|
|
1644
|
+
resolveBinName(coordRoot),
|
|
1645
|
+
);
|
|
1646
|
+
if (commitTreeReason) {
|
|
1647
|
+
const { emitDeny } = await import("./adapter/output.ts");
|
|
1648
|
+
emitDeny(adapter, commitTreeReason);
|
|
1649
|
+
return 0;
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1612
1652
|
// G-guard for ALL adapters. Claude Code previously ran this via a
|
|
1613
1653
|
// pre-tool-use bash adapter (which called `agent-coord verdict --rule=claim`);
|
|
1614
1654
|
// that adapter is now deleted, so agent-hook owns the deny for every adapter.
|
|
@@ -1624,9 +1664,41 @@ async function handleToolRequested(run: HookRun): Promise<number | undefined> {
|
|
|
1624
1664
|
logError(coordRoot, err, { phase: "pre-tool-use-guard" });
|
|
1625
1665
|
}
|
|
1626
1666
|
if (!guardAllowed) return 0;
|
|
1667
|
+
|
|
1668
|
+
// Heavy shell checks queue for a machine-wide admission slot when the
|
|
1669
|
+
// project configures one. Fails open: any error runs the command as written.
|
|
1670
|
+
try {
|
|
1671
|
+
if (await emitAdmissionRewrite(coordRoot, adapter, payload)) return 0;
|
|
1672
|
+
} catch (err) {
|
|
1673
|
+
logError(coordRoot, err, { phase: "pre-tool-use-admission" });
|
|
1674
|
+
}
|
|
1627
1675
|
return undefined;
|
|
1628
1676
|
}
|
|
1629
1677
|
|
|
1678
|
+
async function emitAdmissionRewrite(
|
|
1679
|
+
coordRoot: string,
|
|
1680
|
+
adapter: Adapter,
|
|
1681
|
+
payload: HookRun["payload"],
|
|
1682
|
+
): Promise<boolean> {
|
|
1683
|
+
const resource = readAdmissionConfig(coordRoot)?.guard?.resource;
|
|
1684
|
+
if (!resource) return false;
|
|
1685
|
+
const { admissionHookOutput } = await import("./shell-admission.ts");
|
|
1686
|
+
const output = admissionHookOutput({
|
|
1687
|
+
adapter,
|
|
1688
|
+
resource,
|
|
1689
|
+
bin: resolveBinName(coordRoot),
|
|
1690
|
+
projectRoot: coordRoot,
|
|
1691
|
+
toolName: payload?.tool_name,
|
|
1692
|
+
toolInput: payload?.tool_input,
|
|
1693
|
+
cwd: payload?.cwd,
|
|
1694
|
+
skip: coordEnv("ADMISSION_SKIP") === "1" || Boolean(coordEnv("ADMISSION_HELD")),
|
|
1695
|
+
platform: process.platform,
|
|
1696
|
+
});
|
|
1697
|
+
if (!output) return false;
|
|
1698
|
+
process.stdout.write(`${JSON.stringify(output)}\n`);
|
|
1699
|
+
return true;
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1630
1702
|
async function handlePostToolUse(run: HookRun): Promise<void> {
|
|
1631
1703
|
const { coordRoot, adapter, eventName, payload, owner } = run;
|
|
1632
1704
|
// Inject the full instruction only at the successful tool boundary that
|
|
@@ -1646,9 +1718,16 @@ async function handlePostToolUse(run: HookRun): Promise<void> {
|
|
|
1646
1718
|
// retry response already carries the full instruction and an end-turn
|
|
1647
1719
|
// status is the wrong place to ask.
|
|
1648
1720
|
try {
|
|
1649
|
-
const row = readLiveCoordinationRow(coordRoot, owner.instance_id);
|
|
1650
1721
|
// A workflow child has no user-visible title to set.
|
|
1651
|
-
|
|
1722
|
+
if (coordEnv("WORKFLOW_CHILD") === "1") return;
|
|
1723
|
+
// The live row copies every name-display field from a current heartbeat
|
|
1724
|
+
// cache and leaves them unset otherwise, so a raw cache with nothing
|
|
1725
|
+
// pending proves the row has nothing pending either. That skips a full
|
|
1726
|
+
// ledger read on almost every tool call; only a pending title pays for
|
|
1727
|
+
// the authoritative check below.
|
|
1728
|
+
if (!sessionNameDisplayPending(readHeartbeat(coordRoot, owner.instance_id))) return;
|
|
1729
|
+
const row = readLiveCoordinationRow(coordRoot, owner.instance_id);
|
|
1730
|
+
const name = sessionNameDisplayPending(row);
|
|
1652
1731
|
if (name && toolResponseMintedSessionName(payload?.tool_response, name)) {
|
|
1653
1732
|
// Record the title before asking for it. The suggestion can still
|
|
1654
1733
|
// change afterwards (an assigned-name rewrite, a lifecycle re-mint, a
|
|
@@ -144,29 +144,6 @@ function readPpid(pid: number): number | null {
|
|
|
144
144
|
return null;
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
/**
|
|
148
|
-
* Find the parent owner for a subagent invocation. The per-shell marker at
|
|
149
|
-
* `.harnery/shells/<pid>` is set by `sub-agent-start` and removed by
|
|
150
|
-
* `sub-agent-stop`.
|
|
151
|
-
*
|
|
152
|
-
* Phase 2 stub: the marker file isn't written yet, so we return null in most
|
|
153
|
-
* cases. Phase 2 callers can pass through.
|
|
154
|
-
*/
|
|
155
|
-
export function readShellMarker(coordRoot: string, pid: number): string | null {
|
|
156
|
-
const path = join(coordRoot, ".harnery", "shells", String(pid));
|
|
157
|
-
if (!existsSync(path)) return null;
|
|
158
|
-
try {
|
|
159
|
-
return readFileSync(path, "utf8").trim() || null;
|
|
160
|
-
} catch {
|
|
161
|
-
return null;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/** Read the V3-projected `agent_id` for an owner if it exists. */
|
|
166
|
-
export function readAgentIdForOwner(coordRoot: string, instanceId: string): string | null {
|
|
167
|
-
return readLiveCoordinationRow(coordRoot, instanceId)?.agent_id ?? null;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
147
|
/** Diagnostic: list of pid-map entries (for debugging). */
|
|
171
148
|
export function listPidmap(coordRoot: string): Array<{ pid: number; owner: string }> {
|
|
172
149
|
const dir = join(coordRoot, ".harnery", "pid-map");
|