Skip to content

Proposal: universal PreToolUse/PostToolUse hook layer for Cancel/Pause/Resume across all action types #1917

@aboimpinto

Description

@aboimpinto

Summary

Following the analysis of issues #1886-#1900 (slash commands productization and refactoring), we identified a unifying architectural pattern: a hook-based lifecycle layer (PreToolUse / PostToolUse) that provides Cancel (with rollback), Pause, and Resume for any action that calls a tool — not just slash commands, but Skills, Agents, MCP Servers, and Plugins equally.


The Core Proposal: Hooks as a Universal Lifecycle Layer

Instead of building lifecycle (pause/cancel/resume/continuity) separately for each action type, implement it once at the tool execution boundary:

                    ┌─────────────────────────────────┐
                    │        PreToolUse Hook           │
                    │                                  │
Slash Command ──┐  │  • Check pause.lock?              │
Skill          ├──┤  • Check allowed-tools?            │
Agent          ├──►  • Snapshot file state             │
MCP Server     ─┘  │  • Check pausable policy          │
Plugin         ─┘   │  • Emit state to cockpit        │
                    │                                  │
                    └──────────┬──────────────────────┘
                               │
                    ┌──────────▼──────────────────────┐
                    │        PostToolUse Hook          │
                    │  • Capture receipt               │
                    │  • Save to orientation cache     │
                    │  • Update cockpit status         │
                    │  • Report completion/failure     │
                    └─────────────────────────────────┘

Why this matters

Every action type — slash command, skill, agent, MCP call, plugin — ultimately calls tools (exec_shell, read_file, edit_file, etc.). The tool call boundary is the natural safe-point where the lifecycle can intervene. A hook at this level automatically benefits all action types without each one reimplementing the same logic.


Lifecycle States and Transitions

Cancel (Transactional Rollback)

Triggered by: Double ESC / Ctrl+C

  • PreToolUse (before first tool): Snapshot workspace state (git status, dirty files)
  • PostToolUse (on interrupt): Restore snapshot, revert side effects
  • Outcome: Workspace returned to pre-command state

Pause (Execution Suspend)

Triggered by: Single ESC while pausable: true

  • PreToolUse: Check for .state/pause.lock. If present: block, save execution position, emit "paused" to cockpit
  • On resume: User removes lock, PreToolUse proceeds, model continues from same tool
  • Outcome: Execution frozen between tool calls. No rollback. Dirty state preserved.

Resume

Triggered by: typing "continue" or removing pause lock.

The PostToolUse hook has serialized the last completed tool result. The model context is identical. The next PreToolUse check sees no lock and lets execution proceed. No cache invalidation. No conversational pollution.

Continue (ad-hoc, non-pausable)

When pausable: false or absent, ESC/Ctrl+C simply stops the current tool call. Typing "continue" starts a new model turn. This is the current behaviour and is preserved for simple shortcuts like /commit-push that don't need lifecycle tracking.


The pausable Property

Actions declare whether they participate in the lifecycle via a pausable flag, scoped to each action type:

Custom Slash Commands (frontmatter)

---
description: Commit and push all repositories
allowed-tools: Bash, Read, Grep
pausable: true
---
Please, git commit add the changes...

Skills (SKILL.md frontmatter)

---
name: code-review
description: Review code changes
pausable: true
---
...

Agents (runtime configuration)

[agents.code-review]
pausable = true
max_turns = 20

MCP Servers (config default)

[mcp.servers.postgres]
command = "npx"
pausable = false

Behaviour matrix

pausable Single ESC Double ESC / Ctrl+C "continue"
true Pause at next safe-point Cancel with rollback Resume from safe-point
false / absent Stop tool (current) Stop tool (current) New turn (current)

Relationship to Existing Issues

Slash Commands (#1886-#1892)

Issue Current scope Proposed re-scope
#1887 Cockpit Lifecycle visibility for slash commands only Lifecycle visibility for all action types via hooks
#1888 Control plane Pause/cancel/resume for slash commands Pause/cancel/resume at hook level, benefits all action types
#1889 Continuity Durable receipts for slash commands Receipts captured by PostToolUse hook, action-agnostic
#1890 Docs/help Slash command documentation Document hook lifecycle as well
#1891 Tool studio Tool command consistency PreToolUse enforces allowed-tools across all actions
#1892 Workbench Commands land as cards All pausable actions land as cards

Refactoring (#1894-#1900)

Issue Connection to hook layer
#1894 Truth surface Hooks emit state, shared projection consumes it
#1895 Control plane Hooks implement the inspect/cancel/recover contract
#1896 Workbench model Unified task structure populated by hooks
#1898 Continuity PostToolUse feeds the orientation cache
#1900 Tool studio PreToolUse validates via registry adapters

Questions for Discussion

  1. Should lifecycle hooks be user-configurable (e.g. ~/.deepseek/hooks/) or engine-internal?
  2. Does the existing approval system already serve as a form of pause? Should it be unified with this?
  3. Is pausable the right name, or would lifecycle, managed, or supervised be clearer?
  4. For short commands like /commit-push that complete in seconds, is pausable: false the right default?

Summary

Dimension Current approach Proposed approach
Scope Per-action-type implementation Single hook layer for all action types
Lifecycle Ad-hoc "continue" prompt Formal PreToolUse/PostToolUse gates
Cache impact High (interrupt invalidates prefix cache) Zero (pause at safe-point preserves cache)
State management Probabilistic (model re-derives plan) Deterministic (snapshot + receipt)
Cost High (cache miss on every interrupt) Low (cache preserved across pause/resume)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdocumentationImprovements or additions to documentationenhancementNew feature or requestv0.9.0Targeting v0.9.0

    Projects

    Status
    Backlog

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions