Files
agent-radar 2ed3415fb7
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
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00
..

Agent Host contracts

packages/agent/host is the provider-neutral application boundary for canonical agent session and turn lifecycle orchestration. The package now owns the create, resume, send, durable submit-claim, canonical title, session read, settings, pin, delete, cancel, session fork, interactive response, plan decision, durable runtime-operation, and complete goal-control/reconcile application core. tuttid routes those commands through Host; transport adapters translate their own HTTP or RPC shapes into these provider-neutral contracts.

Tutti Mode turn snapshots use PreferenceVersion to separate the current Effect/Speed pair from the deprecated single-axis OrchestrationIntensity. Current writers set TuttiModePreferenceVersionEffectSpeed and populate the legacy alias with the effect value. Runtime readers treat version zero as a legacy snapshot, mapping its intensity to effect and using balanced speed (50). This is an upgrade read path, not support for connecting a new client to an older daemon.

The module owns:

  • lifecycle command and runtime observation types;
  • narrow canonical store, runtime, preparation, attachment, clock, scheduler, and post-commit observer ports;
  • the runtime-operation coordinator, worker, typed interactive dispositions, and startup recovery order;
  • the direct and typed goal-control saga, revision actor, durable operation and reconcile-inbox workers, exact Goal-generation fences, provider evidence repair, and goal recovery policy;
  • the provider-neutral Session Fork saga, selected-Turn binding check, source mutation fence, exact capability resolution, frozen canonical snapshot, attachment staging, durable lineage, and startup recovery policy;
  • the durable edit-retry saga, effective-history revision fence, authoritative provider-history reconciliation, and explicit replacement recovery policy;
  • the provider-neutral runtime-only Side lifecycle, including live-source capability checks, idempotent open, transient execution, and cleanup;
  • typed conformance scenarios under conformance.

CreateSession has three explicit modes: an empty session, one command with InitialContent, or one typed InitialGoalControl. Initial content prepares its submit claim before provider delivery and rolls back the provisional canonical shell when delivery fails. Typed initial Goal is mutually exclusive with non-empty initial content; it creates a non-provisional Session and enters the same durable Goal saga under ClientSubmitID without opening a Turn. When the provider confirms that the accepted set command will begin autonomous execution, Host persists execution_pending on that Goal generation. Only a canonical goal_arm or goal_continuation Turn carrying the exact operation, revision, and repair epoch clears it; terminal/non-active Goal observation, divergence, failure, replacement, and clear also release it. This fact bridges loading presentation without making Goal convergence imply a Turn. Before runtime preparation or provider startup, a retry with that identity checks the canonical Goal operation. A completed retry returns the existing Session and operation; an in-progress or failed operation returns its existing state instead of starting another provider Session. This preflight is durable across Host process restarts and does not depend on the runtime's in-memory Session registry.

Session creation also has a Host-owned canonical initialization barrier. Every Runtime used by CreateSession must implement RuntimeSessionInitializationPublisher. Host calls Start(CanonicalInitPending=true), durably initializes the exact canonical Session (including immutable rail placement), and only then calls PublishSessionInitialization. While that barrier is pending, the Runtime may start its provider connection but must buffer activity reports, stream events, configuration updates, and command snapshots so none of them can create or expose canonical state before Host commits it. Publication is idempotent and releases the buffered observations in order. An initial-content Session keeps its separate provisional submit barrier after canonical initialization and does not become visible until its submitted intent is durable.

RuntimeStartResult.Created records ownership of the live Runtime for the exact Start call. Failed-create compensation may close only a Runtime with Created=true and may roll back only a canonical Session created by that attempt. A retry that reuses an existing Runtime or canonical Session must not close or delete the earlier owner's resources; it must validate the existing immutable rail and fail closed on a mismatch. Host consumers must preserve the Start -> canonical initialize -> publish sequence instead of publishing a runtime-start report directly from Start.

Provider Turn acceptance is a cross-process barrier, not a generic lifecycle notification. The runtime may move through queued, dispatched, provider_observed, and resolving_identity, but it must not expose provider output or interaction until the exact provider identity has been resolved. The adapter then blocks its provider event path while the Host atomically persists canonicalTurnId + providerSessionId + providerTurnId; only that commit moves the Turn to durably_accepted. Streaming, waiting for approval/input, running tools, checkpoints, and provider-root terminal events all follow the barrier and retain the same authoritative provider Turn ID. Correlation IDs are never provider IDs. A provider may instead fail before any provider Turn identity exists. In that case the adapter returns an exact canonical turn.failed terminal without inventing a provider ID. Runtime dispatch remains an independent admission fact, and the runtime releases its local active-Turn fence only after that canonical terminal crosses the synchronous durable-report barrier. A failed or acknowledgement-lost terminal commit remains pending and is retried idempotently; exact daemon settlement reconciliation is the only other path allowed to release that fence.

