163 Commits
Author SHA1 Message Date
Yichen Jiang 3bcc1a0bf7 Merge remote-tracking branch 'origin/master' into worktree/minimal-no-runtime-context
# Conflicts:
#	packages/core/system-prompt/README.i18n.yaml
#	packages/core/system-prompt/README.md
#	packages/core/system-prompt/README.zh.md
#	packages/examples/agent-spine-demo/README.i18n.yaml
#	packages/examples/agent-spine-demo/README.md
#	packages/examples/agent-spine-demo/README.zh.md
#	packages/self-modification/tool-cordis/src/api-catalog.ts
2026-08-13 10:24:29 +08:00
imccyu 499d397631 feat: optimze skill and tool 2026-08-13 03:46:20 +08:00
imccyu 96d8f9516b feat: optimize prompt 2026-08-13 03:18:04 +08:00
imccyu a7d4cd8e1b fix: ci 2026-08-13 01:33:31 +08:00
imccyu 4064198560 • feat(self-modification): add dynamic Cordis plugin runtime and UI 2026-08-13 01:29:36 +08:00
Tianyi Cui a2d0f7f411 refactor: apply repository naming contract
Apply the accepted pre-release package, service, type, directory, and role renames as one repository-wide change.
2026-08-13 00:54:38 +08:00
Yichen Jiang ece8645080 fix: suppress runtime context in minimal profiles 2026-08-12 23:28:15 +08:00
fz 47e3e6f0ed feat(web): rename code preset to PTC mode 2026-08-12 21:47:28 +08:00
Yichen Jiang d4e06d6dd8 Merge pull request #2279 from deepseek-harness/worktree/preset-user-root-in-package
agent-presets owns the writable preset root instead of awaiting an app
2026-08-12 11:53:55 +08:00
Huanqi Cao 50bedaf03b Merge pull request #2234 from deepseek-harness/feat/loader-entry-disabled-interpolation
feat(loader): interpolate the entry disabled field
2026-08-12 11:40:02 +08:00
Yichen Jiang dfa2d91dcb Merge master into worktree/preset-user-root-in-package
Overlaps master on the preset e2e, the web scaffold, and the generated
config-catalog triplet; all merged textually, so the catalog is regenerated and
its pairing hashes re-recorded rather than trusted.
2026-08-12 11:08:55 +08:00
Huanqi Cao 05a8846c6e refactor(preset): trim the shell-row comments
Drop the filler and factually loose comments added with the tool-pwsh rows:
the platform expressions self-explain, the web-app overlay comment repeats
the file header, and the spec comments now state only the platform-scoped
evaluation fact.
2026-08-11 21:38:51 +08:00
Yichen Jiang 715baae6c1 fix(agent-presets): let the invariant follow the roster it actually scans
The service moved its "is a roster composed" reads to the derived root set;
the invariant companion still read `config.roots`. In the shape this change
exists for — an app configures nothing and the roster is the harness home
alone — that made the advisory warning fire while the fail-loud invariant
stayed silent, so an agent could address a model against an empty global
layer unchecked.

Both now read one source: `roots` exposes the resolved set, and the invariant
asks it. That decides the behavior deliberately rather than by omission — a
composition that mounts the roster now fails an unjoined agent whether its
roots were configured or derived, and `includeUserRoot: false` with no
configured roots is how a deployment keeps its agents on the host plane. Both
shapes are pinned; the derived-only case fails against the old predicate.

Three pieces of prose went stale with the first commit: the web-app bundle
comment still called the writable root an assembly fact patched in by
AppCLIEntry (removed in the profile-plugin-bundles refactor — `composeProfile`
owns it now, and only for the shipped root), and the shipped skill and its
Agent Note still called both roots "configuration". The README gains the
resolved-roster reader and the discoverable-but-undeletable preset a second
writable root produces.
2026-08-11 21:13:41 +08:00
Yichen Jiang adfd54dc5c Merge branch 'master' into worktree/minimal-profiles-bare-runtime 2026-08-11 21:11:32 +08:00
Huanqi Cao ccb3e4aaac Merge remote-tracking branch 'origin/master' into feat/loader-entry-disabled-interpolation
# Conflicts:
#	scripts/verify-cordis-config.ts
2026-08-11 21:04:49 +08:00
Huanqi Cao 32744c2b5c refactor(preset): gate tool-pwsh by platform alongside tool-bash
The web-app overlay now disables the host tool-pwsh row too, and the shipped
presets (standard/code/cordis) declare both shell tool rows with inverted
platform gates — tool-bash on POSIX, tool-pwsh on win32 — so the preset layer
exposes exactly one shell tool per host and a preset can drop or replace the
shell tool on either platform. windows-shell.spec pins both preset gates and
both host tool rows disabled in the web composition; the loader and Windows
pwsh notes are updated in place.
2026-08-11 20:21:57 +08:00
Yichen Jiang 3fb325cfec fix(preset): state the user preset root plainly, resolve the acting path
The previous pass over-corrected: presenting both roots as unknowable made the
guidance vaguer without making it truer. Every `dsh` launcher puts authored
presets under `$DSH_HOME/.agent-presets`, so the skill and persona state that
as the answer to "where do my presets live" and reserve the read-back for the
path an agent actually opens or edits, which `copy()` already reports.
2026-08-11 20:04:32 +08:00
Yichen Jiang 782d670f24 fix(preset): stop presenting the preset roots as constants
Neither root is a property of the preset layer. `Config.roots` defaults to `[]`;
the two the shipped agent sees are patched in by the dsh CLI, whose user root is
`dshHomePath('.agent-presets')` and whose system root ships beside the install.
`writableRoot()` then takes the first `user` root from whatever configuration
supplied it.

The skill and the persona stated `${DSH_HOME:-$HOME/.dsh}/.agent-presets/<id>/`
as where authored presets live, which is only the default install's answer, and
no call reports either root: `authorable` says whether a writable one exists,
and `list()` cannot reveal a user root that is still empty. Both now keep that
path as what to tell a user asking where to look, and direct every path an agent
acts on to `list()` or `resolve()` — which is also what `copy()` already relies
on, since it chooses the writable root itself and reports the file it created.
2026-08-11 19:56:45 +08:00
Yichen Jiang 63b564bb4b fix(preset): cover both root-realm rejections and narrow the packaging claim
Review found two errors in the first pass.

The skill quoted `service "<name>" has been registered at <Owner>` as the
rejection for a service published into the root realm. That message only
appears when the loose name collides with one the host already supplies. A
preset publishing its OWN new service name reaches the root realm successfully
and is rejected by the mount audit instead, with `row(s) published
process-global service(s) [...]` — which is the shape a forgotten realm
actually takes. Both messages are now listed, verified by mounting one preset
of each kind.

The Agent Note claimed every harness package publishes exactly three file
patterns. 139 do; 28 also publish `lib/client.js`, 22 publish
`lib/types/**/*.js`, and `dsh-message-feedback` publishes `src`. The note now
states only the fact it needs: no package's `files` includes its README.

Also from review: the copy step no longer repeats the display name it already
set, the id rule spells out `[a-z0-9][a-z0-9-]*`, and the sandbox step says
reads need no escalation.
2026-08-11 19:49:47 +08:00
Yichen Jiang 0b3ac6356b fix(preset): correct the composition-authoring skill and give it a real check
The `cordis` preset's `editing-cordis-compositions` skill is the only guidance
an agent has when it authors a preset, and four of its statements were false.

`tool-bash` was named as the worked example of a row that hides a service; it
provides nothing and injects `bashEnv` from the host's own `bash-env` row, so
following that advice strands the row behind its realm and the preset fails to
mount. The `isolate` example composed `tasks-local` with `tool-tasks`, which
the shipped compositions' own comments say breaks `run_in_background`. A string
realm label was described as pooling one instance; labels join realms and
`provide()` still throws on the second registration. Rows were to be checked
against a package README, which no harness package publishes.

Verification is now the agent's own: `standingKeyFor(id)` runs the same mount a
session start performs and rejects an unresolvable package, an invalid config, a
service in the root realm, and a row that never activated. The skill states that
`list()`'s `broken` field is a shape check that every one of those passes, ships
the `cordis_mount` plugin that reaches the roster service, and names `copy()` as
the authoring write. The prohibition on touching the shipped install is promoted
to its own section and extended to the host composition.

Fixes #2266
2026-08-11 17:43:09 +08:00
Yichen Jiang 2b5c17ab6b Merge remote-tracking branch 'origin/master' into worktree/minimal-profiles-bare-runtime
# Conflicts:
#	examples/jsonrpc-agent/README.i18n.yaml
#	examples/jsonrpc-agent/README.md
2026-08-11 15:51:03 +08:00
Huanqi Cao 2a6658252c Merge remote-tracking branch 'origin/master' into feat/loader-entry-disabled-interpolation
# Conflicts:
#	.agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.i18n.yaml
#	.agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.md
#	.agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.zh.md
2026-08-11 15:33:09 +08:00
Yichen Jiang 663253099d feat: align minimal runtime profiles 2026-08-11 15:19:33 +08:00
Huanqi Cao 4308f91e88 refactor(bundle): fold the Windows shell platform layer into the base rows
Entry \disabled\ interpolation makes the launcher's separate platform layer
unnecessary: the base bundle's cordis.patch.yml now gates both shell stacks
on its own rows — bash-sandbox/tool-bash disable on win32, and their twins
pwsh-sandbox/tool-pwsh mount only there with the inverted expression — so
exactly one shell stack mounts per host from one shared patch file.

windows.cordis.patch.yml and the launcher's windows-shell.ts injection (boot,
live recomposition, config dumps) are deleted, with the workspace-constraints
entry and the dsh-base exports/files entries following. The windows-shell spec
pins the effective per-platform roster through the real bundle layers, and
base.spec pins the four symmetric gates. The superseded active notes are
updated and cross-linked; the loader note records the fold itself.
2026-08-11 15:11:45 +08:00
Huanqi Cao 6fb226ea24 feat(loader): interpolate the entry disabled field
The Windows platform layer disables tool-bash and inserts the pwsh stack, but the shipped presets each mount a tool-bash row that re-enabled the tool on win32 — the session had both a PowerShell-backed bash tool and tool-pwsh, silently, because no spec pinned the composed preset layer.

The Loader now evaluates a disabled: !!js expression against the loader context at every mount decision; disabled is the only interpolated metadata field, and the raw node stays in the options so write-back keeps the !!js form. The standard/code/cordis presets gate tool-bash with process.platform === 'win32', verify-cordis-config allows expressions in disabled only, and the windows-shell spec pins the preset-level invariant.
2026-08-11 11:33:53 +08:00
Yichen Jiang 19ea7ae573 Merge remote-tracking branch 'origin/master' into worktree/preset-plane-fallout-p1
The pre-commit staged-pairing hook was bypassed for this merge: master's
`docs(notes): archive superseded dsh run decision` (7ee9e16001) arrives as a
rename into `.agents/notes/archived/`, and the hook hands that path to
`verify-translation-pairing`, which correctly refuses an archived note as
out-of-corpus. The full-corpus gate passes (851 pairs).
2026-08-11 00:32:25 +08:00
Yichen Jiang a7eab2867a Merge remote-tracking branch 'origin/master' into worktree/preset-plane-fallout-p1
`minimal` was rewritten upstream into a fixed-prompt PTY surface that now
carries its own compaction group, so this branch's token-meter move applies to
it too: the row leaves the group and `tokenMeter` leaves the realm, exactly as
in the other three presets. Master's header prose is kept whole — the note this
branch had added there described a composition that no longer exists.

