@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,919 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { ensureLayout, layoutPath, readJsonFile, validateJsonFile, writeJson, appendLedger, upsertInboxItem, goalScopeRank, assertLifecycleTransitionAllowed, buildLifecycleCore, lifecycleNow, recordLifecyclePhaseTransition, resolveLifecycleRetargetScopeAndProfile, resolveLifecycleStartScopeAndProfile, } from '../../operator/operatorRuntimeCore.js';
|
|
5
|
+
import { clearContinuationState } from '../../shared/workflow/continuation/continuation.js';
|
|
6
|
+
import { resolveOperatorRoot } from '../../shared/core/contract.js';
|
|
7
|
+
import { readNotepad, renderNotepadContextSection } from '../../shared/workflow/planning/notepad.js';
|
|
8
|
+
import { coalesceTaskId } from '../../shared/core/terminology.js';
|
|
9
|
+
const ULTRAWORK_SCHEMA_VERSION = 1;
|
|
10
|
+
const UltraworkVerificationSchema = z.object({
|
|
11
|
+
phase: z.enum(['clarify', 'plan', 'execute', 'verify', 'review', 'blocked', 'completed', 'aborted']),
|
|
12
|
+
evidence: z.string(),
|
|
13
|
+
recordedAt: z.string(),
|
|
14
|
+
closureScope: z.enum(['lane-local', 'integration', 'repo-end-to-end']).optional(),
|
|
15
|
+
});
|
|
16
|
+
const UltraworkHistoryEntrySchema = z.object({
|
|
17
|
+
phase: z.enum(['clarify', 'plan', 'execute', 'verify', 'review', 'blocked', 'completed', 'aborted']),
|
|
18
|
+
route: z.enum(['direct', 'deep-interview', 'plan-only', 'team-escalated']).optional(),
|
|
19
|
+
at: z.string(),
|
|
20
|
+
by: z.string().optional(),
|
|
21
|
+
note: z.string().optional(),
|
|
22
|
+
retargetEvent: z.object({
|
|
23
|
+
oldIntent: z.string(),
|
|
24
|
+
newIntent: z.string(),
|
|
25
|
+
retargetMode: z.enum(['replace-intent', 'narrow-scope', 'resume-unchanged', 'fork-child-task']),
|
|
26
|
+
preservedState: z.enum(['preserved', 'narrowed', 'replaced', 'forked']),
|
|
27
|
+
reason: z.string().optional(),
|
|
28
|
+
}).optional(),
|
|
29
|
+
});
|
|
30
|
+
const UltraworkActivationMetadataSchema = z.object({
|
|
31
|
+
rawMarker: z.string().optional(),
|
|
32
|
+
source: z.enum(['hook', 'cli', 'mcp']).optional(),
|
|
33
|
+
detectedAt: z.string().optional(),
|
|
34
|
+
originalGoal: z.string().optional(),
|
|
35
|
+
}).catchall(z.unknown());
|
|
36
|
+
const UltraworkStateSchema = z.object({
|
|
37
|
+
schemaVersion: z.literal(ULTRAWORK_SCHEMA_VERSION),
|
|
38
|
+
id: z.string(),
|
|
39
|
+
active: z.boolean(),
|
|
40
|
+
phase: z.enum(['clarify', 'plan', 'execute', 'verify', 'review', 'blocked', 'completed', 'aborted']),
|
|
41
|
+
route: z.enum(['direct', 'deep-interview', 'plan-only', 'team-escalated']),
|
|
42
|
+
intent: z.string(),
|
|
43
|
+
sessionId: z.string().optional(),
|
|
44
|
+
planId: z.string().optional(),
|
|
45
|
+
researchIds: z.array(z.string()),
|
|
46
|
+
taskIds: z.array(z.string()),
|
|
47
|
+
childTasks: z.array(z.object({
|
|
48
|
+
id: z.string(),
|
|
49
|
+
intent: z.string(),
|
|
50
|
+
createdAt: z.string(),
|
|
51
|
+
status: z.enum(['pending', 'completed', 'cancelled']),
|
|
52
|
+
})),
|
|
53
|
+
escalation: z.object({
|
|
54
|
+
active: z.boolean(),
|
|
55
|
+
teamId: z.string().optional(),
|
|
56
|
+
reason: z.string().optional(),
|
|
57
|
+
escalatedAt: z.string().optional(),
|
|
58
|
+
}),
|
|
59
|
+
verifications: z.array(UltraworkVerificationSchema),
|
|
60
|
+
blockerReason: z.string().optional(),
|
|
61
|
+
abortReason: z.string().optional(),
|
|
62
|
+
completionSummary: z.string().optional(),
|
|
63
|
+
history: z.array(UltraworkHistoryEntrySchema),
|
|
64
|
+
activationMetadata: UltraworkActivationMetadataSchema.optional(),
|
|
65
|
+
createdAt: z.string(),
|
|
66
|
+
updatedAt: z.string(),
|
|
67
|
+
closureState: z.enum(['none', 'integration_pending', 'integration_approved', 'goal_closed']),
|
|
68
|
+
goalScope: z.enum(['repo-local', 'operator-runtime', 'operator-replacement']).optional(),
|
|
69
|
+
gateProfile: z.enum(['repo-delivery', 'operator-runtime', 'operator-replacement', 'benchmark-harness']).optional(),
|
|
70
|
+
taskId: z.string().optional(),
|
|
71
|
+
assignmentId: z.string().optional(),
|
|
72
|
+
subagents: z.array(z.object({
|
|
73
|
+
id: z.string(),
|
|
74
|
+
status: z.enum(['active', 'completed', 'failed', 'cancelled']),
|
|
75
|
+
startedAt: z.string(),
|
|
76
|
+
completedAt: z.string().optional(),
|
|
77
|
+
summary: z.string().optional(),
|
|
78
|
+
})),
|
|
79
|
+
scenarios: z.array(z.object({
|
|
80
|
+
class: z.enum(['happy', 'edge', 'regression']),
|
|
81
|
+
description: z.string(),
|
|
82
|
+
passCondition: z.string(),
|
|
83
|
+
surfaceArtifact: z.string(),
|
|
84
|
+
})).optional(),
|
|
85
|
+
scenarioContract: z.enum(['PASS', 'FAIL']).optional(),
|
|
86
|
+
reviewerGateTriggered: z.boolean().optional(),
|
|
87
|
+
reviewerVerdict: z.enum(['UNCONDITIONAL_APPROVE', 'looks good but requires minor cleanup']).optional(),
|
|
88
|
+
});
|
|
89
|
+
// ── Transition table ──────────────────────────────────────────────────────────
|
|
90
|
+
export const ULTRAWORK_TRANSITIONS = {
|
|
91
|
+
clarify: ['plan', 'execute', 'aborted'],
|
|
92
|
+
plan: ['execute', 'review', 'aborted', 'blocked'],
|
|
93
|
+
execute: ['verify', 'blocked', 'review', 'aborted'],
|
|
94
|
+
verify: ['completed', 'review', 'blocked', 'aborted'],
|
|
95
|
+
review: ['completed', 'execute', 'blocked', 'aborted'],
|
|
96
|
+
blocked: ['clarify', 'plan', 'execute', 'aborted'],
|
|
97
|
+
completed: [],
|
|
98
|
+
aborted: [],
|
|
99
|
+
};
|
|
100
|
+
const VERIFICATION_PHASES = new Set(['plan', 'execute', 'verify', 'review']);
|
|
101
|
+
const ESCALATION_PHASES = new Set(['plan', 'execute']);
|
|
102
|
+
const COMPLETION_PHASES = new Set(['verify', 'review']);
|
|
103
|
+
const TERMINAL_PHASES = new Set(['completed', 'aborted']);
|
|
104
|
+
// ── Internal helpers ──────────────────────────────────────────────────────────
|
|
105
|
+
function now() {
|
|
106
|
+
return lifecycleNow();
|
|
107
|
+
}
|
|
108
|
+
function hasPendingChildTasks(state) {
|
|
109
|
+
return state.childTasks.some((task) => task.status === 'pending');
|
|
110
|
+
}
|
|
111
|
+
/** Phase-inference context from ultrawork state only (no tasks.json reads). */
|
|
112
|
+
export function buildUltraworkPhaseContext(state, reviews, root = resolveOperatorRoot()) {
|
|
113
|
+
const children = state.childTasks;
|
|
114
|
+
const childIds = children.map((child) => child.id);
|
|
115
|
+
const activeTasks = children.filter((child) => child.status === 'pending').length;
|
|
116
|
+
const completedTasks = children.filter((child) => child.status === 'completed').length;
|
|
117
|
+
const assignmentRef = coalesceTaskId(state);
|
|
118
|
+
const linkedReviews = Array.from(new Map(reviews
|
|
119
|
+
.filter((review) => {
|
|
120
|
+
const reviewTaskId = coalesceTaskId(review);
|
|
121
|
+
return ((assignmentRef !== undefined && reviewTaskId === assignmentRef)
|
|
122
|
+
|| childIds.includes(reviewTaskId ?? ''));
|
|
123
|
+
})
|
|
124
|
+
.map((review) => [review.id, review])).values());
|
|
125
|
+
const basisIds = childIds.length > 0
|
|
126
|
+
? childIds
|
|
127
|
+
: assignmentRef
|
|
128
|
+
? [assignmentRef]
|
|
129
|
+
: [];
|
|
130
|
+
const notepad = readNotepad('ultrawork', state.id, root);
|
|
131
|
+
return {
|
|
132
|
+
taskIds: basisIds,
|
|
133
|
+
activeTasks,
|
|
134
|
+
completedTasks,
|
|
135
|
+
verifications: state.verifications,
|
|
136
|
+
reviews: linkedReviews,
|
|
137
|
+
currentPhase: state.phase,
|
|
138
|
+
contextSection: renderNotepadContextSection(notepad),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function resolveCompletionClosureState(_root, state) {
|
|
142
|
+
if (state.closureState === 'integration_approved' || state.closureState === 'goal_closed') {
|
|
143
|
+
return { closureState: state.closureState, inferred: false };
|
|
144
|
+
}
|
|
145
|
+
if (state.closureState === 'integration_pending') {
|
|
146
|
+
throw new Error(`Cannot complete ultrawork "${state.id}": closureState is "integration_pending". Record final approval with operator_ultrawork_set_closure_state before completing.`);
|
|
147
|
+
}
|
|
148
|
+
if (state.route === 'team-escalated' || state.escalation.active) {
|
|
149
|
+
throw new Error(`Cannot complete ultrawork "${state.id}": route "${state.route}" still requires explicit closure approval. Set closureState to "integration_approved" or "goal_closed" before completing.`);
|
|
150
|
+
}
|
|
151
|
+
if (hasPendingChildTasks(state)) {
|
|
152
|
+
throw new Error(`Cannot complete ultrawork "${state.id}": closureState is "none" and there are pending child tasks. Complete or cancel them, or set closureState explicitly before completing.`);
|
|
153
|
+
}
|
|
154
|
+
return { closureState: 'goal_closed', inferred: true };
|
|
155
|
+
}
|
|
156
|
+
function cancelPendingChildTasksOnReplaceIntent(state) {
|
|
157
|
+
const cancelledIds = [];
|
|
158
|
+
const childTasks = state.childTasks.map((child) => {
|
|
159
|
+
if (child.status !== 'pending')
|
|
160
|
+
return child;
|
|
161
|
+
cancelledIds.push(child.id);
|
|
162
|
+
return { ...child, status: 'cancelled' };
|
|
163
|
+
});
|
|
164
|
+
return { childTasks, cancelledIds };
|
|
165
|
+
}
|
|
166
|
+
function ultraworkFile(root) {
|
|
167
|
+
return layoutPath(root, 'state', 'ultrawork.json');
|
|
168
|
+
}
|
|
169
|
+
function migrateUltraworkState(raw) {
|
|
170
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
171
|
+
throw new Error('Ultrawork state must be an object.');
|
|
172
|
+
}
|
|
173
|
+
const state = raw;
|
|
174
|
+
const schemaVersion = typeof state.schemaVersion === 'number' ? state.schemaVersion : 0;
|
|
175
|
+
if (schemaVersion !== 0 && schemaVersion !== ULTRAWORK_SCHEMA_VERSION) {
|
|
176
|
+
throw new Error(`Unsupported Ultrawork schemaVersion ${schemaVersion}.`);
|
|
177
|
+
}
|
|
178
|
+
// Forward-fill new optional fields on old records
|
|
179
|
+
const goalScope = state.goalScope === 'repo-local'
|
|
180
|
+
|| state.goalScope === 'operator-runtime'
|
|
181
|
+
|| state.goalScope === 'operator-replacement'
|
|
182
|
+
? state.goalScope
|
|
183
|
+
: undefined;
|
|
184
|
+
const gateProfile = state.gateProfile === 'repo-delivery'
|
|
185
|
+
|| state.gateProfile === 'operator-runtime'
|
|
186
|
+
|| state.gateProfile === 'operator-replacement'
|
|
187
|
+
|| state.gateProfile === 'benchmark-harness'
|
|
188
|
+
? state.gateProfile
|
|
189
|
+
: undefined;
|
|
190
|
+
const childTasks = Array.isArray(state.childTasks) ? state.childTasks : [];
|
|
191
|
+
const taskId = typeof state.taskId === 'string' ? state.taskId : undefined;
|
|
192
|
+
const assignmentId = typeof state.assignmentId === 'string' ? state.assignmentId : undefined;
|
|
193
|
+
const subagents = Array.isArray(state.subagents) ? state.subagents : [];
|
|
194
|
+
const scenarios = Array.isArray(state.scenarios) ? state.scenarios : [];
|
|
195
|
+
const scenarioContract = state.scenarioContract === 'PASS' || state.scenarioContract === 'FAIL'
|
|
196
|
+
? state.scenarioContract
|
|
197
|
+
: undefined;
|
|
198
|
+
const reviewerGateTriggered = typeof state.reviewerGateTriggered === 'boolean'
|
|
199
|
+
? state.reviewerGateTriggered
|
|
200
|
+
: undefined;
|
|
201
|
+
const reviewerVerdict = state.reviewerVerdict === 'UNCONDITIONAL_APPROVE' || state.reviewerVerdict === 'looks good but requires minor cleanup'
|
|
202
|
+
? state.reviewerVerdict
|
|
203
|
+
: undefined;
|
|
204
|
+
return {
|
|
205
|
+
...state,
|
|
206
|
+
schemaVersion: ULTRAWORK_SCHEMA_VERSION,
|
|
207
|
+
closureState: state.closureState === 'integration_pending'
|
|
208
|
+
|| state.closureState === 'integration_approved'
|
|
209
|
+
|| state.closureState === 'goal_closed'
|
|
210
|
+
|| state.closureState === 'none'
|
|
211
|
+
? state.closureState
|
|
212
|
+
: 'none',
|
|
213
|
+
goalScope,
|
|
214
|
+
gateProfile,
|
|
215
|
+
childTasks,
|
|
216
|
+
taskIds: Array.isArray(state.taskIds)
|
|
217
|
+
? state.taskIds
|
|
218
|
+
: [],
|
|
219
|
+
taskId,
|
|
220
|
+
assignmentId,
|
|
221
|
+
subagents,
|
|
222
|
+
scenarios,
|
|
223
|
+
scenarioContract,
|
|
224
|
+
reviewerGateTriggered,
|
|
225
|
+
reviewerVerdict,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function load(root) {
|
|
229
|
+
const path = ultraworkFile(root);
|
|
230
|
+
let raw;
|
|
231
|
+
try {
|
|
232
|
+
raw = readJsonFile(path);
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
console.warn(`[ultrawork] stale or corrupt state file at ${path}: ${err instanceof Error ? err.message : String(err)}, treating as no active ultrawork`);
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
if (raw === undefined) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
return validateJsonFile(path, migrateUltraworkState(raw), UltraworkStateSchema);
|
|
243
|
+
}
|
|
244
|
+
catch (err) {
|
|
245
|
+
console.warn(`[ultrawork] stale or corrupt state file at ${path}: ${err instanceof Error ? err.message : String(err)}, treating as no active ultrawork`);
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function save(root, state) {
|
|
250
|
+
const next = { ...state, schemaVersion: ULTRAWORK_SCHEMA_VERSION, updatedAt: now() };
|
|
251
|
+
writeJson(ultraworkFile(root), next);
|
|
252
|
+
return next;
|
|
253
|
+
}
|
|
254
|
+
// ── Public API ────────────────────────────────────────────────────────────────
|
|
255
|
+
export function startUltrawork(root, opts) {
|
|
256
|
+
ensureLayout(root);
|
|
257
|
+
const existing = load(root);
|
|
258
|
+
if (existing?.active) {
|
|
259
|
+
throw new Error(`Cannot start a new ultrawork session: session "${existing.id}" is already active (phase="${existing.phase}"). Abort or complete it first.`);
|
|
260
|
+
}
|
|
261
|
+
// ── Profile selection guard (D4) ─────────────────────────────────────────
|
|
262
|
+
const { resolvedProfile, resolvedScope } = resolveLifecycleStartScopeAndProfile(root, {
|
|
263
|
+
text: opts.intent,
|
|
264
|
+
explicitGoalScope: opts.goalScope,
|
|
265
|
+
explicitGateProfile: opts.gateProfile,
|
|
266
|
+
contextLabel: 'operator_ultrawork_start',
|
|
267
|
+
hint: "Supply gateProfile='operator-replacement' for Operator self-hosting work, or 'operator-runtime' for runtime health checks.",
|
|
268
|
+
});
|
|
269
|
+
const id = `ultrawork_${randomUUID().slice(0, 12)}`;
|
|
270
|
+
const route = opts.route ?? 'direct';
|
|
271
|
+
const timestamp = lifecycleNow();
|
|
272
|
+
const state = {
|
|
273
|
+
schemaVersion: ULTRAWORK_SCHEMA_VERSION,
|
|
274
|
+
...buildLifecycleCore({
|
|
275
|
+
id,
|
|
276
|
+
phase: 'clarify',
|
|
277
|
+
history: [{ phase: 'clarify', route, at: timestamp }],
|
|
278
|
+
goalScope: resolvedScope,
|
|
279
|
+
gateProfile: resolvedProfile,
|
|
280
|
+
}),
|
|
281
|
+
route,
|
|
282
|
+
intent: opts.intent,
|
|
283
|
+
sessionId: opts.sessionId,
|
|
284
|
+
researchIds: [],
|
|
285
|
+
taskIds: [],
|
|
286
|
+
childTasks: [],
|
|
287
|
+
escalation: { active: false },
|
|
288
|
+
verifications: [],
|
|
289
|
+
scenarios: [],
|
|
290
|
+
closureState: 'none',
|
|
291
|
+
activationMetadata: opts.activationMetadata,
|
|
292
|
+
taskId: opts.taskId,
|
|
293
|
+
assignmentId: opts.assignmentId,
|
|
294
|
+
subagents: [],
|
|
295
|
+
};
|
|
296
|
+
const bootstrappedState = save(root, state);
|
|
297
|
+
appendLedger(root, {
|
|
298
|
+
kind: 'session',
|
|
299
|
+
action: 'ultrawork_start',
|
|
300
|
+
detail: `Started ultrawork "${id}" with intent: ${opts.intent}`,
|
|
301
|
+
metadata: { id, route, phase: 'clarify' },
|
|
302
|
+
});
|
|
303
|
+
upsertInboxItem(root, {
|
|
304
|
+
kind: 'system',
|
|
305
|
+
subject: 'Ultrawork session started',
|
|
306
|
+
body: `Session "${id}" initiated in phase=clarify route=${route}`,
|
|
307
|
+
status: 'open',
|
|
308
|
+
});
|
|
309
|
+
clearContinuationState(root, 'ultrawork started', 'operator_ultrawork_start');
|
|
310
|
+
return bootstrappedState;
|
|
311
|
+
}
|
|
312
|
+
export function getUltrawork(root) {
|
|
313
|
+
return load(root);
|
|
314
|
+
}
|
|
315
|
+
export function routeUltrawork(root, route, note) {
|
|
316
|
+
ensureLayout(root);
|
|
317
|
+
const state = load(root);
|
|
318
|
+
if (!state) {
|
|
319
|
+
throw new Error('Cannot route ultrawork: no ultrawork state file found.');
|
|
320
|
+
}
|
|
321
|
+
if (TERMINAL_PHASES.has(state.phase)) {
|
|
322
|
+
throw new Error(`Cannot re-route ultrawork "${state.id}": session is in terminal phase="${state.phase}".`);
|
|
323
|
+
}
|
|
324
|
+
const entry = { phase: state.phase, route, at: now(), note };
|
|
325
|
+
const next = save(root, {
|
|
326
|
+
...state,
|
|
327
|
+
route,
|
|
328
|
+
history: [...state.history, entry],
|
|
329
|
+
});
|
|
330
|
+
appendLedger(root, {
|
|
331
|
+
kind: 'session',
|
|
332
|
+
action: 'ultrawork_route',
|
|
333
|
+
detail: `Re-routed ultrawork "${state.id}" to route=${route}`,
|
|
334
|
+
metadata: { id: state.id, route, phase: state.phase },
|
|
335
|
+
});
|
|
336
|
+
clearContinuationState(root, `ultrawork routed to ${route}`, 'operator_ultrawork_route');
|
|
337
|
+
return next;
|
|
338
|
+
}
|
|
339
|
+
export function advanceUltrawork(root, opts) {
|
|
340
|
+
ensureLayout(root);
|
|
341
|
+
const state = load(root);
|
|
342
|
+
if (!state) {
|
|
343
|
+
throw new Error('Cannot advance ultrawork: no ultrawork state file found.');
|
|
344
|
+
}
|
|
345
|
+
if (opts.toPhase === 'aborted') {
|
|
346
|
+
throw new Error(`Cannot advance to "aborted" via advanceUltrawork. Use abortUltrawork() instead.`);
|
|
347
|
+
}
|
|
348
|
+
const allowed = ULTRAWORK_TRANSITIONS[state.phase];
|
|
349
|
+
if (allowed.length === 0) {
|
|
350
|
+
throw new Error(`Cannot advance ultrawork "${state.id}": phase="${state.phase}" is terminal with no allowed transitions.`);
|
|
351
|
+
}
|
|
352
|
+
assertLifecycleTransitionAllowed('ultrawork', state.id, state.phase, opts.toPhase, ULTRAWORK_TRANSITIONS);
|
|
353
|
+
const entry = {
|
|
354
|
+
phase: opts.toPhase,
|
|
355
|
+
route: state.route,
|
|
356
|
+
at: lifecycleNow(),
|
|
357
|
+
by: opts.by,
|
|
358
|
+
note: opts.note,
|
|
359
|
+
};
|
|
360
|
+
const next = save(root, {
|
|
361
|
+
...state,
|
|
362
|
+
phase: opts.toPhase,
|
|
363
|
+
history: [...state.history, entry],
|
|
364
|
+
});
|
|
365
|
+
recordLifecyclePhaseTransition(root, {
|
|
366
|
+
modeType: 'ultrawork',
|
|
367
|
+
laneId: state.id,
|
|
368
|
+
fromPhase: state.phase,
|
|
369
|
+
toPhase: opts.toPhase,
|
|
370
|
+
trigger: opts.trigger,
|
|
371
|
+
sessionAction: 'ultrawork_advance',
|
|
372
|
+
sessionDetail: `Advanced ultrawork "${state.id}" from "${state.phase}" to "${opts.toPhase}"`,
|
|
373
|
+
sessionMetadata: { id: state.id, from: state.phase, to: opts.toPhase, by: opts.by },
|
|
374
|
+
actor: opts.by,
|
|
375
|
+
sessionId: state.sessionId,
|
|
376
|
+
runId: state.taskId ?? state.id,
|
|
377
|
+
});
|
|
378
|
+
clearContinuationState(root, `ultrawork advanced to ${opts.toPhase}`, 'operator_ultrawork_advance');
|
|
379
|
+
return next;
|
|
380
|
+
}
|
|
381
|
+
export function escalateToTeam(root, teamId, reason) {
|
|
382
|
+
ensureLayout(root);
|
|
383
|
+
const state = load(root);
|
|
384
|
+
if (!state) {
|
|
385
|
+
throw new Error('Cannot escalate: no ultrawork state file found.');
|
|
386
|
+
}
|
|
387
|
+
if (!ESCALATION_PHASES.has(state.phase)) {
|
|
388
|
+
throw new Error(`Cannot escalate ultrawork "${state.id}" to team: escalation is only allowed from phases [${[...ESCALATION_PHASES].join(', ')}], but current phase is "${state.phase}".`);
|
|
389
|
+
}
|
|
390
|
+
const timestamp = now();
|
|
391
|
+
const entry = {
|
|
392
|
+
phase: state.phase,
|
|
393
|
+
route: 'team-escalated',
|
|
394
|
+
at: timestamp,
|
|
395
|
+
note: `Escalated to team "${teamId}": ${reason}`,
|
|
396
|
+
};
|
|
397
|
+
const next = save(root, {
|
|
398
|
+
...state,
|
|
399
|
+
route: 'team-escalated',
|
|
400
|
+
escalation: { active: true, teamId, reason, escalatedAt: timestamp },
|
|
401
|
+
history: [...state.history, entry],
|
|
402
|
+
});
|
|
403
|
+
appendLedger(root, {
|
|
404
|
+
kind: 'session',
|
|
405
|
+
action: 'ultrawork_escalate',
|
|
406
|
+
detail: `Escalated ultrawork "${state.id}" to team "${teamId}": ${reason}`,
|
|
407
|
+
metadata: { id: state.id, teamId, reason, phase: state.phase },
|
|
408
|
+
});
|
|
409
|
+
return next;
|
|
410
|
+
}
|
|
411
|
+
export function recordUltraworkVerification(root, evidence, options = {}) {
|
|
412
|
+
ensureLayout(root);
|
|
413
|
+
const state = load(root);
|
|
414
|
+
if (!state) {
|
|
415
|
+
throw new Error('Cannot record verification: no ultrawork state file found.');
|
|
416
|
+
}
|
|
417
|
+
if (!VERIFICATION_PHASES.has(state.phase)) {
|
|
418
|
+
throw new Error(`Cannot record verification for ultrawork "${state.id}": verification is only allowed in phases [${[...VERIFICATION_PHASES].join(', ')}], but current phase is "${state.phase}".`);
|
|
419
|
+
}
|
|
420
|
+
const verification = {
|
|
421
|
+
phase: state.phase,
|
|
422
|
+
evidence,
|
|
423
|
+
recordedAt: now(),
|
|
424
|
+
};
|
|
425
|
+
let scenarios = state.scenarios;
|
|
426
|
+
let scenarioContract = state.scenarioContract;
|
|
427
|
+
if (options.scenarios !== undefined) {
|
|
428
|
+
const invalid = options.scenarios.find((scenario) => scenario.description.trim() === ''
|
|
429
|
+
|| scenario.passCondition.trim() === ''
|
|
430
|
+
|| scenario.surfaceArtifact.trim() === '');
|
|
431
|
+
if (invalid) {
|
|
432
|
+
throw new Error(`Invalid ultrawork scenario contract: scenario "${invalid.class}" must include non-empty description, passCondition, and surfaceArtifact.`);
|
|
433
|
+
}
|
|
434
|
+
const requiredClasses = ['happy', 'edge', 'regression'];
|
|
435
|
+
const present = new Set(options.scenarios.map((scenario) => scenario.class));
|
|
436
|
+
const missing = requiredClasses.filter((klass) => !present.has(klass));
|
|
437
|
+
if (missing.length > 0) {
|
|
438
|
+
throw new Error(`Invalid ultrawork scenario contract: missing scenario class: ${missing[0]}.`);
|
|
439
|
+
}
|
|
440
|
+
scenarios = options.scenarios;
|
|
441
|
+
scenarioContract = 'PASS';
|
|
442
|
+
}
|
|
443
|
+
const reviewerGateTriggered = shouldTriggerReviewerGate(root, state);
|
|
444
|
+
const reviewerVerdict = options.reviewerVerdict ?? state.reviewerVerdict;
|
|
445
|
+
const next = save(root, {
|
|
446
|
+
...state,
|
|
447
|
+
verifications: [...state.verifications, verification],
|
|
448
|
+
scenarios,
|
|
449
|
+
scenarioContract,
|
|
450
|
+
reviewerGateTriggered,
|
|
451
|
+
reviewerVerdict,
|
|
452
|
+
});
|
|
453
|
+
appendLedger(root, {
|
|
454
|
+
kind: 'session',
|
|
455
|
+
action: 'ultrawork_verification',
|
|
456
|
+
detail: `Recorded verification evidence for ultrawork "${state.id}" in phase="${state.phase}"`,
|
|
457
|
+
metadata: { id: state.id, phase: state.phase },
|
|
458
|
+
});
|
|
459
|
+
clearContinuationState(root, 'ultrawork verification recorded', 'operator_ultrawork_verify');
|
|
460
|
+
return next;
|
|
461
|
+
}
|
|
462
|
+
export function linkPlan(root, planId) {
|
|
463
|
+
ensureLayout(root);
|
|
464
|
+
const state = load(root);
|
|
465
|
+
if (!state) {
|
|
466
|
+
throw new Error('Cannot link plan: no ultrawork state file found.');
|
|
467
|
+
}
|
|
468
|
+
const next = save(root, { ...state, planId });
|
|
469
|
+
appendLedger(root, {
|
|
470
|
+
kind: 'session',
|
|
471
|
+
action: 'ultrawork_link_plan',
|
|
472
|
+
detail: `Linked plan "${planId}" to ultrawork "${state.id}"`,
|
|
473
|
+
metadata: { id: state.id, planId },
|
|
474
|
+
});
|
|
475
|
+
return next;
|
|
476
|
+
}
|
|
477
|
+
export function linkTaskRef(root, taskId) {
|
|
478
|
+
ensureLayout(root);
|
|
479
|
+
const state = load(root);
|
|
480
|
+
if (!state) {
|
|
481
|
+
throw new Error('Cannot link task ref: no ultrawork state file found.');
|
|
482
|
+
}
|
|
483
|
+
if (state.taskIds.includes(taskId)) {
|
|
484
|
+
return state;
|
|
485
|
+
}
|
|
486
|
+
const next = save(root, { ...state, taskIds: [...state.taskIds, taskId] });
|
|
487
|
+
appendLedger(root, {
|
|
488
|
+
kind: 'session',
|
|
489
|
+
action: 'ultrawork_link_task',
|
|
490
|
+
detail: `Linked task ref "${taskId}" to ultrawork "${state.id}"`,
|
|
491
|
+
metadata: { id: state.id, taskId },
|
|
492
|
+
});
|
|
493
|
+
return next;
|
|
494
|
+
}
|
|
495
|
+
export function retargetUltrawork(root, opts) {
|
|
496
|
+
ensureLayout(root);
|
|
497
|
+
const state = load(root);
|
|
498
|
+
if (!state) {
|
|
499
|
+
throw new Error('Cannot retarget ultrawork: no ultrawork state file found.');
|
|
500
|
+
}
|
|
501
|
+
if (TERMINAL_PHASES.has(state.phase)) {
|
|
502
|
+
throw new Error(`Cannot retarget ultrawork "${state.id}": session is in terminal phase="${state.phase}".`);
|
|
503
|
+
}
|
|
504
|
+
const { resolvedProfile, resolvedScope } = resolveLifecycleRetargetScopeAndProfile(root, {
|
|
505
|
+
text: opts.intent,
|
|
506
|
+
explicitGoalScope: opts.goalScope,
|
|
507
|
+
explicitGateProfile: opts.gateProfile,
|
|
508
|
+
currentGoalScope: state.goalScope,
|
|
509
|
+
currentGateProfile: state.gateProfile,
|
|
510
|
+
contextLabel: 'operator_ultrawork_retarget',
|
|
511
|
+
});
|
|
512
|
+
if (state.goalScope && resolvedScope && goalScopeRank(resolvedScope) > goalScopeRank(state.goalScope)) {
|
|
513
|
+
throw new Error(`Cannot retarget ultrawork "${state.id}": requested scope "${resolvedScope}" would broaden active scope "${state.goalScope}". Start a new session instead.`);
|
|
514
|
+
}
|
|
515
|
+
const event = {
|
|
516
|
+
oldIntent: state.intent,
|
|
517
|
+
newIntent: opts.intent,
|
|
518
|
+
retargetMode: opts.mode,
|
|
519
|
+
preservedState: opts.mode === 'resume-unchanged'
|
|
520
|
+
? 'preserved'
|
|
521
|
+
: opts.mode === 'narrow-scope'
|
|
522
|
+
? 'narrowed'
|
|
523
|
+
: opts.mode === 'fork-child-task'
|
|
524
|
+
? 'forked'
|
|
525
|
+
: 'replaced',
|
|
526
|
+
reason: opts.note ?? `Retargeted intent from "${state.intent}" to "${opts.intent}".`,
|
|
527
|
+
};
|
|
528
|
+
const { childTasks: retargetChildTasks, cancelledIds } = opts.mode === 'replace-intent'
|
|
529
|
+
? cancelPendingChildTasksOnReplaceIntent(state)
|
|
530
|
+
: { childTasks: state.childTasks, cancelledIds: [] };
|
|
531
|
+
if (cancelledIds.length > 0) {
|
|
532
|
+
event.reason = `${event.reason} Cancelled pending child tasks: ${cancelledIds.join(', ')}`;
|
|
533
|
+
}
|
|
534
|
+
if (opts.mode === 'resume-unchanged') {
|
|
535
|
+
const entry = {
|
|
536
|
+
phase: state.phase,
|
|
537
|
+
route: state.route,
|
|
538
|
+
at: now(),
|
|
539
|
+
by: opts.by,
|
|
540
|
+
note: opts.note ?? 'Retarget decision: resume unchanged.',
|
|
541
|
+
retargetEvent: event,
|
|
542
|
+
};
|
|
543
|
+
return save(root, { ...state, history: [...state.history, entry] });
|
|
544
|
+
}
|
|
545
|
+
if (opts.mode === 'fork-child-task') {
|
|
546
|
+
if (state.scenarioContract !== 'PASS') {
|
|
547
|
+
throw new Error(`Cannot dispatch a child task for ultrawork "${state.id}": missing scenario contract. Call operator_ultrawork_verify with happy, edge, and regression scenarios first.`);
|
|
548
|
+
}
|
|
549
|
+
const childTask = {
|
|
550
|
+
id: `child_${randomUUID().slice(0, 12)}`,
|
|
551
|
+
intent: opts.intent,
|
|
552
|
+
createdAt: now(),
|
|
553
|
+
status: 'pending',
|
|
554
|
+
};
|
|
555
|
+
const entry = {
|
|
556
|
+
phase: state.phase,
|
|
557
|
+
route: state.route,
|
|
558
|
+
at: childTask.createdAt,
|
|
559
|
+
by: opts.by,
|
|
560
|
+
note: opts.note ?? `Forked child task ${childTask.id}`,
|
|
561
|
+
retargetEvent: event,
|
|
562
|
+
};
|
|
563
|
+
const next = save(root, {
|
|
564
|
+
...state,
|
|
565
|
+
childTasks: [...state.childTasks, childTask],
|
|
566
|
+
history: [...state.history, entry],
|
|
567
|
+
});
|
|
568
|
+
appendLedger(root, {
|
|
569
|
+
kind: 'session',
|
|
570
|
+
action: 'ultrawork_retarget_fork_child',
|
|
571
|
+
detail: `Forked child task "${childTask.id}" from ultrawork "${state.id}"`,
|
|
572
|
+
metadata: { id: state.id, childTaskId: childTask.id },
|
|
573
|
+
});
|
|
574
|
+
return next;
|
|
575
|
+
}
|
|
576
|
+
const entry = {
|
|
577
|
+
phase: state.phase,
|
|
578
|
+
route: state.route,
|
|
579
|
+
at: now(),
|
|
580
|
+
by: opts.by,
|
|
581
|
+
note: opts.note ?? `Retargeted intent from "${state.intent}" to "${opts.intent}"`,
|
|
582
|
+
retargetEvent: event,
|
|
583
|
+
};
|
|
584
|
+
const next = save(root, {
|
|
585
|
+
...state,
|
|
586
|
+
intent: opts.intent,
|
|
587
|
+
goalScope: resolvedScope,
|
|
588
|
+
gateProfile: resolvedProfile,
|
|
589
|
+
childTasks: retargetChildTasks,
|
|
590
|
+
history: [...state.history, entry],
|
|
591
|
+
});
|
|
592
|
+
for (const childTaskId of cancelledIds) {
|
|
593
|
+
appendLedger(root, {
|
|
594
|
+
kind: 'session',
|
|
595
|
+
action: 'ultrawork_retarget_child_cancelled',
|
|
596
|
+
detail: `Cancelled child task "${childTaskId}" by replace-intent retarget on ultrawork "${state.id}"`,
|
|
597
|
+
metadata: { modeType: 'ultrawork', modeId: state.id, childTaskId, fromIntent: state.intent, toIntent: opts.intent },
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
appendLedger(root, {
|
|
601
|
+
kind: 'session',
|
|
602
|
+
action: 'ultrawork_retarget',
|
|
603
|
+
detail: `Retargeted ultrawork "${state.id}" via ${opts.mode}`,
|
|
604
|
+
metadata: { id: state.id, mode: opts.mode, goalScope: resolvedScope, gateProfile: resolvedProfile },
|
|
605
|
+
});
|
|
606
|
+
clearContinuationState(root, `ultrawork retargeted via ${opts.mode}`, 'operator_ultrawork_retarget');
|
|
607
|
+
return next;
|
|
608
|
+
}
|
|
609
|
+
export function completeUltraworkChildTask(root, childTaskId, opts) {
|
|
610
|
+
return setUltraworkChildTaskStatus(root, childTaskId, 'completed', opts);
|
|
611
|
+
}
|
|
612
|
+
export function cancelUltraworkChildTask(root, childTaskId, opts) {
|
|
613
|
+
return setUltraworkChildTaskStatus(root, childTaskId, 'cancelled', opts);
|
|
614
|
+
}
|
|
615
|
+
function setUltraworkChildTaskStatus(root, childTaskId, status, opts) {
|
|
616
|
+
ensureLayout(root);
|
|
617
|
+
const state = load(root);
|
|
618
|
+
if (!state) {
|
|
619
|
+
throw new Error('Cannot update ultrawork child task state: no ultrawork state file found.');
|
|
620
|
+
}
|
|
621
|
+
const index = state.childTasks.findIndex((task) => task.id === childTaskId);
|
|
622
|
+
if (index === -1) {
|
|
623
|
+
throw new Error(`No ultrawork child task found with id "${childTaskId}".`);
|
|
624
|
+
}
|
|
625
|
+
const existing = state.childTasks[index];
|
|
626
|
+
if (existing.status !== 'pending') {
|
|
627
|
+
throw new Error(`Cannot set ultrawork child task "${childTaskId}" to ${status}; current status is "${existing.status}".`);
|
|
628
|
+
}
|
|
629
|
+
const note = opts?.note ?? `${status === 'completed' ? 'Completed' : 'Cancelled'} child task "${childTaskId}"`;
|
|
630
|
+
const completedAt = now();
|
|
631
|
+
const nextChildTask = { ...existing, status };
|
|
632
|
+
const nextChildTasks = [...state.childTasks];
|
|
633
|
+
nextChildTasks[index] = nextChildTask;
|
|
634
|
+
const entry = {
|
|
635
|
+
phase: state.phase,
|
|
636
|
+
route: state.route,
|
|
637
|
+
at: completedAt,
|
|
638
|
+
by: opts?.by,
|
|
639
|
+
note,
|
|
640
|
+
retargetEvent: {
|
|
641
|
+
oldIntent: state.intent,
|
|
642
|
+
newIntent: state.intent,
|
|
643
|
+
retargetMode: 'resume-unchanged',
|
|
644
|
+
preservedState: 'preserved',
|
|
645
|
+
reason: note,
|
|
646
|
+
},
|
|
647
|
+
};
|
|
648
|
+
const next = save(root, {
|
|
649
|
+
...state,
|
|
650
|
+
childTasks: nextChildTasks,
|
|
651
|
+
history: [...state.history, entry],
|
|
652
|
+
});
|
|
653
|
+
appendLedger(root, {
|
|
654
|
+
kind: 'session',
|
|
655
|
+
action: status === 'completed' ? 'ultrawork_child_task_complete' : 'ultrawork_child_task_cancel',
|
|
656
|
+
detail: `${note} in ultrawork "${state.id}"`,
|
|
657
|
+
metadata: { id: state.id, childTaskId },
|
|
658
|
+
});
|
|
659
|
+
return next;
|
|
660
|
+
}
|
|
661
|
+
export function linkResearch(root, researchId) {
|
|
662
|
+
ensureLayout(root);
|
|
663
|
+
const state = load(root);
|
|
664
|
+
if (!state) {
|
|
665
|
+
throw new Error('Cannot link research: no ultrawork state file found.');
|
|
666
|
+
}
|
|
667
|
+
if (state.researchIds.includes(researchId)) {
|
|
668
|
+
return state;
|
|
669
|
+
}
|
|
670
|
+
const next = save(root, { ...state, researchIds: [...state.researchIds, researchId] });
|
|
671
|
+
appendLedger(root, {
|
|
672
|
+
kind: 'session',
|
|
673
|
+
action: 'ultrawork_link_research',
|
|
674
|
+
detail: `Linked research "${researchId}" to ultrawork "${state.id}"`,
|
|
675
|
+
metadata: { id: state.id, researchId },
|
|
676
|
+
});
|
|
677
|
+
return next;
|
|
678
|
+
}
|
|
679
|
+
export function completeUltrawork(root, summary) {
|
|
680
|
+
ensureLayout(root);
|
|
681
|
+
const state = load(root);
|
|
682
|
+
if (!state) {
|
|
683
|
+
throw new Error('Cannot complete ultrawork: no ultrawork state file found.');
|
|
684
|
+
}
|
|
685
|
+
if (!COMPLETION_PHASES.has(state.phase)) {
|
|
686
|
+
throw new Error(`Cannot complete ultrawork "${state.id}": completion requires phase ∈ [${[...COMPLETION_PHASES].join(', ')}], but current phase is "${state.phase}".`);
|
|
687
|
+
}
|
|
688
|
+
if (state.verifications.length === 0) {
|
|
689
|
+
throw new Error(`Cannot complete ultrawork "${state.id}": at least one verification evidence record is required before completion.`);
|
|
690
|
+
}
|
|
691
|
+
const refreshedReviewerGateTriggered = shouldTriggerReviewerGate(root, state);
|
|
692
|
+
const gateState = refreshedReviewerGateTriggered === state.reviewerGateTriggered
|
|
693
|
+
? state
|
|
694
|
+
: save(root, {
|
|
695
|
+
...state,
|
|
696
|
+
reviewerGateTriggered: refreshedReviewerGateTriggered,
|
|
697
|
+
});
|
|
698
|
+
if (gateState.reviewerGateTriggered === true
|
|
699
|
+
&& gateState.reviewerVerdict !== 'UNCONDITIONAL_APPROVE') {
|
|
700
|
+
throw new Error(`Cannot complete ultrawork "${gateState.id}": reviewer gate triggered and reviewerVerdict is not UNCONDITIONAL_APPROVE.`);
|
|
701
|
+
}
|
|
702
|
+
const completionClosure = resolveCompletionClosureState(root, gateState);
|
|
703
|
+
const entry = {
|
|
704
|
+
phase: 'completed',
|
|
705
|
+
route: gateState.route,
|
|
706
|
+
at: now(),
|
|
707
|
+
note: summary,
|
|
708
|
+
};
|
|
709
|
+
const next = save(root, {
|
|
710
|
+
...gateState,
|
|
711
|
+
phase: 'completed',
|
|
712
|
+
active: false,
|
|
713
|
+
closureState: completionClosure.closureState,
|
|
714
|
+
completionSummary: summary,
|
|
715
|
+
history: [...state.history, entry],
|
|
716
|
+
});
|
|
717
|
+
appendLedger(root, {
|
|
718
|
+
kind: 'session',
|
|
719
|
+
action: 'ultrawork_complete',
|
|
720
|
+
detail: `Completed ultrawork "${state.id}": ${summary}`,
|
|
721
|
+
metadata: {
|
|
722
|
+
id: state.id,
|
|
723
|
+
closureState: completionClosure.closureState,
|
|
724
|
+
closureStateSource: completionClosure.inferred ? 'inferred' : 'explicit',
|
|
725
|
+
},
|
|
726
|
+
});
|
|
727
|
+
upsertInboxItem(root, {
|
|
728
|
+
kind: 'system',
|
|
729
|
+
subject: 'Ultrawork session completed',
|
|
730
|
+
body: `Session "${state.id}" completed. Summary: ${summary}`,
|
|
731
|
+
status: 'open',
|
|
732
|
+
});
|
|
733
|
+
return next;
|
|
734
|
+
}
|
|
735
|
+
function countChangedFiles(root) {
|
|
736
|
+
try {
|
|
737
|
+
const output = execFileSync('git', ['-C', root, 'diff', '--name-only', 'HEAD'], {
|
|
738
|
+
encoding: 'utf8',
|
|
739
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
740
|
+
});
|
|
741
|
+
return output
|
|
742
|
+
.split(/\r?\n/)
|
|
743
|
+
.map((line) => line.trim())
|
|
744
|
+
.filter(Boolean)
|
|
745
|
+
.length;
|
|
746
|
+
}
|
|
747
|
+
catch {
|
|
748
|
+
return 0;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
function shouldTriggerReviewerGate(root, state) {
|
|
752
|
+
if (state.reviewerGateTriggered === true) {
|
|
753
|
+
return true;
|
|
754
|
+
}
|
|
755
|
+
const changedFiles = countChangedFiles(root);
|
|
756
|
+
const riskyIntent = /\b(refactor|migration|migrate|perf|performance|security)\b/i.test(state.intent);
|
|
757
|
+
return changedFiles >= 3 || riskyIntent;
|
|
758
|
+
}
|
|
759
|
+
export function blockUltrawork(root, reason) {
|
|
760
|
+
ensureLayout(root);
|
|
761
|
+
const state = load(root);
|
|
762
|
+
if (!state) {
|
|
763
|
+
throw new Error('Cannot block ultrawork: no ultrawork state file found.');
|
|
764
|
+
}
|
|
765
|
+
if (TERMINAL_PHASES.has(state.phase)) {
|
|
766
|
+
throw new Error(`Cannot block ultrawork "${state.id}": session is already in terminal phase="${state.phase}".`);
|
|
767
|
+
}
|
|
768
|
+
const entry = {
|
|
769
|
+
phase: 'blocked',
|
|
770
|
+
route: state.route,
|
|
771
|
+
at: now(),
|
|
772
|
+
note: reason,
|
|
773
|
+
};
|
|
774
|
+
const next = save(root, {
|
|
775
|
+
...state,
|
|
776
|
+
phase: 'blocked',
|
|
777
|
+
blockerReason: reason,
|
|
778
|
+
history: [...state.history, entry],
|
|
779
|
+
});
|
|
780
|
+
appendLedger(root, {
|
|
781
|
+
kind: 'session',
|
|
782
|
+
action: 'ultrawork_block',
|
|
783
|
+
detail: `Blocked ultrawork "${state.id}": ${reason}`,
|
|
784
|
+
metadata: { id: state.id, reason },
|
|
785
|
+
});
|
|
786
|
+
clearContinuationState(root, reason, 'operator_ultrawork_block');
|
|
787
|
+
return next;
|
|
788
|
+
}
|
|
789
|
+
export function abortUltrawork(root, reason) {
|
|
790
|
+
ensureLayout(root);
|
|
791
|
+
const state = load(root);
|
|
792
|
+
if (!state) {
|
|
793
|
+
throw new Error('Cannot abort ultrawork: no ultrawork state file found.');
|
|
794
|
+
}
|
|
795
|
+
if (TERMINAL_PHASES.has(state.phase)) {
|
|
796
|
+
throw new Error(`Cannot abort ultrawork "${state.id}": session is already in terminal phase="${state.phase}".`);
|
|
797
|
+
}
|
|
798
|
+
const entry = {
|
|
799
|
+
phase: 'aborted',
|
|
800
|
+
route: state.route,
|
|
801
|
+
at: now(),
|
|
802
|
+
note: reason,
|
|
803
|
+
};
|
|
804
|
+
const next = save(root, {
|
|
805
|
+
...state,
|
|
806
|
+
phase: 'aborted',
|
|
807
|
+
active: false,
|
|
808
|
+
abortReason: reason,
|
|
809
|
+
history: [...state.history, entry],
|
|
810
|
+
});
|
|
811
|
+
appendLedger(root, {
|
|
812
|
+
kind: 'session',
|
|
813
|
+
action: 'ultrawork_abort',
|
|
814
|
+
detail: `Aborted ultrawork "${state.id}": ${reason}`,
|
|
815
|
+
metadata: { id: state.id, reason },
|
|
816
|
+
});
|
|
817
|
+
upsertInboxItem(root, {
|
|
818
|
+
kind: 'system',
|
|
819
|
+
subject: 'Ultrawork session aborted',
|
|
820
|
+
body: `Session "${state.id}" aborted. Reason: ${reason}`,
|
|
821
|
+
status: 'open',
|
|
822
|
+
});
|
|
823
|
+
clearContinuationState(root, reason, 'operator_ultrawork_abort');
|
|
824
|
+
return next;
|
|
825
|
+
}
|
|
826
|
+
export function setUltraworkClosureState(root, closureState, note) {
|
|
827
|
+
ensureLayout(root);
|
|
828
|
+
const state = load(root);
|
|
829
|
+
if (!state) {
|
|
830
|
+
throw new Error('Cannot set closure state: no ultrawork state file found.');
|
|
831
|
+
}
|
|
832
|
+
if (TERMINAL_PHASES.has(state.phase)) {
|
|
833
|
+
throw new Error(`Cannot set closure state on ultrawork "${state.id}": session is in terminal phase="${state.phase}".`);
|
|
834
|
+
}
|
|
835
|
+
const next = save(root, { ...state, closureState });
|
|
836
|
+
appendLedger(root, {
|
|
837
|
+
kind: 'session',
|
|
838
|
+
action: 'ultrawork_closure_state',
|
|
839
|
+
detail: `Set closureState="${closureState}" on ultrawork "${state.id}"${note ? ': ' + note : ''}`,
|
|
840
|
+
metadata: { id: state.id, closureState },
|
|
841
|
+
});
|
|
842
|
+
return next;
|
|
843
|
+
}
|
|
844
|
+
// ── Phase 8 — AzDo task link helpers ────────────────────────────────────────
|
|
845
|
+
export function linkUltraworkTask(root, taskId) {
|
|
846
|
+
ensureLayout(root);
|
|
847
|
+
const state = load(root);
|
|
848
|
+
if (!state) {
|
|
849
|
+
throw new Error('No Ultrawork state found. Start Ultrawork first.');
|
|
850
|
+
}
|
|
851
|
+
if (state.taskId && state.taskId !== taskId) {
|
|
852
|
+
throw new Error(`Ultrawork "${state.id}" already linked to "${state.taskId}". ` +
|
|
853
|
+
`Call unlinkUltraworkTask first to retarget.`);
|
|
854
|
+
}
|
|
855
|
+
const next = save(root, { ...state, taskId });
|
|
856
|
+
appendLedger(root, {
|
|
857
|
+
kind: 'session',
|
|
858
|
+
action: 'ultrawork_link_task',
|
|
859
|
+
detail: `Ultrawork "${state.id}" linked to task "${taskId}"`,
|
|
860
|
+
metadata: { modeType: 'ultrawork', modeId: state.id, taskId },
|
|
861
|
+
});
|
|
862
|
+
return next;
|
|
863
|
+
}
|
|
864
|
+
export function linkUltraworkAssignment(root, assignmentId) {
|
|
865
|
+
ensureLayout(root);
|
|
866
|
+
const state = load(root);
|
|
867
|
+
if (!state) {
|
|
868
|
+
throw new Error('No Ultrawork state found. Start Ultrawork first.');
|
|
869
|
+
}
|
|
870
|
+
if (state.assignmentId && state.assignmentId !== assignmentId) {
|
|
871
|
+
throw new Error(`Ultrawork "${state.id}" already linked to assignment "${state.assignmentId}". ` +
|
|
872
|
+
'Call unlinkUltraworkAssignment first to retarget.');
|
|
873
|
+
}
|
|
874
|
+
const next = save(root, { ...state, assignmentId });
|
|
875
|
+
appendLedger(root, {
|
|
876
|
+
kind: 'session',
|
|
877
|
+
action: 'ultrawork_link_assignment',
|
|
878
|
+
detail: `Ultrawork "${state.id}" linked to assignment "${assignmentId}"`,
|
|
879
|
+
metadata: { modeType: 'ultrawork', modeId: state.id, assignmentId },
|
|
880
|
+
});
|
|
881
|
+
return next;
|
|
882
|
+
}
|
|
883
|
+
export function unlinkUltraworkTask(root) {
|
|
884
|
+
ensureLayout(root);
|
|
885
|
+
const state = load(root);
|
|
886
|
+
if (!state) {
|
|
887
|
+
throw new Error('No Ultrawork state found. Start Ultrawork first.');
|
|
888
|
+
}
|
|
889
|
+
const previous = state.taskId;
|
|
890
|
+
const { taskId: _removed, ...rest } = state;
|
|
891
|
+
void _removed;
|
|
892
|
+
const next = save(root, rest);
|
|
893
|
+
appendLedger(root, {
|
|
894
|
+
kind: 'session',
|
|
895
|
+
action: 'ultrawork_unlink_task',
|
|
896
|
+
detail: `Ultrawork "${state.id}" unlinked from task "${previous ?? 'none'}"`,
|
|
897
|
+
metadata: { modeType: 'ultrawork', modeId: state.id, previousTaskId: previous ?? null },
|
|
898
|
+
});
|
|
899
|
+
return next;
|
|
900
|
+
}
|
|
901
|
+
export function unlinkUltraworkAssignment(root) {
|
|
902
|
+
ensureLayout(root);
|
|
903
|
+
const state = load(root);
|
|
904
|
+
if (!state) {
|
|
905
|
+
throw new Error('No Ultrawork state found. Start Ultrawork first.');
|
|
906
|
+
}
|
|
907
|
+
const previous = state.assignmentId;
|
|
908
|
+
const { assignmentId: _removed, ...rest } = state;
|
|
909
|
+
void _removed;
|
|
910
|
+
const next = save(root, rest);
|
|
911
|
+
appendLedger(root, {
|
|
912
|
+
kind: 'session',
|
|
913
|
+
action: 'ultrawork_unlink_assignment',
|
|
914
|
+
detail: `Ultrawork "${state.id}" unlinked from assignment "${previous ?? 'none'}"`,
|
|
915
|
+
metadata: { modeType: 'ultrawork', modeId: state.id, previousAssignmentId: previous ?? null },
|
|
916
|
+
});
|
|
917
|
+
return next;
|
|
918
|
+
}
|
|
919
|
+
//# sourceMappingURL=index.js.map
|