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
| 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 |
| 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
- Should lifecycle hooks be user-configurable (e.g.
~/.deepseek/hooks/) or engine-internal?
- Does the existing approval system already serve as a form of pause? Should it be unified with this?
- Is
pausable the right name, or would lifecycle, managed, or supervised be clearer?
- 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) |
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:
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
Pause (Execution Suspend)
Triggered by: Single ESC while
pausable: true.state/pause.lock. If present: block, save execution position, emit "paused" to cockpitResume
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: falseor 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-pushthat don't need lifecycle tracking.The
pausablePropertyActions declare whether they participate in the lifecycle via a
pausableflag, scoped to each action type:Custom Slash Commands (frontmatter)
Skills (SKILL.md frontmatter)
Agents (runtime configuration)
MCP Servers (config default)
Behaviour matrix
Relationship to Existing Issues
Slash Commands (#1886-#1892)
Refactoring (#1894-#1900)
Questions for Discussion
~/.deepseek/hooks/) or engine-internal?pausablethe right name, or wouldlifecycle,managed, orsupervisedbe clearer?/commit-pushthat complete in seconds, ispausable: falsethe right default?Summary