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
package/dist/commands/agents.js
CHANGED
|
@@ -18,7 +18,10 @@ import { homedir, tmpdir } from "node:os";
|
|
|
18
18
|
import { basename, join, resolve } from "node:path";
|
|
19
19
|
import { coordBinPath } from "../core/agents/coord-bin.js";
|
|
20
20
|
import { readStreamTailBounded } from "../core/agents/events/consume.js";
|
|
21
|
+
import { checkGitFinalization, formatGitFinalizationFailure, readSessionWriteClaims, } from "../core/agents/finalization.js";
|
|
21
22
|
import { emitCanonical, monorepoRoot, normalizeAdapter, readHeartbeat, resolveOwner, resolveOwnerBySessionEnv, resolveOwnerWithSource, } from "../core/agents/index.js";
|
|
23
|
+
import { buildLifecycleSuggestedName, buildSuggestedName, } from "../core/agents/state/heartbeat-writer.js";
|
|
24
|
+
import { foldSessionState, } from "../core/agents/state/session-state.js";
|
|
22
25
|
import { coordFreshnessSeconds, resolveBinName } from "../core/config.js";
|
|
23
26
|
import { readRemoteMachines } from "../core/presence/index.js";
|
|
24
27
|
import { registerContextCommand } from "./context.js";
|
|
@@ -99,6 +102,16 @@ function formatPlatformLabel(platform) {
|
|
|
99
102
|
return "Codex";
|
|
100
103
|
return "CC";
|
|
101
104
|
}
|
|
105
|
+
function activityOf(hb) {
|
|
106
|
+
return hb.activity ?? "unknown";
|
|
107
|
+
}
|
|
108
|
+
function taskStateOf(hb) {
|
|
109
|
+
return hb.task_state ?? "active";
|
|
110
|
+
}
|
|
111
|
+
function lifecycleLabel(hb) {
|
|
112
|
+
const state = taskStateOf(hb);
|
|
113
|
+
return state === "blocked" && hb.task_state_reason ? `${state}: ${hb.task_state_reason}` : state;
|
|
114
|
+
}
|
|
102
115
|
let emit;
|
|
103
116
|
export function registerAgentsCommand(program, emitParam, programContext) {
|
|
104
117
|
emit = emitParam;
|
|
@@ -126,6 +139,7 @@ export function registerAgentsCommand(program, emitParam, programContext) {
|
|
|
126
139
|
.command("status")
|
|
127
140
|
.description("End-of-turn status box (name + session age + files held + peer count)")
|
|
128
141
|
.option("--json", "JSON output instead of the box")
|
|
142
|
+
.option("--end-turn", "Treat this as the turn's closing status: issue the box only when this session's held paths are committed and their repositories are pushed")
|
|
129
143
|
.option("--session-id <id>", "Lookup heartbeat by session_id directly, bypassing the ppid walk. " +
|
|
130
144
|
"Use this when calling from a hook (the hook's process tree may not lead back to Claude Code's session pid). " +
|
|
131
145
|
"The Stop hook payload includes session_id; pass it through.")
|
|
@@ -181,6 +195,14 @@ export function registerAgentsCommand(program, emitParam, programContext) {
|
|
|
181
195
|
.action((text, opts) => {
|
|
182
196
|
runSetTask(text.join(" "), opts);
|
|
183
197
|
});
|
|
198
|
+
cmd
|
|
199
|
+
.command("lifecycle <state>")
|
|
200
|
+
.description("Declare this session's task lifecycle: active, blocked, or done")
|
|
201
|
+
.option("--reason <text>", "Explain the lifecycle declaration")
|
|
202
|
+
.option("--session-id <id>", "Target the heartbeat with this session_id directly, bypassing the ppid walk.")
|
|
203
|
+
.action((state, opts) => {
|
|
204
|
+
runLifecycle(state, opts);
|
|
205
|
+
});
|
|
184
206
|
cmd
|
|
185
207
|
.command("release-claim <path>")
|
|
186
208
|
.description("Drop a file claim from your heartbeat. Operator escape hatch when a " +
|
|
@@ -203,7 +225,7 @@ export function registerAgentsCommand(program, emitParam, programContext) {
|
|
|
203
225
|
.command("wait <name>")
|
|
204
226
|
.description(`Block until a peer agent releases files (their \`files_touched\` becomes empty, OR they exit). Pair with \`${resolveBinName()} agents ping\` to coordinate hand-offs.`)
|
|
205
227
|
.option("--file <path>", "Wait only for these specific paths (repeatable)", collectPath, [])
|
|
206
|
-
.option("--timeout <dur>", "Give up after this duration; suffix s/m/h/d (e.g. 30s, 5m, 1h).
|
|
228
|
+
.option("--timeout <dur>", "Give up after this duration; suffix s/m/h/d is required (e.g. 30s, 5m, 1h). Default 60m.", "60m")
|
|
207
229
|
.option("--poll-secs <n>", "Poll interval in seconds (default 5)", "5")
|
|
208
230
|
.option("--quiet", "Suppress progress lines")
|
|
209
231
|
.option("--json", "JSON output (terminal status, only printed at exit)")
|
|
@@ -223,8 +245,8 @@ export function registerAgentsCommand(program, emitParam, programContext) {
|
|
|
223
245
|
});
|
|
224
246
|
cmd
|
|
225
247
|
.command("health")
|
|
226
|
-
.description("One-screen coord-layer health rollup: heal events,
|
|
227
|
-
"
|
|
248
|
+
.description("One-screen coord-layer health rollup: heal events, council activity, " +
|
|
249
|
+
"zombie detection, and anomalies. Designed for " +
|
|
228
250
|
"daily glance + dashboard ingestion. Reads .harnery/.")
|
|
229
251
|
.option("--since <window>", "Window (Nh | Nd). Default: 24h.", "24h")
|
|
230
252
|
.option("--json", "JSON envelope output")
|
|
@@ -236,7 +258,7 @@ export function registerAgentsCommand(program, emitParam, programContext) {
|
|
|
236
258
|
.description("Adapter wiring probe: ppid chain, comm names, pid-map anchor, sample payload paths. " +
|
|
237
259
|
"With --replay-samples, also replays every checked-in sample payload against the live " +
|
|
238
260
|
"adapter in an isolated sandbox to catch adapter / payload-shape drift. " +
|
|
239
|
-
"Complements heal-events (drift telemetry). Id:
|
|
261
|
+
"Complements heal-events (drift telemetry). Id: claude-code | cursor.")
|
|
240
262
|
.option("--json", "JSON envelope output")
|
|
241
263
|
.option("--replay-samples", "Replay docs/api/<adapter>-hooks/samples/*.json against the live adapter in an isolated sandbox. " +
|
|
242
264
|
"Exits non-zero if any sample crashes the adapter.")
|
|
@@ -359,6 +381,9 @@ function registerIdentityCommands(parent) {
|
|
|
359
381
|
"the event ledger, and the heartbeat.")
|
|
360
382
|
.option("--json", "JSON envelope output")
|
|
361
383
|
.option("--session-id <id>", "Bind the heartbeat with this session_id directly, bypassing the ppid walk.")
|
|
384
|
+
.option("--force-ancestor", "Allow assuming a persona this session's fork lineage descends from " +
|
|
385
|
+
"(default: refused, because a fork's inherited transcript asserting that " +
|
|
386
|
+
"name is not a role handoff).")
|
|
362
387
|
.action((target, opts) => {
|
|
363
388
|
runIdentityAssume(target, opts);
|
|
364
389
|
});
|
|
@@ -385,7 +410,9 @@ function runIdentityAssume(target, opts) {
|
|
|
385
410
|
process.exit(1);
|
|
386
411
|
}
|
|
387
412
|
try {
|
|
388
|
-
const result = assumeIdentity(root, owner, target
|
|
413
|
+
const result = assumeIdentity(root, owner, target, {
|
|
414
|
+
forceAncestor: opts.forceAncestor ?? false,
|
|
415
|
+
});
|
|
389
416
|
emit.data({
|
|
390
417
|
...result,
|
|
391
418
|
display_name: displayAgentName(result.name),
|
|
@@ -590,9 +617,15 @@ function runWhoami(opts) {
|
|
|
590
617
|
last_tool: hb.last_tool ?? null,
|
|
591
618
|
last_tool_target: hb.last_tool_target ?? null,
|
|
592
619
|
task: hb.task ?? null,
|
|
620
|
+
activity: activityOf(hb),
|
|
621
|
+
activity_updated_at: hb.activity_updated_at ?? null,
|
|
622
|
+
activity_source: hb.activity_source ?? null,
|
|
623
|
+
task_state: taskStateOf(hb),
|
|
624
|
+
task_state_updated_at: hb.task_state_updated_at ?? null,
|
|
625
|
+
task_state_reason: hb.task_state_reason ?? null,
|
|
593
626
|
turn_summary: hb.turn_summary ?? null,
|
|
594
627
|
turn_summary_updated_at: hb.turn_summary_updated_at ?? null,
|
|
595
|
-
platform: hb.platform ?? "
|
|
628
|
+
platform: hb.platform ?? "claude-code",
|
|
596
629
|
};
|
|
597
630
|
emit.data({ ...row, resolution_source: resolved.source, note: SUBAGENT_NOTE });
|
|
598
631
|
if (process.stdout.isTTY && !opts.json) {
|
|
@@ -679,9 +712,15 @@ function runList(opts) {
|
|
|
679
712
|
last_tool: h.last_tool ?? null,
|
|
680
713
|
last_tool_target: h.last_tool_target ?? null,
|
|
681
714
|
task: h.task ?? null,
|
|
715
|
+
activity: activityOf(h),
|
|
716
|
+
activity_updated_at: h.activity_updated_at ?? null,
|
|
717
|
+
activity_source: h.activity_source ?? null,
|
|
718
|
+
task_state: taskStateOf(h),
|
|
719
|
+
task_state_updated_at: h.task_state_updated_at ?? null,
|
|
720
|
+
task_state_reason: h.task_state_reason ?? null,
|
|
682
721
|
turn_summary: h.turn_summary ?? null,
|
|
683
722
|
turn_summary_updated_at: h.turn_summary_updated_at ?? null,
|
|
684
|
-
platform: h.platform ?? "
|
|
723
|
+
platform: h.platform ?? "claude-code",
|
|
685
724
|
});
|
|
686
725
|
continue;
|
|
687
726
|
}
|
|
@@ -704,9 +743,15 @@ function runList(opts) {
|
|
|
704
743
|
last_tool: h.last_tool ?? null,
|
|
705
744
|
last_tool_target: h.last_tool_target ?? null,
|
|
706
745
|
task: h.task ?? null,
|
|
746
|
+
activity: activityOf(h),
|
|
747
|
+
activity_updated_at: h.activity_updated_at ?? null,
|
|
748
|
+
activity_source: h.activity_source ?? null,
|
|
749
|
+
task_state: taskStateOf(h),
|
|
750
|
+
task_state_updated_at: h.task_state_updated_at ?? null,
|
|
751
|
+
task_state_reason: h.task_state_reason ?? null,
|
|
707
752
|
turn_summary: h.turn_summary ?? null,
|
|
708
753
|
turn_summary_updated_at: h.turn_summary_updated_at ?? null,
|
|
709
|
-
platform: h.platform ?? "
|
|
754
|
+
platform: h.platform ?? "claude-code",
|
|
710
755
|
});
|
|
711
756
|
}
|
|
712
757
|
// Guard missing started_at: a heartbeat seeded from a stray event can lack it
|
|
@@ -730,9 +775,15 @@ function runList(opts) {
|
|
|
730
775
|
last_tool: a.last_tool ?? null,
|
|
731
776
|
last_tool_target: null,
|
|
732
777
|
task: a.task ?? null,
|
|
778
|
+
activity: a.activity ?? "unknown",
|
|
779
|
+
activity_updated_at: null,
|
|
780
|
+
activity_source: null,
|
|
781
|
+
task_state: a.task_state ?? "active",
|
|
782
|
+
task_state_updated_at: null,
|
|
783
|
+
task_state_reason: a.task_state_reason ?? null,
|
|
733
784
|
turn_summary: a.turn_summary ?? null,
|
|
734
785
|
turn_summary_updated_at: null,
|
|
735
|
-
platform: a.platform ?? "
|
|
786
|
+
platform: a.platform ?? "claude-code",
|
|
736
787
|
machine: rm.machine,
|
|
737
788
|
});
|
|
738
789
|
}
|
|
@@ -975,6 +1026,12 @@ async function runShow(name, opts) {
|
|
|
975
1026
|
age_secs: ageSecs,
|
|
976
1027
|
last_heartbeat_secs_ago: heartbeatAgeSecs,
|
|
977
1028
|
task: hb.task ?? null,
|
|
1029
|
+
activity: activityOf(hb),
|
|
1030
|
+
activity_updated_at: hb.activity_updated_at ?? null,
|
|
1031
|
+
activity_source: hb.activity_source ?? null,
|
|
1032
|
+
task_state: taskStateOf(hb),
|
|
1033
|
+
task_state_updated_at: hb.task_state_updated_at ?? null,
|
|
1034
|
+
task_state_reason: hb.task_state_reason ?? null,
|
|
978
1035
|
turn_summary: hb.turn_summary ?? null,
|
|
979
1036
|
turn_summary_updated_at: hb.turn_summary_updated_at ?? null,
|
|
980
1037
|
title: report?.title ?? null,
|
|
@@ -1002,6 +1059,8 @@ async function runShow(name, opts) {
|
|
|
1002
1059
|
lines.push(`agent-${data.name} ${subtitle}`);
|
|
1003
1060
|
lines.push(` session ${formatAge(ageSecs)} old · kind=${data.kind} · session_id=${data.session_id.slice(0, 8)}…`);
|
|
1004
1061
|
lines.push(` last heartbeat: ${formatAge(heartbeatAgeSecs)} ago`);
|
|
1062
|
+
lines.push(` activity: ${data.activity}`);
|
|
1063
|
+
lines.push(` lifecycle: ${data.task_state}${data.task_state === "blocked" && data.task_state_reason ? `: ${data.task_state_reason}` : ""}`);
|
|
1005
1064
|
if (data.last_tool) {
|
|
1006
1065
|
const target = data.last_tool_target ? ` ${truncate(data.last_tool_target, 80)}` : "";
|
|
1007
1066
|
lines.push(` last activity: ${data.last_tool}${target}`);
|
|
@@ -1168,15 +1227,17 @@ function runSetTask(task, opts) {
|
|
|
1168
1227
|
});
|
|
1169
1228
|
process.exit(1);
|
|
1170
1229
|
}
|
|
1171
|
-
// Snapshot BEFORE the mutation:
|
|
1172
|
-
//
|
|
1173
|
-
//
|
|
1174
|
-
//
|
|
1175
|
-
//
|
|
1176
|
-
//
|
|
1177
|
-
// (a deliberate clear), which carries a stamp and does NOT re-trigger.
|
|
1230
|
+
// Snapshot BEFORE the mutation: the writer stamps `suggested_session_name`
|
|
1231
|
+
// on the session's first NON-EMPTY declaration (one source of truth: the
|
|
1232
|
+
// focus you declare names the session). A bare clear never consumes the
|
|
1233
|
+
// naming window, and subagent/workflow kinds are never named — see
|
|
1234
|
+
// heartbeat-writer.setTask. This call is the naming call exactly when the
|
|
1235
|
+
// stamp appears across the mutation.
|
|
1178
1236
|
const priorHb = readHeartbeat(myOwner);
|
|
1179
|
-
const
|
|
1237
|
+
const normalizedTask = task.length > 0 ? task : undefined;
|
|
1238
|
+
const lifecycleWarning = priorHb && (priorHb.task_state ?? "active") !== "active" && priorHb.task !== normalizedTask
|
|
1239
|
+
? `Task text changed while lifecycle is ${priorHb.task_state}; run \`${resolveBinName(root)} agents lifecycle active\` to reopen it explicitly.`
|
|
1240
|
+
: null;
|
|
1180
1241
|
// Heartbeat mutation goes through agent-coord (atomic temp+rename).
|
|
1181
1242
|
const helper = agentCoordOrExit(root);
|
|
1182
1243
|
const result = spawnSync(helper, ["set-task", myOwner, task], {
|
|
@@ -1191,38 +1252,172 @@ function runSetTask(task, opts) {
|
|
|
1191
1252
|
process.exit(1);
|
|
1192
1253
|
}
|
|
1193
1254
|
const hb = readHeartbeat(myOwner);
|
|
1255
|
+
// The naming call: the mutation just stamped `suggested_session_name` for
|
|
1256
|
+
// the first time. `first_of_session: true` means exactly "this call produced
|
|
1257
|
+
// the session name" — it is never true with a null name (an empty first
|
|
1258
|
+
// declaration or a `--session-id` relay both keep the window open instead).
|
|
1259
|
+
const suggestedName = !priorHb?.suggested_session_name && hb?.suggested_session_name
|
|
1260
|
+
? hb.suggested_session_name
|
|
1261
|
+
: null;
|
|
1262
|
+
const firstOfSession = suggestedName !== null;
|
|
1194
1263
|
emitCanonical({
|
|
1195
1264
|
type: "state.task_set",
|
|
1196
1265
|
owner: myOwner,
|
|
1197
1266
|
session: hb?.session_id ?? myOwner,
|
|
1198
1267
|
adapter: normalizeAdapter(hb?.platform),
|
|
1199
|
-
data: {
|
|
1268
|
+
data: {
|
|
1269
|
+
task,
|
|
1270
|
+
cleared: !task || task.length === 0,
|
|
1271
|
+
first_of_session: firstOfSession,
|
|
1272
|
+
suggested_session_name: suggestedName,
|
|
1273
|
+
},
|
|
1200
1274
|
});
|
|
1201
|
-
// On the first focus declaration of a session, surface a suggested session
|
|
1202
|
-
// name for the operator to set as their adapter tab title. Read the result
|
|
1203
|
-
// (`suggested_session_name`) and reproduce it in a fenced code block so the
|
|
1204
|
-
// UI's Copy button hands over the exact string. null on every later call.
|
|
1205
|
-
const built = firstOfSession && !opts?.sessionId ? buildSuggestedName(hb?.name ?? "unknown", [task]) : null;
|
|
1206
1275
|
emit.data({
|
|
1207
1276
|
instance_id: myOwner,
|
|
1208
1277
|
name: hb?.name ?? null,
|
|
1209
1278
|
task: hb?.task ?? null,
|
|
1210
1279
|
cleared: !task || task.length === 0,
|
|
1211
1280
|
first_of_session: firstOfSession,
|
|
1212
|
-
suggested_session_name:
|
|
1281
|
+
suggested_session_name: suggestedName,
|
|
1282
|
+
...(lifecycleWarning ? { warning: lifecycleWarning } : {}),
|
|
1283
|
+
// Right-time instruction: the UserPromptSubmit nudge fires before the name
|
|
1284
|
+
// exists; this result is the moment the model holds the string.
|
|
1285
|
+
...(suggestedName
|
|
1286
|
+
? {
|
|
1287
|
+
note: "Reproduce suggested_session_name verbatim, by itself, in a fenced code block at the top of your reply — the operator copies it as the session/tab title.",
|
|
1288
|
+
}
|
|
1289
|
+
: {}),
|
|
1213
1290
|
});
|
|
1214
1291
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
return
|
|
1224
|
-
|
|
1225
|
-
|
|
1292
|
+
function runLifecycle(rawState, opts) {
|
|
1293
|
+
const state = rawState.trim().toLowerCase();
|
|
1294
|
+
if (state !== "active" && state !== "blocked" && state !== "done") {
|
|
1295
|
+
emit.error({
|
|
1296
|
+
code: "invalid_lifecycle_state",
|
|
1297
|
+
message: "lifecycle state must be one of: active, blocked, done",
|
|
1298
|
+
});
|
|
1299
|
+
process.exitCode = 1;
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
const root = monorepoRoot();
|
|
1303
|
+
if (!root) {
|
|
1304
|
+
emit.error({
|
|
1305
|
+
code: "not_in_repo",
|
|
1306
|
+
message: "not in an agent session; coord_root() returned null",
|
|
1307
|
+
});
|
|
1308
|
+
process.exitCode = 1;
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
if (!opts.sessionId)
|
|
1312
|
+
ensureCursorSession(root);
|
|
1313
|
+
const myOwner = opts.sessionId ?? resolveOwner();
|
|
1314
|
+
if (!myOwner) {
|
|
1315
|
+
emit.error({
|
|
1316
|
+
code: "no_pidmap_entry",
|
|
1317
|
+
message: "not in an agent session; ppid walk found no pid-map entry (pass --session-id to bypass)",
|
|
1318
|
+
});
|
|
1319
|
+
process.exitCode = 1;
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
const hb = readHeartbeat(myOwner);
|
|
1323
|
+
if (!hb) {
|
|
1324
|
+
emit.error({ code: "no_heartbeat", message: noHeartbeatMessage(myOwner) });
|
|
1325
|
+
process.exitCode = 1;
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
if (hb.kind === "subagent" || hb.kind === "transient" || hb.workflow_run_id) {
|
|
1329
|
+
emit.error({
|
|
1330
|
+
code: "lifecycle_not_human_facing",
|
|
1331
|
+
message: "task lifecycle declarations are limited to human-facing sessions",
|
|
1332
|
+
});
|
|
1333
|
+
process.exitCode = 1;
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
const reason = opts.reason?.trim() || undefined;
|
|
1337
|
+
if (state === "blocked" && !reason) {
|
|
1338
|
+
emit.error({
|
|
1339
|
+
code: "blocked_reason_required",
|
|
1340
|
+
message: "lifecycle blocked requires --reason <text>",
|
|
1341
|
+
});
|
|
1342
|
+
process.exitCode = 1;
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
if (state === "done" && !hb.task) {
|
|
1346
|
+
emit.error({
|
|
1347
|
+
code: "task_required_for_done",
|
|
1348
|
+
message: "declare a current task with agents set-task before marking it done",
|
|
1349
|
+
});
|
|
1350
|
+
process.exitCode = 1;
|
|
1351
|
+
return;
|
|
1352
|
+
}
|
|
1353
|
+
const priorState = hb.task_state ?? "active";
|
|
1354
|
+
const priorReason = hb.task_state_reason || undefined;
|
|
1355
|
+
if (priorState === state && priorReason === reason) {
|
|
1356
|
+
emit.data({
|
|
1357
|
+
instance_id: myOwner,
|
|
1358
|
+
task_state: state,
|
|
1359
|
+
reason: reason ?? null,
|
|
1360
|
+
changed: false,
|
|
1361
|
+
name_reminted: false,
|
|
1362
|
+
git_finalization_checked: false,
|
|
1363
|
+
});
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
let finalization = null;
|
|
1367
|
+
if (state === "done") {
|
|
1368
|
+
const history = readSessionWriteClaims(root, hb.instance_id, hb.session_id ?? myOwner);
|
|
1369
|
+
const touchedPaths = [...new Set([...(hb.files_touched ?? []), ...history.paths])];
|
|
1370
|
+
finalization = checkGitFinalization(root, touchedPaths, {
|
|
1371
|
+
claimHistoryComplete: history.complete,
|
|
1372
|
+
});
|
|
1373
|
+
if (!finalization.ok) {
|
|
1374
|
+
emit.error({
|
|
1375
|
+
code: "git_not_finalized",
|
|
1376
|
+
message: formatGitFinalizationFailure(finalization, resolveBinName(root)).replace("the status box was not issued", "lifecycle was not changed"),
|
|
1377
|
+
});
|
|
1378
|
+
process.exitCode = 1;
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
const suggestedName = buildLifecycleSuggestedName(hb.name ?? "unknown", hb.task, state);
|
|
1383
|
+
const nameReminted = suggestedName !== null && suggestedName !== hb.suggested_session_name;
|
|
1384
|
+
const emitted = emitCanonical({
|
|
1385
|
+
type: "state.task_state",
|
|
1386
|
+
owner: myOwner,
|
|
1387
|
+
session: hb.session_id ?? myOwner,
|
|
1388
|
+
adapter: normalizeAdapter(hb.platform),
|
|
1389
|
+
data: {
|
|
1390
|
+
state,
|
|
1391
|
+
reason: reason ?? null,
|
|
1392
|
+
prior_state: priorState,
|
|
1393
|
+
name_reminted: nameReminted,
|
|
1394
|
+
git_finalization_checked: finalization !== null,
|
|
1395
|
+
...(nameReminted ? { suggested_session_name: suggestedName } : {}),
|
|
1396
|
+
},
|
|
1397
|
+
});
|
|
1398
|
+
if (!emitted) {
|
|
1399
|
+
emit.error({
|
|
1400
|
+
code: "lifecycle_event_failed",
|
|
1401
|
+
message: "the lifecycle event could not be recorded; heartbeat state was left unchanged",
|
|
1402
|
+
});
|
|
1403
|
+
process.exitCode = 1;
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
emit.data({
|
|
1407
|
+
instance_id: myOwner,
|
|
1408
|
+
task_state: state,
|
|
1409
|
+
prior_state: priorState,
|
|
1410
|
+
reason: reason ?? null,
|
|
1411
|
+
changed: true,
|
|
1412
|
+
name_reminted: nameReminted,
|
|
1413
|
+
suggested_session_name: nameReminted ? suggestedName : null,
|
|
1414
|
+
git_finalization_checked: finalization !== null,
|
|
1415
|
+
...(nameReminted
|
|
1416
|
+
? {
|
|
1417
|
+
note: "Reproduce suggested_session_name verbatim, by itself, in a fenced code block at the top of your reply — the operator copies it as the session/tab title.",
|
|
1418
|
+
}
|
|
1419
|
+
: {}),
|
|
1420
|
+
});
|
|
1226
1421
|
}
|
|
1227
1422
|
function runSuggestName(descriptionParts, opts) {
|
|
1228
1423
|
const root = monorepoRoot();
|
|
@@ -1308,20 +1503,21 @@ function runStatus(opts) {
|
|
|
1308
1503
|
});
|
|
1309
1504
|
process.exit(1);
|
|
1310
1505
|
}
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
spawnSync(helper, ["stamp-status-call", myOwner], {
|
|
1318
|
-
encoding: "utf8",
|
|
1319
|
-
timeout: 2000,
|
|
1320
|
-
...coordHelperOpts(root),
|
|
1506
|
+
let finalization = null;
|
|
1507
|
+
if (opts.endTurn) {
|
|
1508
|
+
const history = readSessionWriteClaims(root, hb.instance_id, hb.session_id ?? myOwner);
|
|
1509
|
+
const touchedPaths = [...new Set([...(hb.files_touched ?? []), ...history.paths])];
|
|
1510
|
+
finalization = checkGitFinalization(root, touchedPaths, {
|
|
1511
|
+
claimHistoryComplete: history.complete,
|
|
1321
1512
|
});
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1513
|
+
if (!finalization.ok) {
|
|
1514
|
+
emit.error({
|
|
1515
|
+
code: "git_not_finalized",
|
|
1516
|
+
message: formatGitFinalizationFailure(finalization, resolveBinName(root)),
|
|
1517
|
+
});
|
|
1518
|
+
process.exitCode = 1;
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1325
1521
|
}
|
|
1326
1522
|
emitCanonical({
|
|
1327
1523
|
type: "state.status_checked",
|
|
@@ -1330,6 +1526,7 @@ function runStatus(opts) {
|
|
|
1330
1526
|
adapter: normalizeAdapter(hb.platform),
|
|
1331
1527
|
data: {
|
|
1332
1528
|
format: opts.json ? "json" : "box",
|
|
1529
|
+
git_finalization_checked: opts.endTurn === true,
|
|
1333
1530
|
agent_count: 0, // computed below, not yet available here; Phase 5 verdict reads owner-scope only
|
|
1334
1531
|
included_self: true,
|
|
1335
1532
|
},
|
|
@@ -1411,6 +1608,12 @@ function runStatus(opts) {
|
|
|
1411
1608
|
instance_id: hb.instance_id,
|
|
1412
1609
|
kind: normalizeKind(hb.kind),
|
|
1413
1610
|
session_age_secs: ageSecs,
|
|
1611
|
+
activity: activityOf(hb),
|
|
1612
|
+
activity_updated_at: hb.activity_updated_at ?? null,
|
|
1613
|
+
activity_source: hb.activity_source ?? null,
|
|
1614
|
+
task_state: taskStateOf(hb),
|
|
1615
|
+
task_state_updated_at: hb.task_state_updated_at ?? null,
|
|
1616
|
+
task_state_reason: hb.task_state_reason ?? null,
|
|
1414
1617
|
files_held: filesHeld,
|
|
1415
1618
|
peers_live: livePeers.length,
|
|
1416
1619
|
peers_stale: peersStale,
|
|
@@ -1424,6 +1627,9 @@ function runStatus(opts) {
|
|
|
1424
1627
|
agents: m.agents.map((a) => ({
|
|
1425
1628
|
name: a.name || "unnamed",
|
|
1426
1629
|
task: a.task ?? null,
|
|
1630
|
+
activity: a.activity ?? "unknown",
|
|
1631
|
+
task_state: a.task_state ?? "active",
|
|
1632
|
+
task_state_reason: a.task_state_reason ?? null,
|
|
1427
1633
|
files: a.files_touched?.length ?? 0,
|
|
1428
1634
|
})),
|
|
1429
1635
|
})),
|
|
@@ -1432,6 +1638,7 @@ function runStatus(opts) {
|
|
|
1432
1638
|
context_window: ctxUsage?.window ?? null,
|
|
1433
1639
|
timestamp_iso: new Date().toISOString(),
|
|
1434
1640
|
timestamp_local: timeStr,
|
|
1641
|
+
...(finalization ? { finalization } : {}),
|
|
1435
1642
|
};
|
|
1436
1643
|
if (opts.json) {
|
|
1437
1644
|
emit.config({ format: "json" });
|
|
@@ -1440,6 +1647,8 @@ function runStatus(opts) {
|
|
|
1440
1647
|
}
|
|
1441
1648
|
const rows = [
|
|
1442
1649
|
["session", formatAge(ageSecs)],
|
|
1650
|
+
["activity", activityOf(hb)],
|
|
1651
|
+
["lifecycle", lifecycleLabel(hb)],
|
|
1443
1652
|
["context", ctxStr],
|
|
1444
1653
|
["files", filesStr],
|
|
1445
1654
|
["peers", peersStr],
|
|
@@ -1794,16 +2003,15 @@ function readCanonicalEventsInWindow(root, cutoffMs) {
|
|
|
1794
2003
|
}
|
|
1795
2004
|
return out;
|
|
1796
2005
|
}
|
|
1797
|
-
/** adapter
|
|
1798
|
-
* existing formatPlatformLabel rendering keeps working unchanged. */
|
|
2006
|
+
/** Normalize adapter event data into the heartbeat platform value. */
|
|
1799
2007
|
function adapterToPlatform(adapter) {
|
|
1800
2008
|
if (adapter === "claude-code")
|
|
1801
|
-
return "
|
|
2009
|
+
return "claude-code";
|
|
1802
2010
|
if (adapter === "cursor")
|
|
1803
2011
|
return "cursor";
|
|
1804
2012
|
if (adapter === "codex")
|
|
1805
2013
|
return "codex";
|
|
1806
|
-
return "
|
|
2014
|
+
return "claude-code";
|
|
1807
2015
|
}
|
|
1808
2016
|
/** Project a canonical health.* event into the HealEvent shape the aggregators
|
|
1809
2017
|
* already consume. Returns null for non-heal events. instance_id → display name
|
|
@@ -2123,6 +2331,12 @@ function traceLine(ev, allTools) {
|
|
|
2123
2331
|
case "state.task_set":
|
|
2124
2332
|
detail = d.cleared ? "(cleared)" : clip(s("task"));
|
|
2125
2333
|
break;
|
|
2334
|
+
case "state.task_state":
|
|
2335
|
+
detail = `${s("prior_state") || "active"} → ${s("state") || "active"}${s("reason") ? ` · ${clip(s("reason"), 55)}` : ""}`;
|
|
2336
|
+
break;
|
|
2337
|
+
case "interaction.input_requested":
|
|
2338
|
+
detail = s("request_kind") || s("tool_name") || "operator input requested";
|
|
2339
|
+
break;
|
|
2126
2340
|
case "state.status_checked":
|
|
2127
2341
|
detail = "status box rendered";
|
|
2128
2342
|
break;
|
|
@@ -2236,6 +2450,7 @@ function runTrace(name, opts) {
|
|
|
2236
2450
|
}
|
|
2237
2451
|
}
|
|
2238
2452
|
const events = byId.get(resolvedId) ?? [];
|
|
2453
|
+
const state = foldSessionState(events, { instance_id: resolvedId });
|
|
2239
2454
|
const lines = events
|
|
2240
2455
|
.map((ev) => traceLine(ev, !!opts.allTools))
|
|
2241
2456
|
.filter((l) => l !== null)
|
|
@@ -2249,6 +2464,12 @@ function runTrace(name, opts) {
|
|
|
2249
2464
|
instance_id: resolvedId,
|
|
2250
2465
|
other_instances: candidateIds.filter((id) => id !== resolvedId),
|
|
2251
2466
|
total_events: events.length,
|
|
2467
|
+
activity: state.activity,
|
|
2468
|
+
activity_updated_at: state.activity_updated_at ?? null,
|
|
2469
|
+
activity_source: state.activity_source ?? null,
|
|
2470
|
+
task_state: state.task_state,
|
|
2471
|
+
task_state_updated_at: state.task_state_updated_at ?? null,
|
|
2472
|
+
task_state_reason: state.task_state_reason ?? null,
|
|
2252
2473
|
shown: shown.length,
|
|
2253
2474
|
entries: shown,
|
|
2254
2475
|
};
|
|
@@ -2259,6 +2480,7 @@ function runTrace(name, opts) {
|
|
|
2259
2480
|
emit.data(result);
|
|
2260
2481
|
const header = `Trace: agent-${displayName} (${resolvedId.slice(0, 8)}…) ${events.length} events${result.other_instances.length ? ` · ${result.other_instances.length} older instance(s) of this name` : ""}`;
|
|
2261
2482
|
process.stdout.write(`${header}\n`); // lint-ok-emission: human trace view
|
|
2483
|
+
process.stdout.write(` activity=${state.activity} · lifecycle=${state.task_state}${state.task_state === "blocked" && state.task_state_reason ? `: ${state.task_state_reason}` : ""}\n`); // lint-ok-emission: human trace view
|
|
2262
2484
|
if (shown.length === 0) {
|
|
2263
2485
|
process.stdout.write(" (no events)\n"); // lint-ok-emission: human trace view
|
|
2264
2486
|
return;
|
|
@@ -2292,18 +2514,7 @@ function runHealth(opts) {
|
|
|
2292
2514
|
const councilsDir = resolve(root, ".harnery/councils");
|
|
2293
2515
|
// Coordination telemetry reads from the canonical events.ndjson stream.
|
|
2294
2516
|
// Heals come from health.*; council window-activity from council.*. The
|
|
2295
|
-
// commit-guard + schema-invalid counters below have NO canonical equivalent
|
|
2296
|
-
// yet. Their future home is the decision.* events (defined in schema.ts, not
|
|
2297
|
-
// yet wired). Until then they report 0 (fields kept for output-shape
|
|
2298
|
-
// compatibility).
|
|
2299
2517
|
const heal = [];
|
|
2300
|
-
const schemaInvalid = 0;
|
|
2301
|
-
const schemaSamples = [];
|
|
2302
|
-
const commitBlocked = 0;
|
|
2303
|
-
const commitBypassed = 0;
|
|
2304
|
-
const commitSuppressed = 0;
|
|
2305
|
-
const editBlocked = 0;
|
|
2306
|
-
const shellCandidates = 0;
|
|
2307
2518
|
let councilAdvanced = 0;
|
|
2308
2519
|
let councilClosed = 0;
|
|
2309
2520
|
let councilArchived = 0;
|
|
@@ -2420,10 +2631,6 @@ function runHealth(opts) {
|
|
|
2420
2631
|
}
|
|
2421
2632
|
// Anomaly detection.
|
|
2422
2633
|
const anomalies = [];
|
|
2423
|
-
if (schemaInvalid > 0) {
|
|
2424
|
-
const sampleList = schemaSamples.slice(0, 3).join(", ");
|
|
2425
|
-
anomalies.push(`HEARTBEAT_SCHEMA_INVALID fired ${schemaInvalid}x; heartbeat shape failed validation${sampleList ? ` (samples: ${sampleList})` : ""}`);
|
|
2426
|
-
}
|
|
2427
2634
|
for (const { agent, count } of healTopAgents) {
|
|
2428
2635
|
if (count >= 5) {
|
|
2429
2636
|
anomalies.push(`${agent} self-healed ${count}x in ${opts.since}; possible idle-prune loop or PID instability`);
|
|
@@ -2477,14 +2684,6 @@ function runHealth(opts) {
|
|
|
2477
2684
|
by_platform: healByPlatform,
|
|
2478
2685
|
top_agents: healTopAgents,
|
|
2479
2686
|
},
|
|
2480
|
-
schema_invalid: { count: schemaInvalid, samples: schemaSamples },
|
|
2481
|
-
commit_guards: {
|
|
2482
|
-
blocked: commitBlocked,
|
|
2483
|
-
bypassed: commitBypassed,
|
|
2484
|
-
suppressed: commitSuppressed,
|
|
2485
|
-
edit_blocked: editBlocked,
|
|
2486
|
-
shell_candidates: shellCandidates,
|
|
2487
|
-
},
|
|
2488
2687
|
councils: {
|
|
2489
2688
|
active: activeCouncils,
|
|
2490
2689
|
archived_in_window: councilArchived,
|
|
@@ -2523,17 +2722,6 @@ function renderHealthBox(report) {
|
|
|
2523
2722
|
}
|
|
2524
2723
|
const topHealer = report.heal_events.top_agents[0];
|
|
2525
2724
|
const topHealerStr = topHealer ? `${topHealer.agent} x${topHealer.count}` : "(none)";
|
|
2526
|
-
const guardParts = [];
|
|
2527
|
-
if (report.commit_guards.blocked > 0)
|
|
2528
|
-
guardParts.push(`blocked ${report.commit_guards.blocked}`);
|
|
2529
|
-
if (report.commit_guards.bypassed > 0)
|
|
2530
|
-
guardParts.push(`bypassed ${report.commit_guards.bypassed}`);
|
|
2531
|
-
if (report.commit_guards.suppressed > 0)
|
|
2532
|
-
guardParts.push(`suppressed ${report.commit_guards.suppressed}`);
|
|
2533
|
-
if (report.commit_guards.edit_blocked > 0)
|
|
2534
|
-
guardParts.push(`edit-blocked ${report.commit_guards.edit_blocked}`);
|
|
2535
|
-
if (report.commit_guards.shell_candidates > 0)
|
|
2536
|
-
guardParts.push(`shell ${report.commit_guards.shell_candidates}`);
|
|
2537
2725
|
const councilParts = [`${report.councils.active} active`];
|
|
2538
2726
|
if (report.councils.advanced_in_window > 0)
|
|
2539
2727
|
councilParts.push(`${report.councils.advanced_in_window} advanced`);
|
|
@@ -2566,8 +2754,6 @@ function renderHealthBox(report) {
|
|
|
2566
2754
|
? "0"
|
|
2567
2755
|
: `${report.zombies.count} (${report.zombies.samples.join(", ")})`,
|
|
2568
2756
|
],
|
|
2569
|
-
["schema invalid", String(report.schema_invalid.count)],
|
|
2570
|
-
["commit guards", guardParts.length ? guardParts.join(", ") : "0"],
|
|
2571
2757
|
["councils", councilParts.join(", ")],
|
|
2572
2758
|
["anomalies", report.anomalies.length === 0 ? "(clean)" : `${report.anomalies.length} flagged`],
|
|
2573
2759
|
];
|
|
@@ -2585,10 +2771,10 @@ function runAdapterProbe(id, opts) {
|
|
|
2585
2771
|
if (opts.json)
|
|
2586
2772
|
emit.config({ format: "json" });
|
|
2587
2773
|
const adapter = id.trim();
|
|
2588
|
-
if (adapter !== "
|
|
2774
|
+
if (adapter !== "claude-code" && adapter !== "cursor") {
|
|
2589
2775
|
emit.error({
|
|
2590
2776
|
code: "bad_adapter",
|
|
2591
|
-
message: "adapter id must be
|
|
2777
|
+
message: "adapter id must be claude-code or cursor",
|
|
2592
2778
|
});
|
|
2593
2779
|
process.exit(1);
|
|
2594
2780
|
}
|
|
@@ -2784,7 +2970,7 @@ function replayAdapterSamples(adapter, root, relativeSampleDir, filter) {
|
|
|
2784
2970
|
stopFailure: "stop-failure",
|
|
2785
2971
|
StopFailure: "stop-failure",
|
|
2786
2972
|
};
|
|
2787
|
-
const adapterFlag = adapter
|
|
2973
|
+
const adapterFlag = adapter;
|
|
2788
2974
|
const sandbox = mkdtempSync(join(tmpdir(), "harn-adapter-probe-"));
|
|
2789
2975
|
const results = [];
|
|
2790
2976
|
try {
|
|
@@ -2909,15 +3095,15 @@ function extractEventAndPayload(parsed, filename) {
|
|
|
2909
3095
|
function collectPath(value, prev) {
|
|
2910
3096
|
return [...prev, value];
|
|
2911
3097
|
}
|
|
2912
|
-
/** Parse "
|
|
3098
|
+
/** Parse "30s", "5m", "1h", "2d" into milliseconds. */
|
|
2913
3099
|
function parseDurationToMs(input) {
|
|
2914
|
-
const match = input.trim().match(/^(\d+)([smhd]
|
|
3100
|
+
const match = input.trim().match(/^(\d+)([smhd])$/i);
|
|
2915
3101
|
if (!match)
|
|
2916
3102
|
return null;
|
|
2917
3103
|
const n = Number.parseInt(match[1], 10);
|
|
2918
3104
|
if (!Number.isFinite(n) || n <= 0)
|
|
2919
3105
|
return null;
|
|
2920
|
-
const unit =
|
|
3106
|
+
const unit = match[2].toLowerCase();
|
|
2921
3107
|
const mult = { s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000 };
|
|
2922
3108
|
return n * mult[unit];
|
|
2923
3109
|
}
|