Session Model
Each session is:- Bound to a workspace: A directory root (usually a git repo) attached via
duck [path] - Backed by a Pi subprocess: One Pi process per session, running in RPC mode
- Persisted as JSONL: Conversation history is stored in
~/Library/Application Support/RubberDuck/pi-sessions/<session-id>.jsonl - Resumable: Sessions can be paused, resumed, and continued across app restarts
Workspace
A workspace is a directory root identified by its absolute path. The workspace ID is a hash of the path. When you run:- Attaches
~/projects/my-appas a workspace - Creates or resumes the default session for that workspace (named after the directory)
- Spawns a Pi subprocess with
--workspace ~/projects/my-app
Session
A session is a single conversation thread with its own:- Session ID: Unique identifier (e.g.,
duck-1a2b3c4d) - Session name: Human-readable label (defaults to workspace directory name)
- Conversation history: JSONL file with user messages, assistant responses, tool calls, and metadata
- Pi subprocess: One running Pi process per active session
Multiple Sessions Per Workspace
You can run multiple sessions in the same workspace for different tasks:- Its own Pi subprocess
- Its own conversation history
- Its own tool execution context
Sessions are independent. Tool calls in one session don’t affect another session, and conversation history is isolated.
Active Voice Session
The active voice session is the session that receives new voice utterances when you press Option+D. Rubber Duck automatically sets the active session when you runduck [path]:
- If the workspace has one session, that session becomes active
- If the workspace has multiple sessions, the most recently used session becomes active
- If you attach a new workspace, its default session becomes active
- The menu bar popover (Rubber Duck.app)
- The terminal output when you run
duck sessions
Only one session can be the active voice session at a time. Background sessions can run concurrently, but they won’t receive voice input.
Switching Sessions
You can switch the active voice session in two ways:1. Via CLI
Runduck [path] in the workspace you want to switch to:
attach request, it:
- Sets the workspace’s default session as the active voice session
- Notifies Rubber Duck.app via
voice_session_changedpush event - The app updates the menu bar popover and internal state
2. Via Menu Bar Popover
Click the Rubber Duck menu bar icon and select a session from the “Switch Session” list. The popover shows:- Active workspace path
- Active session name
- All sessions for that workspace
Session Persistence
Conversation history is automatically saved to disk as the session runs. Each session’s history is stored as a JSONL file:- User messages (voice transcripts and text input)
- Assistant responses (text and audio transcripts)
- Tool calls and results
- Metadata (timestamps, session ID, event types)
ConversationHistory.swift and VoiceSessionCoordinator.swift:639-658.
Resuming Sessions
When you attach a workspace, Rubber Duck checks if a session already exists:- If yes, the session is resumed from its persisted state
- If no, a new session is created
Sessions persist across app restarts, daemon restarts, and reboots. You never lose conversation context.
Background Sessions
Sessions can run in the background while you work in another session. For example:-
Start a long-running test suite in session A:
-
Switch to session B and continue development:
Background Session Output
By default, background sessions:- Stream output to the terminal where you’re following them
- Do not speak responses (to avoid interrupting your active session)
- Show a notification when the agent finishes (macOS notification center)
Viewing All Sessions
Runduck sessions to see all sessions for the current workspace:
- SESSION: Session name (with ✔ for active voice session)
- WORKSPACE: Workspace path (shortened with
~) - STATUS:
running(Pi subprocess alive) orstopped(no Pi subprocess) - LAST ACTIVE: Relative timestamp of the last activity
--all to see sessions across all workspaces:
Session Lifecycle
1. Creation
Sessions are created automatically when you attach a workspace:- Generates a session ID (e.g.,
duck-1a2b3c4d) - Assigns a default name (workspace directory name)
- Creates a JSONL history file (
pi-sessions/<session-id>.jsonl) - Spawns a Pi subprocess with
--mode rpc --workspace <path> - Registers the session in
metadata.json
2. Active Use
While the session is active:- User input (voice or text) is sent to the Pi subprocess via
promptRPC method - The Pi subprocess streams events (message deltas, tool calls, tool output) back to the daemon
- The daemon broadcasts events to all subscribed CLI clients
- Conversation history is appended to the JSONL file in real time
3. Background
When you switch to another session, the previous session:- Continues running if there’s an active agent turn
- Stops if the agent has finished and there’s no pending work
- Remains resumable (history is persisted)
duck [path] in that workspace again.
4. Cleanup
The daemon periodically checks session health (every 30 seconds):- If a Pi subprocess crashes, it’s removed from the active session list
- If a session has been idle for 24 hours, it’s marked as stopped (but history is preserved)
Session Metadata
All session metadata is stored in:- Workspaces: Map of workspace ID → path
- Sessions: Map of session ID → workspace ID, name, history file path, last active timestamp
- Active session: The session ID that receives voice input
cli/src/daemon/metadata-store.ts.
Concurrent Sessions Across Workspaces
You can run sessions in multiple workspaces at the same time:- Tool calls in project A don’t affect project B
- Each session has its own
cwdand workspace root - File operations are confined to the workspace (see Configuration)
Rubber Duck.app shows the active workspace path in the menu bar popover. This is the workspace whose session will receive voice input when you press Option+D.
Session Names
By default, sessions are named after the workspace directory:metadata.json (or via a future duck rename command).
Advanced: Shared vs. Isolated Session Storage
By default, Rubber Duck stores Pi sessions in:~/.pi/sessions/) to avoid interfering with standalone Pi usage.
You can enable “Use global Pi sessions” in Settings (advanced) to share session storage with Pi. This allows you to:
- Resume Rubber Duck sessions in standalone Pi (and vice versa)
- Use Pi’s session branching and forking features
--session-dir flag.
Related
- CLI Commands - Attaching workspaces and managing sessions via CLI
- Voice Interface - Using voice with multiple sessions
- Architecture - How the daemon, Pi, and app coordinate sessions