Commit Graph
11837 Commits
Author SHA1 Message Date
imccyu 2c2d9f26e5 fix: client spec 2026-08-12 02:16:39 +08:00
imccyu 7ad54e7791 refactor(client): name the compile face in every client test filename
A test file under packages/client now says which face it covers:
`*.client.spec.{ts,tsx}` and its `*.client.{ts,tsx}` helpers belong to the
Client aggregate, `*.host.spec.ts` to the host aggregate. The carrier's four
node-half specs take the Host suffix.

The two suffixes are mutually exclusive, so each aggregate excludes the
other's and both keep one broad test glob: `exclude` wins over `include`, and
`packages/client/**` no longer has to be excluded wholesale from the host
program with per-file `files` entries carved back out of it. A Host-face spec
that reaches only Host source therefore needs no cross-face project
reference, which the split-project rule rejects.

vitest still discovers every file through `**/*.spec.{ts,tsx}`.
2026-08-12 01:41:40 +08:00
imccyu 7c3bd75bcc test(remotes): unwrap the Remote envelope in the built-lib chain
Every generated method resolves to RemoteResult, so the plain-Node script
must read the business value through `.value`: the CAS ref it passed on to
goals/edit was undefined, which the client codec rejected before the request
left. The invalid-payload case keeps its try/catch — a codec-rejected
argument still throws at the Client Remote face rather than folding into the
error branch.
2026-08-11 23:56:39 +08:00
imccyu b0d3686f57 fix: pkg 2026-08-11 23:33:18 +08:00
imccyu 9b2925e50f fix: restore the credential-rejected branch and admit SRC absence by key
Review follow-ups that are logic rather than documentation:

- rpc.schema.ts had lost the credential-rejected branch while api-proxy.ts
  still returns that code, so a legitimate business error failed the
  client's response parse. Restore the branch and assert it.
- rpc-schemas.spec.ts had lost the workspace list, archiveSession and
  insertSessionBefore cases along with the command schemas; those routes
  still ship, so restore their coverage.
- An omitted SRC field is now recognized by an absent key instead of an
  undefined value, which makes the allowance assertExactArguments already
  granted reachable; an explicitly undefined field stays invalid input. A
  weak descriptor's undefined result rides the wire as an absent value,
  matching the envelope removal.
- The chooser unmounts an already-created backend when the surface entry
  fails to load, and no longer reverses the captured id array in place.
2026-08-11 23:33:18 +08:00
imccyu 378141bf5d fix: test 2026-08-11 23:33:18 +08:00
imccyu 6332bd3513 fix: build 2026-08-11 23:33:18 +08:00
imccyu 91e7b3d086 test(apiproxy): drop the carrier-signal case for the removed command route
`command.execute` no longer exists on the API Proxy, and the fake's own
handler went with it, so the case only reached a 404 body. The same carrier
behaviour is asserted on live routes by its `session.search`,
`subagent.prompt` and `host.pickDirectory` siblings.
2026-08-11 23:33:18 +08:00
imccyu dffc0a9a15 test(gateway): pin each folded carrier failure message verbatim
Matching the full `RemoteFailure` keeps the assertion inside the typed result
and drops the `expect.stringContaining` placeholders, whose `any` return the
lint rule rejects on assignment.
2026-08-11 23:33:18 +08:00
imccyu 7a9d7d2657 chore(hygiene): restore the cordis peers and teach knip the split faces
`@deepseek-ai/cordis` returns to the peer and dev dependencies of
`dsh-client-ui-model` and `dsh-client-ui-skill`; every harness package declares
it, and the client type-assembly rename dropped it from both. The commands
manifest ships `src` alongside its generated typert faces, matching the
`./src/*` export it already declares.

