Commit Graph
100 Commits
Author SHA1 Message Date
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
imccyuandGitHub 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
imccyu 5ca7be5dcb release(dsh): 0.0.1-rc.2 2026-08-11 22:52:39 +08:00
imccyuandGitHub e690867a8a Merge pull request #2265 from deepseek-harness/worktree/multimodal-ui
feat(web): align attachment display with DeepSeek Chat via ui-attachment atoms
2026-08-11 19:55:56 +08:00
imccyuandGitHub f123fb280f Merge pull request #2201 from deepseek-harness/worktree-remoteevent
feat(remote): deliver allowlisted Host events through ctx.remote.$on
2026-08-11 19:38:40 +08:00
imccyu 23c35108d6 fix(test): inject locale settings dependencies 2026-08-11 19:25:43 +08:00
imccyu 3727e242cd fix(ci): satisfy lint contracts-ready lane 2026-08-11 19:25:43 +08:00
imccyu ac01c3b035 fix: lint 2026-08-11 19:25:42 +08:00
imccyu 473289b46a fix: snapshot 2026-08-11 19:25:42 +08:00
imccyu 2c4b3d015b fix: setting scope ctx 2026-08-11 19:25:42 +08:00
imccyu ffdf81b28d fix(ci): repair remote event coverage and catalogs 2026-08-11 19:25:42 +08:00
imccyu e0bbe42242 refactor(web): consume owner remote events directly 2026-08-11 19:25:42 +08:00
imccyu 80f1a064f5 fix: dep 2026-08-11 19:25:41 +08:00
imccyu b7c99b696c test(web): mount the settings domain base in the assembled boot table
`assembled-boot.ts` hand-maintains the boot entries for the built-bundle jsdom
graph, and this branch made the locale and ui-theme rows inject `settingsScope`.
Nothing in that table provided it, so both stayed pending, ui-layout never
activated behind them, and all four assembled snapshots timed out waiting for
the first rendered row. Adds the ui-settings entry ahead of them (it injects
nothing itself) and records `api-gateway` on the runtime entry, which now needs
`remote`.
2026-08-11 19:25:41 +08:00
imccyu 32378d27b4 docs(ui-settings): describe the settings domain base and its shell
`SettingsRoot` and the `sidebar.settings` occupancy moved to ui-settings-general,
but both READMEs still read as if ui-settings were the shell. ui-settings now
documents what it actually is — the `ctx.settingsScope` transport plus the slot
type declarations, with the bind ownership, refresh triggers, write revision
handling, and decode refusal a preference row must know — and records the
loopback-only and single-field write gaps in place of two limitations that no
longer exist. ui-settings-general documents the shell it now renders. The stale
module JSDoc on both halves follows.
2026-08-11 19:25:41 +08:00
imccyu 364f7c7076 docs: compile the note's type-contract and allowlist examples
The six `ignore-check` fences this note added pushed doc-typecheck's repo-wide
opt-out ratio past its 50% ceiling. Two of them were whole compilable units all
along: the type-meta declarations only lacked the cordis `Events` import, and the
allowlist example compiles as one module once the value and its derived type face
sit in the same block. The four that remain are genuine fragments — two interface
member signatures, a union arm, and an expression whose operands are declared
elsewhere.
2026-08-11 19:25:41 +08:00
imccyu e874e80012 fix: test 2026-08-11 19:25:41 +08:00
imccyu 01ecb43ebc docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.

docs: regenerate the catalogs and retarget the moved declarations

The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.

`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.

refactor(client): make ui-settings the settings domain's base layer

The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.

Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.

The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.

The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.

refactor(client): reach the settings transport through ctx.settingsScope

Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.

The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.

The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.

client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.

test(client): bind the settings transport in the specs that boot a preference row

Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.

Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.

The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.

fix(client): carry the settings-scope move across the release manifests

Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.

ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.

fix(api-gateway): give each $on subscription its own registration and containment

Two defects in the forwarded-event subscription table, both raised in review:

A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.

A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.

Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.

Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.

Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.

refactor(remote): deliver forwarded frames through ctx.remote.$dispatch

The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.

`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.

