Move the triplet to implemented/architecture/, rewrite Proposal into a
present-tense Decision, fold Acceptance criteria and Risks into
Consequences (recording what pins the result), retitle the migration
section to what the move actually touched, and repoint the seven
inbound FIXME/README links.
git mv per the regrouping RFC: the five human-collaboration seams and
tui join packages/interaction/, app-boot becomes packages/boot/, and
jsonrpc joins the renamed scaffold/ (formerly sdk/) as its server half
beside client/protocol/create-sdk/helper/scripts/telemetry, whose
folders drop the legacy sdk- prefix. Three new group README triplets
replace the ui/ and sdk/ ones; tsconfig references/paths/globs,
knip keys, vitest globs, gate scripts, catalogs, docs, and the
lockfile follow. Adds the four settled FIXME rename markers
(dsh-sdk-server, dsh-sdk-telemetry, dsh-sdk-helper, dsh-sdk-scripts).
The scaffold folders diverge from their npm names until those renames
land, so tsconfig.base.json maps the three affected names explicitly
beside the group wildcard. Also repairs two pre-existing stale-path
classes the strengthened sweep surfaced: docs/web-styling.md's retired
web-ui host package and type-model spec fixture-literal joins.
app-boot's three Loader-composition specs time out at the default 5s
under full-suite parallel load on this filesystem (pre-existing;
pass isolated with --testTimeout=30000); interaction/scaffold/boot
suites otherwise green (687 passed).
The Gateway serves the goal domain as Remote endpoints, and a Remote method
picks its receiver Service from a generated descriptor — `clear(agent, ref)`
takes its agent as a PARAMETER, so the invocation is direct and the receiver
resolves on the host. Behind the preset's entry-local realm there was nothing
to resolve, and every browser goal call answered `service-unavailable`: the
composer's Clear goal button left the bar on screen.
That is the `bash-env` criterion read from the other side. Injection is not the
only host relationship a Service can have; being READ from a host row is one
too. The registry is keyed by session, so one host instance serves every
session exactly as it did before presets. The preset keeps the model-facing
tool, which is the choice a preset is for.
Also reverts the cold-transcript presenter resolve: resuming an agent to reach
its presenters made the context meter drop its cache and token counts on every
cold-opened session, trading one silent degradation for another. The card
degradation it addressed is diagnosed and still open.
The composer's preset seat stays removed here — this layer moved it to the
hero chip and the session-header action — so only the model seat takes the
`modelSeatLocked` narrowing master introduced.
Conflicts:
apps/web/tests/snapshots/*/*.expected.md
packages/client/connection/README.md
packages/client/connection/README.zh.md
packages/client/ui-conversation/src/client/skeleton/InputBar.tsx
packages/host/apiproxy/src/fetch/client.ts
The Client API carrier's `agentPresets` member was the one member of its class
without an `IApiClient[...]` annotation. Inferring it inlined `AgentPresetEntry`
into the emitted declaration by the specifier TS picks — the host `index.ts` —
dragging the whole gateway, and with it the host `Context` merges, into every
Client program importing the carrier. Annotated like its siblings.
`ApiRemoteAgentOptions.setup` now takes the inspected session rather than its
header alone: this layer resolves a resumed session's preset from the LOG,
because a session that switched while blank ran its turns under the newer
composition and the header is written once at creation.
Conflicts:
apps/web/tests/snapshots/*/*.expected.md
packages/client/ui-conversation/src/client/skeleton/InputBar.tsx
packages/host/apiproxy/src/api-proxy.ts
scripts/doc-budgets.manifest.json
master extracted this layer's inline cold-resume resolver into
@deepseek-ai/dsh-api-remotes, whose `setup` was a fixed AgentSetup. A resumed
session composes the preset ITS header recorded, so the option becomes a
function of that header; the resolver builds the setup before the published
re-checks so those stay adjacent to `resume`.
Conflicts:
docs/cordis-catalog/services.md
docs/module-graph.md
packages/host/apiproxy/package.json
packages/host/apiproxy/src/api-proxy.ts
pnpm-lock.yaml
Adapter schemas now carry only credential references, but the Models join, onboarding readiness, shipped overlays, SDK scaffolding, fixtures, and active decision prose still treated a redacted literal apiKey as a supported compatibility state. That residue made an unsupported field look contractual and pinned Schemastery silent-dropping as behavior.
Delete those branches and examples, and let compositions and scaffolds use adapter-owned reference and environment resolution. Do not add a tombstone validator or change generic unknown-key behavior: literal adapter credentials have no migration contract to preserve.
Code already treats $DSH_HOME/.env as ordinary launch environment and stores managed credentials in .credentials.yaml, but public docs still described the old store, old precedence, removed literal adapter keys, and the deleted TUI. That directed users to the wrong file and overstated the supported configuration surface.
Update the existing English and Chinese owners in place, document inherited > managed > project > user credential resolution, and record the loadLayeredEnv export. Regenerate only pairing records and the source-line catalog; add no new section or site route.
Moving the agent plane behind per-session presets took five rows with it that
the host still owns, and the Web surface stopped booting: `host-apiproxy`
injects `subagents`, so with the registry disabled here the entry never
activated and `dsh web` died at plugin-tree load.
The criterion is injection, not subject matter. A host row that injects a
service resolves it before any session exists, so there is no agent to key by:
`bash-env` (which `apps/cli/src/web.ts` injects to publish `DSH_WEB_URL`), the
`subagents` registry and its spawn/fork backends (a process singleton whose
cross-session queries the api-proxy serves, and whose provider names are
globally unique), and `tool-subagent-report` (a continuable setup on that
singleton, registered once per live session by a list that is not scope-aware)
all stay host-plane. What a preset chooses is which delegation TOOLS it sees.
The browser lane needs the second half: skill roots now resolve inside a preset,
a subtree the lane's include patches cannot reach, so the row's documented
environment fallback is pinned for the whole scaffold lifetime — presets mount
when a session is created, not at boot. Without it a developer's real
~/.dsh/skills enters replay requests and goldens while CI sees none.
The shipped host composition moved: `base.cordis.yml` and `web.cordis.yml`
are the dsh-base and dsh-web-app patch layers now. The gate still opened the
old paths and crashed on ENOENT — a gate that cannot read its inputs proves
nothing, loudly or otherwise.
Retargeting it also surfaced what the move implies for ownership: the web
bundle carries the roster and its browser plugin rows now, so the bundle's own
manifest is what must declare them. The gate's existing bare-plugin check said
so as soon as it could parse the file again.
The browser e2e lane had been failing wholesale since this stack moved the
agent plane into presets, and nothing caught it: 34 of 48 files. Two of the
causes are product defects, not test breakage.
`bashEnv` goes back to the host plane. `apps/cli/src/web.ts` injects it to
publish `DSH_WEB_URL`/`DSH_WEB_MODE`, so the earlier note that "nothing outside
the agent plane injects bashEnv" was simply wrong — behind a preset's `shell`
realm those variables reached no shell at all, and a `dsh web` agent could not
find the address of its own interface. This is the same criterion that returned
`subagents`: a host row that injects a service resolves before any session
exists and has no agent to key by, so the service is host-plane. `tool-bash`
consumes the host registry from inside the preset, which works because an
agent context chains to the host; only the reverse is invisible.
`tool-subagent-report` goes back with it. It is not a tool this agent calls: it
registers a continuable SETUP on the host `subagents` singleton, and that list
is not scope-aware. One copy per mounted preset meant every child was handed
`report` once per live session, so the second registration threw and a cold
subagent resume failed with `subagent-not-resumable` — a diagnostic three
layers removed from the cause.
The lane's own composition facts follow. Skill roots resolve inside a preset
now, a subtree include patches cannot reach, so the scaffold pins the roots'
documented environment fallback for its whole lifetime rather than for the boot
— presets mount per session. Without it the developer's real `~/.dsh/skills`
enters replay requests and goldens while CI sees none. The `apps/cli`
composition test pins `storage-json` for the same reason: unpinned it wrote,
and then read back, the developer's own `~/.dsh/storages/`.
Three tests now address through an agent what they used to read off the root
context, because that is where the thing lives: the tool catalog, the skill
registry, and the token meter. The seeded-history projection baseline asserts
the opposite of what it did — a detached session yields a preset-plane
projection only from a durable checkpoint written while it was live, and this
seed was written straight to persistence and never ran.
Goldens re-recorded for the hero's preset chip and the settings nav entry.
`dsh web` failed to boot: the aggressive plane split moved `subagents` and
its spawn/fork backends into the preset's entry-local realm, but
`dsh-host-apiproxy` is a host row that injects `subagents` to answer the
browser's cross-session queries, so it waited forever for a service only
sessions now provided. A per-session copy is wrong twice over — a provider
name registers once, so the second session would have collided anyway.
The registry and its backends go back to the host composition; the presets
keep the delegation TOOLS, which resolve that host registry. `workflows`
stays entry-local: nothing outside an agent reads it.
The web real-composition test could not have caught this, because it
disabled `api-gateway` — the very row whose pending injection names the
break. It now boots with the api-proxy enabled and the browse directory
picker substituted, so the boot audit covers the whole host-plane injection
graph. Re-introducing the old split makes it fail with the three pending
entries, which is how this was verified.
The creation header names the preset a session STARTED with and is frozen,
which is correct — it is a creation fact. Switching is legal only while a
session is blank, and that looked like enough: no history exists yet.
It is not, because the switch's effect outlives the blank window. The user
switches, then sends the first message; every turn from there runs under the
new composition while the header still names the old one. The session is
then locked around a misrecorded preset, and resume reads the header to
rebuild it — composing one preset's tools over a history another produced,
which is exactly the replay the blank-only lock exists to prevent, reached
by another route. A picker showed `standard` for a session running
`core-web`.
A switch is now an `agent-preset/selected` event appended after the swap
commits, and `resolveSessionPreset()` (last selection, else the header) is
what every reconstruction reads: the summary, resume, the conflict guard,
and the fork introduced one layer down.