From 24193fef4b2cf19a11fbf7a104cea34d830a8513 Mon Sep 17 00:00:00 2001 From: Wang Date: Wed, 9 Sep 2026 21:19:16 +0800 Subject: [PATCH] docs(architecture): rewrite Runtime Host and define Peer Mesh contracts Generated-by: OpenAI Codex --- ARCHITECTURE.md | 4 +- ARCHITECTURE.zh-CN.md | 4 +- docs/README.md | 1 + docs/architecture/peer-mesh-architecture.md | 256 +++++++++++ .../peer-mesh-architecture.zh-CN.md | 256 +++++++++++ .../architecture/runtime-host-architecture.md | 435 +++++++++--------- .../runtime-host-architecture.zh-CN.md | 434 +++++++++-------- 7 files changed, 979 insertions(+), 411 deletions(-) create mode 100644 docs/architecture/peer-mesh-architecture.md create mode 100644 docs/architecture/peer-mesh-architecture.zh-CN.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 07bafbf32d..efe4eada60 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -21,7 +21,7 @@ # Maka Backend Architecture -Maka has one execution authority: Runtime Host. Desktop, TUI, CLI, bots, and evaluation clients ask Runtime Host to execute work; none owns a second Runtime. +Each State Root has its own Runtime Host as execution and write authority. Desktop, TUI, CLI, bots, and evaluation clients execute work through that Host boundary rather than creating a second Runtime for the same state. Multiple Hosts may own different State Roots; Peer Mesh supplies endpoint membership and connections without combining their execution authorities. ```mermaid flowchart LR @@ -76,6 +76,8 @@ The result kernel contains only score, normalized usage, attributable cost, dura ## Reading paths +- Host ownership, admission, observation, Client isolation and lifecycle: [Runtime Host architecture](./docs/architecture/runtime-host-architecture.md). +- Network identity, membership, path selection and stream recovery: [Peer Mesh architecture](./docs/architecture/peer-mesh-architecture.md). - Runtime facts and projections: [Runtime core](./docs/architecture/runtime-core-architecture-draft.md) and [compaction](./docs/architecture/llm-compaction-events-log-projection-draft.md). - Crash recovery and continuation: [Runtime resume](./docs/architecture/runtime-resume-architecture.md). - Multi-agent scheduling: [Agent Graph](./docs/architecture/agent-graph-stream-scheduling-draft.md). diff --git a/ARCHITECTURE.zh-CN.md b/ARCHITECTURE.zh-CN.md index 5557d425b1..7cc86e8ead 100644 --- a/ARCHITECTURE.zh-CN.md +++ b/ARCHITECTURE.zh-CN.md @@ -21,7 +21,7 @@ # Maka 后端架构 -Maka 只有一个执行 authority:Runtime Host。Desktop、TUI、CLI、bot 和 Eval client 都请求 Runtime Host 执行工作,不再拥有第二套 Runtime。 +每个 State Root 的执行与写入 authority 归属于其 Runtime Host。Desktop、TUI、CLI、bot 和 Eval client 都经过 Host 边界执行工作,不为同一份状态创建第二套 Runtime。多个 Host 可以分别拥有不同的 State Root;Peer Mesh 提供端点间的成员关系与连接,不合并这些执行 authority。 ```mermaid flowchart LR @@ -76,6 +76,8 @@ continuation = Maka subject 内部的 Runtime Host 行为 ## 阅读路径 +- Host 权责、准入、观察、Client 隔离与生命周期:[Runtime Host 架构](./docs/architecture/runtime-host-architecture.zh-CN.md)。 +- 网络身份、成员关系、路径选择与 stream 恢复:[Peer Mesh 架构](./docs/architecture/peer-mesh-architecture.zh-CN.md)。 - Runtime 事实与 projection:[Runtime core](./docs/architecture/runtime-core-architecture-draft.zh-CN.md) 与 [compaction](./docs/architecture/llm-compaction-events-log-projection-draft.zh-CN.md)。 - Crash recovery 与 continuation:[Runtime resume](./docs/architecture/runtime-resume-architecture.zh-CN.md)。 - Multi-agent scheduling:[Agent Graph](./docs/architecture/agent-graph-stream-scheduling-draft.zh-CN.md)。 diff --git a/docs/README.md b/docs/README.md index ab229a88d4..a34778cccd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -59,6 +59,7 @@ This page is the authority map for Maka documentation. Code and contract tests r - [WorkHub Coordination Session ADR](./architecture/workhub-coordination-session-adr.md) - [Runtime resume architecture](./architecture/runtime-resume-architecture.md) ([中文](./architecture/runtime-resume-architecture.zh-CN.md)) - [Runtime Host architecture](./architecture/runtime-host-architecture.md) ([中文](./architecture/runtime-host-architecture.zh-CN.md)) +- [Peer Mesh architecture](./architecture/peer-mesh-architecture.md) ([中文](./architecture/peer-mesh-architecture.zh-CN.md)) - [Remote Runtime Host setup](./runtime-host-remote-access.md) ([中文](./runtime-host-remote-access.zh-CN.md)) - [Runtime resume extraction ledger](./architecture/runtime-resume-extraction-ledger.zh-CN.md) - [Runtime resume Phase 3–4 implementation route](./architecture/runtime-resume-phase3-phase4-workspace-checkpoint-design.zh-CN.md) diff --git a/docs/architecture/peer-mesh-architecture.md b/docs/architecture/peer-mesh-architecture.md new file mode 100644 index 0000000000..08a9033a99 --- /dev/null +++ b/docs/architecture/peer-mesh-architecture.md @@ -0,0 +1,256 @@ +--- +doc_id: architecture.peer-mesh +title: "Peer Mesh Architecture" +language: en +source_language: zh-CN +counterpart: ./peer-mesh-architecture.zh-CN.md +implementation_status: current +document_status: current +translation_status: synced +last_verified: 2026-09-09 +owners: + - maka-backend +--- + + +[中文](./peer-mesh-architecture.zh-CN.md) + +# Peer Mesh architecture + +## 1. Architecture contract and scope + +This document answers: **How does Maka retain verifiable Peer relationships as endpoint addresses and network paths change, and provide recoverable connections to the Host protocol?** It defines identity, membership state, address evidence, path admission, and recovery boundaries for networking, Client, and Host integration developers. + +The implementation baseline is `09c73430c` (2026-09-09). This describes the current experimental Peer implementation, not future cross-device workflows discussed in blogs. See [Runtime Host architecture](./runtime-host-architecture.md) for execution, permissions, and storage contracts. + +Peer Mesh supplies durable private membership, verified reachability exchange, and connection candidates derived from that evidence. Native Peer transport supplies authenticated streams. Neither is a general VPN, Host credential authority, distributed State Root, or cross-Host scheduler. Standalone Direct Peer connections can also use explicit connection information without first joining a Mesh. + +Read the layers from bottom to top. An arrow means that a lower layer supplies a capability, not that it grants the upper layer's permissions. + +```mermaid +flowchart BT + T["Native transport: direct / approved transit"] --> S["Authenticated resumable byte stream"] + S --> H["Host credential and protocol admission"] + H --> G["Session / operation grants"] + M["Signed Mesh membership"] --> R["Verified reachability and route candidates"] + R --> T +``` + +## 2. Components and persistence boundaries + +| Component | Owned state/transitions | Output | +|---|---|---| +| Native endpoint | Peer identity, libp2p connections, hole punching, streams and transport quotas | Authenticated peer streams and connectivity/reachability snapshots | +| `RuntimeHostPeerEndpointOwner` | Process lifetime for one endpoint data root, native client and publisher | Endpoint protected by a file-lifetime owner | +| Reachability publisher | Local signed lease revisions, address sets and refresh | Verifiable address evidence | +| `PeerMeshNode` / Mesh store | Mesh authority, signed rosters, invitations, member advertisements, received reachability | Membership view, route resolution and transit policy | +| `RuntimeHostPeerClient` | TS/native bridge, target-bound connect attempts and candidate updates | Application or mesh-control stream | +| `ResumablePeerStream` | Process-local byte offsets, ACKs, attachment generations and recovery window | Logical byte stream retained across path changes | +| Host peer listener | Credential/principal admission, resume attachment validation and quotas | Connection to the common Host dispatcher | +| Desktop reconnect owner | Host/Guest target lifecycle, backoff and current failure diagnostic | Ready/reconnecting/unavailable state | + +A PeerId belongs to a network endpoint, not necessarily one physical machine. Desktop Client and Host endpoints may have different identities. Network keys, Mesh authority keys, Host credentials, rootId, and HostEpoch are not interchangeable. + +Identity, membership, invitations/advertisements/reachability, and required recovery evidence persist. Active sockets, dial Promises, ACK windows, and stream attachments are process-local. Retaining connection information does not persist a resumable live connection. + +Implementation: [endpoint owner](../../packages/runtime-host/src/peer-reachability/owner.ts), [Mesh owner](../../packages/runtime-host/src/peer-mesh/owner.ts), [Mesh node](../../packages/runtime-host/src/peer-mesh/node.ts), [native bridge](../../packages/runtime-host/src/transport/peer-native.ts), [native engine](../../native/runtime-host-peer/src/engine.rs). + +## 3. Identity, membership, and discovery data + +### 3.1 Three signed facts + +| Fact | Signer and principal content | Establishes | +|---|---|---| +| Mesh roster | Mesh authority key; meshId, authorityPeerId, revision, members, closed | Who belongs to this Mesh | +| Member advertisement | Member identity; meshId, peerId, revision, endpointKind, displayName, offersTransit | Announced endpoint role, display metadata and willingness to forward | +| Reachability lease | Corresponding Peer identity; peerId, revision, issuedAt/expiresAt, direct/coordination routes | Which Peer published the address evidence and its validity window | + +Mesh ID is bound to the authority key. That signing role differs from the PeerId carrying connections. Membership changes require authority acceptance and propagation of a new roster. A working connection cannot add members, and an IP or relay observation cannot sign reachability on behalf of the target. + +Advertisements carry limited networking/endpoint metadata. There is currently no Mesh service catalog for models, GPUs, tools, or work capacity. `endpointKind: host` is not Host operation authorization, and `offersTransit` is not a bearer token for unrestricted relay use. + +### 3.2 Membership lifecycle + +Creating a Mesh establishes its authority and initial roster. Invitations have validity and count limits. A joiner verifies the invitation and authority, completes the join protocol, and retains signed membership. Member removal or Mesh closure produces a new authority revision. Control streams exchange verified state and reconcile updates; an offline member returning with an old roster cannot undo newer decisions already received. + +The authority controls membership changes; application bytes do not all have to traverse it. Authority unavailability may prevent new joins or member changes. Valid paths and Host grants between existing members are separate questions. + +Removal recomputes discovery/transit eligibility for the affected Mesh. Leaving one Mesh does not automatically revoke every Host Session grant, nor erase another effective shared Mesh. The corresponding authority owns each authorization withdrawal. + +Implementation: [signed models](../../packages/runtime-host/src/peer-mesh/model.ts), [Mesh protocol](../../packages/runtime-host/src/protocol/peer-mesh.ts), [membership coordination](../../packages/runtime-host/src/peer-mesh/node.ts). + +## 4. Reachability is evidence, not a permanent locator + +Signed leases validate peer identity, revision, time bounds, and route shape. Receipt establishes validity using a monotonic clock. Receiving the same revision/signature again cannot extend its useful lifetime, and wall-clock rollback cannot make old evidence permanently current. + +The current default lease lifetime is 5 minutes, maximum accepted lifetime 10 minutes, and clock-skew allowance 2 minutes. Expired records may remain recovery hints within a 24-hour recovery horizon. Current evidence and historical information worth trying for recovery are different. These are implementation limits, not permanent external service commitments. + +Nodes can obtain fresh signed information through reachable members, existing addresses/coordination peers, and retained hints. Reconciliation performs bounded exchange and convergence; it does not replicate Host business state or turn repeated notifications into new facts. + +If all old addresses, mutually reachable members, and coordination entry points disappear, a PeerId can verify whom a connection reaches but cannot derive a new address. Fresh invitation/connection information or a reachable entry point is then required. Membership may remain durable while route resolution becomes `exhausted`; these states are compatible. + +Implementation: [reachability model](../../packages/runtime-host/src/peer-reachability/model.ts), [publisher](../../packages/runtime-host/src/peer-reachability/publisher.ts), [route recovery](../../packages/runtime-host/src/peer-mesh/node.ts). + +## 5. Path classes and forwarding admission + +| Path | Purpose | Authority/lifetime | +|---|---|---| +| Direct | Carry streams directly between target Peers | Verify the actual remote PeerId; network paths may change | +| Coordination relay | Assist discovery, connection establishment and hole-punch signaling | Public coordination does not confer application transit eligibility | +| Approved Mesh transit | One-hop forwarding by a member satisfying current Mesh eligibility and forwarding policy | Requires membership, advertisements and local admission policy; end-to-end authentication/encryption remain | + +A node offers transit only after explicitly selecting a Mesh for that service; forwarding is disabled by default. Client approved-relay sets derive from verified Mesh evidence and policy. Native application protocols check whether the particular connection is direct or uses an approved transit relay. An existing Circuit Relay v2 connection alone does not authorize sending Host application traffic through a public coordination relay. + +Transit has reservation, circuit, per-peer, byte, and duration limits. It promises neither unlimited bandwidth nor arbitrary recursive multi-hop gateways. A relay can observe network relationships and traffic characteristics and affect availability; it gains no endpoint Host credential authority and does not process plaintext Session content. + +Automatic relay discovery finds public coordination candidates for the current native libp2p path. Retained relay anchors help recovery but are not a guaranteed Maka central directory. Explicit coordination configuration changes the source of that dependency; it does not eliminate the need for reachable third parties under restrictive NATs. + +Implementation: [transit policy](../../packages/runtime-host/src/peer-mesh/node.ts), [native application admission](../../native/runtime-host-peer/src/engine/application_stream.rs), [relay discovery](../../native/runtime-host-peer/src/engine/relay_discovery.rs), [native limits](../../native/runtime-host-peer/src/engine.rs). + +## 6. Direct transport, WebRTC, and connection attempts + +The native endpoint's application PeerId is shared across its libp2p transports. QUIC, TCP/Noise/Yamux, DCUtR, and optional WebRTC provide paths under that identity. Auxiliary relay discovery may have its own internal Swarm; one application identity does not mean exactly one Swarm object in the process. + +WebRTC supplies an additional direct path: + +1. An authenticated coordination connection carries `/webrtc-signaling/0.0.1` to exchange SDP and Trickle ICE. +2. STUN discovers server-reflexive ICE addresses; it supplies neither membership, Host credentials, nor Session grants. +3. ICE checks select a usable candidate pair; WebRTC upgrade verifies the expected peer and handshake information. +4. The resulting direct connection for that Peer enters the same application admission boundary. + +DCUtR and WebRTC signaling are different protocols. DCUtR coordinates libp2p direct hole punching; it does not convert relay-observed TCP/QUIC addresses into WebRTC ICE candidates. At the native options boundary, omitting WebRTC configuration disables that path. An empty STUN list uses no external STUN and does not guarantee discovery of a NAT-external address. This is not a general TURN fallback service. + +A connect attempt fixes its target PeerId and request identity, collects/updates candidates, and has a deadline and cancellation. Direct paths are preferred; approved transit can join after a short delay, and stream opening may use bounded hedging. Current transit fallback and hedge delays are both 250 ms, with at most two parallel stream opens. Racing happens at connection/stream establishment; losers close after selection. The same business mutation is never sent in parallel to race responses. + +`available` means candidates or an existing connection are present. It proves neither that the target is currently online nor that Host credentials will be accepted. + +Implementation: [Peer client](../../packages/runtime-host/src/client/peer-client.ts), [engine](../../native/runtime-host-peer/src/engine.rs), [WebRTC signaling](../../native/runtime-host-peer/src/webrtc_direct/signaling.rs), [WebRTC upgrade](../../native/runtime-host-peer/src/webrtc_direct/upgrade.rs). + +## 7. Separate route-resolution and reconnect state machines + +### 7.1 Route state + +| `RuntimeHostPeerRouteResolution.state` | Meaning | Consumer behavior | +|---|---|---| +| `available` | Candidates exist or the Peer is connected | Connection may be attempted, without a success guarantee | +| `recovering` | Preparation is underway or recovery sources remain | Await bounded recovery and accept candidate updates | +| `exhausted` | The current sweep found no candidates or further recovery basis | End this attempt with `peer_reachability_needs_repair` | + +`PeerMeshNode.prepareRoutes()` changes recovery state when a sweep begins and ends even if candidates remain empty. These changes are progress within one attempt, not external triggers for the next retry. + +### 7.2 Connection lifecycle + +An active dial subscribes to complete route resolution so it can update candidates or cancel on exhaustion. The outer reconnect owner receives only actual availability signals, such as a changed nonempty candidate set or restored Peer connectivity. Losing the last candidate, duplicate candidates, and self-induced `recovering`/`exhausted` transitions do not wake the outer retry. + +Combining these subscription contracts would let a failed sweep wake itself and bypass backoff. Reconnect retains independent bounded exponential backoff; fresh route evidence can wake it early. `peer_reachability_needs_repair` describes the current path, not permanent credential revocation. Explicit authentication/compatibility failures or native endpoint termination follow their own terminal contracts. + +Desktop maintains one current diagnostic per continuous outage: failed-attempt count, first/last failure times, and latest error. It logs the start and successful recovery, not every alternating dial error. Guest state changes cannot invalidate Owner new-task catalogs. UI `needs_repair`, connection `reconnecting`, and effective Session grants are three independent judgments. + +Implementation: [Mesh route notifications](../../packages/runtime-host/src/peer-mesh/node.ts), [filtered reconnect notification](../../packages/runtime-host/src/client/peer-client.ts), [reconnect lifecycle](../../packages/runtime-host/src/client/reconnect-lifecycle.ts), [Desktop owner](../../apps/desktop/src/main/runtime-host-desktop-manager.ts). + +## 8. Path recovery and logical byte streams + +`ResumablePeerStream` maintains a process-local reliable byte stream between two surviving endpoints: send/receive offsets, ACKs, a finite retransmission window, and attachment generations. On physical path loss or upgrade, a new attachment uses the same logical stream session identity, discards duplicate bytes, and retransmits unacknowledged data. + +For resume, the Host listener revalidates credentials, principal kind/ID, credential ID, actual remote PeerId, and session/generation/offset. An old attachment cannot replace a newer one. Revoked authority cannot survive through resume. If the original session is absent, resume is rejected instead of silently creating another Host connection for the old bytes. + +The current window is 2 MiB, chunks are 64 KiB, and recovery lasts at most 30 seconds. Retransmission within the ACK window is not business idempotency; the stream neither interprets nor replays Host requests. FIN/FIN_ACK and cancellation/close have distinct convergence paths. Normal completion must not be treated as a recoverable network failure. + +Process exit, recovery timeout, or lost retained state ends the logical stream. The upper layer establishes a new Host connection, rechecks root/composition/HostEpoch, and rebuilds subscriptions. Unknown outcomes of dispatched commands remain a Domain reconciliation problem; byte deduplication cannot become exactly-once execution across process restarts. + +Implementation: [resumable stream](../../packages/runtime-host/src/transport/resumable-peer-stream.ts), [Host peer listener](../../packages/runtime-host/src/server/peer-listener.ts), [Host connection](../../packages/runtime-host/src/client/connection.ts). + +## 9. Host and Session authorization + +A connection must satisfy successive boundaries, not select one of them: + +```text +Peer identity / path admission + -> Host credential verification + -> root and protocol/composition checks + -> connection principal and explicit operation grants + -> specific Session grant / Domain admission +``` + +A Mesh invitation permits joining a Mesh, an Owner connection code configures Host access, and a Session collaboration invitation grants limited access to a particular shared Session. They are not interchangeable invitation formats. Clients may retain multiple network relationships and independent Guest mounts without importing membership into the Owner profile list. + +Guest mount disconnection must be distinguished from credential rejection or Session access failure. Disconnection retains access data for recovery. Access failure removes or blocks shared content according to the durable decision. Network unreachability must not revoke credentials, and restored networking cannot undo an effective rejection. + +Mesh control traffic owns neither Host Sessions, Runs, Project filesystems, nor Client Capabilities. Even a future network service catalog would still require independent provider binding, policy, and effect admission. Cross-Host delegation would still require durable work identity, results, and cancellation protocols. + +Implementation: [access authority](../../packages/runtime-host/src/server/access-authority.ts), [collaboration protocol](../../packages/runtime-host/src/protocol/session-collaboration.ts), [Guest mounts](../../apps/desktop/src/main/runtime-host-guest-session-mounts.ts), [Host architecture](./runtime-host-architecture.md). + +## 10. Resource budgets and failure model + +Limits belong to separate authorities rather than one global connection count. Representative values at the verified baseline follow; changes must consider decoders, native policy, and contract tests together. + +| Scope | Current limit/constraint | Prevents | +|---|---|---| +| Mesh membership | 64 members per Mesh; 16 local Meshes | Unbounded membership and state merging | +| Mesh control | 128 KiB frames; 32 active streams, 2 per Peer | Unbounded control-plane memory/concurrency | +| Host peer admission | 16 pending authentications; 256 streams, 160 per Peer, 4 per principal | Unauthenticated or single-principal Host exhaustion | +| Native transit | 32 reservations, 8 circuits, 2 circuits per Peer | A member exhausting forwarding slots | +| One transit circuit | 2 hours, 256 MiB | Unbounded forwarding time/bandwidth | +| Resumable stream | 2 MiB window, 64 KiB chunks, 30-second recovery | Unlimited buffering and indefinite suspension | + +Capacity exhaustion is a resource error, not proof of invalid credentials. Tightening quotas or failing Peer transport cannot silently change grants. Data and control planes have their own budgets; timeout, cancellation, and close release the associated resources. + +| Failure | Required invariant | +|---|---| +| Expired/replayed address lease | Do not extend freshness; use only as recovery evidence within the allowed horizon | +| Unavailable authority | Do not fabricate membership changes; assess existing data paths independently | +| Public coordination works but direct fails | Do not promote the public relay to application transit | +| Approved relay disappears | Select another eligible path or fail; do not expand the relay allowlist | +| Peer connected but credential revoked | Reject Host admission/resume and close affected streams | +| Repeated empty route recovery | Preserve backoff, mounts and current diagnostics; await actual new evidence | +| Physical stream breaks | Bounded byte resume; then new connection/Domain recovery beyond that boundary | +| Native endpoint process terminates | Do not resurrect process-local streams; follow endpoint/Client lifecycle recovery | +| Every locator disappears | Require fresh evidence; do not promise lookup from PeerId alone | + +Implementation: [Mesh limits](../../packages/runtime-host/src/peer-mesh/limits.ts), [Mesh control](../../packages/runtime-host/src/peer-mesh/node.ts), [native limits](../../native/runtime-host-peer/src/engine.rs), [Host quotas](../../packages/runtime-host/src/server/peer-listener.ts). + +## 11. Trade-offs and extension boundaries + +| Choice | Reason | Cost/non-guarantee | +|---|---|---| +| Separate identity, membership and locators | Address changes preserve identity; network relationships do not expand permissions | Durable membership cannot guarantee reachability | +| One roster-signing Mesh authority | Membership changes have an explicit issuer and revision | Control is limited while authority is offline; not leaderless consensus | +| Separate public coordination and private transit | Reuse public hole-punch infrastructure while restricting application carriers | Some NATs cannot connect without direct paths or eligible transit | +| One logical connect attempt with bounded internal races | Improve path selection without copying business requests | Requires loser cancellation, concurrency limits and candidate update checks | +| Process-local stream resume | Isolate brief path changes from the Host protocol | Does not solve Host restarts, effect idempotency or state replication | +| Outage diagnostic snapshots | Offline retries do not consume log capacity | Retains current summaries rather than every dial's full trace | + +The current system does not provide a general VPN, reachability through arbitrary NATs, a permanent global PeerId directory, general TURN relaying, a Mesh model/tool/compute catalog, a cross-Host scheduler, or multi-writer State Roots. Future business protocols can build on networking, but must define their own authorities, durable intent, permissions, and failure semantics. They are not implicit capabilities of membership or byte streams. + +## 12. Implementation and verification entry points + +| Contract | Test entry point | +|---|---| +| Signed rosters, membership, transit and locator recovery | [peer-mesh](../../packages/runtime-host/src/__tests__/peer-mesh.test.ts) | +| Lease freshness, receipts and recovery horizon | [peer-reachability](../../packages/runtime-host/src/__tests__/peer-reachability.test.ts) | +| Candidate changes, empty recovery sweeps and native bridge | [peer-native](../../packages/runtime-host/src/__tests__/peer-native.test.ts) | +| Byte offsets, resume, deduplication and close | [resumable-peer-stream](../../packages/runtime-host/src/__tests__/resumable-peer-stream.test.ts) | +| Host credentials, resume identity and quotas | [peer-listener](../../packages/runtime-host/src/__tests__/peer-listener.test.ts) | +| Guest grants and Peer access | [peer-session-collaboration](../../packages/runtime-host/src/__tests__/peer-session-collaboration.test.ts) | +| Guest/Owner recovery isolation and outage summaries | [Desktop manager](../../apps/desktop/src/main/__tests__/runtime-host-desktop-manager.test.ts), [preload catalog](../../apps/desktop/src/main/__tests__/runtime-host-new-task-preload.test.ts), [copied diagnostics](../../apps/desktop/src/main/__tests__/main-process-diagnostics.test.ts) | +| Native application path policy | [application stream tests](../../native/runtime-host-peer/src/engine/application_stream.rs) | +| WebRTC upgrade | [WebRTC tests](../../native/runtime-host-peer/src/webrtc_direct/tests.rs) | + +Tests verify protocol and state-machine contracts, not availability across every real NAT, public relay, network transition, and OS. Product reachability validation additionally needs actual paths, Peer identities, deployment configuration, and network conditions. A local success cannot establish global reliability. diff --git a/docs/architecture/peer-mesh-architecture.zh-CN.md b/docs/architecture/peer-mesh-architecture.zh-CN.md new file mode 100644 index 0000000000..8009cf64d5 --- /dev/null +++ b/docs/architecture/peer-mesh-architecture.zh-CN.md @@ -0,0 +1,256 @@ +--- +doc_id: architecture.peer-mesh +title: "Peer Mesh 架构" +language: zh-CN +source_language: zh-CN +counterpart: ./peer-mesh-architecture.md +implementation_status: current +document_status: current +translation_status: synced +last_verified: 2026-09-09 +owners: + - maka-backend +--- + + +[English](./peer-mesh-architecture.md) + +# Peer Mesh 架构 + +## 1. 架构契约与范围 + +本文回答:**Maka 如何在端点地址和网络路径变化时,维持可验证的 Peer 关系,并为上层 Host 协议提供可恢复的连接?** 面向网络、Client 与 Host 接入层开发者,定义身份、成员状态、地址证据、路径准入和恢复边界。 + +实现基线为 `09c73430c`(2026-09-09)。本文描述当前实验性 Peer 实现,不把博客中的未来跨设备工作形态当成已经存在的协议。Host 执行、权限与存储契约见 [Runtime Host 架构](./runtime-host-architecture.zh-CN.md)。 + +Peer Mesh 提供持久的私有成员关系、受验证的 reachability 交换,以及基于这些事实生成的连接候选。Native Peer transport 提供认证过的 stream。两者不是通用 VPN、Host credential authority、分布式 State Root 或跨 Host scheduler。独立 Direct Peer 连接也可通过显式连接信息建立,不要求先加入 Mesh。 + +下面按层从下向上读取;箭头表示下层提供能力,不表示下层授予上层权限。 + +```mermaid +flowchart BT + T["Native transport: direct / approved transit"] --> S["Authenticated resumable byte stream"] + S --> H["Host credential and protocol admission"] + H --> G["Session / operation grants"] + M["Signed Mesh membership"] --> R["Verified reachability and route candidates"] + R --> T +``` + +## 2. 组件与持久化边界 + +| 组件 | 拥有的状态/转换 | 输出 | +|---|---|---| +| Native endpoint | Peer identity、libp2p 连接、打洞、stream 与 transport quota | 已认证 peer stream、connectivity/reachability snapshot | +| `RuntimeHostPeerEndpointOwner` | 同一 endpoint data root 的进程生命周期、native client 和 publisher | 一个受文件生命周期 owner 保护的 endpoint | +| Reachability publisher | 本地签名 lease 的 revision、地址集合与刷新 | 可验证的地址证据 | +| `PeerMeshNode` / Mesh store | Mesh authority、签名 roster、邀请、member advertisements、收到的 reachability | 成员视图、route resolution、transit policy | +| `RuntimeHostPeerClient` | TS/native bridge、目标绑定的 connect attempt、候选更新 | application 或 mesh-control stream | +| `ResumablePeerStream` | 进程内 byte offsets、ACK、attachment generation、恢复窗口 | 路径变化时保留的逻辑字节流 | +| Host peer listener | credential/principal 准入、resume attachment 校验和 quota | 进入通用 Host dispatcher 的连接 | +| Desktop reconnect owner | Host/Guest 目标生命周期、退避、当前故障诊断 | Ready/reconnecting/unavailable 状态 | + +PeerId 属于网络 endpoint,不必与一台物理机器一一对应。Desktop Client endpoint 与 Host endpoint 可以有不同身份。Network key、Mesh authority key、Host credential、rootId 和 HostEpoch 不能互换。 + +持久化保留 identity、membership、invitation/advertisement/reachability 及必要的恢复证据;活动 socket、拨号 Promise、ACK window 和 stream attachment 是进程内状态。保存了连接信息不意味着已经保存了一个可继续的运行中连接。 + +实现:[endpoint owner](../../packages/runtime-host/src/peer-reachability/owner.ts)、[Mesh owner](../../packages/runtime-host/src/peer-mesh/owner.ts)、[Mesh node](../../packages/runtime-host/src/peer-mesh/node.ts)、[native bridge](../../packages/runtime-host/src/transport/peer-native.ts)、[native engine](../../native/runtime-host-peer/src/engine.rs)。 + +## 3. 身份、成员关系与发现信息 + +### 3.1 三种签名事实 + +| 事实 | 签发者与关键内容 | 证明什么 | +|---|---|---| +| Mesh roster | Mesh authority key;meshId、authorityPeerId、revision、members、closed | 谁属于这个 Mesh | +| Member advertisement | Member identity;meshId、peerId、revision、endpointKind、displayName、offersTransit | 成员公布的端点角色、显示信息和转发意愿 | +| Reachability lease | 对应 Peer identity;peerId、revision、issuedAt/expiresAt、direct/coordination routes | 这些地址证据由哪个 Peer 发布及其有效窗口 | + +Mesh ID 与 authority key 绑定。Authority 的签名角色不同于实际承载连接的 PeerId;成员修改要通过 authority 接受并传播新的 roster。有效连接并不能自行增加成员,IP 或 relay 观察到某个地址也不能替目标签发 reachability。 + +Advertisement 是有限的网络/端点元数据。当前没有模型、GPU、工具或工作容量的 Mesh 服务目录;`endpointKind: host` 不是 Host 操作授权,`offersTransit` 也不是允许任意人使用 relay 的 bearer token。 + +### 3.2 成员生命周期 + +创建 Mesh 建立 authority 与初始 roster;邀请有有效期和数量限制。加入者验证 invitation 与 authority,完成加入协议并保留签名 membership。移除成员或关闭 Mesh 产生新的 authority revision。节点通过控制 stream 交换已验证状态并协调更新;离线成员稍后恢复时不能仅靠旧 roster 撤销已经收到的新决定。 + +Authority 是成员变更的控制点,不要求所有 application bytes 都经 authority 中转。Authority 离线可能阻止新的加入或成员修改,但已有成员间的有效路径和 Host grants 是不同问题。 + +移除成员会重新计算相关 Mesh 的发现/transit 资格。不能把“离开一个 Mesh”自动翻译为“撤销所有 Host Session grants”,也不能忽略另一个仍有效的共享 Mesh。授权回收由对应的 authority 执行。 + +实现:[signed models](../../packages/runtime-host/src/peer-mesh/model.ts)、[Mesh protocol](../../packages/runtime-host/src/protocol/peer-mesh.ts)、[membership coordination](../../packages/runtime-host/src/peer-mesh/node.ts)。 + +## 4. Reachability:地址证据不是永久定位器 + +签名 lease 校验 peer identity、revision、时间边界和 route shape。收到 lease 后建立基于单调时钟的有效期 receipt;再次收到同一 revision/signature 不延长它的有效生命,wall-clock 回拨也不能让旧证据永久有效。 + +当前 lease 默认有效 5 分钟,接受的最大生命周期为 10 分钟,允许的时钟偏差为 2 分钟。过期记录在 24 小时 recovery horizon 内仍可作为恢复线索;“可用证据”和“可以尝试恢复的历史线索”不等价。这些是当前实现限额,不是外部可依赖的永久服务承诺。 + +节点可通过仍可达的成员、已有地址/协调节点及保存的线索获取新的签名信息。Reconcile 负责有界交换与收敛,不复制 Host 的业务状态,也不把重复通知变成新的事实。 + +若所有旧地址、共同在线成员和可用协调入口都消失,知道 PeerId 只能验证找到的是谁,不能推导它的新地址。此时需要新的 invitation/连接信息或重新出现的可达入口。Membership 可以长期保留,而 route resolution 进入 `exhausted`;两者不矛盾。 + +实现:[reachability model](../../packages/runtime-host/src/peer-reachability/model.ts)、[publisher](../../packages/runtime-host/src/peer-reachability/publisher.ts)、[route recovery](../../packages/runtime-host/src/peer-mesh/node.ts)。 + +## 5. 路径类别与转发准入 + +| 路径 | 用途 | 授权/生命周期 | +|---|---|---| +| Direct | 在目标 Peer 间直接承载 stream | 验证实际远端 PeerId;网络路径可变化 | +| Coordination relay | 协助发现、连接建立和打洞信令 | 公共协调节点不因此获得 application transit 资格 | +| Approved Mesh transit | 由符合当前 Mesh 资格和转发策略的成员提供一跳中转 | 同时满足 membership、advertisement 与本地准入策略;端到端认证/加密保留 | + +节点只有显式选择提供 transit 的 Mesh 后才提供这类服务,默认不转发。客户端的 approved relay 集合来自验证后的 Mesh 证据与 policy。Native application protocol 检查具体连接是否 direct 或经过受认可的 transit relay;不能因为某条 Circuit Relay v2 连接已经存在,就在公共协调 relay 上发送 Host application traffic。 + +Transit 有 reservation、circuit、per-peer、字节和时间上限,不是无限带宽承诺,也不递归构建任意多跳网关。Relay 可观察网络连接与流量特征、影响可用性;它不取得端点的 Host credential authority,也不成为明文 Session 的处理者。 + +Automatic relay discovery 为当前 native libp2p 路径发现公共协调候选;保存 relay anchors 有助于后续恢复,但不是保证可用的 Maka 中央目录。配置明确的协调入口可以改变依赖来源,无法消除受限 NAT 下对可达第三方的需要。 + +实现:[transit policy](../../packages/runtime-host/src/peer-mesh/node.ts)、[native application admission](../../native/runtime-host-peer/src/engine/application_stream.rs)、[relay discovery](../../native/runtime-host-peer/src/engine/relay_discovery.rs)、[native limits](../../native/runtime-host-peer/src/engine.rs)。 + +## 6. Direct transport、WebRTC 与连接尝试 + +Native endpoint 的 application PeerId 统一用于 libp2p transport;QUIC、TCP/Noise/Yamux、DCUtR 和可选 WebRTC 在这一身份下提供连接路径。辅助 relay discovery 可以有自己的内部 Swarm,不能把“一份 application identity”理解为进程中只有一个 Swarm 对象。 + +WebRTC 是额外的 direct path: + +1. 已认证的协调连接承载 `/webrtc-signaling/0.0.1`,交换 SDP 和 Trickle ICE。 +2. STUN 用于发现 server-reflexive ICE 地址;它不提供 membership、Host credential 或 Session grant。 +3. ICE 检查选择可用 candidate pair,WebRTC upgrade 验证预期 peer 与握手信息。 +4. 成功得到可用于目标 Peer 的 direct connection,再进入相同的 application admission。 + +DCUtR 与 WebRTC signaling 是不同协议。前者协调 libp2p 直连打洞,不把 relay 观察到的 TCP/QUIC 地址自动转换成 WebRTC ICE candidates。在 native options 边界,没有提供 WebRTC 参数时路径关闭;提供空 STUN 列表时不使用外部 STUN,不能保证获得 NAT 外部地址。当前不是通用 TURN fallback 服务。 + +一个 connect attempt 固定目标 PeerId 和 request identity,收集/更新候选,带 deadline 与取消。Direct 优先,approved transit 可在短延迟后加入;stream-open 可做有界 hedge。当前 transit fallback 与 hedge 延迟均为 250 ms,最多两个并行 stream opens。竞争发生在连接/stream 建立层,选出结果后关闭其他尝试,绝不并行发送同一个业务 mutation 来竞争响应。 + +`available` 只说明有候选或已有连接,不证明目标当前在线,更不证明 Host credential 可用。 + +实现:[Peer client](../../packages/runtime-host/src/client/peer-client.ts)、[engine](../../native/runtime-host-peer/src/engine.rs)、[WebRTC signaling](../../native/runtime-host-peer/src/webrtc_direct/signaling.rs)、[WebRTC upgrade](../../native/runtime-host-peer/src/webrtc_direct/upgrade.rs)。 + +## 7. Route resolution 与 reconnect 的两个状态机 + +### 7.1 路由状态 + +| `RuntimeHostPeerRouteResolution.state` | 含义 | 消费者行为 | +|---|---|---| +| `available` | 存在候选,或 Peer 已连接 | 可尝试建连,不保证成功 | +| `recovering` | 正在准备或仍有恢复来源 | 等待有界恢复,接受候选更新 | +| `exhausted` | 当前检查无候选且没有继续恢复的依据 | 结束本次尝试,返回 `peer_reachability_needs_repair` | + +`PeerMeshNode.prepareRoutes()` 会在检查开始/结束时改变恢复状态,即使前后都没有任何候选。这些变化是一次尝试的进度,不能充当下一次重试的外部触发。 + +### 7.2 连接生命周期 + +正在进行的 dial 订阅完整 route resolution,以便更新候选或在 exhausted 时取消。外层 reconnect owner 只接收“候选变为不同的非空集合”或“Peer 连接恢复”等实际可用性信号。最后一个候选消失、重复候选、以及 `recovering`/`exhausted` 自身切换都不唤醒外层重试。 + +这两个订阅契约不能合并:否则一次失败的检查会唤醒自己、跳过 backoff。Reconnect 保留独立的有界指数退避;新的路由证据可提前唤醒。`peer_reachability_needs_repair` 是当前路径状态,不是永久撤销 credential 的证据。明确的认证/兼容性或 native endpoint 终止错误,按自己的 terminal contract 处理。 + +Desktop 对一个持续断线周期只保留一个当前诊断:失败次数、首次/最近时间和最新错误。开始和成功恢复各记录一条日志,重试中的错误交替不逐次追加。Guest 状态更新不能作废 Owner 新任务 catalog。UI 的 `needs_repair`、连接 `reconnecting` 和 Session grant 是否有效是三个独立判断。 + +实现:[Mesh route notifications](../../packages/runtime-host/src/peer-mesh/node.ts)、[filtered reconnect notification](../../packages/runtime-host/src/client/peer-client.ts)、[reconnect lifecycle](../../packages/runtime-host/src/client/reconnect-lifecycle.ts)、[Desktop owner](../../apps/desktop/src/main/runtime-host-desktop-manager.ts)。 + +## 8. 路径恢复与逻辑字节流 + +`ResumablePeerStream` 在两个仍存活的 endpoint 之间维持进程内可靠字节流:发送 offset、接收 offset、ACK、有限重传 window 和 attachment generation。物理路径中断或升级时,以相同 logical stream session identity 建立新 attachment,去除重复 bytes 并补发未确认部分。 + +Host listener 对 resume 再验证 credential、principal kind/ID、credential ID、实际远端 PeerId,以及 session/generation/offset。旧 attachment 不能覆盖新 attachment;authority 已撤销时不能通过 resume 继续使用旧权限。找不到原 session 时拒绝 resume,不能悄悄创建另一个 Host connection 来承接旧 bytes。 + +当前 window 为 2 MiB、chunk 为 64 KiB、恢复窗口为 30 秒。ACK window 内的重传不是业务幂等层;stream 不解析也不重放 Host requests。FIN/FIN_ACK 与取消/关闭有独立收敛路径,正常结束不能被当成需要恢复的网络故障。 + +当进程退出、恢复窗口耗尽或 retained stream state 不存在时,逻辑 stream 结束。上层建立新的 Host connection,重新校验 root/composition/HostEpoch 并重建 subscription。已发送 command 的未知结果仍交给 Domain 协调;byte dedup 不能扩展成跨进程 exactly-once 执行。 + +实现:[resumable stream](../../packages/runtime-host/src/transport/resumable-peer-stream.ts)、[Host peer listener](../../packages/runtime-host/src/server/peer-listener.ts)、[Host connection](../../packages/runtime-host/src/client/connection.ts)。 + +## 9. Host 与 Session 授权边界 + +建立连接需要逐层满足,而不是任选一项: + +```text +Peer identity / 路径准入 + -> Host credential 验证 + -> root 与 protocol/composition 校验 + -> connection principal 和显式 operation grants + -> 具体 Session grant / Domain admission +``` + +Mesh invitation 授予加入 Mesh 的机会,Host Owner connection code 配置 Host 访问,Session collaboration invitation 授予特定共享 Session 的受限访问;三者不是同一种邀请码。Client 可以保留多个网络关系和独立的 Guest mounts,不能把 membership 自动导入 Owner profile 列表。 + +Guest mount 的离线状态与 credential rejection / Session access failure 必须区分。前者保留授权数据等待恢复;后者按持久访问决定清理或拒绝继续展示。网络不可达不应触发 credential revocation,网络重新连通也不能撤销已经生效的拒绝。 + +Mesh control traffic 不拥有 Host 的 Session、Run、Project filesystem 或 Client Capability。即使将来增加网络服务发现,实际 capability 调用仍需独立的 provider binding、policy 与 effect admission;跨 Host delegation 仍需持久的工作身份、结果与取消协议。 + +实现:[access authority](../../packages/runtime-host/src/server/access-authority.ts)、[collaboration protocol](../../packages/runtime-host/src/protocol/session-collaboration.ts)、[Guest mounts](../../apps/desktop/src/main/runtime-host-guest-session-mounts.ts)、[Host architecture](./runtime-host-architecture.zh-CN.md)。 + +## 10. 资源预算与故障模型 + +限额按不同 authority 分层,不能仅靠一个全局 connection count。以下为所验证基线的代表值,修改时同时检查 decoder、native policy 和契约测试: + +| 范围 | 当前限额/约束 | 防止的失控 | +|---|---|---| +| Mesh membership | 每 Mesh 64 members;本地最多 16 Meshes | 无界成员和状态合并 | +| Mesh control | 128 KiB frame;32 active streams、每 Peer 2 个 | 控制面内存/并发失控 | +| Host peer admission | 16 pending authentications;256 streams、每 Peer 160、每 principal 4 | 尚未授权或单一 principal 占满 Host | +| Native transit | 32 reservations、8 circuits、每 Peer 2 circuits | 一个成员耗尽转发槽位 | +| 单 transit circuit | 2 小时、256 MiB | 无界转发时间与带宽 | +| Resumable stream | 2 MiB window、64 KiB chunk、30 秒恢复 | 无限缓存与永久悬挂 | + +容量不足是资源错误,不是 credential 无效的证明。收紧配额或让 Peer transport 失败不能偷偷改变 grants。数据面和控制面使用各自的 budget,超时、取消和关闭需要释放对应资源。 + +| 故障 | 必须保持的性质 | +|---|---| +| Address lease 过期/重放 | 不延长 freshness;在允许的 horizon 内仅作为恢复线索 | +| Authority 离线 | 不伪造成员变更;已有成员的数据路径独立判断 | +| Public coordination relay 可用但 direct 失败 | 不自动把公共 relay 变成 application transit | +| Approved relay 丢失 | 重新选择合格路径或失败;不能扩大 relay allowlist | +| Peer 已连接但 credential 被撤销 | Host 拒绝新 admission/resume,清理受影响 stream | +| 路由恢复反复无结果 | 正常退避,保留 mount 与当前诊断,等待真实新证据 | +| 物理 stream 中断 | 有界 byte resume;超过边界后交给新连接/Domain recovery | +| Native endpoint 进程终止 | 不能复活进程内 stream;按 endpoint/Client 生命周期恢复 | +| 所有定位入口消失 | 明确需要新线索;不承诺只凭 PeerId 可以找到目标 | + +实现:[Mesh limits](../../packages/runtime-host/src/peer-mesh/limits.ts)、[Mesh control](../../packages/runtime-host/src/peer-mesh/node.ts)、[native limits](../../native/runtime-host-peer/src/engine.rs)、[Host quotas](../../packages/runtime-host/src/server/peer-listener.ts)。 + +## 11. 设计取舍与扩展边界 + +| 选择 | 理由 | 代价/不能推出的结论 | +|---|---|---| +| Identity、membership、locator 分开 | 地址变化不改变成员身份,网络关系不自动扩权 | 持久 membership 不能保证目标可达 | +| 一个 Mesh authority 签 roster | 成员变更有明确签发者与 revision | Authority 离线时控制面受限;不是无 leader 共识系统 | +| 公共 coordination 与私有 transit 分开 | 可利用公共打洞基础设施,同时限制谁承载 application traffic | 某些 NAT 下没有 direct 或合格 transit 就不能连接 | +| 一个逻辑 connect attempt,内部有限竞争 | 改善路径选择而不复制业务请求 | 需要取消 loser、限制并行与检查候选更新 | +| 进程内 stream resume | 隔离短暂路径变化与 Host 协议 | 不解决 Host 重启、业务 effect 幂等或状态复制 | +| Outage diagnostic snapshot | 离线重试不侵占日志容量 | 诊断保留当前概要,不保留每次拨号的完整轨迹 | + +当前不提供通用 VPN、任意 NAT 的必达保证、全网永久 PeerId 目录、通用 TURN relay、Mesh 模型/工具/算力目录、跨 Host scheduler 或多写 State Root。未来可以基于网络层建立这些业务协议,但必须另行定义 authority、durable intent、权限和 failure semantics,不能把它们当成 membership 或 byte stream 的隐含能力。 + +## 12. 实现与验证入口 + +| 契约 | 测试入口 | +|---|---| +| 签名 roster、membership、transit 与 locator 恢复 | [peer-mesh](../../packages/runtime-host/src/__tests__/peer-mesh.test.ts) | +| Lease freshness、receipt 与 recovery horizon | [peer-reachability](../../packages/runtime-host/src/__tests__/peer-reachability.test.ts) | +| 候选变化、空恢复检查与 native bridge | [peer-native](../../packages/runtime-host/src/__tests__/peer-native.test.ts) | +| Byte offset、resume、去重与关闭 | [resumable-peer-stream](../../packages/runtime-host/src/__tests__/resumable-peer-stream.test.ts) | +| Host credential、resume 身份与 quota | [peer-listener](../../packages/runtime-host/src/__tests__/peer-listener.test.ts) | +| Guest grant 与 Peer 接入 | [peer-session-collaboration](../../packages/runtime-host/src/__tests__/peer-session-collaboration.test.ts) | +| Guest/Owner 恢复隔离与断线汇总 | [Desktop manager](../../apps/desktop/src/main/__tests__/runtime-host-desktop-manager.test.ts)、[preload catalog](../../apps/desktop/src/main/__tests__/runtime-host-new-task-preload.test.ts)、[copied diagnostics](../../apps/desktop/src/main/__tests__/main-process-diagnostics.test.ts) | +| Native application path policy | [application stream tests](../../native/runtime-host-peer/src/engine/application_stream.rs) | +| WebRTC upgrade | [WebRTC tests](../../native/runtime-host-peer/src/webrtc_direct/tests.rs) | + +测试能验证协议和状态机契约,不能证明所有真实 NAT、公共 relay、网络切换与 OS 组合都可用。验证产品可达性时需要额外记录实际路径、Peer 身份、部署配置和网络条件;不能从一个局部成功推导全网可靠性。 diff --git a/docs/architecture/runtime-host-architecture.md b/docs/architecture/runtime-host-architecture.md index d6158e7caf..5f3278dc8d 100644 --- a/docs/architecture/runtime-host-architecture.md +++ b/docs/architecture/runtime-host-architecture.md @@ -7,7 +7,7 @@ counterpart: ./runtime-host-architecture.zh-CN.md implementation_status: current document_status: current translation_status: synced -last_verified: 2026-08-12 +last_verified: 2026-09-09 owners: - maka-backend --- @@ -30,296 +30,321 @@ owners: under the License. --> +[中文](./runtime-host-architecture.zh-CN.md) + # Runtime Host architecture -> Runtime Host is the long-lived process that owns one State Root and the Runtime work using it. Desktop, TUI, CLI, bots, and evaluation code are Clients. They ask the Host to do work; they do not own a second Runtime. +## 1. Architecture contract and scope + +This document answers: **How do multiple entry points and connections share execution in one State Root while preserving ownership across concurrency, disconnection, process exit, and upgrades?** It specifies component responsibilities, persistence boundaries, state transitions, and failure semantics for developers maintaining the Host, extending a Domain, or implementing a Client. -This chapter explains the stable boundaries needed to maintain Runtime Host or connect a product feature to it. It does not repeat individual protocol schemas or coordinator internals. +The implementation baseline is `09c73430c` (2026-09-09). Unless explicitly marked historical or unsupported, statements describe that baseline's current implementation; Peer functionality remains experimental. Linked schemas, implementation, and tests own protocol fields and resource limits. See [Peer Mesh architecture](./peer-mesh-architecture.md) for networking and [Runtime resume](./runtime-resume-architecture.md) for execution recovery algorithms. -In this document, an **owner** or **authority** is the only component allowed to make a particular state transition while the Host is online. It is not necessarily the Client or user that initiated the work. +A State Root has at most one writer Host at a time. Desktop, TUI, CLI, Bot, and Eval are entry points or adapters; they do not create another Runtime owning the same work. Execution control, business decisions, storage, and observation remain separate responsibilities inside the Host. Calling the Host an authority does not give its Kernel authority to interpret every business state. -## Why Runtime Host exists +Read the diagram from Client downward. It describes calls and ownership boundaries, not a mandatory path through every node for every request. Transports, deployment, and the full Domain inventory are omitted. -Runtime work outlives a request connection. A model call may continue after a Desktop window reloads, an authenticated remote Client may disconnect, and a process may restart while durable work is active. The State Root is the directory containing the durable state that must survive those events. +```mermaid +flowchart TD + C["Desktop / TUI / CLI / Bot / Eval"] --> K["Host Kernel: admission and connection authority"] + K --> D["Domain operation owners"] + D --> A["RootTurnCoordinator: HostedExecutionAuthority"] + A --> R["SessionManager / AgentRun / Runtime"] + R --> E["RuntimeEvents and control stores"] + E --> P["Session Continuity: canonical projections"] + P --> C + R --> B["Client Capability broker"] + B --> C +``` -If each Client owns its own Runtime and recovery path, the system gains multiple writers, conflicting Session state, and connection-dependent execution. Runtime Host removes that ambiguity: +## 2. Authorities and identities -- one process owns writes for one State Root; -- Local IPC and authenticated WebSocket use the same durable state; -- business code decides what work means; -- one execution authority admits and stops top-level Session work, tracks its final result, and waits for cleanup; -- one model catalog describes what a Connection's models are and can do. The Host resolves each model from the stored row and its own model metadata and projects the result; Clients render that projection. A Client resolves a catalog itself only where the Host has no state to resolve against — a provider not yet added, or an editor draft not yet saved. +### 2.1 Ownership boundaries -## Parts in plain language +| Authority | Decides | Does not decide | +|---|---|---| +| Storage Root owner | Which process may write this root | Client permissions, installed version, business success | +| Host Kernel | Connection admission, request routing, process retention, drain and close | Turn semantics, model selection, scheduling policy | +| Host Composition | Fixed dependency graph, Module set, recovery and close order | Runtime plugin discovery, dynamic per-Session configuration | +| Domain Module | Its operation semantics, business state, recovery policy | Starting a second Runtime outside shared root admission | +| `RootTurnCoordinator` | Implements `HostedExecutionAuthority`: admit, stop, and recover a Session's root execution | How a Goal or Scheduled Task interprets its result | +| Runtime / AgentRun | Model and tool steps, events, execution and continuation | Discovery, the Client's default Host, installation management | +| Session Continuity | Snapshots, transcripts, and live projections from canonical facts | Inferring completion from notifications or resending Client commands | +| Client Capability | Provider selection, bounded reverse calls and uncertain outcomes | Transferring Session/Run ownership to a Client | +| Deployment owner | Service configuration, installation, activation and replacement coordination | Acquiring the State Root writer lease from an installation record | -| Part | Plain meaning | -|---|---| -| Host Kernel | The process gate: owns the State Root's exclusive lease and connections, stops new work, and shuts the process down | -| Host Composition | The fixed startup recipe: creates Stores, shared authorities, and the Module list | -| Domain Module | A static record assigning a group of protocol operations and startup/shutdown duties to one owner | -| Hosted Execution | The traffic controller for top-level Session work: accepts one exact execution, stops or recovers it, and distinguishes its final result from finished cleanup | -| Run Composer | Records the unchanging prompt and tool basis before a provider call | -| Session Continuity | Gives Clients a canonical Session snapshot plus size-limited live updates | -| Client Capability | Lets the Host invoke a capability published by a connected Client without transferring Runtime ownership | +`hosted-execution-coordinator.ts` and `hosted-execution-runner.ts` coordinate/adapt external execution calls; `RootTurnCoordinator` is the actual shared root authority. New entry points reuse the public execution contract rather than creating an execution owner based on a filename. + +### 2.2 Identities are not interchangeable + +| Identity | Scope and meaning | Changes when | +|---|---|---| +| `rootId` | Durable State Root identity; Host identity in the protocol | Explicit creation, import or repair rules apply; it is not a path string | +| `HostEpoch` | Current Host process instance | The Host process restarts | +| Composition ID | Program composition allowed to interpret the root | Explicit persistent binding changes, not an ordinary upgrade | +| Composition Revision | Composition revision checked by Clients | Composition evolves; this is not merely a diagnostic label | +| Protocol version / compatibility epoch | Wire-contract compatibility | Protocol contracts evolve | +| Host Generation | Runtime version/development generation requested by a local owner | Product upgrade or development launch; separate from protocol version | +| `targetEpoch` | A Desktop connection target's lifecycle generation | Target replacement fences old requests and callbacks | +| Profile ID / incarnation | Client connection configuration and its persistent instance | Configuration replacement, credential and local partition rules apply | +| PeerId | Cryptographic identity of a network endpoint | Network identity keys change; not an IP, rootId or HostEpoch | +| Session / Turn / Run ID | Conversation, logical root work, execution instance | One Turn may span physical Runs after handoff | + +Product Session identity is `(rootId, sessionId)`; matching Session IDs do not imply the same work. Lifecycle-scoped Desktop requests also carry `targetEpoch`. This fence excludes stale callbacks and does not replace authentication. + +Implementation: [Root authority](../../packages/storage/src/root-authority.ts), [connection handshake](../../packages/runtime-host/src/client/connection.ts), [reconnecting connection](../../packages/runtime-host/src/client/reconnecting-connection.ts), [Desktop identity](../../apps/desktop/src/shared/runtime-host-identity.ts). -Durable Stores are the recovery source of truth. In the rest of this document, **canonical state** means state rebuilt from those Stores, and a **projection** is a read-oriented view derived from that state. +## 3. State Root ownership and startup -**Bounded** means that the protocol sets explicit schema, size, count, or time limits instead of accepting arbitrary work or payloads. +Root capability acquisition canonicalizes the real path, then validates the root marker's random `rootId` against filesystem object identity. Aliases cannot create another logical owner; copying an initialized directory does not automatically acquire the original root's identity. Import, remount, and repair have separate explicit validation paths. Process-local registration authenticates capabilities and leases; TypeScript types alone are insufficient. -The execution names also describe different scopes: +Write authority comes from an OS lock on a stable file. A durable account-local ownership namespace arbitrates by `rootId`, retaining a compatibility lock boundary. Registration files, PIDs, sockets, health probes, and cache directories are discovery or observation data. Removing discovery caches cannot legitimately create a second writer. -| Name | Scope | +Closing a lease rejects new operations, waits for admitted operations, then releases the OS handles. Store facades receive that owner/lease; business code cannot bypass it by opening another database connection. The lock does not prove that every external descendant process exits with the Host. + +Startup order is: + +1. Acquire and validate the root writer owner. +2. Bind the Composition ID under the write lease; reject incompatibility before listeners or Domain Store writes. +3. Establish listeners and registration, then enter `recovering`. Limited lifecycle information can be available before business readiness. +4. Construct the Composition, install unique operation handlers, and execute recovery phases. +5. Publish `ready` after successful recovery; start optional physical storage maintenance afterward. + +Discovering a Host therefore proves only that a candidate process exists. A handshake cannot bypass Kernel readiness or operation permission checks. Maintenance failures back off independently rather than making physical cleanup another business-readiness authority. + +Implementation: [State Root composition](../../packages/storage/src/state-root-composition.ts), [Host Kernel](../../packages/runtime-host/src/server/host-kernel.ts), [storage maintenance](../../packages/runtime-host/src/server/storage-maintenance.ts). + +## 4. Fixed Composition and Domain lifecycle + +The Composition descriptor is selected before listeners start. Modules and dependencies are constructed during startup, with no dynamic registration after Ready. Dependencies are passed directly rather than looked up by Module name. Each business operation has exactly one Module handler; duplicate ownership is a construction error. Process, access, and diagnostic controls remain Kernel-owned. + +The Module contract contains `handlers`, `recover(phase)`, `beginDrain()`, `close()`, and optional `releaseConnection()`. A Module can combine coordinators; it is neither a separate process nor necessarily a source-directory boundary. + +| Recovery phase | Prerequisite established | |---|---| -| Session | The durable conversation and workspace context | -| Turn | One top-level unit of work represented in that Session, whether started by a user or by the Host | -| Run | The durable execution entity that performs model and tool work for a Turn | -| Root execution | The one top-level execution currently admitted for a Session | +| `state` | Interpretable durable business and control state | +| `resources` | Resource identities and recovery outcomes for retained process/resource records | +| `executions` | Admission, Run, and continuation consistency | +| `domains` | Recovery and execution-result reconciliation for business owners such as Goals and plans | +| `schedulers` | Permission for schedulers to produce work after preceding state is ready | -## One Turn through the system +Close runs in reverse Module construction order. A drain/close failure does not skip other owners; failures are aggregated. Stores close before the writer lease is released. Modules cannot hide external I/O or execution Promises outside lifecycle accounting, because that would prevent the Kernel from proving exit or handoff safety. Drain/close cancellation must also reach starting, queued, or I/O-waiting work and be rechecked after async waits, preventing activation after shutdown begins. -```mermaid -sequenceDiagram - participant Client - participant Kernel as Host Kernel - participant Domain as Domain Module - participant Execution as Hosted Execution - participant Runtime as Maka Runtime - participant Store as Durable Stores - participant Continuity as Session Continuity - participant Capability as Client Capability - - Client->>Kernel: Submit a message - Kernel->>Domain: Route an authenticated operation - Domain->>Execution: Reserve and admit the root execution - Execution->>Runtime: Start the exact execution - loop Model and tool work - Runtime->>Store: Append durable facts - Runtime-->>Continuity: Publish a size-limited live event - Continuity-->>Client: Send the next sequenced update - opt A selected tool needs the Client environment - Runtime->>Capability: Invoke a frozen capability binding - Capability->>Client: Bounded reverse call - end - end - Store-->>Continuity: Rebuild canonical state - Continuity-->>Client: Return a fresh snapshot -``` +Implementation: [Module contract](../../packages/runtime-host/src/server/host-composition.ts), [interactive assembly](../../packages/runtime-host/src/server/execution-composition.ts). -Before requests arrive, the Host Composition has already constructed these parts and assigned each business operation to one Domain Module. Process, diagnostics, upgrade, and access-credential operations remain Kernel-owned. Composition is the startup plan, not an extra service called on every request. +## 5. Root admission and execution results -For a user message, the Kernel authenticates and routes the request but does not interpret it. The owning Domain applies message and Session rules, then uses Hosted Execution when root work can start. Before the first provider request, Run Composer freezes and persists the prompt and tool basis. Runtime writes canonical facts, and Session Continuity projects those facts back to every Client. +### 5.1 Two levels of serialization -A Scheduled Task follows the same execution path but starts inside its Domain rather than from a connected Client. This is why Client disconnects do not control execution lifetime. +`SessionAdmissionGate` provides short per-Session critical sections. Multi-Session operations acquire in a stable order. An explicit lease permits work within an admitted context; implicit nested acquisition is rejected. Execution leaves the admission async context rather than holding this lock throughout model requests. -## Host identities +Above this, `RootTurnCoordinator` manages pending reservations and active execution: -These values answer different questions and must not be used as substitutes for one another: +```text +Per Session: at most one logical root execution being admitted or running +Different Sessions: may execute concurrently +Child Sessions / Graph lineage: retain their execution and lineage constraints +``` -| Identity | Plain meaning | Lifetime | -|---|---|---| -| State Root | The directory containing durable Host state; one process holds its exclusive writer lease | Survives Host processes | -| Host Epoch | The identity of the process currently holding that lease | Ends with that process | -| Composition ID | The kind of Host program allowed to interpret the State Root | Persistently bound to the root | -| Composition Revision | The revision of that Composition expected by a Client | Changes when startup wiring or compatibility changes | -| Host Generation | The replacement generation requested by a local owner Client | Shared by one product version, or unique to one development Client process | +`prepare()` returns a single-consumption reservation or busy/unavailable. `RootAdmissionOwner` persists the exact execution intent: Session, Turn, Run, user message, source messages, and predecessor chain. Reusing an ID with a different intent is not an acceptable retry. An admission chain whose consistency cannot be proved fails closed. + +A `drain` residency is acquired before entering asynchronous admission and remains until execution takes over or admission fails. This closes the exit window in which a request has entered but no active work is yet visible to the Kernel. + +### 5.2 Execution handles and durable facts -A restart changes the Host Epoch. A Composition change may change its Revision. A product-version update or development Client restart may change the Host Generation. None of those changes implicitly moves the State Root or changes its persistently bound Composition ID. +Successful admission returns that execution's `snapshot`, `completion`, and `settled`: -For example, a State Root bound to the interactive Composition cannot be opened by a different kind of Composition. The interactive Composition may evolve to a new revision without changing that persistent identity. +- `completion` describes completion, failure, cancellation, or an explicit inability to establish authority. +- `settled` signals that execution cleanup has finished. +- A Domain retains the handles returned for this execution instead of reconstructing equivalent handles from a Session ID. -## What each part owns +The Promises support different decisions; callback ordering is not another persistence guarantee. Process-local subscriptions are invalidation hints. Lost notifications, reconnection, and recovery require rereading admissions, RuntimeEvents, and control Stores. -### Host Kernel owns process lifecycle +Implementation: [Session admission](../../packages/runtime-host/src/server/session-admission-gate.ts), [Root admission](../../packages/runtime-host/src/server/root-admission-owner.ts), [Root execution](../../packages/runtime-host/src/server/root-turn-coordinator.ts), [public execution contract](../../packages/runtime-host/src/server/hosted-execution-authority.ts). -The Kernel acquires the State Root's exclusive writer lease, starts listeners, and authenticates connections. Authentication produces an immutable set of permissions for that connection. The Kernel also tracks active operations and **residencies**—explicit reasons the process must remain alive—and drives Composition recovery, drain, and close. +## 6. Model input, tool revisions, and policy activation -The Kernel does not interpret business state such as messages, tools, Goals, or Scheduled Tasks. New business behavior enters through a Domain Module rather than another Kernel branch. +`RunComposition` is an immutable C0 baseline for a Run. It records composer/source revisions, hashes of the system prompt/tool catalog/tool availability/provider options, tool names, and context window. It is neither another copy of the complete prompt nor a guarantee that model tools never change during the Run. -### Host Composition is the fixed startup plan +The baseline is durably committed before the first real provider dispatch; commit failure prevents the call. Dynamic tool changes belong to `RequestComposition` epochs. Committing C0 must not resample them and silently replace the original baseline. A recovered or handoff successor checks actual input semantics rather than treating matching field shapes as compatibility. -The Composition ID, revision, and construction function are chosen before listeners start. Modules are created once during startup and remain fixed after the Host becomes Ready. Diagnostics read the actual Module IDs from that created Composition instead of maintaining a second list. +Policy mutations and backend activation serialize through the short `RuntimePolicyActivationGate`. It protects the interval between policy checking and execution activation, not the entire model call. Uncertain policy authority blocks further execution; a stale read projection and a failed authoritative write are different failures. -Each business operation has exactly one Module owner. Composition combines those owners; it does not keep a parallel implementation of their handlers or lifecycle. Kernel operations stay outside Domain Modules. +The Host resolves model catalogs from persistent connection/model configuration and Host metadata, then projects them to Clients. Local Client resolution is limited to cases without authoritative Host state, such as unsaved editor drafts. Display slugs do not replace immutable connection identity. -For example, the interactive Composition constructs Session and Scheduled Task Modules, among others, along with their Stores and shared execution authority. That list is chosen once for the Host process. Composition is not a dynamic plugin registry or per-Session configuration. +Implementation: [Run Composition schema](../../packages/core/src/run-composition.ts), [model composition](../../packages/runtime-host/src/server/execution-model-composition.ts), [policy activation](../../packages/runtime-host/src/server/runtime-policy-activation-gate.ts). -Recovery runs through five fixed phases: +## 7. Canonical observation and message delivery -1. `state` -2. `resources` -3. `executions` -4. `domains` -5. `schedulers` +Ordinary Runtime Session transcripts are projected from durable RuntimeEvents. SQL queries locate immutable event ordering; the projector interprets message semantics. A separate message table no longer independently interprets ordinary Run history alongside RuntimeEvents. Historical data still has compatibility conversion paths, while WorkHub Coordination Sessions have their own domain contract and must not be folded into ordinary Run rules. -This order makes durable state and resources available before executions and business domains recover, and starts schedulers last. +Opening a Session subscription atomically obtains a canonical snapshot, `nextSeq`, and active stream IDs. The open response is written before subsequent subscription frames. Live sequence belongs to the connection observation protocol; transcript cursors and durable event/message ordinals identify pagination. They are neither one counter nor necessarily consecutive integers. -Close runs in reverse Module order. Drain and close attempt every owner and aggregate failures. +Larger transcripts use bounded pagination. Cursors bind the subscription, Session, source, direction, and watermark with integrity checks. Bootstrap, pages, per-Turn projection work, and active overlays have separate bounds. Sequence gaps, HostEpoch changes, subscription loss, and invalid cursors require reopening and rereading canonical state. PTY has separate backpressure/subscription boundaries so it cannot overwhelm ordinary Session observation. -### A Domain Module groups operation and lifecycle ownership +For delivery, reconnection does not imply permission to execute a command again. Queries may retry under their read-only contract. A sent command without a received result retains an unknown outcome and reconciles through its Domain's exact request IDs, admission, or result records. Desktop's durable outbox preserves message and attachment identity, restoring crash-time `sending` as `unknown`; this is not generic transport replay. -A Domain Module is a static record that answers four questions: +Implementation: [Session Continuity](../../packages/runtime-host/src/server/session-continuity-coordinator.ts), [transcript reader](../../packages/runtime-host/src/server/session-transcript-reader.ts), [pager](../../packages/runtime-host/src/server/session-transcript-pager.ts), [Desktop local store](../../apps/desktop/src/main/session-local-store.ts), [local service](../../apps/desktop/src/main/session-local-service.ts). -- which protocol operations does this group handle; -- what must it recover in each startup phase; -- what new work must it reject during drain; -- which resources and connection-scoped state must it release. +## 8. Transport, authentication, and Client Capability -For example, the Scheduled Task Module owns Scheduled Task operations, restores durable scheduling state, starts its scheduler only after recovery, and stops and closes that scheduler during shutdown. When a task fires, the Module still asks the shared Hosted Execution authority to run it; it does not create another Runtime. +### 8.1 One public protocol, multiple connection paths -A Module does not have to be a separate process, package, or source directory. It may represent one focused feature or a closely related lifecycle group. Construction code passes dependencies directly; Modules do not look them up by name at runtime. +| Path | Connection and trust boundary | +|---|---| +| Local IPC | UDS or Windows pipe; Local Owner is granted after validating the same-user boundary | +| TLS WebSocket | Authenticate before upgrade and recheck on admission; no silent plaintext downgrade | +| SSH tunnel | Explicit operator/Client tunnel followed by the Host protocol; tunnel lifetime belongs to the connection | +| Plaintext WebSocket | Explicitly acknowledged insecure configuration; never automatic TLS fallback | +| Native Peer stream | PeerId verification and end-to-end transport still precede Host credential and protocol admission; not WebSocket | -The Domain decides what an execution result means and what should happen next. Hosted Execution only owns the execution lifecycle. +These paths share operation codecs, dispatch, connection permissions, and canonical state. Frames, inflight operations, writer queues, subscriptions, and reverse calls are bounded. The read pump is independent of async handlers, allowing a reverse-call response to arrive while the Host is handling another request. An ordinary request timeout ends that request's wait; liveness failure closes the connection. They are distinct mechanisms. -### Hosted Execution controls top-level work in a Session +A connection's principal, operation grants, and path/capability access are fixed at admission. Credential prepare/finalize, rotation, and revocation follow durable access state; changed authority takes effect through a new connection. Operation sets are explicit, so adding a protocol operation does not expand existing grants. Durable revocation precedes notifications; uncertain commits require conservative fencing. -**Admission** is the atomic decision that reserves a Session for one exact root execution, preventing two top-level Turns from running concurrently. +### 8.2 The reverse-call execution cut -Successful admission returns three related values: +Clients publish versioned, size-limited capability offers. The Host selects bindings by principal, provider instance, contract, and `call`/`turn`/`session` affinity. Losing a session-affine provider cannot silently select another machine, and remote work cannot arbitrarily borrow an unrelated Client's local capabilities. -- `snapshot`: the state observed when the execution was admitted; -- `completion`: a terminal snapshot with completed, failed, or cancelled status, or an explicit `authority_error`; -- `settled`: a signal that execution cleanup has also finished and its temporary resources have been released. +Reverse calls distinguish: -A Domain uses `completion` to decide the business result and `settled` to know that cleanup is over. It keeps the handles returned for that exact execution rather than reconstructing them later from a Session ID or Turn ID. +1. The Host dispatches a call; the provider returns `accepted` evidence. +2. The Host verifies policy/grants and sends `admitted`, authorizing the effect. +3. The provider returns a result; the Host commits it once under the invocation identity. -Hosted Execution subscriptions only tell same-Epoch observers that something may have changed. They are not proof of the new state; recovery always rereads durable facts. +Disconnection before `admitted` is capability loss; disconnection or timeout afterward may mean an unknown outcome. The tool journal preserves this distinction instead of automatically repeating an uncertain external effect. A Client may execute local UI/MCP capabilities while the Host retains Run, Session, and recovery ownership. -### Session Continuity owns Client observation +Implementation: [connection session](../../packages/runtime-host/src/server/connection-session.ts), [outbound writer](../../packages/runtime-host/src/server/serial-outbound-writer.ts), [access authority](../../packages/runtime-host/src/server/access-authority.ts), [capability coordinator](../../packages/runtime-host/src/server/client-capability-coordinator.ts), [invocation broker](../../packages/runtime-host/src/server/client-capability-invocation-broker.ts). -Session Continuity is the public read model for a live Session. Opening a subscription returns a canonical snapshot, the next expected sequence number, and the identities of assistant streams that are still active. The potentially larger transcript is read through a separate size-limited snapshot. +## 9. Owner profiles, Guest mounts, and Client-local state -Live projection, assistant, and tool updates have explicit size limits and sequence numbers. After a connection loss, Host Epoch change, sequence gap, or expired transcript snapshot, a Client opens a new subscription and rereads canonical state. For example, a Desktop reload during model output restores the current transcript and active stream identities; it does not resend the user message. Stream delivery is never a recovery authority. +### 9.1 Two access objects -### Run Composer freezes what the model sees +Owner profiles are Client connection configuration. `local` and enabled remote Owner profiles connect independently; a State Root cannot be enabled through multiple Owner profiles. The default Host applies only to new work and operations without an existing Host scope. Changing it neither moves Sessions nor closes other connections. Environment profile deployment/activation information remains separate from Host runtime authority. -Run Composer freezes the model-visible basis for one Run: base system prompt, tool catalog, tool availability policy, base provider options, and the revisions of inputs used to construct them. +A Guest shared task is an independent **Session mount**, excluded from the Owner profile catalog and ineligible as default Host. The mount store owns Guest credentials and retained shared Session projections. One root can have multiple shared Session mounts. Startup migrates/cleans up historical experimental Guest-as-remote-profile data; that legacy representation does not expand current profile authority. -Before the first real provider request: +| Operation/state | Owner profile | Guest mount | +|---|---|---| +| New-task Host, Project/model/settings catalogs | Participates, subject to operation permissions | Excluded | +| Shared Session observation | Determined by Host authority | Only projections covered by effective Session grants | +| Shared task input | Normal Domain admission | Exact turn request; Owner decision precedes canonical admission | +| Offline | Retain configuration and recover connection | Retain mount; not equivalent to grant revocation | +| Credential rejection or lost Session access | Connection/permission failure handling | Persist access failure and remove shared state that may no longer be displayed | -1. build the immutable Run Composition snapshot; -2. commit it to the AgentRun Store; -3. call the provider only after the commit succeeds. +Guest observation grants, turn-request grants, Host operation grants, tool sandbox permissions, and Client Capability grants are separate contracts. Mesh membership implies none of them. -If composition or persistence fails, the provider is not called. A Run that never reaches provider dispatch does not invent a composition snapshot. +### 9.2 Isolation and local persistence -### Client Capability keeps Client-local effects bounded +Desktop routes operations and events using `(rootId, sessionId)` and `targetEpoch`. Guest connection changes cannot invalidate Owner new-task catalogs; Owner replacement must still invalidate the old catalog. Local outbox/history caches partition by profile incarnation, root, and credential identity. Cached content grants no live authority; Guests do not reuse the Owner offline-history cache policy. -An authenticated Client may publish size-limited, versioned tool or service **offers** describing what it can do. Runtime Host selects an exact provider **binding**, and a Run records selected model tools through its normal Run Composition path. The Host may then make a bounded reverse call for an effect that must execute in the Client environment—for example, an OS-facing capability published by Desktop. +Client preferences, profiles, Guest mounts, deployment bindings, and outbox have different data owners, none equivalent to the Host operational DB. Reading `runtime-host-deployments.json` may migrate it and therefore requires write exclusion. Current operations use a process-lifetime OS lease. After obtaining single-instance authority and before concurrent store access, Desktop reclaims empty legacy directory locks. Ordinary reads cannot steal a lock based on age, and a failed read cannot be replaced with an empty configuration write. -Publishing or invoking a capability does not transfer Session, Run, or execution ownership to the Client. Connection loss makes that provider unavailable; the owning Domain handles capability loss or an explicit result-unknown outcome through its normal durable contract. +Implementation: [profile service](../../apps/desktop/src/main/runtime-host-profile-service.ts), [Guest mounts](../../apps/desktop/src/main/runtime-host-guest-session-mounts.ts), [Desktop manager](../../apps/desktop/src/main/runtime-host-desktop-manager.ts), [preload catalogs](../../apps/desktop/src/preload/preload.ts), [deployment bindings](../../apps/desktop/src/main/runtime-host-managed-services.ts), [process-lifetime file lock](../../packages/storage/src/process-lifetime-file-update-lock.ts). -### Host profiles describe connection targets +## 10. Host retention, drain, and Client lifetime -A Host profile is Client-owned connection configuration, not Host state. The built-in `local` profile keeps the existing zero-configuration Local IPC and candidate-spawn path. A remote profile contains a display name, one explicit transport (direct TLS, SSH tunnel, or acknowledged plaintext), and a required State Root identity; its access credential is stored separately and bound to that exact profile target. A profile ID names an immutable target: changing its connection method, endpoint, or root requires a new profile ID. Its display name and credential may be updated in place. +Natural idle exit, graceful drain, launcher exit, and operator stop are distinct events. -Enabling a profile connects a Client to that Host. One Desktop enables at most one profile for a given State Root, so a Host cannot appear twice under different connection settings. Enabling does not move a Project or Session, change the Host Epoch, or mutate the Host. Every remote transport ends in the same authenticated WebSocket connector and never falls back to local discovery or candidate spawning. A tunnel is a connection-scoped resource: reconnect creates a new tunnel, and closing or losing it closes that connection. Every remote connection pins the profile's State Root identity and fails if the endpoint presents a different root. +| Residency | Prevents natural idle exit | Represents active work that drain must await | Typical holder | +|---|---|---|---| +| `idle` | Yes | No | Future Scheduled Tasks, armed/paused Goals, idle Daily Review | +| `drain` | Yes | Yes | Admission, execution, persistence handoff, active resource work | -Desktop keeps `local` and any enabled remote profiles connected independently. One profile is the default for creating new Sessions and other operations without an existing Host scope; changing it neither reconnects Hosts nor moves existing Sessions. A remote connection failure does not interrupt Local or another remote Host. +Natural ephemeral exit requires no accepted connections, in-progress handshakes, active operations, or residencies of either kind. Maintenance/replacement can distinguish idle retention from actual drain work; one total count cannot decide every exit scenario. -Desktop Settings uses an explicit Host selector for Host-owned configuration. Client-owned preferences such as appearance and locale remain a single Desktop setting and do not change with that selector. +An ordinary Client disconnect releases connection-scoped subscriptions, capabilities, and controller leases without automatically cancelling admitted work. Desktop quit first performs bounded retirement preparation for its currently owned ephemeral Host, allowing the Host to recheck activity and close admission under the selected mode. Quit neither waits for process exit nor enables cooperative handoff. An unreachable Host cannot prevent Desktop exit; the launch-owner guard remains responsible for closing its owned Host when launcher IPC is lost. TUI detach, a one-shot CLI's owned invocation, and an operator-managed Service Host follow their respective lifetime contracts. There is consequently no guarantee that work survives closing any arbitrary Client. -Desktop aggregates Session summaries from its connected Hosts. A Session is identified in the product by the pair `(Host rootId, Session id)`, so equal Session IDs from different Hosts remain distinct. Requests, events, and persistent Client-local resources are routed back to the owning Host. Their transport scope also includes the Client target Epoch (`targetEpoch`), which fences out a late request or event after Desktop replaces that profile's connection lifecycle. The Client target Epoch is not the Host Epoch and is not an authentication boundary. +The Host owns resource processes. Shell/PTY identity is recorded before spawn; observation and control are separate. Control uses connection/controller identity and ordering constraints. Disconnecting an observer does not terminate the process. Recovery reconciles retained resources using provable OS identity. A PID alone cannot safely authorize termination, nor prove that all escaped descendants ended. -Enabled profiles and the default profile are persisted preferences, not proof that a connection is ready. Desktop keeps an unavailable remote profile visible so the user can retry or disable it. TUI and CLI remain single-Host Clients: they resolve one profile when they start and report an unavailable profile as an error. +Implementation: [residency registry](../../packages/runtime-host/src/server/host-residency-registry.ts), [Kernel lifecycle](../../packages/runtime-host/src/server/host-kernel.ts), [launcher guard](../../packages/runtime-host/src/candidate-launch-owner-guard.ts), [Desktop quit](../../apps/desktop/src/main/runtime-host-quit.ts), [resource coordinator](../../packages/runtime-host/src/server/runtime-resource-coordinator.ts). -A remote Desktop generation cannot submit arbitrary Host paths. It reads Project summaries, submits Project IDs, and keeps Client-local capabilities from receiving remote Host paths. Local filesystem actions such as directory picking, Git review, workspace search, and opening Skill files remain available only for `local`. +## 11. Cooperative handoff and crash recovery -The operator and Client setup flow is documented in [Connect to a remote Runtime Host](../runtime-host-remote-access.md). +### 11.1 The irreversible cut -### Runtime Host resolves workspaces +Local upgrades depend on the observed HostEpoch and current activity. Client diagnostic snapshots guide interaction; they do not grant kill authority. The shared handoff flow reobserves its target. Automatic replacement requires provable idleness or supported cooperative handoff; intentionally interrupting work requires the corresponding authorization. Service/installation ownership remains with the deployment owner. -Clients identify a workspace with exactly one of two target forms: +The sequence below describes only successful cooperative handoff. Timeouts, incomplete work coverage, or failed safety checks do not guarantee entry into this path. -```ts -type WorkspaceTarget = - | { kind: "project"; projectId: string } - | { kind: "host_path"; path: string }; +```mermaid +sequenceDiagram + participant Old as Old Host + participant Log as Durable execution facts + participant New as Successor Host + Old->>Old: Hold admission and scheduler triggers + Old->>Old: Reach a durable model/tool boundary + Old->>Log: Seal pause and successor claim + Old->>Old: Prove exact drain residency coverage + Old->>Old: Commit fence, detach, release writer lease + New->>New: Acquire writer lease and recover + New->>Log: Verify claim, prefix and composition + New->>Log: Open successor Run for the same logical Turn ``` -`project` is the portable form. Runtime Host resolves it through its Project Catalog and returns the canonical target plus `hostCwd`, the absolute directory on the Host. `host_path` is for a Client explicitly permitted to name Host paths, such as a local CLI started in a checkout. +`runtime_handoff_pause_v1` records the original root Run, successor Run, invocation/claim, remaining steps, and related facts. Ending the old physical invocation does not record logical Turn termination. The new Run must match the claim, immutable event prefix, lineage, and input semantics. -Project summaries do not expose their registered locations. `canUseHostPaths` controls whether a Client may name a Host path in an operation; it is not a path-confidentiality boundary. Canonical Session projections may include the resolved `hostCwd`, which remote Clients treat as Host metadata rather than a path on the Client filesystem. Reading or changing Project locations and asking the Host to reveal a path remain separate operations, while submitting `host_path` requires Host-path authority. +The Kernel requires exact residency handles from handoff and proof that no other `drain` work was omitted; matching labels or counts are insufficient. No async window may separate the final proof from committing the fence. Before commitment, cancellation can release the hold and resume original work. After the irreversible cut, the transaction must settle; cancellation cannot restart the old Run. -Clients do not combine a path with a Project ID or resolve a Host path themselves. Desktop remembers the selected Project locally for each State Root; selecting it does not mutate global Host state. A remote Desktop may browse directories that the Host explicitly publishes, using an opaque root ID and validated path segments, and ask the Host to register the selection through the Project Catalog. It cannot name or inspect paths outside those roots. Desktop never opens a Client-local directory picker as though it named a Host directory, and CLI/TUI do not reinterpret, validate, relocate, or autocomplete Host paths through the Client filesystem. +Runtime pauses cooperatively at durable model/tool boundaries. It does not hot-migrate arbitrary provider streams, in-progress external effects, or PTYs. Incompatible prompt, tools, provider options, context window, or sandbox provenance prevents successor model/tool execution. -## Lifecycle +### 11.2 A crash is not a cooperative pause -Two Host lifetimes use the same Kernel and Composition: +Startup validates admissions, source-message proof, and Run identity before fixups or continuation. Admitted work without a Run, an already-started old Run, execution with a valid handoff claim, and safe-boundary continuation follow distinct recovery branches. A continuation with missing safety conditions may be parked; it is not permission to run the work again. -| Host kind | Who manages its lifetime | -|---|---| -| Ephemeral Host | A local Client launches it; it may exit when no connection, operation, or residency keeps it alive | -| Service Host | A deployment owner runs it; Client generations do not replace it and it does not use Client-driven idle exit | +Goals, Scheduled Tasks, and Daily Review persist their own business intent and reconcile against the common execution authority. An armed Goal that never ran must not start merely because the process restarted. A Scheduled Task's pending fire retains exact admission identity. An interaction Promise in the old process cannot be resurrected from disk; a committed answer and a resumable invocation are distinct facts. -| Stage | Contract | -|---|---| -| Startup | Acquire the State Root lease, bind Composition identity, build Composition, recover Modules, start schedulers, then publish Ready | -| Request | Authenticate, enforce input limits and connection permissions, then route to the Kernel or the unique Domain Module handler | -| Execution | Reserve and admit through Hosted Execution, then reread durable facts to confirm the final state | -| Drain | Stop accepting new work while already accepted work finishes or reaches a recoverable state | -| Close | Stop listeners from accepting connections, drain operations, close Modules in reverse order, clean up listeners, then release the State Root lease | +Implementation: [Client handoff](../../packages/runtime-host/src/client/host-handoff.ts), [Runtime handoff gate](../../packages/runtime/src/run-handoff-gate.ts), [logical execution](../../packages/core/src/runtime-logical-execution.ts), [root recovery](../../packages/runtime-host/src/server/root-turn-coordinator.ts), [Goal](../../packages/runtime-host/src/server/goal-coordinator.ts), [Scheduled Task](../../packages/runtime-host/src/server/scheduled-task-coordinator.ts). + +## 12. Workspace and deployment authority + +`WorkspaceTarget` has exactly two forms: `{ kind: "project", projectId }` and `{ kind: "host_path", path }`. The Host resolves the canonical workspace through its Project Catalog or an authorized Host path. Clients do not interpret remote `hostCwd` through their own filesystem. `canUseHostPaths` controls path submission, not path confidentiality. + +Remote directory browsing uses Host-published opaque root IDs and validated path segments with realpath containment. Symlinks and Client-local pickers cannot expand that boundary. + +Installation and write authority are also separate. An account-local deployment owner coordinates Desktop, CLI, managed service, or development ownership by root identity and CAS revision. The managed deployment document describes configuration and `active`/`transition`/`blocked` recovery states. Service artifacts are projections of that configuration rather than a competing deployment journal. -A Client disconnect releases only connection-scoped resources. It does not cancel an admitted execution. +Updates verify package version/integrity, prepare the target, confirm actual target readiness, and then commit installation state. Retries must recognize an already-successful successor rather than terminating it again from old process information. Ordinary remote credentials do not grant machine operator installation rights; SSH operator activation is a separate explicit boundary. -### Local ephemeral upgrade handoff +Implementation: [workspace resolver](../../packages/runtime-host/src/server/workspace-resolver.ts), [local deployment owner](../../packages/runtime-host/src/operator/local-deployment-owner.ts), [managed deployment](../../packages/runtime-host/src/operator/managed-deployment.ts). -A Host Generation is separate from protocol compatibility: two local Client generations can speak the same protocol but still request process replacement so the requested Runtime generation becomes authoritative. A local owner Client names the Host Epoch it observed when asking that process to drain; a stale Client therefore cannot drain a replacement process. The next Host waits for the existing State Root lease to be released. +## 13. Failure convergence and diagnostics -When startup finds another generation, the Host may return limited counts of active connections, operations, and residencies. These counts explain why it remains alive but do not permit the Client to kill it. Only local ephemeral Hosts support replacement, and interrupting active work requires an explicit Client decision. Service Host upgrades remain the responsibility of their deployment owner. A waiting Client stops connection attempts until the observed Host exits, so waiting does not keep an otherwise idle Host alive. +| Observed failure | Authority and convergence | Invalid inference/action | +|---|---|---| +| Root owner or composition mismatch | Stop business entry and report the actual conflict | Bypass ownership by deleting registration or editing a PID | +| No usable/recoverable Peer route | Networking reports reachability; Client retains backoff and a recovery entry point | Treat it as credential revocation or repeatedly wake itself | +| Identical or alternating reconnect failures | One current outage diagnostic: count, first/last times, latest error; log start and recovery | Append every retry stack and evict other diagnostics | +| HostEpoch or live sequence changes | Reestablish observation and read canonical state | Replay sent mutations to rebuild the UI | +| Guest disconnect | Recover its mount independently | Invalidate Local new-task catalogs or change the default Host | +| Retained Client data-file lock | Recover through proven ownership/OS lease; retain unexpected contents | Steal locks during ordinary reads or overwrite failed reads with empty configuration | +| Unknown command/admitted capability outcome | Reconcile Domain records or retain unknown | Promise exactly-once external effects | +| An owner fails during drain/close | Continue other releases and aggregate errors | Release the writer lease while a Store can still write | + +Copied diagnostics can read Desktop's current connection state while the target Host is unreachable, without a remote query. Retry counters do not trigger catalog reloads; changed errors may still update connection state. A successful connection ends the outage summary, and a later failure begins a new one. Diagnostics are redacted and own no retry, recovery, or replacement authority. Host protocol teardown for invalid frames, reused request IDs, quota violations, or writer failure retains one bounded failure diagnostic. This is evidence of an actual connection failure, separate from the Client's routine outage summary. + +Implementation: [Desktop diagnostics](../../apps/desktop/src/main/main-process-diagnostics.ts), [Desktop manager](../../apps/desktop/src/main/runtime-host-desktop-manager.ts), [reconnect lifecycle](../../packages/runtime-host/src/client/reconnect-lifecycle.ts). -## Rules that must remain true +## 14. Trade-offs and maintenance checks + +| Choice | Property obtained | Cost and extension constraint | +|---|---|---| +| One root writer + fixed Composition | One execution/recovery authority and provable close order | Cross-Host work needs explicit protocols, not a shared writable root | +| Short admission + long-lived execution handles | Serialized Session conflicts without holding a lock across model I/O | Every entry point must connect reservations, durable intent, and residency | +| Durable facts + bounded projections | Clients reconnect independently; lost transport does not rewrite history | Requires snapshots/cursors/sequences and reconstruction, not unlimited live-event caching | +| No automatic replay of uncertain effects | Avoids duplicate external side effects | Domains must define confirmation, reconciliation, or manual resolution | +| Cooperative-boundary handoff | Preserves logical Turns while replacing physical Runs | Requires verifiable claims, prefixes, input semantics, and complete work coverage | +| Outage summaries instead of per-retry logs | Long outages do not flood diagnostics; current failure remains inspectable | Does not retain every dial's full history; permanent failures retain their separate error path | -1. One State Root has at most one writer owner. -2. One Session has at most one root Hosted Execution or pending root admission. -3. Local IPC and WebSocket share one routing table, permission model, and canonical state. -4. Transport frames and authenticates messages; it does not own business state. -5. Composition identity is fixed before listeners start, and its Module set is fixed before Ready. -6. One business operation has one Module owner; process and access operations remain Kernel-owned. -7. Notifications and streams do not replace Stores as the recovery authority. -8. Provider dispatch waits for a durable Run Composition commit. -9. Domain lifecycle and execution lifecycle remain separate. -10. Shutdown continues closing other owners after one owner fails. -11. Runtime Host is the only resolver from `WorkspaceTarget` to a canonical Host path. -12. Client-local capability execution does not transfer Runtime ownership out of the Host. -13. Clients rebuild observation from canonical snapshots after a stream discontinuity. +Before changing this design, check for a second writer/execution owner; uninterrupted accounting from admission through cleanup; durable facts versus projections versus Client preferences; confused connection/process/installation generations; duplicate effects after unknown mutation outcomes; and accidental promotion of Guest/network identity to Owner permissions. -## How failures converge +These tests are entry points to the contracts, not evidence that every OS, NAT, or deployment combination was tested on hardware: -| Failure | Required behavior | +| Contract | Regression entry points | |---|---| -| Composition mismatch | Fail before listeners or Domain Store mutation; report terminal incompatibility instead of repeatedly starting candidates | -| Host crash | The next Host rereads Stores and safely repeats recovery until execution and Domain state converge | -| Lost notification | Reread the canonical projection; never infer terminal state from callback delivery | -| Lost Session stream | Open a new subscription and reread its snapshot and transcript | -| Run Composition failure | Do not call the provider | -| Client disconnect | Keep admitted work under Host ownership | -| Client Capability loss | Surface bounded capability-loss or outcome-unknown state; do not silently retry an uncertain effect | -| Partial shutdown failure | Aggregate the error while continuing to release remaining resources | - -Runtime Host does not promise that an arbitrary external side effect happens exactly once. If a connection is lost after dispatch, the Host may know only that the outcome is unknown. The Tool or resource contract must preserve that uncertainty and must not retry automatically unless the operation explicitly permits it. - -## Protocol and security boundary - -- Protocol messages use closed schemas that reject unknown fields, explicit size and count limits, and stable error codes. -- Authentication completes before protocol connection admission. -- Local IPC grants Local Owner authority only after its OS endpoint establishes a same-user trust boundary. -- Authentication fixes the principal, allowed operations, and path or capability access for the lifetime of a connection. -- Client Capability offers and reverse calls remain authenticated, size-limited, and tied to that connection. -- Adding a protocol operation does not expand an existing credential grant. -- Status and diagnostics expose only bounded, redacted lifecycle and composition facts. - -## Code-reading map - -- [`host-kernel.ts`](../../packages/runtime-host/src/server/host-kernel.ts): process ownership, listeners, connection lifecycle, drain, and shutdown -- [`host-composition.ts`](../../packages/runtime-host/src/server/host-composition.ts): composition identity, Module contract, recovery, and close order -- [`execution-composition.ts`](../../packages/runtime-host/src/server/execution-composition.ts): static coordinator and Module assembly -- [`hosted-execution-authority.ts`](../../packages/runtime-host/src/server/hosted-execution-authority.ts): root execution contract -- [`session-continuity-coordinator.ts`](../../packages/runtime-host/src/server/session-continuity-coordinator.ts): canonical Client observation and live stream continuity -- [`client-capability-coordinator.ts`](../../packages/runtime-host/src/server/client-capability-coordinator.ts): capability publication, binding, and reverse-call lifecycle -- [`workspace-resolver.ts`](../../packages/runtime-host/src/server/workspace-resolver.ts): Project and Host-path workspace resolution -- [`run-composition.ts`](../../packages/core/src/run-composition.ts): durable Run Composition schema -- [`state-root-composition.ts`](../../packages/storage/src/state-root-composition.ts): persistent Composition binding - -## Summary - -Runtime Host keeps one ownership path. The Kernel controls the process. Composition builds a fixed set of Modules. Modules own business behavior, while Hosted Execution controls top-level work. Run Composer records what the model sees, Session Continuity rebuilds what Clients see, and Client Capability allows limited callbacks into a Client. Durable Stores let all of them recover after a process restart without creating a second Runtime owner. +| Root ownership, fixed recovery and close | [root-authority](../../packages/storage/src/__tests__/root-authority.test.ts), [host-kernel](../../packages/runtime-host/src/__tests__/host-kernel.test.ts), [host-composition](../../packages/runtime-host/src/__tests__/host-composition.test.ts) | +| Exact admission and execution recovery | [root-admission-owner](../../packages/runtime-host/src/__tests__/root-admission-owner.test.ts), [root-turn-coordinator](../../packages/runtime-host/src/__tests__/root-turn-coordinator.test.ts) | +| Input, observation and capability calls | [execution-model-composition](../../packages/runtime-host/src/__tests__/execution-model-composition.test.ts), [session-continuity](../../packages/runtime-host/src/__tests__/session-continuity-coordinator.test.ts), [client-capability-recovery](../../packages/runtime-host/src/__tests__/client-capability-recovery.test.ts) | +| Upgrade and process retention | [host-handoff](../../packages/runtime-host/src/__tests__/host-handoff.test.ts), [host-residency-registry](../../packages/runtime-host/src/__tests__/host-residency-registry.test.ts) | +| Guest/Local isolation | [guest mounts](../../apps/desktop/src/main/__tests__/runtime-host-guest-session-mounts.test.ts), [new-task preload](../../apps/desktop/src/main/__tests__/runtime-host-new-task-preload.test.ts), [Desktop manager](../../apps/desktop/src/main/__tests__/runtime-host-desktop-manager.test.ts) | +| Client lock recovery and diagnostics | [managed services](../../apps/desktop/src/main/__tests__/runtime-host-managed-services.test.ts), [profile service](../../apps/desktop/src/main/__tests__/runtime-host-profile-service.test.ts), [diagnostics](../../apps/desktop/src/main/__tests__/main-process-diagnostics.test.ts) | diff --git a/docs/architecture/runtime-host-architecture.zh-CN.md b/docs/architecture/runtime-host-architecture.zh-CN.md index 5c803d6d80..e26d018d6b 100644 --- a/docs/architecture/runtime-host-architecture.zh-CN.md +++ b/docs/architecture/runtime-host-architecture.zh-CN.md @@ -7,7 +7,7 @@ counterpart: ./runtime-host-architecture.md implementation_status: current document_status: current translation_status: synced -last_verified: 2026-08-12 +last_verified: 2026-09-09 owners: - maka-backend --- @@ -30,295 +30,321 @@ owners: under the License. --> +[English](./runtime-host-architecture.md) + # Runtime Host 架构 -> Runtime Host 是一个长期运行的进程,负责一个 State Root 以及使用该 State Root 的 Runtime work。Desktop、TUI、CLI、bot 和 Eval 都是 Client;它们请求 Host 执行工作,不拥有第二套 Runtime。 +## 1. 架构契约与范围 + +本文回答:**多个入口和多个连接如何共享一个 State Root 上的执行,同时在并发、断线、进程退出和升级后保持权责一致?** 面向维护 Host、扩展 Domain 或实现 Client 的开发者,规定组件职责、持久化边界、状态转换和失败语义。 -本文解释维护 Runtime Host 或接入产品功能时需要理解的稳定边界,不重复每个 protocol schema 或 coordinator 的实现细节。 +实现基线为 `09c73430c`(2026-09-09)。除明确标为历史或不提供的能力外,下文描述该基线的当前实现;Peer 相关能力仍属实验性功能。协议字段和资源限额以链接的 schema、实现与测试为准。网络层另见 [Peer Mesh 架构](./peer-mesh-architecture.zh-CN.md),执行恢复算法另见 [Runtime resume](./runtime-resume-architecture.zh-CN.md)。 -本文所说的 **owner** 或 **authority**,是指 Host 在线时唯一有权改变某类状态的组件,不一定是发起操作的 Client 或用户。 +一个 State Root 同时最多有一个 writer Host。Desktop、TUI、CLI、Bot 与 Eval 是执行入口或适配器,不另建拥有同一份工作状态的 Runtime。Host 进程内部仍分开执行控制、业务决策、存储和观察职责;“Host 是 authority”并不意味着 Kernel 可以解释所有业务状态。 -## 为什么需要 Runtime Host +以下图从 Client 向下读取,表示调用与权责边界,不表示每一次请求都经过所有节点,也不展开传输、部署或全部 Domain。 -Runtime work 的生命周期长于一次连接。模型调用可能在 Desktop window reload 后继续,authenticated remote Client 可能断开,进程也可能在 durable work 尚未结束时重启。State Root 是保存这些持久状态的目录。 +```mermaid +flowchart TD + C["Desktop / TUI / CLI / Bot / Eval"] --> K["Host Kernel: admission and connection authority"] + K --> D["Domain operation owners"] + D --> A["RootTurnCoordinator: HostedExecutionAuthority"] + A --> R["SessionManager / AgentRun / Runtime"] + R --> E["RuntimeEvents and control stores"] + E --> P["Session Continuity: canonical projections"] + P --> C + R --> B["Client Capability broker"] + B --> C +``` -如果每个 Client 都拥有自己的 Runtime 与恢复路径,系统会出现多个 writer、冲突的 Session state,以及依赖连接存活的 execution。Runtime Host 消除这些歧义: +## 2. 权责与标识 -- 一个进程拥有一个 State Root 的写权限; -- Local IPC 与 authenticated WebSocket 使用同一份持久状态; -- 业务代码决定一项工作的含义; -- 一个 execution authority 负责顶层 Session work 的 admission 与 stop,跟踪最终结果,并等待 cleanup 结束。 +### 2.1 Authority 分工 -## 用普通语言理解各组件 +| Authority | 决定什么 | 不决定什么 | +|---|---|---| +| Storage Root owner | 哪个进程可以对这个根执行写操作 | Client 权限、安装版本、业务成功 | +| Host Kernel | 连接准入、请求路由、进程存活、drain 与关闭 | Turn 的业务含义、模型选择、调度策略 | +| Host Composition | 固定依赖图、Module 集合、恢复与关闭顺序 | 运行时插件发现、每个 Session 的动态配置 | +| Domain Module | 自己的操作语义、业务状态和恢复策略 | 绕过统一 root admission 启动另一套 Runtime | +| `RootTurnCoordinator` | 实现 `HostedExecutionAuthority`,准入、停止、恢复一个 Session 的 root execution | Goal 或 Scheduled Task 如何解释执行结果 | +| Runtime / AgentRun | 模型与工具步骤、事件、执行与 continuation | 连接发现、客户端默认 Host、安装管理 | +| Session Continuity | 从 canonical facts 构建 snapshot、transcript 和 live projection | 从通知推断执行已完成,或替 Client 重发命令 | +| Client Capability | 选择 provider、约束 reverse call 及其不确定结果 | 把 Session/Run 所有权转移给 Client | +| Deployment owner | 服务配置、版本安装、激活与替换协调 | 凭安装记录取得 State Root writer lease | -| 组件 | 直观含义 | -|---|---| -| Host Kernel | 进程入口:拥有 State Root 的排他 lease 与 connections,停止接收新工作,并负责关闭进程 | -| Host Composition | 固定的启动方案:创建 Stores、共享 authorities 与 Module 列表 | -| Domain Module | 一条静态记录,把一组 protocol operations 与 startup/shutdown 职责分配给一个 owner | -| Hosted Execution | Session 顶层工作的调度入口:接收一个确切 execution,负责停止或恢复它,并区分最终结果与 cleanup 完成 | -| Run Composer | 在 provider call 前记录不会再变化的 prompt 与 tool 基线 | -| Session Continuity | 向 Client 提供 canonical Session snapshot 与带大小限制的 live updates | -| Client Capability | 允许 Host 调用已连接 Client 发布的能力,但不转移 Runtime ownership | +`hosted-execution-coordinator.ts` 和 `hosted-execution-runner.ts` 是面向外部执行调用的协调/适配层;实际的统一 root authority 是 `RootTurnCoordinator`。新增入口应复用公共执行契约,不按文件名另造一个执行 owner。 + +### 2.2 标识不可互换 + +| 标识 | 范围与含义 | 变化边界 | +|---|---|---| +| `rootId` | State Root 的持久身份,协议中的 Host identity | 显式创建、导入或修复规则;不是路径字符串 | +| `HostEpoch` | 当前 Host 进程实例 | Host 进程重启 | +| Composition ID | 允许解释这个根的程序组合类型 | 持久绑定,不因普通升级自动改变 | +| Composition Revision | 当前组合修订,Client 会检查 | 组合演进;不只是诊断标签 | +| Protocol version / compatibility epoch | wire contract 兼容性 | 协议契约演进 | +| Host Generation | 本地 owner 请求运行的 Runtime 版本/开发代际 | 产品升级或开发启动;不等同协议版本 | +| `targetEpoch` | Desktop 中某个连接目标的生命周期代际 | 替换目标,隔离旧请求与回调 | +| Profile ID / incarnation | Client 的连接配置及其持久实例 | 配置替换、凭据与本地数据分区规则 | +| PeerId | 网络端点的密码学身份 | 网络身份密钥变化;不是 IP、rootId 或 HostEpoch | +| Session / Turn / Run ID | 会话、逻辑顶层工作、执行实例 | 一个 Turn 在 handoff 后可跨多个物理 Run | + +产品中的 Session identity 是 `(rootId, sessionId)`;同名 Session ID 不代表同一份工作。带生命周期的 Desktop 请求还携带 `targetEpoch`。这层 fence 排除旧回调,不能替代认证。 + +实现:[Root authority](../../packages/storage/src/root-authority.ts)、[connection handshake](../../packages/runtime-host/src/client/connection.ts)、[reconnecting connection](../../packages/runtime-host/src/client/reconnecting-connection.ts)、[Desktop identity](../../apps/desktop/src/shared/runtime-host-identity.ts)。 -Durable Stores 是 recovery 的事实来源。下文所说的 **canonical state**,是指从这些 Stores 重建的状态;**projection** 则是从该状态派生、便于读取的视图。 +## 3. State Root 所有权与启动 -**Bounded** 表示 protocol 对 schema、大小、数量或时间设有明确限制,而不是接受任意 work 或 payload。 +Root capability 先规范化真实路径,再验证根标记中的随机 `rootId` 与文件系统对象身份。别名不能产生另一个逻辑 owner;复制一个已初始化目录也不能自动获得原根身份。导入、remount 或 repair 通过各自的显式验证路径处理。Capability 和 lease 的真实性由进程内登记验证,不只依赖 TypeScript 类型。 -下面几个 execution 名称也表示不同范围: +写入 authority 来自稳定文件上的 OS lock。持久的 account-local ownership namespace 按 `rootId` 仲裁,并保留兼容锁边界;registration 文件、PID、socket、health probe 和缓存目录都只是发现或观察信息。删除发现缓存不能合法地产生第二个 writer。 -| 名称 | 范围 | +Lease 关闭先拒绝新操作,等待已进入的操作结束,再释放 OS handle。Store facade 接收这个 owner/lease,业务代码不能通过直接打开另一份数据库绕过它。锁不意味着已证明任意外部子孙进程都随 Host 退出。 + +启动顺序为: + +1. 获得并验证 root writer owner。 +2. 在写 lease 下绑定 Composition ID;不兼容时在监听与 Domain Store 写入前失败。 +3. 建立 listener 与 registration,进入 `recovering`。此时可提供受限生命周期信息,不等于业务已 Ready。 +4. 创建 Composition,安装唯一的 operation handlers,按恢复 phase 执行。 +5. 恢复成功后发布 `ready`;再启动可选的物理存储维护。 + +因此,发现到 Host 只证明存在候选进程;通过握手也不能越过 Kernel 的 readiness 与操作权限检查。维护失败可退避重试,不把物理清理变成业务 readiness 的第二个 authority。 + +实现:[State Root composition](../../packages/storage/src/state-root-composition.ts)、[Host Kernel](../../packages/runtime-host/src/server/host-kernel.ts)、[storage maintenance](../../packages/runtime-host/src/server/storage-maintenance.ts)。 + +## 4. 固定 Composition 与 Domain 生命周期 + +Composition descriptor 在 listener 启动前确定;Module 及其依赖在启动期间构造,Ready 后不动态注册。依赖直接传入,不通过 Module 名称查找服务。每个业务 operation 恰有一个 Module handler;重复 owner 是构造错误。进程、访问和诊断控制仍由 Kernel 负责。 + +Module 契约包括 `handlers`、`recover(phase)`、`beginDrain()`、`close()`,以及可选的 `releaseConnection()`。一个 Module 可以组合多个 coordinator,它不是独立进程,也不要求与目录结构一一对应。 + +| 恢复 phase | 必须先建立的条件 | |---|---| -| Session | 持久存在的对话与 workspace context | -| Turn | Session 中一项被记录的顶层工作,可以由用户或 Host 发起 | -| Run | 为一次 Turn 执行 model 与 tool work 的持久实体 | -| Root execution | 一个 Session 当前唯一被 admit 的顶层 execution | +| `state` | 可解释的持久业务与控制状态 | +| `resources` | 资源身份、遗留进程/资源记录及其恢复结果 | +| `executions` | admission、Run 和 continuation 的一致性 | +| `domains` | Goal、计划等业务 owner 的恢复与执行结果协调 | +| `schedulers` | 前面状态就绪后,允许调度器产生新工作 | -## 一次 Turn 如何穿过系统 +关闭按 Module 构造逆序执行。某个 drain/close 失败不跳过其余 owner,最终聚合错误;Store 必须在 writer lease 释放前关闭。Module 不能把外部 I/O 或执行 Promise 隐藏在生命周期之外,否则 Kernel 无法证明可以退出或 handoff。 Drain/close 的取消信号也必须覆盖正在启动、排队或等待 I/O 的工作,并在异步等待后重新检查,避免关闭开始后出现新的 activation。 -```mermaid -sequenceDiagram - participant Client - participant Kernel as Host Kernel - participant Domain as Domain Module - participant Execution as Hosted Execution - participant Runtime as Maka Runtime - participant Store as Durable Stores - participant Continuity as Session Continuity - participant Capability as Client Capability - - Client->>Kernel: 提交一条消息 - Kernel->>Domain: 路由已认证的 operation - Domain->>Execution: 为 root execution 完成 reservation 与 admission - Execution->>Runtime: 启动这一 execution - loop Model 与 tool work - Runtime->>Store: 写入 durable facts - Runtime-->>Continuity: 发布带大小限制的 live event - Continuity-->>Client: 发送下一个 sequenced update - opt 所选 tool 需要 Client environment - Runtime->>Capability: 调用已冻结的 capability binding - Capability->>Client: 发起有界 reverse call - end - end - Store-->>Continuity: 重建 canonical state - Continuity-->>Client: 返回新的 snapshot -``` +实现:[Module contract](../../packages/runtime-host/src/server/host-composition.ts)、[interactive assembly](../../packages/runtime-host/src/server/execution-composition.ts)。 -请求到来前,Host Composition 已经创建这些组件,并把每个业务操作分配给一个 Domain Module。进程状态、diagnostics、upgrade 与 access credential 操作仍由 Kernel 拥有。Composition 是启动方案,不是每次请求都要调用的一层 service。 +## 5. Root admission 与执行结果 -以用户消息为例:Kernel 负责认证和路由,但不解释消息;拥有该操作的 Domain 应用 message 与 Session 规则,并在 root work 可以开始时使用 Hosted Execution。第一次 provider request 之前,Run Composer 会冻结并持久化 prompt 与 tool 基线。Runtime 写入 canonical facts,Session Continuity 再把这些事实投影给所有 Client。 +### 5.1 两层串行化 -Scheduled Task 使用同一条 execution path,只是它从自己的 Domain 内部启动,而不是由已连接的 Client 发起。这也是 Client disconnect 不会决定 execution lifetime 的原因。 +`SessionAdmissionGate` 提供每 Session 的短临界区;需要多个 Session 时按稳定顺序获取。显式 lease 可以在已准入上下文中继续操作,隐式嵌套获取会被拒绝。执行脱离 admission 的异步上下文,不在整个模型请求期间占用这把锁。 -## Host 的几种身份 +`RootTurnCoordinator` 在此之上管理 pending reservation 与 active execution: -这些值回答不同的问题,不能相互替代: +```text +每个 Session:正在准入或执行的逻辑 root execution 最多一个 +不同 Session:可并发 +child Session / Graph lineage:仍走自己的执行和 lineage 约束 +``` -| Identity | 直观含义 | 生命周期 | -|---|---|---| -| State Root | 保存 Host 持久状态的目录;同一时刻只有一个进程持有其排他写 lease | 跨 Host 进程存在 | -| Host Epoch | 当前持有该 lease 的进程 identity | 随该进程结束 | -| Composition ID | 允许解释该 State Root 的 Host program 类型 | 持久绑定到 root | -| Composition Revision | Client 期望连接的 Composition revision | startup wiring 或 compatibility 改变时更新 | -| Host Generation | 由本地 owner Client 请求的 replacement generation | 一个 product version 共用,或仅属于一个 development Client process | +`prepare()` 返回可消费一次的 reservation 或 busy/unavailable。`RootAdmissionOwner` 持久化确切执行意图,包括 Session、Turn、Run、用户消息、source messages 和 predecessor chain。相同 ID 但不同意图不是可接受的重试;无法证明 admission chain 一致时必须 fail closed。 + +从进入异步 admission 之前开始持有 `drain` residency,直到执行接管或准入失败后释放。这封住“请求已经进入,但 Kernel 还看不到任何活跃工作”的退出窗口。 + +### 5.2 执行句柄与持久事实 -重启会改变 Host Epoch;Composition 变化可能改变其 Revision;product version 更新或 development Client 重启可能改变 Host Generation。这些变化都不会隐式移动 State Root,也不会改变持久绑定的 Composition ID。 +成功准入返回该次执行的 `snapshot`、`completion` 和 `settled`: -例如,绑定到 interactive Composition 的 State Root 不能被另一种 Composition 打开;interactive Composition 自身可以演进到新 revision,而不改变这项持久 identity。 +- `completion` 描述完成、失败、取消,或无法确认 authority 的明确结果。 +- `settled` 表示相关执行清理已结束。 +- Domain 必须保存这次执行返回的句柄,不能仅凭 Session ID 重新拼出一个等价句柄。 -## 各组件分别负责什么 +这两种 Promise 服务不同判断,不应把回调触发顺序当成新的持久化保证。进程内订阅是 invalidation hint;丢通知、重连或恢复时重新读取 admission、RuntimeEvents 与控制 Store。 -### Host Kernel 拥有进程生命周期 +实现:[Session admission](../../packages/runtime-host/src/server/session-admission-gate.ts)、[Root admission](../../packages/runtime-host/src/server/root-admission-owner.ts)、[Root execution](../../packages/runtime-host/src/server/root-turn-coordinator.ts)、[public execution contract](../../packages/runtime-host/src/server/hosted-execution-authority.ts)。 -Kernel 取得 State Root 的排他写 lease,启动 listeners,并认证连接。认证会为该 connection 生成一组不可变的 permissions。Kernel 还会跟踪 active operations 与 **residencies**——让进程必须继续存活的明确原因——并驱动 Composition recovery、drain 与 close。 +## 6. 模型输入、工具版本与权限激活 -Kernel 不解释 message、tool、Goal 或 Scheduled Task 等业务状态。新增业务行为通过 Domain Module 接入,而不是给 Kernel 状态机增加分支。 +`RunComposition` 是一个 Run 的不可变 C0 基线:记录 composer/source revisions、system prompt/tool catalog/tool availability/provider options 的哈希、tool names 和 context window。它不是整份 prompt 的另一份存储,也不表示模型工具集合在整个 Run 内绝不变化。 -### Host Composition 是固定的启动方案 +基线在首次真正 provider dispatch 前持久提交;提交失败不得调用 provider。动态工具变化由 `RequestComposition` epochs 表达,不能在提交 C0 时重新采样并悄悄覆盖原基线。恢复或 handoff 的 successor 必须验证实际输入语义,不把“字段形状相同”当成兼容。 -Composition ID、revision 与 construction function 在 listener 启动前选定。Modules 在启动期间只创建一次,并在 Host Ready 后保持不变。Diagnostics 直接读取已创建 Composition 的实际 Module IDs,不维护第二份列表。 +权限变更与 backend activation 通过短暂的 `RuntimePolicyActivationGate` 串行化。它保护“检查策略到激活执行”的窗口,不包住整个模型调用。策略 authority 不确定时阻断后续执行;只读 projection 变旧与权威写入失败是不同故障。 -每个业务操作只有一个 Module owner。Composition 组合这些 owner,不保留平行的 handler 或 lifecycle 实现。Kernel operations 不属于 Domain Module。 +模型 catalog 由 Host 根据持久 connection/model 配置和 Host metadata 解析并投影;Client 编辑尚未保存的 draft 等没有 Host 权威状态的场景才在本地解析。显示用 slug 不替代不可变 connection identity。 -例如,interactive Composition 会创建 Session、Scheduled Task 等 Modules,以及它们使用的 Stores 与共享 execution authority。这个列表在 Host process 内只选择一次。Composition 不是 dynamic plugin registry,也不是每个 Session 各自拥有的配置。 +实现:[Run Composition schema](../../packages/core/src/run-composition.ts)、[model composition](../../packages/runtime-host/src/server/execution-model-composition.ts)、[policy activation](../../packages/runtime-host/src/server/runtime-policy-activation-gate.ts)。 -Recovery 使用五个固定 phase: +## 7. Canonical observation 与消息交付 -1. `state` -2. `resources` -3. `executions` -4. `domains` -5. `schedulers` +普通 Runtime Session 的 transcript 从 durable RuntimeEvents 投影。SQL 查询层负责定位不可变事件顺序,投影器负责消息语义;不再让另一个独立 message 表与 RuntimeEvents 同时解释普通 Run 历史。历史数据仍有兼容转换路径,WorkHub Coordination Session 则有独立的领域契约,不应混入普通 Run 规则。 -这个顺序保证 durable state 与 resources 先就绪,随后恢复 executions 与 business domains,最后才启动 schedulers。 +打开 Session subscription 时原子地取得 canonical snapshot、`nextSeq` 和 active stream IDs。open response 先于后续 subscription frames 写出。Live sequence 是连接观察协议;transcript cursor 与持久 event/message ordinal 是分页身份,不能假设它们是同一计数器或都连续加一。 -Close 按 Module 反序执行。Drain 与 close 会尝试每个 owner,并聚合失败。 +较大的 transcript 通过有界分页读取。Cursor 绑定 subscription、Session、来源、方向与 watermark,并校验完整性;bootstrap、page、单 Turn 投影工作量和 active overlay 分别有界。Client 遇到 sequence gap、HostEpoch 变化、subscription 丢失或 cursor 失效时重新打开并读取 canonical state。PTY 有独立的背压/订阅边界,不应拖垮普通 Session 观察。 -### Domain Module 负责一组操作及其生命周期 +对发送侧,连接恢复不等于可以重新执行 command。Query 可按自己的只读契约重试;command 已发送但没收到结果时,保留 outcome unknown,通过该 Domain 的确切请求 ID、admission 或结果记录协调。Desktop 的 durable outbox 保留消息与附件身份,并把崩溃时的 `sending` 恢复为 `unknown`;这不是传输层的通用重放。 -Domain Module 是一条静态记录,用于回答四个问题: +实现:[Session Continuity](../../packages/runtime-host/src/server/session-continuity-coordinator.ts)、[transcript reader](../../packages/runtime-host/src/server/session-transcript-reader.ts)、[pager](../../packages/runtime-host/src/server/session-transcript-pager.ts)、[Desktop local store](../../apps/desktop/src/main/session-local-store.ts)、[local service](../../apps/desktop/src/main/session-local-service.ts)。 -- 这一组职责处理哪些 protocol operations; -- 每个 startup phase 需要恢复什么; -- drain 时必须拒绝哪些新工作; -- close 时需要释放哪些 resources 与 connection-scoped state。 +## 8. 传输、认证与 Client Capability -例如,Scheduled Task Module 拥有 Scheduled Task operations,恢复 durable scheduling state,只在 recovery 完成后启动 scheduler,并在 shutdown 时停止和关闭 scheduler。Task 触发后,Module 仍然请求共享的 Hosted Execution authority 执行它,不会创建另一套 Runtime。 +### 8.1 公共协议,多种连接路径 -Module 不一定对应独立 process、package 或源码目录。它可以表示一个聚焦功能,也可以表示生命周期紧密相关的一组职责。Construction code 直接传入依赖;Module 不会在 runtime 按名称查找依赖。 +| 路径 | 连接与信任边界 | +|---|---| +| Local IPC | UDS 或 Windows pipe;验证本地同用户边界后授予 Local Owner | +| TLS WebSocket | 先认证再升级,接入时重新校验;不能静默降级为明文 | +| SSH tunnel | Operator/Client 显式建立 tunnel,再使用 Host 协议;tunnel 属于连接生命周期 | +| 明文 WebSocket | 仅显式确认的不安全配置;不作为 TLS 的自动 fallback | +| Native Peer stream | PeerId 验证与端到端传输之后,仍执行 Host credential 和协议准入;不是 WebSocket | -Domain 决定 execution result 的业务含义和下一步动作。Hosted Execution 只拥有 execution lifecycle。 +各路径复用 Host operation codecs、dispatcher、连接权限和 canonical state。Frame size、inflight、writer queues、subscription 数量与反向调用都有边界。Read pump 与异步 handler 分离,使 Host 正在处理请求时仍能收到 reverse-call 响应。普通请求超时只结束该请求的等待;liveness 失败才关闭连接,不能混为一个超时机制。 -### Hosted Execution 控制 Session 的顶层工作 +连接的 principal、operation grants 与 path/capability 权限在准入时固定。Credential prepare/finalize、轮换或撤销以 durable access state 为准;更新后的 authority 通过新连接生效。操作集合显式授权,新增协议 operation 不自动扩权。持久撤销先于通知;提交结果不确定时必须保守 fence。 -**Admission** 是为一个确切 root execution 原子保留 Session 的决策,用来防止两个顶层 Turn 并发运行。 +### 8.2 Reverse call 的执行切点 -Admission 成功后返回三个相关值: +Client 发布有版本、有大小限制的 capability offer;Host 按 principal、provider instance、contract 与 `call`/`turn`/`session` affinity 选择 binding。Session affinity 的 provider 丢失不能静默切换到另一台机器;remote 工作也不能随意借用无关 Client 的本地能力。 -- `snapshot`:execution 被 admit 时观察到的状态; -- `completion`:状态为 completed、failed 或 cancelled 的 terminal snapshot,或者明确的 `authority_error`; -- `settled`:execution cleanup 已结束、临时 resources 已释放的信号。 +Reverse call 区分: -Domain 使用 `completion` 判断业务结果,使用 `settled` 判断 cleanup 是否结束。它保留这次确切 execution 返回的 handles,而不是事后通过 Session ID 或 Turn ID 重新拼装。 +1. Host 发起调用,provider 返回 `accepted` evidence。 +2. Host 校验 policy/grant 后发出 `admitted`,允许 effect 执行。 +3. Provider 返回结果,Host 按调用身份提交一次。 -Hosted Execution subscription 只告诉同一 Host Epoch 内的 observer“可能发生了变化”,不能证明新状态是什么。Recovery 始终重新读取 durable facts。 +`admitted` 之前断线是 capability loss;之后断线或超时可能是 outcome unknown。工具 journal 保留这个区别,不自动重做未知外部 effect。Client 可以执行本机 UI/MCP 等能力,但不拥有 Host 的 Run、Session 或执行恢复。 -### Session Continuity 负责 Client 如何观察 Session +实现:[connection session](../../packages/runtime-host/src/server/connection-session.ts)、[outbound writer](../../packages/runtime-host/src/server/serial-outbound-writer.ts)、[access authority](../../packages/runtime-host/src/server/access-authority.ts)、[capability coordinator](../../packages/runtime-host/src/server/client-capability-coordinator.ts)、[invocation broker](../../packages/runtime-host/src/server/client-capability-invocation-broker.ts)。 -Session Continuity 是 live Session 面向 Client 的公开 read model。打开 subscription 会返回 canonical snapshot、下一个预期 sequence number,以及仍处于 active 状态的 assistant stream identities。可能更大的 transcript 通过单独、带大小限制的 snapshot 读取。 +## 9. Owner profile、Guest mount 与 Client-local state -Live projection、assistant 与 tool updates 都有明确的大小限制和 sequence number。发生 connection loss、Host Epoch 变化、sequence gap 或 transcript snapshot 过期后,Client 应重新打开 subscription,并重读 canonical state。例如,Desktop 在模型输出期间 reload 时,会恢复当前 transcript 与 active stream identities,而不是重新发送用户消息。Stream delivery 永远不是 recovery authority。 +### 9.1 两种接入对象 -### Run Composer 冻结模型实际看到的内容 +Owner profile 是 Client 的连接配置;`local` 与启用的远端 Owner profile 独立连接。同一 State Root 不重复启用多个 Owner profile。默认 Host 只用于新建工作和没有既有 Host scope 的操作,切换默认项不搬迁 Session,不关闭其他连接。Environment profile 的部署/激活信息仍与 Host 运行时 authority 分开。 -Run Composer 冻结一次 Run 的 model-visible 基线:base system prompt、tool catalog、tool availability policy、base provider options,以及构建这些内容时使用的 input revisions。 +Guest 共享任务是独立的 **Session mount**,不进入 Owner profile catalog,也不能成为默认 Host。Guest credential 和保留的共享 Session projection 由 mount store 管理;一个 root 可有多个共享 Session mount。旧实验版本把 Guest 写成 remote profile 的数据在启动时迁移/清理,不能据此扩大当前 profile 的权限。 -第一次真实 provider request 前必须: +| 操作/状态 | Owner profile | Guest mount | +|---|---|---| +| 新任务 Host、Project/model/settings catalog | 参与,仍受具体权限约束 | 不参与 | +| 观察共享 Session | 由 Host 权限决定 | 仅有效 Session grant 覆盖的投影 | +| 提交共享任务输入 | 正常 Domain admission | 提交确切 turn request,由 Owner 决策后进入 canonical admission | +| 离线 | 保留配置并恢复连接 | 保留 mount,不等同 grant 撤销 | +| 凭据拒绝或 Session access 失效 | 按连接/权限错误处理 | 持久化 access failure,并清理不能再展示的共享状态 | -1. 创建 immutable Run Composition snapshot; -2. 将其提交到 AgentRun Store; -3. durable commit 成功后才能调用 provider。 +Guest 观察 grant、turn-request grant、Host operation grant、工具 sandbox permission 和 Client Capability grant 是不同契约。Mesh membership 也不隐含其中任何一个。 -Composition 或 persistence 失败时不调用 provider。没有到达 provider dispatch 的 Run 不伪造 composition snapshot。 +### 9.2 隔离与本地持久化 -### Client Capability 让 Host 安全调用 Client 能力 +Desktop 根据 `(rootId, sessionId)` 与 `targetEpoch` 路由操作和事件。Guest 连接状态不能作废 Owner 的新任务 catalog;Owner 替换仍必须让旧 catalog 失效。本地 outbox/历史缓存按 profile incarnation、root 和 credential identity 分区,缓存内容不授予实时权限;Guest 不复用 Owner 的离线历史缓存策略。 -Authenticated Client 可以发布带大小限制、带版本的 tool 或 service **offers**,描述自己能够做什么。Runtime Host 选择确切的 provider **binding**;Run 仍通过正常的 Run Composition 路径记录所选 model tools。对于必须在 Client 环境执行的 effect,Host 可以发起有界 reverse call,例如调用 Desktop 发布的 OS-facing capability。 +Client preference、profile、Guest mount、部署绑定和 outbox 是不同的数据所有者,均不等同 Host 的 operational DB。`runtime-host-deployments.json` 的读取可能执行迁移,因此也需要写互斥:当前使用进程生命周期 OS lease;Desktop 在获得单实例权限后、并发打开 Store 前回收旧版本的空目录锁。不能在普通读取时根据锁年龄抢锁,也不能把失败读取当成空配置覆盖原数据。 -发布或调用 capability 不会把 Session、Run 或 execution ownership 转移给 Client。Connection loss 会使对应 provider unavailable;拥有该操作的 Domain 仍通过自己的 durable contract 处理 capability loss 或明确的 result-unknown outcome。 +实现:[profile service](../../apps/desktop/src/main/runtime-host-profile-service.ts)、[Guest mounts](../../apps/desktop/src/main/runtime-host-guest-session-mounts.ts)、[Desktop manager](../../apps/desktop/src/main/runtime-host-desktop-manager.ts)、[preload catalogs](../../apps/desktop/src/preload/preload.ts)、[deployment bindings](../../apps/desktop/src/main/runtime-host-managed-services.ts)、[process-lifetime file lock](../../packages/storage/src/process-lifetime-file-update-lock.ts)。 -### Host profile 描述连接目标 +## 10. Host 存活、drain 与 Client 生命周期 -Host profile 是 Client-owned connection configuration,不是 Host state。内置 `local` profile 保留现有的零配置 Local IPC 与 candidate spawn 路径。Remote profile 包含显示名称、一种明确的 transport(Direct TLS、SSH tunnel 或已确认风险的明文连接)和必填的 State Root identity;access credential 会单独保存,并绑定到这个 profile 的确切 target。一个 profile ID 对应不可变的 target:改变连接方式、endpoint 或 root 时必须创建新的 profile ID;显示名称与 credential 可以原地更新。 +Host 的自然 idle exit、优雅 drain、launcher 退出和 operator stop 是不同事件。 -启用 profile 会让 Client 连接对应 Host。同一个 Desktop 对同一 State Root 最多启用一个 profile,避免同一个 Host 以不同连接配置重复出现。启用操作不会移动 Project 或 Session、改变 Host Epoch,也不会修改 Host。所有 remote transport 最终都进入同一 authenticated WebSocket connector,绝不 fallback 到本地 discovery 或 candidate spawn。Tunnel 是 connection-scoped resource:reconnect 会创建新 tunnel,tunnel 关闭或丢失也会关闭对应 connection。每次远程连接都固定 profile 中的 State Root identity;endpoint 给出不同 root 时必须失败。 +| Residency | 阻止自然 idle exit | 表示 drain 必须等待的活跃工作 | 典型持有者 | +|---|---|---|---| +| `idle` | 是 | 否 | 待触发定时任务、armed/paused Goal、空闲 Daily Review | +| `drain` | 是 | 是 | admission、执行、持久化交接、活跃资源工作 | -Desktop 会让 `local` 与所有已启用的 remote profile 独立保持连接。其中一个 profile 是默认 Host,只用于创建新 Session 和其他没有现成 Host scope 的操作;改变默认 Host 不会重连 Host,也不会移动已有 Session。一个 remote connection 失败不会中断 Local 或其他 remote Host。 +Ephemeral Host 的自然退出要求:没有已接纳连接、进行中的 handshake、活跃 operation,以及任何 residency。维护/替换判断可以区分 idle retention 与真实 drain work;不能拿一个总数代替所有场景的退出条件。 -Desktop Settings 使用显式 Host selector 管理 Host-owned 配置。外观、语言等 Client-owned 偏好仍是 Desktop 唯一一份设置,不随该 selector 改变。 +普通 Client 断线释放 connection-scoped subscriptions、capabilities 和 controller lease,不自动取消已准入工作。Desktop 退出会先对当前拥有的 ephemeral Host 做有界 retirement preparation,让 Host 按选定模式重新检查活跃工作并关闭 admission;quit 不等待进程退出,也不启用 cooperative handoff。Host 不可达不能阻止 Desktop 退出,launch-owner guard 仍在 launcher IPC 丢失时关闭其拥有的 Host。TUI detach、一次性 CLI 所有的 invocation 和 operator 管理的 Service Host 也分别遵循其生命周期契约。因此不能保证“关闭任意 Client 后任务必然继续”。 -Desktop 会聚合所有已连接 Host 的 Session summary。产品中的 Session identity 是 `(Host rootId, Session id)`,因此不同 Host 上相同的 Session id 仍是两个不同 Session。Request、event 与 persistent Client-local resource 都会路由回拥有该 Session 的 Host。Transport scope 还包含 Client target Epoch(`targetEpoch`),用于在 Desktop 替换该 profile 的 connection lifecycle 后阻止迟到的 request 或 event。Client target Epoch 不是 Host Epoch,也不是 authentication boundary。 +资源进程由 Host 管理。Shell/PTY 在 spawn 前记录身份,观察权与控制权分离;控制使用 connection/controller identity 和顺序约束。断开观察不等于终止进程。Host 重启后的遗留资源以可证明的 OS 身份协调,PID 本身不足以安全终止另一进程,也不能声称所有逃逸子进程都已结束。 -已启用 profile 与默认 profile 是持久化偏好,不代表 connection 已 ready。Remote profile 不可用时,Desktop 仍会显示它,供用户重试或停用。TUI 与 CLI 仍是单 Host Client:启动时解析一个 profile,并把 profile 不可用作为错误报告。 +实现:[residency registry](../../packages/runtime-host/src/server/host-residency-registry.ts)、[Kernel lifecycle](../../packages/runtime-host/src/server/host-kernel.ts)、[launcher guard](../../packages/runtime-host/src/candidate-launch-owner-guard.ts)、[Desktop quit](../../apps/desktop/src/main/runtime-host-quit.ts)、[resource coordinator](../../packages/runtime-host/src/server/runtime-resource-coordinator.ts)。 -Remote Desktop generation 不能提交任意 Host path。它读取 Project summary、提交 Project ID,并阻止 Client-local capability 收到远端 Host path。目录选择、Git review、workspace search 和打开 Skill 文件等本地文件系统操作只在 `local` 下可用。 +## 11. 协作式 handoff 与崩溃恢复 -Operator 与 Client 的配置流程见[连接远程 Runtime Host](../runtime-host-remote-access.zh-CN.md)。 +### 11.1 不可逆切点 -### Runtime Host 解析 workspace +本地升级以观察到的 HostEpoch 和当前 activity 为条件。Client 的诊断快照只能指导交互,不能授予 kill 权限。共享 handoff 流程重新观察目标;自动替换只适用于可证明的 idle 或受支持的 cooperative handoff,主动中断工作需要相应授权。Service/安装归属继续由 deployment owner 管理。 -Client 必须使用下面两种 target form 中的一个来表达 workspace: +下面的顺序图只描述 cooperative 成功路径;超时、无法覆盖全部工作或安全校验失败时,不保证可以进入该路径。 -```ts -type WorkspaceTarget = - | { kind: "project"; projectId: string } - | { kind: "host_path"; path: string }; +```mermaid +sequenceDiagram + participant Old as Old Host + participant Log as Durable execution facts + participant New as Successor Host + Old->>Old: Hold admission and scheduler triggers + Old->>Old: Reach a durable model/tool boundary + Old->>Log: Seal pause and successor claim + Old->>Old: Prove exact drain residency coverage + Old->>Old: Commit fence, detach, release writer lease + New->>New: Acquire writer lease and recover + New->>Log: Verify claim, prefix and composition + New->>Log: Open successor Run for the same logical Turn ``` -`project` 是可跨机器传递的形式。Runtime Host 通过自己的 Project Catalog 解析它,并返回 canonical target 与 `hostCwd`;`hostCwd` 是 Host 上的绝对目录。`host_path` 只供被明确允许指定 Host path 的 Client 使用,例如从本地 checkout 启动的 CLI。 +`runtime_handoff_pause_v1` 记录原 root Run、successor Run、invocation/claim 和剩余步骤等事实。结束旧物理 invocation 不写成逻辑 Turn 的终止;新 Run 必须匹配 claim、不可变事件前缀、lineage 和输入语义。 -Project summary 不暴露已注册的 location。`canUseHostPaths` 控制 Client 能否在 operation 中指定 Host path,不是 path confidentiality boundary。Canonical Session projection 可以包含解析后的 `hostCwd`;remote Client 只能把它当作 Host metadata,不能当作 Client filesystem path。读取或修改 Project location、让 Host reveal path 仍是各自独立的 operation,而提交 `host_path` 必须具有 Host-path authority。 +Kernel 要求 handoff 返回确切的 residency handles,并证明没有遗漏其他 `drain` 工作;标签或计数相同不构成该证明。最终证明与提交 fence 之间不能有异步窗口。提交前取消可以释放 hold、继续原工作;越过不可逆切点后,必须先使事务收敛,不能因取消重新启动旧 Run。 -Client 不把 path 与 Project ID 拼在一起,也不自行解析 Host path。Desktop 会按 State Root 在本地记住所选 Project;选择它不会修改 Host 全局状态。Remote Desktop 可以使用不透明 root ID 和经过校验的 path segment 浏览 Host 明确发布的目录,并请求 Host 通过 Project Catalog 注册所选目录;它不能指定或查看这些 root 之外的路径。Desktop 不能打开 Client-local directory picker 并假装它选择了 Host directory;CLI/TUI 也不能通过 Client filesystem 重新解释、验证、迁移或补全 Host path。 +Runtime 在可持久化的模型/工具边界协作暂停,不热迁移任意 provider stream、执行中的外部 effect 或 PTY。Prompt、tools、provider options、context window 或 sandbox provenance 不兼容时,successor 不能直接继续调用模型/工具。 -## 生命周期 +### 11.2 崩溃不等于协作暂停 -两种 Host lifetime 使用同一个 Kernel 与 Composition: +启动恢复先检查 admission、source-message proof 与 Run identity,再执行修复或 continuation。已准入但尚未创建 Run 的工作、已运行的旧 Run、带有效 handoff claim 的执行,以及安全边界 continuation,分别走自己的恢复分支。安全条件缺失的 continuation 可以 parked,不能把它当作“再运行一次”。 -| Host 类型 | 由谁管理生命周期 | -|---|---| -| Ephemeral Host | 由本地 Client 启动;没有 connection、operation 或 residency 要求其继续存活时可以退出 | -| Service Host | 由 deployment owner 运行;Client generation 不能替换它,也不使用 Client 驱动的 idle exit | +Goal、Scheduled Task 和 Daily Review 各自持久化业务意图,恢复时与统一 execution authority 对账。未曾运行的 armed Goal 不应因重启凭空启动;Scheduled Task 的 pending fire 保留确切 admission 身份。旧进程中的交互 Promise 无法从磁盘复活,已提交的回答与仍可继续的 invocation 也不是同一个事实。 -| 阶段 | Contract | -|---|---| -| Startup | 取得 State Root lease,绑定 Composition identity,创建 Composition,恢复 Modules,启动 schedulers,最后发布 Ready | -| Request | Authentication、input limits 与 connection permissions 检查,再路由到 Kernel 或唯一的 Domain Module handler | -| Execution | 通过 Hosted Execution reservation 与 admission,再重读 durable facts 确认最终状态 | -| Drain | 停止接收新工作,同时让已接收工作结束或到达可恢复状态 | -| Close | 停止 listeners 接收连接,drain operations,反序关闭 Modules,清理 listeners,最后释放 State Root lease | +实现:[Client handoff](../../packages/runtime-host/src/client/host-handoff.ts)、[Runtime handoff gate](../../packages/runtime/src/run-handoff-gate.ts)、[logical execution](../../packages/core/src/runtime-logical-execution.ts)、[root recovery](../../packages/runtime-host/src/server/root-turn-coordinator.ts)、[Goal](../../packages/runtime-host/src/server/goal-coordinator.ts)、[Scheduled Task](../../packages/runtime-host/src/server/scheduled-task-coordinator.ts)。 + +## 12. Workspace 与部署 authority + +`WorkspaceTarget` 只有 `{ kind: "project", projectId }` 和 `{ kind: "host_path", path }` 两种形式。Host 通过 Project Catalog 或获准的 Host path 解析 canonical workspace。Client 不用本机文件系统解释 remote `hostCwd`;`canUseHostPaths` 控制是否能提交路径,不是路径保密承诺。 + +Remote 目录浏览使用 Host 发布的 opaque root ID 与验证过的 path segments,并检查 realpath containment,不能通过 symlink 或 Client 本地 picker 扩大范围。 + +安装 authority 与 writer authority 同样分开:account-local deployment owner 按 root identity 和 CAS revision 协调 Desktop、CLI、managed service 或 development 的安装归属;managed deployment 文档描述当前配置及 `active`/`transition`/`blocked` 恢复状态。Service artifacts 是该配置的投影,不另设一份互相竞争的部署日志。 -Client disconnect 只释放 connection-scoped resources,不取消已经 admission 的 execution。 +更新需要验证包版本/integrity、准备目标、确认实际 target Ready,再提交安装状态。重试要识别已经成功的 successor,不能因旧进程信息再次终止新进程。普通 remote credential 不授予机器上的 operator 安装管理能力;SSH operator activation 是另一个显式边界。 -### 本地 Ephemeral Host 的升级交接 +实现:[workspace resolver](../../packages/runtime-host/src/server/workspace-resolver.ts)、[local deployment owner](../../packages/runtime-host/src/operator/local-deployment-owner.ts)、[managed deployment](../../packages/runtime-host/src/operator/managed-deployment.ts)。 -Host Generation 与 protocol compatibility 是两个事实:两个本地 Client generation 即使使用相同 protocol,也可能请求替换进程,让所请求的 Runtime generation 成为 authority。本地 owner Client 请求 drain 时必须带上自己观察到的 Host Epoch,因此过期 Client 无法 drain 后来启动的替代进程。下一个 Host 会等待现有 State Root lease 释放。 +## 13. 故障收敛与诊断 -Startup 发现另一个 generation 时,Host 可以返回数量受限的 active connections、operations 与 residencies。这些数量用于解释进程为什么仍然存活,不允许 Client 直接杀死它。只有本地 ephemeral Host 支持 replacement,中断 active work 必须经过 Client 的明确选择;Service Host 的升级仍由 deployment owner 负责。等待中的 Client 会停止连接尝试,直到所观察的 Host 退出,因此等待本身不会让原本应 idle exit 的 Host 继续驻留。 +| 观察到的故障 | Authority 与收敛方式 | 禁止的推断/动作 | +|---|---|---| +| Root owner 或 composition 不匹配 | 停止进入业务,报告实际冲突 | 通过删 registration 或改 PID 绕过锁 | +| Peer 没有可用/可恢复路由 | 网络层给出 reachability 状态,Client 保留退避与恢复入口 | 当作凭据撤销,或不断自行唤醒重连 | +| 相同或交替的重连失败 | 同次断线保留一个当前诊断:计数、首次/最近时间、最新错误;开始与恢复各记日志 | 每次 retry 追加堆栈挤掉其他诊断 | +| HostEpoch 或 live sequence 改变 | 重新建立观察并读取 canonical state | 重放已发送 mutation 来重建 UI | +| Guest 断线 | 独立恢复 mount | 作废 Local 新任务目录或修改默认 Host | +| Client 数据文件遗留锁 | 由已证明的 owner/OS lease 恢复;未知内容保留 | 普通读请求抢锁,或以空配置覆盖失败读取 | +| command / admitted capability 结果不明 | 通过 Domain 记录协调或保留 unknown | 承诺外部 effect exactly once | +| drain/close 某个 owner 失败 | 继续其余释放并聚合错误 | 提前释放 writer lease,留下仍写入的 Store | + +诊断报告可在目标 Host 不可达时读取 Desktop 当前连接状态,不依赖远端 query。重试计数变化不触发目录重载;实际错误变化仍可更新连接状态。新的成功连接结束当前断线汇总,之后再失败是新的周期。诊断必须脱敏,也不拥有重试、恢复或替换 authority。 Host 协议连接因非法 frame、重复 request ID、配额或 writer 失败而 teardown 时保留一次有界失败诊断;这是实际连接失败的证据,与 Client 的常规离线重试汇总分开。 + +实现:[Desktop diagnostics](../../apps/desktop/src/main/main-process-diagnostics.ts)、[Desktop manager](../../apps/desktop/src/main/runtime-host-desktop-manager.ts)、[reconnect lifecycle](../../packages/runtime-host/src/client/reconnect-lifecycle.ts)。 -## 必须始终成立的规则 +## 14. 设计取舍与维护检查 + +| 选择 | 获得的性质 | 成本与扩展约束 | +|---|---|---| +| 单 root writer + 固定 Composition | 一条执行/恢复 authority,可证明关闭顺序 | 跨 Host 协作需显式协议,不能直接共享可写根 | +| 短 admission + 长执行句柄 | Session 冲突可串行化,模型 I/O 不持锁 | 每个入口必须维护 reservation、durable intent 与 residency 的衔接 | +| Durable facts + 有界 projection | Client 可独立重连,传输丢失不改写历史 | 需要 snapshot/cursor/sequence 与失效重建,不能无限缓存 live events | +| 不确定 effect 不自动重放 | 避免重复外部副作用 | Domain 必须定义确认、协调或人工处理路径 | +| 协作边界 handoff | 保留逻辑 Turn,安全替换物理 Run | 必须有可校验的 claim、前缀、输入语义和完整工作覆盖 | +| 断线汇总而非逐次日志 | 长时间离线不淹没诊断,当前失败仍可检查 | 不保留每次拨号的完整历史;永久失败仍走独立错误路径 | -1. 一个 State Root 最多有一个 writer owner。 -2. 一个 Session 最多有一个 root Hosted Execution 或 pending root admission。 -3. Local IPC 与 WebSocket 共享一个 routing table、permission model 与 canonical state。 -4. Transport 只负责 message framing 与 authentication,不拥有业务状态。 -5. Composition identity 在 listener 启动前固定,Module set 在 Ready 前固定。 -6. 一个业务操作只有一个 Module owner;进程与 access operations 仍由 Kernel 拥有。 -7. Notification 与 stream 不能替代 Store 成为 recovery authority。 -8. Provider dispatch 等待 Run Composition durable commit。 -9. Domain lifecycle 与 execution lifecycle 保持分离。 -10. 一个 owner 关闭失败时,shutdown 仍继续关闭其余 owner。 -11. 只有 Runtime Host 能把 `WorkspaceTarget` 解析为 canonical Host path。 -12. Client-local capability execution 不会把 Runtime ownership 转移出 Host。 -13. Stream 中断后,Client 从 canonical snapshot 重建 observation。 +变更前至少确认:是否增加了第二个 writer/执行 owner;新工作能否从 admission 到 cleanup 始终被追踪;状态属于 durable fact、projection 还是 Client preference;连接/进程/安装的代际是否混用;mutation 结果不明时是否可能重复 effect;Guest/网络身份是否被意外升级成 Owner 权限。 -## 失败如何收敛 +以下测试是对应契约的入口,不代表所有 OS、NAT 或部署组合已实机验证: -| 失败 | 必须遵守的行为 | +| 契约 | 回归入口 | |---|---| -| Composition mismatch | 在 listener 或 Domain Store mutation 前失败;报告终态 incompatibility,不重复启动 Candidate | -| Host crash | 下一个 Host 重读 Stores,并安全地重复 recovery,直到 execution 与 Domain state 收敛 | -| Notification 丢失 | 重读 canonical projection,不能从 callback delivery 推断 terminal state | -| Session stream 丢失 | 重新打开 subscription,并重读 snapshot 与 transcript | -| Run Composition 失败 | 不调用 provider | -| Client disconnect | 已 admission 的工作继续由 Host 持有 | -| Client Capability 丢失 | 暴露有界的 capability-loss 或 outcome-unknown state,不静默重试结果不确定的 effect | -| Partial shutdown failure | 聚合错误,同时继续释放其余 resources | - -Runtime Host 不保证任意 external side effect 恰好发生一次。如果 connection 在 dispatch 后丢失,Host 可能只能确认 outcome unknown。Tool 或 resource contract 必须保留这种不确定性;除非 operation 明确允许,否则不能自动重试。 - -## 协议与安全边界 - -- Protocol message 使用拒绝未知字段的 closed schema、明确的大小与数量限制,以及稳定的 error code。 -- Authentication 在 protocol connection admission 前完成。 -- Local IPC 只有在操作系统 endpoint 建立 same-user 信任边界后,才能授予 Local Owner authority。 -- Authentication 会在 connection 的整个生命周期内固定 principal、允许的 operations,以及 path 或 capability access。 -- Client Capability offers 与 reverse calls 必须经过认证、带大小限制,并绑定到该 connection。 -- 新增 protocol operation 不会扩张既有 credential grant。 -- Status 与 diagnostics 只公开 bounded、redacted 的 lifecycle 与 composition facts。 - -## 代码阅读地图 - -- [`host-kernel.ts`](../../packages/runtime-host/src/server/host-kernel.ts):process ownership、listeners、connection lifecycle、drain 与 shutdown -- [`host-composition.ts`](../../packages/runtime-host/src/server/host-composition.ts):composition identity、Module contract、recovery 与 close order -- [`execution-composition.ts`](../../packages/runtime-host/src/server/execution-composition.ts):静态 coordinator 与 Module assembly -- [`hosted-execution-authority.ts`](../../packages/runtime-host/src/server/hosted-execution-authority.ts):root execution contract -- [`session-continuity-coordinator.ts`](../../packages/runtime-host/src/server/session-continuity-coordinator.ts):canonical Client observation 与 live stream continuity -- [`client-capability-coordinator.ts`](../../packages/runtime-host/src/server/client-capability-coordinator.ts):capability publication、binding 与 reverse-call lifecycle -- [`workspace-resolver.ts`](../../packages/runtime-host/src/server/workspace-resolver.ts):Project 与 Host-path workspace resolution -- [`run-composition.ts`](../../packages/core/src/run-composition.ts):durable Run Composition schema -- [`state-root-composition.ts`](../../packages/storage/src/state-root-composition.ts):persistent Composition binding - -## 小结 - -Runtime Host 只保留一条 ownership path:Kernel 控制 process;Composition 创建一组固定 Modules;Modules 拥有业务行为,Hosted Execution 控制顶层工作;Run Composer 记录模型看到的内容,Session Continuity 重建 Client 看到的内容,Client Capability 则允许 Host 对 Client 发起受限回调。Durable Stores 让这些组件在进程重启后恢复,而不产生第二个 Runtime owner。 +| Root 所有权、固定恢复与关闭 | [root-authority](../../packages/storage/src/__tests__/root-authority.test.ts)、[host-kernel](../../packages/runtime-host/src/__tests__/host-kernel.test.ts)、[host-composition](../../packages/runtime-host/src/__tests__/host-composition.test.ts) | +| 确切 admission 与执行恢复 | [root-admission-owner](../../packages/runtime-host/src/__tests__/root-admission-owner.test.ts)、[root-turn-coordinator](../../packages/runtime-host/src/__tests__/root-turn-coordinator.test.ts) | +| 输入、观察与能力调用 | [execution-model-composition](../../packages/runtime-host/src/__tests__/execution-model-composition.test.ts)、[session-continuity](../../packages/runtime-host/src/__tests__/session-continuity-coordinator.test.ts)、[client-capability-recovery](../../packages/runtime-host/src/__tests__/client-capability-recovery.test.ts) | +| 升级与进程驻留 | [host-handoff](../../packages/runtime-host/src/__tests__/host-handoff.test.ts)、[host-residency-registry](../../packages/runtime-host/src/__tests__/host-residency-registry.test.ts) | +| Guest 与 Local 隔离 | [guest mounts](../../apps/desktop/src/main/__tests__/runtime-host-guest-session-mounts.test.ts)、[new-task preload](../../apps/desktop/src/main/__tests__/runtime-host-new-task-preload.test.ts)、[Desktop manager](../../apps/desktop/src/main/__tests__/runtime-host-desktop-manager.test.ts) | +| Client 锁恢复与诊断 | [managed services](../../apps/desktop/src/main/__tests__/runtime-host-managed-services.test.ts)、[profile service](../../apps/desktop/src/main/__tests__/runtime-host-profile-service.test.ts)、[diagnostics](../../apps/desktop/src/main/__tests__/main-process-diagnostics.test.ts) |