harnery 0.9.0 → 0.11.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 +1 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +6 -0
- package/dist/commands/agents.d.ts +24 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +244 -13
- package/dist/commands/backup.d.ts +5 -4
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +15 -14
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +27 -1
- package/dist/commands/claude-desktop.d.ts +19 -0
- package/dist/commands/claude-desktop.d.ts.map +1 -0
- package/dist/commands/claude-desktop.js +168 -0
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +149 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +2 -3
- package/dist/commands/eml.d.ts +32 -0
- package/dist/commands/eml.d.ts.map +1 -1
- package/dist/commands/eml.js +16 -3
- package/dist/commands/harness.d.ts +11 -0
- package/dist/commands/harness.d.ts.map +1 -0
- package/dist/commands/harness.js +118 -0
- package/dist/commands/presence.d.ts +9 -4
- package/dist/commands/presence.d.ts.map +1 -1
- package/dist/commands/presence.js +88 -5
- package/dist/commands/relay.d.ts +9 -0
- package/dist/commands/relay.d.ts.map +1 -0
- package/dist/commands/relay.js +143 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +5 -0
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +53 -17
- package/dist/core/agents/canonical-emit.d.ts +15 -0
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +41 -3
- package/dist/core/agents/cli.js +4 -2
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +44 -3
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +26 -1
- package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/commit-conflict.js +4 -37
- package/dist/core/agents/session-events.d.ts.map +1 -1
- package/dist/core/agents/session-events.js +16 -43
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-projector.js +11 -2
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +3 -3
- package/dist/core/agents/state/names.d.ts +38 -3
- package/dist/core/agents/state/names.d.ts.map +1 -1
- package/dist/core/agents/state/names.js +46 -7
- package/dist/core/agents/state/pidmap.d.ts +5 -1
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +58 -2
- package/dist/core/agents/state/stale-sweep.d.ts +3 -2
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +6 -6
- package/dist/core/config.d.ts +68 -11
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +191 -25
- package/dist/core/context/index.d.ts +144 -0
- package/dist/core/context/index.d.ts.map +1 -0
- package/dist/core/context/index.js +380 -0
- package/dist/core/harnesses/bench.d.ts +29 -0
- package/dist/core/harnesses/bench.d.ts.map +1 -0
- package/dist/core/harnesses/bench.js +151 -0
- package/dist/core/harnesses/index.d.ts +9 -0
- package/dist/core/harnesses/index.d.ts.map +1 -0
- package/dist/core/harnesses/index.js +4 -0
- package/dist/core/harnesses/profiles.d.ts +62 -0
- package/dist/core/harnesses/profiles.d.ts.map +1 -0
- package/dist/core/harnesses/profiles.js +115 -0
- package/dist/core/harnesses/registry.d.ts +15 -0
- package/dist/core/harnesses/registry.d.ts.map +1 -0
- package/dist/core/harnesses/registry.js +113 -0
- package/dist/core/harnesses/types.d.ts +64 -0
- package/dist/core/harnesses/types.d.ts.map +1 -0
- package/dist/core/harnesses/types.js +20 -0
- package/dist/core/hooks/cli.js +280 -63
- package/dist/core/hooks/events/schema.d.ts +38 -1
- package/dist/core/hooks/events/schema.d.ts.map +1 -1
- package/dist/core/hooks/harness/events.d.ts.map +1 -1
- package/dist/core/hooks/harness/events.js +3 -0
- package/dist/core/hooks/harness/parse.d.ts +1 -1
- package/dist/core/hooks/harness/parse.d.ts.map +1 -1
- package/dist/core/hooks/harness/parse.js +4 -0
- package/dist/core/presence/blob.d.ts +40 -0
- package/dist/core/presence/blob.d.ts.map +1 -0
- package/dist/core/presence/blob.js +91 -0
- package/dist/core/presence/git.d.ts +68 -0
- package/dist/core/presence/git.d.ts.map +1 -0
- package/dist/core/presence/git.js +173 -0
- package/dist/core/presence/index.d.ts +74 -0
- package/dist/core/presence/index.d.ts.map +1 -0
- package/dist/core/presence/index.js +224 -0
- package/dist/core/presence/relay-client.d.ts +39 -0
- package/dist/core/presence/relay-client.d.ts.map +1 -0
- package/dist/core/presence/relay-client.js +295 -0
- package/dist/core/presence/relay-protocol.d.ts +94 -0
- package/dist/core/presence/relay-protocol.d.ts.map +1 -0
- package/dist/core/presence/relay-protocol.js +169 -0
- package/dist/core/workflow/billing.d.ts +1 -1
- package/dist/core/workflow/billing.d.ts.map +1 -1
- package/dist/core/workflow/billing.js +7 -1
- package/dist/core/workflow/engine.d.ts +5 -0
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +164 -16
- package/dist/core/workflow/harnesses.d.ts +3 -3
- package/dist/core/workflow/harnesses.d.ts.map +1 -1
- package/dist/core/workflow/harnesses.js +8 -17
- package/dist/core/workflow/index.d.ts +5 -0
- package/dist/core/workflow/index.d.ts.map +1 -0
- package/dist/core/workflow/index.js +3 -0
- package/dist/core/workflow/proof.d.ts +42 -0
- package/dist/core/workflow/proof.d.ts.map +1 -0
- package/dist/core/workflow/proof.js +342 -0
- package/dist/core/workflow/spawn-claude.d.ts +4 -1
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +39 -18
- package/dist/core/workflow/spawn-codex.d.ts +4 -1
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +44 -22
- package/dist/core/workflow/spawn-cursor.d.ts +4 -1
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +35 -16
- package/dist/core/workflow/types.d.ts +156 -2
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/types.js +1 -1
- package/dist/lib/browser/client.d.ts +16 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +22 -0
- package/dist/lib/browser/index.d.ts +1 -0
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +1 -0
- package/dist/lib/browser/launch-args.d.ts +21 -0
- package/dist/lib/browser/launch-args.d.ts.map +1 -0
- package/dist/lib/browser/launch-args.js +32 -0
- package/dist/lib/claude-desktop.d.ts +105 -0
- package/dist/lib/claude-desktop.d.ts.map +1 -0
- package/dist/lib/claude-desktop.js +217 -0
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +5 -2
- package/dist/lib/identities/assume.d.ts +51 -0
- package/dist/lib/identities/assume.d.ts.map +1 -0
- package/dist/lib/identities/assume.js +274 -0
- package/dist/lib/identities/index.d.ts +7 -7
- package/dist/lib/identities/index.d.ts.map +1 -1
- package/dist/lib/identities/index.js +24 -24
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +15 -2
- package/package.json +22 -6
- package/schemas/config.schema.json +88 -19
- package/src/commander.ts +6 -0
- package/src/commands/agents.ts +280 -14
- package/src/commands/backup.ts +17 -16
- package/src/commands/browse.ts +32 -0
- package/src/commands/claude-desktop.ts +215 -0
- package/src/commands/context.ts +174 -1
- package/src/commands/doctor.ts +2 -3
- package/src/commands/eml.ts +22 -4
- package/src/commands/harness.ts +147 -0
- package/src/commands/presence.ts +111 -5
- package/src/commands/relay.ts +160 -0
- package/src/commands/sync.ts +4 -0
- package/src/commands/workflow.ts +62 -19
- package/src/core/agents/canonical-emit.ts +41 -3
- package/src/core/agents/cli.ts +4 -2
- package/src/core/agents/render/prompt-context.ts +48 -3
- package/src/core/agents/render/session-context.ts +27 -1
- package/src/core/agents/rules/commit-conflict.ts +4 -33
- package/src/core/agents/session-events.ts +15 -39
- package/src/core/agents/state/heartbeat-projector.ts +10 -1
- package/src/core/agents/state/heartbeat-writer.ts +3 -2
- package/src/core/agents/state/names.ts +68 -9
- package/src/core/agents/state/pidmap.ts +59 -2
- package/src/core/agents/state/stale-sweep.ts +6 -11
- package/src/core/config.ts +248 -24
- package/src/core/context/index.ts +575 -0
- package/src/core/harnesses/bench.ts +214 -0
- package/src/core/harnesses/index.ts +31 -0
- package/src/core/harnesses/profiles.ts +141 -0
- package/src/core/harnesses/registry.ts +140 -0
- package/src/core/harnesses/types.ts +90 -0
- package/src/core/hooks/cli.ts +331 -62
- package/src/core/hooks/events/schema.ts +68 -0
- package/src/core/hooks/harness/events.ts +3 -0
- package/src/core/hooks/harness/parse.ts +7 -1
- package/src/core/presence/blob.ts +125 -0
- package/src/core/presence/git.ts +191 -0
- package/src/core/presence/index.ts +274 -0
- package/src/core/presence/relay-client.ts +322 -0
- package/src/core/presence/relay-protocol.ts +246 -0
- package/src/core/workflow/billing.ts +8 -2
- package/src/core/workflow/engine.ts +182 -16
- package/src/core/workflow/harnesses.ts +14 -19
- package/src/core/workflow/index.ts +43 -0
- package/src/core/workflow/proof.ts +434 -0
- package/src/core/workflow/spawn-claude.ts +39 -20
- package/src/core/workflow/spawn-codex.ts +47 -28
- package/src/core/workflow/spawn-cursor.ts +37 -18
- package/src/core/workflow/types.ts +176 -2
- package/src/lib/browser/client.ts +37 -0
- package/src/lib/browser/index.ts +1 -0
- package/src/lib/browser/launch-args.ts +33 -0
- package/src/lib/claude-desktop.ts +293 -0
- package/src/lib/docs-lint.ts +5 -2
- package/src/lib/identities/assume.ts +363 -0
- package/src/lib/identities/index.ts +28 -24
- package/src/lib/instructions/templates.ts +16 -2
|
@@ -11,8 +11,38 @@
|
|
|
11
11
|
import { spawnSync } from "node:child_process";
|
|
12
12
|
import { existsSync } from "node:fs";
|
|
13
13
|
import { dirname, join, resolve } from "node:path";
|
|
14
|
+
import { monorepoRoot } from "./coord-client.js";
|
|
15
|
+
/**
|
|
16
|
+
* Resolve the coord root for a canonical emit.
|
|
17
|
+
*
|
|
18
|
+
* Order: explicit `HARNERY_COORD_ROOT_OVERRIDE` → git-superproject-aware
|
|
19
|
+
* `monorepoRoot()` (must actually carry `.harnery/`) → cwd walk (non-git
|
|
20
|
+
* hosts). The git-aware step is load-bearing: a shell cd'd into a nested
|
|
21
|
+
* directory that carries its own `.harnery/` (e.g. an embedded harnery
|
|
22
|
+
* checkout) made the old cwd-walk resolve the NESTED root, from which
|
|
23
|
+
* `<root>/harnery/bin/agent-coord` doesn't exist — so `agents status` /
|
|
24
|
+
* `set-task` emits silently vanished and the Stop-hook's rule 1/3
|
|
25
|
+
* (`state.status_checked` in-turn) blocked turns that had done the ritual.
|
|
26
|
+
* Same bug class as the coordHelperOpts root-pin fix; this closes the
|
|
27
|
+
* emitCanonical instance of it.
|
|
28
|
+
*/
|
|
29
|
+
export function resolveEmitRoot(start = process.cwd()) {
|
|
30
|
+
const override = process.env.HARNERY_COORD_ROOT_OVERRIDE;
|
|
31
|
+
if (override)
|
|
32
|
+
return override;
|
|
33
|
+
// Memoized per start-dir: the session-tee middleware resolves once per
|
|
34
|
+
// emitted event, and monorepoRoot() spawns git — cache so a streaming
|
|
35
|
+
// command doesn't pay 1-3 subprocess spawns per output line.
|
|
36
|
+
if (cachedRoot && cachedRoot.start === start)
|
|
37
|
+
return cachedRoot.root;
|
|
38
|
+
const gitRoot = monorepoRoot();
|
|
39
|
+
const root = gitRoot && existsSync(join(gitRoot, ".harnery")) ? gitRoot : findRepoRoot(start);
|
|
40
|
+
cachedRoot = { start, root };
|
|
41
|
+
return root;
|
|
42
|
+
}
|
|
43
|
+
let cachedRoot;
|
|
14
44
|
export function emitCanonical(input) {
|
|
15
|
-
const root =
|
|
45
|
+
const root = resolveEmitRoot();
|
|
16
46
|
if (!root)
|
|
17
47
|
return;
|
|
18
48
|
const binary = resolve(root, "harnery", "bin", "agent-coord");
|
|
@@ -38,11 +68,19 @@ export function emitCanonical(input) {
|
|
|
38
68
|
args.push("--parent-session-id", input.parentSessionId);
|
|
39
69
|
if (input.parentTurnId)
|
|
40
70
|
args.push("--parent-turn-id", input.parentTurnId);
|
|
41
|
-
|
|
71
|
+
// Pin cwd + HARNERY_COORD_ROOT_OVERRIDE so the child writes to the SAME
|
|
72
|
+
// stream we resolved, regardless of where the caller's shell is cd'd.
|
|
73
|
+
const result = spawnSync(binary, args, {
|
|
42
74
|
encoding: "utf8",
|
|
43
75
|
stdio: "ignore",
|
|
44
76
|
timeout: 3000,
|
|
77
|
+
cwd: root,
|
|
78
|
+
env: { ...process.env, HARNERY_COORD_ROOT_OVERRIDE: root },
|
|
45
79
|
});
|
|
80
|
+
if (result.error || result.status !== 0) {
|
|
81
|
+
const why = result.error ? result.error.message : `exit ${result.status}`;
|
|
82
|
+
process.stderr.write(`emitCanonical: ${input.type} emit failed (${why})\n`); // lint-ok-emission: soft-fail diagnostic promised by the module doc; silent drops cost blocked turns
|
|
83
|
+
}
|
|
46
84
|
}
|
|
47
85
|
catch {
|
|
48
86
|
/* never break the caller */
|
|
@@ -59,7 +97,7 @@ export function normalizeHarness(platform) {
|
|
|
59
97
|
return "codex";
|
|
60
98
|
return "claude-code";
|
|
61
99
|
}
|
|
62
|
-
function findRepoRoot(start
|
|
100
|
+
function findRepoRoot(start) {
|
|
63
101
|
let dir = resolve(start);
|
|
64
102
|
while (true) {
|
|
65
103
|
if (existsSync(join(dir, ".harnery")))
|
package/dist/core/agents/cli.js
CHANGED
|
@@ -188,7 +188,9 @@ async function handleStateAction(root, action, rest) {
|
|
|
188
188
|
const task = args.join(" ");
|
|
189
189
|
const hb = writer.setTask(root, owner, task);
|
|
190
190
|
if (!hb) {
|
|
191
|
-
|
|
191
|
+
// Name the RESOLVED root: when a nested .harnery/ shadows the real
|
|
192
|
+
// coordination home, the full path is what makes that diagnosable.
|
|
193
|
+
process.stderr.write(`agent-coord set-task: no heartbeat at ${root}/.harnery/active/${owner}.json\n`);
|
|
192
194
|
return 1;
|
|
193
195
|
}
|
|
194
196
|
process.stdout.write(`${JSON.stringify({ instance_id: owner, task: hb.task ?? null, cleared: !task })}\n`);
|
|
@@ -664,7 +666,7 @@ async function handleResolveName(root, rest) {
|
|
|
664
666
|
process.stdout.write(`${JSON.stringify({ instance_id: owner, name: null, kind: null })}\n`);
|
|
665
667
|
return 0;
|
|
666
668
|
}
|
|
667
|
-
process.stdout.write(`${JSON.stringify({ instance_id: owner, name: resolved.name, kind: resolved.kind })}\n`);
|
|
669
|
+
process.stdout.write(`${JSON.stringify({ instance_id: owner, name: resolved.name, kind: resolved.kind, agent_id: resolved.agent_id ?? null })}\n`);
|
|
668
670
|
return 0;
|
|
669
671
|
}
|
|
670
672
|
async function handleEmitEvent(root, rest) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/prompt-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"prompt-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/prompt-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAiCH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;iDAC6C;IAC7C,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAqBnE"}
|
|
@@ -26,6 +26,7 @@ import { createHash } from "node:crypto";
|
|
|
26
26
|
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
27
27
|
import { join } from "node:path";
|
|
28
28
|
import { coordEnv } from "../../../lib/env.js";
|
|
29
|
+
import { readRemoteMachines } from "../../presence/index.js";
|
|
29
30
|
import { formatPendingCouncils } from "./session-context.js";
|
|
30
31
|
/**
|
|
31
32
|
* Build the combined UserPromptSubmit additionalContext string. Returns "" when
|
|
@@ -132,7 +133,10 @@ function computePeerTableIfChanged(coordRoot, selfInstanceId, selfSessionIdFallb
|
|
|
132
133
|
continue;
|
|
133
134
|
peers.push(hb);
|
|
134
135
|
}
|
|
135
|
-
|
|
136
|
+
// Cross-machine presence (ADR 0016): sessions on other machines, from the
|
|
137
|
+
// locally-fetched presence refs. Advisory (no claim blocking in v1).
|
|
138
|
+
const remote = readRemoteMachinesSafe(coordRoot);
|
|
139
|
+
if (peers.length === 0 && remote.length === 0)
|
|
136
140
|
return "";
|
|
137
141
|
// Build hash basis: sorted-by-instance_id projection of semantically-relevant fields.
|
|
138
142
|
const basis = peers
|
|
@@ -146,19 +150,56 @@ function computePeerTableIfChanged(coordRoot, selfInstanceId, selfSessionIdFallb
|
|
|
146
150
|
platform: p.platform ?? null,
|
|
147
151
|
}))
|
|
148
152
|
.sort((a, b) => (a.instance_id ?? "").localeCompare(b.instance_id ?? ""));
|
|
149
|
-
|
|
153
|
+
// Remote basis: machine + per-agent identity/task/files (published_at is
|
|
154
|
+
// excluded — a keepalive re-publish alone shouldn't re-emit the table).
|
|
155
|
+
const remoteBasis = remote.map((m) => ({
|
|
156
|
+
machine: m.machine,
|
|
157
|
+
agents: m.agents.map((a) => ({
|
|
158
|
+
instance_id: a.instance_id,
|
|
159
|
+
name: a.name ?? null,
|
|
160
|
+
task: a.task ?? null,
|
|
161
|
+
files_touched: Array.from(a.files_touched ?? []).sort(),
|
|
162
|
+
})),
|
|
163
|
+
}));
|
|
164
|
+
const newHash = sha256Hex16(JSON.stringify({ local: basis, remote: remoteBasis }));
|
|
150
165
|
const hashFile = join(coordRoot, ".harnery", `.last-peer-hash.${selfInstanceId}`);
|
|
151
166
|
const oldHash = safeRead(hashFile);
|
|
152
167
|
if (oldHash && oldHash === newHash)
|
|
153
168
|
return "";
|
|
154
169
|
// Render peer table via the same formatter used at SessionStart.
|
|
155
|
-
const
|
|
170
|
+
const local = formatPeerTable(peers, mySessionId);
|
|
171
|
+
const remoteTable = formatRemoteTable(remote);
|
|
172
|
+
const table = [local, remoteTable].filter(Boolean).join("\n\n");
|
|
156
173
|
if (!table)
|
|
157
174
|
return "";
|
|
158
175
|
// Persist hash atomically (temp + rename, same convention as other coord writes).
|
|
159
176
|
writeHashFile(hashFile, newHash);
|
|
160
177
|
return table;
|
|
161
178
|
}
|
|
179
|
+
/** readRemoteMachines with a local failure guard (rendering must never throw). */
|
|
180
|
+
function readRemoteMachinesSafe(coordRoot) {
|
|
181
|
+
try {
|
|
182
|
+
return readRemoteMachines(coordRoot);
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/** Render sessions on other machines (presence transport) as a subtable. */
|
|
189
|
+
function formatRemoteTable(remote) {
|
|
190
|
+
if (remote.length === 0)
|
|
191
|
+
return "";
|
|
192
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
193
|
+
const lines = [];
|
|
194
|
+
for (const m of remote) {
|
|
195
|
+
for (const a of m.agents.slice(0, 10)) {
|
|
196
|
+
lines.push(formatRow({ ...a, display_files: Array.from(a.files_touched ?? []).sort() }, nowSec).replace(/^ {2}- (agent-[^ ]+)/, ` - $1 @${m.machine}`));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (lines.length === 0)
|
|
200
|
+
return "";
|
|
201
|
+
return `Sessions on other machines (advisory, via presence refs):\n${lines.join("\n")}`;
|
|
202
|
+
}
|
|
162
203
|
function computeCouncilPendingIfChanged(coordRoot, selfInstanceId, agentName) {
|
|
163
204
|
const councilsDir = join(coordRoot, ".harnery", "councils");
|
|
164
205
|
if (!existsSync(councilsDir))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/session-context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAyC,QAAQ,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"session-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/session-context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAyC,QAAQ,EAAE,MAAM,SAAS,CAAC;AAqB1E,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;+BAC2B;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAiE7D;AA+ID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAwBvD;AAwDD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAiDlF;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,iBAAW,CAAC"}
|
|
@@ -11,6 +11,7 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { resolveBinName, resolveHooksSetupHint } from "../../config.js";
|
|
13
13
|
import { harneryVersion, loadHarnessWiring } from "../../hooks/harness/wiring.js";
|
|
14
|
+
import { readRemoteMachines } from "../../presence/index.js";
|
|
14
15
|
/**
|
|
15
16
|
* Build the combined SessionStart systemMessage. Returns the additionalContext
|
|
16
17
|
* string (or "" if there's nothing to say).
|
|
@@ -20,7 +21,9 @@ export function renderSessionContext(opts) {
|
|
|
20
21
|
const messages = [];
|
|
21
22
|
// 1. Self-name line + peer table (folded if peers present)
|
|
22
23
|
const peers = readActivePeers(coordRoot, instanceId);
|
|
23
|
-
const
|
|
24
|
+
const localTable = formatPeerTable(peers, sessionId);
|
|
25
|
+
// Cross-machine presence (ADR 0016): sessions on other machines (advisory).
|
|
26
|
+
const peerTable = [localTable, formatRemoteMachines(coordRoot)].filter(Boolean).join("\n\n");
|
|
24
27
|
if (agentName) {
|
|
25
28
|
const suffix = platformLabel ? ` (${platformLabel})` : "";
|
|
26
29
|
const selfLine = `You are agent-${agentName}${suffix}.`;
|
|
@@ -72,6 +75,28 @@ export function renderSessionContext(opts) {
|
|
|
72
75
|
}
|
|
73
76
|
return messages.join("\n\n");
|
|
74
77
|
}
|
|
78
|
+
/** One-line-per-agent view of sessions on other machines (presence refs). */
|
|
79
|
+
function formatRemoteMachines(coordRoot) {
|
|
80
|
+
try {
|
|
81
|
+
const remote = readRemoteMachines(coordRoot);
|
|
82
|
+
if (remote.length === 0)
|
|
83
|
+
return "";
|
|
84
|
+
const lines = [];
|
|
85
|
+
for (const m of remote) {
|
|
86
|
+
for (const a of m.agents.slice(0, 10)) {
|
|
87
|
+
const task = a.task ? ` "${a.task.slice(0, 60)}"` : "";
|
|
88
|
+
const files = a.files_touched?.length
|
|
89
|
+
? `holds: ${a.files_touched.slice(0, 3).join(", ")}${a.files_touched.length > 3 ? `, +${a.files_touched.length - 3} more` : ""}`
|
|
90
|
+
: "nothing held";
|
|
91
|
+
lines.push(` - agent-${a.name ?? a.instance_id.slice(0, 8)} @${m.machine}${task} (${files})`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return `Sessions on other machines (advisory, via presence refs):\n${lines.join("\n")}`;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return "";
|
|
98
|
+
}
|
|
99
|
+
}
|
|
75
100
|
/** Read all peer heartbeats from .harnery/active/, excluding self. */
|
|
76
101
|
function readActivePeers(coordRoot, selfInstanceId) {
|
|
77
102
|
const out = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commit-conflict.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/rules/commit-conflict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;
|
|
1
|
+
{"version":3,"file":"commit-conflict.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/rules/commit-conflict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAgBH,UAAU,QAAQ;IAChB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,0BAA0B;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB;wEACoE;IACpE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,qFAAqF;IACrF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,8DAA8D;IAC9D,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,GAAG,mBAAmB,CAsGhG"}
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
import { spawnSync } from "node:child_process";
|
|
23
23
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
24
24
|
import { join } from "node:path";
|
|
25
|
-
import {
|
|
26
|
-
|
|
25
|
+
import { coordFreshnessSeconds } from "../../config.js";
|
|
26
|
+
import { instanceHasLivePid } from "../state/pidmap.js";
|
|
27
27
|
export function evaluateCommit(coordRoot, req) {
|
|
28
28
|
if (req.staged_paths.length === 0) {
|
|
29
29
|
return {
|
|
@@ -36,7 +36,7 @@ export function evaluateCommit(coordRoot, req) {
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
const peers = readActivePeers(coordRoot);
|
|
39
|
-
const cutoffMs = Date.now() -
|
|
39
|
+
const cutoffMs = Date.now() - coordFreshnessSeconds(coordRoot) * 1000;
|
|
40
40
|
const stagedSet = new Set(req.staged_paths);
|
|
41
41
|
const gitlinkSet = new Set(req.staged_gitlinks ?? []);
|
|
42
42
|
const conflicts = [];
|
|
@@ -166,33 +166,7 @@ function isHolderSelfAttributed(coordRoot, holderId, stagedSet, peers) {
|
|
|
166
166
|
return true;
|
|
167
167
|
}
|
|
168
168
|
function holderHasLiveForeignPid(coordRoot, holderId) {
|
|
169
|
-
|
|
170
|
-
if (!existsSync(dir))
|
|
171
|
-
return false;
|
|
172
|
-
for (const f of readdirSync(dir)) {
|
|
173
|
-
let row = "";
|
|
174
|
-
try {
|
|
175
|
-
row = readFileSync(join(dir, f), "utf8").trim();
|
|
176
|
-
}
|
|
177
|
-
catch {
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
const owner = row.split("\t")[0]?.trim() ?? "";
|
|
181
|
-
if (owner !== holderId)
|
|
182
|
-
continue;
|
|
183
|
-
// Process still alive?
|
|
184
|
-
const pid = Number.parseInt(f, 10);
|
|
185
|
-
if (!Number.isFinite(pid))
|
|
186
|
-
continue;
|
|
187
|
-
try {
|
|
188
|
-
process.kill(pid, 0); // signal 0 = liveness probe
|
|
189
|
-
return true; // live foreign pid found
|
|
190
|
-
}
|
|
191
|
-
catch {
|
|
192
|
-
// ESRCH (no such process): pid-map entry is stale, skip
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
return false;
|
|
169
|
+
return instanceHasLivePid(coordRoot, holderId);
|
|
196
170
|
}
|
|
197
171
|
function isPathCleanInHead(coordRoot, relPath) {
|
|
198
172
|
// Path is tracked + diff-clean against HEAD = "already committed, holder
|
|
@@ -230,13 +204,6 @@ function readActivePeers(coordRoot) {
|
|
|
230
204
|
}
|
|
231
205
|
return out;
|
|
232
206
|
}
|
|
233
|
-
function freshnessSecs() {
|
|
234
|
-
const raw = coordEnv("AGENT_FRESHNESS");
|
|
235
|
-
if (!raw)
|
|
236
|
-
return DEFAULT_FRESHNESS_SECS;
|
|
237
|
-
const n = Number.parseInt(raw, 10);
|
|
238
|
-
return Number.isFinite(n) && n > 0 ? n : DEFAULT_FRESHNESS_SECS;
|
|
239
|
-
}
|
|
240
207
|
function shortName(peer) {
|
|
241
208
|
if (peer.name && peer.name.length > 0)
|
|
242
209
|
return `agent-${peer.name}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-events.d.ts","sourceRoot":"","sources":["../../../src/core/agents/session-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH;;iFAEiF;AACjF,MAAM,MAAM,gBAAgB,GACxB,eAAe,GACf,QAAQ,GACR,aAAa,GACb,aAAa,GACb,YAAY,GACZ,UAAU,GACV,YAAY,GACZ,cAAc,GACd,aAAa,GACb,WAAW,CAAC;AAEhB;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"session-events.d.ts","sourceRoot":"","sources":["../../../src/core/agents/session-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH;;iFAEiF;AACjF,MAAM,MAAM,gBAAgB,GACxB,eAAe,GACf,QAAQ,GACR,aAAa,GACb,aAAa,GACb,YAAY,GACZ,UAAU,GACV,YAAY,GACZ,cAAc,GACd,aAAa,GACb,WAAW,CAAC;AAEhB;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAa1C;AAED,6EAA6E;AAC7E,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiBjE;AAsGD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,gBAAgB,EACtB,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,IAAI,CAEN;AAED,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,SAAO,GAAG,MAAM,CAGxD"}
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
* middleware callers need no edits.
|
|
9
9
|
*/
|
|
10
10
|
import { randomBytes } from "node:crypto";
|
|
11
|
-
import { existsSync, readFileSync
|
|
11
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
12
12
|
import { dirname, resolve } from "node:path";
|
|
13
13
|
// Kept dependency-light: vendored verbatim into a downstream consumer, so no coordEnv import.
|
|
14
|
-
import { normalizeHarness } from "./canonical-emit.js";
|
|
14
|
+
import { normalizeHarness, resolveEmitRoot } from "./canonical-emit.js";
|
|
15
15
|
import { emit } from "./events/emit.js";
|
|
16
16
|
/**
|
|
17
17
|
* Resolved path of the ndjson sidecar file. Lives inside `.harnery/` so a
|
|
@@ -22,27 +22,15 @@ export function sessionEventsPath() {
|
|
|
22
22
|
const explicit = process.env.HARNERY_OUTPUT_SESSION_EVENTS;
|
|
23
23
|
if (explicit)
|
|
24
24
|
return explicit;
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
25
|
+
// Superproject-aware root resolution (git first, cwd walk fallback) via
|
|
26
|
+
// resolveEmitRoot. A plain cwd walk here mis-anchored to a NESTED
|
|
27
|
+
// `.harnery/` when the shell sat inside a submodule carrying its own
|
|
28
|
+
// (e.g. an embedded harnery checkout), silently splitting the event
|
|
29
|
+
// stream. Don't reintroduce CLAUDE.md / .git as anchors, which creates
|
|
29
30
|
// stray .harnery/ dirs under submodule subtrees.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
if (existsSync(candidate) && statSync(candidate).isDirectory()) {
|
|
35
|
-
return resolve(candidate, "session-events.ndjson");
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
/* keep walking */
|
|
40
|
-
}
|
|
41
|
-
const parent = dirname(dir);
|
|
42
|
-
if (parent === dir)
|
|
43
|
-
break;
|
|
44
|
-
dir = parent;
|
|
45
|
-
}
|
|
31
|
+
const root = resolveEmitRoot();
|
|
32
|
+
if (root)
|
|
33
|
+
return resolve(root, ".harnery", "session-events.ndjson");
|
|
46
34
|
return resolve(process.env.HOME || "/tmp", ".harnery", "session-events.ndjson");
|
|
47
35
|
}
|
|
48
36
|
/** Random 8-char hex id for grouping output lines under a single command. */
|
|
@@ -60,28 +48,13 @@ export function newCmdId() {
|
|
|
60
48
|
export function readLastIntent(instanceId) {
|
|
61
49
|
if (!instanceId)
|
|
62
50
|
return null;
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const candidate = resolve(dir, ".harnery");
|
|
69
|
-
try {
|
|
70
|
-
if (existsSync(candidate) && statSync(candidate).isDirectory()) {
|
|
71
|
-
agentsDir = candidate;
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
/* keep walking */
|
|
77
|
-
}
|
|
78
|
-
const parent = dirname(dir);
|
|
79
|
-
if (parent === dir)
|
|
80
|
-
break;
|
|
81
|
-
dir = parent;
|
|
82
|
-
}
|
|
83
|
-
if (!agentsDir)
|
|
51
|
+
// Same superproject-aware root resolution as sessionEventsPath(): the
|
|
52
|
+
// intent stamp is written by the PreToolUse hook into the SUPERPROJECT's
|
|
53
|
+
// .harnery/, so a nested-`.harnery/` cwd must not redirect the read.
|
|
54
|
+
const root = resolveEmitRoot();
|
|
55
|
+
if (!root)
|
|
84
56
|
return null;
|
|
57
|
+
const agentsDir = resolve(root, ".harnery");
|
|
85
58
|
const intentPath = resolve(agentsDir, `.last-intent.${instanceId}`);
|
|
86
59
|
if (!existsSync(intentPath))
|
|
87
60
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heartbeat-projector.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/heartbeat-projector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,yEAAyE;IACzE,aAAa,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,cAAc,EAAE,MAAM,CAAC;IACvB,oCAAoC;IACpC,OAAO,EAAE;QACP,cAAc,EAAE,CAAC,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,SAAS,cAAc,EAAE,GAChC;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;CAAE,CAoD9D;
|
|
1
|
+
{"version":3,"file":"heartbeat-projector.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/heartbeat-projector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,yEAAyE;IACzE,aAAa,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,cAAc,EAAE,MAAM,CAAC;IACvB,oCAAoC;IACpC,OAAO,EAAE;QACP,cAAc,EAAE,CAAC,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,SAAS,cAAc,EAAE,GAChC;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;CAAE,CAoD9D;AAyXD,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAE3E"}
|
|
@@ -92,8 +92,8 @@ function seed(ev, coordRoot) {
|
|
|
92
92
|
if (resolved) {
|
|
93
93
|
hb.name = resolved.name;
|
|
94
94
|
hb.kind = resolved.kind;
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
hb.agent_id =
|
|
96
|
+
resolved.agent_id ?? (resolved.kind === "subagent" ? ev.instance_id : undefined);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
catch {
|
|
@@ -239,6 +239,15 @@ function apply(hb, ev, coordRoot) {
|
|
|
239
239
|
case "state.status_checked":
|
|
240
240
|
hb.last_status_at = ev.ts;
|
|
241
241
|
break;
|
|
242
|
+
case "identity.assumed": {
|
|
243
|
+
const name = pickStr(d, "name");
|
|
244
|
+
const agentId = pickStr(d, "agent_id");
|
|
245
|
+
if (name)
|
|
246
|
+
hb.name = name;
|
|
247
|
+
if (agentId)
|
|
248
|
+
hb.agent_id = agentId;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
242
251
|
case "state.presence_change": {
|
|
243
252
|
const to = pickStr(d, "to");
|
|
244
253
|
if (to === "mobile" || to === "office")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heartbeat-writer.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/heartbeat-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA6DH,MAAM,WAAW,SAAS;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAiBD,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAQrF;AAeD,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAS7F;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAKxF;AAED,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,SAAS,GAAG,IAAI,CAMlB;AAED,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,SAAS,GAAG,IAAI,CAYlB;AAED,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,EAAE,CAsChG;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAS5E;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAuBnF;AAED,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,GACf,SAAS,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"heartbeat-writer.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/heartbeat-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA6DH,MAAM,WAAW,SAAS;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAiBD,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAQrF;AAeD,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAS7F;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAKxF;AAED,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,SAAS,GAAG,IAAI,CAMlB;AAED,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,SAAS,GAAG,IAAI,CAYlB;AAED,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,EAAE,CAsChG;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAS5E;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAuBnF;AAED,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,GACf,SAAS,GAAG,IAAI,CAsDlB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,SAAS,GAAG,IAAI,CAOlB"}
|
|
@@ -245,9 +245,9 @@ export function healHeartbeat(coordRoot, instanceId, sessionId, model, harness)
|
|
|
245
245
|
if (resolved) {
|
|
246
246
|
name = resolved.name;
|
|
247
247
|
kind = resolved.kind;
|
|
248
|
-
//
|
|
249
|
-
|
|
250
|
-
|
|
248
|
+
// An explicitly assumed session carries its durable persona UUID in
|
|
249
|
+
// name-history. Native subagents continue to use instance_id.
|
|
250
|
+
agentId = resolved.agent_id ?? (resolved.kind === "subagent" ? instanceId : "");
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
catch {
|
|
@@ -8,14 +8,30 @@
|
|
|
8
8
|
* Durable persistence: `.harnery/.name-history` (JSONL, one row per assignment)
|
|
9
9
|
* + `.harnery/.name-counter` (current counter, atomic temp+rename).
|
|
10
10
|
*
|
|
11
|
-
* Recreation rule
|
|
12
|
-
* 1. Own instance_id in name-history → (
|
|
13
|
-
* 2. session_id in name-history (owner != session) →
|
|
11
|
+
* Recreation rule:
|
|
12
|
+
* 1. Own instance_id in name-history → latest (name, kind, agent_id)
|
|
13
|
+
* 2. session_id in name-history (owner != session) → latest parent's
|
|
14
|
+
* (name, agent_id) with kind="transient"
|
|
15
|
+
*
|
|
16
|
+
* Multiple rows for one instance are intentional. `identity assume` appends a
|
|
17
|
+
* new binding instead of rewriting history; readers therefore resolve from the
|
|
18
|
+
* end of the file. Older one-row histories retain their original behavior.
|
|
14
19
|
* 3. Else: new assignment, consume a counter slot.
|
|
15
20
|
*/
|
|
16
21
|
/** All 260 names. */
|
|
17
22
|
export declare const COORD_NAMES: readonly ["Anna", "Bob", "Carlos", "Dalia", "Erika", "Fred", "Gemma", "Hank", "Imelda", "Jerry", "Kirk", "Lee", "Milton", "Nate", "Oscar", "Patty", "Quentin", "Rafael", "Sara", "Tony", "Ulrik", "Valerie", "Whitney", "Xander", "Yara", "Zara", "Adelaide", "Bertha", "Carmen", "Dorian", "Edna", "Francine", "Greta", "Helene", "Ines", "Joyce", "Karen", "Lorenzo", "Mindy", "Nadine", "Odette", "Paulette", "Quinn", "Rebekah", "Sean", "Tammy", "Ursula", "Vinson", "Wallace", "Xanthe", "Yusuf", "Zoe", "Alex", "Beatrice", "Cyrus", "Delia", "Ernesto", "Florence", "Gordon", "Hester", "Isaias", "Jenna", "Klaus", "Larry", "Maxine", "Nicholas", "Otto", "Peter", "Quill", "Renee", "Stella", "Theresa", "Umberto", "Virgil", "Willow", "Xavier", "Yolanda", "Zane", "Anita", "Bruno", "Cora", "Damon", "Edith", "Felix", "Gloria", "Holly", "Iris", "Jasper", "Kaia", "Linda", "Marco", "Nora", "Olive", "Petra", "Querida", "Roman", "Sebastian", "Tobias", "Una", "Vera", "Walter", "Xena", "Yusra", "Zelda", "Aaron", "Bonnie", "Caleb", "Daphne", "Elias", "Fiona", "Galileo", "Hugo", "Ian", "Jude", "Knox", "Leo", "Maya", "Nash", "Owen", "Paxton", "Quetzal", "Rosa", "Sage", "Talia", "Uri", "Vincent", "Wesley", "Ximena", "Yvette", "Zephyr", "Astrid", "Beau", "Celeste", "Dexter", "Ezra", "Fitz", "Genevieve", "Hadley", "Imogen", "Joaquin", "Kira", "Lila", "Mason", "Nila", "Olga", "Pearl", "Quincy", "Royce", "Sterling", "Trent", "Ulysses", "Violet", "Wynne", "Xerxes", "Yael", "Zinnia", "Amelia", "Bianca", "Cody", "Dahlia", "Esme", "Fern", "Greer", "Harriet", "Ivan", "Juno", "Kendrick", "Luther", "Margot", "Nigel", "Orion", "Phillip", "Quenby", "Reagan", "Sienna", "Tessa", "Uma", "Vesper", "Watson", "Xiomara", "Yuri", "Zoltan", "Andre", "Boris", "Cassidy", "Davis", "Evander", "Forrest", "Gibson", "Henry", "Irene", "Justus", "Kasper", "Lainey", "Miles", "Naomi", "Ophelia", "Phoebe", "Quark", "Rylie", "Saul", "Truman", "Ulrich", "Voss", "Winifred", "Xuxa", "Yvonne", "Zia", "Aria", "Brenda", "Crispin", "Dolores", "Estelle", "Felicity", "Goldie", "Hazel", "Ira", "Jovi", "Kestrel", "Lyric", "Mavis", "Nico", "Otis", "Percy", "Querubin", "Rhett", "Scout", "Tatum", "Unity", "Vivian", "Wyatt", "Xan", "Yancy", "Zev", "Atticus", "Barnaby", "Calliope", "Drake", "Eustace", "Foster", "Granger", "Hollis", "Indira", "Jericho", "Kaleb", "Logan", "Magnus", "Noor", "Oakley", "Posy", "Quito", "Rosalind", "Sloane", "Theron", "Upton", "Vance", "Wendell", "Xola", "Yann", "Zora"];
|
|
18
23
|
export type NameKind = "session" | "subagent" | "transient";
|
|
24
|
+
export interface NameHistoryRow {
|
|
25
|
+
instance_id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
kind: NameKind;
|
|
28
|
+
ts: string;
|
|
29
|
+
/** Durable persona UUID. Present after `agents identity assume`. */
|
|
30
|
+
agent_id?: string;
|
|
31
|
+
/** Audit marker distinguishing an explicit role adoption from pool assignment. */
|
|
32
|
+
source?: "pool" | "identity.assume";
|
|
33
|
+
previous_name?: string;
|
|
34
|
+
}
|
|
19
35
|
/**
|
|
20
36
|
* Resolve (name, kind) for an existing owner without consuming a counter
|
|
21
37
|
* slot. Returns null if no history match found.
|
|
@@ -26,7 +42,26 @@ export type NameKind = "session" | "subagent" | "transient";
|
|
|
26
42
|
export declare function resolveName(coordRoot: string, instanceId: string, sessionId?: string): {
|
|
27
43
|
name: string;
|
|
28
44
|
kind: NameKind;
|
|
45
|
+
agent_id?: string;
|
|
29
46
|
} | null;
|
|
47
|
+
export interface NameAssumptionResult {
|
|
48
|
+
changed: boolean;
|
|
49
|
+
previous: {
|
|
50
|
+
name: string;
|
|
51
|
+
kind: NameKind;
|
|
52
|
+
agent_id?: string;
|
|
53
|
+
} | null;
|
|
54
|
+
current: {
|
|
55
|
+
name: string;
|
|
56
|
+
kind: NameKind;
|
|
57
|
+
agent_id: string;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Append an explicit instance → durable-persona binding. Latest-row-wins makes
|
|
62
|
+
* this auditable and retry-safe without mutating prior assignment history.
|
|
63
|
+
*/
|
|
64
|
+
export declare function recordNameAssumption(coordRoot: string, instanceId: string, name: string, agentId: string, kind?: NameKind): NameAssumptionResult;
|
|
30
65
|
/**
|
|
31
66
|
* Assign a name to <instanceId> with the given <kind>. Counter-consuming when
|
|
32
67
|
* the owner is new. Idempotent: returns existing name on resume.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"names.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/names.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"names.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,qBAAqB;AACrB,eAAO,MAAM,WAAW,y5EA+Qd,CAAC;AAMX,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAsCD;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA4B5D;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACrE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,QAAoB,GACzB,oBAAoB,CAoBtB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CAsBxF"}
|
|
@@ -8,9 +8,14 @@
|
|
|
8
8
|
* Durable persistence: `.harnery/.name-history` (JSONL, one row per assignment)
|
|
9
9
|
* + `.harnery/.name-counter` (current counter, atomic temp+rename).
|
|
10
10
|
*
|
|
11
|
-
* Recreation rule
|
|
12
|
-
* 1. Own instance_id in name-history → (
|
|
13
|
-
* 2. session_id in name-history (owner != session) →
|
|
11
|
+
* Recreation rule:
|
|
12
|
+
* 1. Own instance_id in name-history → latest (name, kind, agent_id)
|
|
13
|
+
* 2. session_id in name-history (owner != session) → latest parent's
|
|
14
|
+
* (name, agent_id) with kind="transient"
|
|
15
|
+
*
|
|
16
|
+
* Multiple rows for one instance are intentional. `identity assume` appends a
|
|
17
|
+
* new binding instead of rewriting history; readers therefore resolve from the
|
|
18
|
+
* end of the file. Older one-row histories retain their original behavior.
|
|
14
19
|
* 3. Else: new assignment, consume a counter slot.
|
|
15
20
|
*/
|
|
16
21
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
@@ -333,20 +338,53 @@ function appendHistory(coordRoot, row) {
|
|
|
333
338
|
*/
|
|
334
339
|
export function resolveName(coordRoot, instanceId, sessionId) {
|
|
335
340
|
const history = readHistory(coordRoot);
|
|
336
|
-
for (
|
|
341
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
342
|
+
const row = history[i];
|
|
337
343
|
if (row.instance_id === instanceId) {
|
|
338
|
-
return {
|
|
344
|
+
return {
|
|
345
|
+
name: row.name,
|
|
346
|
+
kind: row.kind,
|
|
347
|
+
...(row.agent_id ? { agent_id: row.agent_id } : {}),
|
|
348
|
+
};
|
|
339
349
|
}
|
|
340
350
|
}
|
|
341
351
|
if (sessionId && sessionId !== instanceId) {
|
|
342
|
-
for (
|
|
352
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
353
|
+
const row = history[i];
|
|
343
354
|
if (row.instance_id === sessionId) {
|
|
344
|
-
return {
|
|
355
|
+
return {
|
|
356
|
+
name: row.name,
|
|
357
|
+
kind: "transient",
|
|
358
|
+
...(row.agent_id ? { agent_id: row.agent_id } : {}),
|
|
359
|
+
};
|
|
345
360
|
}
|
|
346
361
|
}
|
|
347
362
|
}
|
|
348
363
|
return null;
|
|
349
364
|
}
|
|
365
|
+
/**
|
|
366
|
+
* Append an explicit instance → durable-persona binding. Latest-row-wins makes
|
|
367
|
+
* this auditable and retry-safe without mutating prior assignment history.
|
|
368
|
+
*/
|
|
369
|
+
export function recordNameAssumption(coordRoot, instanceId, name, agentId, kind = "session") {
|
|
370
|
+
const previous = resolveName(coordRoot, instanceId);
|
|
371
|
+
const current = { name, kind, agent_id: agentId };
|
|
372
|
+
if (previous?.name === current.name &&
|
|
373
|
+
previous.kind === current.kind &&
|
|
374
|
+
previous.agent_id === current.agent_id) {
|
|
375
|
+
return { changed: false, previous, current };
|
|
376
|
+
}
|
|
377
|
+
appendHistory(coordRoot, {
|
|
378
|
+
instance_id: instanceId,
|
|
379
|
+
name,
|
|
380
|
+
kind,
|
|
381
|
+
agent_id: agentId,
|
|
382
|
+
source: "identity.assume",
|
|
383
|
+
previous_name: previous?.name,
|
|
384
|
+
ts: new Date().toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
385
|
+
});
|
|
386
|
+
return { changed: true, previous, current };
|
|
387
|
+
}
|
|
350
388
|
/**
|
|
351
389
|
* Assign a name to <instanceId> with the given <kind>. Counter-consuming when
|
|
352
390
|
* the owner is new. Idempotent: returns existing name on resume.
|
|
@@ -370,6 +408,7 @@ export function assignName(coordRoot, instanceId, kind) {
|
|
|
370
408
|
instance_id: instanceId,
|
|
371
409
|
name,
|
|
372
410
|
kind,
|
|
411
|
+
source: "pool",
|
|
373
412
|
ts: new Date().toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
374
413
|
});
|
|
375
414
|
return name;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pid-map writer.
|
|
2
|
+
* Pid-map writer + liveness helpers.
|
|
3
3
|
*
|
|
4
4
|
* Per-harness pid-map at `.harnery/pid-map/<pid>` containing
|
|
5
5
|
* `<instance_id>\t<platform>`. `harn agents whoami` walks ppid up 20 hops looking
|
|
@@ -8,4 +8,8 @@
|
|
|
8
8
|
* Atomic temp+rename. Idempotent: re-writing the same row is a no-op.
|
|
9
9
|
*/
|
|
10
10
|
export declare function writePidmapRow(coordRoot: string, pid: number, instanceId: string, platform: string): void;
|
|
11
|
+
/** True when any pid-map row for `instanceId` still belongs to a live process. */
|
|
12
|
+
export declare function instanceHasLivePid(coordRoot: string, instanceId: string): boolean;
|
|
13
|
+
/** Drop every pid-map row owned by `instanceId`. Returns how many files were removed. */
|
|
14
|
+
export declare function removePidmapRowsForInstance(coordRoot: string, instanceId: string): number;
|
|
11
15
|
//# sourceMappingURL=pidmap.d.ts.map
|