Skip to the content.

Session clients: full-power attachment and portable continuity

Status: normative product contract and generation constraint (2026-08-13)

This document answers one product question: what does opening a fak session mean?

It does not mean opening a dashboard card, a reduced chat replica, or a product layered over the terminal product. It means attaching another client to the same logical session. The terminal is one client. A Fleet workspace, browser, phone, editor, or future native app is another. A separate product may deliberately consume a smaller public API, but it must say that it is a separate product rather than presenting a partial projection as “the session.”

Value frame

The real next-best alternative is a browser console specialized to the current gateway routes. That can look complete quickly, but every control or state field added directly to it creates a second product contract and makes later device/provider/compute movement harder.

Problem centrality and all-work checks

Centrality: Core. Client-independent managed context is the user-facing form of fak’s kernel boundary, not a convenience shell around it.

Check Requirement
P1 — managed context The authoritative transcript, tool effects, pending interactions, budgets, and compaction lineage belong to a logical session, never to one renderer.
P2 — net-true efficiency Reattachment reuses that state and provider-cache lineage where valid; it must not replay the whole conversation merely to reconstruct a UI. Any migration claim includes checkpoint, transfer, warm-up, and lost-cache costs.
P3 — bounded adaptation Provider/account/model/compute changes occur at typed execution-epoch boundaries with policy, capability, budget, and safe-point checks. No client can mutate placement by editing local UI state.
P4 — integrated operations Discovery, attach, control, approval, replay, migration, audit, and recovery use the same session identity and emit the same journaled facts across terminal and non-terminal clients.

The invariant

Selecting a first-party session opens a full session attachment. It does not navigate to a separately implemented approximation of that session.

“Full” means semantic and control parity, not pixel parity. A phone may lay out information more compactly than a terminal. It may not omit a currently valid action without declaring the missing capability and offering a handoff to a capable client. A terminal may render ANSI while a browser renders structured blocks. Both consume and change the same authoritative state.

For a native fak loop, the canonical seam is a structured session protocol. For a wrapped program whose only truthful interface is a PTY, fak may expose a lossless PTY attachment mode. The browser must not scrape terminal output when structured events exist, and fak must not invent structured controls that the wrapped runtime cannot actually perform.

One identity, several replaceable bindings

A session is not a PID, terminal, gateway port, provider thread, account, model, machine, or browser URL. Those are bindings with narrower lifetimes.

Layer Identity / state Lifetime and rule
Logical session session_id; objective; addressed event journal; transcript/tool-effect lineage; policy; budgets; pending interactions; checkpoint lineage Stable across client and placement changes. This is what a user opens.
Execution epoch execution_epoch; runtime adapter; provider thread/run ID; provider, account reference, model, compute target; process/gateway coordinates; cache lineage Replaceable at a safe boundary. Every replacement is journaled; old coordinates never become session identity.
Client attachment attachment_id; client kind; advertised capabilities; replay cursor; focus/presence; short-lived control lease Disposable and reconnectable. Disconnecting a client does not end the session.
Presentation viewport, theme, local keymap, notification preference, draft text not yet submitted Client-local unless explicitly synchronized. It cannot be authoritative execution state.

Credentials are references resolved under destination policy, never portable session payload. A move to another device or compute node proves that the destination can resolve an allowed account; it does not copy bearer tokens through the event journal or snapshot.

Explicit clear is a new logical session

Replacing a provider thread during resume, compaction, migration, or account/model movement does not change the logical session. An explicit user clear/new command does: it declares the prior conversation finished and opens a new logical session while the guard process remains alive.

The provider adapter must report the typed boundary rather than relying on terminal text. fak keeps the old session terminal and auditable, switches omitted-trace traffic to a fresh child trace, clears conversation-local roots and cache affinity, refreshes the context-token axis, and carries cumulative hard envelopes. The detailed field contract and provider matrix live in provider clear/new boundaries.

The attachment contract

All first-party clients use the same conceptual operations, whether the transport is local IPC, HTTP/SSE plus command POSTs, WebSocket, or a future relay:

  1. Discover — resolve session_id to its current execution epoch and reachable attachment endpoint without making the endpoint part of identity.
  2. Describe — return the durable session descriptor, current placement, pending interaction, available controls, and a monotonically addressed event head.
  3. Attach — authenticate, advertise client capabilities, receive an attachment_id, and replay from an explicit logical cursor. Delivery IDs and TCP connection lifetime are not replay state.
  4. Observe — receive the same ordered transcript, tool call/result, control transition, approval, budget, checkpoint, placement, and terminal-frame facts visible to every other client.
  5. Act — submit typed input or a typed control against session_id plus expected execution_epoch. The kernel validates policy and stale-epoch preconditions; the renderer does not directly operate a process.
  6. Detach — release focus/control leases while the logical session and execution continue according to policy.
  7. Move — request a new execution epoch with explicit placement constraints. Checkpoint, destination admission, state transfer, credential resolution, and cutover are kernel-owned transitions, not UI choreography.

