harnery 0.36.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commander.js +4 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +87 -46
- package/dist/commands/artifacts.d.ts.map +1 -1
- package/dist/commands/artifacts.js +26 -1
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +11 -0
- package/dist/commands/diagnostics.d.ts +4 -0
- package/dist/commands/diagnostics.d.ts.map +1 -0
- package/dist/commands/diagnostics.js +229 -0
- package/dist/commands/files.d.ts +12 -0
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +54 -1
- package/dist/commands/governor.d.ts.map +1 -1
- package/dist/commands/governor.js +4 -0
- package/dist/commands/ledger-v3.d.ts.map +1 -1
- package/dist/commands/ledger-v3.js +71 -2
- package/dist/commands/qa-run.d.ts +4 -0
- package/dist/commands/qa-run.d.ts.map +1 -0
- package/dist/commands/qa-run.js +141 -0
- package/dist/commands/resources.d.ts +4 -0
- package/dist/commands/resources.d.ts.map +1 -0
- package/dist/commands/resources.js +29 -0
- package/dist/commands/supervisor.d.ts +4 -0
- package/dist/commands/supervisor.d.ts.map +1 -0
- package/dist/commands/supervisor.js +101 -0
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +17 -0
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +4 -0
- package/dist/core/agents/cli.js +7 -3
- package/dist/core/agents/health.d.ts +17 -0
- package/dist/core/agents/health.d.ts.map +1 -0
- package/dist/core/agents/health.js +109 -0
- package/dist/core/agents/mailbox.d.ts +92 -0
- package/dist/core/agents/mailbox.d.ts.map +1 -0
- package/dist/core/agents/mailbox.js +325 -0
- package/dist/core/agents/reconcile-coordination-v3.d.ts +44 -0
- package/dist/core/agents/reconcile-coordination-v3.d.ts.map +1 -0
- package/dist/core/agents/reconcile-coordination-v3.js +58 -0
- package/dist/core/agents/render/prompt-context.d.ts +5 -0
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +34 -13
- package/dist/core/agents/render/session-context.d.ts +8 -0
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +34 -3
- package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/commit-conflict.js +50 -1
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +6 -10
- package/dist/core/agents/session-finalizer-v3.js +4 -1
- package/dist/core/artifacts/index.d.ts +26 -1
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +146 -6
- package/dist/core/config.d.ts +12 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +43 -0
- package/dist/core/diagnostics/advice.d.ts +9 -0
- package/dist/core/diagnostics/advice.d.ts.map +1 -0
- package/dist/core/diagnostics/advice.js +112 -0
- package/dist/core/diagnostics/bundle.d.ts +35 -0
- package/dist/core/diagnostics/bundle.d.ts.map +1 -0
- package/dist/core/diagnostics/bundle.js +874 -0
- package/dist/core/diagnostics/comparison.d.ts +5 -0
- package/dist/core/diagnostics/comparison.d.ts.map +1 -0
- package/dist/core/diagnostics/comparison.js +444 -0
- package/dist/core/diagnostics/contract.d.ts +245 -0
- package/dist/core/diagnostics/contract.d.ts.map +1 -0
- package/dist/core/diagnostics/contract.js +18 -0
- package/dist/core/diagnostics/identity.d.ts +3 -0
- package/dist/core/diagnostics/identity.d.ts.map +1 -0
- package/dist/core/diagnostics/identity.js +10 -0
- package/dist/core/diagnostics/index.d.ts +8 -0
- package/dist/core/diagnostics/index.d.ts.map +1 -0
- package/dist/core/diagnostics/index.js +7 -0
- package/dist/core/diagnostics/replay.d.ts +8 -0
- package/dist/core/diagnostics/replay.d.ts.map +1 -0
- package/dist/core/diagnostics/replay.js +213 -0
- package/dist/core/diagnostics/sanitize.d.ts +9 -0
- package/dist/core/diagnostics/sanitize.d.ts.map +1 -0
- package/dist/core/diagnostics/sanitize.js +84 -0
- package/dist/core/events/legacy-storage/compression.d.ts +13 -0
- package/dist/core/events/legacy-storage/compression.d.ts.map +1 -0
- package/dist/core/events/legacy-storage/compression.js +107 -0
- package/dist/core/events/legacy-storage/index.d.ts +1 -0
- package/dist/core/events/legacy-storage/index.d.ts.map +1 -1
- package/dist/core/events/legacy-storage/index.js +1 -0
- package/dist/core/events/v3/archive-retention.d.ts +33 -0
- package/dist/core/events/v3/archive-retention.d.ts.map +1 -0
- package/dist/core/events/v3/archive-retention.js +195 -0
- package/dist/core/events/v3/capabilities.d.ts +1 -1
- package/dist/core/events/v3/capabilities.d.ts.map +1 -1
- package/dist/core/events/v3/capabilities.js +1 -0
- package/dist/core/events/v3/index.d.ts +1 -0
- package/dist/core/events/v3/index.d.ts.map +1 -1
- package/dist/core/events/v3/index.js +1 -0
- package/dist/core/events/v3/live-routing.d.ts.map +1 -1
- package/dist/core/events/v3/live-routing.js +1 -0
- package/dist/core/events/v3/producers/hook-base.d.ts +1 -1
- package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
- package/dist/core/events/v3/producers/hook-base.js +13 -2
- package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
- package/dist/core/events/v3/producers/intake.js +1 -0
- package/dist/core/events/v3/producers/recorder.d.ts +16 -0
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +88 -1
- package/dist/core/hooks/adapter/output.d.ts +8 -0
- package/dist/core/hooks/adapter/output.d.ts.map +1 -1
- package/dist/core/hooks/adapter/output.js +11 -1
- package/dist/core/hooks/cli.js +183 -192
- package/dist/core/hooks/health.d.ts +59 -0
- package/dist/core/hooks/health.d.ts.map +1 -0
- package/dist/core/hooks/health.js +84 -0
- package/dist/core/resources/contract.d.ts +90 -0
- package/dist/core/resources/contract.d.ts.map +1 -0
- package/dist/core/resources/contract.js +2 -0
- package/dist/core/resources/index.d.ts +6 -0
- package/dist/core/resources/index.d.ts.map +1 -0
- package/dist/core/resources/index.js +5 -0
- package/dist/core/resources/sampler.d.ts +27 -0
- package/dist/core/resources/sampler.d.ts.map +1 -0
- package/dist/core/resources/sampler.js +357 -0
- package/dist/core/resources/service-status.d.ts +3 -0
- package/dist/core/resources/service-status.d.ts.map +1 -0
- package/dist/core/resources/service-status.js +48 -0
- package/dist/core/resources/service.d.ts +26 -0
- package/dist/core/resources/service.d.ts.map +1 -0
- package/dist/core/resources/service.js +292 -0
- package/dist/core/resources/storage.d.ts +13 -0
- package/dist/core/resources/storage.d.ts.map +1 -0
- package/dist/core/resources/storage.js +30 -0
- package/dist/core/storage/atomic-json.d.ts +3 -0
- package/dist/core/storage/atomic-json.d.ts.map +1 -0
- package/dist/core/storage/atomic-json.js +15 -0
- package/dist/core/storage/builtins.d.ts.map +1 -1
- package/dist/core/storage/builtins.js +45 -6
- package/dist/core/storage/logger.d.ts +2 -0
- package/dist/core/storage/logger.d.ts.map +1 -1
- package/dist/core/storage/logger.js +2 -0
- package/dist/core/storage/query.d.ts +14 -0
- package/dist/core/storage/query.d.ts.map +1 -1
- package/dist/core/storage/query.js +51 -0
- package/dist/core/supervisor/activity.d.ts +4 -0
- package/dist/core/supervisor/activity.d.ts.map +1 -0
- package/dist/core/supervisor/activity.js +141 -0
- package/dist/core/supervisor/contract.d.ts +268 -0
- package/dist/core/supervisor/contract.d.ts.map +1 -0
- package/dist/core/supervisor/contract.js +35 -0
- package/dist/core/supervisor/explanations.d.ts +4 -0
- package/dist/core/supervisor/explanations.d.ts.map +1 -0
- package/dist/core/supervisor/explanations.js +71 -0
- package/dist/core/supervisor/findings.d.ts +20 -0
- package/dist/core/supervisor/findings.d.ts.map +1 -0
- package/dist/core/supervisor/findings.js +399 -0
- package/dist/core/supervisor/history.d.ts +9 -0
- package/dist/core/supervisor/history.d.ts.map +1 -0
- package/dist/core/supervisor/history.js +46 -0
- package/dist/core/supervisor/hook-health-alerts.d.ts +14 -0
- package/dist/core/supervisor/hook-health-alerts.d.ts.map +1 -0
- package/dist/core/supervisor/hook-health-alerts.js +70 -0
- package/dist/core/supervisor/hook-health-storage.d.ts +5 -0
- package/dist/core/supervisor/hook-health-storage.d.ts.map +1 -0
- package/dist/core/supervisor/hook-health-storage.js +24 -0
- package/dist/core/supervisor/hook-health.d.ts +72 -0
- package/dist/core/supervisor/hook-health.d.ts.map +1 -0
- package/dist/core/supervisor/hook-health.js +203 -0
- package/dist/core/supervisor/hooks.d.ts +10 -0
- package/dist/core/supervisor/hooks.d.ts.map +1 -0
- package/dist/core/supervisor/hooks.js +28 -0
- package/dist/core/supervisor/index.d.ts +15 -0
- package/dist/core/supervisor/index.d.ts.map +1 -0
- package/dist/core/supervisor/index.js +14 -0
- package/dist/core/supervisor/log-feed.d.ts +9 -0
- package/dist/core/supervisor/log-feed.d.ts.map +1 -0
- package/dist/core/supervisor/log-feed.js +103 -0
- package/dist/core/supervisor/service.d.ts +29 -0
- package/dist/core/supervisor/service.d.ts.map +1 -0
- package/dist/core/supervisor/service.js +450 -0
- package/dist/core/supervisor/services.d.ts +11 -0
- package/dist/core/supervisor/services.d.ts.map +1 -0
- package/dist/core/supervisor/services.js +155 -0
- package/dist/core/supervisor/status.d.ts +3 -0
- package/dist/core/supervisor/status.d.ts.map +1 -0
- package/dist/core/supervisor/status.js +48 -0
- package/dist/core/supervisor/storage.d.ts +30 -0
- package/dist/core/supervisor/storage.d.ts.map +1 -0
- package/dist/core/supervisor/storage.js +73 -0
- package/dist/core/supervisor/timeline.d.ts +3 -0
- package/dist/core/supervisor/timeline.d.ts.map +1 -0
- package/dist/core/supervisor/timeline.js +87 -0
- package/dist/core/workflow/admission.d.ts +23 -0
- package/dist/core/workflow/admission.d.ts.map +1 -0
- package/dist/core/workflow/admission.js +136 -0
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +77 -2
- package/dist/core/workflow/index.d.ts +3 -2
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +2 -1
- package/dist/core/workflow/proof.d.ts +2 -1
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +39 -1
- package/dist/core/workflow/run-state.d.ts +3 -2
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +8 -1
- package/dist/core/workflow/types.d.ts +33 -1
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/types.js +2 -1
- package/dist/lib/browser/client.d.ts +5 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +1 -0
- package/dist/lib/browser/index.d.ts +2 -0
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +2 -0
- package/dist/lib/browser/qa-run-contracts.d.ts +173 -0
- package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -0
- package/dist/lib/browser/qa-run-contracts.js +226 -0
- package/dist/lib/browser/qa-run.d.ts +51 -0
- package/dist/lib/browser/qa-run.d.ts.map +1 -0
- package/dist/lib/browser/qa-run.js +684 -0
- package/dist/lib/coord-root-id.d.ts +9 -0
- package/dist/lib/coord-root-id.d.ts.map +1 -0
- package/dist/lib/coord-root-id.js +17 -0
- package/dist/lib/instructions/apply.d.ts +3 -1
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +74 -3
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +6 -0
- package/package.json +12 -1
- package/src/commander.ts +26 -0
- package/src/commands/agents.ts +102 -49
- package/src/commands/artifacts.ts +40 -2
- package/src/commands/browse.ts +15 -0
- package/src/commands/diagnostics.ts +271 -0
- package/src/commands/files.ts +102 -2
- package/src/commands/governor.ts +8 -0
- package/src/commands/ledger-v3.ts +82 -0
- package/src/commands/qa-run.ts +184 -0
- package/src/commands/resources.ts +38 -0
- package/src/commands/supervisor.ts +141 -0
- package/src/commands/web.ts +19 -0
- package/src/commands/workflow.ts +8 -0
- package/src/core/agents/cli.ts +7 -3
- package/src/core/agents/health.ts +142 -0
- package/src/core/agents/mailbox.ts +373 -0
- package/src/core/agents/reconcile-coordination-v3.ts +77 -0
- package/src/core/agents/render/prompt-context.ts +42 -12
- package/src/core/agents/render/session-context.ts +37 -3
- package/src/core/agents/rules/commit-conflict.ts +58 -1
- package/src/core/agents/rules/stop-hook.ts +6 -12
- package/src/core/agents/session-finalizer-v3.ts +4 -1
- package/src/core/artifacts/index.ts +196 -10
- package/src/core/config.ts +111 -2
- package/src/core/diagnostics/advice.ts +147 -0
- package/src/core/diagnostics/bundle.ts +1142 -0
- package/src/core/diagnostics/comparison.ts +565 -0
- package/src/core/diagnostics/contract.ts +311 -0
- package/src/core/diagnostics/identity.ts +11 -0
- package/src/core/diagnostics/index.ts +7 -0
- package/src/core/diagnostics/replay.ts +314 -0
- package/src/core/diagnostics/sanitize.ts +97 -0
- package/src/core/events/legacy-storage/compression.ts +130 -0
- package/src/core/events/legacy-storage/index.ts +1 -0
- package/src/core/events/v3/archive-retention.ts +251 -0
- package/src/core/events/v3/capabilities.ts +2 -0
- package/src/core/events/v3/index.ts +9 -0
- package/src/core/events/v3/live-routing.ts +1 -0
- package/src/core/events/v3/producers/hook-base.ts +14 -2
- package/src/core/events/v3/producers/intake.ts +1 -0
- package/src/core/events/v3/producers/recorder.ts +110 -1
- package/src/core/hooks/adapter/output.ts +12 -1
- package/src/core/hooks/cli.ts +192 -215
- package/src/core/hooks/health.ts +146 -0
- package/src/core/resources/contract.ts +98 -0
- package/src/core/resources/index.ts +5 -0
- package/src/core/resources/sampler.ts +458 -0
- package/src/core/resources/service-status.ts +62 -0
- package/src/core/resources/service.ts +337 -0
- package/src/core/resources/storage.ts +44 -0
- package/src/core/storage/atomic-json.ts +15 -0
- package/src/core/storage/builtins.ts +62 -6
- package/src/core/storage/logger.ts +2 -0
- package/src/core/storage/query.ts +58 -0
- package/src/core/supervisor/activity.ts +193 -0
- package/src/core/supervisor/contract.ts +312 -0
- package/src/core/supervisor/explanations.ts +104 -0
- package/src/core/supervisor/findings.ts +788 -0
- package/src/core/supervisor/history.ts +61 -0
- package/src/core/supervisor/hook-health-alerts.ts +108 -0
- package/src/core/supervisor/hook-health-storage.ts +25 -0
- package/src/core/supervisor/hook-health.ts +301 -0
- package/src/core/supervisor/hooks.ts +35 -0
- package/src/core/supervisor/index.ts +14 -0
- package/src/core/supervisor/log-feed.ts +131 -0
- package/src/core/supervisor/service.ts +511 -0
- package/src/core/supervisor/services.ts +203 -0
- package/src/core/supervisor/status.ts +59 -0
- package/src/core/supervisor/storage.ts +126 -0
- package/src/core/supervisor/timeline.ts +133 -0
- package/src/core/workflow/admission.ts +215 -0
- package/src/core/workflow/engine.ts +93 -1
- package/src/core/workflow/index.ts +10 -0
- package/src/core/workflow/proof.ts +54 -1
- package/src/core/workflow/run-state.ts +13 -1
- package/src/core/workflow/types.ts +39 -1
- package/src/lib/browser/client.ts +6 -0
- package/src/lib/browser/index.ts +29 -0
- package/src/lib/browser/qa-run-contracts.ts +380 -0
- package/src/lib/browser/qa-run.ts +784 -0
- package/src/lib/coord-root-id.ts +23 -0
- package/src/lib/instructions/apply.ts +81 -2
- package/src/lib/instructions/templates.ts +6 -0
package/src/core/hooks/cli.ts
CHANGED
|
@@ -23,7 +23,6 @@ import { dirname, join } from "node:path";
|
|
|
23
23
|
import { coordEnv } from "../../lib/env.ts";
|
|
24
24
|
import { buildInstructionBundle } from "../../lib/instructions/bundle.ts";
|
|
25
25
|
import type { Adapter } from "../adapter.ts";
|
|
26
|
-
import { coordBinPath } from "../agents/coord-bin.ts";
|
|
27
26
|
import { resolveCoordRoot } from "../agents/coord-client.ts";
|
|
28
27
|
import {
|
|
29
28
|
type ClaimFinalizationDecision,
|
|
@@ -31,7 +30,10 @@ import {
|
|
|
31
30
|
classifyWriteClaimFinalization,
|
|
32
31
|
formatWriteClaimFinalizationDenial,
|
|
33
32
|
} from "../agents/finalization.ts";
|
|
34
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
recordLiveClaimChangeV3,
|
|
35
|
+
restoreLiveCoordinationStateAfterEpochV3,
|
|
36
|
+
} from "../agents/live-authority-v3.ts";
|
|
35
37
|
import { evaluateStopHook, STOP_REMEDIATION_MARKER } from "../agents/rules/stop-hook.ts";
|
|
36
38
|
import {
|
|
37
39
|
assistantTextStartsWithSessionNameBlock,
|
|
@@ -45,6 +47,7 @@ import type { Heartbeat } from "../agents/state/heartbeat-reader.ts";
|
|
|
45
47
|
import { readHeartbeat, stampSessionNameSeen } from "../agents/state/heartbeat-writer.ts";
|
|
46
48
|
import { readLiveCoordinationRow } from "../agents/state/live-coordination-view.ts";
|
|
47
49
|
import { ensureLiveCoordinationHeartbeat } from "../agents/state/live-coordination-writer.ts";
|
|
50
|
+
import { assignName } from "../agents/state/names.ts";
|
|
48
51
|
import { writePidmapRow } from "../agents/state/pidmap.ts";
|
|
49
52
|
import { agentsRequireGitFinalization, resolveBinName } from "../config.ts";
|
|
50
53
|
import {
|
|
@@ -100,6 +103,13 @@ import {
|
|
|
100
103
|
} from "./effects/index.ts";
|
|
101
104
|
import { toolInputHash, toolTargetHash } from "./events/input-hash.ts";
|
|
102
105
|
import { canonicalize } from "./guard-path.ts";
|
|
106
|
+
import {
|
|
107
|
+
beginHookHealth,
|
|
108
|
+
type HookHealthState,
|
|
109
|
+
observeHookDebug,
|
|
110
|
+
observeHookError,
|
|
111
|
+
writeHookHealthCompletion,
|
|
112
|
+
} from "./health.ts";
|
|
103
113
|
import { adapterPidFromEnv, parsePsChainLine, selectAnchorPid } from "./resolve/anchor.ts";
|
|
104
114
|
import { extractIntentComment, resolveIntent } from "./resolve/intent.ts";
|
|
105
115
|
import { resolveOwner } from "./resolve/owner.ts";
|
|
@@ -118,6 +128,8 @@ interface Argv {
|
|
|
118
128
|
extra: string[];
|
|
119
129
|
}
|
|
120
130
|
|
|
131
|
+
let hookHealthState: HookHealthState | undefined;
|
|
132
|
+
|
|
121
133
|
function parseArgv(argv: string[]): Argv {
|
|
122
134
|
const out: Argv = { eventName: null, extra: [] };
|
|
123
135
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -155,17 +167,16 @@ async function readStdin(): Promise<string> {
|
|
|
155
167
|
}
|
|
156
168
|
|
|
157
169
|
/**
|
|
158
|
-
* Env for
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* without the pin a child could write state to a different .harnery than the
|
|
162
|
-
* one this hook resolved.
|
|
170
|
+
* Env for a detached hook retry worker, with the coord root pinned. The hook
|
|
171
|
+
* may run with the session shell's cwd (inside a submodule or journal dir), so
|
|
172
|
+
* the worker must retain the root this process already resolved.
|
|
163
173
|
*/
|
|
164
174
|
function childEnv(coordRoot: string): NodeJS.ProcessEnv {
|
|
165
175
|
return { ...process.env, HARNERY_COORD_ROOT_OVERRIDE: coordRoot };
|
|
166
176
|
}
|
|
167
177
|
|
|
168
178
|
function appendDebug(coordRoot: string, entry: Record<string, unknown>): void {
|
|
179
|
+
observeHookDebug(hookHealthState, entry);
|
|
169
180
|
if (coordEnv("SHARED_LOGS") !== "0") {
|
|
170
181
|
try {
|
|
171
182
|
processLogger(coordRoot, "agent-hook").debug("agent_hook.diagnostic", legacyLogFields(entry));
|
|
@@ -184,6 +195,7 @@ function appendDebug(coordRoot: string, entry: Record<string, unknown>): void {
|
|
|
184
195
|
}
|
|
185
196
|
|
|
186
197
|
function logError(coordRoot: string | null, err: unknown, context: Record<string, unknown>): void {
|
|
198
|
+
observeHookError(hookHealthState, context.phase);
|
|
187
199
|
if (!coordRoot) return;
|
|
188
200
|
if (coordEnv("SHARED_LOGS") !== "0") {
|
|
189
201
|
try {
|
|
@@ -216,38 +228,18 @@ function logError(coordRoot: string | null, err: unknown, context: Record<string
|
|
|
216
228
|
}
|
|
217
229
|
|
|
218
230
|
/**
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
* session.started emission never breaks the adapter flow.
|
|
222
|
-
*
|
|
223
|
-
* Lives at agent-hooks side (not agent-coord) to keep emitter/consumer
|
|
224
|
-
* separation: we spawn rather than import.
|
|
231
|
+
* Mint or recover the hurricane-style name for this owner in-process. Returns
|
|
232
|
+
* null on any failure so session.started emission never breaks adapter flow.
|
|
225
233
|
*/
|
|
226
|
-
function
|
|
234
|
+
function assignNameInProcess(
|
|
227
235
|
coordRoot: string,
|
|
228
236
|
instanceId: string,
|
|
229
237
|
kind: "session" | "subagent" | "transient",
|
|
230
238
|
forkedFrom?: string,
|
|
231
239
|
): { name: string; kind: string } | null {
|
|
232
|
-
const binary = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
233
|
-
if (!existsSync(binary)) return null;
|
|
234
240
|
try {
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
const result = spawnSync(binary, args, {
|
|
238
|
-
encoding: "utf8",
|
|
239
|
-
timeout: 2000,
|
|
240
|
-
env: childEnv(coordRoot),
|
|
241
|
-
});
|
|
242
|
-
if (result.status !== 0 || !result.stdout) return null;
|
|
243
|
-
const parsed = JSON.parse(result.stdout.trim()) as {
|
|
244
|
-
name?: string;
|
|
245
|
-
kind?: string;
|
|
246
|
-
};
|
|
247
|
-
if (parsed.name && parsed.kind) {
|
|
248
|
-
return { name: parsed.name, kind: parsed.kind };
|
|
249
|
-
}
|
|
250
|
-
return null;
|
|
241
|
+
const name = assignName(coordRoot, instanceId, kind, forkedFrom ? { forkedFrom } : undefined);
|
|
242
|
+
return { name, kind };
|
|
251
243
|
} catch {
|
|
252
244
|
return null;
|
|
253
245
|
}
|
|
@@ -321,14 +313,9 @@ function buildEventData(
|
|
|
321
313
|
// tool.requested heal path instead. The forkedFrom plumbing below stays
|
|
322
314
|
// for adapters that DO report a parent at session start.
|
|
323
315
|
const forkedFrom: string | undefined = undefined;
|
|
324
|
-
// Assign (or recover) name + kind
|
|
316
|
+
// Assign (or recover) name + kind in-process. Idempotent: resume
|
|
325
317
|
// returns the original name; new owner consumes a counter slot.
|
|
326
|
-
const assigned =
|
|
327
|
-
ctx.coordRoot,
|
|
328
|
-
ctx.instanceId,
|
|
329
|
-
"session",
|
|
330
|
-
forkedFrom,
|
|
331
|
-
);
|
|
318
|
+
const assigned = assignNameInProcess(ctx.coordRoot, ctx.instanceId, "session", forkedFrom);
|
|
332
319
|
// Write the adapter pid-map row so `harn agents whoami` ppid-walks find
|
|
333
320
|
// this owner. Prefer the payload pid (the actual claude binary), then the
|
|
334
321
|
// anchor walk (the `node` ancestor for Cursor, which has no payload pid),
|
|
@@ -396,7 +383,7 @@ function buildEventData(
|
|
|
396
383
|
ctx.adapter === "cursor" &&
|
|
397
384
|
!readLiveCoordinationRow(ctx.coordRoot, ctx.instanceId)?.name
|
|
398
385
|
) {
|
|
399
|
-
|
|
386
|
+
assignNameInProcess(ctx.coordRoot, ctx.instanceId, "session");
|
|
400
387
|
}
|
|
401
388
|
const prompt = p?.prompt ?? "";
|
|
402
389
|
const { value, truncated } = clampString(prompt, 4000);
|
|
@@ -467,7 +454,7 @@ function buildEventData(
|
|
|
467
454
|
// (agent-coord/state/names.ts → kind=transient). Use the call ID as the
|
|
468
455
|
// instance_id input; assignName falls through to transient.
|
|
469
456
|
const childInstanceId = subagentCallId ?? ctx.instanceId;
|
|
470
|
-
const assigned =
|
|
457
|
+
const assigned = assignNameInProcess(ctx.coordRoot, childInstanceId, "subagent");
|
|
471
458
|
return {
|
|
472
459
|
agent_type:
|
|
473
460
|
(p?.raw.agent_type as string | undefined) ??
|
|
@@ -597,10 +584,18 @@ function numberField(value: unknown): number | undefined {
|
|
|
597
584
|
|
|
598
585
|
async function main(): Promise<number> {
|
|
599
586
|
const hookStartedAt = performance.now();
|
|
587
|
+
const hookStartedRss = process.memoryUsage().rss;
|
|
600
588
|
const hookClock = captureSpanClockV3();
|
|
601
589
|
const { eventName, extra } = parseArgv(process.argv.slice(2));
|
|
602
590
|
const adapter = detectAdapter(process.argv.slice(2));
|
|
603
591
|
const raw = await readStdin();
|
|
592
|
+
hookHealthState = beginHookHealth({
|
|
593
|
+
started_at_ms: hookStartedAt,
|
|
594
|
+
started_rss_bytes: hookStartedRss,
|
|
595
|
+
event_name: eventName,
|
|
596
|
+
adapter,
|
|
597
|
+
payload_bytes: raw.length,
|
|
598
|
+
});
|
|
604
599
|
|
|
605
600
|
// Cursor executes the Claude Code project hooks too on hosts wired for both
|
|
606
601
|
// adapters, piping them its own payload (identifiable by the top-level
|
|
@@ -612,6 +607,7 @@ async function main(): Promise<number> {
|
|
|
612
607
|
if (coordEnv("AGENT_COORD_OFF") !== "1") {
|
|
613
608
|
const coordRoot = resolveCoordRoot(process.cwd());
|
|
614
609
|
if (coordRoot) {
|
|
610
|
+
hookHealthState.coord_root = coordRoot;
|
|
615
611
|
appendDebug(coordRoot, {
|
|
616
612
|
ts: new Date().toISOString(),
|
|
617
613
|
event_name: eventName,
|
|
@@ -668,6 +664,7 @@ async function main(): Promise<number> {
|
|
|
668
664
|
|
|
669
665
|
const coordRoot = resolveCoordRoot(process.cwd());
|
|
670
666
|
if (!coordRoot) return 0;
|
|
667
|
+
hookHealthState.coord_root = coordRoot;
|
|
671
668
|
if (eventName === "runtime-context-retry") {
|
|
672
669
|
return runRuntimeContextRetryWorker(coordRoot, adapter, extra);
|
|
673
670
|
}
|
|
@@ -696,8 +693,9 @@ async function main(): Promise<number> {
|
|
|
696
693
|
// that tool. It is not a transcript and therefore cannot prove that an
|
|
697
694
|
// earlier assistant response omitted the required session-name block.
|
|
698
695
|
// afterAgentResponse is the one native boundary that carries the completed
|
|
699
|
-
// assistant text
|
|
700
|
-
//
|
|
696
|
+
// assistant text. Preserve only privacy-safe ritual booleans in the V3
|
|
697
|
+
// producer's open-turn state; Stop consumes them into the authoritative
|
|
698
|
+
// turn.completed event. The response body never enters V3 or heartbeat.
|
|
701
699
|
if (adapter === "cursor" && eventName === "after-agent-response") {
|
|
702
700
|
const payload = parsePayload(raw, adapter);
|
|
703
701
|
const owner = resolveOwner({ payload: payload?.raw ?? null, coordRoot });
|
|
@@ -713,6 +711,53 @@ async function main(): Promise<number> {
|
|
|
713
711
|
} else {
|
|
714
712
|
appendDebug(coordRoot, { ...debugBase, skipped: "no-session-name-sighting" });
|
|
715
713
|
}
|
|
714
|
+
if (ledgerRoute.state === "blocked") {
|
|
715
|
+
appendDebug(coordRoot, {
|
|
716
|
+
...debugBase,
|
|
717
|
+
skipped: "v3-control-blocked",
|
|
718
|
+
reason: ledgerRoute.reason,
|
|
719
|
+
owner_source: owner.source,
|
|
720
|
+
});
|
|
721
|
+
return 0;
|
|
722
|
+
}
|
|
723
|
+
const statusBoxPresentStrict = scanAssistantStatusBoxPresent(undefined, text);
|
|
724
|
+
// The hook parser needs the completed reply long enough to scan it, but
|
|
725
|
+
// durable hook intake must never retain that body. Keep every other native
|
|
726
|
+
// field so turn correlation remains available to the producer.
|
|
727
|
+
const { text: _completedReply, ...privacySafeRaw } = payload?.raw ?? {};
|
|
728
|
+
const privacySafePayload = payload ? { ...payload, raw: privacySafeRaw } : null;
|
|
729
|
+
const v3Result = recordLiveHookSignalV3({
|
|
730
|
+
coordRoot,
|
|
731
|
+
route: ledgerRoute,
|
|
732
|
+
eventName,
|
|
733
|
+
payload: privacySafePayload,
|
|
734
|
+
adapter,
|
|
735
|
+
instanceId: owner.instance_id,
|
|
736
|
+
hook_name: eventName,
|
|
737
|
+
hook_duration_ms: Math.max(0, Math.floor(performance.now() - hookStartedAt)),
|
|
738
|
+
monotonic_ns: hookClock.monotonic_ns,
|
|
739
|
+
turn_ritual: {
|
|
740
|
+
status_box_present: statusBoxPresentStrict,
|
|
741
|
+
status_box_present_strict: statusBoxPresentStrict,
|
|
742
|
+
session_name_required: name !== undefined,
|
|
743
|
+
session_name_present: name ? assistantTextStartsWithSessionNameBlock(text, name) : false,
|
|
744
|
+
},
|
|
745
|
+
});
|
|
746
|
+
observeHookDebug(hookHealthState, { event_v3_state: v3Result.state });
|
|
747
|
+
appendDebug(coordRoot, {
|
|
748
|
+
...debugBase,
|
|
749
|
+
effect: "cursor-response-ritual-observed",
|
|
750
|
+
owner_source: owner.source,
|
|
751
|
+
event_v3_state: v3Result.state,
|
|
752
|
+
...(v3Result.state === "observed"
|
|
753
|
+
? {
|
|
754
|
+
generation_id: v3Result.generation_id,
|
|
755
|
+
turn_id: v3Result.turn_id,
|
|
756
|
+
response_observed_at: v3Result.observed_at,
|
|
757
|
+
status_box_present_strict: statusBoxPresentStrict,
|
|
758
|
+
}
|
|
759
|
+
: {}),
|
|
760
|
+
});
|
|
716
761
|
return 0;
|
|
717
762
|
}
|
|
718
763
|
|
|
@@ -735,6 +780,7 @@ async function main(): Promise<number> {
|
|
|
735
780
|
});
|
|
736
781
|
return 0;
|
|
737
782
|
}
|
|
783
|
+
hookHealthState.owner_id = owner.instance_id;
|
|
738
784
|
|
|
739
785
|
const sessionId = payload?.session_id ?? payload?.conversation_id ?? owner.instance_id;
|
|
740
786
|
const priorCoordination = readHeartbeat(coordRoot, owner.instance_id);
|
|
@@ -812,6 +858,9 @@ async function main(): Promise<number> {
|
|
|
812
858
|
...(turnRitual ? { turn_ritual: turnRitual } : {}),
|
|
813
859
|
...(deferV3Drain ? { defer_drain: true } : {}),
|
|
814
860
|
});
|
|
861
|
+
observeHookDebug(hookHealthState, {
|
|
862
|
+
...(v3Result ? { event_v3_state: v3Result.state } : {}),
|
|
863
|
+
});
|
|
815
864
|
const recordedGenerationId =
|
|
816
865
|
v3Result && "event" in v3Result && "generation_id" in v3Result.event.scope
|
|
817
866
|
? (v3Result.event.scope.generation_id as `gen_${string}`)
|
|
@@ -966,6 +1015,7 @@ async function main(): Promise<number> {
|
|
|
966
1015
|
// Session start is the only caller of the artifact janitor and the storage
|
|
967
1016
|
// maintenance pass, so both load here instead of on every tool call.
|
|
968
1017
|
const { autoCleanArtifacts } = await import("../artifacts/index.ts");
|
|
1018
|
+
const { autoCleanEventV3Archives } = await import("../events/v3/archive-retention.ts");
|
|
969
1019
|
const { createAutomaticMaintenanceComposition, runAutomaticMaintenancePass } = await import(
|
|
970
1020
|
"../storage/maintenance-providers.ts"
|
|
971
1021
|
);
|
|
@@ -977,6 +1027,13 @@ async function main(): Promise<number> {
|
|
|
977
1027
|
} catch (err) {
|
|
978
1028
|
logError(coordRoot, err, { phase: "artifact-auto-clean" });
|
|
979
1029
|
}
|
|
1030
|
+
// Effect: apply the same daily throttle to closed V3 epochs. The newest
|
|
1031
|
+
// recovery epochs remain protected by the archive retention policy.
|
|
1032
|
+
try {
|
|
1033
|
+
autoCleanEventV3Archives(coordRoot);
|
|
1034
|
+
} catch (err) {
|
|
1035
|
+
logError(coordRoot, err, { phase: "event-v3-archive-auto-clean" });
|
|
1036
|
+
}
|
|
980
1037
|
// Plan one bounded, claim-first global slice from cached pressure only.
|
|
981
1038
|
// Existing owner janitors above remain the only active executors until
|
|
982
1039
|
// each provider receives a separate production activation.
|
|
@@ -1040,7 +1097,7 @@ async function main(): Promise<number> {
|
|
|
1040
1097
|
// agnostic since v0.5.0.
|
|
1041
1098
|
if (norm.event_type === "session.ended") {
|
|
1042
1099
|
try {
|
|
1043
|
-
cleanupSessionEnd(coordRoot, owner.instance_id
|
|
1100
|
+
cleanupSessionEnd(coordRoot, owner.instance_id);
|
|
1044
1101
|
} catch (err) {
|
|
1045
1102
|
logError(coordRoot, err, { phase: "session-end-cleanup" });
|
|
1046
1103
|
}
|
|
@@ -1065,27 +1122,9 @@ async function main(): Promise<number> {
|
|
|
1065
1122
|
if (norm.event_type === "agent.started") {
|
|
1066
1123
|
try {
|
|
1067
1124
|
const childInstanceId = (data.agent_id as string | undefined) ?? owner.instance_id;
|
|
1068
|
-
|
|
1069
|
-
if (existsSync(agentCoordBin)) {
|
|
1070
|
-
spawnSync(agentCoordBin, ["project"], {
|
|
1071
|
-
encoding: "utf8",
|
|
1072
|
-
timeout: 3000,
|
|
1073
|
-
env: childEnv(coordRoot),
|
|
1074
|
-
});
|
|
1075
|
-
spawnSync(
|
|
1076
|
-
agentCoordBin,
|
|
1077
|
-
[
|
|
1078
|
-
"log",
|
|
1079
|
-
`SUBAGENT_START agent_type=${(data.agent_type as string) ?? "unknown"} agent_id=${childInstanceId.slice(0, 8)} platform=${adapterPlatform(adapter)}`,
|
|
1080
|
-
"--instance",
|
|
1081
|
-
childInstanceId,
|
|
1082
|
-
],
|
|
1083
|
-
{ encoding: "utf8", timeout: 2000, env: childEnv(coordRoot) },
|
|
1084
|
-
);
|
|
1085
|
-
}
|
|
1086
|
-
emitSubagentStartContext(coordRoot, childInstanceId, sessionId, data, adapter);
|
|
1125
|
+
await emitSubagentStartContext(coordRoot, childInstanceId, sessionId, data, adapter);
|
|
1087
1126
|
} catch (err) {
|
|
1088
|
-
logError(coordRoot, err, { phase: "subagent-start-
|
|
1127
|
+
logError(coordRoot, err, { phase: "subagent-start-context" });
|
|
1089
1128
|
}
|
|
1090
1129
|
}
|
|
1091
1130
|
|
|
@@ -1093,20 +1132,7 @@ async function main(): Promise<number> {
|
|
|
1093
1132
|
if (norm.event_type === "agent.completed") {
|
|
1094
1133
|
try {
|
|
1095
1134
|
const childInstanceId = payload?.subagent_id ?? payload?.agent_id ?? owner.instance_id;
|
|
1096
|
-
cleanupSessionEnd(coordRoot, childInstanceId
|
|
1097
|
-
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1098
|
-
if (existsSync(agentCoordBin)) {
|
|
1099
|
-
spawnSync(
|
|
1100
|
-
agentCoordBin,
|
|
1101
|
-
[
|
|
1102
|
-
"log",
|
|
1103
|
-
`SUBAGENT_STOP agent_id=${childInstanceId.slice(0, 8)} platform=${adapterPlatform(adapter)}`,
|
|
1104
|
-
"--instance",
|
|
1105
|
-
childInstanceId,
|
|
1106
|
-
],
|
|
1107
|
-
{ encoding: "utf8", timeout: 2000, env: childEnv(coordRoot) },
|
|
1108
|
-
);
|
|
1109
|
-
}
|
|
1135
|
+
cleanupSessionEnd(coordRoot, childInstanceId);
|
|
1110
1136
|
const { reconcileSessionFinalizationV3 } = await import("../agents/session-finalizer-v3.ts");
|
|
1111
1137
|
reconcileSessionFinalizationV3(coordRoot, { archive_observations: [] });
|
|
1112
1138
|
} catch (err) {
|
|
@@ -1494,42 +1520,19 @@ async function runPreToolUseGuard(
|
|
|
1494
1520
|
}
|
|
1495
1521
|
if (targets.length === 0) return true;
|
|
1496
1522
|
|
|
1497
|
-
const
|
|
1498
|
-
if (!existsSync(agentCoordBin)) {
|
|
1499
|
-
if (agentsRequireGitFinalization(coordRoot)) {
|
|
1500
|
-
const { emitDeny } = await import("./adapter/output.ts");
|
|
1501
|
-
emitDeny(
|
|
1502
|
-
adapter,
|
|
1503
|
-
"Harnery denied the write before mutation because the claim coordinator is unavailable; repair the project's Harnery installation and retry.",
|
|
1504
|
-
);
|
|
1505
|
-
return false;
|
|
1506
|
-
}
|
|
1507
|
-
return true;
|
|
1508
|
-
}
|
|
1523
|
+
const { evaluateClaim } = await import("../agents/rules/claim-conflict.ts");
|
|
1509
1524
|
|
|
1510
1525
|
// For apply_patch (multi-file), collect siblings so the deny reason names
|
|
1511
1526
|
// them. For single-file tools the array has one entry.
|
|
1512
1527
|
for (const target of targets) {
|
|
1513
|
-
const verdictReq = JSON.stringify({
|
|
1514
|
-
rule: "claim",
|
|
1515
|
-
instance_id: instanceId,
|
|
1516
|
-
session_id: sessionId,
|
|
1517
|
-
path: target.path,
|
|
1518
|
-
});
|
|
1519
|
-
const result = spawnSync(agentCoordBin, ["verdict"], {
|
|
1520
|
-
input: verdictReq,
|
|
1521
|
-
encoding: "utf8",
|
|
1522
|
-
timeout: 3000,
|
|
1523
|
-
env: childEnv(coordRoot),
|
|
1524
|
-
});
|
|
1525
|
-
if (result.status !== 0 || !result.stdout) continue;
|
|
1526
|
-
let parsed: { allow?: boolean; reason?: string } = {};
|
|
1527
1528
|
try {
|
|
1528
|
-
parsed =
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1529
|
+
const parsed = evaluateClaim(coordRoot, {
|
|
1530
|
+
rule: "claim",
|
|
1531
|
+
instance_id: instanceId,
|
|
1532
|
+
session_id: sessionId,
|
|
1533
|
+
path: target.path,
|
|
1534
|
+
});
|
|
1535
|
+
if (parsed.allow !== false) continue;
|
|
1533
1536
|
let reason =
|
|
1534
1537
|
parsed.reason ?? `Path ${target.path} is currently being edited by another agent.`;
|
|
1535
1538
|
if (targets.length > 1) {
|
|
@@ -1545,7 +1548,7 @@ async function runPreToolUseGuard(
|
|
|
1545
1548
|
const { emitDeny } = await import("./adapter/output.ts");
|
|
1546
1549
|
emitDeny(adapter, reason);
|
|
1547
1550
|
return false;
|
|
1548
|
-
}
|
|
1551
|
+
} catch {}
|
|
1549
1552
|
}
|
|
1550
1553
|
return true;
|
|
1551
1554
|
}
|
|
@@ -1748,16 +1751,23 @@ function releaseClaimOnFailure(
|
|
|
1748
1751
|
const row = readLiveCoordinationRow(coordRoot, instanceId);
|
|
1749
1752
|
if (!row?.files_touched?.includes(canonical)) return;
|
|
1750
1753
|
|
|
1751
|
-
const
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1754
|
+
const before = readHeartbeat(coordRoot, instanceId);
|
|
1755
|
+
try {
|
|
1756
|
+
recordLiveClaimChangeV3({
|
|
1757
|
+
coordRoot,
|
|
1758
|
+
owner: instanceId,
|
|
1759
|
+
nativeSessionId: before?.session_id ?? instanceId,
|
|
1760
|
+
adapter: adapterFromPlatform(before?.platform),
|
|
1761
|
+
operation: "released",
|
|
1762
|
+
path: canonical,
|
|
1763
|
+
access: "write",
|
|
1764
|
+
});
|
|
1765
|
+
} catch {
|
|
1766
|
+
/* best-effort release must not break adapter flow */
|
|
1767
|
+
}
|
|
1758
1768
|
}
|
|
1759
1769
|
|
|
1760
|
-
function cleanupSessionEnd(coordRoot: string, instanceId: string
|
|
1770
|
+
function cleanupSessionEnd(coordRoot: string, instanceId: string): void {
|
|
1761
1771
|
// Sweep pid-map entries pointing to this instance
|
|
1762
1772
|
const pidmapDir = join(coordRoot, ".harnery", "pid-map");
|
|
1763
1773
|
if (existsSync(pidmapDir)) {
|
|
@@ -1779,15 +1789,6 @@ function cleanupSessionEnd(coordRoot: string, instanceId: string, reason: string
|
|
|
1779
1789
|
/* swallow */
|
|
1780
1790
|
}
|
|
1781
1791
|
}
|
|
1782
|
-
// Activity log
|
|
1783
|
-
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1784
|
-
if (existsSync(agentCoordBin)) {
|
|
1785
|
-
spawnSync(
|
|
1786
|
-
agentCoordBin,
|
|
1787
|
-
["log", `SESSION_END reason=${reason}`, "--instance", instanceId],
|
|
1788
|
-
{ encoding: "utf8", timeout: 2000 },
|
|
1789
|
-
);
|
|
1790
|
-
}
|
|
1791
1792
|
}
|
|
1792
1793
|
|
|
1793
1794
|
async function emitUserPromptSubmitSystemMessage(
|
|
@@ -1798,33 +1799,18 @@ async function emitUserPromptSubmitSystemMessage(
|
|
|
1798
1799
|
workspaceCwd?: string,
|
|
1799
1800
|
recoveryBriefing = "",
|
|
1800
1801
|
): Promise<boolean> {
|
|
1801
|
-
const
|
|
1802
|
-
let additionalContext =
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
// Only the boolean route travels through argv, never the host's text.
|
|
1814
|
-
if (adapter === "codex") args.push("--host-prompt-reminder");
|
|
1815
|
-
// Codex gets a fresh status-footer reminder on every prompt. Its Stop hook
|
|
1816
|
-
// stays observe-only, so missing the footer never replaces the answer.
|
|
1817
|
-
// Claude Code gets a fresh per-turn ritual reminder: satisfying the
|
|
1818
|
-
// contract up front is far cheaper than the bounce-and-retry the Stop hook
|
|
1819
|
-
// otherwise forces. Cursor's beforeSubmitPrompt cannot inject context, so
|
|
1820
|
-
// its Stop follow-up remains the supported enforcement channel.
|
|
1821
|
-
args.push("--session-name-nudge");
|
|
1822
|
-
if (adapter === "cursor" || adapter === "codex") args.push("--task-nudge");
|
|
1823
|
-
if (adapter === "codex") args.push("--status-footer-nudge");
|
|
1824
|
-
if (adapter === "claude-code") args.push("--turn-ritual-nudge", adapter);
|
|
1825
|
-
const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
|
|
1826
|
-
if (result.status === 0 && result.stdout) additionalContext = result.stdout.trim();
|
|
1827
|
-
}
|
|
1802
|
+
const { renderPromptContext } = await import("../agents/render/prompt-context.ts");
|
|
1803
|
+
let additionalContext = renderPromptContext({
|
|
1804
|
+
coordRoot,
|
|
1805
|
+
instanceId,
|
|
1806
|
+
sessionId,
|
|
1807
|
+
adapter,
|
|
1808
|
+
sessionNameNudge: true,
|
|
1809
|
+
taskNudge: adapter === "cursor" || adapter === "codex",
|
|
1810
|
+
hostPromptReminder: adapter === "codex",
|
|
1811
|
+
statusFooterNudge: adapter === "codex",
|
|
1812
|
+
turnRitualNudge: adapter === "claude-code" ? adapter : undefined,
|
|
1813
|
+
}).trim();
|
|
1828
1814
|
if (adapter === "codex") {
|
|
1829
1815
|
const fileLinkContext = renderCodexWslFileLinkContext(coordRoot, workspaceCwd);
|
|
1830
1816
|
if (fileLinkContext) {
|
|
@@ -1841,14 +1827,14 @@ async function emitUserPromptSubmitSystemMessage(
|
|
|
1841
1827
|
return true;
|
|
1842
1828
|
}
|
|
1843
1829
|
|
|
1844
|
-
function emitSubagentStartContext(
|
|
1830
|
+
async function emitSubagentStartContext(
|
|
1845
1831
|
coordRoot: string,
|
|
1846
1832
|
instanceId: string,
|
|
1847
1833
|
sessionId: string,
|
|
1848
1834
|
data: Record<string, unknown>,
|
|
1849
1835
|
adapter: Adapter,
|
|
1850
|
-
): void {
|
|
1851
|
-
// Look up the subagent's assigned name (just-written by
|
|
1836
|
+
): Promise<void> {
|
|
1837
|
+
// Look up the subagent's assigned name (just-written by the in-process name
|
|
1852
1838
|
// in session.started data) + the parent's short id for the "you are a subagent
|
|
1853
1839
|
// of X" framing.
|
|
1854
1840
|
const subagentName = (data.name as string | undefined) ?? "";
|
|
@@ -1861,23 +1847,20 @@ function emitSubagentStartContext(
|
|
|
1861
1847
|
// Render peer table inline since the subagent might want to know who else
|
|
1862
1848
|
// is around. Reuse prompt-context (which dedups against the per-owner hash);
|
|
1863
1849
|
// first call will always emit.
|
|
1864
|
-
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1865
1850
|
let combined = message;
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
}
|
|
1851
|
+
const { renderPromptContext } = await import("../agents/render/prompt-context.ts");
|
|
1852
|
+
const context = renderPromptContext({
|
|
1853
|
+
coordRoot,
|
|
1854
|
+
instanceId,
|
|
1855
|
+
sessionId,
|
|
1856
|
+
agentName: subagentName,
|
|
1857
|
+
}).trim();
|
|
1858
|
+
if (context) combined = `${message}\n\n${context}`;
|
|
1875
1859
|
|
|
1876
1860
|
// Use SubagentStart event-name in CC's hookSpecificOutput shape; cursor's
|
|
1877
1861
|
// flat `additional_context` works the same way.
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
});
|
|
1862
|
+
const { emitContext } = await import("./adapter/output.ts");
|
|
1863
|
+
emitContext(adapter, "SubagentStart", combined);
|
|
1881
1864
|
}
|
|
1882
1865
|
|
|
1883
1866
|
function adapterPlatform(adapter: Adapter): string {
|
|
@@ -1885,6 +1868,12 @@ function adapterPlatform(adapter: Adapter): string {
|
|
|
1885
1868
|
return adapter;
|
|
1886
1869
|
}
|
|
1887
1870
|
|
|
1871
|
+
function adapterFromPlatform(platform: unknown): Adapter {
|
|
1872
|
+
if (platform === "cursor") return "cursor";
|
|
1873
|
+
if (platform === "codex") return "codex";
|
|
1874
|
+
return "claude-code";
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1888
1877
|
async function emitSessionStartSystemMessage(
|
|
1889
1878
|
coordRoot: string,
|
|
1890
1879
|
instanceId: string,
|
|
@@ -1893,53 +1882,29 @@ async function emitSessionStartSystemMessage(
|
|
|
1893
1882
|
adapter: Adapter,
|
|
1894
1883
|
recoveryBriefing = "",
|
|
1895
1884
|
): Promise<boolean> {
|
|
1896
|
-
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1897
1885
|
const workflowChild = coordEnv("WORKFLOW_CHILD") === "1";
|
|
1898
1886
|
let additionalContext = "";
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
"
|
|
1919
|
-
|
|
1920
|
-
"--instance",
|
|
1921
|
-
instanceId,
|
|
1922
|
-
],
|
|
1923
|
-
{ encoding: "utf8", timeout: 2000 },
|
|
1924
|
-
);
|
|
1925
|
-
|
|
1926
|
-
// Workflow children retain lifecycle/event capture but do not receive
|
|
1927
|
-
// operator-facing peer, council, or init-remediation context. Injecting
|
|
1928
|
-
// that context can make a bounded child follow housekeeping instructions
|
|
1929
|
-
// instead of its assigned prompt.
|
|
1930
|
-
if (!workflowChild) {
|
|
1931
|
-
const agentName = (emittedData.name as string | undefined) ?? "";
|
|
1932
|
-
const args = ["session-context", "--instance", instanceId, "--session", sessionId];
|
|
1933
|
-
if (agentName) args.push("--name", agentName);
|
|
1934
|
-
// The "You are agent-X." prefix in session-context renders unqualified by
|
|
1935
|
-
// default (claude-code-style). For cursor/codex the bash dispatchers add
|
|
1936
|
-
// a "(Cursor)" / "(Codex)" suffix; pass it through as --platform-label.
|
|
1937
|
-
if (adapter !== "claude-code") {
|
|
1938
|
-
args.push("--platform-label", platform === "cursor" ? "Cursor" : "Codex");
|
|
1939
|
-
}
|
|
1940
|
-
const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
|
|
1941
|
-
if (result.status === 0 && result.stdout) additionalContext = result.stdout.trim();
|
|
1942
|
-
}
|
|
1887
|
+
// Opportunistic reconciliation makes normal session starts a failsafe for
|
|
1888
|
+
// archive, idle, cascade, and host lifecycle observations. It also runs the
|
|
1889
|
+
// stale cache sweep through the shared ADR 0077 composition.
|
|
1890
|
+
const { reconcileCoordinationV3 } = await import("../agents/reconcile-coordination-v3.ts");
|
|
1891
|
+
reconcileCoordinationV3(coordRoot);
|
|
1892
|
+
|
|
1893
|
+
// Workflow children retain lifecycle/event capture but do not receive
|
|
1894
|
+
// operator-facing peer, council, or init-remediation context. Injecting
|
|
1895
|
+
// that context can make a bounded child follow housekeeping instructions
|
|
1896
|
+
// instead of its assigned prompt.
|
|
1897
|
+
if (!workflowChild) {
|
|
1898
|
+
const agentName = (emittedData.name as string | undefined) ?? "";
|
|
1899
|
+
const { renderSessionContext } = await import("../agents/render/session-context.ts");
|
|
1900
|
+
additionalContext = renderSessionContext({
|
|
1901
|
+
coordRoot,
|
|
1902
|
+
instanceId,
|
|
1903
|
+
sessionId,
|
|
1904
|
+
agentName: agentName || undefined,
|
|
1905
|
+
platformLabel:
|
|
1906
|
+
adapter === "claude-code" ? undefined : adapter === "cursor" ? "Cursor" : "Codex",
|
|
1907
|
+
}).trim();
|
|
1943
1908
|
}
|
|
1944
1909
|
|
|
1945
1910
|
// Effect (claude-code): merge the journal recovery cue into the session-start
|
|
@@ -1987,6 +1952,11 @@ function completeRecoveryInjection(
|
|
|
1987
1952
|
|
|
1988
1953
|
main()
|
|
1989
1954
|
.then(async (code) => {
|
|
1955
|
+
writeHookHealthCompletion(hookHealthState, {
|
|
1956
|
+
finished_at_ms: performance.now(),
|
|
1957
|
+
finished_rss_bytes: process.memoryUsage().rss,
|
|
1958
|
+
pid: process.pid,
|
|
1959
|
+
});
|
|
1990
1960
|
await closeProcessLoggers();
|
|
1991
1961
|
process.exit(code);
|
|
1992
1962
|
})
|
|
@@ -1994,6 +1964,13 @@ main()
|
|
|
1994
1964
|
logError(resolveCoordRoot(process.cwd()), err, {
|
|
1995
1965
|
argv: process.argv.slice(2),
|
|
1996
1966
|
pid: process.pid,
|
|
1967
|
+
phase: "top-level",
|
|
1968
|
+
});
|
|
1969
|
+
writeHookHealthCompletion(hookHealthState, {
|
|
1970
|
+
finished_at_ms: performance.now(),
|
|
1971
|
+
finished_rss_bytes: process.memoryUsage().rss,
|
|
1972
|
+
pid: process.pid,
|
|
1973
|
+
faulted: true,
|
|
1997
1974
|
});
|
|
1998
1975
|
await closeProcessLoggers();
|
|
1999
1976
|
process.exit(0);
|