harnery 0.31.6 → 0.33.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 +2 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +5 -1
- package/dist/commands/agents.d.ts +0 -9
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +280 -94
- package/dist/commands/browse-session.d.ts +21 -0
- package/dist/commands/browse-session.d.ts.map +1 -0
- package/dist/commands/browse-session.js +157 -0
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +205 -37
- package/dist/commands/checkpoint.js +1 -1
- package/dist/commands/claude-desktop.d.ts +2 -2
- package/dist/commands/claude-desktop.d.ts.map +1 -1
- package/dist/commands/claude-desktop.js +74 -3
- package/dist/commands/deinit.d.ts.map +1 -1
- package/dist/commands/deinit.js +4 -0
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +40 -0
- package/dist/commands/doctor.d.ts +4 -2
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +141 -19
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +26 -5
- package/dist/commands/instructions.d.ts +4 -0
- package/dist/commands/instructions.d.ts.map +1 -0
- package/dist/commands/instructions.js +100 -0
- package/dist/commands/presence.d.ts.map +1 -1
- package/dist/commands/presence.js +4 -1
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +2 -3
- package/dist/core/agents/canonical-emit.d.ts +2 -3
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +6 -5
- package/dist/core/agents/cli.js +128 -89
- package/dist/core/agents/coord-client.d.ts +18 -3
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +5 -4
- package/dist/core/agents/finalization.d.ts +68 -0
- package/dist/core/agents/finalization.d.ts.map +1 -0
- package/dist/core/agents/finalization.js +443 -0
- package/dist/core/agents/git-hook.d.ts +51 -0
- package/dist/core/agents/git-hook.d.ts.map +1 -0
- package/dist/core/agents/git-hook.js +118 -0
- 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/render/prompt-context.d.ts +12 -5
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +70 -16
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +23 -4
- package/dist/core/agents/rules/claim-conflict.js +3 -3
- package/dist/core/agents/rules/commit-conflict.d.ts +15 -6
- package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/commit-conflict.js +21 -5
- package/dist/core/agents/rules/stop-hook.d.ts +3 -0
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +59 -23
- package/dist/core/agents/session-events.d.ts +8 -16
- package/dist/core/agents/session-events.d.ts.map +1 -1
- package/dist/core/agents/session-events.js +12 -26
- package/dist/core/agents/state/heartbeat-projector.d.ts +14 -0
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-projector.js +98 -1
- package/dist/core/agents/state/heartbeat-writer.d.ts +47 -5
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +86 -17
- package/dist/core/agents/state/names.d.ts +22 -1
- package/dist/core/agents/state/names.d.ts.map +1 -1
- package/dist/core/agents/state/names.js +40 -2
- package/dist/core/agents/state/session-state.d.ts +44 -0
- package/dist/core/agents/state/session-state.d.ts.map +1 -0
- package/dist/core/agents/state/session-state.js +81 -0
- package/dist/core/config.d.ts +30 -2
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +74 -11
- package/dist/core/governor/planning.d.ts.map +1 -1
- package/dist/core/governor/planning.js +12 -13
- package/dist/core/hooks/adapter/detect.d.ts +2 -4
- package/dist/core/hooks/adapter/detect.d.ts.map +1 -1
- package/dist/core/hooks/adapter/detect.js +4 -14
- package/dist/core/hooks/adapter/events.d.ts.map +1 -1
- package/dist/core/hooks/adapter/events.js +1 -0
- package/dist/core/hooks/adapter/output.d.ts +1 -1
- package/dist/core/hooks/adapter/output.d.ts.map +1 -1
- package/dist/core/hooks/adapter/output.js +5 -4
- package/dist/core/hooks/adapter/parse.d.ts +1 -1
- package/dist/core/hooks/adapter/parse.d.ts.map +1 -1
- package/dist/core/hooks/adapter/parse.js +2 -0
- package/dist/core/hooks/adapter/wiring.d.ts +23 -0
- package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
- package/dist/core/hooks/adapter/wiring.js +32 -0
- package/dist/core/hooks/cli.d.ts +1 -2
- package/dist/core/hooks/cli.d.ts.map +1 -1
- package/dist/core/hooks/cli.js +258 -32
- package/dist/core/hooks/codex-authorization.d.ts +30 -0
- package/dist/core/hooks/codex-authorization.d.ts.map +1 -0
- package/dist/core/hooks/codex-authorization.js +199 -0
- package/dist/core/hooks/codex-wsl-bridge.d.ts +46 -0
- package/dist/core/hooks/codex-wsl-bridge.d.ts.map +1 -0
- package/dist/core/hooks/codex-wsl-bridge.js +137 -0
- package/dist/core/hooks/effects/index.d.ts +1 -1
- package/dist/core/hooks/effects/index.js +3 -3
- package/dist/core/hooks/events/schema.d.ts +57 -1
- package/dist/core/hooks/events/schema.d.ts.map +1 -1
- package/dist/core/hooks/resolve/owner.d.ts +7 -1
- package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
- package/dist/core/hooks/resolve/owner.js +15 -0
- package/dist/core/hooks/resolve/transcript.d.ts +44 -0
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
- package/dist/core/hooks/resolve/transcript.js +176 -1
- package/dist/core/hooks/session-name-presence.d.ts +30 -0
- package/dist/core/hooks/session-name-presence.d.ts.map +1 -0
- package/dist/core/hooks/session-name-presence.js +43 -0
- package/dist/core/hooks/unsafe-cross-shell.d.ts +13 -0
- package/dist/core/hooks/unsafe-cross-shell.d.ts.map +1 -0
- package/dist/core/hooks/unsafe-cross-shell.js +151 -0
- package/dist/core/presence/blob.d.ts +4 -0
- package/dist/core/presence/blob.d.ts.map +1 -1
- package/dist/core/presence/blob.js +12 -0
- package/dist/core/presence/index.js +15 -1
- package/dist/core/work/state.d.ts +4 -5
- package/dist/core/work/state.d.ts.map +1 -1
- package/dist/core/work/state.js +5 -8
- package/dist/core/workflow/index.d.ts +1 -1
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/proof.d.ts +2 -2
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +2 -2
- package/dist/core/workflow/run-state.d.ts +2 -2
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +2 -2
- package/dist/core/workflow/types.d.ts +3 -3
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/execution.d.ts +2 -2
- package/dist/core/workflow/workspaces/execution.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/execution.js +5 -0
- package/dist/core/workflow/workspaces/index.d.ts +1 -1
- package/dist/core/workflow/workspaces/index.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +1 -2
- package/dist/core/workflow/workspaces/paths.d.ts +2 -2
- package/dist/core/workflow/workspaces/types.d.ts +0 -6
- package/dist/core/workflow/workspaces/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/validate.d.ts +0 -2
- package/dist/core/workflow/workspaces/validate.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/validate.js +0 -2
- package/dist/lib/browser/client.d.ts +118 -1
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +435 -6
- package/dist/lib/browser/geometry.d.ts.map +1 -1
- package/dist/lib/browser/geometry.js +180 -37
- package/dist/lib/browser/index.d.ts +5 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +5 -1
- package/dist/lib/browser/netscape-cookies.d.ts +6 -0
- package/dist/lib/browser/netscape-cookies.d.ts.map +1 -0
- package/dist/lib/browser/netscape-cookies.js +31 -0
- package/dist/lib/browser/proxy.d.ts +24 -0
- package/dist/lib/browser/proxy.d.ts.map +1 -0
- package/dist/lib/browser/proxy.js +84 -0
- package/dist/lib/browser/runts.d.ts +6 -0
- package/dist/lib/browser/runts.d.ts.map +1 -1
- package/dist/lib/browser/runts.js +21 -4
- package/dist/lib/browser/session-control.d.ts +112 -0
- package/dist/lib/browser/session-control.d.ts.map +1 -0
- package/dist/lib/browser/session-control.js +670 -0
- package/dist/lib/claude-desktop.d.ts +47 -0
- package/dist/lib/claude-desktop.d.ts.map +1 -1
- package/dist/lib/claude-desktop.js +169 -1
- package/dist/lib/docs-links.d.ts +108 -0
- package/dist/lib/docs-links.d.ts.map +1 -0
- package/dist/lib/docs-links.js +555 -0
- package/dist/lib/exec.d.ts +1 -1
- package/dist/lib/exec.js +1 -1
- package/dist/lib/identities/assume.d.ts +4 -2
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +15 -2
- package/dist/lib/instructions/bundle.d.ts +33 -0
- package/dist/lib/instructions/bundle.d.ts.map +1 -0
- package/dist/lib/instructions/bundle.js +149 -0
- package/dist/lib/instructions/git-hooks.d.ts +75 -0
- package/dist/lib/instructions/git-hooks.d.ts.map +1 -0
- package/dist/lib/instructions/git-hooks.js +238 -0
- package/dist/lib/instructions/splice.d.ts +10 -4
- package/dist/lib/instructions/splice.d.ts.map +1 -1
- package/dist/lib/instructions/splice.js +19 -11
- package/package.json +2 -2
- package/schemas/config.schema.json +32 -0
- package/src/commander.ts +5 -1
- package/src/commands/agents.ts +341 -113
- package/src/commands/browse-session.ts +245 -0
- package/src/commands/browse.ts +287 -44
- package/src/commands/checkpoint.ts +1 -1
- package/src/commands/claude-desktop.ts +106 -5
- package/src/commands/deinit.ts +7 -1
- package/src/commands/docs.ts +56 -0
- package/src/commands/doctor.ts +155 -21
- package/src/commands/init.ts +28 -9
- package/src/commands/instructions.ts +129 -0
- package/src/commands/presence.ts +7 -1
- package/src/commands/tunnel.ts +2 -5
- package/src/core/agents/canonical-emit.ts +7 -6
- package/src/core/agents/cli.ts +143 -89
- package/src/core/agents/coord-client.ts +21 -5
- package/src/core/agents/finalization.ts +595 -0
- package/src/core/agents/git-hook.ts +126 -0
- package/src/core/agents/index.ts +1 -0
- package/src/core/agents/render/prompt-context.ts +96 -16
- package/src/core/agents/render/session-context.ts +29 -4
- package/src/core/agents/rules/claim-conflict.ts +3 -3
- package/src/core/agents/rules/commit-conflict.ts +35 -8
- package/src/core/agents/rules/stop-hook.ts +71 -23
- package/src/core/agents/session-events.ts +14 -41
- package/src/core/agents/state/heartbeat-projector.ts +108 -1
- package/src/core/agents/state/heartbeat-writer.ts +129 -16
- package/src/core/agents/state/names.ts +54 -2
- package/src/core/agents/state/session-state.ts +131 -0
- package/src/core/config.ts +92 -12
- package/src/core/governor/planning.ts +17 -12
- package/src/core/hooks/adapter/detect.ts +4 -13
- package/src/core/hooks/adapter/events.ts +1 -0
- package/src/core/hooks/adapter/output.ts +9 -4
- package/src/core/hooks/adapter/parse.ts +3 -0
- package/src/core/hooks/adapter/wiring.ts +43 -0
- package/src/core/hooks/cli.ts +296 -27
- package/src/core/hooks/codex-authorization.ts +253 -0
- package/src/core/hooks/codex-wsl-bridge.ts +188 -0
- package/src/core/hooks/effects/index.ts +5 -5
- package/src/core/hooks/events/schema.ts +71 -0
- package/src/core/hooks/resolve/owner.ts +22 -1
- package/src/core/hooks/resolve/transcript.ts +164 -1
- package/src/core/hooks/session-name-presence.ts +52 -0
- package/src/core/hooks/unsafe-cross-shell.ts +160 -0
- package/src/core/presence/blob.ts +18 -0
- package/src/core/presence/index.ts +18 -1
- package/src/core/work/state.ts +8 -12
- package/src/core/workflow/engine.ts +2 -2
- package/src/core/workflow/index.ts +0 -1
- package/src/core/workflow/proof.ts +4 -4
- package/src/core/workflow/run-state.ts +5 -5
- package/src/core/workflow/types.ts +3 -3
- package/src/core/workflow/workspaces/execution.ts +11 -4
- package/src/core/workflow/workspaces/index.ts +0 -3
- package/src/core/workflow/workspaces/local-git.ts +1 -2
- package/src/core/workflow/workspaces/paths.ts +2 -2
- package/src/core/workflow/workspaces/types.ts +0 -9
- package/src/core/workflow/workspaces/validate.ts +1 -5
- package/src/lib/browser/client.ts +528 -6
- package/src/lib/browser/geometry.ts +197 -37
- package/src/lib/browser/index.ts +39 -0
- package/src/lib/browser/netscape-cookies.ts +39 -0
- package/src/lib/browser/proxy.ts +105 -0
- package/src/lib/browser/runts.ts +27 -3
- package/src/lib/browser/session-control.ts +892 -0
- package/src/lib/claude-desktop.ts +232 -1
- package/src/lib/docs-links.ts +674 -0
- package/src/lib/exec.ts +1 -1
- package/src/lib/identities/assume.ts +22 -1
- package/src/lib/instructions/bundle.ts +199 -0
- package/src/lib/instructions/git-hooks.ts +259 -0
- package/src/lib/instructions/splice.ts +41 -11
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import type { Command } from "commander";
|
|
5
|
+
import { countTokens } from "gpt-tokenizer/model/gpt-4o";
|
|
6
|
+
import type { EmitContext } from "../commander.ts";
|
|
7
|
+
import { monorepoRoot } from "../core/agents/index.ts";
|
|
8
|
+
import type { Adapter } from "../core/hooks/events/schema.ts";
|
|
9
|
+
import {
|
|
10
|
+
buildInstructionBundle,
|
|
11
|
+
type InstructionBundleComponent,
|
|
12
|
+
} from "../lib/instructions/bundle.ts";
|
|
13
|
+
|
|
14
|
+
interface ManifestOptions {
|
|
15
|
+
adapter: string;
|
|
16
|
+
format: string;
|
|
17
|
+
output?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function registerInstructionsCommand(program: Command, emit: EmitContext): void {
|
|
21
|
+
const command = program
|
|
22
|
+
.command("instructions")
|
|
23
|
+
.description(
|
|
24
|
+
"Inspect and version the repo-authored instruction bundle loaded by each adapter.",
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
command
|
|
28
|
+
.command("manifest [paths...]")
|
|
29
|
+
.description("Emit a machine-readable bundle manifest for one or more working paths")
|
|
30
|
+
.option("--adapter <adapter>", "claude-code, cursor, codex, or all", "all")
|
|
31
|
+
.option("--format <type>", "Output format: json or table", "json")
|
|
32
|
+
.option("-o, --output <file>", "Write the complete JSON manifest to a file")
|
|
33
|
+
.action((paths: string[], options: ManifestOptions) => {
|
|
34
|
+
if (options.format === "json") emit.config({ format: "json" });
|
|
35
|
+
const coordRoot = monorepoRoot() ?? process.cwd();
|
|
36
|
+
const adapters = parseAdapters(options.adapter);
|
|
37
|
+
const profiles = (paths.length > 0 ? paths : [process.cwd()]).flatMap((path) =>
|
|
38
|
+
adapters.map((adapter) =>
|
|
39
|
+
enrichBundle(
|
|
40
|
+
buildInstructionBundle({ coordRoot, cwd: resolve(path), adapter }),
|
|
41
|
+
coordRoot,
|
|
42
|
+
),
|
|
43
|
+
),
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const manifest = {
|
|
47
|
+
ok: true,
|
|
48
|
+
schema_version: 1,
|
|
49
|
+
generated_at: new Date().toISOString(),
|
|
50
|
+
source_commit: gitHead(coordRoot),
|
|
51
|
+
tokenizer: "o200k_base",
|
|
52
|
+
coord_root: ".",
|
|
53
|
+
profile_count: profiles.length,
|
|
54
|
+
profiles,
|
|
55
|
+
};
|
|
56
|
+
if (options.output) {
|
|
57
|
+
const output = resolve(options.output);
|
|
58
|
+
writeFileSync(output, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
59
|
+
emit.data({
|
|
60
|
+
ok: true,
|
|
61
|
+
output,
|
|
62
|
+
schema_version: manifest.schema_version,
|
|
63
|
+
generated_at: manifest.generated_at,
|
|
64
|
+
source_commit: manifest.source_commit,
|
|
65
|
+
profile_count: manifest.profile_count,
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
emit.data(manifest);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function parseAdapters(value: string): Adapter[] {
|
|
74
|
+
if (value === "all") return ["claude-code", "cursor", "codex"];
|
|
75
|
+
if (value === "claude-code" || value === "cursor" || value === "codex") return [value];
|
|
76
|
+
throw new Error(`unsupported adapter '${value}' (expected claude-code, cursor, codex, or all)`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function enrichBundle(bundle: ReturnType<typeof buildInstructionBundle>, coordRoot: string) {
|
|
80
|
+
const components = bundle.components.map((component) => enrichComponent(component, coordRoot));
|
|
81
|
+
const canonicalSources = bundle.canonical_sources.map((component) =>
|
|
82
|
+
enrichComponent(component, coordRoot),
|
|
83
|
+
);
|
|
84
|
+
return {
|
|
85
|
+
...bundle,
|
|
86
|
+
coord_root: ".",
|
|
87
|
+
components,
|
|
88
|
+
canonical_sources: canonicalSources,
|
|
89
|
+
totals: totals(components),
|
|
90
|
+
canonical_source_totals: totals(canonicalSources),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function enrichComponent(component: InstructionBundleComponent, coordRoot: string) {
|
|
95
|
+
const bytes = readFileSync(resolve(coordRoot, component.path));
|
|
96
|
+
const text = bytes.includes(0) ? null : bytes.toString("utf8");
|
|
97
|
+
return {
|
|
98
|
+
...component,
|
|
99
|
+
chars: text?.length ?? null,
|
|
100
|
+
tokens: text === null ? null : countTokens(text),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function totals(components: ReturnType<typeof enrichComponent>[]) {
|
|
105
|
+
const roles = ["always_loaded", "just_in_time", "runtime_config"] as const;
|
|
106
|
+
return {
|
|
107
|
+
files: components.length,
|
|
108
|
+
bytes: components.reduce((sum, component) => sum + component.bytes, 0),
|
|
109
|
+
tokens: components.reduce((sum, component) => sum + (component.tokens ?? 0), 0),
|
|
110
|
+
by_role: Object.fromEntries(
|
|
111
|
+
roles.map((role) => {
|
|
112
|
+
const selected = components.filter((component) => component.role === role);
|
|
113
|
+
return [
|
|
114
|
+
role,
|
|
115
|
+
{
|
|
116
|
+
files: selected.length,
|
|
117
|
+
bytes: selected.reduce((sum, component) => sum + component.bytes, 0),
|
|
118
|
+
tokens: selected.reduce((sum, component) => sum + (component.tokens ?? 0), 0),
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
}),
|
|
122
|
+
),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function gitHead(root: string): string | null {
|
|
127
|
+
const result = spawnSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" });
|
|
128
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
129
|
+
}
|
package/src/commands/presence.ts
CHANGED
|
@@ -175,7 +175,13 @@ export function registerPresenceCommand(program: Command, emit: EmitContext): vo
|
|
|
175
175
|
for (const a of m.agents) {
|
|
176
176
|
const task = a.task ? ` "${a.task.slice(0, 70)}"` : "";
|
|
177
177
|
const files = a.files_touched?.length ? ` [${a.files_touched.length} files]` : "";
|
|
178
|
-
|
|
178
|
+
const lifecycle =
|
|
179
|
+
a.task_state === "blocked" && a.task_state_reason
|
|
180
|
+
? `${a.task_state}: ${a.task_state_reason}`
|
|
181
|
+
: a.task_state;
|
|
182
|
+
emit.text(
|
|
183
|
+
` agent-${a.name ?? a.instance_id.slice(0, 8)}${task}${files} activity=${a.activity} lifecycle=${lifecycle}\n`,
|
|
184
|
+
);
|
|
179
185
|
}
|
|
180
186
|
}
|
|
181
187
|
});
|
package/src/commands/tunnel.ts
CHANGED
|
@@ -76,7 +76,6 @@ interface LogsOpts {
|
|
|
76
76
|
name?: string;
|
|
77
77
|
follow?: boolean;
|
|
78
78
|
gate?: boolean;
|
|
79
|
-
cloudflared?: boolean;
|
|
80
79
|
provider?: boolean;
|
|
81
80
|
}
|
|
82
81
|
|
|
@@ -916,7 +915,7 @@ function status(opts: StatusOpts): void {
|
|
|
916
915
|
return;
|
|
917
916
|
}
|
|
918
917
|
if (states.length === 1) {
|
|
919
|
-
//
|
|
918
|
+
// A single tunnel is clearer as a detail block than as a one-row table.
|
|
920
919
|
statusDetail(states[0]);
|
|
921
920
|
return;
|
|
922
921
|
}
|
|
@@ -953,8 +952,7 @@ function logs(opts: LogsOpts): void {
|
|
|
953
952
|
const name = resolveName(opts.name);
|
|
954
953
|
const state = readState(name);
|
|
955
954
|
const provider = state?.provider ?? "cloudflare";
|
|
956
|
-
const which =
|
|
957
|
-
opts.cloudflared || opts.provider ? providerLogFile(name, provider) : gateLogFile(name);
|
|
955
|
+
const which = opts.provider ? providerLogFile(name, provider) : gateLogFile(name);
|
|
958
956
|
const path = cachePath("tunnel", which);
|
|
959
957
|
if (!existsSync(path)) {
|
|
960
958
|
emit.error({ code: "tunnel_no_log", message: `No log file at ${path}` });
|
|
@@ -1089,7 +1087,6 @@ export function registerTunnelCommand(
|
|
|
1089
1087
|
.option("-f, --follow", "follow the log")
|
|
1090
1088
|
.option("--gate", "tail the gate log (default)")
|
|
1091
1089
|
.option("--provider", "tail the provider log instead")
|
|
1092
|
-
.option("--cloudflared", "tail the Cloudflare provider log instead (legacy alias)")
|
|
1093
1090
|
.action(logs);
|
|
1094
1091
|
|
|
1095
1092
|
const allow = cmd
|
|
@@ -37,11 +37,11 @@ export function resolveEmitRoot(start: string = process.cwd()): string | null {
|
|
|
37
37
|
return resolveCoordRoot(start);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
export function emitCanonical(input: CanonicalEmitInput):
|
|
40
|
+
export function emitCanonical(input: CanonicalEmitInput): boolean {
|
|
41
41
|
const root = resolveEmitRoot();
|
|
42
|
-
if (!root) return;
|
|
42
|
+
if (!root) return false;
|
|
43
43
|
const binary = coordBinPath("agent-coord", root);
|
|
44
|
-
if (!binary) return;
|
|
44
|
+
if (!binary) return false;
|
|
45
45
|
try {
|
|
46
46
|
const args = [
|
|
47
47
|
"emit-event",
|
|
@@ -71,15 +71,16 @@ export function emitCanonical(input: CanonicalEmitInput): void {
|
|
|
71
71
|
if (result.error || result.status !== 0) {
|
|
72
72
|
const why = result.error ? result.error.message : `exit ${result.status}`;
|
|
73
73
|
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
|
|
74
|
+
return false;
|
|
74
75
|
}
|
|
76
|
+
return true;
|
|
75
77
|
} catch {
|
|
76
|
-
|
|
78
|
+
return false;
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
/**
|
|
81
|
-
* Normalize the heartbeat's `platform` field
|
|
82
|
-
* `claude_code`) to the canonical kebab-case Adapter type.
|
|
83
|
+
* Normalize the heartbeat's `platform` field to the canonical Adapter type.
|
|
83
84
|
*/
|
|
84
85
|
export function normalizeAdapter(platform: string | undefined): "claude-code" | "cursor" | "codex" {
|
|
85
86
|
if (platform === "cursor") return "cursor";
|
package/src/core/agents/cli.ts
CHANGED
|
@@ -96,23 +96,11 @@ async function handleVerdict(root: string): Promise<number> {
|
|
|
96
96
|
} else if (parsed.rule === "claim") {
|
|
97
97
|
const { evaluateClaim } = await import("./rules/claim-conflict.ts");
|
|
98
98
|
verdict = evaluateClaim(root, parsed as unknown as Parameters<typeof evaluateClaim>[1]);
|
|
99
|
-
} else if (parsed.rule === "commit") {
|
|
100
|
-
const { evaluateCommit } = await import("./rules/commit-conflict.ts");
|
|
101
|
-
const result = evaluateCommit(root, parsed as unknown as Parameters<typeof evaluateCommit>[1]);
|
|
102
|
-
// Map CommitVerdictResult → the standard verdict envelope, stash details
|
|
103
|
-
// on extra fields so the bash caller can pull conflicts + log_lines.
|
|
104
|
-
verdict = {
|
|
105
|
-
allow: result.allow,
|
|
106
|
-
exit_code: result.exit_code,
|
|
107
|
-
rule: result.rule,
|
|
108
|
-
reason: result.message,
|
|
109
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
110
|
-
} as typeof verdict;
|
|
111
|
-
(verdict as Record<string, unknown>).conflicts = result.conflicts;
|
|
112
|
-
(verdict as Record<string, unknown>).log_lines = result.log_lines;
|
|
113
|
-
(verdict as Record<string, unknown>).suppressed_self_attribution =
|
|
114
|
-
result.suppressed_self_attribution ?? false;
|
|
115
99
|
} else {
|
|
100
|
+
// NB: rule === "commit" was served here until 2026-08-09; the commit
|
|
101
|
+
// guard now enters through `git-hook pre-commit`, which runs the same
|
|
102
|
+
// evaluateCommit in-process. A straggler host lands in this fail-open
|
|
103
|
+
// branch, which is the pre-migration behavior for a malformed request.
|
|
116
104
|
verdict = {
|
|
117
105
|
allow: true,
|
|
118
106
|
exit_code: 0,
|
|
@@ -261,14 +249,6 @@ async function handleStateAction(root: string, action: string, rest: string[]):
|
|
|
261
249
|
);
|
|
262
250
|
return 0;
|
|
263
251
|
}
|
|
264
|
-
case "stamp-status-call": {
|
|
265
|
-
const hb = writer.stampStatusCheck(root, owner);
|
|
266
|
-
if (!hb) return 1;
|
|
267
|
-
process.stdout.write(
|
|
268
|
-
`${JSON.stringify({ instance_id: owner, last_status_at: hb.last_status_at })}\n`,
|
|
269
|
-
);
|
|
270
|
-
return 0;
|
|
271
|
-
}
|
|
272
252
|
case "set-turn-summary": {
|
|
273
253
|
const summary = args.join(" ");
|
|
274
254
|
const hb = writer.setTurnSummary(root, owner, summary);
|
|
@@ -344,10 +324,20 @@ async function handleStateAction(root: string, action: string, rest: string[]):
|
|
|
344
324
|
// fragility. Positionals (sessionId, model) stay as-is once flags are
|
|
345
325
|
// filtered out.
|
|
346
326
|
const adapter = args.find((a) => a.startsWith("--adapter="))?.slice("--adapter=".length);
|
|
327
|
+
const forkedFrom = args
|
|
328
|
+
.find((a) => a.startsWith("--forked-from="))
|
|
329
|
+
?.slice("--forked-from=".length);
|
|
347
330
|
const positional = args.filter((a) => !a.startsWith("--"));
|
|
348
331
|
const sessionId = positional[0];
|
|
349
332
|
const model = positional[1];
|
|
350
|
-
const hb = writer.healHeartbeat(
|
|
333
|
+
const hb = writer.healHeartbeat(
|
|
334
|
+
root,
|
|
335
|
+
owner,
|
|
336
|
+
sessionId,
|
|
337
|
+
model,
|
|
338
|
+
adapter,
|
|
339
|
+
forkedFrom ? { forkedFrom } : undefined,
|
|
340
|
+
);
|
|
351
341
|
process.stdout.write(`${JSON.stringify({ instance_id: owner, recreated: !!hb })}\n`);
|
|
352
342
|
return hb ? 0 : 1;
|
|
353
343
|
}
|
|
@@ -497,69 +487,39 @@ async function handleCouncilAction(root: string, action: string, rest: string[])
|
|
|
497
487
|
|
|
498
488
|
async function handleAssignName(root: string, rest: string[]): Promise<number> {
|
|
499
489
|
const { assignName } = await import("./state/names.ts");
|
|
500
|
-
|
|
490
|
+
// Optional recorded-fork-lineage flag: --forked-from <parent_instance_id>.
|
|
491
|
+
// Supplied by an adapter layer that knows (or detected) that this session
|
|
492
|
+
// was branched from another conversation. Inert on resume: assignName only
|
|
493
|
+
// stamps lineage on the row that first assigns the instance.
|
|
494
|
+
let forkedFrom: string | undefined;
|
|
495
|
+
const positional: string[] = [];
|
|
496
|
+
for (let i = 0; i < rest.length; i++) {
|
|
497
|
+
if (rest[i] === "--forked-from") {
|
|
498
|
+
forkedFrom = rest[++i];
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
positional.push(rest[i]!);
|
|
502
|
+
}
|
|
503
|
+
const [owner, kindArg] = positional;
|
|
501
504
|
if (!owner || !kindArg) {
|
|
502
|
-
process.stderr.write(
|
|
505
|
+
process.stderr.write(
|
|
506
|
+
"agent-coord assign-name <instance_id> <session|subagent|transient> [--forked-from <instance_id>]\n",
|
|
507
|
+
);
|
|
503
508
|
return 2;
|
|
504
509
|
}
|
|
505
510
|
if (kindArg !== "session" && kindArg !== "subagent" && kindArg !== "transient") {
|
|
506
511
|
process.stderr.write(`agent-coord assign-name: invalid kind ${kindArg}\n`);
|
|
507
512
|
return 2;
|
|
508
513
|
}
|
|
509
|
-
const name = assignName(root, owner, kindArg);
|
|
510
|
-
process.stdout.write(
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
req = JSON.parse(raw);
|
|
519
|
-
} catch {
|
|
520
|
-
return 0;
|
|
521
|
-
}
|
|
522
|
-
const { groupUnclaim } = await import("./state/heartbeat-writer.ts");
|
|
523
|
-
|
|
524
|
-
// Session-group-wide unclaim. `owner` is the parent's session_id which is
|
|
525
|
-
// also the group key (parent + subagents share session_id). Each actual
|
|
526
|
-
// removal also emits a durable claim.release event — the projector rebuilds
|
|
527
|
-
// files_touched from the permanent Edit/Write events, so a file-only prune
|
|
528
|
-
// would resurrect on the next replay.
|
|
529
|
-
if (req.owner && Array.isArray(req.prune)) {
|
|
530
|
-
for (const path of req.prune) {
|
|
531
|
-
try {
|
|
532
|
-
for (const hit of groupUnclaim(root, req.owner, path)) {
|
|
533
|
-
await emitClaimRelease(root, hit.instance_id, hit, path, "commit");
|
|
534
|
-
}
|
|
535
|
-
} catch {
|
|
536
|
-
/* best-effort */
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
return 0;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
async function handlePostCheckout(root: string, _rest: string[]): Promise<number> {
|
|
544
|
-
const raw = await readStdin();
|
|
545
|
-
let req: { owner?: string; removed?: string[] } = {};
|
|
546
|
-
try {
|
|
547
|
-
req = JSON.parse(raw);
|
|
548
|
-
} catch {
|
|
549
|
-
return 0;
|
|
550
|
-
}
|
|
551
|
-
const { groupUnclaim } = await import("./state/heartbeat-writer.ts");
|
|
552
|
-
if (req.owner && Array.isArray(req.removed)) {
|
|
553
|
-
for (const path of req.removed) {
|
|
554
|
-
try {
|
|
555
|
-
for (const hit of groupUnclaim(root, req.owner, path)) {
|
|
556
|
-
await emitClaimRelease(root, hit.instance_id, hit, path, "checkout");
|
|
557
|
-
}
|
|
558
|
-
} catch {
|
|
559
|
-
/* best-effort */
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
}
|
|
514
|
+
const name = assignName(root, owner, kindArg, forkedFrom ? { forkedFrom } : undefined);
|
|
515
|
+
process.stdout.write(
|
|
516
|
+
`${JSON.stringify({
|
|
517
|
+
instance_id: owner,
|
|
518
|
+
name,
|
|
519
|
+
kind: kindArg,
|
|
520
|
+
...(forkedFrom ? { forked_from: forkedFrom } : {}),
|
|
521
|
+
})}\n`,
|
|
522
|
+
);
|
|
563
523
|
return 0;
|
|
564
524
|
}
|
|
565
525
|
|
|
@@ -660,9 +620,13 @@ async function handlePromptContext(root: string, rest: string[]): Promise<number
|
|
|
660
620
|
const sessionNameNudge = args["session-name-nudge"] === "true";
|
|
661
621
|
const taskNudge = args["task-nudge"] === "true";
|
|
662
622
|
const statusFooterNudge = args["status-footer-nudge"] === "true";
|
|
623
|
+
// Value is the adapter id; a bare flag (parsed as "true") still enables the
|
|
624
|
+
// reminder with the adapter-neutral wording.
|
|
625
|
+
const turnRitualNudge =
|
|
626
|
+
args["turn-ritual-nudge"] === "true" ? "generic" : args["turn-ritual-nudge"];
|
|
663
627
|
if (!instanceId) {
|
|
664
628
|
process.stderr.write(
|
|
665
|
-
"agent-coord prompt-context --instance <id> [--session <id>] [--name <agent-name>] [--session-name-nudge] [--task-nudge] [--status-footer-nudge]\n",
|
|
629
|
+
"agent-coord prompt-context --instance <id> [--session <id>] [--name <agent-name>] [--session-name-nudge] [--task-nudge] [--status-footer-nudge] [--turn-ritual-nudge <adapter>]\n",
|
|
666
630
|
);
|
|
667
631
|
return 2;
|
|
668
632
|
}
|
|
@@ -675,6 +639,7 @@ async function handlePromptContext(root: string, rest: string[]): Promise<number
|
|
|
675
639
|
sessionNameNudge,
|
|
676
640
|
taskNudge,
|
|
677
641
|
statusFooterNudge,
|
|
642
|
+
turnRitualNudge,
|
|
678
643
|
});
|
|
679
644
|
process.stdout.write(text);
|
|
680
645
|
return 0;
|
|
@@ -841,6 +806,100 @@ async function handleEmitEvent(root: string, rest: string[]): Promise<number> {
|
|
|
841
806
|
return 0;
|
|
842
807
|
}
|
|
843
808
|
|
|
809
|
+
/**
|
|
810
|
+
* `agent-coord git-hook <event> [args]` — the single entry a host git hook
|
|
811
|
+
* invokes. Owns ALL of the coordination git plumbing that hosts used to carry
|
|
812
|
+
* as bash (staged/committed/checkout-removed collection, submodule
|
|
813
|
+
* canonicalization, gitlink probe, verdict, claim pruning), so a harnery
|
|
814
|
+
* upgrade upgrades hook behavior with no change to the host's hook file.
|
|
815
|
+
*
|
|
816
|
+
* Events: pre-commit (E-guard verdict; exit 1 blocks the commit),
|
|
817
|
+
* post-commit (prune committed paths' claims), post-checkout <old> <new>
|
|
818
|
+
* (release claims on paths the ref move rewrote).
|
|
819
|
+
*
|
|
820
|
+
* Fail-open by design: an internal error never blocks a commit (parity with
|
|
821
|
+
* the bash era, where a crashed verdict read as empty and the hook moved on).
|
|
822
|
+
* A clean conflict verdict still blocks.
|
|
823
|
+
*/
|
|
824
|
+
async function handleGitHook(fallbackRoot: string, rest: string[]): Promise<number> {
|
|
825
|
+
const event = rest[0];
|
|
826
|
+
if (process.env.HARNERY_AGENT_COORD_OFF === "1") return 0;
|
|
827
|
+
const cwd = process.cwd();
|
|
828
|
+
|
|
829
|
+
try {
|
|
830
|
+
const gh = await import("./git-hook.ts");
|
|
831
|
+
// Pin the coord root to the superproject: a submodule cwd may carry its
|
|
832
|
+
// own config-only .harnery that the generic root walk would land on.
|
|
833
|
+
const root = gh.discoverCoordRoot(cwd) ?? fallbackRoot;
|
|
834
|
+
|
|
835
|
+
if (event === "pre-commit") {
|
|
836
|
+
const { staged, gitlinks } = gh.collectStaged(cwd);
|
|
837
|
+
if (staged.length === 0) return 0;
|
|
838
|
+
const { evaluateCommit } = await import("./rules/commit-conflict.ts");
|
|
839
|
+
const result = evaluateCommit(root, {
|
|
840
|
+
staged_paths: staged,
|
|
841
|
+
staged_gitlinks: gitlinks,
|
|
842
|
+
bypass: process.env.HARNERY_AGENT_COORD_BYPASS === "1",
|
|
843
|
+
});
|
|
844
|
+
// Forensics channel shared with the stdin `verdict` path.
|
|
845
|
+
try {
|
|
846
|
+
const logPath = join(root, ".harnery", "debug", "agent-coord-verdict.ndjson");
|
|
847
|
+
await mkdir(dirname(logPath), { recursive: true });
|
|
848
|
+
await appendFile(
|
|
849
|
+
logPath,
|
|
850
|
+
`${JSON.stringify({ ts: new Date().toISOString(), via: "git-hook", staged, verdict: result })}\n`,
|
|
851
|
+
);
|
|
852
|
+
} catch {
|
|
853
|
+
/* diagnostics never break a verdict */
|
|
854
|
+
}
|
|
855
|
+
if (result.rule !== "commit.pass") {
|
|
856
|
+
if (result.message) process.stdout.write(`\n${result.message}\n\n`);
|
|
857
|
+
for (const c of result.conflicts) {
|
|
858
|
+
process.stdout.write(` ${c.staged_path} (held by ${c.short_name})\n`);
|
|
859
|
+
}
|
|
860
|
+
process.stdout.write("\n");
|
|
861
|
+
}
|
|
862
|
+
if (!result.allow) {
|
|
863
|
+
process.stdout.write(
|
|
864
|
+
" Wait for them to finish, split the commit, or set\n" +
|
|
865
|
+
" HARNERY_AGENT_COORD_BYPASS=1 to force this commit through.\n\n",
|
|
866
|
+
);
|
|
867
|
+
return result.exit_code || 1;
|
|
868
|
+
}
|
|
869
|
+
return 0;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
if (event === "post-commit" || event === "post-checkout") {
|
|
873
|
+
const paths =
|
|
874
|
+
event === "post-commit"
|
|
875
|
+
? gh.collectCommitted(cwd)
|
|
876
|
+
: gh.collectCheckoutRemoved(cwd, rest[1] ?? "", rest[2] ?? "");
|
|
877
|
+
if (paths.length === 0) return 0;
|
|
878
|
+
const { resolveOwner } = await import("../hooks/resolve/owner.ts");
|
|
879
|
+
const owner = resolveOwner({ payload: null, coordRoot: root })?.instance_id;
|
|
880
|
+
if (!owner) return 0;
|
|
881
|
+
const { groupUnclaim } = await import("./state/heartbeat-writer.ts");
|
|
882
|
+
const reason = event === "post-commit" ? ("commit" as const) : ("checkout" as const);
|
|
883
|
+
for (const path of paths) {
|
|
884
|
+
try {
|
|
885
|
+
for (const hit of groupUnclaim(root, owner, path)) {
|
|
886
|
+
await emitClaimRelease(root, hit.instance_id, hit, path, reason);
|
|
887
|
+
}
|
|
888
|
+
} catch {
|
|
889
|
+
/* best-effort */
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
return 0;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
process.stderr.write(`agent-coord git-hook: unknown event ${event ?? "(none)"}\n`);
|
|
896
|
+
return 0;
|
|
897
|
+
} catch {
|
|
898
|
+
// Fail open: coordination must never brick git.
|
|
899
|
+
return 0;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
844
903
|
async function main(): Promise<number> {
|
|
845
904
|
const [subcommand, ...rest] = process.argv.slice(2);
|
|
846
905
|
const root = findCoordRoot(process.cwd());
|
|
@@ -860,7 +919,6 @@ async function main(): Promise<number> {
|
|
|
860
919
|
|
|
861
920
|
if (
|
|
862
921
|
subcommand === "set-task" ||
|
|
863
|
-
subcommand === "stamp-status-call" ||
|
|
864
922
|
subcommand === "set-turn-summary" ||
|
|
865
923
|
subcommand === "release-claim" ||
|
|
866
924
|
subcommand === "kill-heartbeat" ||
|
|
@@ -918,12 +976,8 @@ async function main(): Promise<number> {
|
|
|
918
976
|
return handleShellMutationClaimLog(root, rest);
|
|
919
977
|
}
|
|
920
978
|
|
|
921
|
-
if (subcommand === "
|
|
922
|
-
return
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
if (subcommand === "post-checkout") {
|
|
926
|
-
return handlePostCheckout(root, rest);
|
|
979
|
+
if (subcommand === "git-hook") {
|
|
980
|
+
return handleGitHook(root, rest);
|
|
927
981
|
}
|
|
928
982
|
|
|
929
983
|
await logNoop(root, subcommand ?? "(none)", rest);
|
|
@@ -75,12 +75,27 @@ export interface Heartbeat {
|
|
|
75
75
|
task?: string;
|
|
76
76
|
/** UTC ISO-8601 timestamp when task was last set/cleared. Used by adapters without Stop enforcement to compute staleness. 2026-05-24. */
|
|
77
77
|
task_updated_at?: string | null;
|
|
78
|
+
/** Current evidence-derived activity axis. */
|
|
79
|
+
activity?: "unknown" | "working" | "needs_input" | "idle";
|
|
80
|
+
activity_updated_at?: string;
|
|
81
|
+
activity_source?: string;
|
|
82
|
+
/** Explicit task lifecycle axis. */
|
|
83
|
+
task_state?: "active" | "blocked" | "done";
|
|
84
|
+
task_state_updated_at?: string;
|
|
85
|
+
task_state_reason?: string;
|
|
86
|
+
/** Session name built on the first non-empty set-task; its presence means "this session has been named". 2026-08-09. */
|
|
87
|
+
suggested_session_name?: string;
|
|
88
|
+
/** Stamped by turn.stop once the suggested name is seen in assistant reply text. 2026-08-09. */
|
|
89
|
+
session_name_seen_at?: string;
|
|
90
|
+
/** Which suggested name the latest sighting covered. */
|
|
91
|
+
session_name_seen_for?: string;
|
|
78
92
|
/** Auto-generated per-turn summary written by the Stop hook via Haiku. 2026-05-23. */
|
|
79
93
|
turn_summary?: string | null;
|
|
80
94
|
/** UTC ISO-8601 timestamp when turn_summary was last refreshed. */
|
|
81
95
|
turn_summary_updated_at?: string | null;
|
|
82
|
-
/** Hook client: `
|
|
96
|
+
/** Hook client: `claude-code` (default) or `cursor`. Cursor Phase 1. */
|
|
83
97
|
platform?: string;
|
|
98
|
+
workflow_run_id?: string;
|
|
84
99
|
}
|
|
85
100
|
|
|
86
101
|
/**
|
|
@@ -260,10 +275,10 @@ export function parsePidmapRowOwner(row: string): string {
|
|
|
260
275
|
return tab >= 0 ? trimmed.slice(0, tab) : trimmed;
|
|
261
276
|
}
|
|
262
277
|
|
|
263
|
-
/** Parse platform from a pid-map row; legacy rows default to `
|
|
278
|
+
/** Parse platform from a pid-map row; legacy rows default to `claude-code`. */
|
|
264
279
|
export function parsePidmapRowPlatform(row: string): string {
|
|
265
280
|
const platform = row.trim().split("\t")[1]?.trim();
|
|
266
|
-
return platform || "
|
|
281
|
+
return platform || "claude-code";
|
|
267
282
|
}
|
|
268
283
|
|
|
269
284
|
/** Parse the start token from a pid-map row; rows written before it carry none. */
|
|
@@ -368,7 +383,7 @@ function readPidmapRow(pidmapDir: string, pid: number): string | null {
|
|
|
368
383
|
*
|
|
369
384
|
* Pid-map files are `instance_id` or `instance_id\tplatform` (Cursor Phase 1).
|
|
370
385
|
* Prefer a row whose platform matches `HARNERY_AGENT_COORD_PLATFORM` (default
|
|
371
|
-
* `
|
|
386
|
+
* `claude-code`); otherwise return the first owner seen on the walk.
|
|
372
387
|
*
|
|
373
388
|
* Subagents intentionally do not write pid-map entries; a Bash-tool ppid-walk
|
|
374
389
|
* from inside a subagent therefore resolves to the *parent's* pid-map entry.
|
|
@@ -443,7 +458,7 @@ export function resolveOwnerByPidmap(root: string): {
|
|
|
443
458
|
const pidmapDir = resolve(root, ".harnery", "pid-map");
|
|
444
459
|
if (!existsSync(pidmapDir)) return { owner: null, source: "none" };
|
|
445
460
|
|
|
446
|
-
const prefer = process.env.HARNERY_AGENT_COORD_PLATFORM?.trim() || "
|
|
461
|
+
const prefer = process.env.HARNERY_AGENT_COORD_PLATFORM?.trim() || "claude-code";
|
|
447
462
|
let fallbackOwner: string | null = null;
|
|
448
463
|
let pid: number | null = process.pid;
|
|
449
464
|
|
|
@@ -482,6 +497,7 @@ const SESSION_ID_ENV_VARS = [
|
|
|
482
497
|
"CURSOR_SESSION_ID",
|
|
483
498
|
"CURSOR_CONVERSATION_ID",
|
|
484
499
|
"CODEX_SESSION_ID",
|
|
500
|
+
"CODEX_THREAD_ID",
|
|
485
501
|
] as const;
|
|
486
502
|
|
|
487
503
|
/** Read normalized candidates from the first non-empty adapter session-id env var. */
|