# How the ai-memory Cross-Agent Handoff Mechanism Transfers Context Between Sessions

> Learn how ai-memory's cross-agent handoff mechanism transfers context between sessions. It persists records to SQLite and re-injects them into new sessions for seamless workflow continuity.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: deep-dive
- Published: 2026-08-19

---

**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`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/migrations/V02__handoffs.sql) and [`crates/ai-memory-store/migrations/V39__handoff_ownership.sql`](https://github.com/akitaonrails/ai-memory/blob/main/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`](https://github.com/akitaonrails/ai-memory/blob/main/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`](https://github.com/akitaonrails/ai-memory/blob/main/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`](https://github.com/akitaonrails/ai-memory/blob/main/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:

```json
{
  "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`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-web/src/routes/api.rs). Clients can retrieve open handoffs via:

```text
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`](https://github.com/akitaonrails/ai-memory/blob/main/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:

```rust
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:

```rust
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`](https://github.com/akitaonrails/ai-memory/blob/main/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 in [`crates/ai-memory-wiki/src/admission.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/admission.rs).
- Context is re-injected via a `hook_result` message parsed by [`crates/ai-memory-workstream/src/transcript.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/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.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-web/src/routes/api.rs) exposes 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`](https://github.com/akitaonrails/ai-memory/blob/main/V02__handoffs.sql) and [`V39__handoff_ownership.sql`](https://github.com/akitaonrails/ai-memory/blob/main/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`](https://github.com/akitaonrails/ai-memory/blob/main/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`](https://github.com/akitaonrails/ai-memory/blob/main/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`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/admission.rs).