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
|
@@ -21,11 +21,21 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { STOP_REMEDIATION_MARKER } from "../../agents/rules/stop-hook.js";
|
|
23
23
|
import { endOfTurnStatusCommand, resolveBinName } from "../../config.js";
|
|
24
|
+
/**
|
|
25
|
+
* Whether this adapter can actually receive injected context for an event.
|
|
26
|
+
* Cursor's beforeSubmitPrompt hook can allow or block a turn but cannot inject
|
|
27
|
+
* model context, so anything rendered for that event is discarded. A caller
|
|
28
|
+
* that consumes state to build the text (draining a mailbox, for instance)
|
|
29
|
+
* must check this first, or the state is spent on output nobody reads.
|
|
30
|
+
*/
|
|
31
|
+
export function canReceiveContext(adapter, event) {
|
|
32
|
+
return !(adapter === "cursor" && event === "UserPromptSubmit");
|
|
33
|
+
}
|
|
24
34
|
/** Emit a context-injection (peer table, wiring check, council pending, …). */
|
|
25
35
|
export function emitContext(adapter, event, text) {
|
|
26
36
|
if (!text || text.length === 0)
|
|
27
37
|
return;
|
|
28
|
-
if (adapter
|
|
38
|
+
if (!canReceiveContext(adapter, event))
|
|
29
39
|
return;
|
|
30
40
|
const json = buildContextJson(adapter, event, text);
|
|
31
41
|
process.stdout.write(`${JSON.stringify(json)}\n`);
|
package/dist/core/hooks/cli.js
CHANGED
|
@@ -21,15 +21,15 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
|
21
21
|
import { dirname, join } from "node:path";
|
|
22
22
|
import { coordEnv } from "../../lib/env.js";
|
|
23
23
|
import { buildInstructionBundle } from "../../lib/instructions/bundle.js";
|
|
24
|
-
import { coordBinPath } from "../agents/coord-bin.js";
|
|
25
24
|
import { resolveCoordRoot } from "../agents/coord-client.js";
|
|
26
25
|
import { classifyWriteClaimFinalization, formatWriteClaimFinalizationDenial, } from "../agents/finalization.js";
|
|
27
|
-
import { restoreLiveCoordinationStateAfterEpochV3 } from "../agents/live-authority-v3.js";
|
|
26
|
+
import { recordLiveClaimChangeV3, restoreLiveCoordinationStateAfterEpochV3, } from "../agents/live-authority-v3.js";
|
|
28
27
|
import { evaluateStopHook, STOP_REMEDIATION_MARKER } from "../agents/rules/stop-hook.js";
|
|
29
28
|
import { assistantTextStartsWithSessionNameBlock, isSessionNameRemediationCommand, sessionNameDisplayInstruction, sessionNameDisplayPending, sessionNameDisplayRecoveryInstruction, toolResponseMintedSessionName, } from "../agents/session-name-display.js";
|
|
30
29
|
import { readHeartbeat, stampSessionNameSeen } from "../agents/state/heartbeat-writer.js";
|
|
31
30
|
import { readLiveCoordinationRow } from "../agents/state/live-coordination-view.js";
|
|
32
31
|
import { ensureLiveCoordinationHeartbeat } from "../agents/state/live-coordination-writer.js";
|
|
32
|
+
import { assignName } from "../agents/state/names.js";
|
|
33
33
|
import { writePidmapRow } from "../agents/state/pidmap.js";
|
|
34
34
|
import { agentsRequireGitFinalization, resolveBinName } from "../config.js";
|
|
35
35
|
import { checkpointContext, completeContextRecovery, extractContextSample, markContextCompactionCompleted, prepareContextRecovery, readContextState, recordContextSample, } from "../context/index.js";
|
|
@@ -46,12 +46,14 @@ import { codexWslFileLinkTelemetry, inspectCodexWslBridge, isWslUncPath, renderC
|
|
|
46
46
|
import { captureImages, detectPresence, imageJanitor, journalArchive, journalJanitor, journalRecoveryCue, playSound, recordImageArtifactsV3, resetSoundCounters, runSessionSyncExtension, soundForEvent, } from "./effects/index.js";
|
|
47
47
|
import { toolInputHash, toolTargetHash } from "./events/input-hash.js";
|
|
48
48
|
import { canonicalize } from "./guard-path.js";
|
|
49
|
+
import { beginHookHealth, observeHookDebug, observeHookError, writeHookHealthCompletion, } from "./health.js";
|
|
49
50
|
import { adapterPidFromEnv, parsePsChainLine, selectAnchorPid } from "./resolve/anchor.js";
|
|
50
51
|
import { extractIntentComment, resolveIntent } from "./resolve/intent.js";
|
|
51
52
|
import { resolveOwner } from "./resolve/owner.js";
|
|
52
53
|
import { inspectSessionNameDisplayImmediately, scanAssistantStatusBoxPresent, scanSessionNameDisplayedImmediately, scanStatusBoxPresent, scanTranscriptRuntime, } from "./resolve/transcript.js";
|
|
53
54
|
import { sessionNamePresence } from "./session-name-presence.js";
|
|
54
55
|
import { unsafeCrossShellReason } from "./unsafe-cross-shell.js";
|
|
56
|
+
let hookHealthState;
|
|
55
57
|
function parseArgv(argv) {
|
|
56
58
|
const out = { eventName: null, extra: [] };
|
|
57
59
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -90,16 +92,15 @@ async function readStdin() {
|
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
94
|
/**
|
|
93
|
-
* Env for
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* without the pin a child could write state to a different .harnery than the
|
|
97
|
-
* one this hook resolved.
|
|
95
|
+
* Env for a detached hook retry worker, with the coord root pinned. The hook
|
|
96
|
+
* may run with the session shell's cwd (inside a submodule or journal dir), so
|
|
97
|
+
* the worker must retain the root this process already resolved.
|
|
98
98
|
*/
|
|
99
99
|
function childEnv(coordRoot) {
|
|
100
100
|
return { ...process.env, HARNERY_COORD_ROOT_OVERRIDE: coordRoot };
|
|
101
101
|
}
|
|
102
102
|
function appendDebug(coordRoot, entry) {
|
|
103
|
+
observeHookDebug(hookHealthState, entry);
|
|
103
104
|
if (coordEnv("SHARED_LOGS") !== "0") {
|
|
104
105
|
try {
|
|
105
106
|
processLogger(coordRoot, "agent-hook").debug("agent_hook.diagnostic", legacyLogFields(entry));
|
|
@@ -119,6 +120,7 @@ function appendDebug(coordRoot, entry) {
|
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
function logError(coordRoot, err, context) {
|
|
123
|
+
observeHookError(hookHealthState, context.phase);
|
|
122
124
|
if (!coordRoot)
|
|
123
125
|
return;
|
|
124
126
|
if (coordEnv("SHARED_LOGS") !== "0") {
|
|
@@ -145,33 +147,13 @@ function logError(coordRoot, err, context) {
|
|
|
145
147
|
}
|
|
146
148
|
}
|
|
147
149
|
/**
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
* session.started emission never breaks the adapter flow.
|
|
151
|
-
*
|
|
152
|
-
* Lives at agent-hooks side (not agent-coord) to keep emitter/consumer
|
|
153
|
-
* separation: we spawn rather than import.
|
|
150
|
+
* Mint or recover the hurricane-style name for this owner in-process. Returns
|
|
151
|
+
* null on any failure so session.started emission never breaks adapter flow.
|
|
154
152
|
*/
|
|
155
|
-
function
|
|
156
|
-
const binary = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
157
|
-
if (!existsSync(binary))
|
|
158
|
-
return null;
|
|
153
|
+
function assignNameInProcess(coordRoot, instanceId, kind, forkedFrom) {
|
|
159
154
|
try {
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
args.push("--forked-from", forkedFrom);
|
|
163
|
-
const result = spawnSync(binary, args, {
|
|
164
|
-
encoding: "utf8",
|
|
165
|
-
timeout: 2000,
|
|
166
|
-
env: childEnv(coordRoot),
|
|
167
|
-
});
|
|
168
|
-
if (result.status !== 0 || !result.stdout)
|
|
169
|
-
return null;
|
|
170
|
-
const parsed = JSON.parse(result.stdout.trim());
|
|
171
|
-
if (parsed.name && parsed.kind) {
|
|
172
|
-
return { name: parsed.name, kind: parsed.kind };
|
|
173
|
-
}
|
|
174
|
-
return null;
|
|
155
|
+
const name = assignName(coordRoot, instanceId, kind, forkedFrom ? { forkedFrom } : undefined);
|
|
156
|
+
return { name, kind };
|
|
175
157
|
}
|
|
176
158
|
catch {
|
|
177
159
|
return null;
|
|
@@ -227,9 +209,9 @@ function buildEventData(eventType, ctx) {
|
|
|
227
209
|
// tool.requested heal path instead. The forkedFrom plumbing below stays
|
|
228
210
|
// for adapters that DO report a parent at session start.
|
|
229
211
|
const forkedFrom = undefined;
|
|
230
|
-
// Assign (or recover) name + kind
|
|
212
|
+
// Assign (or recover) name + kind in-process. Idempotent: resume
|
|
231
213
|
// returns the original name; new owner consumes a counter slot.
|
|
232
|
-
const assigned =
|
|
214
|
+
const assigned = assignNameInProcess(ctx.coordRoot, ctx.instanceId, "session", forkedFrom);
|
|
233
215
|
// Write the adapter pid-map row so `harn agents whoami` ppid-walks find
|
|
234
216
|
// this owner. Prefer the payload pid (the actual claude binary), then the
|
|
235
217
|
// anchor walk (the `node` ancestor for Cursor, which has no payload pid),
|
|
@@ -293,7 +275,7 @@ function buildEventData(eventType, ctx) {
|
|
|
293
275
|
// a later SessionStart remains idempotent.
|
|
294
276
|
if (ctx.adapter === "cursor" &&
|
|
295
277
|
!readLiveCoordinationRow(ctx.coordRoot, ctx.instanceId)?.name) {
|
|
296
|
-
|
|
278
|
+
assignNameInProcess(ctx.coordRoot, ctx.instanceId, "session");
|
|
297
279
|
}
|
|
298
280
|
const prompt = p?.prompt ?? "";
|
|
299
281
|
const { value, truncated } = clampString(prompt, 4000);
|
|
@@ -351,7 +333,7 @@ function buildEventData(eventType, ctx) {
|
|
|
351
333
|
// (agent-coord/state/names.ts → kind=transient). Use the call ID as the
|
|
352
334
|
// instance_id input; assignName falls through to transient.
|
|
353
335
|
const childInstanceId = subagentCallId ?? ctx.instanceId;
|
|
354
|
-
const assigned =
|
|
336
|
+
const assigned = assignNameInProcess(ctx.coordRoot, childInstanceId, "subagent");
|
|
355
337
|
return {
|
|
356
338
|
agent_type: p?.raw.agent_type ??
|
|
357
339
|
p?.raw.subagent_type ??
|
|
@@ -466,10 +448,18 @@ function numberField(value) {
|
|
|
466
448
|
}
|
|
467
449
|
async function main() {
|
|
468
450
|
const hookStartedAt = performance.now();
|
|
451
|
+
const hookStartedRss = process.memoryUsage().rss;
|
|
469
452
|
const hookClock = captureSpanClockV3();
|
|
470
453
|
const { eventName, extra } = parseArgv(process.argv.slice(2));
|
|
471
454
|
const adapter = detectAdapter(process.argv.slice(2));
|
|
472
455
|
const raw = await readStdin();
|
|
456
|
+
hookHealthState = beginHookHealth({
|
|
457
|
+
started_at_ms: hookStartedAt,
|
|
458
|
+
started_rss_bytes: hookStartedRss,
|
|
459
|
+
event_name: eventName,
|
|
460
|
+
adapter,
|
|
461
|
+
payload_bytes: raw.length,
|
|
462
|
+
});
|
|
473
463
|
// Cursor executes the Claude Code project hooks too on hosts wired for both
|
|
474
464
|
// adapters, piping them its own payload (identifiable by the top-level
|
|
475
465
|
// cursor_version envelope field). That stray `--adapter claude-code`
|
|
@@ -480,6 +470,7 @@ async function main() {
|
|
|
480
470
|
if (coordEnv("AGENT_COORD_OFF") !== "1") {
|
|
481
471
|
const coordRoot = resolveCoordRoot(process.cwd());
|
|
482
472
|
if (coordRoot) {
|
|
473
|
+
hookHealthState.coord_root = coordRoot;
|
|
483
474
|
appendDebug(coordRoot, {
|
|
484
475
|
ts: new Date().toISOString(),
|
|
485
476
|
event_name: eventName,
|
|
@@ -532,6 +523,7 @@ async function main() {
|
|
|
532
523
|
const coordRoot = resolveCoordRoot(process.cwd());
|
|
533
524
|
if (!coordRoot)
|
|
534
525
|
return 0;
|
|
526
|
+
hookHealthState.coord_root = coordRoot;
|
|
535
527
|
if (eventName === "runtime-context-retry") {
|
|
536
528
|
return runRuntimeContextRetryWorker(coordRoot, adapter, extra);
|
|
537
529
|
}
|
|
@@ -557,8 +549,9 @@ async function main() {
|
|
|
557
549
|
// that tool. It is not a transcript and therefore cannot prove that an
|
|
558
550
|
// earlier assistant response omitted the required session-name block.
|
|
559
551
|
// afterAgentResponse is the one native boundary that carries the completed
|
|
560
|
-
// assistant text
|
|
561
|
-
//
|
|
552
|
+
// assistant text. Preserve only privacy-safe ritual booleans in the V3
|
|
553
|
+
// producer's open-turn state; Stop consumes them into the authoritative
|
|
554
|
+
// turn.completed event. The response body never enters V3 or heartbeat.
|
|
562
555
|
if (adapter === "cursor" && eventName === "after-agent-response") {
|
|
563
556
|
const payload = parsePayload(raw, adapter);
|
|
564
557
|
const owner = resolveOwner({ payload: payload?.raw ?? null, coordRoot });
|
|
@@ -575,6 +568,53 @@ async function main() {
|
|
|
575
568
|
else {
|
|
576
569
|
appendDebug(coordRoot, { ...debugBase, skipped: "no-session-name-sighting" });
|
|
577
570
|
}
|
|
571
|
+
if (ledgerRoute.state === "blocked") {
|
|
572
|
+
appendDebug(coordRoot, {
|
|
573
|
+
...debugBase,
|
|
574
|
+
skipped: "v3-control-blocked",
|
|
575
|
+
reason: ledgerRoute.reason,
|
|
576
|
+
owner_source: owner.source,
|
|
577
|
+
});
|
|
578
|
+
return 0;
|
|
579
|
+
}
|
|
580
|
+
const statusBoxPresentStrict = scanAssistantStatusBoxPresent(undefined, text);
|
|
581
|
+
// The hook parser needs the completed reply long enough to scan it, but
|
|
582
|
+
// durable hook intake must never retain that body. Keep every other native
|
|
583
|
+
// field so turn correlation remains available to the producer.
|
|
584
|
+
const { text: _completedReply, ...privacySafeRaw } = payload?.raw ?? {};
|
|
585
|
+
const privacySafePayload = payload ? { ...payload, raw: privacySafeRaw } : null;
|
|
586
|
+
const v3Result = recordLiveHookSignalV3({
|
|
587
|
+
coordRoot,
|
|
588
|
+
route: ledgerRoute,
|
|
589
|
+
eventName,
|
|
590
|
+
payload: privacySafePayload,
|
|
591
|
+
adapter,
|
|
592
|
+
instanceId: owner.instance_id,
|
|
593
|
+
hook_name: eventName,
|
|
594
|
+
hook_duration_ms: Math.max(0, Math.floor(performance.now() - hookStartedAt)),
|
|
595
|
+
monotonic_ns: hookClock.monotonic_ns,
|
|
596
|
+
turn_ritual: {
|
|
597
|
+
status_box_present: statusBoxPresentStrict,
|
|
598
|
+
status_box_present_strict: statusBoxPresentStrict,
|
|
599
|
+
session_name_required: name !== undefined,
|
|
600
|
+
session_name_present: name ? assistantTextStartsWithSessionNameBlock(text, name) : false,
|
|
601
|
+
},
|
|
602
|
+
});
|
|
603
|
+
observeHookDebug(hookHealthState, { event_v3_state: v3Result.state });
|
|
604
|
+
appendDebug(coordRoot, {
|
|
605
|
+
...debugBase,
|
|
606
|
+
effect: "cursor-response-ritual-observed",
|
|
607
|
+
owner_source: owner.source,
|
|
608
|
+
event_v3_state: v3Result.state,
|
|
609
|
+
...(v3Result.state === "observed"
|
|
610
|
+
? {
|
|
611
|
+
generation_id: v3Result.generation_id,
|
|
612
|
+
turn_id: v3Result.turn_id,
|
|
613
|
+
response_observed_at: v3Result.observed_at,
|
|
614
|
+
status_box_present_strict: statusBoxPresentStrict,
|
|
615
|
+
}
|
|
616
|
+
: {}),
|
|
617
|
+
});
|
|
578
618
|
return 0;
|
|
579
619
|
}
|
|
580
620
|
const norm = normalizeEventName(eventName);
|
|
@@ -595,6 +635,7 @@ async function main() {
|
|
|
595
635
|
});
|
|
596
636
|
return 0;
|
|
597
637
|
}
|
|
638
|
+
hookHealthState.owner_id = owner.instance_id;
|
|
598
639
|
const sessionId = payload?.session_id ?? payload?.conversation_id ?? owner.instance_id;
|
|
599
640
|
const priorCoordination = readHeartbeat(coordRoot, owner.instance_id);
|
|
600
641
|
const data = buildEventData(norm.event_type, {
|
|
@@ -661,6 +702,9 @@ async function main() {
|
|
|
661
702
|
...(turnRitual ? { turn_ritual: turnRitual } : {}),
|
|
662
703
|
...(deferV3Drain ? { defer_drain: true } : {}),
|
|
663
704
|
});
|
|
705
|
+
observeHookDebug(hookHealthState, {
|
|
706
|
+
...(v3Result ? { event_v3_state: v3Result.state } : {}),
|
|
707
|
+
});
|
|
664
708
|
const recordedGenerationId = v3Result && "event" in v3Result && "generation_id" in v3Result.event.scope
|
|
665
709
|
? v3Result.event.scope.generation_id
|
|
666
710
|
: undefined;
|
|
@@ -806,6 +850,7 @@ async function main() {
|
|
|
806
850
|
// Session start is the only caller of the artifact janitor and the storage
|
|
807
851
|
// maintenance pass, so both load here instead of on every tool call.
|
|
808
852
|
const { autoCleanArtifacts } = await import("../artifacts/index.js");
|
|
853
|
+
const { autoCleanEventV3Archives } = await import("../events/v3/archive-retention.js");
|
|
809
854
|
const { createAutomaticMaintenanceComposition, runAutomaticMaintenancePass } = await import("../storage/maintenance-providers.js");
|
|
810
855
|
// Effect: throttled daily sweep of expired artifact workspaces (guarded,
|
|
811
856
|
// managed-expired only; see autoCleanArtifacts). Best-effort like every
|
|
@@ -816,6 +861,14 @@ async function main() {
|
|
|
816
861
|
catch (err) {
|
|
817
862
|
logError(coordRoot, err, { phase: "artifact-auto-clean" });
|
|
818
863
|
}
|
|
864
|
+
// Effect: apply the same daily throttle to closed V3 epochs. The newest
|
|
865
|
+
// recovery epochs remain protected by the archive retention policy.
|
|
866
|
+
try {
|
|
867
|
+
autoCleanEventV3Archives(coordRoot);
|
|
868
|
+
}
|
|
869
|
+
catch (err) {
|
|
870
|
+
logError(coordRoot, err, { phase: "event-v3-archive-auto-clean" });
|
|
871
|
+
}
|
|
819
872
|
// Plan one bounded, claim-first global slice from cached pressure only.
|
|
820
873
|
// Existing owner janitors above remain the only active executors until
|
|
821
874
|
// each provider receives a separate production activation.
|
|
@@ -874,7 +927,7 @@ async function main() {
|
|
|
874
927
|
// agnostic since v0.5.0.
|
|
875
928
|
if (norm.event_type === "session.ended") {
|
|
876
929
|
try {
|
|
877
|
-
cleanupSessionEnd(coordRoot, owner.instance_id
|
|
930
|
+
cleanupSessionEnd(coordRoot, owner.instance_id);
|
|
878
931
|
}
|
|
879
932
|
catch (err) {
|
|
880
933
|
logError(coordRoot, err, { phase: "session-end-cleanup" });
|
|
@@ -900,40 +953,17 @@ async function main() {
|
|
|
900
953
|
if (norm.event_type === "agent.started") {
|
|
901
954
|
try {
|
|
902
955
|
const childInstanceId = data.agent_id ?? owner.instance_id;
|
|
903
|
-
|
|
904
|
-
if (existsSync(agentCoordBin)) {
|
|
905
|
-
spawnSync(agentCoordBin, ["project"], {
|
|
906
|
-
encoding: "utf8",
|
|
907
|
-
timeout: 3000,
|
|
908
|
-
env: childEnv(coordRoot),
|
|
909
|
-
});
|
|
910
|
-
spawnSync(agentCoordBin, [
|
|
911
|
-
"log",
|
|
912
|
-
`SUBAGENT_START agent_type=${data.agent_type ?? "unknown"} agent_id=${childInstanceId.slice(0, 8)} platform=${adapterPlatform(adapter)}`,
|
|
913
|
-
"--instance",
|
|
914
|
-
childInstanceId,
|
|
915
|
-
], { encoding: "utf8", timeout: 2000, env: childEnv(coordRoot) });
|
|
916
|
-
}
|
|
917
|
-
emitSubagentStartContext(coordRoot, childInstanceId, sessionId, data, adapter);
|
|
956
|
+
await emitSubagentStartContext(coordRoot, childInstanceId, sessionId, data, adapter);
|
|
918
957
|
}
|
|
919
958
|
catch (err) {
|
|
920
|
-
logError(coordRoot, err, { phase: "subagent-start-
|
|
959
|
+
logError(coordRoot, err, { phase: "subagent-start-context" });
|
|
921
960
|
}
|
|
922
961
|
}
|
|
923
962
|
// Phase 8: SubagentStop: delete subagent heartbeat + log.
|
|
924
963
|
if (norm.event_type === "agent.completed") {
|
|
925
964
|
try {
|
|
926
965
|
const childInstanceId = payload?.subagent_id ?? payload?.agent_id ?? owner.instance_id;
|
|
927
|
-
cleanupSessionEnd(coordRoot, childInstanceId
|
|
928
|
-
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
929
|
-
if (existsSync(agentCoordBin)) {
|
|
930
|
-
spawnSync(agentCoordBin, [
|
|
931
|
-
"log",
|
|
932
|
-
`SUBAGENT_STOP agent_id=${childInstanceId.slice(0, 8)} platform=${adapterPlatform(adapter)}`,
|
|
933
|
-
"--instance",
|
|
934
|
-
childInstanceId,
|
|
935
|
-
], { encoding: "utf8", timeout: 2000, env: childEnv(coordRoot) });
|
|
936
|
-
}
|
|
966
|
+
cleanupSessionEnd(coordRoot, childInstanceId);
|
|
937
967
|
const { reconcileSessionFinalizationV3 } = await import("../agents/session-finalizer-v3.js");
|
|
938
968
|
reconcileSessionFinalizationV3(coordRoot, { archive_observations: [] });
|
|
939
969
|
}
|
|
@@ -1273,40 +1303,19 @@ async function runPreToolUseGuard(coordRoot, instanceId, sessionId, data, adapte
|
|
|
1273
1303
|
}
|
|
1274
1304
|
if (targets.length === 0)
|
|
1275
1305
|
return true;
|
|
1276
|
-
const
|
|
1277
|
-
if (!existsSync(agentCoordBin)) {
|
|
1278
|
-
if (agentsRequireGitFinalization(coordRoot)) {
|
|
1279
|
-
const { emitDeny } = await import("./adapter/output.js");
|
|
1280
|
-
emitDeny(adapter, "Harnery denied the write before mutation because the claim coordinator is unavailable; repair the project's Harnery installation and retry.");
|
|
1281
|
-
return false;
|
|
1282
|
-
}
|
|
1283
|
-
return true;
|
|
1284
|
-
}
|
|
1306
|
+
const { evaluateClaim } = await import("../agents/rules/claim-conflict.js");
|
|
1285
1307
|
// For apply_patch (multi-file), collect siblings so the deny reason names
|
|
1286
1308
|
// them. For single-file tools the array has one entry.
|
|
1287
1309
|
for (const target of targets) {
|
|
1288
|
-
const verdictReq = JSON.stringify({
|
|
1289
|
-
rule: "claim",
|
|
1290
|
-
instance_id: instanceId,
|
|
1291
|
-
session_id: sessionId,
|
|
1292
|
-
path: target.path,
|
|
1293
|
-
});
|
|
1294
|
-
const result = spawnSync(agentCoordBin, ["verdict"], {
|
|
1295
|
-
input: verdictReq,
|
|
1296
|
-
encoding: "utf8",
|
|
1297
|
-
timeout: 3000,
|
|
1298
|
-
env: childEnv(coordRoot),
|
|
1299
|
-
});
|
|
1300
|
-
if (result.status !== 0 || !result.stdout)
|
|
1301
|
-
continue;
|
|
1302
|
-
let parsed = {};
|
|
1303
1310
|
try {
|
|
1304
|
-
parsed =
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1311
|
+
const parsed = evaluateClaim(coordRoot, {
|
|
1312
|
+
rule: "claim",
|
|
1313
|
+
instance_id: instanceId,
|
|
1314
|
+
session_id: sessionId,
|
|
1315
|
+
path: target.path,
|
|
1316
|
+
});
|
|
1317
|
+
if (parsed.allow !== false)
|
|
1318
|
+
continue;
|
|
1310
1319
|
let reason = parsed.reason ?? `Path ${target.path} is currently being edited by another agent.`;
|
|
1311
1320
|
if (targets.length > 1) {
|
|
1312
1321
|
const siblings = targets
|
|
@@ -1322,6 +1331,7 @@ async function runPreToolUseGuard(coordRoot, instanceId, sessionId, data, adapte
|
|
|
1322
1331
|
emitDeny(adapter, reason);
|
|
1323
1332
|
return false;
|
|
1324
1333
|
}
|
|
1334
|
+
catch { }
|
|
1325
1335
|
}
|
|
1326
1336
|
return true;
|
|
1327
1337
|
}
|
|
@@ -1520,16 +1530,23 @@ function releaseClaimOnFailure(coordRoot, instanceId, data, rawPayload) {
|
|
|
1520
1530
|
const row = readLiveCoordinationRow(coordRoot, instanceId);
|
|
1521
1531
|
if (!row?.files_touched?.includes(canonical))
|
|
1522
1532
|
return;
|
|
1523
|
-
const
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1533
|
+
const before = readHeartbeat(coordRoot, instanceId);
|
|
1534
|
+
try {
|
|
1535
|
+
recordLiveClaimChangeV3({
|
|
1536
|
+
coordRoot,
|
|
1537
|
+
owner: instanceId,
|
|
1538
|
+
nativeSessionId: before?.session_id ?? instanceId,
|
|
1539
|
+
adapter: adapterFromPlatform(before?.platform),
|
|
1540
|
+
operation: "released",
|
|
1541
|
+
path: canonical,
|
|
1542
|
+
access: "write",
|
|
1543
|
+
});
|
|
1544
|
+
}
|
|
1545
|
+
catch {
|
|
1546
|
+
/* best-effort release must not break adapter flow */
|
|
1547
|
+
}
|
|
1531
1548
|
}
|
|
1532
|
-
function cleanupSessionEnd(coordRoot, instanceId
|
|
1549
|
+
function cleanupSessionEnd(coordRoot, instanceId) {
|
|
1533
1550
|
// Sweep pid-map entries pointing to this instance
|
|
1534
1551
|
const pidmapDir = join(coordRoot, ".harnery", "pid-map");
|
|
1535
1552
|
if (existsSync(pidmapDir)) {
|
|
@@ -1553,44 +1570,20 @@ function cleanupSessionEnd(coordRoot, instanceId, reason) {
|
|
|
1553
1570
|
/* swallow */
|
|
1554
1571
|
}
|
|
1555
1572
|
}
|
|
1556
|
-
// Activity log
|
|
1557
|
-
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1558
|
-
if (existsSync(agentCoordBin)) {
|
|
1559
|
-
spawnSync(agentCoordBin, ["log", `SESSION_END reason=${reason}`, "--instance", instanceId], { encoding: "utf8", timeout: 2000 });
|
|
1560
|
-
}
|
|
1561
1573
|
}
|
|
1562
1574
|
async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionId, adapter, workspaceCwd, recoveryBriefing = "") {
|
|
1563
|
-
const
|
|
1564
|
-
let additionalContext =
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
if (adapter === "codex")
|
|
1576
|
-
args.push("--host-prompt-reminder");
|
|
1577
|
-
// Codex gets a fresh status-footer reminder on every prompt. Its Stop hook
|
|
1578
|
-
// stays observe-only, so missing the footer never replaces the answer.
|
|
1579
|
-
// Claude Code gets a fresh per-turn ritual reminder: satisfying the
|
|
1580
|
-
// contract up front is far cheaper than the bounce-and-retry the Stop hook
|
|
1581
|
-
// otherwise forces. Cursor's beforeSubmitPrompt cannot inject context, so
|
|
1582
|
-
// its Stop follow-up remains the supported enforcement channel.
|
|
1583
|
-
args.push("--session-name-nudge");
|
|
1584
|
-
if (adapter === "cursor" || adapter === "codex")
|
|
1585
|
-
args.push("--task-nudge");
|
|
1586
|
-
if (adapter === "codex")
|
|
1587
|
-
args.push("--status-footer-nudge");
|
|
1588
|
-
if (adapter === "claude-code")
|
|
1589
|
-
args.push("--turn-ritual-nudge", adapter);
|
|
1590
|
-
const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
|
|
1591
|
-
if (result.status === 0 && result.stdout)
|
|
1592
|
-
additionalContext = result.stdout.trim();
|
|
1593
|
-
}
|
|
1575
|
+
const { renderPromptContext } = await import("../agents/render/prompt-context.js");
|
|
1576
|
+
let additionalContext = renderPromptContext({
|
|
1577
|
+
coordRoot,
|
|
1578
|
+
instanceId,
|
|
1579
|
+
sessionId,
|
|
1580
|
+
adapter,
|
|
1581
|
+
sessionNameNudge: true,
|
|
1582
|
+
taskNudge: adapter === "cursor" || adapter === "codex",
|
|
1583
|
+
hostPromptReminder: adapter === "codex",
|
|
1584
|
+
statusFooterNudge: adapter === "codex",
|
|
1585
|
+
turnRitualNudge: adapter === "claude-code" ? adapter : undefined,
|
|
1586
|
+
}).trim();
|
|
1594
1587
|
if (adapter === "codex") {
|
|
1595
1588
|
const fileLinkContext = renderCodexWslFileLinkContext(coordRoot, workspaceCwd);
|
|
1596
1589
|
if (fileLinkContext) {
|
|
@@ -1606,8 +1599,8 @@ async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionI
|
|
|
1606
1599
|
emitContext(adapter, "UserPromptSubmit", additionalContext);
|
|
1607
1600
|
return true;
|
|
1608
1601
|
}
|
|
1609
|
-
function emitSubagentStartContext(coordRoot, instanceId, sessionId, data, adapter) {
|
|
1610
|
-
// Look up the subagent's assigned name (just-written by
|
|
1602
|
+
async function emitSubagentStartContext(coordRoot, instanceId, sessionId, data, adapter) {
|
|
1603
|
+
// Look up the subagent's assigned name (just-written by the in-process name
|
|
1611
1604
|
// in session.started data) + the parent's short id for the "you are a subagent
|
|
1612
1605
|
// of X" framing.
|
|
1613
1606
|
const subagentName = data.name ?? "";
|
|
@@ -1619,69 +1612,55 @@ function emitSubagentStartContext(coordRoot, instanceId, sessionId, data, adapte
|
|
|
1619
1612
|
// Render peer table inline since the subagent might want to know who else
|
|
1620
1613
|
// is around. Reuse prompt-context (which dedups against the per-owner hash);
|
|
1621
1614
|
// first call will always emit.
|
|
1622
|
-
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1623
1615
|
let combined = message;
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1616
|
+
const { renderPromptContext } = await import("../agents/render/prompt-context.js");
|
|
1617
|
+
const context = renderPromptContext({
|
|
1618
|
+
coordRoot,
|
|
1619
|
+
instanceId,
|
|
1620
|
+
sessionId,
|
|
1621
|
+
agentName: subagentName,
|
|
1622
|
+
}).trim();
|
|
1623
|
+
if (context)
|
|
1624
|
+
combined = `${message}\n\n${context}`;
|
|
1630
1625
|
// Use SubagentStart event-name in CC's hookSpecificOutput shape; cursor's
|
|
1631
1626
|
// flat `additional_context` works the same way.
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
});
|
|
1627
|
+
const { emitContext } = await import("./adapter/output.js");
|
|
1628
|
+
emitContext(adapter, "SubagentStart", combined);
|
|
1635
1629
|
}
|
|
1636
1630
|
function adapterPlatform(adapter) {
|
|
1637
1631
|
if (adapter === "claude-code")
|
|
1638
1632
|
return "claude-code";
|
|
1639
1633
|
return adapter;
|
|
1640
1634
|
}
|
|
1635
|
+
function adapterFromPlatform(platform) {
|
|
1636
|
+
if (platform === "cursor")
|
|
1637
|
+
return "cursor";
|
|
1638
|
+
if (platform === "codex")
|
|
1639
|
+
return "codex";
|
|
1640
|
+
return "claude-code";
|
|
1641
|
+
}
|
|
1641
1642
|
async function emitSessionStartSystemMessage(coordRoot, instanceId, sessionId, emittedData, adapter, recoveryBriefing = "") {
|
|
1642
|
-
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1643
1643
|
const workflowChild = coordEnv("WORKFLOW_CHILD") === "1";
|
|
1644
1644
|
let additionalContext = "";
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
const platform = adapterPlatform(adapter);
|
|
1660
|
-
spawnSync(agentCoordBin, [
|
|
1661
|
-
"log",
|
|
1662
|
-
`SESSION_START model=${model} source=${source} platform=${platform}`,
|
|
1663
|
-
"--instance",
|
|
1645
|
+
// Opportunistic reconciliation makes normal session starts a failsafe for
|
|
1646
|
+
// archive, idle, cascade, and host lifecycle observations. It also runs the
|
|
1647
|
+
// stale cache sweep through the shared ADR 0077 composition.
|
|
1648
|
+
const { reconcileCoordinationV3 } = await import("../agents/reconcile-coordination-v3.js");
|
|
1649
|
+
reconcileCoordinationV3(coordRoot);
|
|
1650
|
+
// Workflow children retain lifecycle/event capture but do not receive
|
|
1651
|
+
// operator-facing peer, council, or init-remediation context. Injecting
|
|
1652
|
+
// that context can make a bounded child follow housekeeping instructions
|
|
1653
|
+
// instead of its assigned prompt.
|
|
1654
|
+
if (!workflowChild) {
|
|
1655
|
+
const agentName = emittedData.name ?? "";
|
|
1656
|
+
const { renderSessionContext } = await import("../agents/render/session-context.js");
|
|
1657
|
+
additionalContext = renderSessionContext({
|
|
1658
|
+
coordRoot,
|
|
1664
1659
|
instanceId,
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
// instead of its assigned prompt.
|
|
1670
|
-
if (!workflowChild) {
|
|
1671
|
-
const agentName = emittedData.name ?? "";
|
|
1672
|
-
const args = ["session-context", "--instance", instanceId, "--session", sessionId];
|
|
1673
|
-
if (agentName)
|
|
1674
|
-
args.push("--name", agentName);
|
|
1675
|
-
// The "You are agent-X." prefix in session-context renders unqualified by
|
|
1676
|
-
// default (claude-code-style). For cursor/codex the bash dispatchers add
|
|
1677
|
-
// a "(Cursor)" / "(Codex)" suffix; pass it through as --platform-label.
|
|
1678
|
-
if (adapter !== "claude-code") {
|
|
1679
|
-
args.push("--platform-label", platform === "cursor" ? "Cursor" : "Codex");
|
|
1680
|
-
}
|
|
1681
|
-
const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
|
|
1682
|
-
if (result.status === 0 && result.stdout)
|
|
1683
|
-
additionalContext = result.stdout.trim();
|
|
1684
|
-
}
|
|
1660
|
+
sessionId,
|
|
1661
|
+
agentName: agentName || undefined,
|
|
1662
|
+
platformLabel: adapter === "claude-code" ? undefined : adapter === "cursor" ? "Cursor" : "Codex",
|
|
1663
|
+
}).trim();
|
|
1685
1664
|
}
|
|
1686
1665
|
// Effect (claude-code): merge the journal recovery cue into the session-start
|
|
1687
1666
|
// context. Was a standalone additionalContext emission from the previous
|
|
@@ -1722,6 +1701,11 @@ function completeRecoveryInjection(coordRoot, instanceId, sessionId, recovery) {
|
|
|
1722
1701
|
}
|
|
1723
1702
|
main()
|
|
1724
1703
|
.then(async (code) => {
|
|
1704
|
+
writeHookHealthCompletion(hookHealthState, {
|
|
1705
|
+
finished_at_ms: performance.now(),
|
|
1706
|
+
finished_rss_bytes: process.memoryUsage().rss,
|
|
1707
|
+
pid: process.pid,
|
|
1708
|
+
});
|
|
1725
1709
|
await closeProcessLoggers();
|
|
1726
1710
|
process.exit(code);
|
|
1727
1711
|
})
|
|
@@ -1729,6 +1713,13 @@ main()
|
|
|
1729
1713
|
logError(resolveCoordRoot(process.cwd()), err, {
|
|
1730
1714
|
argv: process.argv.slice(2),
|
|
1731
1715
|
pid: process.pid,
|
|
1716
|
+
phase: "top-level",
|
|
1717
|
+
});
|
|
1718
|
+
writeHookHealthCompletion(hookHealthState, {
|
|
1719
|
+
finished_at_ms: performance.now(),
|
|
1720
|
+
finished_rss_bytes: process.memoryUsage().rss,
|
|
1721
|
+
pid: process.pid,
|
|
1722
|
+
faulted: true,
|
|
1732
1723
|
});
|
|
1733
1724
|
await closeProcessLoggers();
|
|
1734
1725
|
process.exit(0);
|