knip gains the two directory-picker surface workspaces, whose specs are `.tsx`
and matched no default pattern, and ignores `zod` in the commands workspace:
that dependency belongs to the generated Remote and Host faces in `lib/`, which
knip never scans. The unused `dsh-client-test-runtime` and
`dsh-client-connection` dev dependencies are gone; the picker surface's own spec
never imported the former, and ui-settings reads the carrier's types through the
Remote assembly now.
2026-08-11 23:33:17 +08:00
imccyu 3e41b38b39 test(gateway): fold a non-Error carrier throw into the client error branch 2026-08-11 23:33:17 +08:00
imccyu 160aec7115 chore(deps): record the commands zod dependency in the lockfile 2026-08-11 23:33:17 +08:00
imccyu 380b8a18a8 fix(commands): declare the zod runtime dependency of the generated faces
Exporting `./typert` and `./remote` ships two generated modules that
`import { z } from 'zod'`, and the package declared no runtime dependency at
all. Under pnpm's isolated layout nothing resolves zod for it — there is no
root `node_modules/zod` to walk up to — so loading the plugin tree failed with
ERR_MODULE_NOT_FOUND the moment a composition mounted the commands typert
face. The two other packages exporting `./typert` both declare it; this
matches them.
2026-08-11 23:33:17 +08:00
imccyu 92e0e3377f test: finish the Remote-result and picker-split test migrations
Every generated Remote method resolves to `RemoteResult<T>`, so the Gateway
client spec asserts the ok and error branches instead of the unwrapped value
and a throw, and the generator fixtures declare the wrapper in the consumer
face they typecheck. The RPC-failure test splits into the Host error carried
verbatim in the error branch plus a transport throw folded into it.

The runtime client, ui-command and ui-plan benches answer the generated
commands Remote through its result branches and provide the `remote.commands`
namespace their plugins now inject; the ui-command bench also serves the `$on`
the service subscribes on construction.

The directory-picker chooser mounts a backend and its surface as a pair, so the
real-Loader composition serves both surface packages and asserts each entry
arrives and leaves with its backend.
2026-08-11 23:33:17 +08:00
imccyu 03f88e3c3d test(gateway): report a business rejection under an aborted carrier as cancelled 2026-08-11 23:33:17 +08:00
imccyu ea791827f4 test(typert): reject a lookup parameter that accepts undefined 2026-08-11 23:33:17 +08:00
imccyu 51b5f5b565 test(picker): cover the node halves of the split directory-picker faces 2026-08-11 23:33:17 +08:00
imccyu 92e817bf38 chore(lint): satisfy the trailing-comma rule and drop dead oxlint directives
The Gateway client spec's synthetic Remote namespace now resolves under both
analyzers, so its typescript/no-unsafe-call suppressions report as unused.
2026-08-11 23:33:16 +08:00
imccyu df97e4ce2d fix: dep 2026-08-11 23:33:16 +08:00
imccyu 159d102f99 refactor(client): name one assembly package for the carrier types
A business package imported the Remote assembly for `ctx.remote` and the
Connection plugin for the wire types it passes around. The assembly now
re-exports the carrier's Client-facing types, so a business package names one
package. The re-export is type-only: the carrier's runtime values keep their
own module edge, since inlining them here would duplicate the carrier inside
the assembly bundle.

Four surfaces that had no Remote assembly dependency declare one now.
2026-08-11 23:33:16 +08:00
imccyu 027cbdfe5d refactor(faces): keep the generated contributions out of the Host aggregate
The Gateway and the carrier each compiled both halves under one tsconfig, so
the Host aggregate built their browser faces — including the face that owns
`ctx.remote`, the most likely future consumer of a generated `/remote`
contribution. Both packages now expose a host and a client face, and each
aggregate references only its own; three modules the halves share appear in
both file lists, as api/remotes already does.

The two apps/web specs in the Host aggregate restate the conversation engine's
Context key format instead of importing the Client runtime for it. A drift
makes the key miss its rendered node, so the assertion fails loudly.

The Host aggregate now reaches one Client project, the carrier's host face,
which the Gateway's own dispatch face needs; no generated contribution is
reachable from it.
2026-08-11 23:33:16 +08:00
imccyu 40af20cafe refactor(picker): split the directory-picker faces into their own packages
The browse and native backends were dual-face packages: a Node backend plus a
browser surface under one tsconfig that referenced Client packages. That put
Client projects — and through them the Client runtime — inside the Host
compiler aggregate, which builds before the generated Remote contributions
exist. Each browser half moves to its own Client package, and both backends
become Node-only.

