Session Dispatch & Context Restore
Session continuity and task delegation. Agents can dispatch tasks to the Claude CLI as fire-and-forget background jobs, restore context from past sessions, or truly resume an existing session thread.
Architecture
dispatch_task()
|
┌──────────┴──────────┐
│ │
session_id? resume_session_id?
│ │
v v
restore_session_context() --resume <id> flag
(inject context into prompt) (continue thread)
│ │
└──────────┬──────────┘
│
v
asyncio.create_task()
(fire and forget)
│
v
Returns job_id immediately
│
┌──────────┴──────────┐
│ │
job running get_dispatch_job(job_id)
in background → status / output
MCP Tools
dispatch_task
Dispatches a task to the Claude CLI as a background job. Returns immediately with a job_id — does not wait for completion.
Args:
task_description (str) — what to do
project (str, optional) — project path hint
session_id (str, optional) — inject context from past session
resume_session_id (str, optional) — resume an existing session thread
command (str, default "default") — model preset
context_turns (int, default 10) — turns to include from context
Returns immediately:
{
"success": true,
"dispatched": true,
"background": true,
"job_id": "71dad581-e7d2-40c2-81a5-bed77bcb813d",
"status": "running",
"context_session": null,
"resumed_session": null,
"prompt_length": 162
}
get_dispatch_job
Poll a background job for status and output.
Args:
job_id (str) — UUID returned by dispatch_task
While running:
{
"job_id": "71dad581...",
"status": "running",
"started_at": "2026-02-21T11:04:31Z",
"output": null
}
When done:
{
"job_id": "71dad581...",
"status": "completed",
"started_at": "2026-02-21T11:04:31Z",
"completed_at": "2026-02-21T11:05:05Z",
"output": "Created tests/test_hello.py with the requested test.",
"error": null,
"exit_code": 0,
"duration_ms": 17412,
"timed_out": false,
"claude_session_id": "a0f4dd54-5a16-4463-b7b2-24ca41901e12"
}
status values: running · completed · failed
list_dispatch_jobs
List dispatch jobs with optional status filter. Returns job summaries (newest first) without the full output field.
Args:
status (str, optional) — filter by status ("running", "completed", "failed")
limit (int, default 20) — max jobs to return
Response:
{
"success": true,
"count": 3,
"jobs": [
{
"job_id": "71dad581...",
"status": "completed",
"started_at": "2026-02-27T10:00:00Z",
"completed_at": "2026-02-27T10:01:00Z",
"task": "Fix the failing tests"
},
{
"job_id": "a3bc9e12...",
"status": "running",
"started_at": "2026-02-27T10:05:00Z"
}
]
}
restore_session
Load context from a past session for injection into a new conversation.
Args: session_id (str), last_n (int, default 20)
Returns: JSON with formatted context string + char_count
Two Ways to Use a Past Session
1. Context Injection (session_id)
Extracts recent turns from the past session and injects them as text into the new prompt. Claude starts a fresh conversation but is aware of the previous work.
dispatch_task(
task_description="Deploy the auth changes to staging",
session_id="abc-123", # context injected as text
context_turns=15,
)
Use when: continuing work thematically, summarising, or handing off across projects.
2. True Session Resume (resume_session_id)
Passes --resume <id> to the Claude CLI. The existing session thread is literally continued — same conversation, same memory, same context window.
dispatch_task(
task_description="Continue where we left off — fix the failing tests",
resume_session_id="abc-123", # --resume flag passed to CLI
)
Use when: mid-task interruption, picking up an in-progress session, or appending turns to an existing thread.
Natural Language Status Checks
The job_id stays in the conversation context, so you can just ask:
“How is that job going?” “Did the antoncore task finish?” “What did it output?”
The agent will call get_dispatch_job on the relevant job_id without needing it repeated. From a new conversation, use list_dispatch_jobs to browse recent jobs — no need to remember job IDs.
“What jobs have I dispatched recently?” “Show me the failed jobs” “Any running jobs?”
Permissions
All dispatched Claude CLI invocations run with --dangerously-skip-permissions. No approval prompts will block background jobs.
Job Storage
Job state is persisted with a Redis-primary, file-fallback pattern:
- Redis (primary): stored as a hash at
agentibridge:sb:job:{job_id}with a sorted set index atagentibridge:sb:idx:jobs(scored by start time). TTL is 24 hours. - File (fallback): always written to
/tmp/agentibridge_jobs/<job_id>.json, even when Redis is available.
Reads check Redis first; if unavailable, fall back to file. list_jobs() reads from the Redis sorted set (newest first) or scans the file directory by mtime. Jobs survive MCP server restarts. Background tasks are held in memory via asyncio.create_task() with GC protection while running.
Command Presets
command | Model |
|---|---|
default | Sonnet |
thinkhard | Sonnet |
ultrathink | Opus |
Dispatch Bridge (Docker Mode)
When AgentiBridge runs inside Docker, the claude CLI binary isn’t available in the container. A lightweight HTTP bridge runs on the host and proxies dispatch requests to the local CLI.
┌─────────────────────┐ HTTP ┌──────────────────────┐
│ Docker container │◄──────────────────►│ Host bridge │
│ AgentiBridge MCP │ POST /dispatch │ dispatch.py │
│ │ → 202 + job_id │ │
│ claude_runner.py │ │ Spawns claude CLI │
│ _run_claude_http() │ GET /job/{id} │ Returns result │
│ submit + poll │ → job state │ │
└─────────────────────┘ └──────────────────────┘
Submit + Poll Pattern
The bridge is fire-and-forget: POST /dispatch validates the request, spawns a background task, and returns HTTP 202 with a job_id immediately. The client polls GET /job/{id} with exponential backoff (2s → 10s cap) until the job finishes or the deadline is exceeded.
This replaces the old blocking pattern where the HTTP connection was held open for the entire duration of a Claude CLI run (up to 600s).
Bridge Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
GET | /health | No | Health check |
POST | /dispatch | X-Dispatch-Secret | Submit a dispatch request, returns 202 |
GET | /job/{id} | No | Get job status and result |
GET | /jobs | No | List all bridge-level job summaries |
Recursion Prevention
The bridge clears CLAUDE_DISPATCH_URL from its environment before calling run_claude(), preventing the CLI runner from routing back to the bridge (which would cause infinite recursion).
Components
| File | Role |
|---|---|
agentibridge/dispatch.py | Job management (Redis + file), context restore, background task runner |
agentibridge/claude_runner.py | Claude CLI subprocess / HTTP bridge proxy (submit + poll) |
agentibridge/dispatch.py | Host-side HTTP bridge (Docker mode), fire-and-forget |
Configuration
CLAUDE_BINARY=claude
CLAUDE_DISPATCH_MODEL=sonnet
CLAUDE_DISPATCH_TIMEOUT=300
# Docker mode — bridge on host proxies CLI calls
# CLAUDE_DISPATCH_URL removed (native dispatch)
DISPATCH_SECRET=<shared-secret>
DISPATCH_BRIDGE_HOST=0.0.0.0
DISPATCH_BRIDGE_PORT=8101