| name | durable-request |
| author | Heng Shi <heng.shi@enflame-tech.com> |
| description | Durable request skill — keeps every AI agent conversation alive across multiple tasks instead of silently finishing. AGENT SHOULD STRICTLY FOLLOW THIS PROTOCOL WHEN ACTIVATED. ONLY activate when the user explicitly invokes. |
Durable Request — Never End Silently
The Number One Rule
DO NOT DIRECTLY STOP AT ANY CONDITION !!!
When you finish ANY task or reach ANY natural stopping point, you MUST present an interactive checkpoint to the user before ending your turn. No exceptions. No silent completions.
This applies to:
- Code changes (new features, bug fixes, refactors)
- File operations (create, edit, delete, move)
- Research or analysis tasks
- Writing or documentation
- Debugging sessions
- Configuration changes
- ANY work that produces output or side effects
- Pure informational Q&A (e.g., "What does git rebase do?")
No exceptions. No silent completions. Always checkpoint.
Checkpoint Architecture
durable-request uses platform-specific blocking checkpoints:
Cursor Editor Copilot IDE (VSCode) Cursor CLI Opencode / Other
───────────────────────── ──────────────────────────── ──────────────────────────── ────────────────────────
AskQuestion #vscode/askQuestions checkpoint.sh via Shell question / other tool
Built-in UI widget Question Carousel UI Tmux split-pane interactive Built-in UI widget
Blocks turn, same request Blocks turn, same request Blocks via file polling Blocks turn, same request
User picks from structured User picks or types User picks in tmux pane User picks or types
Checkpoint Mechanism
Step 1: Anchor Checkpoint Intent with TodoWrite
Before attempting ANY interactive tool, always call TodoWrite first (or the equivalent task-tracking tool in your environment) to register the checkpoint as an unfinished obligation:
TodoWrite([
{ id: "durable-checkpoint", content: "Present interactive checkpoint to user", status: "in_progress" },
... (keep any existing todos)
])
This serves two purposes:
- Structural anchor: The todo list now shows an incomplete item. You cannot end your turn with an
in_progress todo — it forces you to resolve it.
- Audit trail: If the interactive tool fails or is unavailable, the todo makes the failure visible rather than silent.
After the user responds to the checkpoint, mark it complete:
TodoWrite([
{ id: "durable-checkpoint", status: "completed" }
])
Step 2: Attempt the Interactive Tool
Detect your environment and attempt the appropriate interactive tool. Always try the tool first. Report the outcome explicitly.
Environment Detection
Detect your environment and use the right tool:
| Priority | Signal | Environment | Action |
|---|
| 1 | You are running inside Cursor CLI (no GUI, terminal-only) | Cursor CLI | Call checkpoint.sh via Shell |
| 2 | AskQuestion is available | Cursor editor (IDE) | Call AskQuestion |
| 3 | #vscode/askQuestions tool is available | Copilot IDE (VSCode) | Call #vscode/askQuestions |
| 4 | AskUserQuestion is available | Claude Code | Call AskUserQuestion |
| 5 | question is available | OpenCode | Call question |
| 6 | You are a subagent with no interactive tools | Subagent only | Conversational fallback |
| — | None of the above (unexpected) | Unknown | ERROR — report failure |
How to detect Cursor CLI vs IDE: If AskQuestion is called and it fails or is interrupted (returns an error), you are in Cursor CLI. Immediately switch to checkpoint.sh via Shell. Do NOT fall back to conversational text.
How to detect Copilot IDE: If you are running in VS Code with GitHub Copilot, the #vscode/askQuestions tool is available. Use it instead of AskQuestion.
Platform-specific tools are MANDATORY. Never substitute conversational fallback.
- Cursor IDE →
AskQuestion
- Cursor CLI →
checkpoint.sh via Shell (if AskQuestion fails, that confirms CLI)
- Copilot IDE (VSCode) →
#vscode/askQuestions
- OpenCode →
question
- Claude Code →
AskUserQuestion
- Subagent → Conversational fallback (ONLY case where text fallback is allowed)
- Unknown/Other → ERROR. Do not guess. Report the issue.
Cursor Editor (IDE): Call AskQuestion
In the Cursor IDE (graphical editor), call AskQuestion with a single question:
{
"questions": [
{
"id": "next_action",
"prompt": "<1-2 sentence summary>. What would you like to do next?",
"options": [
{"id": "A", "label": "<most likely next action>"},
{"id": "B", "label": "<second most likely action>"},
{"id": "C", "label": "<third most likely action>"},
{"id": "D", "label": "/deep-sleep"}
],
"allow_multiple": false
}
]
}
Rules:
- Single question only — never use multiple questions
- Exactly 4 options: A, B, C are context-generated likely paths; D is always
/deep-sleep
- Generate A/B/C based on context — predict the 3 most probable user intents
- D is a fixed shortcut — when user selects it, invoke deep-sleep.sh immediately
- Cursor IDE automatically appends a freeform text input after the last option — do NOT add an empty option manually
- The
prompt should be a 1-2 sentence summary of what was completed
AskQuestion blocks your turn without ending the request. This is what makes the request "durable."
If AskQuestion fails or is interrupted → you are in Cursor CLI. Switch to checkpoint.sh immediately (see below).
Copilot IDE (VSCode): Call #vscode/askQuestions
In VS Code with GitHub Copilot, call #vscode/askQuestions to present a Question Carousel UI:
{
"sections": [
{
"title": "What would you like to do next?",
"fields": [
{
"type": "radio",
"name": "next_action",
"label": "<1-2 sentence summary of what was completed>",
"options": ["<A: most likely>", "<B: second likely>", "<C: third likely>"]
},
{
"type": "text",
"name": "D",
"label": ""
}
]
}
]
}
Rules:
- 3 radio options + 1 deep-sleep option (D) — generate A/B/C based on context; D is always
/deep-sleep
- The IDE auto-appends a freeform text field — do NOT add an empty option
- The
label in the radio field is the task summary
- The Question Carousel blocks the turn until user responds
Cursor CLI: Call checkpoint.sh via Shell
In Cursor CLI, ALWAYS use checkpoint.sh via Shell. NEVER use AskQuestion (it does not work in CLI). NEVER fall back to conversational text.
bash ~/.cursor/skills/durable-request/checkpoint.sh \
"<1-2 sentence summary>. What would you like to do next?" \
"<A: most likely next action>" \
"<B: second most likely>" \
"<C: third most likely>"
Prerequisite: cursor-agent must be running inside a tmux session. Add this alias to ~/.bashrc:
alias cursor-agent='tmux new-session -A -s cursor -- cursor-agent'
Rules:
- Call this via the Shell tool
- First argument is the prompt (1-2 sentence summary)
- Generate 3 context-specific options (A, B, C) — predict most likely user intents
- The script automatically appends D (
/deep-sleep) — freeform input is handled by the UI
- The script returns
[durable-request] User responded: <choice> — use this to continue
- If tmux is not available, the script auto-selects the first option
How it works: The Shell call blocks while checkpoint.sh creates a tmux split pane running checkpoint-ui.sh. The user sees an interactive prompt in the bottom pane, selects an option, and the pane auto-closes. The agent reads the response and continues — same request, same context.
Claude Code: Call AskUserQuestion
Same pattern, adapted to Claude Code's schema.
OpenCode: Call question
In OpenCode, question is the required checkpoint mechanism. Do not use conversational
fallback when question is available.
Use a single question with concise context-adapted options and let the built-in custom
answer path handle freeform follow-up.
Step 3: Handle the Result + Call Reinforce
After the checkpoint tool returns the user's response, IMMEDIATELY call reinforce.sh via Shell:
bash ~/.cursor/skills/durable-request/reinforce.sh \
--skill-path "~/.cursor/skills/durable-request/SKILL.md"
This applies to ALL environments (IDE, CLI, Claude Code, OpenCode). In CLI mode, checkpoint.sh calls reinforce.sh automatically — you don't need to call it again. In IDE mode (AskQuestion), you MUST call it yourself via Shell after receiving the user's response.
reinforce.sh outputs:
[ANCHOR] — reminder to keep the durable-checkpoint todo in_progress
[PROTOCOL REFRESH] (every 5 checkpoints) — re-read SKILL.md to prevent attention decay
When you see [PROTOCOL REFRESH], you MUST re-read the SKILL.md file before continuing. Also re-read when context compaction occurs (you'll notice earlier instructions feeling unfamiliar).
Reporting the result
For AskQuestion / AskUserQuestion / question:
[durable-request] Called the builtin interactive checkpoint tool — your selection was received. Continuing in the same request.
For checkpoint.sh (CLI): The output contains [durable-request] User responded: <choice>. Parse the choice and continue:
[durable-request] Called checkpoint.sh — user responded: "". Continuing.
Then execute the user's selected action. After completing it, loop back to Step 1 (register new checkpoint todo → checkpoint again → ...). Continue until the user selects "done."
If checkpoint.sh FAILED (no tmux / error):
The script auto-selects the first option and prints setup instructions. Tell the user:
[durable-request] checkpoint.sh requires tmux. Run cursor-agent inside tmux: tmux new-session -A -s cursor -- cursor-agent
The agent continues with the auto-selected option.
If the expected tool is missing
The environment detection table above already specifies each platform's tool. If that tool is unavailable: ERROR — report failure. Conversational fallback (numbered text options) is ONLY for subagents that truly lack any interactive mechanism.
The TodoWrite + Checkpoint Reinforcement Pattern
Always call TodoWrite and the checkpoint tool in the same tool call batch — this creates a double-lock. TodoWrite anchors the obligation; the checkpoint tool blocks the turn. This is not optional.
Anti-Silent-Completion Rules
If you catch yourself thinking any of these, STOP — you're about to end silently:
- "Task is done" → present checkpoint FIRST
- "That's all they asked for" → they may want more — ASK
- "The output speaks for itself" → never assume, present options
- "They'll ask if they want more" → YOUR job to offer
- "TodoWrite is overhead" → it's the anchor that prevents silent endings
Multi-Step Tasks
For tasks with multiple steps:
- Complete each step
- Present a brief checkpoint after each significant step (not every micro-action)
- If user selects "Continue", proceed and checkpoint again after the next step
- Final checkpoint should be more comprehensive
Significant step = anything that changes files, produces output, or takes > 30 seconds.
Automatic Todo Cleanup
When the todo list exceeds 20 items, clean up before the next checkpoint.
Using the Cleanup Script
Call todo-cleanup.sh via Shell with the current todos as JSON input:
echo '$CURRENT_TODOS_JSON' | bash ~/.cursor/skills/durable-request/todo-cleanup.sh
The script outputs the cleaned list. Pass it to TodoWrite({ todos: ..., merge: false }).
Rules (enforced by script)
- Only delete
completed items — never delete pending or in_progress
- Never delete
durable-checkpoint — regardless of status
- Target: 5 items — keeps all active + newest completed until total = 5
- If active > 5 — keeps all active, removes all completed
When to Clean Up
- Before checkpoint — if count > 20
- After user responds — to remove stale completed items
The Durable Loop Pattern
Do Work → TodoWrite (anchor) → Checkpoint tool (block) → User responds → repeat.
If user selects "done" → END. Otherwise → execute their choice → checkpoint again.
For detailed flow diagrams, see references/flow-diagrams.md.
Steering (Optional)
Steering allows users to redirect the agent mid-execution without waiting for a checkpoint.
How Steering Works
User runs: steer "focus on the API layer"
│
▼
~/.durable-request/data/steering-message (file created)
│
▼
preToolUse hook detects file at next tool call
│
▼
Hook injects: "⚡ [USER STEERING]: focus on the API layer"
│
▼
Agent sees steering in context and adjusts behavior
Detecting Steering Messages
CRITICAL: Steering messages appear in Shell tool output. After every Shell tool call, scan the output for this pattern:
╔══════════════════════════════════════════════════════════════╗
║ ⚡ USER STEERING MESSAGE ║
╠══════════════════════════════════════════════════════════════╣
║ <user's steering message here> ║
╚══════════════════════════════════════════════════════════════╝
When you see this in Shell output, you MUST:
- Immediately acknowledge using the bounding-box format shown below
- Adjust your approach based on the user's direction
- Continue with the modified plan
This is NOT optional. Failing to acknowledge steering is a protocol violation.
Steering Acknowledgement Format — MANDATORY BOUNDING BOX
You MUST repeat the steering message verbatim AND your response inside a bounding box in your reply text. Use this exact format:
╔══════════════════════════════════════════════════════════════╗
║ ⚡ STEERING RECEIVED ║
╠══════════════════════════════════════════════════════════════╣
║ Message : <exact steering text from the box> ║
╠══════════════════════════════════════════════════════════════╣
║ Response: <your acknowledgement and adjusted plan, ≤2 lines> ║
╚══════════════════════════════════════════════════════════════╝
Example:
If Shell output contains:
╔══════════════════════════════════════════════════════════════╗
║ ⚡ USER STEERING MESSAGE ║
╠══════════════════════════════════════════════════════════════╣
║ focus on the API layer ║
╚══════════════════════════════════════════════════════════════╝
Your reply MUST contain:
╔══════════════════════════════════════════════════════════════╗
║ ⚡ STEERING RECEIVED ║
╠══════════════════════════════════════════════════════════════╣
║ Message : focus on the API layer ║
╠══════════════════════════════════════════════════════════════╣
║ Response: Understood. Pivoting to the API layer now — ║
║ deprioritising UI changes until you say otherwise. ║
╚══════════════════════════════════════════════════════════════╝
Rules for the bounding box:
- Always use the full box — never abbreviate or omit it
- Message line: copy the steering text exactly as received
- Response lines: keep to ≤ 3 lines; be concrete about what changes
- Place the box at the very top of your reply, before any other content
Steering Detection Protocol
After every Shell tool call, check the output for steering:
- Look for
USER STEERING MESSAGE or ⚡ in the output
- If found, extract the message content
- Immediately acknowledge in your next response text
- Adjust your plan accordingly
Non-Shell tools (Read, Write, Grep, etc.) do NOT show steering. Steering only appears when Shell is called. If the user sends steering but you're only using non-Shell tools, you won't see it until your next Shell call.
Steering Does NOT Replace Checkpoints
Steering is for mid-turn adjustments. You still MUST present a checkpoint when:
- The task is complete
- A significant step is finished
- You reach a natural stopping point
Steering and checkpoints are complementary:
- Steering: User redirects you while you're working
- Checkpoint: You pause and ask what to do next
Guardrail Harness (G1 + G3)
reinforce.sh provides layered drift defense for long sessions:
- G1 —
[NEXT] directives: Tell you what to do after the checkpoint (execute user's choice, then checkpoint again). Appears in the most-recent tool output for maximum attention.
- G3 — Protocol refresh: Every 5 checkpoints, prints
[PROTOCOL REFRESH] instructing you to re-read this SKILL.md. When you see this, you MUST re-read the file. This prevents attention decay in long sessions.
- G2 — Anchor reminder: Reminds you to keep
durable-checkpoint as in_progress.
How it's called:
- CLI:
checkpoint.sh calls reinforce.sh automatically — zero agent effort.
- IDE / all other environments: You MUST call
reinforce.sh via Shell after every AskQuestion / question / AskUserQuestion response (see Step 3 above).
- Context compaction: If you suspect context compaction occurred (early instructions feel unfamiliar), call
reinforce.sh proactively.
Deep Sleep (Optional)
Deep sleep keeps the agent alive when the user is away for an extended period. Instead of the request timing out, the agent enters a low-power polling loop that prints periodic keep-alive messages.
When to Use Deep Sleep
Use deep sleep when:
- You've presented a checkpoint (AskQuestion) but suspect the user has stepped away
- A long-running background task is still executing and you need to stay alive
- The user explicitly says they'll be back later (e.g., "I'll be back in 30 minutes")
Do NOT use deep sleep:
- As a substitute for checkpoints — always checkpoint first
- When the user is actively responding
- In subagents (they should just return results)
How to Invoke Deep Sleep
Call deep-sleep.sh via the Shell tool:
bash ~/.cursor/skills/durable-request/deep-sleep.sh [timeout_minutes]
- Default timeout: 1440 minutes (24 hours)
- The script prints
[deep-sleep] Still sleeping... X/Y min elapsed. I am working, keep waiting... every 60 seconds
- This keeps the Shell tool blocking and prevents the request from timing out
Wake Mechanism
The script polls for a wake file. The user can wake the agent by:
touch ~/.cursor/skills/durable-request/.deep-sleep-wake
When woken (or when max timeout is reached), the script prints:
[deep-sleep] Wake signal received after X minute(s).
[deep-sleep] Agent resuming — continue your work and present a checkpoint.
After waking, you MUST present a new checkpoint to the user. Deep sleep is a bridge, not a destination.
Deep Sleep Flow
┌──────────────────────────────────────────────────────────────┐
│ │
│ Checkpoint presented (AskQuestion / checkpoint.sh) │
│ │ │
│ ▼ │
│ User doesn't respond for a while / says "brb" │
│ │ │
│ ▼ │
│ Shell: deep-sleep.sh 120 │
│ │ │
│ ├── every 60s: "[deep-sleep] Still sleeping..." │
│ │ │
│ ▼ │
│ Wake signal received OR timeout │
│ │ │
│ ▼ │
│ Present new checkpoint to user │
│ │
└──────────────────────────────────────────────────────────────┘
Integration with Other Skills
This skill does NOT override task-specific loop behavior. Skills with their own loop/continuation logic (e.g., tuning sweeps, FSM engines) take precedence internally. This checkpoint applies at task boundaries when those skills complete.
Priority: Task-specific loops > durable-request (at task boundaries only)
Integration with /goal
When /goal is active (goal.json exists with status: "pursuing"):
-
Suppress durable-request checkpoints — do NOT call AskQuestion or
checkpoint.sh while the goal is being pursued. The /goal stop hook
handles auto-continuation via followup_message.
-
When goal completes (achieved or budget-limited):
- Report the goal outcome to the user
- Invoke
/deep-sleep to keep the session alive (user is likely away
since they set an autonomous goal)
- When user wakes from deep sleep, present a normal checkpoint
-
When goal is cleared by user:
- Return to normal durable-request behavior immediately
- Present a checkpoint at the next stopping point
What This Skill Is NOT
- NOT a gate that blocks progress
- NOT a replacement for task-specific checkpoints
- NOT permission to slow down autonomous work within a task
- It IS a universal "don't disappear after finishing" mechanism