The interaction is still one choice: the adaptive chooser mounts the backend
and its surface as a pair of Loader entries and tears both down in reverse, so
a resolved kind still swaps both faces. Compositions that pin an interaction
directly now pin the pair, and the chooser's runtime-string package list keeps
naming everything a composing app must resolve.
2026-08-11 23:33:16 +08:00
imccyu 070a2a7f1e refactor(commands): move the command service to Remote
`CommandService.list` and `execute` carry the wire contract directly through
`@Remote`, and the Client assembly mounts the generated commands
contribution. The legacy API Proxy route, its schemas, the map rows, the
generated client methods and the fixture's command domain are removed, so the
catalog and the admission call have one owner again.

`Session.command()` keeps a result-shaped public face for parity with the
prompt, cancel and attachment neighbours it sits beside, and reads the
generated namespace through one `SessionRemotes` parameter. The Session
cluster declares that face against the owning business package rather than the
generated contribution: the Host compiler aggregate builds this package, and
it runs before any contribution is emitted.

Migrated calls lose the `title-invalid` class of protocol-only error codes and
report `internal`; no production caller branched on them.
2026-08-11 23:33:15 +08:00
imccyu a2981207b0 feat(typert): deliver the carrier outcome from ctx.remote
Every generated Remote method now resolves to `RemoteResult<T>`: the Client
face folds a carrier failure, a transport throw and a rejected result payload
into one error branch, so no consumer wraps a call to recover them. Only
assembly faults still reject — a wrong argument count, an unmounted method, a
missing Context binder, an absent Connection.

`RemoteFailure.code` stays an open string because the closed RPC code union
lives in the carrier package, which already depends on type-meta; naming it
here would invert that edge.

The goal surface drops its own try/catch plus the structural probe that
guessed whether a thrown cause was an RPC failure, and reads the result
instead.
2026-08-11 23:33:15 +08:00
imccyu 027e5fe9a4 feat(typert): carry Remote absence without a second result envelope
Absence crosses the wire as a missing field: an omitted argument and a void
or undefined result both arrive as an absent JSON member, and the wide RPC
result slot accepts a success response without a value. Parameters declared
optional stay optional in the generated consumer declaration, so a business
signature is never widened to `T | undefined` to suit the wire. The weak SRC
descriptor reads parameter names from a JavaScript signature and cannot see
optionality, so a source-launched Host accepts an absent field and the strict
LIB pass owns rejecting a genuinely missing required parameter.
2026-08-11 23:33:15 +08:00
imccyu 8c31290abb docs: propose unary API Remote migration 2026-08-11 23:33:15 +08:00
Yichen Jiang 0a0737f33b Merge pull request #1873 from deepseek-harness/fix/code-mode-executor-collapse
fix(tools): collapse code-mode executor to run_code for model-direct calls
2026-08-11 23:22:54 +08:00
Yichen Jiang db4e3e39a5 docs: refresh generated source references after the collapse-section constants
Adding COLLAPSE_SECTION_ORDER and CODE_ONLY_INSTRUCTION shifted every
later line in packages/core/tools/src/index.ts by 14, and three generated
artifacts cite those lines: the subsystems cordis-surface region, the
event producer/consumer matrix, and the Agent Note pair record left
inconsistent by the cherry-pick resolution.
2026-08-11 23:11:12 +08:00
imccyu 38f99f04f1 Merge pull request #2286 from deepseek-harness/release/dsh-0.0.1-rc.2
release(dsh): 0.0.1-rc.2
2026-08-11 23:04:55 +08:00
Yichen Jiang 261349c95e test(acp): refresh both-mode expected prompt onto the merged base 2026-08-11 22:55:53 +08:00
Yichen Jiang e258cf7a2d fix(tools): resolve the collapse through the scope, not the deployment default
`collapses()` read `defaultMode`, so the collapse only applied when the
DEPLOYMENT was `code`. An agent handed `code` by an agent preset under a
native default announced `[run_code]` on the wire and still executed a
model-direct native call -- the bypass this collapse exists to close,
reopened for exactly the composition `dsh-agent-tool-mode` produces.

