Commit Graph

397 Commits

Author SHA1 Message Date
Yichen Jiang
bf0828e469 docs(cli): correct the core-web overlay's ui-question rationale
The comment claimed `ui-question`'s host half owns the `ask_user_question`
registration. That was true and is the exact mechanism that leaked the tool
past every preset; with the node half emptied, disabling the row is a UI
decision rather than a capability one.
2026-08-07 00:41:50 +08:00
Yichen Jiang
9ac1547335 fix(web): stop leaking ask_user_question into every preset
`ui-question`'s node half called `ctx.tools.register` on the host context.
`ScopedLayers.merge()` combines the global layer with the agent's exact-scope
layer, and an unscoped registration lands in the global one — so the tool
reached every agent no matter which preset composed it. `core-web`, sold as
a two-tool benchmark surface, really presented three, and a locally authored
`bash-only` preset presented two.

Rendering a question is a host UI capability; having the tool is an agent
capability, and only a preset decides that. The node half is now empty and
the `tool-ask-user` row moved into the presets that want it. The TUI keeps
its own row, having no presets.

The web composition test now asserts the global tool layer is EMPTY, which
is the invariant that would have caught this: any tool outside a preset
reaches every agent.
2026-08-07 00:41:50 +08:00
Yichen Jiang
6595301afc fix(web): keep the subagent registry in the host plane
`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.
2026-08-07 00:41:50 +08:00
Yichen Jiang
6dfc568ec2 feat(web): author agent presets from a settings page
A composition is a file, but "edit it on the filesystem" is not a browser
affordance. The roster gains `read`/`write`/`remove` beside `select`, and
the browser gains a settings section over them: the presets as rows, one
composition open in a YAML editor at a time, and per-row default, duplicate,
and delete.

All four authoring methods are loopback-pinned. A composition names the
plugins a session runs, so reading one is reconnaissance, writing one is
arbitrary capability, and selecting one can move a session onto a preset
that edits the live runtime. `agentPreset.list` deliberately stays ordinary
and now reports `authorable`, so a surface knows whether creating is
possible at all rather than offering a button whose save always fails.

Authoring starts by duplicating: a shipped preset opens read-only because
the deployment's copy is what a broken local one is compared against. Ids
are contained before they become directory names, and the text is parsed
with the loader's own schema, so a save cannot leave a file no session
could load.

Fixes a defect the real-composition test found: a preset written under the
user's home could never mount, because the loader resolves a row against the
composition's own directory and Node's `node_modules` walk from there never
reaches the installed harness. The mount now records the host base and sends
bare specifiers there, leaving relative paths resolving from the preset.

Also closes the coverage the earlier surfaces in this stack shipped without —
the General row, the composer seat, and the plugin halves now have tests.
2026-08-07 00:41:50 +08:00
Yichen Jiang
3ae22b3835 feat(agent-presets): ship a cordis agent that can author compositions
A third built-in preset: the standard coding agent plus the self-referential
Cordis toolset, a persona that explains the two-plane split, and a skill
teaching composition authoring. It exists so a person can ask an agent to
author another agent.

The skill ships INSIDE the preset directory rather than in the user's skill
root, and the root is derived from the preset's own `baseUrl` — the loader
evaluates `!!js` with `with (ctx)`, so a composition can locate itself. A
preset is the unit that gets copied and edited, so its documentation should
travel with it.

The skill leads with the rule that actually bites: a row publishing a service
may not sit loose in a preset, whether a row publishes one is not visible from
its name (`tool-bash` provides `bashEnv`), and a consumer left outside its
provider's isolate group resolves the host registry and then contributes
nothing — the quietest failure this design has.

Writing the test surfaced a consequence worth stating: an entry-local realm
makes the service invisible to the agent's own scope too, not just to the host.
Only rows inside that group resolve it, which is precisely what makes
`tool-skill` this agent's own rather than a shared one. The test asserts what
is actually observable from outside instead of reaching for the isolated
service.

TRUST: `cordis_mount` evaluates model-written JavaScript against the live
runtime, and a composition this agent writes becomes a preset other sessions
mount. Both the preset header and the toolset's own documentation say to treat
this as shell access. The tools stay opt-in per session — a test pins that they
are absent from every other preset.
2026-08-07 00:39:35 +08:00
Yichen Jiang
98fbe0ee94 fix(web): record which preset a session actually runs
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.
2026-08-07 00:38:11 +08:00
Yichen Jiang
6758da87ae feat(web): choose the default agent preset from General settings
One settings row picking which preset new sessions are composed from.

It is deliberately a new-session preference, not a live switch: a session's
preset is fixed at creation and the host refuses to adopt an existing session
under a different one, so the row says "applies to sessions you start from now
on" rather than implying it can retune a running agent.

Options and the current value come from one `agentPreset.list` call — the
roster already reports which id an unspecified session gets, so the row needs
no settings-schema introspection, unlike the permission row it is modelled on.
The write targets only the namespace's `default` field.

The menu marks `user` rows: a locally authored preset is exactly as privileged
as the plugins it names, and presenting it identically to a shipped one would
hide that.

An empty roster reads as `unavailable` and renders nothing, because composing
no presets is a valid deployment rather than a failure — distinct from a
roster call that failed, which surfaces its message.
2026-08-07 00:38:11 +08:00
Yichen Jiang
8d06b2d576 feat(agent-presets): make the default preset a user setting
`config.default` becomes the composition base of an `agent-presets` settings
namespace, so the user document layers over the deployment's engineering
default and a person can change which preset new sessions get without a
restart.

The value is read per resolution rather than snapshotted: a hot-reloaded
document takes effect on the next session created, and every running session
stays on the preset it was composed from — which is the same rule the
session-header guard enforces from the other side.

`resolve()` read `config.default` directly, which would have made the whole
setting inert; it now goes through `defaultId` like every other caller.

The write-protection test is rewritten against a temp profile root. It was
passing vacuously: the un-overridden Loader REWRITES the composition it read —
stamping `disabled: true` onto the self-disposing row — so the committed
fixture had been mutated by the very run that proved the bug, and every later
run compared against the damaged file and passed. Building the preset in a
temp directory makes the assertion immune to its own failure mode, and it now
fails with a visible `+ disabled: true` when the override is removed.

Review follow-ups on this layer. The exported schema is
`AgentPresetSettingsSchema`, symmetric with the `AgentPresetSettings`
interface it resolves and self-describing at an import site. The `session.create`
JSDoc promised "the deployment's default preset" for an omitted `agentPreset`,
which this layer makes false — it now names the effective default. The
constructor records why it does not use `installSettingsSection`: that helper
re-judges what a consumer DERIVED across attach and detach, and nothing here is
derived. The provider-unload test disposes the fiber `ctx.plugin()` handed back
instead of reaching into `ctx.reflect.store`, and the write-protection wait says
why slack is the right shape for an absence assertion.

The real composition covers the layering too. `apps/cli` boots the shipped
`cordis.yml`, stores `agent-presets.default`, and asserts an unnamed session
composes from it — the package suite proves the layering against a hand-built
context, this proves the roster and the settings provider are wired to each
other. That test also pins the settings row at a temp file: it defaulted to
`$DSH_HOME/settings.yaml`, so a developer's own stored default decided the
outcome of a file whose whole point is that only the shipped root does.

The Agent Note records the per-resolution read and its correspondence with the
session header, and the vacuous-test finding above.
2026-08-07 00:36:54 +08:00
Yichen Jiang
5ed79887fb fix(web): correct the preset-layer contracts review found stale
None of these change behavior; each said something that was not true.

`SessionCwdConflict`'s doc block had been left stranded above the
`AgentPresetConflict` inserted under it, so one class carried a comment
about the other and the second carried none.

The roster comment named a `.system` directory that does not exist; the
shipped root is `config/agent-presets/`, and `system` is the trust its
entries carry.

The real-composition test attributed the disabled `api-gateway` row to
"side effects outside this process" alongside the port and the exporter.
It is disabled for a different reason — the api-proxy cannot mount in
this layer at all — and hiding that behind the same phrase would leave a
later layer unable to tell whether the line can come out.

One test claimed to refuse an adoption while asserting only that the
header records the preset; it now says what it checks.

`PERSONA_SECTION`/`PERSONA_ORDER` existed twice, once in the registry
that declares the slot and once restated in the row that replaces it —
a drift that would land a preset's persona beside the deployment's
instead of shadowing it. The registry exports them now.

The preset conflict message read "already runs agent preset undefined"
for a session that records none, which is the shape a deployment with no
roster produces; it names that case instead, with the regression that
reaches it through the gateway.

Finally, `PresetTree.write()` drops the `loader/config-update` the
inherited method emits — recorded on the override, since a future
edit-while-running flow needs its own persistence path.
2026-08-07 00:35:30 +08:00
Yichen Jiang
25b6381c84 fix(web): compose a forked session, and give the shell realm its provider
Two consequences of moving the agent plane behind presets, both invisible
until the host plane stopped carrying model-facing rows.

`sessions.fork` built its child with a bare `installTarget` and a `meta`
without `agentPreset`. That was harmless while every tool sat in the host
plane — the child inherited them for free. It now comes up with an EMPTY
tool set. The child composes the parent's preset instead, for the same
reason a resumed session keeps its own: the seeded history was produced
under those tools.

`bashEnv` lives in its own `dsh-bash-env` row rather than inside
`tool-bash`, so a preset that isolates the realm must compose the provider
beside its consumer; the host row is disabled here like every other
model-facing one. Nothing outside the agent plane injects `bashEnv`, so it
stays per-session.
2026-08-07 00:35:30 +08:00
Yichen Jiang
3d68185480 feat(web): move the agent plane behind per-session presets
The Web overlay disables base's 32 agent-plane rows and mounts the preset
roster instead, so each session composes its own tools and prompt rather than
sharing one process-wide set. The TUI keeps base unchanged: it is single-session
and composing its agent process-wide is correct there.

`roots` is patched in by AppCLIEntry, like `distIndex`: the shipped presets sit
beside the composition that names them and the user's live under the Harness
home, neither of which a config author chooses.

A session's preset is fixed at creation. Naming a different one for an existing
identity is `agent-preset-conflict` rather than a switch, because that
session's history was produced under the first preset's tools. The guard sits
after `await creation`, beside the cwd check, so it covers every path that
yields a live agent — freshly created, adopted live, resumed, or recovered by
the concurrent-creation catch. A request naming no preset adopts the session as
it is, keeping reconnect and retry ordinary.

Two bugs the real-composition test caught, both invisible to unit tests:

`PresetTree` now refuses to write. The Loader persists a tree whose plugin
self-disposed, and tearing an agent down disposes its whole subtree — inherited,
that rewrote the shipped composition, truncating a 241-line preset to `[]` the
first time a session ended.

`dsh-tool-skill` compared against a lookup of its own name in the global layer,
so it threw inside any preset: `register()` files into the calling context's
scope. It now compares against the definition it registered, which is what the
identity check meant all along.

The `standard` catalog is asserted exactly, not spot-checked: a row that
registers into the wrong layer mounts cleanly and simply contributes nothing, so
an omission is this design's quietest failure. It matches the shipped TUI
catalog plus `glob`/`grep`, the pair that composition documents as
ripgrep-dependent.

Re-records `cordis-inspect-jsdoc`, whose rendered `SessionHeader` gains the
`agentPreset` field. `fs-glob-sampling` fails identically on pristine master
and is untouched here.

The browser e2e scaffold gains the roster fact AppCLIEntry supplies. `roots` is
resolved and patched in by the CLI entry, like `distIndex` on the webserver row,
and this lane boots the shipped tree without that entry — so it has to supply
the same fact or the roster resolves nothing and every session in the lane
composes an agent with no tools, no persona, and no token meter. Only the
shipped root: a developer's own `~/.dsh/.agent-presets` must not decide a golden. The
`cordis:group` builtin comes with it, exactly as `boot()` registers it, because
a preset resolving package names from its own directory cannot reach
`@cordisjs/plugin-group` by name.

The lane stays red through this layer and the next four for the reason stated
above — the api-proxy injects `subagents`, `workspace`, and `tools`, so
`api-gateway` cannot activate and the browser has no `/api` at all. It goes
green again in the layer that returns those registries to the host plane; this
change is what makes that layer's fix sufficient rather than partial.
2026-08-07 00:35:30 +08:00
Yichen Jiang
91b55b9245 feat(web): compose a web session's agent from a named preset
`session.create` takes an optional `agentPreset`, and the host resolves it,
mounts it during pre-publication setup, and records the resolved id on the
session header so a later resume rebuilds the same agent.

Resolution happens BEFORE the session exists, not inside setup: the session
boundary snapshots `meta` before asynchronous setup begins, so an id discovered
during setup could never reach the header. Mounting still happens in setup,
where a failure rolls the whole creation back rather than publishing a session
whose capabilities are half-installed.

Resume ignores whatever the request names and rebuilds from the stored id. A
resumed session's history was produced under that composition; restoring a
different one would replay tool calls the model can no longer make.

`dsh-agent-presets` now throws `UnknownPresetError` / `PresetMountError` so
the host can tell a bad request from a broken preset — they become
`agent-preset-not-found` and `agent-preset-invalid`.

Ships the two built-in compositions (`standard`, `core-web`) and the persona
row that lets them differ in identity. Nothing mounts them yet: no roster is
configured, so `composeAgent` finds no service and every session keeps the host
composition. Wiring the roster and moving base's agent-plane rows behind it is
the next commit, so the switch happens atomically with a real-composition test.
2026-08-07 00:35:30 +08:00
Turtle
ef30572e63 fix: retire leftovers of the removed $DSH_HOME/config.yaml personal overlay
The profile rework left references to the old entry modes behind. Renames
the user patch-layer API and its spec file (watchPersonalPatches ->
watchUserPatches, personal-config.spec.ts -> user-patches.spec.ts) and
retargets the prose that still named `config.yaml`, `--config`, raw-config
mode, and surface overlays: repository-plugin and mcp-memory READMEs, the
credentials-local anchor into app-boot, vendor manifest items 12-13, the
vendored include/hmr comments, and install.sh.

Restores the boot-failure guard the rework dropped with raw mode: the
built-bin case now boots `--profile web --patch <invalid>` and asserts the
settled diagnostic and exit 1, so the HMR initial-scan deadlock stays
covered; its orphaned raw fixture is renamed and the unused one deleted.
The superseded personal-config Agent Note and its superseding profile note
are now cross-linked.
2026-08-06 17:28:58 +08:00
Turtle
62d0f26fd6 refactor(cli)!: namespace the profile and bundle manifests under dsh.profile and dsh.bundle
A profile manifest and a bundle manifest are different kinds and shared one
flat `dsh` section: `dsh.plugins` listed bundles (not plugins) and `dsh.patch`
declared a bundle's layer. Each kind now names its role — a bundle declares
`dsh.bundle.patch`, a profile declares `dsh.profile.bundles` — so a
package.json states which role it plays and the list name matches its contents.

`DEFAULT_PROFILE_PLUGINS` becomes `DEFAULT_PROFILE_BUNDLES`, and
`DshManifestSection` splits into `DshBundleManifest`/`DshProfileManifest`.
Pre-release: no compatibility shim; turtle-ui moved with it (bd5ff10).
2026-08-06 17:28:30 +08:00
Turtle
52d7515936 fix(cli): plugin UX — anchor relative specs, reconcile by installed state, guide blocked git builds
- Relative path specs (., ../plugin, file:/link: forms) anchor to the
  invoking directory before forwarding: pnpm's cwd is the profile dir, so a
  bare 'add .' from a plugin checkout used to self-link the profile (exit 0,
  nothing installed). Bare paths stay bare and prefixed specs keep their
  prefix, preserving pnpm's link-vs-copy semantics.
- dsh.plugins reconciles against the INSTALLED state on every successful
  pnpm run, not the dependency diff: an update whose new version gains
  dsh.patch activates the layer; a version that drops it (or a removal)
  deactivates it. Template bundles are never touched.
- A failed pnpm run now names the profile directory, and a git-spec failure
  explains pnpm >=10's prepare-script block with a pointer at the profile's
  pnpm-workspace.yaml allowBuilds (turtle-ui's prepare-based git install is
  the reference consumer); reference README documents all three.
2026-08-06 12:16:32 +08:00
Turtle
65770325e7 feat(cli): restore the home-level user patch layer as $DSH_HOME/cordis.patch.yml
The old $DSH_HOME/config.yaml personal overlay returns under the profile
scheme's filename: machine-local preferences that apply to every profile,
loaded after the profile's own cordis.patch.yml (so the home layer outranks
it) and before --patch overlays and flag patches. Both user layers are
hot-reloaded on long-lived surfaces and shown in --dump-config with their
own provenance labels; the built-bin e2e covers the home layer landing live.
2026-08-06 11:02:42 +08:00
Turtle
0071862d48 refactor(cli): simplify profile composition and dump paths
- composeProfile keeps layers as bundle/user/overlay+flags segments instead
  of one flat list later re-sliced by index arithmetic; the row index drops
  the group-walk (profile trees are flat patch compositions) and the double
  composition.
- The config dump anchors on the profile's real empty root (written by the
  shared prepareProfile) instead of materializing a temp file, so dump and
  boot compose over the identical base by construction.
- dsh-base drops its patchPath export: the dsh.patch manifest field is the
  one contract; the package carries no runtime API.
- packageDirFromAnchor is paths-probe only (the require.resolve fast path
  duplicated the probe's outcome); basename() replaces hand-rolled path
  splitting; verify-cordis-config stops re-reading bundle manifests in-loop.
2026-08-06 09:53:49 +08:00
Turtle
925daf141b fix: address ds-review-bot round — insert-aliasing clones, settlement gates, closure module fallback
- Clone patch lists per generation (boot + composeLive): the include pushes
  insert rows by reference and mutates them in place, so a reused object
  baked user overrides into bundle rows and removal could not revert; the
  built-bin hot-reload e2e now asserts an override AND its removal reverting.
- The headless runner awaits Loader settlement before prompting (its inject
  gate covers only apiProxy/httpServer) and abandons cleanly when the tree
  died during the wait.
- healProfilesModuleFallback walks the app's full dependency+peer closure:
  out-of-tree plugins import seam packages (dsh-compact, dsh-subprocess, ...)
  that only implementations reach, and peers are how seams are declared.
- Profile init writes pnpm-workspace.yaml (nodeLinker: hoisted), not .npmrc
  — pnpm >=10 reads settings from the workspace manifest.
- Web dumps reject boot-only flags instead of printing a tree that differs
  from the same invocation's boot; --port validates at the flag;
  --dump-default-config no longer parses the (possibly broken) user layer;
  trustedHosts flag derivation merges over the composed value instead of
  replacing it; web-runtime gains surfaceContext (headless disables the GUI
  prompt/bash-vars the old -p never mounted); 'node_modules' is a reserved
  profile name; plugin-warning names the recovery step; client AGENTS.md
  registration surfaces point at the web-app bundle.
- Ship session-reference/tmux-context/tool-ask-user as app dependencies for
  terminal front-door patch layers (turtle-ui), same stance as mcp-client.
2026-08-06 09:27:44 +08:00
Turtle
273f27260d fix(ci): telemetry switch trivially satisfied without the row; coverage-lane test fixes
A custom profile that mounts no telemetry-otel row exports nothing, so
DSH_TELEMETRY_DISABLED must not fail its boot (CI exports the switch
globally, which broke the lifecycle-fixture profile). The web-app dist
resolution test accepts the fail-loud unbuilt outcome the CI coverage lane
sees before any build, and the headless spec covers the idle-anchor and
pre-start skip branches under the per-file gate.
2026-08-06 07:30:32 +08:00
Turtle
cd6b4ee3c9 feat(cli)!: dsh boots profiles; plugin subcommand manages them via pnpm
dsh --profile <name> replaces the fixed entry modes: --config and -p are
removed, --patch adds overlays over the composed profile, a positional task
selects one-shot mode (requires the headless-runner row), and dsh web stays as
the alias for --profile web carrying the Web flag family as patches. dsh
plugin --profile <name> forwards verbatim to pnpm in the profile directory,
initializes on first use, and reconciles the dsh.plugins layer list after
add/remove (patch-less packages warn and stay plain dependencies). Config
dumps and the keyless web e2e scaffold compose the same bundle layers over the
same empty root as the boot.
2026-08-06 06:29:06 +08:00
Turtle
8af3babaea docs: rescan rebased documentation hierarchy 2026-08-05 16:16:57 +08:00
Turtle
012bb0a549 docs: address review feedback 2026-08-05 16:13:28 +08:00
Turtle
dd8d446286 docs: apply hierarchy across the corpus 2026-08-05 16:13:28 +08:00
imccyu
ca27512529 fix(packages): omit source publication payloads 2026-08-05 01:15:19 +08:00
imccyu
b7c0e4ea08 fix: dsh exports
fix: pty-local postinstall
2026-08-05 01:11:49 +08:00
imccyu
1dffc3cb86 pkg: upgrade node-addon 2026-08-05 01:11:49 +08:00
Huanqi Cao
39b0695d48 Merge remote-tracking branch 'origin/master' into feat/pwsh-tool
# Conflicts:
#	apps/cli/composition.md
#	docs/testing.i18n.yaml
#	docs/testing.md
#	docs/testing.zh.md
2026-08-04 17:54:45 +08:00
fz
193d918856 Merge remote-tracking branch 'origin/master' into codex/fix-headless-sigint
# Conflicts:
#	apps/cli/README.i18n.yaml
#	apps/cli/README.md
#	apps/cli/README.zh.md
#	apps/cli/config/base.cordis.yml
2026-08-04 15:18:48 +08:00
Turtle
10bb9cbf4a cleanup: remove TUI package and legacy dsh entrypoints 2026-08-04 13:20:28 +08:00
fz
f0e5a39d19 Merge remote-tracking branch 'origin/master' into codex/fix-headless-sigint 2026-08-04 11:07:36 +08:00
fz
8ea88a589b test(cli): cover shutdown timer invariants 2026-08-04 11:03:52 +08:00
Huanqi Cao
9abc0723dd Merge remote-tracking branch 'origin/master' into feat/pwsh-tool 2026-08-03 23:44:38 +08:00
Huanqi Cao
b978c62a22 Merge pull request #1119 from deepseek-harness/feat/ripgrep-packaged-binary
feat(fs-search): spawn the packaged ripgrep binary through the subprocess seam
2026-08-03 23:31:29 +08:00
fz
003d297442 Merge remote-tracking branch 'origin/master' into codex/fix-headless-sigint 2026-08-03 22:21:46 +08:00
Turtle
05a76c3d98 Merge pull request #1355 from deepseek-harness/codex/remove-invariants-shipped-config
cleanup(cli): omit invariants from shipped configs
2026-08-03 22:18:51 +08:00
fz
ef23b88ad4 fix(cli): bound telemetry shutdown on signals
Refs #1356
2026-08-03 22:08:47 +08:00
Turtle
414c310324 cleanup(cli): omit invariants from shipped configs 2026-08-03 22:04:53 +08:00
Turtle
25d8bfac31 Merge remote-tracking branch 'origin/master' into perf/tui-resume-scan
# Conflicts:
#	packages/ui/tui/README.i18n.yaml
2026-08-03 21:16:51 +08:00
Turtle
7a26214a81 feat(tui): resolve resume titles through the projection cache
session-title already registers a title projection unit; /resume now
reads it instead of scanning logs: live rows from the registry
snapshot, persisted rows from the durable checkpoint row
(cachedSnapshot, zero I/O), and only rows without a usable checkpoint
pay a coldSnapshot — checkpoint plus readFrom tail, written back so the
next scan is metadata-only. Cold reads are bounded by the new
resumeScanConcurrency config; compositions without the cache fall back
to the bounded readTitleSnapshots batch. The TUI overlay mounts the
projection registry, storage, and projection-cache rows over the same
storages root the web surface uses, so checkpoints serve both.
2026-08-03 21:13:52 +08:00
Huanqi Cao
ffeccc138b Merge branch 'feat/ripgrep-packaged-binary' into feat/pwsh-tool
# Conflicts:
#	docs/module-graph.md
2026-08-03 20:22:16 +08:00
Huanqi Cao
0ee198d70e Merge remote-tracking branch 'origin/master' into feat/ripgrep-packaged-binary 2026-08-03 20:19:49 +08:00
Yichen Jiang
85b9914934 Merge remote-tracking branch 'origin/master' into worktree/fix-multi-select-custom-answer
# Conflicts:
#	packages/ui/tui/README.i18n.yaml
2026-08-03 16:26:55 +08:00
Yichen Jiang
109d5b5e1c Merge remote-tracking branch 'origin/master' into worktree/fix-multi-select-custom-answer
# Conflicts:
#	apps/web/tests/snapshots/question-composer/answered.expected.md
#	apps/web/tests/snapshots/question-composer/session.jsonl
#	docs/core-data-structures/user-interaction.i18n.yaml
#	packages/client/ui-question/README.i18n.yaml
#	packages/host/apiproxy/README.i18n.yaml
#	packages/host/apiproxy/README.md
#	packages/host/apiproxy/README.zh.md
#	packages/ui/tui/README.i18n.yaml
#	packages/ui/user-interaction/README.i18n.yaml
2026-08-03 16:09:17 +08:00
Turtle
2178c66b0d test(tui): refresh queued compaction help snapshot 2026-08-03 16:06:51 +08:00
Turtle
ad4aeacd19 fix(hmr,include): settle a failing boot instead of a silent exit 13
Master's transactional loader made the invalid-provider PTY case regress:
the HMR main watcher's initial scan refreshed the include mid-initial-apply,
the concurrent group updates stranded the include fiber, and once serialized
the failing apply's rollback deadlocked on HMR's refresh drain — dsh exited
13 with no diagnostic and the terminal stranded, the exact symptom this
branch fixes. Serialize every include child-tree mutation through one queue
and pass ignoreInitial to the HMR main watcher; the failing boot now settles
through boot()'s labelled rejection with the tree disposed and exit 1. The
PTY case asserts the settled diagnostic; the fail-loud release remains the
guard for rejections boot cannot see.
2026-08-03 14:14:20 +08:00
Turtle
b37c22bd0a Merge origin/master: combine boot-owned settlement with the fail-loud release
Conflicts: apps/cli/src/tui.ts (keep the release install over master's comment
rewording), packages/ui/app-boot/README* (master's new installFailLoud row
wording plus this branch's release and timeout rows).
2026-08-03 14:10:23 +08:00
Tianyi Cui
1ea72962eb Merge master into fix/subagent-stack-end-result 2026-08-02 23:13:00 +08:00
Huanqi Cao
37bceb7dad Merge branch 'feat/ripgrep-packaged-binary' into feat/pwsh-tool 2026-08-02 22:29:44 +08:00
Huanqi Cao
ffb7d681f4 Merge remote-tracking branch 'origin/master' into feat/ripgrep-packaged-binary 2026-08-02 22:28:21 +08:00
Ziya
fff0172208 Merge pull request #1125 from deepseek-harness/agent/web-search-timeout-budget
fix(cli): give shipped web search a 60s budget
2026-08-02 05:42:41 -04:00