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/src/commands/agents.ts
CHANGED
|
@@ -30,6 +30,12 @@ import type { Command } from "commander";
|
|
|
30
30
|
import type { EmitContext, HarneryProgramContext } from "../commander.ts";
|
|
31
31
|
import { coordBinPath } from "../core/agents/coord-bin.ts";
|
|
32
32
|
import { readStreamTailBounded } from "../core/agents/events/consume.ts";
|
|
33
|
+
import {
|
|
34
|
+
checkGitFinalization,
|
|
35
|
+
formatGitFinalizationFailure,
|
|
36
|
+
type GitFinalizationResult,
|
|
37
|
+
readSessionWriteClaims,
|
|
38
|
+
} from "../core/agents/finalization.ts";
|
|
33
39
|
import {
|
|
34
40
|
emitCanonical,
|
|
35
41
|
type Heartbeat,
|
|
@@ -40,6 +46,15 @@ import {
|
|
|
40
46
|
resolveOwnerBySessionEnv,
|
|
41
47
|
resolveOwnerWithSource,
|
|
42
48
|
} from "../core/agents/index.ts";
|
|
49
|
+
import {
|
|
50
|
+
buildLifecycleSuggestedName,
|
|
51
|
+
buildSuggestedName,
|
|
52
|
+
} from "../core/agents/state/heartbeat-writer.ts";
|
|
53
|
+
import {
|
|
54
|
+
type AgentActivity,
|
|
55
|
+
foldSessionState,
|
|
56
|
+
type TaskState,
|
|
57
|
+
} from "../core/agents/state/session-state.ts";
|
|
43
58
|
import { coordFreshnessSeconds, resolveBinName } from "../core/config.ts";
|
|
44
59
|
import { type RemoteMachine, readRemoteMachines } from "../core/presence/index.ts";
|
|
45
60
|
import { registerContextCommand } from "./context.ts";
|
|
@@ -175,6 +190,12 @@ interface Row {
|
|
|
175
190
|
last_tool?: string | null;
|
|
176
191
|
last_tool_target?: string | null;
|
|
177
192
|
task?: string | null;
|
|
193
|
+
activity: AgentActivity;
|
|
194
|
+
activity_updated_at?: string | null;
|
|
195
|
+
activity_source?: string | null;
|
|
196
|
+
task_state: TaskState;
|
|
197
|
+
task_state_updated_at?: string | null;
|
|
198
|
+
task_state_reason?: string | null;
|
|
178
199
|
turn_summary?: string | null;
|
|
179
200
|
turn_summary_updated_at?: string | null;
|
|
180
201
|
platform?: string | null;
|
|
@@ -183,6 +204,19 @@ interface Row {
|
|
|
183
204
|
machine?: string | null;
|
|
184
205
|
}
|
|
185
206
|
|
|
207
|
+
function activityOf(hb: Pick<Heartbeat, "activity">): AgentActivity {
|
|
208
|
+
return hb.activity ?? "unknown";
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function taskStateOf(hb: Pick<Heartbeat, "task_state">): TaskState {
|
|
212
|
+
return hb.task_state ?? "active";
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function lifecycleLabel(hb: Pick<Heartbeat, "task_state" | "task_state_reason">): string {
|
|
216
|
+
const state = taskStateOf(hb);
|
|
217
|
+
return state === "blocked" && hb.task_state_reason ? `${state}: ${hb.task_state_reason}` : state;
|
|
218
|
+
}
|
|
219
|
+
|
|
186
220
|
let emit: EmitContext;
|
|
187
221
|
|
|
188
222
|
export function registerAgentsCommand(
|
|
@@ -221,13 +255,17 @@ export function registerAgentsCommand(
|
|
|
221
255
|
.command("status")
|
|
222
256
|
.description("End-of-turn status box (name + session age + files held + peer count)")
|
|
223
257
|
.option("--json", "JSON output instead of the box")
|
|
258
|
+
.option(
|
|
259
|
+
"--end-turn",
|
|
260
|
+
"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",
|
|
261
|
+
)
|
|
224
262
|
.option(
|
|
225
263
|
"--session-id <id>",
|
|
226
264
|
"Lookup heartbeat by session_id directly, bypassing the ppid walk. " +
|
|
227
265
|
"Use this when calling from a hook (the hook's process tree may not lead back to Claude Code's session pid). " +
|
|
228
266
|
"The Stop hook payload includes session_id; pass it through.",
|
|
229
267
|
)
|
|
230
|
-
.action((opts: { json?: boolean; sessionId?: string }) => {
|
|
268
|
+
.action((opts: { endTurn?: boolean; json?: boolean; sessionId?: string }) => {
|
|
231
269
|
runStatus(opts);
|
|
232
270
|
});
|
|
233
271
|
|
|
@@ -306,6 +344,18 @@ export function registerAgentsCommand(
|
|
|
306
344
|
runSetTask(text.join(" "), opts);
|
|
307
345
|
});
|
|
308
346
|
|
|
347
|
+
cmd
|
|
348
|
+
.command("lifecycle <state>")
|
|
349
|
+
.description("Declare this session's task lifecycle: active, blocked, or done")
|
|
350
|
+
.option("--reason <text>", "Explain the lifecycle declaration")
|
|
351
|
+
.option(
|
|
352
|
+
"--session-id <id>",
|
|
353
|
+
"Target the heartbeat with this session_id directly, bypassing the ppid walk.",
|
|
354
|
+
)
|
|
355
|
+
.action((state: string, opts: { reason?: string; sessionId?: string }) => {
|
|
356
|
+
runLifecycle(state, opts);
|
|
357
|
+
});
|
|
358
|
+
|
|
309
359
|
cmd
|
|
310
360
|
.command("release-claim <path>")
|
|
311
361
|
.description(
|
|
@@ -343,7 +393,7 @@ export function registerAgentsCommand(
|
|
|
343
393
|
)
|
|
344
394
|
.option(
|
|
345
395
|
"--timeout <dur>",
|
|
346
|
-
"Give up after this duration; suffix s/m/h/d (e.g. 30s, 5m, 1h).
|
|
396
|
+
"Give up after this duration; suffix s/m/h/d is required (e.g. 30s, 5m, 1h). Default 60m.",
|
|
347
397
|
"60m",
|
|
348
398
|
)
|
|
349
399
|
.option("--poll-secs <n>", "Poll interval in seconds (default 5)", "5")
|
|
@@ -381,8 +431,8 @@ export function registerAgentsCommand(
|
|
|
381
431
|
cmd
|
|
382
432
|
.command("health")
|
|
383
433
|
.description(
|
|
384
|
-
"One-screen coord-layer health rollup: heal events,
|
|
385
|
-
"
|
|
434
|
+
"One-screen coord-layer health rollup: heal events, council activity, " +
|
|
435
|
+
"zombie detection, and anomalies. Designed for " +
|
|
386
436
|
"daily glance + dashboard ingestion. Reads .harnery/.",
|
|
387
437
|
)
|
|
388
438
|
.option("--since <window>", "Window (Nh | Nd). Default: 24h.", "24h")
|
|
@@ -397,7 +447,7 @@ export function registerAgentsCommand(
|
|
|
397
447
|
"Adapter wiring probe: ppid chain, comm names, pid-map anchor, sample payload paths. " +
|
|
398
448
|
"With --replay-samples, also replays every checked-in sample payload against the live " +
|
|
399
449
|
"adapter in an isolated sandbox to catch adapter / payload-shape drift. " +
|
|
400
|
-
"Complements heal-events (drift telemetry). Id:
|
|
450
|
+
"Complements heal-events (drift telemetry). Id: claude-code | cursor.",
|
|
401
451
|
)
|
|
402
452
|
.option("--json", "JSON envelope output")
|
|
403
453
|
.option(
|
|
@@ -552,12 +602,23 @@ function registerIdentityCommands(parent: Command): void {
|
|
|
552
602
|
"--session-id <id>",
|
|
553
603
|
"Bind the heartbeat with this session_id directly, bypassing the ppid walk.",
|
|
554
604
|
)
|
|
555
|
-
.
|
|
556
|
-
|
|
557
|
-
|
|
605
|
+
.option(
|
|
606
|
+
"--force-ancestor",
|
|
607
|
+
"Allow assuming a persona this session's fork lineage descends from " +
|
|
608
|
+
"(default: refused, because a fork's inherited transcript asserting that " +
|
|
609
|
+
"name is not a role handoff).",
|
|
610
|
+
)
|
|
611
|
+
.action(
|
|
612
|
+
(target: string, opts: { json?: boolean; sessionId?: string; forceAncestor?: boolean }) => {
|
|
613
|
+
runIdentityAssume(target, opts);
|
|
614
|
+
},
|
|
615
|
+
);
|
|
558
616
|
}
|
|
559
617
|
|
|
560
|
-
function runIdentityAssume(
|
|
618
|
+
function runIdentityAssume(
|
|
619
|
+
target: string,
|
|
620
|
+
opts: { json?: boolean; sessionId?: string; forceAncestor?: boolean },
|
|
621
|
+
): void {
|
|
561
622
|
if (opts.json) emit.config({ format: "json" });
|
|
562
623
|
const root = monorepoRoot();
|
|
563
624
|
if (!root) {
|
|
@@ -578,7 +639,9 @@ function runIdentityAssume(target: string, opts: { json?: boolean; sessionId?: s
|
|
|
578
639
|
process.exit(1);
|
|
579
640
|
}
|
|
580
641
|
try {
|
|
581
|
-
const result = assumeIdentity(root, owner, target
|
|
642
|
+
const result = assumeIdentity(root, owner, target, {
|
|
643
|
+
forceAncestor: opts.forceAncestor ?? false,
|
|
644
|
+
});
|
|
582
645
|
emit.data({
|
|
583
646
|
...result,
|
|
584
647
|
display_name: displayAgentName(result.name),
|
|
@@ -877,9 +940,15 @@ function runWhoami(opts: { json?: boolean }): void {
|
|
|
877
940
|
last_tool: hb.last_tool ?? null,
|
|
878
941
|
last_tool_target: hb.last_tool_target ?? null,
|
|
879
942
|
task: hb.task ?? null,
|
|
943
|
+
activity: activityOf(hb),
|
|
944
|
+
activity_updated_at: hb.activity_updated_at ?? null,
|
|
945
|
+
activity_source: hb.activity_source ?? null,
|
|
946
|
+
task_state: taskStateOf(hb),
|
|
947
|
+
task_state_updated_at: hb.task_state_updated_at ?? null,
|
|
948
|
+
task_state_reason: hb.task_state_reason ?? null,
|
|
880
949
|
turn_summary: hb.turn_summary ?? null,
|
|
881
950
|
turn_summary_updated_at: hb.turn_summary_updated_at ?? null,
|
|
882
|
-
platform: hb.platform ?? "
|
|
951
|
+
platform: hb.platform ?? "claude-code",
|
|
883
952
|
};
|
|
884
953
|
|
|
885
954
|
emit.data({ ...row, resolution_source: resolved.source, note: SUBAGENT_NOTE });
|
|
@@ -975,9 +1044,15 @@ function runList(opts: { all?: boolean; stale?: boolean; json?: boolean }): void
|
|
|
975
1044
|
last_tool: h.last_tool ?? null,
|
|
976
1045
|
last_tool_target: h.last_tool_target ?? null,
|
|
977
1046
|
task: h.task ?? null,
|
|
1047
|
+
activity: activityOf(h),
|
|
1048
|
+
activity_updated_at: h.activity_updated_at ?? null,
|
|
1049
|
+
activity_source: h.activity_source ?? null,
|
|
1050
|
+
task_state: taskStateOf(h),
|
|
1051
|
+
task_state_updated_at: h.task_state_updated_at ?? null,
|
|
1052
|
+
task_state_reason: h.task_state_reason ?? null,
|
|
978
1053
|
turn_summary: h.turn_summary ?? null,
|
|
979
1054
|
turn_summary_updated_at: h.turn_summary_updated_at ?? null,
|
|
980
|
-
platform: h.platform ?? "
|
|
1055
|
+
platform: h.platform ?? "claude-code",
|
|
981
1056
|
});
|
|
982
1057
|
continue;
|
|
983
1058
|
}
|
|
@@ -1000,9 +1075,15 @@ function runList(opts: { all?: boolean; stale?: boolean; json?: boolean }): void
|
|
|
1000
1075
|
last_tool: h.last_tool ?? null,
|
|
1001
1076
|
last_tool_target: h.last_tool_target ?? null,
|
|
1002
1077
|
task: h.task ?? null,
|
|
1078
|
+
activity: activityOf(h),
|
|
1079
|
+
activity_updated_at: h.activity_updated_at ?? null,
|
|
1080
|
+
activity_source: h.activity_source ?? null,
|
|
1081
|
+
task_state: taskStateOf(h),
|
|
1082
|
+
task_state_updated_at: h.task_state_updated_at ?? null,
|
|
1083
|
+
task_state_reason: h.task_state_reason ?? null,
|
|
1003
1084
|
turn_summary: h.turn_summary ?? null,
|
|
1004
1085
|
turn_summary_updated_at: h.turn_summary_updated_at ?? null,
|
|
1005
|
-
platform: h.platform ?? "
|
|
1086
|
+
platform: h.platform ?? "claude-code",
|
|
1006
1087
|
});
|
|
1007
1088
|
}
|
|
1008
1089
|
|
|
@@ -1028,9 +1109,15 @@ function runList(opts: { all?: boolean; stale?: boolean; json?: boolean }): void
|
|
|
1028
1109
|
last_tool: a.last_tool ?? null,
|
|
1029
1110
|
last_tool_target: null,
|
|
1030
1111
|
task: a.task ?? null,
|
|
1112
|
+
activity: a.activity ?? "unknown",
|
|
1113
|
+
activity_updated_at: null,
|
|
1114
|
+
activity_source: null,
|
|
1115
|
+
task_state: a.task_state ?? "active",
|
|
1116
|
+
task_state_updated_at: null,
|
|
1117
|
+
task_state_reason: a.task_state_reason ?? null,
|
|
1031
1118
|
turn_summary: a.turn_summary ?? null,
|
|
1032
1119
|
turn_summary_updated_at: null,
|
|
1033
|
-
platform: a.platform ?? "
|
|
1120
|
+
platform: a.platform ?? "claude-code",
|
|
1034
1121
|
machine: rm.machine,
|
|
1035
1122
|
});
|
|
1036
1123
|
}
|
|
@@ -1295,6 +1382,12 @@ async function runShow(name: string, opts: { json?: boolean }): Promise<void> {
|
|
|
1295
1382
|
age_secs: ageSecs,
|
|
1296
1383
|
last_heartbeat_secs_ago: heartbeatAgeSecs,
|
|
1297
1384
|
task: hb.task ?? null,
|
|
1385
|
+
activity: activityOf(hb),
|
|
1386
|
+
activity_updated_at: hb.activity_updated_at ?? null,
|
|
1387
|
+
activity_source: hb.activity_source ?? null,
|
|
1388
|
+
task_state: taskStateOf(hb),
|
|
1389
|
+
task_state_updated_at: hb.task_state_updated_at ?? null,
|
|
1390
|
+
task_state_reason: hb.task_state_reason ?? null,
|
|
1298
1391
|
turn_summary: hb.turn_summary ?? null,
|
|
1299
1392
|
turn_summary_updated_at: hb.turn_summary_updated_at ?? null,
|
|
1300
1393
|
title: report?.title ?? null,
|
|
@@ -1326,6 +1419,10 @@ async function runShow(name: string, opts: { json?: boolean }): Promise<void> {
|
|
|
1326
1419
|
` session ${formatAge(ageSecs)} old · kind=${data.kind} · session_id=${data.session_id.slice(0, 8)}…`,
|
|
1327
1420
|
);
|
|
1328
1421
|
lines.push(` last heartbeat: ${formatAge(heartbeatAgeSecs)} ago`);
|
|
1422
|
+
lines.push(` activity: ${data.activity}`);
|
|
1423
|
+
lines.push(
|
|
1424
|
+
` lifecycle: ${data.task_state}${data.task_state === "blocked" && data.task_state_reason ? `: ${data.task_state_reason}` : ""}`,
|
|
1425
|
+
);
|
|
1329
1426
|
if (data.last_tool) {
|
|
1330
1427
|
const target = data.last_tool_target ? ` ${truncate(data.last_tool_target, 80)}` : "";
|
|
1331
1428
|
lines.push(` last activity: ${data.last_tool}${target}`);
|
|
@@ -1495,15 +1592,18 @@ function runSetTask(task: string, opts?: { sessionId?: string }): void {
|
|
|
1495
1592
|
process.exit(1);
|
|
1496
1593
|
}
|
|
1497
1594
|
|
|
1498
|
-
// Snapshot BEFORE the mutation:
|
|
1499
|
-
//
|
|
1500
|
-
//
|
|
1501
|
-
//
|
|
1502
|
-
//
|
|
1503
|
-
//
|
|
1504
|
-
// (a deliberate clear), which carries a stamp and does NOT re-trigger.
|
|
1595
|
+
// Snapshot BEFORE the mutation: the writer stamps `suggested_session_name`
|
|
1596
|
+
// on the session's first NON-EMPTY declaration (one source of truth: the
|
|
1597
|
+
// focus you declare names the session). A bare clear never consumes the
|
|
1598
|
+
// naming window, and subagent/workflow kinds are never named — see
|
|
1599
|
+
// heartbeat-writer.setTask. This call is the naming call exactly when the
|
|
1600
|
+
// stamp appears across the mutation.
|
|
1505
1601
|
const priorHb = readHeartbeat(myOwner);
|
|
1506
|
-
const
|
|
1602
|
+
const normalizedTask = task.length > 0 ? task : undefined;
|
|
1603
|
+
const lifecycleWarning =
|
|
1604
|
+
priorHb && (priorHb.task_state ?? "active") !== "active" && priorHb.task !== normalizedTask
|
|
1605
|
+
? `Task text changed while lifecycle is ${priorHb.task_state}; run \`${resolveBinName(root)} agents lifecycle active\` to reopen it explicitly.`
|
|
1606
|
+
: null;
|
|
1507
1607
|
|
|
1508
1608
|
// Heartbeat mutation goes through agent-coord (atomic temp+rename).
|
|
1509
1609
|
const helper = agentCoordOrExit(root);
|
|
@@ -1520,44 +1620,187 @@ function runSetTask(task: string, opts?: { sessionId?: string }): void {
|
|
|
1520
1620
|
}
|
|
1521
1621
|
|
|
1522
1622
|
const hb = readHeartbeat(myOwner);
|
|
1623
|
+
|
|
1624
|
+
// The naming call: the mutation just stamped `suggested_session_name` for
|
|
1625
|
+
// the first time. `first_of_session: true` means exactly "this call produced
|
|
1626
|
+
// the session name" — it is never true with a null name (an empty first
|
|
1627
|
+
// declaration or a `--session-id` relay both keep the window open instead).
|
|
1628
|
+
const suggestedName =
|
|
1629
|
+
!priorHb?.suggested_session_name && hb?.suggested_session_name
|
|
1630
|
+
? hb.suggested_session_name
|
|
1631
|
+
: null;
|
|
1632
|
+
const firstOfSession = suggestedName !== null;
|
|
1633
|
+
|
|
1523
1634
|
emitCanonical({
|
|
1524
1635
|
type: "state.task_set",
|
|
1525
1636
|
owner: myOwner,
|
|
1526
1637
|
session: hb?.session_id ?? myOwner,
|
|
1527
1638
|
adapter: normalizeAdapter(hb?.platform),
|
|
1528
|
-
data: {
|
|
1639
|
+
data: {
|
|
1640
|
+
task,
|
|
1641
|
+
cleared: !task || task.length === 0,
|
|
1642
|
+
first_of_session: firstOfSession,
|
|
1643
|
+
suggested_session_name: suggestedName,
|
|
1644
|
+
},
|
|
1529
1645
|
});
|
|
1530
1646
|
|
|
1531
|
-
// On the first focus declaration of a session, surface a suggested session
|
|
1532
|
-
// name for the operator to set as their adapter tab title. Read the result
|
|
1533
|
-
// (`suggested_session_name`) and reproduce it in a fenced code block so the
|
|
1534
|
-
// UI's Copy button hands over the exact string. null on every later call.
|
|
1535
|
-
const built =
|
|
1536
|
-
firstOfSession && !opts?.sessionId ? buildSuggestedName(hb?.name ?? "unknown", [task]) : null;
|
|
1537
|
-
|
|
1538
1647
|
emit.data({
|
|
1539
1648
|
instance_id: myOwner,
|
|
1540
1649
|
name: hb?.name ?? null,
|
|
1541
1650
|
task: hb?.task ?? null,
|
|
1542
1651
|
cleared: !task || task.length === 0,
|
|
1543
1652
|
first_of_session: firstOfSession,
|
|
1544
|
-
suggested_session_name:
|
|
1653
|
+
suggested_session_name: suggestedName,
|
|
1654
|
+
...(lifecycleWarning ? { warning: lifecycleWarning } : {}),
|
|
1655
|
+
// Right-time instruction: the UserPromptSubmit nudge fires before the name
|
|
1656
|
+
// exists; this result is the moment the model holds the string.
|
|
1657
|
+
...(suggestedName
|
|
1658
|
+
? {
|
|
1659
|
+
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.",
|
|
1660
|
+
}
|
|
1661
|
+
: {}),
|
|
1545
1662
|
});
|
|
1546
1663
|
}
|
|
1547
1664
|
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
const
|
|
1560
|
-
|
|
1665
|
+
function runLifecycle(rawState: string, opts: { reason?: string; sessionId?: string }): void {
|
|
1666
|
+
const state = rawState.trim().toLowerCase();
|
|
1667
|
+
if (state !== "active" && state !== "blocked" && state !== "done") {
|
|
1668
|
+
emit.error({
|
|
1669
|
+
code: "invalid_lifecycle_state",
|
|
1670
|
+
message: "lifecycle state must be one of: active, blocked, done",
|
|
1671
|
+
});
|
|
1672
|
+
process.exitCode = 1;
|
|
1673
|
+
return;
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
const root = monorepoRoot();
|
|
1677
|
+
if (!root) {
|
|
1678
|
+
emit.error({
|
|
1679
|
+
code: "not_in_repo",
|
|
1680
|
+
message: "not in an agent session; coord_root() returned null",
|
|
1681
|
+
});
|
|
1682
|
+
process.exitCode = 1;
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
if (!opts.sessionId) ensureCursorSession(root);
|
|
1686
|
+
const myOwner = opts.sessionId ?? resolveOwner();
|
|
1687
|
+
if (!myOwner) {
|
|
1688
|
+
emit.error({
|
|
1689
|
+
code: "no_pidmap_entry",
|
|
1690
|
+
message:
|
|
1691
|
+
"not in an agent session; ppid walk found no pid-map entry (pass --session-id to bypass)",
|
|
1692
|
+
});
|
|
1693
|
+
process.exitCode = 1;
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
const hb = readHeartbeat(myOwner);
|
|
1698
|
+
if (!hb) {
|
|
1699
|
+
emit.error({ code: "no_heartbeat", message: noHeartbeatMessage(myOwner) });
|
|
1700
|
+
process.exitCode = 1;
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
if (hb.kind === "subagent" || hb.kind === "transient" || hb.workflow_run_id) {
|
|
1704
|
+
emit.error({
|
|
1705
|
+
code: "lifecycle_not_human_facing",
|
|
1706
|
+
message: "task lifecycle declarations are limited to human-facing sessions",
|
|
1707
|
+
});
|
|
1708
|
+
process.exitCode = 1;
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
const reason = opts.reason?.trim() || undefined;
|
|
1713
|
+
if (state === "blocked" && !reason) {
|
|
1714
|
+
emit.error({
|
|
1715
|
+
code: "blocked_reason_required",
|
|
1716
|
+
message: "lifecycle blocked requires --reason <text>",
|
|
1717
|
+
});
|
|
1718
|
+
process.exitCode = 1;
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1721
|
+
if (state === "done" && !hb.task) {
|
|
1722
|
+
emit.error({
|
|
1723
|
+
code: "task_required_for_done",
|
|
1724
|
+
message: "declare a current task with agents set-task before marking it done",
|
|
1725
|
+
});
|
|
1726
|
+
process.exitCode = 1;
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
const priorState: TaskState = hb.task_state ?? "active";
|
|
1731
|
+
const priorReason = hb.task_state_reason || undefined;
|
|
1732
|
+
if (priorState === state && priorReason === reason) {
|
|
1733
|
+
emit.data({
|
|
1734
|
+
instance_id: myOwner,
|
|
1735
|
+
task_state: state,
|
|
1736
|
+
reason: reason ?? null,
|
|
1737
|
+
changed: false,
|
|
1738
|
+
name_reminted: false,
|
|
1739
|
+
git_finalization_checked: false,
|
|
1740
|
+
});
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
let finalization: GitFinalizationResult | null = null;
|
|
1745
|
+
if (state === "done") {
|
|
1746
|
+
const history = readSessionWriteClaims(root, hb.instance_id, hb.session_id ?? myOwner);
|
|
1747
|
+
const touchedPaths = [...new Set([...(hb.files_touched ?? []), ...history.paths])];
|
|
1748
|
+
finalization = checkGitFinalization(root, touchedPaths, {
|
|
1749
|
+
claimHistoryComplete: history.complete,
|
|
1750
|
+
});
|
|
1751
|
+
if (!finalization.ok) {
|
|
1752
|
+
emit.error({
|
|
1753
|
+
code: "git_not_finalized",
|
|
1754
|
+
message: formatGitFinalizationFailure(finalization, resolveBinName(root)).replace(
|
|
1755
|
+
"the status box was not issued",
|
|
1756
|
+
"lifecycle was not changed",
|
|
1757
|
+
),
|
|
1758
|
+
});
|
|
1759
|
+
process.exitCode = 1;
|
|
1760
|
+
return;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
const suggestedName = buildLifecycleSuggestedName(hb.name ?? "unknown", hb.task, state);
|
|
1765
|
+
const nameReminted = suggestedName !== null && suggestedName !== hb.suggested_session_name;
|
|
1766
|
+
const emitted = emitCanonical({
|
|
1767
|
+
type: "state.task_state",
|
|
1768
|
+
owner: myOwner,
|
|
1769
|
+
session: hb.session_id ?? myOwner,
|
|
1770
|
+
adapter: normalizeAdapter(hb.platform),
|
|
1771
|
+
data: {
|
|
1772
|
+
state,
|
|
1773
|
+
reason: reason ?? null,
|
|
1774
|
+
prior_state: priorState,
|
|
1775
|
+
name_reminted: nameReminted,
|
|
1776
|
+
git_finalization_checked: finalization !== null,
|
|
1777
|
+
...(nameReminted ? { suggested_session_name: suggestedName } : {}),
|
|
1778
|
+
},
|
|
1779
|
+
});
|
|
1780
|
+
if (!emitted) {
|
|
1781
|
+
emit.error({
|
|
1782
|
+
code: "lifecycle_event_failed",
|
|
1783
|
+
message: "the lifecycle event could not be recorded; heartbeat state was left unchanged",
|
|
1784
|
+
});
|
|
1785
|
+
process.exitCode = 1;
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
emit.data({
|
|
1790
|
+
instance_id: myOwner,
|
|
1791
|
+
task_state: state,
|
|
1792
|
+
prior_state: priorState,
|
|
1793
|
+
reason: reason ?? null,
|
|
1794
|
+
changed: true,
|
|
1795
|
+
name_reminted: nameReminted,
|
|
1796
|
+
suggested_session_name: nameReminted ? suggestedName : null,
|
|
1797
|
+
git_finalization_checked: finalization !== null,
|
|
1798
|
+
...(nameReminted
|
|
1799
|
+
? {
|
|
1800
|
+
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.",
|
|
1801
|
+
}
|
|
1802
|
+
: {}),
|
|
1803
|
+
});
|
|
1561
1804
|
}
|
|
1562
1805
|
|
|
1563
1806
|
function runSuggestName(
|
|
@@ -1624,7 +1867,7 @@ function runSuggestName(
|
|
|
1624
1867
|
process.stdout.write(`${suggestedName}\n`); // lint-ok-emission: chat-paste path
|
|
1625
1868
|
}
|
|
1626
1869
|
|
|
1627
|
-
function runStatus(opts: { json?: boolean; sessionId?: string }): void {
|
|
1870
|
+
function runStatus(opts: { endTurn?: boolean; json?: boolean; sessionId?: string }): void {
|
|
1628
1871
|
const root = monorepoRoot();
|
|
1629
1872
|
if (!root) {
|
|
1630
1873
|
emit.error({
|
|
@@ -1656,20 +1899,23 @@ function runStatus(opts: { json?: boolean; sessionId?: string }): void {
|
|
|
1656
1899
|
process.exit(1);
|
|
1657
1900
|
}
|
|
1658
1901
|
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
spawnSync(helper, ["stamp-status-call", myOwner], {
|
|
1666
|
-
encoding: "utf8",
|
|
1667
|
-
timeout: 2000,
|
|
1668
|
-
...coordHelperOpts(root),
|
|
1902
|
+
let finalization: GitFinalizationResult | null = null;
|
|
1903
|
+
if (opts.endTurn) {
|
|
1904
|
+
const history = readSessionWriteClaims(root, hb.instance_id, hb.session_id ?? myOwner);
|
|
1905
|
+
const touchedPaths = [...new Set([...(hb.files_touched ?? []), ...history.paths])];
|
|
1906
|
+
finalization = checkGitFinalization(root, touchedPaths, {
|
|
1907
|
+
claimHistoryComplete: history.complete,
|
|
1669
1908
|
});
|
|
1670
|
-
|
|
1671
|
-
|
|
1909
|
+
if (!finalization.ok) {
|
|
1910
|
+
emit.error({
|
|
1911
|
+
code: "git_not_finalized",
|
|
1912
|
+
message: formatGitFinalizationFailure(finalization, resolveBinName(root)),
|
|
1913
|
+
});
|
|
1914
|
+
process.exitCode = 1;
|
|
1915
|
+
return;
|
|
1916
|
+
}
|
|
1672
1917
|
}
|
|
1918
|
+
|
|
1673
1919
|
emitCanonical({
|
|
1674
1920
|
type: "state.status_checked",
|
|
1675
1921
|
owner: myOwner,
|
|
@@ -1677,6 +1923,7 @@ function runStatus(opts: { json?: boolean; sessionId?: string }): void {
|
|
|
1677
1923
|
adapter: normalizeAdapter(hb.platform),
|
|
1678
1924
|
data: {
|
|
1679
1925
|
format: opts.json ? "json" : "box",
|
|
1926
|
+
git_finalization_checked: opts.endTurn === true,
|
|
1680
1927
|
agent_count: 0, // computed below, not yet available here; Phase 5 verdict reads owner-scope only
|
|
1681
1928
|
included_self: true,
|
|
1682
1929
|
},
|
|
@@ -1763,6 +2010,12 @@ function runStatus(opts: { json?: boolean; sessionId?: string }): void {
|
|
|
1763
2010
|
instance_id: hb.instance_id,
|
|
1764
2011
|
kind: normalizeKind(hb.kind),
|
|
1765
2012
|
session_age_secs: ageSecs,
|
|
2013
|
+
activity: activityOf(hb),
|
|
2014
|
+
activity_updated_at: hb.activity_updated_at ?? null,
|
|
2015
|
+
activity_source: hb.activity_source ?? null,
|
|
2016
|
+
task_state: taskStateOf(hb),
|
|
2017
|
+
task_state_updated_at: hb.task_state_updated_at ?? null,
|
|
2018
|
+
task_state_reason: hb.task_state_reason ?? null,
|
|
1766
2019
|
files_held: filesHeld,
|
|
1767
2020
|
peers_live: livePeers.length,
|
|
1768
2021
|
peers_stale: peersStale,
|
|
@@ -1776,6 +2029,9 @@ function runStatus(opts: { json?: boolean; sessionId?: string }): void {
|
|
|
1776
2029
|
agents: m.agents.map((a) => ({
|
|
1777
2030
|
name: a.name || "unnamed",
|
|
1778
2031
|
task: a.task ?? null,
|
|
2032
|
+
activity: a.activity ?? "unknown",
|
|
2033
|
+
task_state: a.task_state ?? "active",
|
|
2034
|
+
task_state_reason: a.task_state_reason ?? null,
|
|
1779
2035
|
files: a.files_touched?.length ?? 0,
|
|
1780
2036
|
})),
|
|
1781
2037
|
})),
|
|
@@ -1784,6 +2040,7 @@ function runStatus(opts: { json?: boolean; sessionId?: string }): void {
|
|
|
1784
2040
|
context_window: ctxUsage?.window ?? null,
|
|
1785
2041
|
timestamp_iso: new Date().toISOString(),
|
|
1786
2042
|
timestamp_local: timeStr,
|
|
2043
|
+
...(finalization ? { finalization } : {}),
|
|
1787
2044
|
};
|
|
1788
2045
|
|
|
1789
2046
|
if (opts.json) {
|
|
@@ -1794,6 +2051,8 @@ function runStatus(opts: { json?: boolean; sessionId?: string }): void {
|
|
|
1794
2051
|
|
|
1795
2052
|
const rows: Array<[string, string]> = [
|
|
1796
2053
|
["session", formatAge(ageSecs)],
|
|
2054
|
+
["activity", activityOf(hb)],
|
|
2055
|
+
["lifecycle", lifecycleLabel(hb)],
|
|
1797
2056
|
["context", ctxStr],
|
|
1798
2057
|
["files", filesStr],
|
|
1799
2058
|
["peers", peersStr],
|
|
@@ -2154,13 +2413,12 @@ function readCanonicalEventsInWindow(root: string, cutoffMs: number): CanonicalE
|
|
|
2154
2413
|
return out;
|
|
2155
2414
|
}
|
|
2156
2415
|
|
|
2157
|
-
/** adapter
|
|
2158
|
-
* existing formatPlatformLabel rendering keeps working unchanged. */
|
|
2416
|
+
/** Normalize adapter event data into the heartbeat platform value. */
|
|
2159
2417
|
function adapterToPlatform(adapter: string | undefined): string {
|
|
2160
|
-
if (adapter === "claude-code") return "
|
|
2418
|
+
if (adapter === "claude-code") return "claude-code";
|
|
2161
2419
|
if (adapter === "cursor") return "cursor";
|
|
2162
2420
|
if (adapter === "codex") return "codex";
|
|
2163
|
-
return "
|
|
2421
|
+
return "claude-code";
|
|
2164
2422
|
}
|
|
2165
2423
|
|
|
2166
2424
|
/** Project a canonical health.* event into the HealEvent shape the aggregators
|
|
@@ -2387,14 +2645,6 @@ interface HealthReport {
|
|
|
2387
2645
|
by_platform: Record<string, number>;
|
|
2388
2646
|
top_agents: Array<{ agent: string; count: number }>;
|
|
2389
2647
|
};
|
|
2390
|
-
schema_invalid: { count: number; samples: string[] };
|
|
2391
|
-
commit_guards: {
|
|
2392
|
-
blocked: number;
|
|
2393
|
-
bypassed: number;
|
|
2394
|
-
suppressed: number;
|
|
2395
|
-
edit_blocked: number;
|
|
2396
|
-
shell_candidates: number;
|
|
2397
|
-
};
|
|
2398
2648
|
councils: {
|
|
2399
2649
|
active: number;
|
|
2400
2650
|
archived_in_window: number;
|
|
@@ -2554,6 +2804,12 @@ function traceLine(ev: CanonicalEvent, allTools: boolean): TraceEntry | null {
|
|
|
2554
2804
|
case "state.task_set":
|
|
2555
2805
|
detail = d.cleared ? "(cleared)" : clip(s("task"));
|
|
2556
2806
|
break;
|
|
2807
|
+
case "state.task_state":
|
|
2808
|
+
detail = `${s("prior_state") || "active"} → ${s("state") || "active"}${s("reason") ? ` · ${clip(s("reason"), 55)}` : ""}`;
|
|
2809
|
+
break;
|
|
2810
|
+
case "interaction.input_requested":
|
|
2811
|
+
detail = s("request_kind") || s("tool_name") || "operator input requested";
|
|
2812
|
+
break;
|
|
2557
2813
|
case "state.status_checked":
|
|
2558
2814
|
detail = "status box rendered";
|
|
2559
2815
|
break;
|
|
@@ -2674,6 +2930,7 @@ function runTrace(
|
|
|
2674
2930
|
}
|
|
2675
2931
|
|
|
2676
2932
|
const events = byId.get(resolvedId) ?? [];
|
|
2933
|
+
const state = foldSessionState(events, { instance_id: resolvedId });
|
|
2677
2934
|
const lines = events
|
|
2678
2935
|
.map((ev) => traceLine(ev, !!opts.allTools))
|
|
2679
2936
|
.filter((l): l is TraceEntry => l !== null)
|
|
@@ -2688,6 +2945,12 @@ function runTrace(
|
|
|
2688
2945
|
instance_id: resolvedId,
|
|
2689
2946
|
other_instances: candidateIds.filter((id) => id !== resolvedId),
|
|
2690
2947
|
total_events: events.length,
|
|
2948
|
+
activity: state.activity,
|
|
2949
|
+
activity_updated_at: state.activity_updated_at ?? null,
|
|
2950
|
+
activity_source: state.activity_source ?? null,
|
|
2951
|
+
task_state: state.task_state,
|
|
2952
|
+
task_state_updated_at: state.task_state_updated_at ?? null,
|
|
2953
|
+
task_state_reason: state.task_state_reason ?? null,
|
|
2691
2954
|
shown: shown.length,
|
|
2692
2955
|
entries: shown,
|
|
2693
2956
|
};
|
|
@@ -2699,6 +2962,9 @@ function runTrace(
|
|
|
2699
2962
|
emit.data(result);
|
|
2700
2963
|
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` : ""}`;
|
|
2701
2964
|
process.stdout.write(`${header}\n`); // lint-ok-emission: human trace view
|
|
2965
|
+
process.stdout.write(
|
|
2966
|
+
` activity=${state.activity} · lifecycle=${state.task_state}${state.task_state === "blocked" && state.task_state_reason ? `: ${state.task_state_reason}` : ""}\n`,
|
|
2967
|
+
); // lint-ok-emission: human trace view
|
|
2702
2968
|
if (shown.length === 0) {
|
|
2703
2969
|
process.stdout.write(" (no events)\n"); // lint-ok-emission: human trace view
|
|
2704
2970
|
return;
|
|
@@ -2736,18 +3002,7 @@ function runHealth(opts: { since: string; json?: boolean }): void {
|
|
|
2736
3002
|
|
|
2737
3003
|
// Coordination telemetry reads from the canonical events.ndjson stream.
|
|
2738
3004
|
// Heals come from health.*; council window-activity from council.*. The
|
|
2739
|
-
// commit-guard + schema-invalid counters below have NO canonical equivalent
|
|
2740
|
-
// yet. Their future home is the decision.* events (defined in schema.ts, not
|
|
2741
|
-
// yet wired). Until then they report 0 (fields kept for output-shape
|
|
2742
|
-
// compatibility).
|
|
2743
3005
|
const heal: HealEvent[] = [];
|
|
2744
|
-
const schemaInvalid = 0;
|
|
2745
|
-
const schemaSamples: string[] = [];
|
|
2746
|
-
const commitBlocked = 0;
|
|
2747
|
-
const commitBypassed = 0;
|
|
2748
|
-
const commitSuppressed = 0;
|
|
2749
|
-
const editBlocked = 0;
|
|
2750
|
-
const shellCandidates = 0;
|
|
2751
3006
|
let councilAdvanced = 0;
|
|
2752
3007
|
let councilClosed = 0;
|
|
2753
3008
|
let councilArchived = 0;
|
|
@@ -2865,12 +3120,6 @@ function runHealth(opts: { since: string; json?: boolean }): void {
|
|
|
2865
3120
|
|
|
2866
3121
|
// Anomaly detection.
|
|
2867
3122
|
const anomalies: string[] = [];
|
|
2868
|
-
if (schemaInvalid > 0) {
|
|
2869
|
-
const sampleList = schemaSamples.slice(0, 3).join(", ");
|
|
2870
|
-
anomalies.push(
|
|
2871
|
-
`HEARTBEAT_SCHEMA_INVALID fired ${schemaInvalid}x; heartbeat shape failed validation${sampleList ? ` (samples: ${sampleList})` : ""}`,
|
|
2872
|
-
);
|
|
2873
|
-
}
|
|
2874
3123
|
for (const { agent, count } of healTopAgents) {
|
|
2875
3124
|
if (count >= 5) {
|
|
2876
3125
|
anomalies.push(
|
|
@@ -2937,14 +3186,6 @@ function runHealth(opts: { since: string; json?: boolean }): void {
|
|
|
2937
3186
|
by_platform: healByPlatform,
|
|
2938
3187
|
top_agents: healTopAgents,
|
|
2939
3188
|
},
|
|
2940
|
-
schema_invalid: { count: schemaInvalid, samples: schemaSamples },
|
|
2941
|
-
commit_guards: {
|
|
2942
|
-
blocked: commitBlocked,
|
|
2943
|
-
bypassed: commitBypassed,
|
|
2944
|
-
suppressed: commitSuppressed,
|
|
2945
|
-
edit_blocked: editBlocked,
|
|
2946
|
-
shell_candidates: shellCandidates,
|
|
2947
|
-
},
|
|
2948
3189
|
councils: {
|
|
2949
3190
|
active: activeCouncils,
|
|
2950
3191
|
archived_in_window: councilArchived,
|
|
@@ -2988,17 +3229,6 @@ function renderHealthBox(report: HealthReport): void {
|
|
|
2988
3229
|
const topHealer = report.heal_events.top_agents[0];
|
|
2989
3230
|
const topHealerStr = topHealer ? `${topHealer.agent} x${topHealer.count}` : "(none)";
|
|
2990
3231
|
|
|
2991
|
-
const guardParts: string[] = [];
|
|
2992
|
-
if (report.commit_guards.blocked > 0) guardParts.push(`blocked ${report.commit_guards.blocked}`);
|
|
2993
|
-
if (report.commit_guards.bypassed > 0)
|
|
2994
|
-
guardParts.push(`bypassed ${report.commit_guards.bypassed}`);
|
|
2995
|
-
if (report.commit_guards.suppressed > 0)
|
|
2996
|
-
guardParts.push(`suppressed ${report.commit_guards.suppressed}`);
|
|
2997
|
-
if (report.commit_guards.edit_blocked > 0)
|
|
2998
|
-
guardParts.push(`edit-blocked ${report.commit_guards.edit_blocked}`);
|
|
2999
|
-
if (report.commit_guards.shell_candidates > 0)
|
|
3000
|
-
guardParts.push(`shell ${report.commit_guards.shell_candidates}`);
|
|
3001
|
-
|
|
3002
3232
|
const councilParts: string[] = [`${report.councils.active} active`];
|
|
3003
3233
|
if (report.councils.advanced_in_window > 0)
|
|
3004
3234
|
councilParts.push(`${report.councils.advanced_in_window} advanced`);
|
|
@@ -3035,8 +3265,6 @@ function renderHealthBox(report: HealthReport): void {
|
|
|
3035
3265
|
? "0"
|
|
3036
3266
|
: `${report.zombies.count} (${report.zombies.samples.join(", ")})`,
|
|
3037
3267
|
],
|
|
3038
|
-
["schema invalid", String(report.schema_invalid.count)],
|
|
3039
|
-
["commit guards", guardParts.length ? guardParts.join(", ") : "0"],
|
|
3040
3268
|
["councils", councilParts.join(", ")],
|
|
3041
3269
|
["anomalies", report.anomalies.length === 0 ? "(clean)" : `${report.anomalies.length} flagged`],
|
|
3042
3270
|
];
|
|
@@ -3070,10 +3298,10 @@ function runAdapterProbe(
|
|
|
3070
3298
|
if (opts.json) emit.config({ format: "json" });
|
|
3071
3299
|
|
|
3072
3300
|
const adapter = id.trim();
|
|
3073
|
-
if (adapter !== "
|
|
3301
|
+
if (adapter !== "claude-code" && adapter !== "cursor") {
|
|
3074
3302
|
emit.error({
|
|
3075
3303
|
code: "bad_adapter",
|
|
3076
|
-
message: "adapter id must be
|
|
3304
|
+
message: "adapter id must be claude-code or cursor",
|
|
3077
3305
|
});
|
|
3078
3306
|
process.exit(1);
|
|
3079
3307
|
}
|
|
@@ -3293,7 +3521,7 @@ function replayAdapterSamples(
|
|
|
3293
3521
|
stopFailure: "stop-failure",
|
|
3294
3522
|
StopFailure: "stop-failure",
|
|
3295
3523
|
};
|
|
3296
|
-
const adapterFlag = adapter
|
|
3524
|
+
const adapterFlag = adapter;
|
|
3297
3525
|
|
|
3298
3526
|
const sandbox = mkdtempSync(join(tmpdir(), "harn-adapter-probe-"));
|
|
3299
3527
|
const results: SampleReplayResult[] = [];
|
|
@@ -3430,13 +3658,13 @@ function collectPath(value: string, prev: string[]): string[] {
|
|
|
3430
3658
|
return [...prev, value];
|
|
3431
3659
|
}
|
|
3432
3660
|
|
|
3433
|
-
/** Parse "
|
|
3661
|
+
/** Parse "30s", "5m", "1h", "2d" into milliseconds. */
|
|
3434
3662
|
function parseDurationToMs(input: string): number | null {
|
|
3435
|
-
const match = input.trim().match(/^(\d+)([smhd]
|
|
3663
|
+
const match = input.trim().match(/^(\d+)([smhd])$/i);
|
|
3436
3664
|
if (!match) return null;
|
|
3437
3665
|
const n = Number.parseInt(match[1], 10);
|
|
3438
3666
|
if (!Number.isFinite(n) || n <= 0) return null;
|
|
3439
|
-
const unit =
|
|
3667
|
+
const unit = match[2].toLowerCase();
|
|
3440
3668
|
const mult: Record<string, number> = { s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000 };
|
|
3441
3669
|
return n * mult[unit];
|
|
3442
3670
|
}
|