`modeFor(scope)` is the same resolution `wireSchemas` and the SDK section
already use, so presentation and execution cannot disagree, and a mode
inherited from a standing preset scope collapses like a declared one.

The per-agent and preset tests asserted only the wire, which is why the
regression passed them. They now assert through the executor: the body
never runs, the call resolves UNKNOWN_TOOL, and the native sibling beside
it still executes.
2026-08-11 22:53:17 +08:00
imccyu 5ca7be5dcb release(dsh): 0.0.1-rc.2 2026-08-11 22:52:39 +08:00
Yichen Jiang 5d2c943d38 fix(tools): state the code-mode collapse in the prompt and the denial
The executor collapse landed without telling the model it exists. Every
tool contributes its own guidance section naming its tool, none of them
qualify how that tool is reached, and they all render before the SDK
(orders 100-199 against SDK_SECTION_ORDER 150), so the prompt said "Use
the read tool" eleven times and never said only run_code is callable.

A real session shows the consequence: the model emitted a native call,
read `unknown tool "read"` for a tool the same prompt declares, and
concluded the deployment was inconsistent rather than routing through
run_code.

The registry now contributes `tools:code-only` at order 99 -- ahead of
the guidance band -- stating the rule, registered wherever `tools:sdk`
is and rendering empty outside an effective `code`. `both` renders it
empty because its native calls do execute, which is also why
both-mode-turn no longer shares code-mode-turn's expected prompt. The
denial itself now names the route back, since a bare UNKNOWN_TOOL for a
declared tool is what misled the model.
2026-08-11 22:51:16 +08:00
Chinesezjc 428aec44e6 ci: retrigger windows native runner 2026-08-11 22:42:17 +08:00
Chinesezjc 47f108bf50 test(tools): use type-safe error assertions instead of any casts
oxlint's no-unsafe-member-access rejects member access through an any
cast; the error info is reachable through the declared optional chain.
2026-08-11 22:40:36 +08:00
Chinesezjc 610dc74ea8 test(tools): fix error code assertions for collapsed call tests
The error code lives on ToolFailure.info.code, not ToolFailure.code.
2026-08-11 22:40:36 +08:00
Chinesezjc 6d0a7c12e1 test(tools): add coverage for collapsed model-direct call under code mode
Add two executor-level tests covering the previously uncovered branches
in createExecution:
- collapsed call (non-aborted signal) returns UNKNOWN_TOOL
- collapsed call (pre-aborted signal) returns ABORTED_BEFORE_DISPATCH
2026-08-11 22:40:36 +08:00
Chinesezjc 846969900a ci: retrigger 2026-08-11 22:40:36 +08:00
Chinesezjc 1e78513806 fix(tools): add collapses() method and fix createExecution collapse logic 2026-08-11 22:40:36 +08:00
Chinesezjc 24dd48b133 fix(tools): collapse code-mode executor to run_code for model-direct calls
wireSchemas() already advertised only run_code under mode: 'code', but the
executor resolved every call through get(), which returns the full visible
map plus the reserved transport. A model could name a native tool directly
and bypass run_code entirely. Route the execution-path lookups through a
new private resolveExecution() that applies the mode collapse at the
operation boundary: model-direct calls under 'code' may only name run_code
(UNKNOWN_TOOL otherwise), while SDK sub-dispatches (parent token set) keep
every visible tool. get()/schemas() public semantics are unchanged.

The denial happens at createExecution, before the extensible policy
pipeline — pre-execute listeners, approval ask, and guards never observe
a call that is deterministically denied. A collapsed call honors the
pre-dispatch cancellation contract, routes aborted results through the
visible tool's finalizeContent, and captures the finalizer before
argument materialization.

Regenerated docs, catalogs, graphs, scoped events, re-recorded
translation pairs, and updated test assertions.