`docs/event-producer-consumer.*` is generated; regenerated after the merge.
2026-08-10 23:50:28 +08:00
pku-xht 396925ad4c Merge pull request #2137 from deepseek-harness/codex/product-subagent-presets
feat(agent-presets): add Codex and Claude Code subagent tools
2026-08-10 23:43:23 +08:00
Yichen Jiang 114b41eca4 refactor(presets): use one editor family 2026-08-10 22:47:28 +08:00
Yichen Jiang 1cfbafab6a fix(preset): keep the token meter host-plane and name unjoined agents
Moving the agent plane behind presets left two readers on the wrong side of
the host/agent line.

`dsh-token-meter` was disabled on the host and mounted inside each preset's
`compaction` realm, but its three projection units register into the
process-wide `sessionProjections` table. A unit registered from one preset
answers for every session, so whether a `minimal` session showed a context
meter depended on whether some other session had mounted `standard` since
boot, and a process that only ever ran `minimal` showed none. The meter takes
no configuration, keys every fold by Session, and registers no tool or prompt
section, so it returns to the host composition and leaves the presets'
`isolate` map; the realm and `compact-basic` stay, because what a preset
chooses is whether its agent compacts, not whether its tokens are counted.

Nothing named an agent that joined no preset. The join is a scope-parent link,
and without it the tools, prompt-section, and skill views resolve the empty
global layer: the agent publishes, the turn runs, and the model receives
nothing. `AgentPresets` now logs one warning per such agent while a roster is
configured, and the invariant companion fails outright — at
`system-prompt/assemble` rather than at publication, because an unjoined agent
is legal until it addresses a model and `recompose` binds exactly such an
agent. The warning stays advisory: a synchronous `agent/created` throw vetoes
publication, and the ACP bridge, SDK server, and headless bundle all create an
unjoined agent today.

Three limits are recorded rather than fixed: projection key presence is not a
per-session capability signal, a superseded standing generation is never
reclaimed, and a `cordis_mount` temporary plugin belongs to the composition
rather than the session that mounted it.

Fixes #2203
2026-08-10 22:36:06 +08:00
pku-xht 8d88edb880 Merge commit '0b307c4ea80ba603dee91b46731e4d14644d84d2' into codex/product-subagent-presets 2026-08-10 21:07:22 +08:00
Yichen Jiang 388ce89823 Merge remote-tracking branch 'origin/master' into worktree/fix-minimal-preset-prompt
# Conflicts:
#	apps/cli/config/agent-presets/minimal/preset.yml
2026-08-10 21:05:29 +08:00
pku-xht e6b6f1a047 Merge master at 35eeb4a112 2026-08-10 20:25:58 +08:00
Yichen Jiang eacd5e2167 fix(prompt): remove unreachable complete branches 2026-08-10 20:09:06 +08:00
Yichen Jiang 13d4d4b3f3 fix(web): focus creator preset description 2026-08-10 19:44:42 +08:00
Yichen Jiang 5e3dd2fd34 fix(web): clarify shipped preset capabilities 2026-08-10 19:40:19 +08:00
Yichen Jiang 86d5dd4384 fix(preset): align minimal agent with RL composition 2026-08-10 18:02:11 +08:00
Yichen Jiang 8ae1a9d6a8 Merge remote-tracking branch 'origin/master' into fix/preset-host-plane-task-registry 2026-08-10 16:53:03 +08:00
Turtle a27efdef36 docs: make technical prose concrete 2026-08-10 16:34:20 +08:00
Yichen Jiang 59e759ce13 fix(tasks-local): layer control surfaces and listeners by registering scope
One host registry serves every composition in the process, so its two
service-wide collections answered per-owner questions process-wide. `start()`
asked only whether SOME surface was attached, so an agent whose own composition
loads no `tool-tasks` could start work it has no tool to collect or stop as soon
as any other preset attached one — and the answer changed depending on which
sessions happened to be open. `settle()` walked every registered listener, so a
task settling without a waiter injected one completion notice per mounted
preset into the same owner.

Both collections now sit in `ScopedLayers`, the layered-registry primitive
`tools` and `skills` already use: a registration files into its registering
context's scope, and a read unions the global layer with the owner's scope
chain. A surface or listener registered from an unscoped context lands in the
global layer and serves every owner, which is exactly the host-plane
composition's own controls, so the TUI path is unchanged without a special
case.