The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.

runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.

refactor(api-remotes): keep the allowlist value out of types.ts

`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.

The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.

test(ui-task): bind the locale plugin's new injections in its bench

The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.

docs: close the documentation gates for the forwarded-event surface

Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:

- The generators write only the English side, so the Chinese sides of
  `event-producer-consumer` and `module-graph` had drifted: the former still
  listed the three deleted client-face events and pointed at declaration sites
  this branch moved into `types.ts` modules, and the latter carried a stale
  dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
  apiproxy README and the design note now link the English note from both
  languages, and the note's code blocks are byte-identical across the pair
  (a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
  documents a discipline reviewers apply, so it belongs in the bilingual corpus
  rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
  signature, a union arm, or a snippet that names symbols it does not import, so
  none is a compilable unit.

docs(agent-note): transition the forwarded-event note to implemented

The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).

Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.

Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.

Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.

fix: docs

fix: test
2026-08-11 19:25:41 +08:00
imccyu 77ccffa35e feat(api-remotes): enforce the forwardable shape and resolve the invariant companion
The allowlist's satisfies assertion is active again, so a name that is not a
declared event, binds a Scope, or is not one-way fails at the allowlist rather
than on the wire; the owner packages' ./types imports are what give it a real
event vocabulary to judge. The companion's own paths entry lets its test resolve
it from source instead of a built artifact, matching the api-gateway entry.
2026-08-11 19:25:40 +08:00
imccyu 04b2f7bcc9 test(web): keep the browser e2e out of the Client build graph
These e2e boot a real harness and read Host services, so they type-check in the
Host aggregate; importing a Client package there pulls its whole project — and
everything it references — into the Host build phase, where a Client project
waiting on generated artifacts cannot compile. The welcome-notice constants are
mirrored in the scaffold instead, which lets apps/cli drop the Client project
references that only served as their owner map.
2026-08-11 19:25:40 +08:00
imccyu d88f771e19 feat(remote): deliver allowlisted Host events through ctx.remote.$on
api/remotes owns the allowlist and its type projection; type-meta owns the shape
predicate, the selection seat, and the internal remote/host-event carrier
signal; api/gateway's Client half turns that signal into $on callbacks through a
private dispatch. apiproxy forwards each allowlisted emission verbatim in one
host/remote-event frame, registered ahead of the derived invalidation frames so
frame order is unchanged, and drops the three per-event variants it replaces.
Owner packages move their Events declarations into client-safe ./types exports,
so a consumer's listener signature is the Host's own declaration.
2026-08-11 19:25:40 +08:00
imccyu b64da061a8 docs: record the forwarded-event surface and its face boundaries
Each owner package's README states which of its cordis events are forwarded and
that the declaration now lives in the client-safe ./types export. api/remotes
documents the allowlist as the single control point and the deliberate
both-faces listing of src/types.ts; development.md keeps the split-tsconfig rule
current.
2026-08-11 19:25:40 +08:00
imccyu 7c8ee81818 docs(agent-note): propose Remote event delivery via ctx.remote.$on
An allowlist in api/remotes drives verbatim forwarding of Host cordis events
that bind no AgentScope, wrapped in one host/remote-event frame on the existing
host stream. Consumers subscribe through ctx.remote.$on, whose listener type is
the owner package's own Events declaration, so the payload contract holds by
construction rather than through a restated table.
2026-08-11 19:25:39 +08:00
imccyuandGitHub ea574b5fca Merge pull request #2180 from deepseek-harness/worktree-dark
fix(client): apply theme before plugin loading
2026-08-11 16:41:50 +08:00
imccyu f0a6c25291 fix(client): apply theme before plugin loading 2026-08-11 15:35:32 +08:00
imccyuandGitHub 3fcf947765 Merge pull request #2200 from deepseek-harness/fix/agent-preset-ui-flow
fix(web): Some UI problem: agent preset UI fix, subagent badge flash fix
2026-08-11 11:04:04 +08:00
imccyuandGitHub 45befb87e2 Merge branch 'master' into fix/agent-preset-ui-flow 2026-08-11 10:54:13 +08:00
imccyuandGitHub db1028a84d Merge pull request #2188 from deepseek-harness/feat/trajectory-conversation-nodes
refactor(ui-trajectory): assemble registered conversation nodes
2026-08-11 04:20:45 +08:00
imccyu 3f7c2a25b6 docs: record trajectory context assembly 2026-08-11 03:29:04 +08:00
imccyu 1af8c96682 perf(ui-trajectory): avoid locale formatting in hot path 2026-08-11 03:29:04 +08:00
imccyu d66e693c63 fix(ui-trajectory): place steering before request boundary 2026-08-11 03:29:04 +08:00
imccyu 98a4cc6569 style(ui-trajectory): align presentation render tree 2026-08-11 03:29:04 +08:00
imccyu 2695f31edb test(ui-trajectory): assert deferred preview sources 2026-08-11 03:29:04 +08:00
imccyu e27dba8457 perf(ui-trajectory): defer trajectory text processing 2026-08-11 03:29:04 +08:00
imccyu 58ea69f64f build(ui-trajectory): use rescoped cordis package 2026-08-11 03:29:04 +08:00
imccyu cc25bceec8 fix(ui-trajectory): simplify terminal contribution branch 2026-08-11 03:29:04 +08:00
imccyu 556b11ef56 docs: refresh module graph 2026-08-11 03:29:04 +08:00
imccyu 62f5d05039 fix(ui-trajectory): tighten conversation assembly contracts 2026-08-11 03:29:04 +08:00
imccyu b3d3e423f2 fix(ui-trajectory): retain parallel tool interruptions 2026-08-11 03:29:04 +08:00
imccyu fc4df896a0 perf(ui-trajectory): index trajectory snapshot assembly 2026-08-11 03:29:04 +08:00
imccyu 3469de58eb fix(ui-trajectory): consume prompt changes once 2026-08-11 03:29:04 +08:00
imccyu a342b329e3 docs(client): document trajectory conversation assembly 2026-08-11 03:29:04 +08:00
imccyu c828d38f51 refactor(client-runtime): remove legacy history fold 2026-08-11 03:29:04 +08:00
imccyu f479c60b6d feat(ui-trajectory): assemble registered conversation nodes 2026-08-11 03:29:04 +08:00
imccyu 3e79f7106e feat(client-runtime): add target-owned conversation snapshots 2026-08-11 03:29:04 +08:00
imccyuandGitHub aa6d0b6efd Merge pull request #2220 from deepseek-harness/release/dsh-0.0.1-rc.1
Release: dsh@0.0.1-rc.1
2026-08-11 03:25:09 +08:00
imccyu b64c3ac1ba release(dsh): 0.0.1-rc.1 2026-08-11 03:20:36 +08:00
imccyu a5c23dd36a fix: prerelease version 2026-08-11 03:20:34 +08:00
imccyuandGitHub 41360d98bc Merge pull request #2218 from deepseek-harness/release/cordis-4.0.1-rc.1
Release: cordis 4.0.1 rc.1
2026-08-11 02:52:55 +08:00
imccyu 4cd77a5ad9 release(vendor): cordis 4.0.1-rc.1, cosmokit 1.8.2-rc.1, group 1.0.1-rc.1, hmr 1.0.16-rc.1, include 1.0.5-rc.1, loader 1.0.1-rc.1, logger-console 1.0.1-rc.1, schemastery 3.18.1-rc.1, timer 1.1.3-rc.1 2026-08-11 02:46:10 +08:00
imccyu 9840d39ba0 feat(release): rehearse a vendored publication with a prerelease
release:vendor --prerelease rc.1 appends the identifier to the version each
package would take, so a first publication can be thrown away instead of holding
the stable dist-tag: publish already routes any version with a prerelease
segment to --tag next.

A prerelease does not consume its release numbers, so the stable release that
follows takes the same ones rather than skipping a patch. Deciding that needs
semver precedence, which git's version sort does not provide — it places
4.0.1-rc.1 above 4.0.1 — so the newest published version is chosen by comparing
versions here, numeric prerelease fields numerically.
2026-08-11 02:43:12 +08:00
imccyuandGitHub 1af1eb84a4 Merge pull request #2212 from deepseek-harness/worktree-npmpub2
build(release): publish as three independent npm sequences
2026-08-11 02:00:52 +08:00
imccyu ae75aca776 fix(release): supply the Landlock entry tarball to the packed install
dsh-sandbox-local declares @deepseek-ai/node-addon-landlock-run in
dependencies, not optionalDependencies, so omitting optional dependencies left
npm resolving it from a registry that does not carry it. The dsh pack job now
packs that entry for verification; its own platform packages stay out, being
optional and needing a musl toolchain per architecture.

The verification reads each directory by its contents rather than a pack order
file, because a directory packed only to satisfy a cross-sequence dependency has
no release order to describe.
2026-08-11 01:48:56 +08:00
imccyu 21db3220d6 fix(release): omit optional dependencies from the packed install
The Landlock platform packages sit behind optionalDependencies, and npm fails
the install on their 404 rather than skipping them: they belong to the native
sequence, whose pack needs a musl toolchain and one build per architecture, so
this job cannot produce them and holds no credentials to fetch them. A consumer
that cannot install them must still start, which is what optional means here.

The release spec also gains block bodies where the lint rule rejects returning a
void expression from an arrow shorthand.
2026-08-11 01:39:01 +08:00
imccyu d9dcf5a484 fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.

The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.

Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.

Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.

Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.

The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
imccyu bcc4890038 fix(release): follow the workspace protocol in the invariant fixture and knip config
The package-invariant spec built its fixture with the range the rule no longer
accepts. knip stopped needing the tar ignore entry once the tarball helpers
became the one place that spawns it.
2026-08-11 01:01:40 +08:00
imccyu 787443b13f fix(release): verify the packed install against the framework it peers on
The harness packages declare the vendored framework as a peer, so installing
only the dsh tarballs left npm resolving @deepseek-ai/cordis from a private
registry the credential-free pack job cannot reach. The verification now takes
several pack directories and installs every tarball in them, and the dsh
workflow packs the vendored family for that purpose while still publishing only
its own set.

The verification also reads what each tarball declares instead of what the
checkout says, which is what let the process and tarball helpers become one
home each - the three copies of a spawn wrapper were a duplication finding.
2026-08-11 00:51:02 +08:00
imccyu bb489d1325 fix(release): align the package-invariant and public-link gates
The invariant companion rule pinned @deepseek-ai/dsh-invariants to a ^0.0.1
peerDependency, which the workspace protocol replaces; it now requires
workspace:^ like every other workspace-internal reference.

The release note stated the provenance risk by naming the internal repository,
which the public-link gate rejects in tracked files. It now states the same
constraint without naming it.
2026-08-11 00:46:04 +08:00
imccyu a943e67798 feat(release): bump and commit a release family in one command
release:dsh takes major, minor, patch, or an explicit version and writes one
version across the family; release:vendor takes none and increments each
package's own patch, but only for packages whose published payload changed since
their vendor-<package>-v* tag. That tag is the record of the commit a package
last published from, so the change judgement needs no state file, and the diff
is filtered through the manifest's files rules - editing a vendored comment does
not trigger a release.

Both refresh the lockfile, commit, and print the tag to create after the commit
merges. --dry-run reports the plan and writes nothing.

Incrementing the release numbers is also what drops an upstream prerelease
segment: cordis 4.0.0-rc.7 publishes as 4.0.1, because a prerelease version
would not satisfy a consumer's plain range.
2026-08-11 00:36:39 +08:00
imccyu 27c9ca12a2 feat(release): drive the installed entry from the packed tarballs
A throwaway consumer outside the repository declares every member as a file:
dependency, installs, and runs the installed executable with plain Node,
asserting the version it reports. That is the check a workspace link or a stale
lib/ in the checkout cannot pass for: it reads only what files selected.

The family declares its executable, so the vendored family — libraries a
consumer imports, with no executable — states that it has none instead of
carrying a probe that would prove nothing.

Both pack workflows run it after packing, still without credentials.
2026-08-11 00:26:26 +08:00
imccyu 2c85c484d3 build(release): reference workspace members through the workspace protocol
1504 hand-written ranges pointing at workspace members become workspace:^, so
pnpm pack substitutes each member's real version at publication: sibling
peerDependencies follow the family version instead of being pinned at ^0.0.1,
and a reference to a vendored package follows that package's own line. Without
this, publishing 0.0.2 ships peer ranges naming a version that does not exist,
and 0.0.1-rc.1 does not satisfy ^0.0.1 either.

It also retires ranges that had gone stale against the workspace: ^4.0.0-rc.6
for a 4.0.0-rc.7 checkout, ^3.17.0 for schemastery 3.18.0.

workspace:* stays where an exact published version is the point, which is how
the Landlock entry pins its platform packages.

A workspace constraint now requires the protocol, so a new package cannot
reintroduce a hand-written range. The same constraint caught packages/boot/cmdline
arriving on master without the publishable trio, which this change completes.
2026-08-11 00:17:09 +08:00
imccyu d4128ae57f chore(release): satisfy knip for the release scripts
The release scripts spawn tar to read a packed tarball, and resolving a family
by id is the only entry point into the family list.
2026-08-11 00:09:32 +08:00
imccyu 35e2601a28 docs(release): add the English npm release sequences note and its pairing
Also corrects the Chinese text where decisions moved after it was written: the
release set is every member of packages/, apps/, and vendor/ rather than a
dependency closure, because plugins are mounted by name from cordis.yml and a
closure misses runtime-required packages; the Landlock packages publish
privately with the rest; and the manifests carry repository metadata alongside
the access level.
2026-08-11 00:09:32 +08:00
imccyu 97eb14a007 build(release): make the release set publishable under the private scope
Every package under packages/, apps/, and vendor/ drops "private": true and
declares publishConfig.access "restricted": the repository now states which
packages it publishes instead of deciding it at publish time. Each one also
declares its repository and directory, which is how a consumer of a private
package reaches its source.

The Landlock packages move to restricted with them. They have never been
published, so nothing anonymous depends on them today, and the whole
@deepseek-ai scope stays private.

The workspace constraint that required every package to be private now applies
to non-members only, and asserts the publishable trio on each release member.
2026-08-11 00:09:31 +08:00
imccyu 4e91230dd6 ci(release): pack on pull requests and publish from family tags
Each sequence gets its own workflow so the two version models never meet in one
file. Pack runs without credentials on every pull request and master push, so a
pull request proves the whole publish set still packs; publication is a manual
dispatch guarded by the npm-publish environment, runs only from that family's
tag, and never builds - it uploads the bytes pack produced.
2026-08-11 00:09:29 +08:00
imccyu 8cd38945f1 feat(release): add release family metadata, pack, verify, and publish
A release family owns its member discovery, version baseline, tag naming, and
packed-payload rule; the dsh family shares one version across packages/ and
apps/, while every vendor/ package keeps its own version line. Publish order is
topological over runtime dependencies so no package reaches the registry before
one it depends on.

pack packs the whole family into one directory and records the upload order;
publish decides per package against the registry, skipping a version whose
published tarball has the same integrity and failing when it differs, which is
what makes re-running publish over one artifact safe.

The vendored packages keep upstream's payload: their manifests export ./src/*,
so the harness rule that rejects sources and declaration maps would publish an
export map pointing at absent files.
2026-08-11 00:09:28 +08:00
imccyu a7c056c512 docs(release): propose three independent npm publish sequences
packages/, vendor/, and native/ carry different version baselines and change
at different times, so each releases on its own bump sequence, tag prefix, and
workflow. Records the vendor change judgement (per-package tags as the
last-published pointer), the registry-state publish rule that makes re-runs
idempotent, and the workspace:^ prerequisite for publishing at any version
other than the current 0.0.1.

English counterpart and pairing record follow once the Chinese text is
reviewed.
2026-08-11 00:09:28 +08:00
imccyuandGitHub f19ac640b5 Merge pull request #2122 from deepseek-harness/worktree-npmpub
build(vendor): rescope vendored Cordis into @deepseek-ai
2026-08-10 22:15:34 +08:00
imccyu ec601ca13d build(vendor): rescope the vendored Cordis packages into @deepseek-ai
Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it
prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`,
`verify-translation-pairing --write` for the touched bilingual pairs,
`gen-doc-graphs`, and one typert snapshot whose ids embed character offsets.
`pnpm run rescope-vendor --check` verifies the result.

