f0410d592d
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.
40 lines
5.2 KiB
Markdown
40 lines
5.2 KiB
Markdown
# @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`](../apiproxy/README.md) 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](../../context/workspace-context/README.md) 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`](../../session-title/session-title-llm/README.md) 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](../../context/workspace-context/README.md#prompt-shape) 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.version` is a placeholder** — it does not yet report the `apps/cli` package 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.
|