19 KiB
Agent Session Resume Evidence for Sleep/Wake
Date: June 5, 2026
Scope: issue stablyai/orca#1796, current worktree code, installed agent CLIs on this machine, cloned open-source CLI repos under /tmp/orca-agent-resume-research, and official provider docs where available.
Executive conclusion
Orca can implement one-click agent resume on wake, but it cannot use terminal PTY IDs, pane keys, tab IDs, or worktree IDs as the agent session ID. Those are Orca terminal identifiers. The required value is the provider's conversation/session/thread ID.
For several first-tier providers, the exact provider ID is available in hook payloads today, but Orca drops it during normalization:
- Claude: hook payloads include
session_id; resume command isclaude --resume <session_id>. - Codex: hook runtime sends
session_id; resume command iscodex resume <session_id>. - Gemini: hook payloads include
session_id; resume command isgemini --resume <session_id>. - Antigravity: hook payloads include
conversationId; resume command isagy --conversation <conversationId>. - OpenCode: Orca's managed plugin already reads OpenCode
sessionID; resume command isopencode --session <sessionID>. - Droid: hook payloads include
session_id; resume command isdroid --resume <session_id>. - Grok: hook payloads include
sessionId/session_id; resume command isgrok --resume <sessionId>.
The current blocker is not primarily "how do we resume?" The exact resume action is to spawn a new agent process on wake in the same workspace/pane context using the provider-specific resume command. The blocker is that Orca's current hook event and status types have no durable provider-session field, so the ID is discarded before sleep.
Current Orca behavior
Issue #1796 is accurate about current behavior. The issue says sleep loses agent type, session ID, last prompt, and resume command because dropAgentStatusByWorktree wipes rows before wake can offer resume.
Verified current code path:
- Sleep calls terminal shutdown with identifiers preserved, not provider conversation metadata. See
src/renderer/src/components/sidebar/sleep-worktree-flow.ts. shutdownWorktreeTerminals(worktreeId, { keepIdentifiers: true })preserves terminal IDs/layout wake hints, then callsdropAgentStatusByWorktree(worktreeId). Seesrc/renderer/src/store/slices/terminals.ts.agentStatusByPaneKeyis explicitly documented as "Real-time only - lives in renderer memory, not persisted to disk." Seesrc/renderer/src/store/slices/agent-status.ts.WorkspaceSessionStatepersists terminal/browser/editor/SSH state, including terminal layouts and remote relay PTY IDs, but no sleeping-agent records. Seesrc/shared/types.ts,src/renderer/src/lib/workspace-session.ts, andsrc/shared/workspace-session-schema.ts.AgentStatusEntryhasstate,prompt, timestamps,agentType, pane/tab/worktree attribution, tool previews, assistant preview, and orchestration context. It has no provider session ID or resume command field. Seesrc/shared/agent-status-types.ts.
The existing sleep persistence is terminal-resume metadata. The feature needs provider-conversation-resume metadata.
Hook pipeline gap
The current local and SSH hook wire shapes drop provider IDs:
AgentHookEventPayloadinsrc/shared/agent-hook-listener.tsincludes pane/tab/worktree, prompt cache fields, hook event name, Claude tool/subagent IDs, and normalizedpayload. It has no provider session ID field.AgentHookRelayEnvelopeinsrc/shared/agent-hook-relay.tsmirrors that normalized shape for SSH. Its comment states the relay normalizes before sending the envelope. Therefore remote/SSH events lose provider IDs at the relay boundary too unless the new field is added to the shared envelope.ParsedAgentStatusPayloadinsrc/shared/agent-status-types.tsincludes only status fields:state,prompt,agentType,toolName,toolInput,lastAssistantMessage,interrupted.
Implementation implication: provider-session metadata must be extracted before normalization completes and must be carried through both the local HTTP path and the SSH relay path.
Installed CLI versions checked
These exact binaries were available on this machine:
| Agent | Version checked |
|---|---|
| Claude Code | 2.1.165 |
| Codex | codex-cli 0.137.0 |
| Gemini | 0.44.0 |
| OpenCode | 1.15.13 |
| Cursor Agent | 2025.09.18-7ae6800 |
| Antigravity | 1.0.5 |
| Droid | 0.122.0 |
| Grok | grok 0.2.22 (967574cb117) [stable] |
| Pi | 0.72.1 |
| Amp, Copilot, Command Code, Hermes | Not installed locally |
Provider matrix
Legend:
Exact supported: exact resume command and exact ID source are both verified.Resume verified, ID capture missing: CLI can resume, but current Orca hook path does not expose a durable exact ID.ID seen, resume not verified: Orca/source exposes an ID, but no exact resume CLI command was verified.Out of current hook scope: Orca has no current hook source for this TUI agent.
| Agent | Resume command evidence | Exact ID source evidence | Current Orca support status |
|---|---|---|---|
| Claude | claude --resume <session_id> verified by local claude --help and Anthropic CLI docs. |
Claude hook docs show hook input includes session_id and transcript_path; current Orca normalizer drops session_id. |
Exact supported after adding provider-session metadata extraction. |
| Codex | codex resume <session_id> verified by local help and cloned Codex source codex-rs/utils/cli/src/resume_command.rs. |
Cloned Codex source codex-rs/core/src/hook_runtime.rs sends session_id on SessionStart, PreToolUse, PermissionRequest, and PostToolUse hook requests. |
Exact supported after extraction. |
| Gemini | gemini --resume <session_id> verified by local help and Gemini docs. |
Gemini hooks reference says all hooks receive session_id and transcript_path. |
Exact supported after extraction. |
| Antigravity | agy --conversation <uuid> verified by local help and Antigravity docs. |
Antigravity hook docs list conversationId as the active conversation UUID. |
Exact supported after extraction. |
| OpenCode | opencode --session <sessionID> verified by local help, official docs, and cloned OpenCode TUI source. |
Orca's OpenCode plugin already reads event.properties?.sessionID before posting. |
Exact supported after preserving sessionID. |
| Droid | droid --resume <sessionId> verified by local help and Factory CLI docs. |
Factory hooks reference includes session_id; Orca installs Droid hooks and already subscribes to SessionStart. |
Exact supported after extraction. |
| Grok | grok --resume <SESSION_ID> verified by local help. |
Orca already reads sessionId/session_id in getGrokChatHistoryPath, but only to locate chat history. |
Exact supported after preserving that same ID. |
| Cursor | cursor-agent --resume <chatId> and cursor-agent resume verified by local help and Cursor docs. |
Current Orca Cursor hook install deliberately omits sessionStart/sessionEnd; the subscribed event set does not currently prove a chat ID source. cursor-agent create-chat was locally verified to return a UUID, but preallocation for Orca launches still needs an end-to-end test before relying on it. |
Resume verified, ID capture missing. |
| Pi | `pi --session <path | id>, pi --resume, and pi --continue` verified by local help and Pi docs. |
Orca's bundled Pi/OMP extension posts status events but does not include session file path, session UUID, or session name in the status payload. |
| OMP | Same launch family as Pi in Orca. | Orca reuses Pi extension API shape and does not include exact session pointer. | Resume verified by inheritance only from Pi-like CLI contract; exact OMP ID capture missing. |
| Amp | Orca's managed plugin posts threadId: event.thread.id. |
Amp is not installed locally, and no exact Amp CLI resume command was verified in this investigation. | ID seen, resume not verified. |
| Hermes | Orca's managed Hermes plugin selects session_id for session/LLM/tool events. |
Hermes is not installed locally, and no exact Hermes CLI resume command was verified in this investigation. | ID seen, resume not verified. |
| Copilot | Orca installs broad Copilot hooks. | Local Copilot CLI not installed; no exact resume command or session ID contract was verified. | Not supported for exact resume yet. |
| Command Code | Orca installs hooks for PreToolUse, PostToolUse, and Stop. |
Local Command Code CLI not installed; no exact resume command or session ID contract was verified. | Not supported for exact resume yet. |
| openclaude, autohand, aider, goose, kilo, kiro, crush, aug, cline, codebuff, continue, kimi, mistral-vibe, qwen-code, rovo, openclaw | Not investigated to exact-resume standard. | Not in HOOK_SOURCE_BY_PATHNAME today. |
Out of current hook scope. |
Exact resume behavior by provider
Claude
Verified:
- Local
claude --helpshows--continue,--resume [value],--session-id <uuid>,--fork-session, and--no-session-persistence. - Anthropic CLI docs show
claude -r "<session-id>" "query"and document--resumeas resuming a specific session by ID. - Claude hooks docs show hook input includes
session_idandtranscript_path.
Resume command:
claude --resume <session_id>
If Orca stores lastPromptSummary separately, it should not pass that summary as a new user prompt by default. The one-click resume action should reopen the prior conversation. Auto-submitting a summary would create a new turn.
Codex
Verified:
- Local
codex resume --helpaccepts[SESSION_ID] [PROMPT], where the session target is a UUID or session name. - Cloned Codex source
codex-rs/utils/cli/src/resume_command.rsformatscodex resume <thread_id>. - Cloned Codex source
codex-rs/core/src/hook_runtime.rssendssession_idon hook requests. - Cloned Codex source has tests asserting resumed root sessions use the thread ID as session ID.
Resume command:
codex resume <session_id>
Avoid codex resume --last for this feature when an exact ID is available. --last can resume the wrong conversation if another Codex session ran after the slept workspace.
Gemini
Verified:
- Local
gemini --helpshows--resume,--session-id,--session-file, and--list-sessions. - Gemini official hooks reference states all hook input includes
session_idandtranscript_path. - Gemini official configuration docs describe
--resume [session_id]and state that a UUID can be supplied.
Resume command:
gemini --resume <session_id>
Antigravity
Verified:
- Local
agy --helpshows--continueand--conversation. - Antigravity conversation docs show
agy --continuefor most recent andagy --conversation <uuid>for a specific session. - Antigravity hooks docs define common hook input
conversationId.
Resume command:
agy --conversation <conversationId>
OpenCode
Verified:
- Local
opencode --helpshows--continueand--session. - Official OpenCode docs list
--sessionas the session ID to continue. - Cloned OpenCode source
packages/opencode/src/cli/cmd/tui/thread.tsdefines--sessionand passes it into TUI args assessionID. - Cloned OpenCode source
validate-session.tsvalidates the supplied session ID through the OpenCode client. - Orca's managed OpenCode plugin reads
event.properties?.sessionID.
Resume command:
opencode --session <sessionID>
Droid
Verified:
- Local
droid --helpshows--resume [sessionId],--fork <sessionId>, and--cwd. - Factory CLI docs list resume behavior and
droid exec -s <id>for exec mode. - Factory hooks reference says
SessionStarthas aresumematcher and hook input includessession_id.
Resume command:
droid --resume <session_id>
Grok
Verified:
- Local
grok --helpshows--resume [<SESSION_ID>],--continue,--cwd, and--restore-code. - Orca already reads
sessionId/session_idingetGrokChatHistoryPath.
Resume command:
grok --resume <sessionId>
Cursor
Verified:
- Local
cursor-agent --helpshows--resume [chatId]. - Cursor docs show listing prior chats with
cursor-agent ls, resuming latest withcursor-agent resume, and resuming a specific conversation withcursor-agent --resume="chat-id-here". - Local help shows
cursor-agent create-chatcreates an empty chat and returns its ID. - Running
cursor-agent create-chatlocally returned UUID1fff2e62-9dd0-4cf3-9d8c-3569fea0aff7, proving the command can preallocate a chat ID without an initial prompt.
Not verified:
- Current Orca Cursor hook install subscribes to
beforeSubmitPrompt,stop, tool events, approval events, andafterAgentResponse, but intentionally does not subscribe tosessionStart/sessionEnd. - I did not verify a current subscribed Cursor hook payload containing
chatId. - I did not run
cursor-agent --resume <chatId> <prompt>because that would start a real agent turn and may make workspace changes.
Conclusion: exact Cursor resume is CLI-supported, but Orca currently needs either a verified hook payload chat ID or a launch preallocation strategy that is tested end to end.
Pi and OMP
Verified:
- Local
pi --helpshows--continue,--resume,--session <path|id>,--fork <path|id>,--session-dir, and--no-session. - Pi docs state
/resumeopens a picker andpi -ropens the same picker at startup. - Orca's Pi/OMP extension posts status events from Pi's extension API, but the status events do not include session path, UUID, or session name.
Resume command:
pi --session <path-or-id>
Conclusion: exact Pi resume is CLI-supported, but current Orca capture is missing the exact pointer.
What Orca should store
Store a durable record per sleeping agent pane, not per worktree only:
type SleepingAgentSessionRecord = {
id: string
worktreeId: string
tabId?: string
paneKey: string
connectionId: string | null
agentType: TuiAgent
providerSession: {
kind: 'session_id' | 'conversation_id' | 'thread_id' | 'chat_id' | 'path'
value: string
}
resumeCommand: {
argv: string[]
cwd?: string
}
lastPrompt: string
lastAssistantMessage?: string
capturedAt: number
retentionUntil?: number | null
}
Important constraints:
resumeCommand.argvshould be structured argv, not a shell string. This avoids cross-platform quoting bugs.- Do not persist a terminal PTY ID as
providerSession.value. - For SSH,
cwdand the command execute on the remote host. The provider session ID also belongs to the remote CLI's session store. - For local workspaces, execute locally in the worktree path.
- For auto-resume, launch exactly one process per sleeping agent record and guard against double-click/duplicate wake races.
Provider extraction map
This is the extraction map that is supported by the evidence above:
| Hook source | Provider session field(s) to extract | Resume argv |
|---|---|---|
claude |
session_id |
['claude', '--resume', id] |
codex |
session_id |
['codex', 'resume', id] |
gemini |
session_id |
['gemini', '--resume', id] |
antigravity |
conversationId |
['agy', '--conversation', id] |
opencode |
sessionID |
['opencode', '--session', id] |
droid |
session_id |
['droid', '--resume', id] |
grok |
sessionId, fallback session_id |
['grok', '--resume', id] |
amp |
threadId |
not enabled until Amp resume command is verified |
hermes |
session_id |
not enabled until Hermes resume command is verified |
Required implementation shape
- Add a provider-session metadata type to shared hook types.
- Extract provider session evidence in
parseAgentHookEventfrom rawhookPayloadRecordbefore normalizing toParsedAgentStatusPayload. - Carry it in
AgentHookEventPayload. - Carry it in
AgentHookRelayEnvelopeso SSH does not lose it. - Store latest provider-session evidence by pane key alongside live agent status.
- On sleep, snapshot the live agent status plus latest provider-session evidence before
dropAgentStatusByWorktree. - Persist sleeping-agent records in
WorkspaceSessionStateand schema. - On wake, render sleeping resumable rows separately from active rows and launch the provider-specific structured argv when the user resumes.
- Clear records on explicit worktree removal, not on sleep.
- If the provider session no longer exists, let the provider CLI fail and surface that failure without deleting the record until the user dismisses it.
Open questions that remain genuinely unverified
- Cursor: whether any currently subscribed Cursor hook payload carries
chatId. If not, the only plausible exact strategy is preallocating a chat withcursor-agent create-chatat launch and starting/resuming that known chat. That needs an end-to-end test before productizing. - Pi/OMP: whether the extension API can expose the current session file/path/UUID. Current Orca extension does not forward it.
- Amp: whether the Amp CLI has an exact thread resume command matching
threadId. - Hermes: whether Hermes has an exact session resume CLI command matching
session_id. - Copilot and Command Code: exact resume command and exact hook session field were not verified because the CLIs were not installed locally and no sufficient primary docs/source were found during this pass.
- Non-hook TUI agents: no exact support should be promised until Orca adds a hook/metadata capture path for each.
Sources
Local Orca source:
src/shared/agent-hook-listener.tssrc/shared/agent-hook-relay.tssrc/shared/agent-status-types.tssrc/shared/types.tssrc/shared/tui-agent-config.tssrc/renderer/src/store/slices/terminals.tssrc/renderer/src/store/slices/agent-status.tssrc/renderer/src/lib/workspace-session.tssrc/shared/workspace-session-schema.tssrc/main/opencode/hook-service.tssrc/main/amp/hook-service.tssrc/main/hermes/hook-service.tssrc/main/pi/agent-status-extension-source.tssrc/main/cursor/hook-service.ts
Cloned source:
/tmp/orca-agent-resume-research/codex/tmp/orca-agent-resume-research/gemini-cli/tmp/orca-agent-resume-research/opencode
Official docs:
- GitHub issue: https://github.com/stablyai/orca/issues/1796
- Claude CLI reference: https://docs.anthropic.com/en/docs/claude-code/cli-usage
- Claude hooks reference: https://code.claude.com/docs/en/hooks
- Gemini hooks reference: https://geminicli.com/docs/hooks/reference/
- Cursor CLI parameters: https://docs.cursor.com/en/cli/reference/parameters
- Cursor CLI usage: https://docs.cursor.com/en/cli/using
- OpenCode CLI docs: https://opencode.ai/docs/cli/
- Antigravity conversation docs: https://antigravity.google/docs/cli-conversations
- Antigravity hooks docs: https://www.antigravity.google/docs/hooks
- Factory Droid hooks reference: https://docs.factory.ai/cli/configuration/hooks-reference
- Factory Droid CLI reference: https://docs.factory.ai/cli/configuration/cli-reference
- Pi docs: https://pi.dev/docs/latest/tree