Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
122 lines
6.5 KiB
Markdown
122 lines
6.5 KiB
Markdown
# Agent Side Conversations
|
|
|
|
Side is a runtime-only branch of an Agent session. It is deliberately not
|
|
Session Fork:
|
|
|
|
| Property | Session Fork | Side |
|
|
| -------- | ------------------------------------------- | ------------------------------------------------ |
|
|
| Identity | canonical child Session | runtime-scoped side id |
|
|
| Storage | durable history and lineage | no canonical writes |
|
|
| Boundary | full session or settled Turn | provider snapshot, including an active live Turn |
|
|
| Recovery | saga recovery and provider resume | expires when its provider process is lost |
|
|
| Events | optimistic stream plus committed projection | transient stream only |
|
|
|
|
## Standard provider contract
|
|
|
|
A provider integrates Side by implementing `SideConversationAdapter`:
|
|
|
|
1. `SideCapabilities` attests support for the selected runtime and may use
|
|
persisted provider metadata when the source connection is idle.
|
|
2. `OpenSide` creates the provider-native ephemeral branch and returns its
|
|
provider session id.
|
|
3. `OpenSide` quiesces callbacks and releases any child it created before
|
|
returning an error.
|
|
4. The ordinary `Adapter` methods provide Exec, Cancel, and Close after open;
|
|
interactive Side flows additionally use `InteractiveAdapter`.
|
|
|
|
The Controller owns scope isolation, open reservation/buffering, idempotency,
|
|
transient event routing, lifecycle reuse, cleanup, and the prohibition on
|
|
durable reporting or automatic resume. Agent Host owns the product lifecycle
|
|
entry points and keeps Side separate from canonical submit/cancel sagas.
|
|
Open holds the source and Side lifecycle identities in stable order until the
|
|
provider snapshot is committed or rolled back, so source close cannot overtake
|
|
the live fork.
|
|
In particular, Side never consumes or repairs the durable
|
|
canonical-Turn-to-provider-Turn binding used by Session Fork. Its
|
|
`clientSubmitId` is transient provider/event correlation only and must not be
|
|
paired with a canonical submit occurrence.
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
GUI[AgentGUI Side pane] --> Store[Transient Side controller/store]
|
|
Store --> AdapterAPI[Desktop Side transport adapter]
|
|
AdapterAPI --> API[tuttid typed Side API]
|
|
API --> Host[Agent Host Side lifecycle]
|
|
Host --> Controller[Runtime Controller]
|
|
Controller --> Scope[Side scope registry and open buffer]
|
|
Controller --> Adapter[SideConversationAdapter]
|
|
Adapter --> Provider[Provider native ephemeral fork]
|
|
Provider --> Adapter
|
|
Adapter --> Scope
|
|
Scope --> Transient[agent.side.updated bridge]
|
|
Transient --> AdapterAPI
|
|
AdapterAPI --> Store
|
|
|
|
Controller -. ordinary Exec / Cancel / Respond / Close .-> Adapter
|
|
Controller -- canonical sessions only --> Durable[Durable activity reporter]
|
|
Scope -. Side is blocked .-> Durable
|
|
```
|
|
|
|
The Side runtime is an optional, provider-neutral AgentGUI port. Desktop
|
|
injects a factory, and each mounted embedded or detached AgentGUI surface owns
|
|
and disposes one runtime instance. AgentGUI does not branch on provider names:
|
|
it probes the selected live session and exposes `/side` only when the runtime
|
|
reports support. The Side pane and its messages live in a separate external
|
|
store; they are never dispatched to
|
|
`AgentSessionEngine`, never appear in the conversation rail, and are discarded
|
|
on explicit close, owning-surface disposal, or event-stream disconnect. A
|
|
typed `/side` remains an isolated Side intent even while capability resolution
|
|
is pending or unsupported; it must never fall through to the canonical main
|
|
conversation submit path.
|
|
Once capability discovery has enabled the command, AgentGUI enters the local
|
|
`opening` state and renders the Side shell immediately; it does not repeat the
|
|
capability RPC before opening. An empty Side renders a dedicated temporary-
|
|
conversation explanation while the provider fork completes and before the
|
|
first local Turn.
|
|
|
|
The service boundary is generated from OpenAPI and exposes resolve, open,
|
|
send, exact-turn cancel, interactive response, and close operations. Live
|
|
events use the workspace-scoped `agent.side.updated` topic with source/Side
|
|
identity and a Side-local monotonic sequence. AgentGUI consumes the generated
|
|
discriminated payload union directly, and transport adapters must expose
|
|
current connection state plus connection-state subscription. The canonical
|
|
`agent.activity.updated` bridge is not registered as the Side observer.
|
|
|
|
## Codex reference adapter
|
|
|
|
Codex uses the source-owned app-server connection while it is live. For an
|
|
idle or historical source, it starts a Side-owned app-server connection and
|
|
loads the persisted source by `threadId`. The historical path restores the
|
|
source's isolated `CODEX_HOME` from persisted runtime metadata so the new
|
|
process can find the original rollout; capability discovery fails closed when
|
|
that metadata is absent. The dedicated process is launched with the Side
|
|
session identity while the source `threadId` is used only by `thread/fork`, so
|
|
resource preparation, logs, and initialization-time events cannot attach to
|
|
the canonical source. Both paths send:
|
|
|
|
1. `thread/fork` with `ephemeral: true`, `excludeTurns: true`, no
|
|
`lastTurnId`, and Side-specific developer instructions composed with the
|
|
source thread's latest Tutti-owned host context.
|
|
2. `thread/inject_items` on the returned child thread to insert a model-visible
|
|
Side boundary.
|
|
|
|
`excludeTurns` hides inherited Turns from the Side-local response and UI; it
|
|
does not remove them from the provider fork's model context. Subsequent Side
|
|
Turns keep both the inherited Tutti host context and the Side-specific
|
|
instructions in their collaboration settings.
|
|
|
|
The connection has a thread-aware router. A live-source connection is
|
|
referenced by both runtime sessions; a historical-source connection is owned
|
|
only by Side and closes with it. Notifications for a new child that arrive
|
|
before `thread/fork` responds are buffered in a provisional route and replayed
|
|
only after lineage validation and Side registration. Closing Side unsubscribes
|
|
the ephemeral child and drops only the Side reference; it does not issue
|
|
`thread/delete` and cannot close the live parent connection. Losing the Side
|
|
connection produces `ErrSideConversationExpired`; Side is never resumed as a
|
|
canonical thread.
|
|
If a physical close fails, the adapter retains the dedicated connection as a
|
|
cleanup owner and retries it through the bounded runtime resource sweeper.
|
|
|
|
This change establishes the infrastructure, Codex reference vertical slice,
|
|
typed daemon API, and AgentGUI Side pane.
|