Repository navigation
Research: AI agent state persistence compatibility #1329
Description
Activity
Compatibility matrix and storage guidelines draft PR available at: coder/coder#22222
Implementation guide for bringing each AI agent module to full pause/resume support.
- Foundation: PR #736 adds
enable_state_persistence,state_file_path,pid_file_pathto the agentapi base module.
Status
Agent Module Path State Work Required Effort Claude Code coder/modules/claude-codeDone None Done Codex coder-labs/modules/codexDone Add --type codexTrivial Copilot coder-labs/modules/copilotDone Update session dir check (minor) Trivial OpenCode coder-labs/modules/opencodeNeeds update Change continuedefault totrueTrivial Auggie coder-labs/modules/auggieNeeds update Change two defaults to true, add--typeSmall Aider coder/modules/aiderNeeds update Change report_tasksdefault, fix--typeconditionalSmall Goose coder/modules/gooseNeeds update Wire --resume, add--typeSmall Gemini coder-labs/modules/geminiNeeds update Wire --resume, add--typeSmall Amazon Q coder/modules/amazon-qNeeds update Wire --resume, add--type. Deprecated upstream.Small Cursor CLI coder-labs/modules/cursor-cliNeeds update Wire --resumewith chat ID captureMedium Sourcegraph Amp coder-labs/modules/sourcegraph-ampNeeds update Wire amp threads continuewith thread ID captureMedium Priority 1: Default changes (Trivial)
OpenCode
File:
registry/coder-labs/modules/opencode/main.tf, line 116variable "continue" { # Change default from false to true default = true }
The module's
start.shalready handles this: whenARG_CONTINUE=true, it passes--continueto opencode. No script changes needed.File:
registry/coder-labs/modules/opencode/scripts/start.sh(no changes, for reference)- Line 14:
ARG_CONTINUE=${ARG_CONTINUE:-false}(receives from main.tf) - Line 48:
OPENCODE_ARGS+=(--continue)(already wired)
Copilot (minor)
File:
registry/coder-labs/modules/copilot/scripts/start.sh,check_existing_session()functionCurrently checks only the legacy path:
local session_dir="$HOME/.copilot/history-session-state"
Update to also check the new path (v0.0.342+):
local legacy_dir="$HOME/.copilot/history-session-state" local new_dir="$HOME/.copilot/session-state" if { [ -d "$legacy_dir" ] && [ -n "$(ls "$legacy_dir"/session_*_*.json 2>/dev/null)" ]; } || \ { [ -d "$new_dir" ] && [ -n "$(ls "$new_dir"/*.json 2>/dev/null)" ]; }; then
Alternatively, simplify by always passing
--continuesince Copilot gracefully starts a new session when none exists.Auggie
File:
registry/coder-labs/modules/auggie/main.tfTwo variables need default changes:
variable "continue_previous_conversation" { # Line 114, change default from false to true default = true } variable "report_tasks" { # Line 142, change default from false to true default = true }
The module's
start.shalready handlescontinue_previous_conversation: when true, it passes--continueto auggie (line 83). No script changes needed for the default flip.Caveat: Headless
--continuebehavior is unverified. Test in an agentapi-wrapped workspace before shipping.Priority 2: Resume flag wiring (Small)
Goose
File:
registry/coder/modules/goose/scripts/start.shCurrent launch (line 33-35):
agentapi server --term-width 67 --term-height 1190 -- \ bash -c "$(printf '%q ' "$GOOSE_CMD" "${GOOSE_ARGS[@]}")"Required changes:
- Add session existence check before setting args
- On resume: use
--resume --name "coder-task"instead of--instructions - On cold start: use current behavior plus
--name "coder-task"to create a named session
SESSION_NAME="coder-task" SESSION_DB="$HOME/.local/share/goose/sessions/sessions.db" if [ -f "$SESSION_DB" ] && sqlite3 "$SESSION_DB" \ "SELECT 1 FROM sessions WHERE name='$SESSION_NAME' LIMIT 1" 2>/dev/null | grep -q 1; then echo "Resuming existing Goose session: $SESSION_NAME" GOOSE_ARGS=(run --resume --name "$SESSION_NAME" --interactive) elif [ ! -z "$GOOSE_TASK_PROMPT" ]; then # ... existing prompt logic, add --name "$SESSION_NAME" ... GOOSE_ARGS=(run --interactive --name "$SESSION_NAME" --instructions "$PROMPT_FILE") else GOOSE_ARGS=(--name "$SESSION_NAME") fi
Note: Prompt re-delivery -- when resuming, skip
--instructionsto avoid re-sending the task prompt into an active conversation.Gemini
File:
registry/coder-labs/modules/gemini/scripts/start.shCurrent launch (last 2 lines):
agentapi server --term-width 67 --term-height 1190 -- \ bash -c "$(printf '%q ' gemini "${GEMINI_ARGS[@]}")"Required changes:
- Check for existing sessions before adding
--resume - Guard against dead-loop bug (google-gemini/gemini-cli#15892) when no sessions exist
GEMINI_SESSION_DIR="$HOME/.gemini/tmp" # Check if any chat session files exist if [ -d "$GEMINI_SESSION_DIR" ] && find "$GEMINI_SESSION_DIR" -path "*/chats/*.json" -print -quit 2>/dev/null | grep -q .; then echo "Resuming latest Gemini session" GEMINI_ARGS=(--resume "latest") # Skip --prompt-interactive on resume to avoid re-sending task prompt elif [ -n "$GEMINI_TASK_PROMPT" ]; then # ... existing prompt logic ... fi
Critical: The
--resume "latest"flag causes a dead loop when called with no existing sessions. The existence check is mandatory, not optional.Amazon Q
File:
registry/coder/modules/amazon-q/scripts/start.shCurrent launch (last line):
agentapi server ${ARG_SERVER_PARAMETERS} --term-width 67 --term-height 1190 -- "$Q_CMD" "${ARGS[@]}"
Required changes:
- Check for existing conversation state
- Add
--resumeflag on resume, skip prompt re-delivery
# Amazon Q stores per-directory conversation state # Check if prior conversation exists for this workdir if "$Q_CMD" chat --resume --trust-all-tools 2>&1 | head -1 | grep -q "Resuming"; then # Use --resume instead of appending prompt ARGS=(chat --resume) if [ "$ARG_TRUST_ALL_TOOLS" = "true" ]; then ARGS+=(--trust-all-tools) fi fi
Warning: Amazon Q Developer CLI is deprecated in favor of Kiro CLI. Assess longevity before investing significant effort.
Aider (status reporting only)
File:
registry/coder/modules/aider/main.tf, line 45variable "report_tasks" { # Change default from false to true default = true }
File:
registry/coder/modules/aider/scripts/start.sh,start_agentapi()functionCurrent code has a conditional
--typebug (lines 46-49):if [ -n "$initial_prompt" ]; then echo "Starting agentapi with initial prompt" agentapi server -I="$initial_prompt" --type aider --term-width=67 --term-height=1190 -- aider --model $ARG_MODEL --yes-always else agentapi server --term-width=67 --term-height=1190 -- aider --model $ARG_MODEL --yes-always fi
Fix: add
--type aiderto the else branch:agentapi server --type aider --term-width=67 --term-height=1190 -- aider --model $ARG_MODEL --yes-alwaysNo resume work: Aider lacks viable session resume.
--restore-chat-historyis lossy and crashes on large histories. Blocked on upstream.Priority 3: Resume with ID capture (Medium)
Cursor CLI
File:
registry/coder-labs/modules/cursor-cli/scripts/start.shCurrent launch (line 67):
agentapi server --type cursor --term-width 67 --term-height 1190 -- "$CURSOR_CMD" "${ARGS[@]}"
Cursor supports
--resume [chatId]andagent resume, but requires a chat ID from the prior session. The module needs:- Chat ID capture: After initial launch, capture the chat ID. Possible approaches:
- Parse
cursor-agent lsoutput for the most recent chat - Monitor
~/.cursor/chats/for new directories - Capture from process stdout (format undocumented)
- Parse
- ID persistence: Write captured chat ID to a file (e.g.,
$HOME/.cursor-cli-module/last-chat-id) - Resume logic: On restart, if the ID file exists, pass
--resume <chatId>
CHAT_ID_FILE="$HOME/$ARG_MODULE_DIR_NAME/last-chat-id" if [ -f "$CHAT_ID_FILE" ]; then CHAT_ID=$(cat "$CHAT_ID_FILE") echo "Resuming Cursor session: $CHAT_ID" # Replace prompt args with resume ARGS=(--resume "$CHAT_ID") # Re-add global flags (--model, -f) fi
Open question: Exact mechanism for capturing chat ID. Requires testing
cursor-agent lsoutput format and~/.cursor/chats/directory structure.Sourcegraph Amp
File:
registry/coder-labs/modules/sourcegraph-amp/scripts/start.shAmp stores threads entirely server-side. Resume requires a thread ID.
- Thread ID capture: After initial launch, capture from one of:
AMP_THREAD_IDenv var (if Amp sets it)--stream-jsonoutput parsingamp threads listafter first run
- ID persistence: Write thread ID to a file (e.g.,
$HOME/.amp-module/last-thread-id) - Resume logic: Use
amp threads continue <threadId>instead of piping prompt toamp
THREAD_ID_FILE="$HOME/.amp-module/last-thread-id" if [ -f "$THREAD_ID_FILE" ]; then THREAD_ID=$(cat "$THREAD_ID_FILE") echo "Continuing Amp thread: $THREAD_ID" agentapi server --type amp --term-width=67 --term-height=1190 -- \ amp threads continue "$THREAD_ID" "${ARGS[@]}" else # ... existing launch logic ... fi
Constraint: Requires network connectivity to Sourcegraph servers. Air-gapped environments cannot use thread resume.
Priority 4:
--typeflag cleanupModules launching
agentapi serverwithout--type:Module File Current Command Add Flag Goose coder/modules/goose/scripts/start.sh:33agentapi server --term-width 67 ...--type gooseAmazon Q coder/modules/amazon-q/scripts/start.sh:66agentapi server ${ARG_SERVER_PARAMETERS} --term-width 67 ...--type amazon-qGemini coder-labs/modules/gemini/scripts/start.sh:76agentapi server --term-width 67 ...--type geminiCodex coder-labs/modules/codex/scripts/start.sh:216agentapi server --term-width 67 ...--type codexAuggie coder-labs/modules/auggie/scripts/start.sh:101agentapi server --term-width 67 ...--type auggieAdditionally, Aider conditionally omits
--type aider(see Priority 2 above).These should be bundled with the resume flag changes for each module.
Cross-cutting: Prompt re-delivery on resume
Several modules pass
ai_prompt/task_prompton every startup viaagentapi -Ior as a CLI argument. When resume is added, the prompt must be conditionally skipped to avoid re-sending the task into an already-active conversation.Module How prompt is passed Skip logic needed Goose --instructions "$PROMPT_FILE"arg togoose runYes: omit --instructionswhen resumingGemini --prompt-interactive "$PROMPT"arg togeminiYes: omit --prompt-interactivewhen resumingAmazon Q Prompt appended directly to ARGSarrayYes: don't append prompt when resuming Auggie --instruction "$PROMPT"arg toauggieYes: omit --instructionwhen resumingOpenCode agentapi -I "$PROMPT"Yes: omit -Iwhen resuming with--continueModules that already handle this correctly:
- Claude Code: Prompt only sent on new sessions.
- Codex: Prompt only sent on new sessions.
- Copilot: Prompt skipped when
check_existing_session()returns true (uses--continuewithout-I).
Not actionable (blocked on upstream)
Aider session resume
Aider's
--restore-chat-historyflag reads from.aider.chat.history.mdin the working directory. This is not viable for production use:- Reconstruction is lossy (markdown log, not structured state)
- Frequently crashes on large histories
- Does not restore file context
- Summarizes entire cross-session history rather than targeting the last session
Aider also lacks MCP support (TODO at
registry/coder/modules/aider/scripts/start.sh:53). Status reporting relies on text-based output parsing via--type aider.What to monitor: MCP support in Aider-AI/aider. When added, implement
coder_report_taskMCP tool config and re-evaluate session persistence.Recommendation: Keep
enable_state_persistence = falsefor Aider. It is the only agent where this is the correct long-term setting.- Foundation: PR #736 adds
- added a commit that references this issue
on Mar 4, 2026
Scope: Research and document how each AI agent module handles state persistence to inform implementation of pause/resume functionality.
Deliverables:
Agents to research (11 total):
coder/modules/claude-code- Known:--continueflag +.claude/directory (full support baseline)coder/modules/aidercoder/modules/goosecoder/modules/amazon-qcoder-labs/modules/auggiecoder-labs/modules/codexcoder-labs/modules/copilotcoder-labs/modules/cursor-clicoder-labs/modules/geminicoder-labs/modules/opencodecoder-labs/modules/sourcegraph-ampResearch questions for each agent:
Compatibility levels (from RFC):
Acceptance criteria:
enable_state_persistencedefaults per agentBlocks:
References: