Include the root merge from #3678 without altering the previously validated implementation tree. Preserve structural V3 system messages and the current native-runtime recordings.
Review follow-up. The mode label predicate lacked `!empty`, so a running
continuable child with an empty draft showed a disabled "Queue message";
and a claimed slash command or a `/` line headed for adjudication executes
a command rather than delivering a message, so naming Queue/Steer there
misdescribed the click. The label now names the mode only when the click
would deliver a plain message: running, steer-capable, enabled, non-empty,
unclaimed, and not a `/` line. Tests pin the empty-draft child, the slash
line, and the claimed command; the Agent Note, README pair, and
ComposerKeyboard.submit JSDoc state the exact predicate.
`src/index.ts` held the installer, the manager, the view fold, and every
helper in one 1100-line file. It is now the package API alone, re-exporting
from `installer.ts` (pnpm runs, the probe cache, post-install checks),
`manager.ts` (every operation over the booted profile and the mutex),
`view.ts` (the package view fold and the row-ownership walk the dependents
query shares), `modules.ts` (declared-module row naming and views), and
`helpers.ts` (the diagnostic prefix, tooling bounds, spawn seam, and manifest
readers). The public surface, the tests, and the failure codes are unchanged.
The ui-conversation.busyEnter setting selected Queue or Steer for plain
Enter only; the running draft's Send button always submitted through the
public InputActions.submit() face, which is fixed to Queue, while labeled
"Send message". A user who chose Steer got Steer from Enter and Queue
from the button beside the same draft with nothing explaining why.
InputBar now resolves the primary click through the same `enter` gesture
and steer-availability predicate as the keyboard path (ordinary Sessions
and continuable children) and submits via ComposerKeyboard.submit(mode).
While that delivery is available the button's tooltip and accessible name
state it ("Queue message" / "Steer message"; 排队发送 / 插话发送), so the
mode is never hidden; idle sessions, one-shot children, and locked
composers keep the plain Send label. The composer bar inject face
publishes the live preference as hooks.busyEnter instead of a resolver
closure, and resolveSubmitMode is a pure function taking the preference
explicitly, so label and delivery derive from one value in one render and
the label follows live Settings changes. The Settings row is retitled
"Send behavior while busy" with a description that names both Enter and
the button; the busyEnter field, its default, and the Host schema are
unchanged.
Tests pin both preferences on the running button for ordinary sessions
and continuable children, the live relabel, the idle Queue path, and the
pure resolver; the settings-chrome ARIA goldens and the live-interactions
running-draft and queue-actions failed goldens carry the new copy. A new
Agent Note owns the decision and reverses the pointer-ignores-preference
clause of the archived 2026-08-20 running-draft note.
Preserve both sdk-minimal-in-history and native runner smoke scenarios. Record the current Cordis inspection through the actual upper headless owner while retaining upstream V2 bytes and strict prompt admission semantics.
Preserve strict live-smoke challenges and structural system messages while adopting the native subprocess runtime. Regenerate only the current Cordis inspection recording from the upstream V2 source to retain its no-ordinary-PID coverage; predecessor bytes match master.
Preserve the root V3 current-writer, PTC dispatch identity, and reliability fixes while incorporating provider-owned native ranges and the private packaged runner bootstrap.
Refresh only the current cordis-inspect-jsdoc V3 owner through the built headless profile so upstream subprocess API coverage is exercised. Preserve incoming master V2 bytes and all other fixtures and benchmark thresholds.
Preserve prepared-route admission and V3 prompt semantics while adopting latest master. Regenerate the new model-switch recording at its actual upper-layer title ordering, retain the V2 predecessor, and keep queued-image and publint lifetime assertions strict.
Adopt current model-switch notices, lazy observation, and open-in-app integration while preserving structural V3 admission. Regenerate only the new model-switch owner’s native V3 recording, prompt-count manifest and expected delimiter; keep the master V2 predecessor byte-exact.
The `cordis.patch.yml` parser and the comment-preserving writer are the
Loader's format, not a mechanical primitive: they build on the include
plugin's dialect and the Loader's row types, which no other `util` package
does. They now live in `app-boot` as `src/patch-file.ts`, exported as
`@deepseek-ai/dsh-app-boot/patch-file` with its own bundle entry, so the
agent-preset roster and the plugin manager load them without the boot entry.
`dsh-patch-file` goes, with its README and catalog entries; `yaml` moves into
app-boot's dependencies.
A dozen plugin packages already depend on `dsh-app-boot`, so the roster
joining them costs nothing; the note records why the separate package was
tried and folded back.
Bring model-switch notices, lazy observations, and open-in-app into the root V3 integration without importing the child structural header changes. Preserve V3 migration, PTC, and all predecessor fixtures.
Refresh only the model-switch-notice owner through dsh --profile headless to harvest native current-writer session.v3.jsonl. Keep the master session.v2.jsonl byte-exact; existing header pin and versioned writer helpers require no changes.
- A media-named FIFO or device node is refused before the open would
block on it (pre-open isFile check); POSIX FIFO regression test added.
- The full-body stream is bounded to the stat'ed size so concurrent
appends cannot exceed the declared Content-Length.
- Error responses carry no body for HEAD requests.
- Docs no longer overclaim the replacement race: the stat-identity
comparison narrows (does not fully close) the replacement window, and
the earlier realpath-to-stat window is acknowledged. Pairs re-recorded.
range-parser never validates the range unit, so /api/file now parses only
headers that start with the bytes unit: unsatisfiable ranges answer 416,
while malformed, unknown-unit, and multi-range headers are ignored for a
full 200 body per RFC 9110. The filesystem-root workspace case stays
POSIX-only in tests (a Windows drive-root spelling cannot be constructed
portably); containment already treats any separator-terminated root the
same. Docs (README pair, Agent Note pair) re-recorded.
Review follow-up. A forwarding module's explicit re-exports are tried
before its star re-exports and an entered module is skipped on later
edges, so a star edge that loops back no longer hides the explicit
re-export beside it. Import resolution outside the checker goes through
the face's shared compiler host and module-resolution cache, and an
import naming an unregistered package fails the public-export check
instead of throwing a TypeError. Tests pin the looping star, the
relative-only cycle exit, namespace re-exports, and re-exported
namespace imports.
Add the first-party open-in-app host and client packages with a localized Open In menu in the Web Session header.
Resolve installed applications on macOS, Windows, and Linux, launch workspace directories through platform-specific adapters, and remember the selected application.
Closes#1500.
Co-authored-by: ihsiang <[email protected]>
What plugin management does now lives in `dsh-plugin-manager`, a boot-group
package beside `app-boot`: `PluginInstaller` runs pnpm, probes, and judges new
packages over the profile on disk, and `PluginManager` adds every operation
over the booted tree, each refusal a `PluginOperationError` with a `plugins/*`
code and no Remote protocol attached. The manager reads the profile runtime,
the preset roster, and the running-agent count through per-call readers, and
the roster only as `PresetLayers`, so the package depends on neither the
roster nor the agent registry.
`dsh-host-plugin-manager` keeps `pluginManager` and the `plugins` Remote as a
relay: one Remote method per manager method, and one exhaustive switch
turning a failure into the Remote error of the same code (`plugins/bad-request`
into `gateway/bad-request`). Its `./types` re-exports the manager's types, so
`api-remotes` and the client import one vocabulary unchanged.
`dsh plugin add` and `remove` now run the installer instead of a bare pnpm
forwarder: every new package is probed, a package that declares neither a
bundle nor a plugin module is removed again with its reason, and newly
installed bundles are enabled as before. Other pnpm verbs still forward.
The watermark marking and position comparison sit next to deriveEventMessage
in surface.ts; the append/seed validation sits with the other event validators
in index.ts and the fold is inline. Only compareImagePositions stays exported.
- HEAD never opens a file stream; GET streams are destroyed on client
abort and are never opened for already-aborted requests.
- Validation and reading bind to the same opened file: pre-open and
opened stat identities are compared, so replacement/re-linking races
are refused (dev/ino check, read through the FileHandle).
- Workspace containment compares path components, so a filesystem-root
workspace serves files instead of doubling the separator.
- Multi-range headers are ignored for a full 200 body instead of a
mislabeled single-segment 206; single-range behavior unchanged.
- Tests now cover the reviewed edges (empty path, multi-range, root
workspace, HEAD+range, mid/early abort, unreadable file, explicit
unregister-on-dispose, private outside-target directories); module
coverage is back at 100/100/100/100.
- Agent Note bilingual facts and the ui-primitives/session-controller
README pairs are synced to the shipped behavior (pairs re-recorded).