Canonical external identity inheritance is separate from provider acceptance and Turn lifecycle. A Turn may carry one immutable IdentityAnchorTurnID pointing to an ultimate Turn in the same Session. Plan-decision completion uses that generic relation when it confirms the implementation Turn, and commits the anchor before the completed notice in the same canonical transaction. Host and downstream projections consume only the relation; they never recover it from plan text, status notices, submit IDs, or transcript page history.

The acceptance barrier does not decide whether the user's prompt is durable. After Exec returns an explicit rejection or an outcome-unknown timeout, Host records the submit provenance and lossless replay envelope on a cancellation-independent context. An explicit rejection settles an existing canonical Turn as failed and transitions its submit claim to terminal rejected; replaying the same ClientSubmitID returns that failed Turn without provider dispatch. For an initial-Session rejection, Host then closes the startup runtime with canonical completion suppressed: the failed Session, Turn, and prompt remain historical facts, but that runtime cannot be reactivated. An outcome-unknown result retains the prepared submit claim for reconciliation and never blindly redispatches. Only a new provisional Session with no visible message or provider identity may be compensated away.

Adapters must carry the structured action/objective instead of reconstructing it from presentation text. ParseTypedGoalControl remains the compatibility path for callers that still send /goal ... as initial content. Resume eligibility is decided by ResolveResumePolicy: root sessions resume normally, explicit imports may recreate a missing provider session, and child, tombstoned, or non-resumable imports are rejected. Canonical titles may be empty; only an explicit title or the first eligible prompt establishes one. For typed initial Goal, the display prompt (or a synthesized /goal command) is the eligible prompt and is established before provider startup, even though the Goal path does not create a Turn. ReprepareRuntimeSession is the non-destructive boundary for replacing an idle provider connection with freshly prepared MCP bindings. Host serializes it with other Session commands, rejects both canonical and runtime active-Turn evidence, and preserves the canonical Session, provider session ID, and history. Its trusted RuntimeContextOverlay is visible only to runtime preparation (for example to mint an Invocation-scoped bearer); it is not persisted or installed as provider RuntimeContext. A successful reprepare must precede the Turn whose tools use that binding. DisconnectWorkspaceRuntime is the attachment-loss boundary for releasing every live provider transport in one Workspace without deleting canonical or Controller Session state. Host serializes each Session against ordinary mutations, preserves the provider Session identity and history, and never resumes a provider or replays a prompt. Provider adapters terminalize active work and pending interactions before dropping the transport, and transport-only disconnect must not invoke a destructive provider session/close. A later user command follows the ordinary just-in-time Resume path. Host consumers that perform a provisional runtime mutation use WithWorkspaceRuntimeOperation; its callback receives the reentrant admitted context and must own startup through cleanup. Attachment observers first call AcquireWorkspaceRuntimeDisconnectFence, which closes admission immediately, then retry Wait until already-admitted mutations drain. Canceling one Wait does not reopen admission; every joined owner must call Release, and one owner cannot reopen the Workspace while another disconnect remains active. CreateSessionInput.RailPlacement optionally carries the caller-selected, versioned canonical rail identity. Host validates it before provider startup and persists its opaque SectionKey exactly on first creation. An idempotent retry that supplies a placement must use the same placement; project deletion or another adapter-side view change never reassigns an existing session to conversations. By default, a new explicit project placement must still exist in the Host's local project registry, which fences a stale local selection after project deletion. A trusted adapter may set CreateSessionInput.RailPlacementAuthoritative when an external canonical authority already fixed the placement. That opt-in accepts a project absent from the local registry, but it applies only to first initialization and never allows an existing session's immutable placement to change. Before provider startup, Host resolves the final placement from the immutable existing session, an explicit caller placement, or the prepared cwd through the canonical store. It then installs the prepared cwd in TUTTI_AGENT_CWD and the normalized versioned RailPlacement JSON in TUTTI_AGENT_RAIL_PLACEMENT. Create, resume, runtime reprepare, and historical Session Fork sources all receive that same pair. Nested callers inherit it when they omit an explicit cwd; an explicit cwd is a new placement-selection request, not a request to reinterpret the caller's environment. Adapters must not derive placement from a session id, binding id, PeerCommand, or another view lookup.