Every successful state-changing request produces an addressed journal event before a client may present it as complete. Reconnect uses the last applied logical event address. This is the same replay discipline required by the open run-progress work in #6486.

Single-writer interaction, multi-reader presence

Many clients may observe concurrently. Exactly one attachment at a time holds the short-lived input lease for an interactive turn or PTY. Taking it is explicit and visible to all clients; stale clients receive STALE_EPOCH or LEASE_NOT_HELD, never a best-effort duplicate submission. Approvals and destructive controls retain their existing policy/confirmation gates even when the input lease is held.

This is not “one active browser.” A terminal can watch while a phone answers an approval, then take input back. Presence is ephemeral; history and pending interactions are durable.

Capability parity is negotiated, not assumed

Describe exposes a versioned capability set derived from the runtime and policy, for example:

A first-party client must render every advertised action or visibly mark it unavailable with a reason and a handoff URI/command. It may not silently hide controls to simplify its product. New kernel controls appear through capability discovery rather than custom frontend releases wherever possible. A conformance fixture compares terminal and UI clients against the same descriptor and action corpus.

Portable continuity and placement changes

A placement is the tuple (runtime adapter, provider, account ref, model, compute target). Changing any member creates a new execution epoch; it does not create a new logical session.

The move state machine is:

ATTACHED/RUNNING
  -> SAFE_POINT_REQUESTED
  -> CHECKPOINTED(address, digest, source_epoch)
  -> DESTINATION_ADMITTED(policy, capabilities, credentials, budget)
  -> RESTORED(new_epoch, placement, cache_lineage)
  -> CUTOVER_COMMITTED
  -> RUNNING

Failure before CUTOVER_COMMITTED leaves the source epoch authoritative when it is still healthy. Failure after cutover is recovered from the committed checkpoint and journal. An active provider call is not magically portable: fak waits for or induces a supported safe point, or returns a typed MOVE_UNSAFE refusal. Model/provider changes disclose semantic degradation (unsupported tools, context limit, unavailable provider-side cache) before commit.

State classes during movement:

Product boundaries

First-party session surfaces

The terminal, Fleet workspace drill-in, browser console, and mobile client are views of this contract. Their route is conceptually:

select session -> resolve session_id -> describe -> attach/replay -> full live surface

A Fleet overview remains a projection. The moment the operator selects a row, it crosses to the session attachment instead of growing its own per-row controls. A per-agent gateway URL is a current transport coordinate discovered for an epoch, not the bookmark users carry between devices.

Clearly separate products

A read-only status wall, Slack digest, embedded approval widget, or third-party integration may intentionally consume a subset. It must identify itself as a projection/integration, name its capability ceiling, and never claim that selecting its card is equivalent to opening the session. It can deep-link or hand off to a full client.

Required end-to-end spine

The first implementation slice is deliberately vertical, not a frontend mock:

  1. Add a durable logical-session descriptor and discovery record that can resolve a current local execution epoch without treating gateway_url as identity.
  2. Implement fak session open SESSION_ID as the reference terminal client using describe, replay-from-address, attach, input-lease, one text input, and detach.
  3. Implement one minimal browser page over exactly the same operations and capability descriptor; selecting its fixture session resumes at the same event address and can submit the next input.
  4. Capture a two-client witness: terminal creates/opens, browser attaches, browser acts, terminal observes the same addressed result after reconnect.
  5. Only then expand renderer polish, remote relay, migration envelopes, and mobile layout.

The reference CLI is essential: it prevents the protocol from becoming “whatever the web page needs” and makes terminal/UI equivalence executable.

Acceptance witnesses for the full objective

The objective is not complete until all are captured against real objects rather than renderer-only unit tests:

Relationship to existing plans

Non-goals

Shippable implementation backlog

The contract is split into independently witnessed leaves rather than silently deferred prose:

#6486 owns the replay-address seam reused by these leaves; #6476 remains the read-only Fleet overview spine rather than absorbing session operation.