This supersedes the consumer-side filter in the previous commit. That filter
produced the right notices but sat in the wrong layer: it left the `start()`
gate process-wide, it could not be enforced against a producer that resolves
the registry directly, and it made a Consumer carry scope knowledge that the
other layered registries keep in the registry. `tool-tasks` is scope-agnostic
again and the `dsh-scope` edge moves to `tasks-local`.

`start()`'s refusal is now owner-relative, so its model-visible text names the
agent rather than the process. The shipped `minimal` preset keeps
`enableRunInBackground: false`, no longer as the safety boundary — the registry
owns that now — but so an agent that could never collect a task is not offered
the parameter at all.

Refs #2141
2026-08-10 16:12:41 +08:00
Yichen Jiang 5d86a284e5 fix(web-app,agent-presets): keep the task registry on the host plane
`tool-bash` resolves the background-task registry with `ctx.get('tasks')`,
and it sits at the preset's top level. The registry sat inside an
entry-local `isolate: { tasks: true }` realm, which is invisible to every
sibling row outside it, while the Web surface disabled the host row — so
both lookups missed and every `run_in_background` call answered
"background tasks unavailable" with `task_output`, `task_list`, and
`task_kill` still listed in the catalog. `task_list` returning
"(no background tasks)" is what made the outage read as an empty queue
rather than a severed producer.

That is the `goals` criterion read from inside the preset: a Service a row
outside its realm READS belongs to the plane both can see. `tasks` already
keys access by owning agent (`assertAccess` compares `task.owner.id`) and
mints an independent token per `attachSurface` call, so one host instance
serves every session exactly as before presets — the per-preset-standing-mounts
note records that sharing `tasks-local` is a return to its design.

`minimal` mounts no `tool-tasks`, and the `start()` control-surface gate is
a service-wide set that another preset's controls would open for it, so its
`tool-bash` disables `run_in_background` and drops the parameter from the
schema.

Fixes #2141
2026-08-10 14:18:41 +08:00
pku-xht b1d67a6935 feat(agent-presets): add Codex and Claude Code subagent tools 2026-08-10 12:45:05 +08:00
Yichen Jiang 7dcefeda50 Merge branch 'stack/agent-profiles-5-web-ui' into stack/agent-profiles-8-authoring
# Conflicts:
#	apps/cli/tests/web-agent-presets.e2e.ts
#	packages/client/connection/README.i18n.yaml
#	packages/client/connection/README.md
#	packages/client/connection/README.zh.md
#	packages/client/ui-conversation/src/client/contract/slots.ts
#	packages/client/ui-primitives/tests/icons.spec.tsx
#	packages/client/ui-settings/src/client/contract/slots.ts
#	packages/host/apiproxy/tests/api-proxy-agent-preset.spec.ts
2026-08-09 23:02:37 +08:00
Yichen Jiang 0d7106d902 Merge branch 'stack/agent-profiles-3-wire' into stack/agent-profiles-5-web-ui
# Conflicts:
#	apps/cli/tests/web-agent-presets.e2e.ts
#	packages/host/apiproxy/tests/api-proxy-agent-preset.spec.ts
2026-08-09 22:48:47 +08:00
Yichen Jiang 4788569889 feat(skill): layer the host skill registry per scope like the tools registry
The skills capability moves back to the host plane as one singleton, layered
with the ScopedLayers infrastructure the tools registry established: a
registration files into its calling context's layer (repository plugins and
host rows land global, a preset's skill-local lands in that preset's layer),
and a read merges the global layer with the viewing scope's chain, the
nearest layer winning duplicate names outright while rank keeps deciding
duplicates within one layer.

The web-app bundle re-enables the base skill registry row (skill-local and
tool-skill stay preset-owned), the standard preset drops its isolate skills
realm for bare rows over the host registry, tool-skill passes the calling
agent as the view scope, and the gateway's skills domain reads the host
registry in the presenter scope — a cold session now resolves its recorded
preset's standing key instead of failing.
2026-08-09 22:10:38 +08:00
Yichen Jiang c56f23ae1f fix(agent-presets,web): broken presets are roster rows, not gaps
A hand-damaged preset was silent until the worst moment. An unparsable
composition listed as an ordinary selectable row and failed only at the
next session start — set as default, every new session failed. A
directory whose composition file was deleted vanished from the roster
while still occupying its id: copy answered "delete the existing preset
first" while remove answered "not found", a dead end.

