@amsterdamdatalabs/enact-operator 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/lanes/autopilot/index.d.ts +95 -0
- package/dist/lanes/autopilot/index.d.ts.map +1 -0
- package/dist/lanes/autopilot/index.js +499 -0
- package/dist/lanes/autopilot/index.js.map +1 -0
- package/dist/lanes/ralph/index.d.ts +118 -0
- package/dist/lanes/ralph/index.d.ts.map +1 -0
- package/dist/lanes/ralph/index.js +680 -0
- package/dist/lanes/ralph/index.js.map +1 -0
- package/dist/lanes/team/index.d.ts +110 -0
- package/dist/lanes/team/index.d.ts.map +1 -0
- package/dist/lanes/team/index.js +748 -0
- package/dist/lanes/team/index.js.map +1 -0
- package/dist/lanes/team/roles.d.ts +15 -0
- package/dist/lanes/team/roles.d.ts.map +1 -0
- package/dist/lanes/team/roles.js +89 -0
- package/dist/lanes/team/roles.js.map +1 -0
- package/dist/lanes/ultrawork/burst.d.ts +69 -0
- package/dist/lanes/ultrawork/burst.d.ts.map +1 -0
- package/dist/lanes/ultrawork/burst.js +254 -0
- package/dist/lanes/ultrawork/burst.js.map +1 -0
- package/dist/lanes/ultrawork/index.d.ts +159 -0
- package/dist/lanes/ultrawork/index.d.ts.map +1 -0
- package/dist/lanes/ultrawork/index.js +919 -0
- package/dist/lanes/ultrawork/index.js.map +1 -0
- package/dist/lanes/ultrawork/phases.d.ts +57 -0
- package/dist/lanes/ultrawork/phases.d.ts.map +1 -0
- package/dist/lanes/ultrawork/phases.js +116 -0
- package/dist/lanes/ultrawork/phases.js.map +1 -0
- package/dist/mcp/activation.d.ts +5 -0
- package/dist/mcp/activation.d.ts.map +1 -0
- package/dist/mcp/activation.js +234 -0
- package/dist/mcp/activation.js.map +1 -0
- package/dist/mcp/cli.d.ts +11 -0
- package/dist/mcp/cli.d.ts.map +1 -0
- package/dist/mcp/cli.js +140 -0
- package/dist/mcp/cli.js.map +1 -0
- package/dist/mcp/continuation.d.ts +5 -0
- package/dist/mcp/continuation.d.ts.map +1 -0
- package/dist/mcp/continuation.js +360 -0
- package/dist/mcp/continuation.js.map +1 -0
- package/dist/mcp/jobs.d.ts +5 -0
- package/dist/mcp/jobs.d.ts.map +1 -0
- package/dist/mcp/jobs.js +212 -0
- package/dist/mcp/jobs.js.map +1 -0
- package/dist/mcp/laneSupport.d.ts +19 -0
- package/dist/mcp/laneSupport.d.ts.map +1 -0
- package/dist/mcp/laneSupport.js +21 -0
- package/dist/mcp/laneSupport.js.map +1 -0
- package/dist/mcp/lanes/autopilot.d.ts +5 -0
- package/dist/mcp/lanes/autopilot.d.ts.map +1 -0
- package/dist/mcp/lanes/autopilot.js +227 -0
- package/dist/mcp/lanes/autopilot.js.map +1 -0
- package/dist/mcp/lanes/ralph.d.ts +5 -0
- package/dist/mcp/lanes/ralph.d.ts.map +1 -0
- package/dist/mcp/lanes/ralph.js +392 -0
- package/dist/mcp/lanes/ralph.js.map +1 -0
- package/dist/mcp/lanes/team.d.ts +5 -0
- package/dist/mcp/lanes/team.d.ts.map +1 -0
- package/dist/mcp/lanes/team.js +413 -0
- package/dist/mcp/lanes/team.js.map +1 -0
- package/dist/mcp/lanes/ultrawork.d.ts +5 -0
- package/dist/mcp/lanes/ultrawork.d.ts.map +1 -0
- package/dist/mcp/lanes/ultrawork.js +497 -0
- package/dist/mcp/lanes/ultrawork.js.map +1 -0
- package/dist/mcp/linkage.d.ts +5 -0
- package/dist/mcp/linkage.d.ts.map +1 -0
- package/dist/mcp/linkage.js +126 -0
- package/dist/mcp/linkage.js.map +1 -0
- package/dist/mcp/planning.d.ts +5 -0
- package/dist/mcp/planning.d.ts.map +1 -0
- package/dist/mcp/planning.js +584 -0
- package/dist/mcp/planning.js.map +1 -0
- package/dist/mcp/rpcTransport.d.ts +17 -0
- package/dist/mcp/rpcTransport.d.ts.map +1 -0
- package/dist/mcp/rpcTransport.js +90 -0
- package/dist/mcp/rpcTransport.js.map +1 -0
- package/dist/mcp/schemas/ralphVerify.d.ts +32 -0
- package/dist/mcp/schemas/ralphVerify.d.ts.map +1 -0
- package/dist/mcp/schemas/ralphVerify.js +20 -0
- package/dist/mcp/schemas/ralphVerify.js.map +1 -0
- package/dist/mcp/server.d.ts +5 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +176 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/system.d.ts +5 -0
- package/dist/mcp/system.d.ts.map +1 -0
- package/dist/mcp/system.js +445 -0
- package/dist/mcp/system.js.map +1 -0
- package/dist/mcp/toolFilter.d.ts +2 -0
- package/dist/mcp/toolFilter.d.ts.map +1 -0
- package/dist/mcp/toolFilter.js +2 -0
- package/dist/mcp/toolFilter.js.map +1 -0
- package/dist/mcp/toolPartitions.d.ts +20 -0
- package/dist/mcp/toolPartitions.d.ts.map +1 -0
- package/dist/mcp/toolPartitions.js +280 -0
- package/dist/mcp/toolPartitions.js.map +1 -0
- package/dist/mcp/toolRuntime.d.ts +9 -0
- package/dist/mcp/toolRuntime.d.ts.map +1 -0
- package/dist/mcp/toolRuntime.js +179 -0
- package/dist/mcp/toolRuntime.js.map +1 -0
- package/dist/mcp/workflow.d.ts +12 -0
- package/dist/mcp/workflow.d.ts.map +1 -0
- package/dist/mcp/workflow.js +723 -0
- package/dist/mcp/workflow.js.map +1 -0
- package/dist/operator/hooks/hookDispatcher.d.ts +39 -0
- package/dist/operator/hooks/hookDispatcher.d.ts.map +1 -0
- package/dist/operator/hooks/hookDispatcher.js +58 -0
- package/dist/operator/hooks/hookDispatcher.js.map +1 -0
- package/dist/operator/hooks/hookGating.d.ts +28 -0
- package/dist/operator/hooks/hookGating.d.ts.map +1 -0
- package/dist/operator/hooks/hookGating.js +61 -0
- package/dist/operator/hooks/hookGating.js.map +1 -0
- package/dist/operator/hooks/hooks.d.ts +20 -0
- package/dist/operator/hooks/hooks.d.ts.map +1 -0
- package/dist/operator/hooks/hooks.js +124 -0
- package/dist/operator/hooks/hooks.js.map +1 -0
- package/dist/operator/hooks/toolGuardHooks.d.ts +86 -0
- package/dist/operator/hooks/toolGuardHooks.d.ts.map +1 -0
- package/dist/operator/hooks/toolGuardHooks.js +163 -0
- package/dist/operator/hooks/toolGuardHooks.js.map +1 -0
- package/dist/operator/hud.d.ts +115 -0
- package/dist/operator/hud.d.ts.map +1 -0
- package/dist/operator/hud.js +229 -0
- package/dist/operator/hud.js.map +1 -0
- package/dist/operator/missions.d.ts +125 -0
- package/dist/operator/missions.d.ts.map +1 -0
- package/dist/operator/missions.js +304 -0
- package/dist/operator/missions.js.map +1 -0
- package/dist/operator/operatorCore.d.ts +11 -0
- package/dist/operator/operatorCore.d.ts.map +1 -0
- package/dist/operator/operatorCore.js +9 -0
- package/dist/operator/operatorCore.js.map +1 -0
- package/dist/operator/operatorRuntimeCore.d.ts +17 -0
- package/dist/operator/operatorRuntimeCore.d.ts.map +1 -0
- package/dist/operator/operatorRuntimeCore.js +17 -0
- package/dist/operator/operatorRuntimeCore.js.map +1 -0
- package/dist/operator/runtime.d.ts +74 -0
- package/dist/operator/runtime.d.ts.map +1 -0
- package/dist/operator/runtime.js +357 -0
- package/dist/operator/runtime.js.map +1 -0
- package/dist/shared/core/bootstrap.d.ts +16 -0
- package/dist/shared/core/bootstrap.d.ts.map +1 -0
- package/dist/shared/core/bootstrap.js +77 -0
- package/dist/shared/core/bootstrap.js.map +1 -0
- package/dist/shared/core/contract.d.ts +71 -0
- package/dist/shared/core/contract.d.ts.map +1 -0
- package/dist/shared/core/contract.js +452 -0
- package/dist/shared/core/contract.js.map +1 -0
- package/dist/shared/core/events.d.ts +57 -0
- package/dist/shared/core/events.d.ts.map +1 -0
- package/dist/shared/core/events.js +36 -0
- package/dist/shared/core/events.js.map +1 -0
- package/dist/shared/core/jobs.d.ts +46 -0
- package/dist/shared/core/jobs.d.ts.map +1 -0
- package/dist/shared/core/jobs.js +137 -0
- package/dist/shared/core/jobs.js.map +1 -0
- package/dist/shared/core/json.d.ts +12 -0
- package/dist/shared/core/json.d.ts.map +1 -0
- package/dist/shared/core/json.js +52 -0
- package/dist/shared/core/json.js.map +1 -0
- package/dist/shared/core/layoutMigration.d.ts +11 -0
- package/dist/shared/core/layoutMigration.d.ts.map +1 -0
- package/dist/shared/core/layoutMigration.js +56 -0
- package/dist/shared/core/layoutMigration.js.map +1 -0
- package/dist/shared/core/lifecycle.d.ts +61 -0
- package/dist/shared/core/lifecycle.d.ts.map +1 -0
- package/dist/shared/core/lifecycle.js +123 -0
- package/dist/shared/core/lifecycle.js.map +1 -0
- package/dist/shared/core/packagePaths.d.ts +2 -0
- package/dist/shared/core/packagePaths.d.ts.map +1 -0
- package/dist/shared/core/packagePaths.js +9 -0
- package/dist/shared/core/packagePaths.js.map +1 -0
- package/dist/shared/core/terminology.d.ts +38 -0
- package/dist/shared/core/terminology.d.ts.map +1 -0
- package/dist/shared/core/terminology.js +54 -0
- package/dist/shared/core/terminology.js.map +1 -0
- package/dist/shared/core/types.d.ts +30 -0
- package/dist/shared/core/types.d.ts.map +1 -0
- package/dist/shared/core/types.js +2 -0
- package/dist/shared/core/types.js.map +1 -0
- package/dist/shared/diagnostics/doctor.d.ts +19 -0
- package/dist/shared/diagnostics/doctor.d.ts.map +1 -0
- package/dist/shared/diagnostics/doctor.js +82 -0
- package/dist/shared/diagnostics/doctor.js.map +1 -0
- package/dist/shared/diagnostics/hostRollout.d.ts +16 -0
- package/dist/shared/diagnostics/hostRollout.d.ts.map +1 -0
- package/dist/shared/diagnostics/hostRollout.js +78 -0
- package/dist/shared/diagnostics/hostRollout.js.map +1 -0
- package/dist/shared/observability/emit.d.ts +3 -0
- package/dist/shared/observability/emit.d.ts.map +1 -0
- package/dist/shared/observability/emit.js +17 -0
- package/dist/shared/observability/emit.js.map +1 -0
- package/dist/shared/observability/testWorkspace.d.ts +4 -0
- package/dist/shared/observability/testWorkspace.d.ts.map +1 -0
- package/dist/shared/observability/testWorkspace.js +35 -0
- package/dist/shared/observability/testWorkspace.js.map +1 -0
- package/dist/shared/state/installRegistry.d.ts +22 -0
- package/dist/shared/state/installRegistry.d.ts.map +1 -0
- package/dist/shared/state/installRegistry.js +51 -0
- package/dist/shared/state/installRegistry.js.map +1 -0
- package/dist/shared/state/session.d.ts +18 -0
- package/dist/shared/state/session.d.ts.map +1 -0
- package/dist/shared/state/session.js +127 -0
- package/dist/shared/state/session.js.map +1 -0
- package/dist/shared/state/state.d.ts +16 -0
- package/dist/shared/state/state.d.ts.map +1 -0
- package/dist/shared/state/state.js +91 -0
- package/dist/shared/state/state.js.map +1 -0
- package/dist/shared/state/tasks.d.ts +113 -0
- package/dist/shared/state/tasks.d.ts.map +1 -0
- package/dist/shared/state/tasks.js +274 -0
- package/dist/shared/state/tasks.js.map +1 -0
- package/dist/shared/workflow/activation/activeSkill.d.ts +46 -0
- package/dist/shared/workflow/activation/activeSkill.d.ts.map +1 -0
- package/dist/shared/workflow/activation/activeSkill.js +158 -0
- package/dist/shared/workflow/activation/activeSkill.js.map +1 -0
- package/dist/shared/workflow/activation/gateProfiles.d.ts +26 -0
- package/dist/shared/workflow/activation/gateProfiles.d.ts.map +1 -0
- package/dist/shared/workflow/activation/gateProfiles.js +102 -0
- package/dist/shared/workflow/activation/gateProfiles.js.map +1 -0
- package/dist/shared/workflow/activation/modeMatrix.d.ts +28 -0
- package/dist/shared/workflow/activation/modeMatrix.d.ts.map +1 -0
- package/dist/shared/workflow/activation/modeMatrix.js +91 -0
- package/dist/shared/workflow/activation/modeMatrix.js.map +1 -0
- package/dist/shared/workflow/activation/skillActivation.d.ts +74 -0
- package/dist/shared/workflow/activation/skillActivation.d.ts.map +1 -0
- package/dist/shared/workflow/activation/skillActivation.js +485 -0
- package/dist/shared/workflow/activation/skillActivation.js.map +1 -0
- package/dist/shared/workflow/activation/skillVariant.d.ts +18 -0
- package/dist/shared/workflow/activation/skillVariant.d.ts.map +1 -0
- package/dist/shared/workflow/activation/skillVariant.js +44 -0
- package/dist/shared/workflow/activation/skillVariant.js.map +1 -0
- package/dist/shared/workflow/authority/authority.d.ts +34 -0
- package/dist/shared/workflow/authority/authority.d.ts.map +1 -0
- package/dist/shared/workflow/authority/authority.js +64 -0
- package/dist/shared/workflow/authority/authority.js.map +1 -0
- package/dist/shared/workflow/closure/closureManifest.d.ts +39 -0
- package/dist/shared/workflow/closure/closureManifest.d.ts.map +1 -0
- package/dist/shared/workflow/closure/closureManifest.js +62 -0
- package/dist/shared/workflow/closure/closureManifest.js.map +1 -0
- package/dist/shared/workflow/closure/closureRequirements.d.ts +12 -0
- package/dist/shared/workflow/closure/closureRequirements.d.ts.map +1 -0
- package/dist/shared/workflow/closure/closureRequirements.js +30 -0
- package/dist/shared/workflow/closure/closureRequirements.js.map +1 -0
- package/dist/shared/workflow/closure/contractParity.d.ts +24 -0
- package/dist/shared/workflow/closure/contractParity.d.ts.map +1 -0
- package/dist/shared/workflow/closure/contractParity.js +238 -0
- package/dist/shared/workflow/closure/contractParity.js.map +1 -0
- package/dist/shared/workflow/closure/contractParityContext.d.ts +8 -0
- package/dist/shared/workflow/closure/contractParityContext.d.ts.map +1 -0
- package/dist/shared/workflow/closure/contractParityContext.js +50 -0
- package/dist/shared/workflow/closure/contractParityContext.js.map +1 -0
- package/dist/shared/workflow/closure/scopeGuard.d.ts +11 -0
- package/dist/shared/workflow/closure/scopeGuard.d.ts.map +1 -0
- package/dist/shared/workflow/closure/scopeGuard.js +69 -0
- package/dist/shared/workflow/closure/scopeGuard.js.map +1 -0
- package/dist/shared/workflow/closure/ultragoalArtifact.d.ts +13 -0
- package/dist/shared/workflow/closure/ultragoalArtifact.d.ts.map +1 -0
- package/dist/shared/workflow/closure/ultragoalArtifact.js +31 -0
- package/dist/shared/workflow/closure/ultragoalArtifact.js.map +1 -0
- package/dist/shared/workflow/closure/workflowReconcile.d.ts +70 -0
- package/dist/shared/workflow/closure/workflowReconcile.d.ts.map +1 -0
- package/dist/shared/workflow/closure/workflowReconcile.js +267 -0
- package/dist/shared/workflow/closure/workflowReconcile.js.map +1 -0
- package/dist/shared/workflow/continuation/continuation.d.ts +109 -0
- package/dist/shared/workflow/continuation/continuation.d.ts.map +1 -0
- package/dist/shared/workflow/continuation/continuation.js +550 -0
- package/dist/shared/workflow/continuation/continuation.js.map +1 -0
- package/dist/shared/workflow/continuation/continuationEventReservations.d.ts +17 -0
- package/dist/shared/workflow/continuation/continuationEventReservations.d.ts.map +1 -0
- package/dist/shared/workflow/continuation/continuationEventReservations.js +88 -0
- package/dist/shared/workflow/continuation/continuationEventReservations.js.map +1 -0
- package/dist/shared/workflow/continuation/continuationFollowUp.d.ts +114 -0
- package/dist/shared/workflow/continuation/continuationFollowUp.d.ts.map +1 -0
- package/dist/shared/workflow/continuation/continuationFollowUp.js +330 -0
- package/dist/shared/workflow/continuation/continuationFollowUp.js.map +1 -0
- package/dist/shared/workflow/continuation/continuationOrchestrator.d.ts +66 -0
- package/dist/shared/workflow/continuation/continuationOrchestrator.d.ts.map +1 -0
- package/dist/shared/workflow/continuation/continuationOrchestrator.js +144 -0
- package/dist/shared/workflow/continuation/continuationOrchestrator.js.map +1 -0
- package/dist/shared/workflow/continuation/hookContinuation.d.ts +44 -0
- package/dist/shared/workflow/continuation/hookContinuation.d.ts.map +1 -0
- package/dist/shared/workflow/continuation/hookContinuation.js +85 -0
- package/dist/shared/workflow/continuation/hookContinuation.js.map +1 -0
- package/dist/shared/workflow/continuation/stopPolicy.d.ts +38 -0
- package/dist/shared/workflow/continuation/stopPolicy.d.ts.map +1 -0
- package/dist/shared/workflow/continuation/stopPolicy.js +435 -0
- package/dist/shared/workflow/continuation/stopPolicy.js.map +1 -0
- package/dist/shared/workflow/continuation/stopVerdict.d.ts +31 -0
- package/dist/shared/workflow/continuation/stopVerdict.d.ts.map +1 -0
- package/dist/shared/workflow/continuation/stopVerdict.js +98 -0
- package/dist/shared/workflow/continuation/stopVerdict.js.map +1 -0
- package/dist/shared/workflow/delegation/delegatedSession.d.ts +118 -0
- package/dist/shared/workflow/delegation/delegatedSession.d.ts.map +1 -0
- package/dist/shared/workflow/delegation/delegatedSession.js +496 -0
- package/dist/shared/workflow/delegation/delegatedSession.js.map +1 -0
- package/dist/shared/workflow/delegation/delegationPrompt.d.ts +3 -0
- package/dist/shared/workflow/delegation/delegationPrompt.d.ts.map +1 -0
- package/dist/shared/workflow/delegation/delegationPrompt.js +15 -0
- package/dist/shared/workflow/delegation/delegationPrompt.js.map +1 -0
- package/dist/shared/workflow/delegation/sessionTasks.d.ts +40 -0
- package/dist/shared/workflow/delegation/sessionTasks.d.ts.map +1 -0
- package/dist/shared/workflow/delegation/sessionTasks.js +73 -0
- package/dist/shared/workflow/delegation/sessionTasks.js.map +1 -0
- package/dist/shared/workflow/delegation/subagentRuntime.d.ts +26 -0
- package/dist/shared/workflow/delegation/subagentRuntime.d.ts.map +1 -0
- package/dist/shared/workflow/delegation/subagentRuntime.js +231 -0
- package/dist/shared/workflow/delegation/subagentRuntime.js.map +1 -0
- package/dist/shared/workflow/delivery/adoAuxiliaryRuntime.d.ts +79 -0
- package/dist/shared/workflow/delivery/adoAuxiliaryRuntime.d.ts.map +1 -0
- package/dist/shared/workflow/delivery/adoAuxiliaryRuntime.js +166 -0
- package/dist/shared/workflow/delivery/adoAuxiliaryRuntime.js.map +1 -0
- package/dist/shared/workflow/delivery/finalWave.d.ts +57 -0
- package/dist/shared/workflow/delivery/finalWave.d.ts.map +1 -0
- package/dist/shared/workflow/delivery/finalWave.js +123 -0
- package/dist/shared/workflow/delivery/finalWave.js.map +1 -0
- package/dist/shared/workflow/delivery/lifecyclePushMap.d.ts +68 -0
- package/dist/shared/workflow/delivery/lifecyclePushMap.d.ts.map +1 -0
- package/dist/shared/workflow/delivery/lifecyclePushMap.js +82 -0
- package/dist/shared/workflow/delivery/lifecyclePushMap.js.map +1 -0
- package/dist/shared/workflow/delivery/prSidecar.d.ts +7 -0
- package/dist/shared/workflow/delivery/prSidecar.d.ts.map +1 -0
- package/dist/shared/workflow/delivery/prSidecar.js +4 -0
- package/dist/shared/workflow/delivery/prSidecar.js.map +1 -0
- package/dist/shared/workflow/delivery/reviewStaleness.d.ts +37 -0
- package/dist/shared/workflow/delivery/reviewStaleness.d.ts.map +1 -0
- package/dist/shared/workflow/delivery/reviewStaleness.js +94 -0
- package/dist/shared/workflow/delivery/reviewStaleness.js.map +1 -0
- package/dist/shared/workflow/planning/hyperplan.d.ts +40 -0
- package/dist/shared/workflow/planning/hyperplan.d.ts.map +1 -0
- package/dist/shared/workflow/planning/hyperplan.js +133 -0
- package/dist/shared/workflow/planning/hyperplan.js.map +1 -0
- package/dist/shared/workflow/planning/notepad.d.ts +17 -0
- package/dist/shared/workflow/planning/notepad.d.ts.map +1 -0
- package/dist/shared/workflow/planning/notepad.js +84 -0
- package/dist/shared/workflow/planning/notepad.js.map +1 -0
- package/dist/shared/workflow/planning/planFormat.d.ts +3 -0
- package/dist/shared/workflow/planning/planFormat.d.ts.map +1 -0
- package/dist/shared/workflow/planning/planFormat.js +14 -0
- package/dist/shared/workflow/planning/planFormat.js.map +1 -0
- package/package.json +155 -0
- package/runtime/hooks/_continuation.mjs +2 -0
- package/runtime/hooks/_lib.mjs +2 -0
- package/runtime/hooks/_toolGuards.mjs +2 -0
- package/runtime/hooks/lanes/resolveSkillActivation.mjs +241 -0
- package/runtime/hooks/lanes/user-prompt-submit.mjs +326 -0
- package/runtime/hooks/layout-dir.mjs +2 -0
- package/runtime/hooks/lifecycle/pre-compact.mjs +121 -0
- package/runtime/hooks/lifecycle/session-start.mjs +356 -0
- package/runtime/hooks/lifecycle/stop.mjs +223 -0
- package/runtime/hooks/observability.mjs +2 -0
- package/runtime/hooks/post-tool-use-failure.mjs +5 -0
- package/runtime/hooks/post-tool-use.mjs +4 -0
- package/runtime/hooks/pre-compact.mjs +4 -0
- package/runtime/hooks/pre-tool-use.mjs +4 -0
- package/runtime/hooks/resolveSkillActivation.mjs +4 -0
- package/runtime/hooks/root-dir.mjs +2 -0
- package/runtime/hooks/session-start.mjs +4 -0
- package/runtime/hooks/shared/_continuation.mjs +341 -0
- package/runtime/hooks/shared/_lib.mjs +60 -0
- package/runtime/hooks/shared/_toolGuards.mjs +237 -0
- package/runtime/hooks/shared/config.mjs +143 -0
- package/runtime/hooks/shared/layout-dir.mjs +12 -0
- package/runtime/hooks/shared/observability.mjs +257 -0
- package/runtime/hooks/shared/root-dir.mjs +9 -0
- package/runtime/hooks/stop.mjs +4 -0
- package/runtime/hooks/subagent-tracker.mjs +4 -0
- package/runtime/hooks/subagents/subagent-tracker.mjs +106 -0
- package/runtime/hooks/tools/post-tool-use-failure.mjs +251 -0
- package/runtime/hooks/tools/post-tool-use.mjs +205 -0
- package/runtime/hooks/tools/pre-tool-use.mjs +268 -0
- package/runtime/hooks/user-prompt-submit.mjs +4 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { appendFileSync, mkdirSync, readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { rootDirFrom } from "../shared/root-dir.mjs";
|
|
7
|
+
import { layoutPath } from "../shared/layout-dir.mjs";
|
|
8
|
+
import { readHooksVerbose, verboseLog } from "../shared/_lib.mjs";
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
const repoRoot = join(__dirname, "..", "..", "..");
|
|
13
|
+
const { isHookActive } = await import("@amsterdamdatalabs/enact-operator/hookGating");
|
|
14
|
+
|
|
15
|
+
function readStdin() {
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(readFileSync(0, "utf8") || "{}");
|
|
18
|
+
} catch {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function appendLedger(root, entry) {
|
|
24
|
+
const dir = layoutPath(root, "logs");
|
|
25
|
+
mkdirSync(dir, { recursive: true });
|
|
26
|
+
const line = JSON.stringify({
|
|
27
|
+
schemaVersion: 1,
|
|
28
|
+
id: `ledger_${randomUUID().replace(/-/g, "").slice(0, 13)}`,
|
|
29
|
+
...entry,
|
|
30
|
+
createdAt: new Date().toISOString(),
|
|
31
|
+
});
|
|
32
|
+
appendFileSync(layoutPath(root, "logs", "ledger.json"), line + "\n");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// CLI arg: "start" or "stop"
|
|
36
|
+
const mode = process.argv[2];
|
|
37
|
+
if (mode !== "start" && mode !== "stop") {
|
|
38
|
+
process.stdout.write(JSON.stringify({ continue: true }));
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const payload = readStdin();
|
|
43
|
+
const root = rootDirFrom(payload?.cwd ?? process.cwd());
|
|
44
|
+
|
|
45
|
+
// ── Skill gate (team only) ────────────────────────────────────────────────────
|
|
46
|
+
// Subagent tracking is only meaningful when a team lane is active.
|
|
47
|
+
// In vanilla TUI mode or any non-team skill, exit immediately with noop.
|
|
48
|
+
if (!isHookActive("subagent-tracker", root, payload)) {
|
|
49
|
+
process.stdout.write(JSON.stringify({ continue: true }));
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
const timestamp = new Date().toISOString();
|
|
53
|
+
|
|
54
|
+
const agentId = payload?.agent_id ?? null;
|
|
55
|
+
const task = payload?.task ?? null;
|
|
56
|
+
const result = payload?.result ?? null;
|
|
57
|
+
|
|
58
|
+
const isStart = mode === "start";
|
|
59
|
+
const action = isStart ? "subagent_start" : "subagent_stop";
|
|
60
|
+
const detail = isStart ? "Subagent spawned" : "Subagent completed";
|
|
61
|
+
const hookEventName = isStart ? "SubagentStart" : "SubagentStop";
|
|
62
|
+
|
|
63
|
+
// Log to hooks.log
|
|
64
|
+
try {
|
|
65
|
+
mkdirSync(layoutPath(root, "logs"), { recursive: true });
|
|
66
|
+
appendFileSync(
|
|
67
|
+
layoutPath(root, "logs", "hooks.log"),
|
|
68
|
+
`${timestamp} ${hookEventName} agentId=${agentId ?? "unknown"} ${JSON.stringify(payload)}\n`,
|
|
69
|
+
"utf8",
|
|
70
|
+
);
|
|
71
|
+
} catch {
|
|
72
|
+
// best-effort
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Append to ledger
|
|
76
|
+
try {
|
|
77
|
+
appendLedger(root, {
|
|
78
|
+
kind: "session",
|
|
79
|
+
action,
|
|
80
|
+
detail,
|
|
81
|
+
actor: "enact-operator-hook",
|
|
82
|
+
metadata: {
|
|
83
|
+
agentId,
|
|
84
|
+
...(isStart ? { task } : { result: typeof result === "string" ? result.slice(0, 200) : result }),
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
} catch {
|
|
88
|
+
// best-effort
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Phase 2 — durable lifecycle + continuation / follow-up integration (compiled TS)
|
|
92
|
+
try {
|
|
93
|
+
const { recordSubagentHookStart, recordSubagentHookStop } = await import(
|
|
94
|
+
"@amsterdamdatalabs/enact-operator/subagentRuntime",
|
|
95
|
+
);
|
|
96
|
+
if (isStart) {
|
|
97
|
+
recordSubagentHookStart(root, payload);
|
|
98
|
+
} else {
|
|
99
|
+
recordSubagentHookStop(root, payload);
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
// dist may be absent in partial checkouts; ledger + hooks.log still capture evidence
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (readHooksVerbose(root)) verboseLog("subagent-tracker", `${hookEventName} — agentId: ${agentId ?? "unknown"}`);
|
|
106
|
+
process.stdout.write(JSON.stringify({ continue: true }));
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { rootDirFrom } from "../shared/root-dir.mjs";
|
|
7
|
+
import { layoutPath } from "../shared/layout-dir.mjs";
|
|
8
|
+
import { emitHookFired, registerHookFailureTrap } from "../shared/observability.mjs";
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = dirname(__filename);
|
|
11
|
+
const repoRoot = join(__dirname, "..", "..", "..");
|
|
12
|
+
const {
|
|
13
|
+
recordContinuationFailure,
|
|
14
|
+
FAILURE_ESCALATION_THRESHOLD,
|
|
15
|
+
reserveContinuationEvent,
|
|
16
|
+
buildContinuationGuidance,
|
|
17
|
+
} = await import("@amsterdamdatalabs/enact-operator/continuation");
|
|
18
|
+
const {
|
|
19
|
+
enqueueContinuationFollowUp,
|
|
20
|
+
buildPostToolFailureDedupeKey,
|
|
21
|
+
} = await import("@amsterdamdatalabs/enact-operator/continuationFollowUp");
|
|
22
|
+
const { readOperatorAuthority } = await import("@amsterdamdatalabs/enact-operator/authority");
|
|
23
|
+
const { isHookActive } = await import("@amsterdamdatalabs/enact-operator/hookGating");
|
|
24
|
+
|
|
25
|
+
function readStdin() {
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(readFileSync(0, "utf8") || "{}");
|
|
28
|
+
} catch {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function readJson(path, fallback) {
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
36
|
+
} catch {
|
|
37
|
+
return fallback;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function writeJson(path, value) {
|
|
42
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
43
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function appendLedger(root, entry) {
|
|
47
|
+
const dir = layoutPath(root, "logs");
|
|
48
|
+
mkdirSync(dir, { recursive: true });
|
|
49
|
+
const line = JSON.stringify({
|
|
50
|
+
schemaVersion: 1,
|
|
51
|
+
id: `ledger_${randomUUID().replace(/-/g, "").slice(0, 13)}`,
|
|
52
|
+
...entry,
|
|
53
|
+
createdAt: new Date().toISOString(),
|
|
54
|
+
});
|
|
55
|
+
appendFileSync(layoutPath(root, "logs", "ledger.json"), line + "\n");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Tools that are non-critical startup/memory reads — suppress output silently
|
|
59
|
+
const OPTIONAL_STARTUP_TOOLS = new Set([
|
|
60
|
+
"Read",
|
|
61
|
+
"Glob",
|
|
62
|
+
"mcp__mempalace__mempalace_status",
|
|
63
|
+
"mcp__mempalace__mempalace_list_rooms",
|
|
64
|
+
"mcp__mempalace__mempalace_wakeup",
|
|
65
|
+
"mcp__enact_ctx__ctx_read",
|
|
66
|
+
"mcp__enact_ctx__ctx_session",
|
|
67
|
+
"mcp__enact_ctx__ctx_knowledge",
|
|
68
|
+
"mcp__plugin_oh-my-claudecode_t__project_memory_read",
|
|
69
|
+
"mcp__plugin_oh-my-claudecode_t__shared_memory_read",
|
|
70
|
+
"mcp__plugin_oh-my-claudecode_t__state_read",
|
|
71
|
+
"mcp__plugin_oh-my-claudecode_t__notepad_read",
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Exported check() for use by post-tool-use.mjs entry point.
|
|
76
|
+
* Records consecutive failures and returns a deny result when the
|
|
77
|
+
* FAILURE_ESCALATION_THRESHOLD is reached. Returns null to allow.
|
|
78
|
+
*
|
|
79
|
+
* @param {object} payload - Hook payload (tool_name, error etc.)
|
|
80
|
+
* @param {string} [rootOverride] - Optional workspace root (defaults to rootDir())
|
|
81
|
+
* @returns {object|null} deny result or null (allow)
|
|
82
|
+
*/
|
|
83
|
+
export async function check(payload, rootOverride) {
|
|
84
|
+
const root = rootOverride ?? rootDir();
|
|
85
|
+
const toolName = payload?.tool_name ?? "unknown";
|
|
86
|
+
|
|
87
|
+
// Only check escalation — diagnostics/logging are handled by the standalone path
|
|
88
|
+
let continuationState = null;
|
|
89
|
+
try {
|
|
90
|
+
const authority = readOperatorAuthority(root);
|
|
91
|
+
continuationState = recordContinuationFailure(root, authority, `post-tool-use-failure:${toolName}`);
|
|
92
|
+
} catch {
|
|
93
|
+
// best-effort
|
|
94
|
+
}
|
|
95
|
+
const failureEscalated = (continuationState?.consecutiveFailureCount ?? 0) >= FAILURE_ESCALATION_THRESHOLD;
|
|
96
|
+
|
|
97
|
+
if (failureEscalated) {
|
|
98
|
+
return {
|
|
99
|
+
decision: "block",
|
|
100
|
+
reason: `Runtime exec failed ${continuationState?.consecutiveFailureCount ?? FAILURE_ESCALATION_THRESHOLD} times this turn. Per loop-prevention guard, you must now write your best-effort answer using the information you already have. Manually re-implementing the flow's logic via shell commands is forbidden.`,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ── Standalone script entry point (PostToolUseFailure event) ──────────────────
|
|
108
|
+
// Only runs when executed directly (not imported as a module).
|
|
109
|
+
export async function runHookScript(payload = readStdin()) {
|
|
110
|
+
const startedAt = Date.now();
|
|
111
|
+
const completeHook = registerHookFailureTrap({
|
|
112
|
+
payload,
|
|
113
|
+
hookEvent: "PostToolUseFailure",
|
|
114
|
+
startedAt,
|
|
115
|
+
toolName: payload?.tool_name ?? "unknown",
|
|
116
|
+
});
|
|
117
|
+
const root = rootDirFrom(payload?.cwd ?? process.cwd());
|
|
118
|
+
|
|
119
|
+
const toolName = payload?.tool_name ?? "unknown";
|
|
120
|
+
|
|
121
|
+
// ── Skill-gated: escalation check (ralph | team | ultrawork | autopilot) ────
|
|
122
|
+
// recordContinuationFailure and the deny path only run when an autonomous
|
|
123
|
+
// skill is active. Always-on: last-tool-error.json, ledger, hooks.log, obs.
|
|
124
|
+
const skillGateActive = isHookActive("post-tool-use-failure", root, payload);
|
|
125
|
+
let continuationState = null;
|
|
126
|
+
if (skillGateActive) {
|
|
127
|
+
try {
|
|
128
|
+
const authority = readOperatorAuthority(root);
|
|
129
|
+
continuationState = recordContinuationFailure(root, authority, `post-tool-use-failure:${toolName}`);
|
|
130
|
+
} catch {
|
|
131
|
+
// best-effort
|
|
132
|
+
}
|
|
133
|
+
const failureEscalated = (continuationState?.consecutiveFailureCount ?? 0) >= FAILURE_ESCALATION_THRESHOLD;
|
|
134
|
+
if (failureEscalated) {
|
|
135
|
+
try {
|
|
136
|
+
const authority = readOperatorAuthority(root);
|
|
137
|
+
const guidance = buildContinuationGuidance(authority);
|
|
138
|
+
const sid = typeof payload.session_id === "string" ? payload.session_id : null;
|
|
139
|
+
enqueueContinuationFollowUp(root, {
|
|
140
|
+
sessionId: sid,
|
|
141
|
+
dedupeKey: buildPostToolFailureDedupeKey({ sessionId: sid, toolName }),
|
|
142
|
+
trigger: "post-tool-failure",
|
|
143
|
+
lane: guidance.lane,
|
|
144
|
+
laneId: guidance.laneId,
|
|
145
|
+
prompt: `Escalated repeated failures on ${toolName}. ${guidance.resumeHint ?? ""}`.trim(),
|
|
146
|
+
});
|
|
147
|
+
} catch {
|
|
148
|
+
// best-effort
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const failureEscalated = skillGateActive && (continuationState?.consecutiveFailureCount ?? 0) >= FAILURE_ESCALATION_THRESHOLD;
|
|
153
|
+
|
|
154
|
+
const rawError = payload?.error ?? payload?.tool_output?.error ?? String(payload?.tool_output ?? "");
|
|
155
|
+
const errorStr = typeof rawError === "string" ? rawError : JSON.stringify(rawError);
|
|
156
|
+
const inputPreview = JSON.stringify(payload?.tool_input ?? {}).slice(0, 200);
|
|
157
|
+
const timestamp = new Date().toISOString();
|
|
158
|
+
|
|
159
|
+
// Read existing error state to get retry count
|
|
160
|
+
const errorStatePath = layoutPath(root, "state", "last-tool-error.json");
|
|
161
|
+
const lastError = readJson(errorStatePath, { toolName: null, retryCount: 0 });
|
|
162
|
+
const retryCount = lastError.toolName === toolName ? (lastError.retryCount ?? 0) + 1 : 0;
|
|
163
|
+
|
|
164
|
+
// Write last-tool-error.json (always-on)
|
|
165
|
+
try {
|
|
166
|
+
writeJson(errorStatePath, {
|
|
167
|
+
toolName,
|
|
168
|
+
inputPreview,
|
|
169
|
+
error: errorStr.slice(0, 500),
|
|
170
|
+
retryCount,
|
|
171
|
+
timestamp,
|
|
172
|
+
});
|
|
173
|
+
} catch {
|
|
174
|
+
// best-effort
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Append to ledger (always-on)
|
|
178
|
+
try {
|
|
179
|
+
appendLedger(root, {
|
|
180
|
+
kind: "session",
|
|
181
|
+
action: "tool_failure",
|
|
182
|
+
detail: `Tool ${toolName} failed: ${errorStr.slice(0, 200)}`,
|
|
183
|
+
actor: "enact-operator-hook",
|
|
184
|
+
metadata: { toolName, retryCount },
|
|
185
|
+
});
|
|
186
|
+
} catch {
|
|
187
|
+
// best-effort
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Log to hooks.log (always-on)
|
|
191
|
+
try {
|
|
192
|
+
appendFileSync(
|
|
193
|
+
layoutPath(root, "logs", "hooks.log"),
|
|
194
|
+
`${timestamp} PostToolUseFailure tool=${toolName} retryCount=${retryCount} ${JSON.stringify(payload)}\n`,
|
|
195
|
+
"utf8",
|
|
196
|
+
);
|
|
197
|
+
} catch {
|
|
198
|
+
// best-effort
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Suppress output for optional startup tools
|
|
202
|
+
await emitHookFired({
|
|
203
|
+
payload,
|
|
204
|
+
hookEvent: "PostToolUseFailure",
|
|
205
|
+
outcome: "noop",
|
|
206
|
+
latencyMs: Date.now() - startedAt,
|
|
207
|
+
toolName,
|
|
208
|
+
});
|
|
209
|
+
completeHook();
|
|
210
|
+
if (OPTIONAL_STARTUP_TOOLS.has(toolName)) {
|
|
211
|
+
process.stdout.write(JSON.stringify({ continue: true, suppressOutput: true }));
|
|
212
|
+
} else if (failureEscalated) {
|
|
213
|
+
// Hard HALT: N consecutive failures reached — force synthesis, no further tool calls
|
|
214
|
+
process.stdout.write(
|
|
215
|
+
JSON.stringify({
|
|
216
|
+
decision: "block",
|
|
217
|
+
reason: `Runtime exec failed ${continuationState?.consecutiveFailureCount ?? FAILURE_ESCALATION_THRESHOLD} times this turn. Per loop-prevention guard, you must now write your best-effort answer using the information you already have. Manually re-implementing the flow's logic via shell commands is forbidden.`,
|
|
218
|
+
}),
|
|
219
|
+
);
|
|
220
|
+
} else {
|
|
221
|
+
const reservation = reserveContinuationEvent(root, {
|
|
222
|
+
key: [
|
|
223
|
+
"post-tool-use-failure",
|
|
224
|
+
toolName,
|
|
225
|
+
"normal",
|
|
226
|
+
errorStr.slice(0, 120),
|
|
227
|
+
].join(":"),
|
|
228
|
+
source: "post-tool-use-failure",
|
|
229
|
+
ttlMs: 45_000,
|
|
230
|
+
metadata: { retryCount },
|
|
231
|
+
});
|
|
232
|
+
if (reservation.reserved) {
|
|
233
|
+
process.stdout.write(
|
|
234
|
+
JSON.stringify({
|
|
235
|
+
continue: true,
|
|
236
|
+
hookSpecificOutput: {
|
|
237
|
+
hookEventName: "PostToolUseFailure",
|
|
238
|
+
additionalContext: `Tool ${toolName} failed: ${errorStr.slice(0, 200)}`,
|
|
239
|
+
},
|
|
240
|
+
}),
|
|
241
|
+
);
|
|
242
|
+
} else {
|
|
243
|
+
process.stdout.write(JSON.stringify({ continue: true }));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const isMain = process.argv[1] === new URL(import.meta.url).pathname;
|
|
249
|
+
if (isMain) {
|
|
250
|
+
await runHookScript();
|
|
251
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { rootDirFrom } from "../shared/root-dir.mjs";
|
|
7
|
+
import { layoutPath } from "../shared/layout-dir.mjs";
|
|
8
|
+
import { emitHookFired, registerHookFailureTrap } from "../shared/observability.mjs";
|
|
9
|
+
import { readHooksVerbose, verboseLog } from "../shared/_lib.mjs";
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
const repoRoot = join(__dirname, "..", "..", "..");
|
|
14
|
+
const { readOperatorAuthority } = await import("@amsterdamdatalabs/enact-operator/authority");
|
|
15
|
+
const { buildContinuationGuidance, continuationReminderThresholdForGuidance, formatMandatoryLocalContinuationContext, maybeRecordContinuationReminder, touchContinuationActivity } = await import("@amsterdamdatalabs/enact-operator/continuation");
|
|
16
|
+
const { appendLedger } = await import("@amsterdamdatalabs/enact-operator/runtime");
|
|
17
|
+
const { buildToolGuardHandlers, runToolGuards } = await import("../_toolGuards.mjs");
|
|
18
|
+
|
|
19
|
+
const CONTINUATION_REMINDER_THRESHOLD = 25;
|
|
20
|
+
|
|
21
|
+
function readStdin() {
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(readFileSync(0, "utf8") || "{}");
|
|
24
|
+
} catch {
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function readJson(path, fallback) {
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
32
|
+
} catch {
|
|
33
|
+
return fallback;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function writeJson(path, value) {
|
|
38
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function explicitExitCode(input) {
|
|
42
|
+
const candidate = input?.tool_output?.exit_code ?? input?.exit_code;
|
|
43
|
+
return Number.isInteger(candidate) ? candidate : null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function extractToolOutput(input) {
|
|
47
|
+
const candidates = [
|
|
48
|
+
input?.tool_output?.output,
|
|
49
|
+
input?.tool_output?.stdout,
|
|
50
|
+
input?.tool_output?.stderr,
|
|
51
|
+
input?.output,
|
|
52
|
+
input?.err,
|
|
53
|
+
];
|
|
54
|
+
const match = candidates.find((value) => typeof value === "string" && value.length > 0);
|
|
55
|
+
return typeof match === "string" ? match : "";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function recordFailureDiagnostics(root, payload, status) {
|
|
59
|
+
const stateDir = layoutPath(root, "state");
|
|
60
|
+
const logsDir = layoutPath(root, "logs");
|
|
61
|
+
mkdirSync(stateDir, { recursive: true });
|
|
62
|
+
mkdirSync(logsDir, { recursive: true });
|
|
63
|
+
|
|
64
|
+
const command = payload?.tool_input?.command ?? "unknown command";
|
|
65
|
+
const toolUseId = payload?.tool_use_id;
|
|
66
|
+
const detail = `Bash command failed with exit code ${status}`;
|
|
67
|
+
const timestamp = new Date().toISOString();
|
|
68
|
+
|
|
69
|
+
const inboxPath = layoutPath(root, "state", "inbox.json");
|
|
70
|
+
const inbox = readJson(inboxPath, []);
|
|
71
|
+
inbox.push({
|
|
72
|
+
id: `inbox_${randomUUID().slice(0, 12)}`,
|
|
73
|
+
kind: "failed",
|
|
74
|
+
subject: detail,
|
|
75
|
+
body: command,
|
|
76
|
+
status: "open",
|
|
77
|
+
createdAt: timestamp,
|
|
78
|
+
updatedAt: timestamp,
|
|
79
|
+
});
|
|
80
|
+
writeJson(inboxPath, inbox);
|
|
81
|
+
|
|
82
|
+
appendLedger(root, {
|
|
83
|
+
kind: "session",
|
|
84
|
+
action: "bash_command_failed",
|
|
85
|
+
detail,
|
|
86
|
+
actor: "enact-operator-hook",
|
|
87
|
+
metadata: {
|
|
88
|
+
command,
|
|
89
|
+
exitCode: status,
|
|
90
|
+
toolName: payload?.tool_name ?? null,
|
|
91
|
+
toolUseId: toolUseId ?? null,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const payload = readStdin();
|
|
97
|
+
const startedAt = Date.now();
|
|
98
|
+
const completeHook = registerHookFailureTrap({
|
|
99
|
+
payload,
|
|
100
|
+
hookEvent: "PostToolUse",
|
|
101
|
+
startedAt,
|
|
102
|
+
toolName: payload?.tool_name ?? "Bash",
|
|
103
|
+
});
|
|
104
|
+
const root = rootDirFrom(payload?.cwd ?? process.cwd());
|
|
105
|
+
mkdirSync(layoutPath(root, "logs"), { recursive: true });
|
|
106
|
+
|
|
107
|
+
const status = explicitExitCode(payload);
|
|
108
|
+
|
|
109
|
+
// ── Doom-loop prevention: failure escalation guard ────────────────────────────
|
|
110
|
+
// Only runs when the tool actually failed (non-zero exit code), matching the
|
|
111
|
+
// original PostToolUseFailure event semantics.
|
|
112
|
+
if (status !== null && status !== 0) {
|
|
113
|
+
const { check: failureCheck } = await import("./post-tool-use-failure.mjs");
|
|
114
|
+
const failResult = await failureCheck(payload, root);
|
|
115
|
+
if (failResult?.decision === "deny") {
|
|
116
|
+
await emitHookFired({
|
|
117
|
+
payload,
|
|
118
|
+
hookEvent: "PostToolUse",
|
|
119
|
+
outcome: "denied",
|
|
120
|
+
latencyMs: Date.now() - startedAt,
|
|
121
|
+
toolName: payload?.tool_name ?? "Bash",
|
|
122
|
+
});
|
|
123
|
+
completeHook({ blocked: true });
|
|
124
|
+
process.stdout.write(JSON.stringify(failResult));
|
|
125
|
+
process.exit(0);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
touchContinuationActivity(root, `post-tool-use:${payload?.tool_name ?? "unknown"}`);
|
|
129
|
+
appendFileSync(
|
|
130
|
+
layoutPath(root, "logs", "hooks.log"),
|
|
131
|
+
`${new Date().toISOString()} PostToolUse status=${status ?? "unknown"} ${JSON.stringify(payload)}\n`,
|
|
132
|
+
"utf8",
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const output = { continue: true };
|
|
136
|
+
let continuationReminder = null;
|
|
137
|
+
try {
|
|
138
|
+
const authority = readOperatorAuthority(root);
|
|
139
|
+
const guidance = buildContinuationGuidance(authority);
|
|
140
|
+
const reminderThreshold = continuationReminderThresholdForGuidance(guidance, CONTINUATION_REMINDER_THRESHOLD);
|
|
141
|
+
const mandatoryContinuation = formatMandatoryLocalContinuationContext(guidance, "using more Bash turns");
|
|
142
|
+
const reminder = maybeRecordContinuationReminder(root, authority, "post-tool-use", reminderThreshold);
|
|
143
|
+
if (reminder.remind && reminder.state.resumeHint) {
|
|
144
|
+
continuationReminder =
|
|
145
|
+
[
|
|
146
|
+
`Operator reminder: no lifecycle, task, or proof progress was recorded in the last ${reminderThreshold} Bash turns.`,
|
|
147
|
+
mandatoryContinuation ?? reminder.state.resumeHint,
|
|
148
|
+
].filter(Boolean).join(" ");
|
|
149
|
+
}
|
|
150
|
+
} catch {
|
|
151
|
+
continuationReminder = null;
|
|
152
|
+
}
|
|
153
|
+
if (status !== null && status !== 0) {
|
|
154
|
+
recordFailureDiagnostics(root, payload, status);
|
|
155
|
+
output.hookSpecificOutput = {
|
|
156
|
+
hookEventName: "PostToolUse",
|
|
157
|
+
additionalContext: [
|
|
158
|
+
"Operator noticed a failing Bash command. Check .enact/operator inbox/ledger diagnostics.",
|
|
159
|
+
continuationReminder,
|
|
160
|
+
].filter(Boolean).join(" "),
|
|
161
|
+
};
|
|
162
|
+
} else if (continuationReminder) {
|
|
163
|
+
output.hookSpecificOutput = {
|
|
164
|
+
hookEventName: "PostToolUse",
|
|
165
|
+
additionalContext: continuationReminder,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
await emitHookFired({
|
|
169
|
+
payload,
|
|
170
|
+
hookEvent: "PostToolUse",
|
|
171
|
+
outcome: "noop",
|
|
172
|
+
latencyMs: Date.now() - startedAt,
|
|
173
|
+
toolName: payload?.tool_name ?? "Bash",
|
|
174
|
+
extraPayload: {
|
|
175
|
+
status: status ?? "unknown",
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
// ── Tool-guard suite (PostToolUse context protection) ─────────────────────────
|
|
179
|
+
try {
|
|
180
|
+
const guardResult = await runToolGuards({
|
|
181
|
+
toolName: payload?.tool_name ?? "Bash",
|
|
182
|
+
output: extractToolOutput(payload),
|
|
183
|
+
exitCode: status ?? 0,
|
|
184
|
+
workingDirectory: payload?.cwd ?? process.cwd(),
|
|
185
|
+
});
|
|
186
|
+
const guardContext = guardResult?.hookSpecificOutput?.additionalContext;
|
|
187
|
+
if (guardContext) {
|
|
188
|
+
const existing = output.hookSpecificOutput?.additionalContext ?? "";
|
|
189
|
+
output.hookSpecificOutput = {
|
|
190
|
+
...(output.hookSpecificOutput ?? {}),
|
|
191
|
+
hookEventName: "PostToolUse",
|
|
192
|
+
additionalContext: [existing, guardContext].filter(Boolean).join(" "),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
} catch {
|
|
196
|
+
// guard suite failures are non-fatal; continue with existing output
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (readHooksVerbose(root)) {
|
|
200
|
+
const toolLabel = payload?.tool_name ?? "Bash";
|
|
201
|
+
const statusLabel = status === null ? "no exit code" : status === 0 ? "exit 0" : `exit ${status}`;
|
|
202
|
+
verboseLog("post-tool-use", `${toolLabel} completed — ${statusLabel}`);
|
|
203
|
+
}
|
|
204
|
+
completeHook();
|
|
205
|
+
process.stdout.write(JSON.stringify(output));
|