How the ai-memory Cross-Agent Handoff Mechanism Transfers Context Between Sessions
The cross-agent handoff mechanism persists a portable handoff record to SQLite and re-injects it into new sessions as a structured hook_result message, enabling seamless workflow continuity across agents.
The akitaonrails/ai-memory repository implements a cross-agent handoff mechanism that allows workflows to continue when switching between LLM agents or resuming halted sessions. This feature captures the state of an ongoing interaction in a durable record and then restores that context into the next session. Understanding this design is essential for building reliable multi-agent pipelines with ai-memory.
How the Cross-Agent Handoff Mechanism Stores State in SQLite
When an agent decides to transfer a conversation, it creates a handoff record inside the SQLite store. This record lives in a dedicated table outside the wiki tree and is defined by migrations such as crates/ai-memory-store/migrations/V02__handoffs.sql and crates/ai-memory-store/migrations/V39__handoff_ownership.sql.
The record stores:
summary— a concise description of the current task.open_questions— unresolved questions for the next agent.next_steps— suggested actions for the successor.owner— an optional user identifier for access control.project/workspace— scoping identifiers.
Insertion happens through the store API using store.writer.insert_handoff. This path is exercised in the end-to-end test suite, specifically in tests/e2e/handoff_smoke.sh.
Handoff Lifecycle Operations
The handoff follows a three-step lifecycle managed by the MCP tool layer. The admission system in crates/ai-memory-wiki/src/admission.rs maps these operations to string keys:
handful_begin— creates the handoff record.handful_accept— marks the handoff as consumed by the next agent.handful_cancel— removes or aborts the handoff.
This explicit state machine ensures that handoffs do not leak between sessions and that each transition is auditable.
Injecting the Handoff Delta into New Sessions
When the next agent starts, the framework injects the handoff delta as a special hook_result message. The workstream transcript parser in crates/ai-memory-workstream/src/transcript.rs (lines 73–78) recognizes this payload and excludes it from normal user-message imports while still writing it to the underlying ledger.
The injected message structure looks like this:
{
"type": "context.append_message",
"message": {
"role": "assistant",
"origin": { "kind": "hook_result", "event": "UserPromptSubmit" },
"content": [{ "type": "text", "text": "injected handoff delta" }]
}
}
Because the parser discards non-user origin messages during transcript import, the handoff does not appear as duplicate user input. However, the ledger still receives the delta, making the stored context available to the successor agent.
Exposing Handoffs Through the Web API
The web layer surfaces handoff data through crates/ai-memory-web/src/routes/api.rs. Clients can retrieve open handoffs via:
GET /workspaces/{workspace}/projects/{project}/handoffs
This endpoint returns a list containing summaries, open questions, and next steps. The response format and authentication logic are verified in crates/ai-memory-web/tests/routes.rs (lines 1461–1614), which asserts fields like handoff["summary"] and enforces ownership rules.
Enforcing Ownership and Access Control
Visibility is determined by the optional owner field. According to the web-layer tests (lines 1970–1994), the rules are strict:
- Owned handoffs are visible only to the owning user or root.
- Unowned handoffs are visible to all callers.
The test suite confirms that a named caller receives full details for their own handoffs while other users see only the summary.
Practical Example: Creating and Consuming a Handoff
The following Rust snippets demonstrate the core operations. First, an agent inserts a handoff record through the store:
let new_handoff = NewHandoff {
workspace: "default".into(),
project: "scratch".into(),
summary: "Refactor data pipeline".into(),
open_questions: vec!["What is the target latency?".into()],
next_steps: vec!["Add benchmark tests".into()],
owner: Some(user_key("alice")),
};
store.writer.insert_handoff(new_handoff).await?;
A successor agent or client then queries the HTTP API to discover open handoffs:
let resp = reqwest::Client::new()
.get("http://localhost:49374/workspaces/default/projects/scratch/handoffs")
.send()
.await?
.json::<serde_json::Value>()
.await?;
println!("Open handoffs: {:#}", resp["handoffs"]);
Finally, the ai-memory framework automatically injects the retrieved handoff into the new session through the transcript parser. No manual intervention is required because crates/ai-memory-workstream/src/transcript.rs handles the hook_result detection and ledger update internally.
Summary
- The cross-agent handoff mechanism stores a durable snapshot of conversation state in a dedicated SQLite table.
- Lifecycle transitions (
handful_begin,handful_accept,handful_cancel) are mapped incrates/ai-memory-wiki/src/admission.rs. - Context is re-injected via a
hook_resultmessage parsed bycrates/ai-memory-workstream/src/transcript.rs, which prevents duplicate imports while updating the ledger. - The web API in
crates/ai-memory-web/src/routes/api.rsexposes handoffs with fine-grained ownership controls verified in the test suite.
Frequently Asked Questions
How is a handoff record created in ai-memory?
An agent creates a record by calling store.writer.insert_handoff with a NewHandoff struct containing the summary, open questions, next steps, and optional owner. The underlying schema is defined in migrations such as V02__handoffs.sql and V39__handoff_ownership.sql.
What prevents handoff context from appearing as a duplicate user message?
The workstream transcript parser in crates/ai-memory-workstream/src/transcript.rs checks the origin.kind field and excludes hook_result messages from normal user-message imports. The ledger still receives the delta, so the context remains available without being double-counted.
Who can view an open handoff?
Visibility depends on ownership. Owned handoffs are restricted to the owning user or root, while unowned handoffs are visible to all callers. These rules are enforced by the web API and validated in crates/ai-memory-web/tests/routes.rs.
What are the three lifecycle states of a cross-agent handoff?
The MCP tool layer defines handful_begin to create the record, handful_accept to mark it consumed, and handful_cancel to abort or remove it. These operations are enumerated in crates/ai-memory-wiki/src/admission.rs.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →