The web host now composes the sandboxed product path (sandbox-local + sandbox-policy behind bash-sandbox/fs-sandbox, with user-approval and permission on top); BootHostOptions.sandbox carries the deployment defaults (workspace-write + ask). createApiProxy owns the approval pending registry: a ctx.approval ask becomes an answerable approval/requested mux frame with a stable rpcId, replayed verbatim on every mux open until settled; respond routes by the echoed rpcId, validates the ApprovalResponsePayload audit correlation, and broadcasts approval/resolved; the ask's abort signal withdraws the question as cancelled. session.permissions / session.setPermission project ctx.permission into a protocol-owned PermissionOption select; idle switches are held last-write-wins and flushed into the next prompted turn (the ACP bridge's anchoring pattern). The shared hasOpenTurn fold moved to dsh-session, deduplicating the private copies in user-approval, the ACP bridge, and the proxy. Client, per the designer draft: a pending approval takes over the composer (ApprovalPanel replaces the InputBar — amber strip, justification headline, paired command, one-shot refuse/allow, keyed by rpcId so a queued second approval remounts live; the resolved frame restores the composer); the sidebar session row shows an amber waiting-approval dot that outranks the running ring (manager-tracked approvalId set, idempotent under mux-open replays, cleared per connection generation, lit for uninstantiated sessions too); the permission selector is a composer bottom-row chip over an invisible native select, with a presentation-only title-case transform (workspace-write renders as Workspace Write; wire names untouched). Question placeholders stay in the message flow. The connection fixture mirrors the host behavior for keyless browser acceptance.
@deepseek-ai/dsh-host-runtime
Host runtime assembly for dsh: bootHost composes the core plugin spine (LLM service + DeepSeek adapter, sessions with JSONL persistence and immediate fallback titles, optional first-message model summaries, system prompt, tools, agents, agent loop, workspace instructions, the provider-neutral user-interaction service, and the sandboxed product path — dsh-sandbox-local + dsh-sandbox-policy behind the confined dsh-bash-sandbox/dsh-fs-sandbox families, with dsh-user-approval and dsh-permission on top), createApiProxy implements the dsh-host-apiproxy contract over that composition, and startHost is the one-step shell seam returning { api, handler, defaults, ctx, dispose }.
Which plugins mount and with what defaults is decided only here — shells must not ctx.plugin to alter the assembly. RunningHost.ctx is a formal seam with exactly two sanctioned uses: mounting protocol front-door plugins (e.g. a future dsh acp) and headless session-event subscription; consuming clients must not bypass api through it.
Configuration
| Key | Default | Contract |
|---|---|---|
persistenceRoot |
(required) | Root directory for JSONL session persistence. |
workspaceContext |
(required) | AGENTS.md/CLAUDE.md loader config with an explicit maxBytes, or false to disable it. |
provider |
'deepseek' |
Default provider route injected as agentOptions on create/resume and reported by host.describe. |
model |
'deepseek-v4-flash' |
Default model id, same single source as provider. |
cwd |
process.cwd() |
Default project directory for a session whose create request omits cwd. |
sessionTitle |
5 words / 40 fallback bytes / 80 accepted bytes | Deterministic fallback and accepted-title limits. |
sessionTitleLlm |
disabled | true enables the 5-word / 10-CJK-character, 4,096-input-byte, 64-output-token, 60-second first-message policy; an explicit config overrides it. An omitted route inherits the logged main-request provider and model. |
sandbox.mode |
'workspace-write' |
File-sandbox mode sessions start from (ctx.sandboxPolicy default; per-session switches ride sandbox/mode events). |
sandbox.approvalPolicy |
'ask' |
Approval policy for sessions without an approval/policy override. |
ApiProxy implementation notes
Unary methods take the narrow RpcRequest<P> and echo request.rpcId; a prompt's rpcId rides MessageSource into the user/message event so clients can promote optimistic echoes. history/prompt on a cold session implicitly resume it, deduplicating concurrent calls through an in-flight table; history paginates backwards on message boundaries (never mid-message). The mux stream replays a session/subscribed baseline per attached session and every still-pending question with its original rpcId. Question responses, including blank per-item answers, are validated against the owning session and exact request before an atomic first-wins claim; answer, whole-request cancellation, owner abort, and provider disposal broadcast question/resolved. The host stream carries session lifecycle, running flips, and agent/error as the only outlet for live failures with no turn position.
The proxy is also the approval channel for the agents this host owns: an ask through ctx.approval becomes an answerable approval/requested mux frame with a stable rpcId held in a pending table, replayed verbatim on every mux open until settled. respond routes by the echoed rpcId (approvals first, then questions), validates the ApprovalResponsePayload audit correlation at the wire boundary, resolves the answerer, and broadcasts approval/resolved; the ask's own abort signal withdraws the question as cancelled. session.permissions/session.setPermission project ctx.permission (empty select when not composed); idle switches are held last-write-wins and flushed into the next prompted turn on agent/prompt-submit, because knob events must stay turn-enclosed for durable replay (the ACP bridge's anchoring pattern).
Model Experience
Indirectly, through the non-blocking first-message title request owned by dsh-session-title-llm when sessionTitleLlm is enabled, the provider/model defaults injected into created and resumed agents, the other model-facing plugins bootHost mounts, and the logged workspace-instruction prefix when workspaceContext is enabled.
KV Cache effect
No main-request invalidation; when enabled, the auxiliary title request has its own cache behavior and leaves the conversation prefix unchanged.
Known Limitations and Deferred Work
- Question and approval waits are process-memory state — browser reconnects recover them, but a host process restart aborts the owning tool call instead of restoring the wait from persistence.
host.describe.versionis a placeholder — it does not yet report theapps/clipackage version.- The assembly is fixed — per-deployment plugin selection (user profile, log sinks, alternative persistence) has a documented home here but no configuration surface yet.