Fixes #1815
2026-08-11 22:40:36 +08:00
Chinesezjc 2aef2d83fa fix(tools): remove tool:* prompt filtering per review feedback
LegGasai noted that filtering prompt sections by tool:* prefix is a poor
heuristic: it conflates section naming convention with presentation
semantics and would incorrectly drop tool:structured_output. The executor
collapse already enforces the boundary — a model-direct native call is
rejected as UNKNOWN_TOOL regardless of what the prompt says, so filtering
the prompt adds no security and only risks losing useful guidance.

The tool:read/tool:bash/etc sections describe capability usage patterns
that apply to both native and code presentations, and keeping them does
not reopen the native direct-call path because the executor blocks it.
2026-08-11 22:40:36 +08:00
Chinesezjc b558afc373 fix(tools): use @deepseek-ai/cordis import in README type blocks 2026-08-11 22:40:20 +08:00
Chinesezjc 6f6defd64a fix(tools): collapse code-mode executor to run_code for model-direct calls
wireSchemas() already advertised only run_code under mode: 'code', but the
executor resolved every call through get(), which returns the full visible
map plus the reserved transport. A model could name a native tool directly
and bypass run_code entirely. Route the execution-path lookups through a
new private resolveExecution() that applies the mode collapse at the
operation boundary: model-direct calls under 'code' may only name run_code
(UNKNOWN_TOOL otherwise), while SDK sub-dispatches (parent token set) keep
every visible tool. get()/schemas() public semantics are unchanged.

The denial happens at createExecution, before the extensible policy
pipeline — pre-execute listeners, approval ask, and guards never observe
a call that is deterministically denied. A collapsed call honors the
pre-dispatch cancellation contract, routes aborted results through the
visible tool's finalizeContent, and captures the finalizer before
argument materialization.

Under code mode, a system-prompt/assemble listener filters out tool:*
guidance sections that told the model to call native tools directly.
The tools:sdk section and SDK types remain so programs can still use
all tools through run_code.

Regenerated docs, catalogs, graphs, scoped events, and re-recorded
translation pairs.

Fixes #1815
2026-08-11 22:40:20 +08:00
Chinesezjc 75a09efd51 fix(tools): use @deepseek-ai/cordis and @deepseek-ai/schemastery imports 2026-08-11 22:40:19 +08:00
Chinesezjc 4806fdabab fix(tools): collapse code-mode executor to run_code for model-direct calls
wireSchemas() already advertised only run_code under mode: 'code', but the
executor resolved every call through get(), which returns the full visible
map plus the reserved transport. A model could name a native tool directly
and bypass run_code entirely. Route the execution-path lookups through a
new private resolveExecution() that applies the mode collapse at the
operation boundary: model-direct calls under 'code' may only name run_code
(UNKNOWN_TOOL otherwise), while SDK sub-dispatches (parent token set) keep
every visible tool. get()/schemas() public semantics are unchanged.

The denial happens at createExecution, before the extensible policy
pipeline — pre-execute listeners, approval ask, and guards never observe
a call that is deterministically denied. A collapsed call honors the
pre-dispatch cancellation contract, routes aborted results through the
visible tool's finalizeContent, and captures the finalizer before
argument materialization.

Under code mode, a system-prompt/assemble listener filters out tool:*
guidance sections that told the model to call native tools directly.
The tools:sdk section and SDK types remain so programs can still use
all tools through run_code.

Fixes #1815
2026-08-11 22:40:19 +08:00
Tianyi Cui 564a853a04 Merge pull request #2277 from deepseek-harness/codex/background-first-continuable-subagents
feat(subagent): default continuable delegation to background
2026-08-11 22:21:02 +08:00
Yichen Jiang 925df04112 Merge pull request #2249 from deepseek-harness/worktree/minimal-profiles-bare-runtime
feat: align minimal runtime profiles
2026-08-11 21:57:03 +08:00
Tianyi Cui cb235da37f Merge pull request #2282 from deepseek-harness/worktree/deny-deepseek-base-urls-519
fix(boot): deny DeepSeek endpoint env overrides
2026-08-11 21:55:26 +08:00
Dudu-0223 c778b5b0db fix(subagent): address background-first review 2026-08-11 21:52:59 +08:00
Dudu-0223 98d4551597 docs: refresh module graph 2026-08-11 21:52:59 +08:00