Renames nine vendored packages (cordis, cosmokit, schemastery and the six
@cordisjs plugins) and every reference that resolves them: manifest names and
dependency keys, module specifiers including declare-module merges, cordis.yml
plugin names, tsconfig paths, every Markdown fence, and `docs/` prose.
Directory names, upstream versions, and dependency ranges are unchanged, so
vendor/README.md still reads as an upstream snapshot; its manifest table gains
an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed
at each fork's origin.

The tutorial tier follows the rename end to end: its yaml fences named plugins
the Loader can no longer resolve, its `ts ignore-check` fences disagreed with
the compiled fences beside them, and its prose quoted both. The contracts that
told readers to keep upstream names — the root convention and the vendoring
cookbook's tree comment and manifest invariant — now say to rescope instead.

Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle
purity gate now names the vendored libraries a browser bundle inlines, and the
files where a bare `cordis` is an agent-preset id keep that product data.
2026-08-10 22:04:13 +08:00
imccyu 78e9b8bec5 fix(scaffold): resolve the framework peer from this repository, not a registry
Two sites reached a registry for the vendored framework, which the rescope
turns from a silent second copy into a hard failure.

Live-link mode relinked only the root manifest, so a generated workspace member
— `plugins/*/package.json` — resolved its own dependencies from the registry
and installed upstream cordis beside this repository's vendored copy.
`LinkWorkspace.relinkNestedManifest()` relinks every nested generated manifest;
`peerDependencies` keeps its range because package managers reject a link spec
there.