Discovery now owns health: every id-shaped directory is a roster slot,
broken when its composition is missing or unloadable, checked with the
loader's own entryListSchema dialect (!!js included) so health never
rejects what the loader accepts. `broken` rides AgentPreset, the
agentPreset.list entry, and the UI row; mount/recompose/standingKeyFor
refuse broken up front with the discovery-reported reason, while
resolve/read/remove still answer. The section renders marked red cards —
unselectable, uncopyable, deletable, location kept on custom rows — and
both pickers drop broken rows entirely.

The cordis preset's persona now forbids editing the shipped install
(corrupting cordis would disable the mode itself) and points authoring
at $DSH_HOME/.agent-presets; its skill teaches preset.yml metadata, the
copy-first workflow, the one-escalation sandbox reality, and honest
verification. Exercised live: asked to edit the shipped composition the
composed agent refuses citing both rules; asked for real presets (simple
and complex) it lands them under the user root with one approved
escalation each and self-checks with the loader dialect.
2026-08-09 02:17:56 +08:00
Yichen Jiang cacc8fbca7 Merge branch 'stack/agent-profiles-5-web-ui' into stack/agent-profiles-8-authoring
Authoring meets standing mounts: write() and remove() drop the standing
pointer so the NEXT session composes the edited roster, while every session
already joined keeps the generation it runs on — a superseded generation is
never disposed while the process lives. The settings-dialog golden re-records
with this layer's Agent Preset nav entry, which the incoming layer-3 record
had overwritten.
2026-08-08 19:09:32 +08:00
Yichen Jiang 01bd8ecefe Merge branch 'stack/agent-profiles-3-wire' into stack/agent-profiles-5-web-ui
recompose becomes a parent re-link: the new preset's standing mount is
ensured BEFORE the link moves, so a failed switch leaves the agent exactly as
it was — the unmount-then-restore dance (and unmountPresetFor with it) is
gone, and the restore-failure test now asserts the agent KEEPS its tools with
the source directory deleted, because the standing mount is not the file.
2026-08-08 18:52:45 +08:00
Yichen Jiang 46649858f6 Merge branch 'stack/agent-profiles-1-seam' into stack/agent-profiles-3-wire
Rewires this layer onto the standing-mount model:

- serviceForAgent roots its search at the agent's standing mount (parent
  scope key → live mount fiber) — the composition no longer lives under the
  agent's own fiber, and two agents on one preset now address ONE instance,
  which the sharing test asserts instead of distinctness.
- viewFor/historyPage take a registry view SCOPE. A live agent is that scope;
  a cold read uses the recorded preset's standing key via standingKeyFor —
  composing plugins but starting no agent, session, or turn. A header without
  a preset (a pre-roster log) renders through the DEFAULT preset's standing
  layer; an unusable preset degrades the read to generic cards, never fails
  it. This turns produced-files and chat-scroll green structurally, with the
  token counts untouched (no resume, so the projections fold stays detached).
- The detached projections baseline now includes every standing unit's key at
  its empty fold (todos: null): the standing mount registers units
  deterministically, which is what makes the client's "omitted key =
  capability absence → clear" rule safe again. seeded-history's contract test
  asserts the new shape.
- The standard preset's realm preamble no longer claims a shared label pools
  instances — provide() throws on the second registration under one realm
  symbol; labels join REALMS.
2026-08-08 18:41:07 +08:00
Yichen Jiang 65730f2aef fix(agent-presets): keep the goal service on the host plane in the other presets
`cordis` and `code` are full copies of `standard`, so each carried its own
copy of the goals group the Gateway cannot reach through. Same change, same
reason.
2026-08-08 15:56:22 +08:00