Host supplies the exact canonical assignments last; the runtime process adapter owns target-platform environment-key semantics when it materializes the child process environment.

Cancellation exposes durable intent acceptance, provider confirmation, and canonical settlement as separate facts. GoalControl, GetGoalState, and ReconcileGoal are provider-neutral Host APIs; typed /goal commands enter the same durable saga without opening a turn. GoalControlResult.Goal is always the durable desired projection after persistence; provider output is retained separately in GoalState.Observed. A provider may return no observation for pause or resume without erasing the visible Goal, and only a durable tombstone returns a nil Goal. GoalControlResult.IntentAccepted becomes true as soon as that durable operation exists, even when immediate runtime readiness or delivery returns an error; GoalState then distinguishes pending delivery from terminal failure. A provider-accepted or applied Goal is also canonical resume evidence for a turnless Goal session after the live runtime disappears.

An exact-provider cancel response can be delivery-unconfirmed: the provider received the request but could not prove it stopped the requested Turn. Host retains that exact durable operation for retry and canonical reconciliation; it does not infer either canceled or failed from the response. If the canonical Turn reaches a terminal state first, the operation completes as a no-op and preserves that existing outcome.

If the runtime reports provider_state_lost, or reports an absent provider target while the canonical Turn is still unsettled, Host performs the same canonical-terminal check once. Without authoritative terminal evidence it settles the target as failed with execution_status_unknown; it never fabricates completed, canceled, or interrupted from provider absence.

AdoptProviderGoal is the narrow reverse boundary for a Goal created by a provider tool during an already accepted Turn. It atomically records the active provider generation as a completed, applied operation and converged desired/observed state; it never dispatches another provider mutation. The provider session plus immutable generation fingerprint form its replay identity. A conflicting pending or active durable generation is rejected, so runtime continuation remains fail-closed instead of inheriting whichever Goal happens to be current. Every adoption also carries the canonical Goal revision observed before its asynchronous dispatch. Host compares that revision inside the serialized Goal actor, so an observation queued before a newer set, clear, pause, or resume cannot advance after the newer mutation commits. Terminal and cleared generations may advance to a genuinely later provider-authored Goal; that transition observes the current revision and receives a new durable revision. A caller-stable ClientSubmitID makes one goal mutation idempotent across retries and Host restarts (and takes precedence over the legacy metadata field). GetGoalState is a pure canonical read: only GoalControl, AdoptProviderGoal, ReconcileGoal, and recovery workers may create or change the durable goal projection. Recover first requeues and recovers durable runtime operations, then goal operations and the goal reconcile inbox, then settles unrecoverable stale turns. Configuring a goal store without its runtime or inbox consumer fails recovery with ErrGoalConsumerUnavailable instead of silently accumulating work.

FenceGoalGeneration is the durable boundary for revoking work created by one exact Goal operation. The caller supplies the original operation identity; Host persists (operationId, revision, repairEpoch) in workspace_agent_goal_generation_fences before attempting any provider call. IntentAccepted means Host owns later retries even when the provider is offline. Accepting or recovering a fence never resumes an offline provider Session. Host immediately prepares a revision-conditional local clear so the revoked target cannot replay. During startup recovery, an absent Runtime Session completes that clear and the fence locally without resuming a provider; the operation remains explicit that provider application is unknown. Provider delivery waits until the Session is already live or a user action resumes it. A pre-crash live revocation may already have prepared its exact-Turn cancel; startup completes that internal operation locally with an interrupted outcome instead of retrying a missing Runtime or letting the operation shield a stale Turn from restart settlement. A fence is an admission rule, not session deletion: completed rows remain durable and are loaded into Runtime resume before the Session can accept Goal or Turn work, so a delayed provider generation cannot become canonical. The runtime Controller retains the exact fence set independently of an adapter connection and installs it into every replacement connection before dispatching a user operation; failed installation closes the unprotected connection. Host cancels a live Turn only when its immutable Goal provenance exactly matches the fenced identity; that internal cancel is require-live and never reconnects an offline provider. The fence remains unsettled until the Turn reaches an authoritative terminal state. It never retargets session.activeTurnId, clears a newer Goal revision, or infers ownership from the current session state. FindGoalControlOperationByClientSubmitID lets an adapter recover the original operation after an accept-before-response crash without duplicating Host's operation-ID algorithm. Startup and steady-state workers process fences before ordinary Goal operations; otherwise a prepared revoked Goal could be replayed during recovery before its fence reached the runtime.