The sandbox publish-path rehearsal installs this repository's vendored cordis
and cosmokit tarballs instead of naming a registry version.
2026-08-10 22:04:10 +08:00
imccyu 194828e8b8 build(vendor): add the @deepseek-ai rescope codemod, its mapping doc, and its Agent Note
Every harness package declares cordis as a peer dependency, so publishing the
harness publishes the vendored framework layer too; under the upstream names
that publication would squat them on the registry.

scripts/rescope-vendor.ts owns the rename: the nine-package mapping, a
delimited-token rule that leaves cordis.yml, the Loader's cordis: builtins and
vendor directory names alone, per-file exemptions where a name is a directory
or an upstream runtime identifier, and the exact edits for sites a token rule
cannot express — dot-notation lookups, unquoted manifest keys, a regex literal
whose failure would make every Context-merge scan silently find nothing, the
vendored-manifest table, and the contracts that told readers vendored packages
keep their upstream names.

Markdown follows the rename inside every fence, because a fence is code a
reader copies or configuration they mount, and in `docs/` prose as well, where
a sentence quoting a name teaches something this repository no longer resolves.
Prose elsewhere records what was true when it was written, and the same
spelling can mean something else: the Python SDK's `cordis` option, or the
unvendored `@cordisjs/plugin-http`. `docs/rescope.md` states both names on
purpose and is exempt.

