Desktop client adapters
Status: source-implemented client platform-adapter candidate; Desktop node-status, terminal, Workspace, and ephemeral Realtime token adapters are not release-qualified.
This directory owns reviewed host/native adapters for platform-neutral Maia
client ports. The explicit .web.ts and .web.tsx port projections in
src/platform select them through this folder's index.ts; features and
native-mobile projections do not import this boundary.
node-status.ts composes the
browser-safe public SDK with the dedicated Tauri node_status command and caps
requests at 2 KiB and responses at 128 KiB. The renderer still supplies only
the SDK-generated envelope: the native adapter validates the ordinary Node API
v1 descriptor, adds its bearer, and sends the read over fixed authenticated
numeric-loopback HTTP.
terminal.ts composes the public SDK with the Tauri
node_request and node_cancel commands; it is the one Desktop SDK transport
for every reviewed Node operation. workspace.ts reuses
that exact bounded SDK transport for Workspace context show/select; it adds no
native command. voice.ts invokes
voice_realtime_token, validates the returned bounded short-lived setup
through client-core, and exposes SDK-backed Workspace context, voice
orchestrator, and Pi relay services on SingleSessionPlatform. All Node
requests reuse node_request and node_cancel without a renderer bearer
credential. Consequential and destructive requests wait for the native
confirmation sheet; a decline maps to cancelled and an unavailable sheet to
saturated, with the native reason (confirmation_declined,
confirmation_unavailable) kept as the error cause. Nothing is sent in either
case. This transport grants no mutation authority;
sessionAvailability remains unavailable, so final relay dispatch stays
disabled while Maia Node omits its external Pi adapter.
observation.ts adapts the native observation_subscribe relay to the
SDK's stream relay, and the SDK's watchNodeChanges does the rest: it builds
the subscribe message without a credential, validates every relayed frame with
the protocol schema and its correlation id, turns committed events and
snapshots into NodeChanges signals, and resumes from the last cursor after a
terminal frame or a relay loss. An invalid frame, a refused subscription or a
Node that restarted (epoch_changed) fails the watch, so watchers resubscribe;
the cockpit reads the Node instance again before it does. Frames are change
signals, never UI patches or resource data.
interaction.ts adds the mounted record-only question candidate through
client-core and the same public SDK transport. Its native projection admits only
interaction.list, interaction.show, and interaction.respond, with exact
bounded parameters; creation and cancellation remain CLI operations. The client
checks the visible Project/Session before sending an answer. Scoped requests stop
on target closure, while route-owned bounded memory retains uncertain original
answers and command IDs across target/drawer remounts for explicit retry. It does
not persist that client memory across application restarts or retry automatically.
Answers never authorize operations or activate Sessions. Mounted/native/human
qualification is still pending; source presence is not acceptance.
The renderer adapter does not discover profiles, read connection descriptors,
expose long-lived credentials to JavaScript, supervise the node, or become
another backend. Rust owns descriptor and Gateway credential discovery and
authenticated native I/O. All local-node HTTP is native-only, proxy-free, redirect-denying,
deadline-bounded, and restricted to the descriptor's exact 127.0.0.1 port and
fixed operation path. Before an allowed terminal request, native code verifies
a fresh authenticated status identity and rechecks descriptor plus credential;
the renderer remains an SDK client and receives only a correlated protocol
response. The terminal adapter caps each encoded request at 512 KiB, supplies a
five-second native deadline, forwards cancellation with a per-request opaque
ID, preserves native outcome_unknown even after its AbortSignal becomes
aborted, removes abort listeners through an Effect-owned release, and fails
closed when Tauri is absent.
The voice adapter maps native errors to a small Maia-owned vocabulary and never
returns raw native detail. Neither adapter logs request envelopes, terminal
content, tokens, or credentials.
dictation/ adapts the dictation_start, dictation_finish and
dictation_cancel commands into client-core's DictationEngine: one Effect
stream per session over a Tauri Channel. Every event is validated against a
closed schema (malformed or oversized events fail the stream); closing the
stream before its final or error event cancels the native session. Dictation
reaches no Node and receives text and a level, never audio. Outside the
Desktop host the engine reports engine_unavailable.
microphone-permission.ts owns the microphone_permission command used
by the web voice-input projection before browser capture. It returns only
authorized, denied, unavailable, or not_required outside the Desktop
host, and imports the Tauri API on first use.
The native allowlist is the set of protocol feature descriptors with
desktop: "reviewed-renderer-local-node", plus the exact node.status read
and one token-minting command; see the
Desktop README.
managed-session.ts reuses the public SDK and existing native transport
for bounded managed-history reads. It obtains the current Node Workspace context,
queries its focus Project, filters other Workspaces, checks exact show identity,
and rechecks context before returning. States are preserved without inferring
turn readiness or external Pi relay eligibility. This adapter is not yet mounted
in the voice UI; no authoritative picker or production fixture replacement is
claimed. All other Session methods and every turn method remain denied. Maia Node remains the sole relay authority and fails final dispatch
closed until the external adapter is explicitly composed and qualified.
Windows, observer attach or resync, controller renew, restart, delete,
reconcile and device capabilities are unsupported; Node supervision belongs to
the native node_lifecycle_* commands, not to this adapter. Outside the reviewed Desktop host, both
Realtime token issuance and Workspace scope access fail closed.
Run focused verification with Node 24.22.1:
pnpm --filter @maiaos/client exec node --test "src/platform/desktop/**/*.test.ts"
pnpm --filter @maiaos/client lint
pnpm --filter @maiaos/client typecheck
pnpm --filter @maiaos/client test
pnpm --filter @maiaos/client build
The real boundary is additionally checked by @maiaos/desktop native tests and
headless WebKit compatibility evidence. See
Architecture,
Desktop and mobile clients, and
client documentation.
Imported from Maia OS docs at a2175bb