GetGoalActivityTurn is the read-only projection proof for consumers that observe a turnless Goal Session. A candidate must be the latest active Turn, have a Goal-owned origin, and carry an exact operation/revision/repair-epoch identity backed by the durable Goal operation store. Consumers may use the returned Session and Turn to authorize live projection; they must not infer Goal ownership from Session.ActiveTurnID, Turn recency, or origin alone.

Currently disabled. Durable edit-and-retry is neutralized in production via Config.EditRetryDisabled: its saga can strand a session in a rolled-back-but- not-resent state whose runtime operation becomes a cold-recovery poison pill that crashes tuttid on launch. While disabled, GetEditRetryAvailability reports unsupported, EditRetry/RecoverEditRetry refuse, and recovery quarantines any leftover operation (failing it and clearing the session's history fence back to ready). Re-enable only once the resend/recovery gap is fixed. See the troubleshooting entry "A stuck edit-and-retry operation crashes the daemon on every launch". The behavior below describes the feature when enabled.

A completed latest user Turn may be edited and retried only through GetEditRetryAvailability, EditRetry, and RecoverEditRetry. Host owns the complete lifecycle: it snapshots the lossless submitted content, serializes the Session mutation, checkpoints before provider rollback, retracts exactly one effective Turn only after authoritative confirmation, then submits a stable replacement Turn. The replacement keeps attachments, mentions, capability references, and the Tutti mode snapshot while replacing only the first text block. Retraction changes model-visible history and canonical projections; it does not compensate filesystem changes produced by the original Turn. Conversation timelines hide retracted Turns, while audit reads and generated file projections retain their submitted content and filesystem side effects.

operationId, replacement turnId, and clientSubmitId remain stable across retries and process restarts. A direct provider acceptance receipt completes the operation without polling. If a rollback or replacement response is lost, Host reads the provider's effective history and fails closed on identity divergence. An uncertain rollback is never dispatched twice. An uncertain replacement is never resent until an explicit retry_replacement command has proved its absence from authoritative history; a read-only reconcile command cannot dispatch provider work. While history is fenced in rollback_pending, resend_pending, or recovery_required, ordinary sends are rejected rather than appended to an uncertain model context. The authoritative absence proof is consumed in the same SQLite transaction that removes a discardable failed replacement placeholder and advances the stable replacement attempt. There is no separately committed redispatch authorization checkpoint. Replaying the same proof is idempotent; a later authoritative proof advances a new attempt only after the previous failed placeholder and claim are safely discarded.

A provider-accepted Goal operation has crossed the delivery boundary. The steady-state worker waits for applied evidence and never resubmits that mutation; the accepted convergence deadline terminates a lost-evidence case. Startup recovery may replay an accepted mutation only according to the adapter's recovery policy. In particular, a query-incapable adapter may replay an idempotent clear once to resolve a crash window, while unsafe set replay remains rejected.

GetSession reads canonical session truth plus an optional live runtime observation without starting a provider. GetSessionWithRailPlacement adds the Host-owned immutable rail proof for idempotent recovery; application adapters must not reproduce rail normalization from canonical fields. GetTurn, GetInteraction, ListSessionTurns, ListSessionMessages, FindTurnByClientSubmitID, and GetSessionInteractionSnapshot expose canonical queries without leaking an adapter's concrete store. GetSessionInteractionTreeSnapshot is the execution-tree read boundary: it accepts only a root Session, resolves an optional latest root Turn in the same read transaction, and returns that root Turn plus every descendant Session's latest-Turn interactions. Deleted Sessions and retracted latest Turns are excluded, and the full result is ordered by Session, Turn, and request identity. Turn pages are newest-first, bounded metadata reads with stable cursors. Message pages use per-session version cursors and may be narrowed to one turn. GetInteraction requires the complete (workspaceId, agentSessionId, turnId, requestId) identity. The interaction snapshot contains every interaction on the latest turn and derives its pending subset from that same read; older-turn pending rows can never become current actionable state. Canonical transaction participation derives one deduplicated interaction_tree/dirty fact for every affected execution tree. Interaction changes, Turn creation/settlement/retraction, and Session deletion are all covered; the fact carries the immutable root Session and root Turn identity so consumers wake and reread one tree without reconstructing lineage. A root Session deletion uses an empty root Turn as an explicit all-turns wake. These facts are invalidation hints, not partial row updates. Consumers publish the reread result as one complete interaction_snapshot carrying the returned exact root Turn; an empty interactions array is an authoritative clear only for that explicit root. Collection contents or authorization-list order must never be used to infer the current root. CreateSessionInput.ClientSubmitID and SendInput.ClientSubmitID are the typed idempotency identities and override the legacy metadata value when both are present. The matching durable submit claim's immutable CreatedAtUnixMS is the canonical occurrence of that user message. Host passes it to both runtime execution and durable submit-provenance reporting; adapters must derive the same message sequence from that occurrence regardless of which report reaches storage first. ClientSubmitID identifies the submission but is not itself an ordering value.

Guidance is a mutation of an existing canonical Turn, not a request to steer whatever happens to be current when transport completes. SendInput.Guidance therefore requires an explicit TurnID at the Host boundary. Host and the runtime Controller compare that identity with the live active Turn under the session lifecycle lock before provider admission. A mismatch returns a typed NotDispatched result, makes zero provider calls, and removes a prepared submit claim; callers must surface the rejection or retry with a newly captured target rather than silently redirecting the guidance. Accepted guidance follows the provider's native active-turn semantics while keeping the canonical Turn active. A soft-steering adapter may insert guidance into the current provider response without interrupting it. A preemptive adapter must close the interrupted response's live message/tool projections and publish its provider-turn terminal boundary before admitting guided output. Neither form is a canonical Turn cancel or a second user Turn.

Interactive responses follow the same ownership rule. Runtime may return a provider-neutral follow-up intent after an interactive denial, but it does not dispatch that prompt itself. Host checkpoints the intent on the leased interactive operation, waits for the answered Turn to become idle, and submits the prompt through SendInput with the stable id interactive-deny:<operation-id>. The checkpoint also persists the terminal interactive disposition, so recovery does not depend on Controller memory or an existing Runtime Session. Recovery reuses that disposition and id; if the provider connection is temporarily absent, the operation remains retryable until ordinary Host admission can replay the prompt without creating a duplicate Turn.

Accepted runtime Session reports reconcile their Goal snapshot through the canonical bottom-up observation path without overwriting a newer desired intent. When that changes the public Goal projection, the same transaction emits a goal_state mutation; timestamp- or evidence-only refreshes remain silent. A matching applied observation may also complete the pending Goal operation in that transaction. Consumers use these post-commit facts only as wake hints and reread canonical Goal state. For a user Turn, runtime acceptance is not complete until the provider returns its exact Turn identity and the activity reporter durably installs canonicalTurnId -> providerSessionId + providerTurnId. The direct acceptance path is synchronous with the Host command while subsequent provider output remains asynchronous. A local persistence failure after provider acceptance is reported as delivery-unknown and retains the submit claim; it must never cause an automatic redispatch. Providers receive only the opaque ClientSubmitID as a correlation identity. Canonical Turn ids remain Tutti-owned and are not projected into provider client-identity fields.

CaptureHistoricalSessionGraph and RestoreHistoricalSessionGraph are the provider-neutral Replay boundary for settled Session, Turn, Message, Interaction, Goal, hierarchy, stable settings state, and the narrow portable providerResumeCheckpoint required to resume an already-initialized protocol boundary. The checkpoint is opaque to Host; full provider runtime context is not exported. Restore is for a fresh isolated Workspace before normal Host recovery. HistoricalSessionGraphRestoreInput carries the target Workspace and required current User outside the portable graph; restore binds every imported Session to that runtime-owned identity without serializing it into a Cassette. It is idempotent only for identical content and the same target User, rejects graph or ownership conflicts, and never starts or resumes a Provider.

Runtime adapters preserve explicit downstream failures as ProviderError so Host consumers can distinguish provider-owned rejection from preparation, canonical-store, timeout, and other local failures with errors.As. The provider code and diagnostic text remain local observations rather than a stable cross-service taxonomy; coordination layers persist only their own coarse product reason when needed. NewProviderError deliberately leaves cancellation and deadline failures unclassified because their delivery result is unknown and must remain recoverable. The narrow NewProviderStartTimeoutError exception is used only after the runtime owner has observed the provider adapter's Start stage time out before establishing a runtime Session. The daemon keeps the existing request_timed_out AppError code for API and presentation behavior and carries that narrow verdict as ErrProviderStartTimeout in the error chain. The Host runtime adapter maps the marker to provider_start_timeout while preserving the deadline cause; callers must not infer that verdict from an arbitrary context deadline. UpdateSettings serializes with runtime resume: historical sessions persist settings only, while live sessions update the runtime first and persist the resulting settings only after the runtime accepts the change. Provider-specific model, reasoning, and speed normalization stays behind SettingsPolicy. A model change invalidates the previous model's context-window usage in both the live observation and canonical metadata; provider quotas remain valid and are preserved. UpdatePin mutates canonical metadata only. DeleteSession and DeleteSessions enter one deletion coordinator. The canonical store first resolves the complete root/child closure; Host acquires the shared session-mutation actor and session locks in stable order, closes every live runtime in that closure, and commits only if the store resolves the same closure inside the write transaction. An optional provider-neutral SessionDeletionGuard receives that exact Host-owned closure before any runtime close or canonical delete. A rejected admission returns with no canonical lifecycle side effects. Every admitted attempt is reported as completed or failed; reporting is observational and cannot change the command result. A changed child tree is reported as a failed attempt, replanned, and re-admitted with its new exact closure before Host closes any newly discovered runtime or writes a tombstone. A requested runtime that is live before its first canonical report is still closed and cleaned up by the same coordinator; the empty canonical plan simply skips the tombstone transaction, so deleting an already absent session is a successful no-op and batch responses retain empty arrays for their ID fields. Goal provider mutations use the same outer session-mutation actor, so clear/set/reconcile work cannot race session deletion. Post-commit runtime cleanup failures are reported separately from the committed delete result. Authorization, shared bindings, transport DTOs, and local view cleanup remain adapter responsibilities.

PurgeDeletedSessions is the separate permanent-removal command for bounded batches of canonical tombstones. Host owns the command boundary and delegates the atomic hard delete to its narrow SessionPurgeStore; retention timing, daemon-idle scheduling, HTTP exposure, and optional compaction stay in the host adapter. When durable adapter-owned filesystem cleanup is required, its intent must commit in the same product transaction as the canonical hard delete; the adapter performs that work after commit and retries failures. Host neither chooses those paths nor treats cleanup failure as a canonical purge failure. Each candidate is fenced by the exact persisted deleted_at value, so a concurrently restored or recreated row is preserved. Candidate selection groups each topmost tombstone with its complete descendant tree. A tree containing a live or too-new member is retained, while an eligible tree is removed in one transaction even when its row or payload count exceeds a normal batch bound; unrelated eligible trees cannot be starved by blocked ancestors. Purge results expose only content-free session descriptors and aggregate message/payload counts. The shared conformance scenario verifies live and too-new preservation, exact-cutoff removal, complete-tree atomicity, and idempotent replay through Host.

Recoverable deletion is a separate lossless tombstone lifecycle. New deletes preserve every selected root/child Session component, its Turns, Messages, Interactions, effective-history records, provider resume identity, and attachment references; only live work is terminalized. Batch deletion computes component size from connectivity inside that exact delete set, so sibling child subtrees under one canonical root remain independently restorable. If a later delete selects a live ancestor, complete current-version descendant tombstones are absorbed into one new component generation without settling their work a second time. Legacy or incomplete descendants are never upgraded: the new topmost component stays unavailable for restore but remains permanently deletable. Stable Goal and effective-history state is left byte-for-byte unchanged. A pending Goal operation is failed and detached from the state, while its desired Goal, observed Goal, revision, and tombstone semantics remain available after restore. ListDeletedSessions exposes workspace-scoped topmost tombstones—those with no tombstoned parent—with stable updatedAt + sessionId paging and explicitly marks legacy lossy tombstones unavailable. Its summaries, project-option catalog, and optional filter use the exact persisted railSectionKey as their identity; the retained project path is presentation metadata only. RestoreDeletedSession restores the exact component atomically without starting or resuming a provider. PurgeDeletedSessionTrees permanently removes selected topmost components, or all such components in one Workspace. The optional DeletedSessionLifecycleScenarios conformance catalog verifies that delete-to-restore boundary independently from retention maintenance.

ForkSession creates an independent root Session through an inclusive canonical SessionForkPoint. The provider driver must attest native throughTurn support and the selected canonical Turn must be settled. A non-empty provider Turn id is not sufficient: the owning Agent must also have written opaque provider_turn_binding_json, and its CanForkProviderTurn hook must accept that exact id/JSON pair. Session-detail projection and Fork execution call the same hook. Rows created before the JSON binding existed intentionally remain unbound. Descendants, active work on other Turns, and pending Interactions are not eligibility inputs. If an otherwise eligible historical Turn is missing only that binding, ForkSession performs one read-only provider-history repair before repeating the canonical boundary check. The primary proof is the durable submit claim's opaque correlation identity. Truly old Claude text-only submissions may use a per-request HMAC equality proof over one complete text block; multimodal, attachment-bearing, context-enriched, incomplete, duplicated, and ambiguous history fails closed. Codex has no legacy text recovery because its stable thread/read shape does not expose an equally authoritative complete prompt. No provider Turn is ever selected by index. The SQLite repair is an idempotent empty-binding compare-and-swap and rejects provider Turn identities already owned by another canonical Turn. Recovery and provider-owned Fork results pass through the owning Agent's binding writer. Claude stores its checkpoint inside its private JSON payload; Codex and Tutti Agent thread/fork(lastTurnId) use the shared app-server payload schema and provider Turn id while retaining separate runtime version attestations and isolated provider homes. Target titles use one lineage-family sequence (Title (2), Title (3), ...) rather than restarting the suffix when a child Session becomes the next source. Every fail-closed boundary rejection retains a stable, content-free reason through Host. HTTP adapters may project it as structured diagnostic metadata while preserving their existing coarse conflict reason; transcript payloads and attachment contents never enter that reason.

Session Fork is exposed directly when the provider/runtime attestation and the selected canonical Turn satisfy the capability boundary. Product adapters do not add a separate feature-preference gate; execution still revalidates the exact provider and Turn facts before dispatch.

Capability projection is preparation-free. It reads either the live runtime observation or the persisted runtime/driver attestation and never resolves credentials, prepares a target context, or starts a provider process merely to render a Fork action. ForkSession performs the complete preparation and revalidates the exact driver before dispatch.

Prepare freezes the complete canonical snapshot through the selected Turn, allocates all target canonical identities, and stages only the attachments referenced by that snapshot. Source reporting and Goal/runtime/submit activity continue against the live source; only physical deletion is retained while the operation may still need frozen resources. Multiple explicit Forks from the same boundary are valid. Host eligibility remains independent of source activity. The shared GUI exposes Fork only on settled Turns whose provider binding is durably bound, including earlier settled Turns while newer work is active. A recovery_required Turn must be repaired and reprojected as bound before the action is exposed; the Fork action is not a binding-repair control. The GUI disables only the exact Turn whose own Fork request is currently in flight.

Fork uses the durable prepared -> dispatching -> provider_accepted -> committed saga. Provider dispatch happens only after dispatching commits. The selected provider Turn must exist in the provider source; earlier provider history is trusted and is not compared with Tutti's canonical prefix. Provider acceptance, including the child provider Session id, is persisted before any host-copy binding or canonical materialization. A provider_accepted retry therefore performs only idempotent local binding and commit and never invokes the provider again. If provider-owned acceptance evidence cannot cover every provider-bound Turn in the frozen canonical snapshot, Host treats that evidence as permanently inconsistent rather than inventing provider identities. It atomically marks the operation failed, preserves the provider acceptance evidence and source history, removes any incomplete target, and releases the target reservation and source-boundary barrier so startup and later operations can continue. Failure to persist that quarantine remains a startup recovery error; transient SQLite failures are never reclassified as permanent inconsistency.

prepared is safe to continue during startup recovery because provider dispatch has not begun. A crash in dispatching becomes unknown and is never automatically redispatched. There is no deterministic-replay compatibility path. Public status retains accepted / committed / failed / unknown, while the operation phase exposes frozen / dispatching / materializing / committed / failed / deliveryUnknown.

Canonical commit consumes the frozen snapshot rather than re-reading the live source. It remaps Session, Turn, Message, Interaction, and attachment ids, persists immutable lineage, normalizes a nonterminal boundary to settled/interrupted, and changes copied pending Interactions to superseded. Provider-owned mode records the returned selected-Turn mapping; host-copy mode first makes the accepted provider child independently readable. The target cwd, settings, and runtime context come from the same prepared runtime observation used for provider dispatch.

Interactive responses establish their winner at the canonical interaction transition, not in a GUI or CLI adapter. Preparing an interactive runtime operation atomically moves the interaction from pending to answered with the requested action, option, and payload. A competing response compares its request with that durable output: an identical response is answered, while a different response is superseded; neither path leaks operation-conflict or in-progress errors to the responder. The Interaction's pre-delivery answered state is a durable claim marker, not the runtime's terminal result; completed operation and responder dispositions follow an authoritative runtime superseded result instead of being overwritten by that marker. Interactive identity is always the typed InteractionRef tuple (workspaceId, agentSessionId, turnId, requestId). Provider request ids remain unchanged and are only unique within their owning Turn. The response payload contains no identity fields. Durable operation idempotency uses the same tuple; an operation id that disagrees with its structured identity is an invariant failure and must fail closed rather than guessing or rewriting stored data.

Adapters retain authorization and identity, transport, runtime process or VM selection, desktop APIs, attachment ingress, and cloud inbox/outbox behavior. Adapter-only create fields such as transcript source paths and materialized skill bundles intentionally remain outside the Host contract.

tuttid production wiring resolves canonical/runtime ports and grouped adapter dependencies before constructing the agent service. It creates shared narrow components, uses their HostSupportPorts with canonical/runtime ports to compose one long-lived Host, then passes that completed Host and the same components to NewService. Production never mutates Service fields or calls a post-construction Host setter. Support adapters retain only their narrow component dependencies, never the complete Service facade. Runtime preparation may read Session Fork lineage and its operation through a committed-only seam: the lineage is created atomically with the committed fork, and the seam performs no canonical write or lifecycle reconciliation. That reader is a required canonical composition port rather than an optional runtime assertion, so provider-state binding cannot silently omit committed Fork identity verification.

Startup invokes Host.Recover before serving traffic and starts the Host-owned runtime and goal workers. Adapters can use the supervised Host.Run entrypoint to start the runtime-operation, goal-operation, and goal reconcile-inbox workers as one lifecycle; an infrastructure-level worker exit cancels its siblings, while retryable item failures remain worker-local. The individual worker entrypoints remain available for existing focused wiring and tests. The service package translates HTTP/query/composer/analytics concerns and provider-specific preparation only; session, turn, runtime-operation, and goal lifecycle decisions remain in Host. Isolated service tests may lazily compose the same adapter set, but production startup never creates a Host per request or per session.

Canonical commits have two distinct extension points. A store-sqlite TransactionParticipant may append a caller-owned durable marker inside the same transaction as runtime/goal intent and canonical facts; it receives a narrow transaction writer rather than *sql.Tx. After commit, Host emits a typed CommittedDelta to CommitObserver for view invalidation, event-stream wakeups, analytics, and worker scheduling. Observer failure never rolls back or changes the command result. Work that must survive observer failure must first be represented by the transaction participant's durable marker; legacy workspace-only change notifiers are optional latency optimizations.

TerminalFailureObserver is the aggregated failure-only seam. A failed command or a Turn whose canonical outcome is failed may produce one observation; completed, canceled, and startup-reconciled interrupted Turns do not. Consumers that need the exhaustive terminal population must read CommittedDelta.RootTurnsSettled from CommitObserver and classify the canonical outcome instead of inferring cancellation from a failure callback.

Re-derivable adapter projections are deliberately outside the participant contract. Adapters repair those while consuming canonical state rather than coupling their schema to every Host transaction.

Canonical deletion tombstones are not re-derivable after hard deletion, so session delete, batch clear, and failed-create compensation also participate before commit.

The conformance harness depends only on the public Host contract. An implementation supplies a conformance.Driver, seeds its own canonical and runtime fakes in Reset, and runs every value returned by conformance.Scenarios. This lets tuttid, the extracted Host, and downstream adapters share one behavior baseline without importing one another. Coordinator, goal, and commit-observer scenario groups extend the same driver with recovery ordering, recovery failure propagation, post-commit failure semantics, and exact-tombstone permanent removal semantics. Deletion-admission scenarios are required members of both the standard adapter and application-core catalogs; the focused deletion-admission catalog reuses those same scenario values rather than defining a second behavior suite.

The conformance package keeps its shared fixture and driver contract in conformance.go, explicit scenario membership in scenarios.go, and scenario runners in capability-named files. A scenario shared by multiple catalogs must reuse the same package-level scenario value; catalog ownership must not be inferred by matching its display name.

The Host release module depends on store-sqlite and store-sqlite/canonical, but not on daemon, sidecars, or tuttid. Canonical activity snapshots, report observer types, provider identities, capability vocabulary, and plan-decision strategy live in store-sqlite/canonical. Daemon packages retain source-compatible aliases for existing consumers; runtime mechanics remain daemon-owned. Title normalization and initial-title CAS derivation are Host application behavior rather than canonical vocabulary.