exactEditState() classifies every exact edit as pending, applied, or invalid.
An insertion keeps its anchor and a deletion keeps its remainder, so each side
counts the form that survives: a duplicated insertion, a half-applied
replacement, and a deletion whose remainder moved are all invalid. The run
classifies every edit before writing anything and aborts on the first invalid
one, so a disagreement between the mapping and the tree cannot leave a
half-rescoped checkout; each write re-reads its file, because two edits can
target one. rescope-vendor.spec.ts pins those rejections, and --check asserts
the whole post-state from the hygiene gate, so CI owns the invariant.

--reverse restores the upstream names, verified as a round trip: reverse, then
apply, reproduces this tree byte for byte.

docs/rescope.md is the consumer-facing reference: the old-name/new-name table
with each package's role, what the rename deliberately leaves alone, the sites
callers must change, and the commands to apply, verify, and revert. The Agent
Note carries the decision and its consequences.

The rename itself lands in the next commit, produced by running the script.
2026-08-10 22:04:10 +08:00
imccyuandGitHub 32f4235981 Merge pull request #2194 from deepseek-harness/worktree-dshclient
refactor: nest client manifest metadata under dsh
2026-08-10 21:19:28 +08:00
imccyu 717792b631 refactor: nest client manifest metadata under dsh 2026-08-10 21:11:09 +08:00
imccyu 854f6623bb docs: rename client manifest field references 2026-08-10 21:11:09 +08:00
imccyuandGitHub 6648ff984a Merge pull request #1817 from deepseek-harness/feat/web-queue-steer-all
feat(web): 空输入时 Ctrl/Cmd+Enter 将当前排队消息全部插话发送
2026-08-10 12:58:20 +08:00
imccyuandGitHub 793f6f55df Merge pull request #2101 from deepseek-harness/feat/clientsessionbiz
refactor(client): decouple conversation node assembly
2026-08-09 21:00:32 +08:00
imccyu f4c817648d docs fix compact checkpoint type example 2026-08-09 20:53:06 +08:00
imccyu 1566a00eeb test compact checkpoint snapshot identity 2026-08-09 20:42:39 +08:00
imccyu d99ad2b1d0 fix conversation node review follow-ups 2026-08-09 20:36:57 +08:00
imccyu 81822f4076 fix(ui-conversation): skip uncorrelated legacy events 2026-08-09 20:23:58 +08:00
imccyu 0cfe852c2d fix(ui-conversation): ignore legacy compact events without ids 2026-08-09 20:21:49 +08:00
imccyu fcbc97a88d fix(client): address conversation assembly review 2026-08-09 20:08:37 +08:00
imccyu 126ad5bb02 docs: development 2026-08-09 19:48:58 +08:00
imccyu e11f630fcd fix(client): address remaining conversation review feedback 2026-08-09 19:36:03 +08:00
imccyu 27682b9384 fix: docs 2026-08-09 19:00:22 +08:00
imccyu 924cfcef95 fix: coverage 2026-08-09 18:56:29 +08:00
imccyu 1b1e170c1f refactor(agent): restore conventional types entrypoint 2026-08-09 18:43:37 +08:00
imccyu 10464d155d fix(client): consume typed business session events 2026-08-09 18:42:25 +08:00
imccyu 3d70889a8d refactor(session): unify event map augmentation entrypoint 2026-08-09 18:17:19 +08:00
imccyu 990f700d3c fix(client): address conversation assembly review feedback 2026-08-09 18:17:19 +08:00
imccyu 8723a0e15f fix(client): address conversation assembly review findings 2026-08-09 18:16:44 +08:00
imccyu 522acc7432 test: update conversation assembly coverage and docs 2026-08-09 18:16:44 +08:00