Compare commits

..
Author SHA1 Message Date
imccyu f3803c9697 release(dsh): 0.2.2-alpha.2 (#7437) 2026-10-28 02:17:58 +08:00
imccyu 00d541438b release(dsh): 0.2.2-alpha.2 2026-10-28 02:03:15 +08:00
imccyu 7509c174d9 fix(build): serialize release builds and give Client checks 8 GiB (#7435)
* fix(build): serialize publication and raise client aggregate heap

* docs: refresh development guide translation pairing

* fix(release): align pack runner and allow explicit tag-check bypass
2026-10-28 01:58:28 +08:00
Xu Hanxiang 635d9c88f6 feat(liveshare): add the host, viewer, and client share surfaces (#6749)
* feat(liveshare): add the owner host, viewer host, and share runtime

The owner-side half of the bundle: the share host that publishes a Session to a Relay and answers one viewer per share, the owner credential/authorization records, the fork registry and its enforcement, the read-only viewer access policy and read budgets, the turn-requester fold, and the Relay gateway worker. The viewer host renders the served page's assets and declarations; no browser code is included in this layer.

* test(liveshare): cover the owner host, viewer host, and gateway

Keyless host-side coverage: share publication and restore, owner login/authority, fork registry and enforcement, viewer read policy and budgets, the turn-requester fold, the Relay tunnel worker and gateway commands, and the served viewer page. The browser surfaces are covered by the client layer's own suite.

* chore(build): register the host bundle in the compiler, dependency policy, and CI

The package manifest, Host compiler face, and tsdown host entries the bundle needs; the workspace constraints the built gateway/declaration artifacts require; the release-family and README model-experience rows; the coverage-lane exemption; and the keyless CI job that runs the share cases on Linux. The dependency-policy classifications wait for the client layer, which is where the package's client face (and its imports) first appears.

* docs(liveshare): record the bundle's subsystem, projection, and decision rationale

The package README for the host layer, the subsystem page, the persistence record that declares the viewer principal projection, and the note that keeps the protocol and owner-surface decisions with their evidence.

* feat(boot): offer the Liveshare host bundle in the CLI profile and plugin manager

The bundle joins the optional bundles the CLI installation carries and the Official group the plugin manager lists, so an owner can switch it on without editing a profile by hand. The plugin-manager and plugin-config expectations follow in the client layer that renders the card's browser half.

* chore(docs): regenerate the graphs, catalogs, and persistence records for the host

Run the generators over the host layer: the event producer/consumer graphs, the persistence catalog and schema inventory, the config catalog, the module graph, and the Cordis API catalog. The persisted viewer-principal event appears in the catalog and the historical format index counts it.

* fix(liveshare): keep the bundle's locale display metadata in this layer

The CLI profile registers the bundle as optional, so the plugin manager reads title and description from ./locale/*.json. Ship the two locale files and their export here; the browser half is not needed for display metadata.

* refactor(liveshare): read the turn boundary through the typed Agents service

The retained projection-handle migration left two assertions converted through unknown; ctx.get('agents') already exposes turnBoundaryOf, so the host and the cancel-admission case call it directly and the unknown-cast baseline stays untouched.

* test(liveshare): expect the canonical viewer tier from the three-mode harness

The Relay reports a share published with the withdrawn fork-readonly tier as view and carries the fork grant separately, so the harness compares the canonical tier and forwards an explicit allowsFork when the case names one.

* refactor(liveshare): share the reader-facing record and body readers

The fork decision and the access snapshot built the same share facts twice, and the two share-writing routes read the same share-naming JSON body twice; both pairs are now one helper each, so jscpd no longer reports them. Behavior is unchanged.

* test(web): record the host layer's optional-bundle expectations

Registering the LiveShare bundle in the Official group adds its card and description to the plugin manager and plugin-config pages, and the packed preview must state that its host plugin needs LIVESHARE_RELAY_URL instead of a generic applied toggle. Refresh the affected goldens locally (keyless web snapshot refresh) and keep the card badge at this layer: the client list that labels it experimental arrives with the client layer.

* docs(liveshare): mark the shipping owner policy note implemented

The protocol-and-owner-surfaces decision shipped with this bundle, so the note moves from proposed/ to implemented/ in both languages, its proposal-era headings become Decision/Verification/Consequences, and the subsystem page plus package README link to the new path. The pairing record moves with the triplet.

* fix(liveshare): judge tool turns from the open Session turn and recheck the narrowed write

The master baseline has no ToolExecutionInput.turn, so the host derives the turn a tool execution serves from the Agent's Session: the latest turn/start with no turn/end after it. A missing Agent, Session, or open turn yields no turn and the fork guard refuses the execution (fail-closed) instead of reading it as the owner's. Also refuse a fork that names a child without a bound rpcId/operationId, classify forwarded paths by their normalized pathname so /api/session/./fork or a query suffix cannot skip the fork decision, and ask the write gate again after the final authorization refresh so a share narrowed to view during fs/write-intent cannot write. The subsystem page, package README, and model-experience census follow the two-tier model and the bounded-write child ceiling.

* refactor(liveshare): read the open turn beside the requester fold

The derivation reads the same Session log the requester fold decides, so it
lives with that fold and the host imports it instead of keeping a private copy.

* test(liveshare): cover the derived turn and the refusal paths

Drive the host's real derivation from Session events: the open turn of the
Agent's Session decides a viewer turn is confined and an owner turn is not,
while a missing Agent, an absent open turn, and a requester the host no longer
drives all refuse instead of being read as the owner's. Cover the three safety
behaviours the host commit added: fork child naming without a bound request
identity, dot-segment/query fork paths that must still reach the fork decision,
and a share narrowed to view while fs/write-intent awaits.

* fix(liveshare): avoid the deprecated snapshot reader and the extra casts

The host derived the open turn by scanning the Session event log, which the
deprecation note forbids for new production calls. Read the same state from the
turnBoundary projection the Agents service maintains instead, and let a spec
supply a fixture boundary reader where production supplies turnBoundaryOf.
The live attribution of an execution is unchanged: an absent boundary or one
between turns still yields no turn and the guard refuses it fail-closed.

* docs(liveshare): describe the bundle's indirect model experience

The bundle's Model Experience moved to the audited indirect classification,
so the README sentence now names the path: viewer prompts the owner host
forwards into a shared Session drive that Session's model requests. The
KV-cache paragraph and the bilingual pairing record follow.

* test(liveshare): pin the turn-start admission for steered and admitted reader input

The pre-step admission already re-reads Relay authority from each entering message's own source and refuses queued reader input whose grant ended before the model runs; the new cases pin the steer path and the admitted viewer turn, so the turn-start half stays proven next to the delivery-time recheck the Session Controller owns.

* test(liveshare): adapt the replay and cancel expectations to the parsed tier

* test(liveshare): align the access-state expectations with protocol v2

The parsed share-access-state frame always carries allowsFileUpload, the
withdrawn fork-readonly word no longer implies a view tier or forking, and a
hand-built frame may not name it. Update the hand-built expectations and the
two fixture specs that named the retired word, without touching production
semantics.

* test(liveshare): record the in-flight cancel race after the controller revert

The Session Controller no longer re-reads a caller's access before cancelling,
so a cancel admitted before the reader's grant ends keeps its effect. State that
as the known limitation the earlier refusal tests documented instead.

* feat(liveshare): add the viewer attachment-upload switch

The share record, registration handshake, access-state replay, and share-viewer
policy carry an owner switch that is off by default: it admits fileUploads/upload
on the shared Session and the viewer's own forks, is independent of the tier and
of forking, and is normalized to off at every untrusted state-file or frame
boundary.

* docs(liveshare): state the sandbox tiers and the attachment switch

Name the two execution tiers as a read-only sandbox and a bounded writable
sandbox, and document the independent default-off switch that lets admitted
viewers upload attachments, including that legacy shares read as off.

* refactor(liveshare): drop the withdrawn fork-readonly migration

The feature never shipped, so no stored record can name the withdrawn tier and
the boundary needs no compatibility path. Read a stored fork switch as exactly
what it states, let an old Relay state the switch explicitly instead of
inferring it from the tier, and stop accepting the retired mode name on create
or restore.

* fix(liveshare): preflight a resumed child's limit before publishing it

A resumed child is announced through agent/created before its execution limit
installs, so an installability failure left a created Session whose guard never
landed. Resolve the recorded child's granted workspace before activation and
refuse the resume there, so a child this host cannot confine is never published.

* refactor(liveshare): render the viewer page through the reader's own Peer

The served-page seam now hands the frontend reader the reader's Peer, which renders that page's own
opening access rows; the Liveshare side no longer assembles a global access snapshot and the removed
global is gone. The viewer read service builds the same Peer it already creates for every dispatch and
passes it to the asset reader.

* fix(liveshare): keep the owner upload switch across an older Relay's access frame

The Relay is the authority for what a viewer receives, but only for the fields its frame states. An
older Relay predates the upload switch and omits it entirely; the protocol parser rightly normalizes a
missing switch to off for frames that state it, so the host must inspect the raw answer to tell not

* test(liveshare): adapt the host specs to the current tool and session APIs

The host specs drove a single tool through the pre-batch executor shape and handed
some tests a Session outside its store; the current tool executor takes a call batch
and Session appends require the owning SessionStore. This adapts those fixtures to
the current shapes without changing what each test asserts.

Also gives the reader's own fork the same Session permissions as the source grant:
forking its own child again and renaming it are allowed on the child only, while the
source Session still refuses renaming and keeps forking as its one Session verb.

* fix(liveshare): let the owner's background turns run while a share is served

The requester fold only counts a message with `source.kind === 'user'`, so a
turn a background event opened has no attributable input and stayed `unknown`;
the source-session tool guard refused every tool in that turn. Establish the
owner's own turn for an input-less turn and leave unattributable input unknown.

* fix(liveshare): grant the reader its own fork's rename and gate child forks by the fork switch

* refactor(liveshare): read the served page through webServer.readResource

* feat(boot): offer the Liveshare host bundle from the official bundle list

`@deepseek-ai/dsh-official-bundles` owns the shipped template, retired, and
optional bundle lists now, so the liveshare bundle joins `OPTIONAL_BUNDLES`
there instead of being restated in the app-boot profile module.

* feat(liveshare): gate the reader's own fork independently of its input permissions

The reader's child gets prompt, cancel, and rename from a role with no extra
condition, while forking the shared Session or one of the reader's own forks
belongs to a separate role bound to the owner's fork switch. A viewer cannot
rename the shared Session, and a withdrawn fork switch also withdraws forking
a child.

* test(liveshare): adapt the admission specs to the current tool and driver APIs

* feat(liveshare): accept only the declarations a plugin's own package owns

A viewer plugin may contribute endpoints a share can offer, so a declaration
has to name a live Host Remote endpoint whose generated id belongs to the
declaring package. Fail closed: no loader entry, no local endpoint, no owning
package, or a foreign owner all refuse the declaration, and the recorded
attribution is the package name the owner sees.

The package's Typert artifact registers after its apply runs, so a plugin
declares once that registration has happened.

* feat(liveshare): classify every Remote endpoint before the reader policy decides it

The share-viewer policy refuses by default, so the endpoints it decides were a
handwritten whitelist that could drift from the generated endpoint set. State
every endpoint's reason in one shared table: the Client face proves it covers
`RemoteEndpoint` (both directions), the Host derives `POLICY_DECIDED_ACTIONS`
from the reader entries, and a load-time assertion fails if the derived set and
the roles' actions disagree.

* fix(liveshare): declare the Client face its classification gate compiles in

Typert attributes a package's exports to a face only for manifests declaring
`dsh.client` plus a `./client` subpath (`isDualFacePackage`). Without that row
the client catalog read `exports["."]` — the Host entry — from the Client
program, which holds only the classification table and its completeness gate,
and failed on the source that program does not contain.

Export the gate module as `./client`, declare the Client row, publish its
bundle, and exempt the package from Client/Host dependency treatment: the
runtime importing these dependencies is the Host's, and `dsh.client` here only
attributes the compilation face.

* test(liveshare): align worker fixtures with the protocol tenant contract

The protocol layer rejects a share RPC actor whose tenant is empty at the IPC ingress. The worker fixtures still described a peer normalizing the tenant to an empty string, so the Host dropped every fabricated request event and the lifecycle and correlation polls timed out. Use a concrete tenant in the fixtures; those scenarios do not depend on an empty tenant.

* chore(docs): count the liveshare persistence nodes in the format index

The regenerated current-checkout catalog adds the liveshare viewer-principal nodes, so the v4 row's node count moves from 622 to 626. Refresh the generated format index rather than keeping the pre-liveshare count.

* refactor(liveshare): consume the protocol package's single public entry

The protocol layer now publishes only its root entry, so the Host face reads the wire codecs, identity contract, and viewer-state vocabulary from the package root instead of the removed /protocol and /identity subpaths.

* fix(liveshare): classify the schedule cleanup endpoint before the Client gate reads it

The Client completeness gate compiles against the generated Remote endpoint map; master added schedule/deleteInactive after the table was last filled. It mutates the owner's schedules like its neighbours, so it stays owner-only.

* chore(release): carry the liveshare package to the rc.3 family version

* docs(liveshare): align the host docs with the shipped two-tier shares

The subsystem page still described a withdrawn fork-readonly compatibility, a
write-only source drive, and an immutable execution.turn. State the two tiers,
the independent fork and upload switches, the refused unknown mode, and the
turn derived from the Session boundary. Mirror the Chinese pages and re-record
the pairing hashes.

* fix(liveshare): describe the writable tier in the plugin card

The package description, both locale descriptions, and the three Web goldens
still told the owner that sharing is read-only. State driving, bounded writes,
forking, and the independent upload switch instead.

* chore(liveshare): forward the owner credential file from the bundle patch

The README lists LIVESHARE_OWNER_CREDENTIAL_FILE beside the other owner
settings, but the patch left it to a direct process.env read. Pass it through
the same config field as its siblings.

* docs(liveshare): drop stale fork module comments

The registry header still said read-only forks only, and both the registry
and the policy carried an orphan JSDoc block in front of the real one.

* refactor(liveshare): drop unreachable fork tool effect fields

ForkToolRequest.writesWorkspace and changesConfiguration had no producer in
the repository: the guard never sets them, and the only callers were the
tests. The write-denying branch also contradicted WRITE_FORK_CEILING, which
admits the bounded write tool. Remove the fields, their refusal codes, and
the test cases that manufactured them.

* refactor(liveshare-protocol): retire the attachment-upload capability

Sharing itself now carries attachment upload, so the protocol no longer
states an independent switch: `LIVESHARE_CAPABILITIES` drops
`file-upload`, and share specs and access-state frames drop
`allowsFileUpload`. Wire frames stay v1 and no Relay is deployed, so no
compatibility layer is needed.

* refactor(liveshare): grant attachment upload with visibility

The owner controls sharing, audience, the tier, and forking only.
`fileUploads/upload` now rides the Session read role, so every Session a
share serves the viewer accepts files, independent of the tier and the
fork switch. The owner record, the share APIs, the registration handshake,
and access-state adoption no longer carry an upload switch; an older owner
state file that still contains the field loads with it ignored.

Accepted risk, recorded in Known Limitations: an owner cannot refuse
uploads for an admitted viewer.

* feat(liveshare): share the owner wire records

Move the owner identity report into a module both compiler faces can import, and
declare the share-management request, result, audit, and capability records there
with the same fields the same-origin routes already answer. The Remote failure
vocabulary joins the same module so a client learns the liveshareShares/ codes
without importing the Host.

* refactor(liveshare): extract the owner share operations

Move the twelve share-management handler bodies out of the route closures into
operations implemented once inside apply(), leaving each HTTP route with its JSON
parsing, normalization, and legacy status/body. Each operation documents the
side-effect order it keeps and what reordering would break.

* feat(liveshare): serve share management over Typert Remote

Register a LiveshareShares Remote service over the same operations the routes
call, publish its ./typert and ./remote artifacts plus the shared ./types module,
and make the shared records part of the Client compiler face. The owner page gains
the Remote endpoints while the HTTP routes stay as thin adapters.

* chore(docs): regenerate the Cordis catalog for share management

Record the liveshareShares service on the LiveShare subsystem page, link its
wire records to that page, and regenerate the runtime API catalog. The service
declaration moves to the package entry so the host-face catalog projection can
render it.

* test(liveshare): refuse share management to viewer Peers over Remote

Cover the owner Peer's Remote list and the share-viewer denial of list, create,
stop, and setRelay. The refused calls leave the share list empty, the selected
Relay unchanged, and the worker channel without an add-share.

* chore(liveshare): mark the typert resource follow-ups behind #7003

The share-management Remote ships against today's resource registration. Default
resource resolvers and the stricter identity rules arrive with #7003, so the two
places that will change carry a TODO naming the PR.

* fix(docs): classify the liveshareShares service role

The share-management Remote adds one owner-side service, so the capability
seams graph owed it a role row; without the classification the generator
refuses to render and doc-sync fails.

* fix(liveshare): complete the bundle metadata and restore the static gates

The bundle shipped only two of the eight shipped-language metadata files, and its
browser face had no named tsdown config, so the locale metadata gate and the
browser bundle walk both failed. Ship the full locale set and name the client
build; share one owner-body registrar between the two POST adapters so jscpd
stops reporting an eight-line clone.

* fix(liveshare): give the bundle a client source route and a typed peer double

The product-use walk follows `${name}/client` for every mounted bundle, so the
package needs the source mapping and a Client entry the mapping resolves to.
Replace the unknown-cast peer double with a complete PeerScope literal.

* test(liveshare): adapt the viewer admission rows to the Session default resolver

#7003 replaced the `session/page` and `session/follow` resource readers with the
Session kind's default resolver. A body carrying both `sessionId` and `address`
is now refused as invalid resources before the share policy reads either, while
a lone hidden `sessionId` still resolves and is refused by the policy.

Records the new expectations; the served page names one identity per request.

* feat(liveshare): resolve create's request Session through the default resolver

#7003 registers the `session` kind's default resolver with the Session
Controller, and `liveshareShares/create` names its Session in the one request
object, so the Gateway resolves that Session for a non-owner Peer without a
reader declared on the method. The owner Peer reaches the operation without
an access decision.

Adds the Gateway resource assertion and the owner-path assertion to the
share-management security spec, regenerating the Cordis catalog.

* test(liveshare): refuse a viewer body naming two Session identities

A prompt or cancel body carrying both `sessionId` and `address` reaches the
Gateway, whose Session resolver refuses it as invalid resources, so the target
child operation never runs and no request content comes back. Prompt and
cancel requests name their Session by `sessionId` alone.

* test(liveshare): widen the two-identity coverage across the reader endpoints

The resolver refuses a request that names two Session identities for every
namespace, so cover the remaining reader endpoints with one parametrized case
instead of leaving them to the generic Gateway tests.

* refactor(liveshare): remove the unreferenced gateway heartbeat policy

The production worker keeps its own interval and missed-pong counter, so
nothing imported this module; only its spec did.

* refactor(liveshare): extract the owner state persistence

The state file reader, writer, serialized write queue, and stored-share
record types move out of the host plugin unchanged.

* refactor(liveshare): extract the Relay authority reader

The access-state cache, its generation fence, the admission rule, and the
authoritative read that adopts a frame move behind one factory.

* refactor(liveshare): extract the tunnel worker supervision

Worker start, settlement, registration replay, expiry timers, owed Relay
rows, and the credential state each registration presents move out of the
host plugin behind one factory.

* refactor(liveshare): extract the owner share operations

The twelve operations keep their order and side effects but move behind an
explicit deps factory, so the host plugin mounts them instead of owning
their bodies.

* refactor(liveshare): extract the owner routes and source access

The sixteen owner HTTP routes, their request adapters, and the credential
store move behind registerOwnerHttpRoutes. The confined source Session
layer and its turn control move to source-access, which the share-host
re-exports for its public helpers.

* refactor(liveshare): split the viewer reader into dispatch, fork, and lifecycle

The request entry and endpoint handlers, fork orchestration and branch
facts, and the streams/calls/Peers lifecycle move out of viewer-read,
which keeps the deps types and assembly. Order, TOCTOU re-checks, and
Peer ownership are unchanged.

* refactor(liveshare): share the wire JSON helpers and the fork enforcement record

The split duplicated `json`/`isRecord` across the extracted owner modules and the
child enforcement options at three call sites; the stored ACL selection type also
lost its reference to the wire type. One helper module and one record factory keep
the split modules from carrying their own copies.

* refactor(liveshare): move the shared owner record and one-definition helpers

The split published OwnerShare from the assembly while every extracted module
imported it back, and left each host module carrying its own isRecord. Move the
share record and its ACL summary to a neutral module, name the two optional
service seams instead of inline casts, and import one wire JSON helper.

* refactor(liveshare): remove the fork-enforcement audit event

Nothing consumes `liveshare/fork-enforcement` except the spec that watched
it, so the minimal-surface rule drops the declaration, the reporter, and
every report call instead of reserving an extension point for later.

The wiring spec now asserts the outcomes an observer could previously
infer stages from: an install that returns nothing leaves
`ForkRegistry.enforcementInstalled` false and the child's prompt gate
refused with `fork-ceiling-uninstalled`. The subsystem prose no longer
names the removed refusal code, and the Cordis/doc catalogs are
regenerated.

* fix(liveshare): surface fork enforcement failures

Three registration paths swallowed the error behind a fail-closed
refusal. Keep refusing, but carry the reason:

- `preflightForkChild` now throws `fork <id> preflight failed: <message>`
  when the stored child cannot be read, instead of returning and letting
  activation fail later; `share-host`'s `prepareChild` caller already
  propagates that through the viewer's `fork-prepare-failed` answer.
- A child's confined `read`/`write` registration keeps its error in a
  `ForkEnforcementFailure`, so `prepareChild` throws
  `fork execution limits are unavailable: <message>` with the error as
  the cause instead of the bare message.
- Source-session access keeps the refused registration on the
  installation and logs it beside the existing confinement refusal.

The wiring spec asserts the preflight failure and both captured errors.

* refactor(liveshare): move the client placeholder face out of this layer

The `./client` entry existed only so the package's Client face could
compile the viewer endpoint classification against the generated Remote
map; it published no runtime this product used. P3b owns the real Client
face, so P3a drops the placeholder instead of shipping a surface ahead of
its consumer:

- delete `src/client/*` and the package's Client tsconfig; the endpoint
  spec keeps the Host-table diagnostics, and the Host still derives the
  policy action set from that table
- drop `dsh.client`, `exports["./client"]`, the `lib/client.js` publish
  entry, the browser tsdown build, and the package path alias
- remove the dependency-policy exemption and the devDependencies only
  those client sources imported
- the Host tsdown face still skips the Client build pass; `./typert` and
  `./remote` stay Host-generated and the Client tsconfig never fed them
- README and subsystem prose no longer claim a browser face

* chore(ci): drop the stacked-PR e2e dispatch input

The narrow dispatch scope existed only to verify a stack's exact SHA while its
ordinary pull-request checks were unavailable; it is not part of LiveShare and
never belonged in this layer's diff.

* feat(liveshare): add the share dialogs, viewer seats, and client surfaces

The browser half of the bundle: the owner's share dialog and management surfaces, the owner sign-in flow, the viewer page's session plugin, brand, status, and seat components, and the read-only permission and model seats that replace the shipped composer controls at a shadow rank. The package README regains the client implementation links this layer provides.

* test(liveshare): cover the client surfaces and viewer seats

jsdom coverage for the share dialogs, owner login and identity, the viewer page composition, the read-only seats and their shadow rank, the served-page artifacts, and the client-face registration. The host-side suite stays in the lower layer.

* chore(build): ship the Liveshare client bundle

The client compiler face and solution reference, the tsdown client entry beside the host phase, the manifest's client export, client runtime files, locale assets, and client dependencies; the dependency-policy classifications whose consumers appear with the client face; the generated client slot catalog; and the plugin-manager expectations that list the bundle's card.

* fix(liveshare): shadow the shipped brand seats instead of colliding with them

sidebar.brand.mark and sidebar.brand.name are single slots, and a served viewer page mounts the owner's plugins, so on an official build ui-brand-official already registers both at the default rank. Register the viewer entries at a shadow rank and pin the collision in a registration spec, as the composer seats already do.

* fix(liveshare): clear the viewer's composer blocks when its plugin is disposed

The honest-composer effect raised a per-Session block but its disposer only unsubscribed, so a disposed or replaced viewer page left the owner's composer blocked. Track the Sessions it addressed and clear their blocks on disposal; a spec pins the recovery.

* style(liveshare): drop unnecessary type assertions in the registration specs

The slot test runtime's renderSlot already accepts these owner props, so the contracts-ready lint reports the as-never assertions as unnecessary.

* test(web): mark the client layer's LiveShare card experimental

The client layer ships the card that renders the bundle, and the recorded goldens already carry its Experimental badge. Add the bundle to the manager's always-held extension list and to the presentation's experimental set, and expect badge 1 in the plugin-manager spec.

* fix(liveshare-client): return to the share after viewer sign-in

The viewer page names its share in its own query (?shareId=...), so a
sign-in that returned to the bare path brought the reader back to a page
that no longer named their share. Build the Relay sign-in entry from the
page's path and query, and keep the entry's construction in one testable
function.

* fix(liveshare-client): state a half-saved share permission accurately

Saving the share permission writes the session tier first and the plugin
endpoint selection second. When the second write was refused the dialog
claimed the whole save had failed, though the tier and fork switch were
already stored. State which half landed, and re-read the host's record so
the fields show what it kept while the refused endpoint pick stays for the
retry.

* fix(liveshare-client): route viewer copy through the client locale seat

The viewer surfaces translated their own copy from navigator.language, so
a language chosen in Settings could not reach them and a Node boot read the
machine's language instead of the page's. Register the package dictionaries
once, as the client face already does, and take the bound translate seat:
React seats get it from their locale registration, and the plain-DOM status
banner takes it from the plugin and repaints on locale changes.

Also move the viewer status banner's copy and reason helpers to that seat,
and update the viewer specs to provide a LocaleRuntime the way the assembled
viewer page does.

* test(liveshare): assert the two-tier fork ceiling in the viewer artifact

The branched row states the ceiling the host installed: a view-tier
(fork-readonly reads as view) share's child runs under the read-only fork
ceiling, while a write-tier share's child carries the bounded write
ceiling, whose host half readonly-fork-cold-child.spec.ts covers. Read
that statement through the shipped Tooltip's pointer events: after the
rebase it listens to pointerenter/pointerleave, so the previous mouse-event
hover never raised the bubble and the row's statement stayed unread.

* feat(plugins): hide LiveShare until its plugin rows run

A default installation ships the LiveShare package but composes none of
its plugins, so the plugin manager must list it nowhere; enabling it
through the profile's cordis.patch.yml or its bundle selection starts
those rows and only then shows the card under Experimental plugins.

The visibility rule reads the bundle rows' own run state rather than the
profile's bundle selection, because a row inserted through cordis.patch.yml
runs while the bundle stays unselected. The hidden bundle ID stays
addressable, so openBundle still opens its details.

* test(web): assert the hidden LiveShare card across both lists

The plugin-manager scenario now checks that the default profile lists no
LiveShare card in the Experimental plugins group or on the More page, and
that the offered count is one lower than the shipped optional bundles. A
dedicated scaffold writes the bundle's declarations row into the profile
patch, waits for the loader to recompose it with bounded timeouts, and
checks the card joins the list and leaves it again when the row is removed.

* docs(plugins): document LiveShare's default hidden state and enablement

The package README now states that a default installation shows LiveShare
in neither plugin list, and gives both enable routes: inserting the
bundle's two entries in the profile's cordis.patch.yml (placeholders for
the deployment's Relay identity values, with the recomposition caveat), or
selecting the bundle in dsh.profile.bundles. It also states that removing
or disabling the rows takes the card away again. The subsystem page keeps
the rule and links to the README for the exact entries, and the plugin
manager README describes the hidden-until-running list behavior.

This also repairs the README's pre-existing Model Experience mismatch with
its audited indirect classification, which failed the documentation gate
before this change.

* docs(agent-notes): record LiveShare's hidden-until-running rule

Record why the plugin manager keys visibility on the bundle rows' run state
rather than the profile's bundle selection (the cordis.patch.yml route
leaves the bundle unselected while its plugins run), and why the detail
entry by bundle ID survives the hidden card. The rejected alternatives and
the costs of the decision are stated for review.

* test(web): expect the hidden LiveShare card in the configuration scenario

The configuration-page scenario reads the same More list as the plugin
manager, so its offered count is one lower than the installed optional
bundles while LiveShare's declared rows do not run.

* test(web): re-record the plugin goldens without the default LiveShare card

Refresh goldens remove the LiveShare entry from the Experimental plugins
list of both plugin-manager snapshots and from the plugin-configuration
snapshot, and drop the refresh card count from 12 to 11. Re-analysis under
replay confirms both scenarios green.

* fix(liveshare-client): release the viewer page state with its plugin

The viewer context facts, capability, and status stores are module-level so
every viewer surface reads one page's answer, but nothing released them when
the plugin owning the page was disposed or HMR-replaced, so the next mount
inherited the previous page's share facts, tier, and status. The viewer
session plugin now owns that lifetime: it clears the stores when it mounts
and disposes, and advances the read generation so a previous page's in-flight
answer cannot publish into the new page. A spec pins the disposed page and
the inherited state.

* feat(liveshare-client): add the viewer attachment-upload switch

The share carries whether viewers may upload attachments as its own switch,
off by default and independent of the session tier and the fork switch: a
view-tier share may allow uploads and a writable one may refuse them. The
share dialog sets it in both the create and edit flows and submits it with
the permission write; the manager sets it per share beside the fork switch.
A share the Host does not report reads as off. The dialog also states the
tier as the sandbox it runs in, so neither tier is described as carrying
file uploads.

* docs(liveshare): describe the sandbox tiers and the upload switch

The host layer owns the package README statement of the two tier sandboxes
and the upload switch; this layer's documentation adds the subsystem
contract for the switch and the client README's attachment-intake sentence —
file intake follows fileUploads/upload, which a share grants only while its
own switch is on.

* refactor(liveshare-client): drive the owner dialogs through the modal layer and shared primitives

Both owner dialogs ran their own focus trap beside Modal's layer, so initial focus, Tab wrapping, Escape
and focus restoration were each installed twice and could disagree. The hand-written trap is removed;
the shared layer owns the keyboard lifetime, including the dialog's own restore target. The hand-written
returns focus to the opener exactly as before.

The same surfaces also stop inventing their own controls: the session permission and the share lifetime
are SegmentedControl choices like the capability tier, and the independent fork and upload switches are
the shared Switch primitive. Stopping a published share now asks for the shared RiskConfirmation
acknowledgement before the destructive request runs.

* feat(liveshare-client): state the narrowed owner copy and correct the viewer surface

The share manager no longer promises a request-and-approval flow that the protocol dropped: its
description states the online state and access scope it actually shows. Stopping a share is now gated
on acknowledging that the published link stops working immediately.

The viewer status banner keeps its placement inline but takes its colors from the theme tokens with
system-color fallbacks, so the served page stays readable when the client stylesheet never loads. Both
owner dialogs size against the Modal padding box instead of a second viewport calculation, and a long
list title wraps instead of pushing the status out of its row.

A forked branch is stated as a branch, without claiming a read-only workspace the write tier does not
have; the same correction applies to a branch whose sandbox limits failed to install.

* fix(liveshare-client): align the viewer face with the current Client services

The rebased Client surface no longer carries uiConversation.noticeBranchRefusal: a refused branch leaves the view unchanged, so the viewer row consumes the rejection and the refusal-notice tests become containment tests. The plugin manager's bundle lists moved to dsh-official-bundles, so the liveshare bundle joins the centralized experimental and extension lists. Master's sidebar keeps the brand identity out of the accessibility tree when the page cannot start a Session, so the assembled-page test reads the viewer home link from the DOM. The roster gains the file-drag plugin ui-chat and ui-attachment now inject, and the fixture reads the protocol's Client-face carrier from its own project and brands the fabricated reasoning effort.

* refactor(liveshare-client): consume the protocol package's single public entry

The protocol layer now publishes only its root entry; the client tests read the wire and identity contracts from that root. The dependency policy keeps one liveshare entry carrying both the protocol vocabulary and the identity helpers.

* feat(plugin-manager): leave the LiveShare bundle unranked in the extension list

The bundle already stays invisible until one of its patch rows runs, which
keeps ordinary users from encountering it. Removing its ranking entry means an
internal profile that enables it finds the card at the end of the experimental
list instead of pinned to the front; the Experimental badge and the hidden-
until-running card both stay.

* refactor(liveshare-client): drop the attachment-upload switch

The share dialog and share management present only the tier and the fork
switch; attachment upload follows visibility on the Host, so there is
nothing left to toggle. Removes the field from the client share record,
both locale seats, and the dialog tests, and states the new rule in the
viewer README.

* feat(liveshare-client): drive share management through the liveshareShares Remote

The owner dialogs stop posting the twelve management routes and call the
generated namespace instead. The share list, creation, stopping, permission and
endpoint writes, the Relay read and override, the owner identity read, the
audit, and both Relay checks resolve to a RemoteResult, so a refusal is read
from its `liveshareShares/` code instead of an HTTP status. The sign-in round
trip keeps its three same-origin routes.

The package's client entry mounts its own generated contribution and passes the
namespace to the share surfaces through the slot inject face. `dsh.client.inject`
now names `@deepseek-ai/dsh-api-remotes`, and the viewer endpoint table
classifies the twelve management endpoints as owner-only so the Client
completeness gate stays total.

Client specs substitute the namespace with a scripted double instead of stubbing
`fetch`; the assertions read the recorded requests.

* test(liveshare): drive the owner host cases through the Remote service

The host cases no longer register a route map and post JSON to it. They call
`ctx.liveshareShares` directly, which runs the same OwnerShareOperations the
remote Gateway dispatches, so each case keeps its side-effect coverage and
asserts the RemoteError code and details a refusal carries instead of a status
and body.

The ping case is gone with the route: the header action reads `relay()` for the
effective address, which the Relay-state cases already cover. The e2e harness
publishes through `liveshareShares/create` over the owner's own `/api` carrier,
and the sign-in/credential cases keep their same-origin routes.

* refactor(liveshare): remove the owner share-management HTTP routes

The twelve same-origin management routes are gone, together with the thin
adapter layer that existed only for them: the status/body error translation,
the per-route request-body readers, and the capability-selection parser. Every
side effect stays in OwnerShareOperations, which the `liveshareShares` Remote
dispatches.

The four remaining routes are the sign-in and credential ones:
ownerLoginStart, ownerLoginRedeem, ownerLoginCancel, and ownerCredential. The
route-registration case now asserts exactly those.

* docs(liveshare): describe share management as the liveshareShares Remote

The package README (both languages) and the subsystem page now state that the
share list, creation, stopping, permission and endpoint writes, Relay state,
identity, audit, and both Relay checks are `liveshareShares` methods, and that
the sign-in round trip and credential storage are the only same-origin HTTP
routes left. The owner source table points at share-operations.ts and
share-remote.ts for management.

Re-record the README pairing hashes and regenerate the Cordis surface sections,
including the inspect catalog's service description.

* test(liveshare): restore the Client classification gate

P3a dropped the Client-face completeness gate together with its placeholder
face. P3b owns the browser runtime, so the gate returns: the Client program
proves every generated Remote endpoint has a table row, and the spec pins the
compiled export beside the diagnostics the Host also uses.

* chore(build): restore the Liveshare Client compiler face

The rebase onto the Host layer lost the solution reference and the Host
exclude that kept `src/client` out of the Host program. The package now
carries a real browser runtime, so it takes the ordinary Client/Host
dependency treatment instead of the exemption its placeholder face needed.

* docs(liveshare): restore the package's client faces

The subsystem overview names the client faces again now that the browser face
is back, and the translation pairing record is re-recorded.

* test(liveshare): cover the browser surfaces and client readers

The browser half of the package now meets the per-file gate on its own
terms: the viewer request decoder, the share-type readers, the viewer
session selection, and the owner identity readers are pinned by direct
specs, and the seats, brand row, share-path chip, status banner, model
seat, and menu row gain the cases their uncovered branches named.

Two internal simplifications remove branches that no input can reach: the
brand row strips build metadata with one replace instead of a split whose
second operand was unreachable, and the viewer-session subscription no
longer starts behind a placeholder disposer that subscribe can never call.

* test(liveshare): cover the owner host and gateway glue

The host half gains the direct specs its uncovered branches named: the
stored credential file (absent, corrupt, unbound, empty facts, deletion),
the viewer principal comparison, the share Remote's audit delegation, the
registration handshake's plain-socket scheme, the relay refusal status
reader, the JSON body reader, the fork policy's mode helpers and empty
prefix, and the fork guard's unreachable-context refusal. The worker entry
is covered through a mocked worker module, so its boot decision runs
without a spawned process.

The served-page resource answer no longer carries a `string` body: both
call sites hand it bytes, and the string branch could not run.

* test(liveshare): cover owner state, authorization, and the header action

Pins the owner state file's restore and refusal rules against real files
(every optional fact, every invalid record, the owed Relay rows, and a
failed write), the authorization transaction's four bindings and its
single-use state, the declaration surface's stable package order and
repeated withdrawal, and the Session-header action that opens the share
dialog and switches it to management.

The owner state write chain no longer catches a rejection it cannot
observe: the map holds only promises whose failures were already absorbed.

* test(liveshare): close the owner and declaration surface gaps

Pins the authorization URL the browser opens and the production clock and
lifetime a transaction gets when the host passes neither, the declaration
surface's stable package order and repeated withdrawal, the owner state
file's restore and refusal rules against real files, and the Session-header
action's open, manage, and close paths.

The declaration disposer's two optional lookups are annotated instead of
tested: this effect registers each accepted name once, so its own disposer
cannot reach a missing entry, and the effect disposers are idempotent, so a
second withdrawal never re-enters that loop.

* test(liveshare): satisfy the lint and compiler gates in the new specs

The refusal double takes the empty details its code declares, the waits
return nothing instead of a void expression, and the IPC entry reads the
channel through a computed access so the lint rule does not read it as an
unbound method reference.

* test(liveshare): cover the gateway worker and tunnel supervision

* test(liveshare): cover the owner, relay, fork, and viewer host modules

* refactor(liveshare): keep the coverage hooks behavior-preserving

* test(liveshare): fix the typed listener and endpoint fixtures

* test(liveshare): cover the owner login, share manager, and viewer plugin

* test(liveshare): cover the share dialog and host fork wiring

* test(liveshare): satisfy the contract lint in the gateway worker spec

* chore(coverage): drop the liveshare lane exemption

* feat(liveshare): ship the remaining shipped-language dictionaries

The locale parity gate requires every shipped language beside each object-form
dictionary, so the Liveshare browser plugin now carries Japanese, Spanish,
French, German, Russian, and Brazilian Portuguese alongside `zh` and `en`, and
registers all eight.

Each dictionary is typed `Record<keyof typeof zh, string>` so a missing or extra
key fails the build, and named placeholders, slash commands, and API field names
stay verbatim in every language.

* docs(liveshare): document the host config and two exported host contracts

`verify-export-jsdoc` requires prose on every exported declaration: `Config`
states what a deployment sets and why `relayUrl` has no default,
`requestedBoundary` states how a fork request names its prefix and what an
unnamed one becomes, and `OpenStream` states what the reader tracks and when it
releases the stream.

* chore(docs): regenerate the liveshare catalogs and acknowledgement after rebasing

Rebasing the host layer onto current master left the generated artifacts at the
new base's state, because each conflict was resolved to master's side rather
than merged by hand. Recompute them from the rebased tree with the repository
generators: `gen-config-catalog`, `gen-doc-graphs`, `gen-persistence-catalog`,
`gen-client-catalog`, `gen-tool-catalog`, and `verify-persistence-formats
--write`. `gen-module-graph`, `gen-cordis-catalog`, and
`gen-official-bundle-catalog` computed no change.

The LiveShare persistence acknowledgement predated master's later changes to the
four user-message source roots and stored the inventory in a non-canonical
layout, so `verify-persistence-changes` could not read it. Re-record it with
`persistence-changes --update ... --decision same-version`, which chains it onto
master's current tips and rewrites the snapshot, the bilingual declaration, and
their pairing record. Re-record the ui-plugin-manager README pair, whose
LiveShare paragraph now matches on both sides.

* test(liveshare): read attachments by the complete image reference

Current master sends `session/attachment` the complete image reference the
conversation folded from the log and answers with the stored reference plus
bytes, instead of accepting a bare `attachmentId`. The viewer fixture still
read the old field, so every assembled-page image read failed and the
viewer-shell and viewer-global-privacy artifact tests timed out waiting for the
delivered image. Follow the new request field and keep verifying by id against
the Session's log.

* test(liveshare): pass the complete reference in the owner attachment probe

`session/attachment` now takes the complete image reference, so the admission
spec's owner-side probe sends the fixture's reference instead of a bare id.

* fix(liveshare): classify the current Client endpoints for the share-viewer policy

Master added session read/observation endpoints to the Client's Remote map, and
the share-viewer policy's reader role is the only grant they can receive. Name
the six read endpoints in the reader role and in the classification table, keep
the endpoints this Client assembly cannot name out of the table (they stay
owner-only by default), and extend the served-page contract plus an admission
test so a hidden Session is still refused.

* fix(liveshare): await and type the turn-requester projection

Register the fold before publishing the policy and source layer that read
it, surface a refused registration instead of leaving it unobserved,
reset the retained handle when the projection service reloads, and type
the boundary and execution reads from Agent and TurnBoundaryProjection.

* fix(liveshare): withdraw only the fork children a revocation saw

Apply a withdrawal to the entries the registry file holds when it
commits, leave a child recorded afterwards live across restarts, and
retry a failed withdrawal without replaying it over a newer child.

* fix(liveshare): refuse a fork that names no Session

Answer 400 with a named code before dispatch, so a malformed fork body
cannot reach the owner's Gateway unrecorded.

* fix(liveshare): stop injecting a child id into fork requests

SessionForkRequest mints the child id, so the injected field was ignored.
Forward the caller's envelope and rely on the recorded operation for a
retry; document the crash window that needs a caller-supplied child id.

* fix(liveshare): classify and dispatch one normalized path

Use the same pathname plus query for the endpoint decision and the owner
dispatch, and parse with URL.canParse instead of an empty catch.

* docs(liveshare): record the two-tier driving model

Rewrite the implemented note's stale tier, turn-carrier, upload, and
retry facts, add the superseding decision note, and keep the subsystem
page and package READMEs aligned.

* fix(liveshare): publish only the exported type module from lib/types

The package published every tsc-emitted lib/types/**/*.js file, so the client
JavaScript that imports .module.css files entered the tarball without those
stylesheets. Retain only lib/types/share-api-types.js, the one runtime file
the ./types export names, alongside the declaration tree.

Teach the publication allowlist that this package's emitted tree is completed
by its exact runtime file rather than the whole glob, and pin both the accepted
and rejected payloads.

* test(liveshare): mock the generated remote for assembled viewer pages

The assembled viewer specs load the client entry, which statically imports the
generated ./remote contribution. A clean checkout has no lib/ build, and Vite
resolves a static import while transforming the entry, before vi.mock can
answer it, so the jsdom suites failed to import the page roster.

Mock the contribution in each assembly spec, matching the owner-side face
tests, and provide a resolvable test stand-in whenever the artifact is absent.

* fix(liveshare): guard hover styles and draw the viewer banner hairline

Wrap the owner action, dialog actions, and viewer wordmark hover rules in
@media (hover: hover) so touch devices do not latch the hover treatment, and
draw the viewer status banner's neutral border at the 0.5px hairline width.

* refactor(liveshare): name the owner gateway directory

The owner-side Relay connection process is LiveShare's own gateway, not the
Host API gateway; move it to src/owner-gateway so the published entry
lib/owner-gateway/cli.js and the module headers say which gateway they mean.
The protocol vocabulary (frames, tunnel mode) keeps the word gateway.

* docs(liveshare): merge the tier and visibility notes into the owner note

* docs(liveshare): call the Relay the Liveshare server

* docs(liveshare): name the Liveshare server in the share-management JSDoc

* docs(liveshare): document that new shares are organization links

The shipped Share dialog sends neither accessMode nor allowedSubjectIds, so
every share it creates is open. The agent note, both package READMEs, and
CreateShareRequest's JSDoc now say that selected/approval shares are legacy
stored records that keep their ACL and access summary, that the UI has no
creation entry for them, and that the create fields remain for those records
and direct Remote callers.

* refactor(liveshare): drop the configurable owner display name

The reader-facing owner name now always resolves from the Relay-verified
credential: its display name, otherwise the subject label, otherwise the
Relay's host name. Config.ownerDisplayName and the namedOwner dependency
only fed the removed override, so delete both and the test configuration
that set the field.

* docs(liveshare): read the owner name only from the verified credential

Remove the LIVESHARE_OWNER_NAME row and its cordis patch mapping, and say
in both READMEs that a reader-facing share name comes from the
Relay-verified credential rather than local configuration. The
LIVESHARE_OWNER_DISPLAY_NAME legacy identity label stays.

* feat(liveshare): save the relay address from the share dialog

The public address is the owner's runtime choice, not deployment configuration:
the host loads without one, the dialog saves it into the owner state, and new
shares publish there until the owner saves another or clears it. A host with no
saved address contacts no server and its sharing surfaces state that.

* docs(liveshare): describe the address saved in the share dialog

The README, subsystem page, generated API reference, and their bilingual
pairing now state the dialog-owned address and the no-address host state.

* refactor(liveshare): sandbox a shared session instead of attributing turns

Retire per-turn origin isolation: delete the turn-requester fold, the
turn-boundary derivation, and every per-turn verdict, and make the source
access layer a session-level sandbox.

While any share over a Session is active the layer's guard permits only the
confined read it mounted (plus the bounded write when some share may write);
every turn of that Session, the owner's own included, runs through them and
nothing else. The layer is released once no active share is left, which
restores the Session's own tools, and the read-only layer is re-installed
when the last write share narrows to view. Each operation re-reads the
shares' current Relay grants and checks the resolved target against the
granted root.

A viewer's session/cancel now travels the standard Gateway dispatch under
the source-write role instead of a host-side ownership comparison, and a
share that stops, expires, or loses admission stops whatever turn its
Session is running.

Behavior changes:
- The owner is confined too: while a share is active the owner's own turns
  can only use the confined read (and bounded write), and the full tool set
  returns when every share stops or expires.
- Cancellation has no attribution: a viewer's stop cancels the shared
  Session's running turn, including one the owner started.

* test(liveshare): exercise the session sandbox and gateway cancel dispatch

Delete the specs that asserted per-turn attribution (turn-requester, source
cancel ownership, source execution turn, source turn revocation) and the test
fixtures only they used. Cover the behavior that replaced them: every turn of
a shared Session is confined while the layer is mounted, the layer is released
when no share is left, a write-to-view narrowing re-installs the read-only
layer, a share ending stops the running turn, and a viewer's cancel and prompt
both travel the standard Gateway dispatch as the reader's Peer.

The pre-step admission test now covers the messages' own principals, and the
projection double is gone with the projection it stood in for.

* docs(liveshare): document the session sandbox and retiring turn origin isolation

Update the package READMEs, the LiveShare subsystem, the share-viewer
principal persistence record, and the merged Agent Note: remove the
turn-requester fold, turn-boundary derivation, pinned source cancel, and
per-turn origin prose, and state the shipped facts instead — a Session-level
sandbox that also confines the owner while a share is active, a standard
Gateway cancel with no ownership comparison, and the pre-step admission check
as the principal's only consumer. Re-record the bilingual pairing records.

* refactor(liveshare): extract share dialog relay and identity hooks

Move the Relay address read/save/draft state and its connectivity and
permission checks into useShareRelayAddress, and the owner identity read
plus the one sign-in attempt into useOwnerIdentityLogin. ShareDialog
keeps the step guide and passes the identity facts saving an address
needs, so neither hook reaches into the other.

* refactor(liveshare): extract the share dialog create form

Move the session permission, fork switch, lifetime, and the create
action into useShareCreateForm and ShareCreateForm. The dialog keeps the
link, error, and created-record adoption it already owned, and the
shared choice builders live in share-options so the edit panel can use
the same session-mode list.

* refactor(liveshare): extract the share edit panel and tier picker

Move the stored share's permission, fork switch, endpoint tier, and the
one save into useShareEdit and ShareEditPanel, with the tier expansion and
summary in ShareCapabilitiesPicker. ShareDialog now composes the relay,
identity, create, and edit hooks and keeps only the link, advanced help,
and close/manage surface.

* fix(liveshare): take share dialog hook options as named parameters

The exported hooks documented an `options` parameter while destructuring it
in their signatures, so their JSDoc could not name the binding.

* chore(docs): regenerate graphs, catalogs, and pairing after rebasing on master

The rebase merged this branch's liveshare entries with master's forge entries;
the generated English sources and their bilingual pairing records now reflect
both.

* feat(liveshare): open the shared read and write tool surface

The source-session sandbox and each fork child's limit now admit one shared surface: read, read_image, glob, grep, present, todo_write, skill, web_search, and web_fetch at the view tier, plus edit and write at the write tier.

Every path a call declares is resolved and checked against the granted root before the deployment tool receives its canonical form; a pathless search uses the Session's working directory. Tools with no local file effect keep the deployment's own definition, which the guard vouches for by identity. Everything else, including bash, pwsh, cua, the plugin manager, and the schedule tools, stays refused.

* test(liveshare): cover the shared tool surface

Drive the view and write surfaces through the source sandbox and a fork child's limit: canonical paths, symlink and relative escapes, pathless searches, deliveries, the no-effect tools, both mutation tools, and every capability the surface does not name. Registration counts in the host wiring now read the shared table instead of one confined read.

* docs(liveshare): describe the shared tool surface

The package README, the subsystem reference, and the owner note now state the admitted tools per tier, how each declared path is checked, and that the no-effect tools keep the deployment's definition. Re-record the bilingual pairing and the generated event graph for the new fs/edit-intent consumer.

* feat(liveshare): confine viewer turns and admit PTC run_code

Owner turns of a shared Session keep the deployment's native tool chain. The
source layer now decides per turn, from the admitted input's share-viewer
principals, whether the turn is confined; agent/pre-step records that answer
in Host memory only, and the guard plus every confined replacement read it.
A turn the owner starts runs the deployment's tools unchanged, an owner's
steering input inside a viewer's turn stays confined, and stopping every
share still disposes the layer.

Both tiers also admit the PTC transport run_code. A non-empty
sandbox_permissions is refused; every nested tools.* call is a separate
execution through the same guard and confined replacements, so tools.bash
stays refused and read/edit/glob/grep keep their grantedRoot checks. Before
the runtime starts a program, run_code dispatches the new sandbox/policy
waterfall with the Session-resolved policy; the source layer caps it to the
share tier (read-only for view, workspace-write at most for write, never
danger-full-access) and drops the deployment's extra writable roots. A fork
child's installation applies the same cap from its live tier.

Verification: pnpm run typecheck, oxlint on the changed files, and the
focused liveshare specs for the turn marker, owner/viewer turns, run_code
admission and nested calls, and the policy caps. Local commits only; not
pushed.

* refactor(liveshare): drop the uncommitted-prefix fork state

The host never reads the source Session log, so it cannot know a completed
sequence. The forking command validates the requested boundary against its own
committed history instead, which makes the local boundary refusal unreachable.

* feat(ui-chat): export the official user renderer and shared-session copy

* feat(liveshare): mark shared-session reader messages through the chat node seat

* test(liveshare): cover shared-session attribution and the reused chat node seat

* fix(liveshare): leave steering rows to Chat

Only the message that opens a conversation carries the reader attribution;
steering is a turn-internal insertion, so its official Chat cell is untouched.
The wrapper now shadows the user cell alone.

* feat(plugin-manager): hide default bundles through their manifest

The LiveShare bundle now declares `dsh.bundle.listedByDefault: false` and the
page applies one manifest rule to every bundle: a default-hidden bundle is
listed only when the profile enables it, the installation carries it selected,
or one of its declared rows runs. The client-side name set and its special
details-by-id fallback are gone.

* docs(sandbox): document the per-program sandbox policy extension

The new `sandbox/policy` waterfall owns one event scope, the generated event
graph and subsystem reference now list it, and the catalog classifies its
payload type.

* chore(docs): regenerate the graphs and pairing after rebasing on master

The rebased branch now shares master's forge and sandbox service entries; the
generated English graphs and their Chinese counterparts are re-recorded.

* test(web): keep the plugin lists contiguous around the LiveShare card

The recorded accessible tree has no blank line between the last experimental
card and the installed-plugins heading.

* test(typert): expect the PTC sandbox policy event in the executor catalog

The new waterfall is part of the executor's model events.

* fix(liveshare): name the PTC transport without the unclassified tools export

The dependency policy does not classify @deepseek-ai/dsh-tools' RUN_CODE_NAME
as a safe host export. Liveshare now owns the reserved name locally, pinned to
the canonical constant by its test suite.

* fix(liveshare): close the coverage gaps in the owner-turn and PTC paths

The asynchronous policy resolver is covered by the executor tests; the
share-host guard covers unattributable executions and same-turn narrowing;
the owner's edit path exercises the native pass-through.

* test(web): align plugin goldens with the manifest visibility rule

The default profile no longer lists LiveShare before its rows run, the refresh
count includes the forge bundle master added, and the creation recommendation
skill catalog records the new bundle entry.

* fix(ptc): keep the disposed check lint-clean after policy resolution

The narrowed property read became an always-false conditional; the helper
method keeps the runtime check without the narrowing.

* test(liveshare): cover a relay probe stopped by host disposal

The probe checks the disposed flag after its fetch; a disposal landing during the
request now exercises that early return.

* fix(liveshare): encode identifiers without the base64url Buffer name

Packed worker deployments run a Buffer polyfill that only names standard
base64; the new helper produces the same unpadded base64url text from it. The
packed preview's generic bundle toggles skip the declaration-first Liveshare
bundle, which the ordinary Web scaffold covers.

* test(liveshare): build worker command fixtures from real file URLs

`file:///pkg/...` has no drive letter, so Windows `fileURLToPath` refuses it;
the launch-command test now derives both fixtures with pathToFileURL.

* feat(ui-chat): offer a user-origin seat above user rows

Chat now declares a session-scoped `conversation.chat.user.origin` list seat and passes the flow's renderer to the official user row through `ChatNodeOwnerProps.renderUserOrigin`. The flow supplies the callback only while the seat has an entry, and the official renderer calls it for `user` nodes alone, so steering keeps its DOM. Entries receive the durable message source and own their markup and copy; ui-chat ships none, and its shared-session dictionary entry moves back to the feature that writes it.

* feat(liveshare): attribute shared-session messages through Chat's origin seat

The client registers one `conversation.chat.user.origin` list entry on both faces instead of shadowing Chat's user node renderer. The component recognizes the durable share-viewer principal and states the Relay-asserted subject from the package's own `inputOrigin.sharedSession` copy in all eight dictionaries; owner-authored, malformed, and steering rows render nothing. The ui-chat runtime external and its client-inject edge are gone: the remaining ui-chat import is type-only, and `slots.inject` makes the registration independent of declaration order.

* chore(client): record the user-origin seat in the slot catalog

Regenerate the model-facing client slot catalog for `conversation.chat.user.origin`: the new list seat, its owner props, its declaring flow entry, and the Liveshare occupant. The subsystem slot tree follows the same declaration.

* chore(liveshare): publish the types entry as declarations only

`./types` no longer declares a runtime target into the tsc-emitted tree, so the
package no longer publishes `lib/types/share-api-types.js` and the workspace
constraint check no longer needs a package-name exception.

* docs(ui-chat): name the message-source decoration

The prose uses the exact field it decorates instead of the abstract term the
repository's concrete-terms gate rejects.

* fix(liveshare): classify master's tool-results stream after rebasing

Master added the `toolResults/watch` Remote endpoint and bumped the workspace to
0.2.1-rc.4; the viewer endpoint table and the read role now cover the stream,
and the package version follows the root.

* refactor(ptc): keep sandbox policy dispatch synchronous

The policy cap is a pure, cheap computation, so the waterfall should not force
every confined program through an await before the call is registered. The
event now returns the policy directly: no admission window between reading the
mode and registering the call, no async mode resolver, and no fallback branch
in PtcCalls. The event can still be dispatched twice for one Agent-owned
program; its contract names the synchronous, idempotent listener requirement
and tests lock both dispatch counts.

* docs(ptc): state the synchronous sandbox-policy contract

The README now names the synchronous waterfall and the two policy resolutions
an Agent-owned program performs, so readers know listeners must be synchronous,
pure, idempotent, and return the same cap each time.

* fix(liveshare): let the declaration entry win the fallback race

A live bundle activation can start the share host before the separate
declaration entry's service is visible in the host context. The host now gives
that entry one task turn before providing its own fallback surface, so both
rows no longer register `liveshareViewerDeclarations` twice.

* chore(liveshare): drop the now-unused require-await suppression

The deferred fallback makes apply genuinely await, so the file no longer needs
the suppression it carried for a synchronous throw.

* fix(liveshare): read the owner Remote namespace after mounting it

The client face accessed `remote.liveshareShares` on its own context before
that namespace existed, which the Remote carrier refuses without an inject
entry. The header action now registers inside a child scope injected after the
carrier mounts, and the face tests provide the mounted namespace.

* feat(liveshare): simplify the owner share dialog

Saving the relay address now checks it in the same action, and the numbered
step guide is gone. Sign-in is a single control above the share settings, and
every setting below it stays disabled until the owner signs in. Fork wording
and the read-only permission hint now state the actual permissions.

* feat(liveshare): save the relay address on blur

The address field no longer needs a Save button: leaving the field (or pressing
Enter) stores it and probes the address it put in force. Chinese copy now uses
观看者 consistently instead of mixing viewer and 观看者.

* chore(ptc): restore the run signature formatting

The synchronous policy refactor left only a line-wrap difference in this file;
the signature returns to its original one-line form.

* feat(liveshare): configure plugin sharing when creating a share

The create form now carries the same share-wide plugin-endpoint tier as the
stored-share editor and writes it immediately after the host creates the
share. The Advanced disclosure only renders for an existing share, where it
has stored-share details to show.

* fix(liveshare): show the owner name on shared-message attribution

The durable source names the reader by subject id, so the line showed the opaque
id even when the reader was the owner. The client now remembers the verified
owner identity it reads on mount, and the attribution uses that display name for
the matching subject while keeping the persisted id for every other reader.

* feat(liveshare): use the viewer's Relay name in shared-message attribution

The viewer context already carries the reader's own display name; reading it
lets a viewer's messages show their name instead of the opaque subject id,
while a reader with no reported name keeps the durable id.

* feat(liveshare): persist the viewer display name in message attribution

The Relay asserts a display name for the viewer it forwards. Copying that name
into the durable share-viewer principal lets the owner and every reader show
who sent a message, instead of only the current reader or an opaque subject id.
The name is attribution data only and stays out of authorization and model
requests; old logs and readers without it keep showing the subject id.

* feat(liveshare): share the permission fields between create and edit

Creating and managing a share now render the same session permission, fork
switch, and plugin-endpoint fields, and the stored-share view states its
lifetime and link scope inline instead of behind an empty Advanced disclosure.
The first version publishes only the never-expiring lifetime.

* feat(liveshare): edit a share lifetime and link scope after creation

`updatePermissions` now accepts a new deadline (null means never) and the
open/selected link scope. The shared permission fields expose both while
creating and editing; an existing share can be switched to the open link or
cleared back to never, and selected shares accept provider subject ids.
LiveShare prose now calls every fork product a 分叉会话.

* test(liveshare): remove duplicate lint suppression

* fix(liveshare): keep row labels on one line

* fix(liveshare): clear the static catalog and unknown-cast gates

The owner-display test no longer casts through unknown, and the generated
Cordis runtime catalog now includes the extended permission request type.

* test(liveshare): cover the unchanged update and subject-order branches

* test(liveshare): cover the unchanged restored-access branch

* fix(liveshare): re-probe a stale owner credential

* refactor(liveshare): remove the unfinished selected-colleague scope

The share dialog no longer offers or edits a selected-colleague link scope;
new shares use the organization link and stored legacy scopes stay read-only.
The create form's lifetime is the single never-expiring row, and the host
identity read re-probes a stale credential.

* feat(liveshare): simplify the share manager and add plugin sharing

The global manager now lists Session titles and opens the matching Session's
share dialog, which names the Session it edits and exposes one plugin-sharing
switch. Shares can still be listed without any plugin declaring endpoints, and
the switch states that absence.

* fix(liveshare): open the selected share from the manager

The manager's Manage action now opens that Session's share dialog directly,
and binds workspace navigation when the page has it. The rows use one grid so
titles, online state, and actions line up, and the footer offers Cancel next to
Done.

* refactor(liveshare): elect shared-session attribution through a chain seat

Chat now declares `conversation.chat.user.before` as a chain seat whose owner
props carry the durable source and row kind. Liveshare's selector elects only
share-viewer user rows, so the component no longer probes or returns null, and
the official user row stays unchanged. The manager aligns its columns, adds a
per-share cancellation action, and no longer offers a footer cancel that meant
the window rather than a share.

* refactor(liveshare): drop PTC sharing from this change

The share surface now admits only the native read/write tools. PTC
run_code admission, its sandbox-policy cap, and the executor-side policy
extension move to a separate change, so this PR keeps non-PTC sharing focused.
Core PTC files, sandbox docs, and generated catalogs return to master.

* docs(liveshare): fold the viewer display name into the principal record

The optional displayName is part of the share-viewer principal, so its
persistence acknowledgement now lives in the 2026-10-13 principal record
instead of a separate 2026-10-27 record.

* refactor(liveshare): render user attribution through its own chain slot

* refactor(liveshare): keep one user message renderer for the attribution seat

* refactor(liveshare): render the attribution seat inside the user bubble

* docs(liveshare): regenerate the event graph pages

* test(liveshare): close the endpoint control coverage gaps

Cover the endpoint tier control's empty-offer return and drop the unused expiry choice list.

* chore(docs): regenerate the persistence catalogs after rebasing onto master

* chore(liveshare): carry the 0.2.2-alpha.1 release version
2026-10-28 01:44:19 +08:00
imccyu 5106da3bb7 Merge pull request #7434 from deepseek-harness/worktree/release-dsh-0.2.2-alpha.1
release: [email protected]
2026-10-28 00:17:25 +08:00
imccyu 69c389ae11 release(dsh): 0.2.2-alpha.1 2026-10-28 00:00:58 +08:00
imccyu a4c9c208cd Merge pull request #7431 from deepseek-harness/release/vendor-alpha-for-0.2.2-alpha.1
release(vendor): prepare alpha.1 family for 0.2.2-alpha.1
2026-10-27 23:51:40 +08:00
imccyu 656c740669 release(vendor): cordis 4.0.6-alpha.1, cosmokit 1.8.7-alpha.1, group 1.0.6-alpha.1, hmr 1.0.21-alpha.1, include 1.0.11-alpha.1, loader 1.0.7-alpha.1, logger-console 1.0.6-alpha.1, schemastery 3.18.6-alpha.1, timer 1.1.8-alpha.1 2026-10-27 23:39:36 +08:00
imccyu c232e618fd Merge pull request #7430 from deepseek-harness/revert-7413-perf/web-startup-assets
Revert "perf(web): 压缩动态插件资源以加快冷启动"
2026-10-27 23:27:45 +08:00
imccyu 3ddab34194 Revert "perf(web): 压缩动态插件资源以加快冷启动" 2026-10-27 23:25:57 +08:00
imccyu 873c68c15b feat(models): show model selector badges from the /models listing (#7027)
* refactor(llm-deepseek): share the /models listing machinery

Move the account route's `/models` request and decoding, its
credential-scoped listing cache, and listed-catalog resolution with the
version gate into the shared DeepSeek Messages library, so a second
route can follow the same listing. Names drop the account prefix, the
cache takes each access's endpoint settings with the call, and it gains
`invalidate()` for a forced reload. `isBuiltInCatalog()` recognizes the
catalog an uncustomized `models` setting resolves to.

The account route keeps its behavior; only its imports and the
override diagnostic wording ("model listing") change.

* feat(llm-deepseek-api-key): follow the official /models listing

While `models` equals the built-in catalog and the Messages origin is
https://api.deepseek.com, `deepseek-official` serves the official
`GET /models` listing, authenticated with the API key in `x-api-key`.
Loading, refresh, fallback, merging, and the client-version gate match
the account route: the first access waits up to `modelsTimeoutMs`, a
listing older than `modelsMaxAgeMs` refreshes in the background on the
next access, a failure keeps the last good listing or the built-in
catalog, and listed models this build cannot serve are refused before
network I/O.

A customized `models` list replaces the catalog and requests nothing.
Switching between a customized catalog and the listing discards the
cached listing, so Restore defaults reloads it on the next access.

Suites that boot the route on the official endpoint now answer the
listing themselves instead of reaching the network.

* docs: describe the official route model listing

Document when `deepseek-official` follows the official `/models`
listing, its timing fields, customization precedence, and the forced
reload after Restore defaults in the API-key README, the shared
transport README, and the provider guide. Record the decision in a new
Agent Note, keep the account note's statement of where the shared
listing code lives current, and regenerate the configuration catalog.

* test(llm-deepseek-api-key): pin the once-per-listing hidden-entry diagnostic

* docs: regenerate module and event graphs for the shared listing

* fix(llm-deepseek-api-key): report credential failures on the model request

On the official endpoint, `connect` waited for the listing view, whose
credential lookup rethrew a malformed key or a credential-store failure
before the agent loop recorded the prompt. A failed lookup now resolves
the connection from the current listing view, so `resolveAuth` reports
the failure on the model request and the turn keeps the prompt and the
failed attempt. A caller abort still rejects.

* docs(llm-deepseek-api-key): name which hidden listing entries are logged

* test: keep unit suites off the official model listing

Suites that boot the DeepSeek routes with a dummy or inherited key now
request `GET https://api.deepseek.com/models` on the official endpoint.
A unit-lane setup file rejects that request at once, so such a suite
serves the built-in catalog instead of reaching production or waiting
out the listing deadline on a network that never answers; suites that
exercise the listing answer `fetch` themselves. Other requests pass
through unchanged.

* feat(llm-deepseek): read tool_update from the model listing

Both DeepSeek routes now take a listed model's Messages tool update mode
from `api_capabilities.anthropic_messages.tool_update`, alongside the
system prompt mode. `in-history` and `addition-only` replace the
bundled mode; any other value reads as `leading-only` and removes it.
An absent or `null` mode keeps the bundled one, so a listing that does
not declare the field leaves `deepseek-flash` on `addition-only`.

`system_prompt_update: null` likewise keeps the bundled mode now; it
previously read as `leading-only`, which a serializer writing empty
values as null would have turned into a cache-unfriendly downgrade.

* fix(llm-deepseek): sync the built-in context window with the official listing

The official `/models` listing declares a 1,048,576-token context window
for `deepseek-flash` and `deepseek-v4-pro`; the built-in catalog said
1,000,000, a decimal-versus-binary mismatch. Both built-in entries now
carry the listed value, so offline starts and listing failures size the
context the same way the listing does. The Models page shows the exact
count for these rows, since 1,048,576 has no decimal K/M spelling.

`defaultContextWindow`, the fallback for model ids the catalog does not
describe, stays 1,000,000.

* test(llm-deepseek-api-key): reject the stubbed listing request with an Error

* test(cli): keep the built SDK smoke off the official model listing

The SDK profile's initialize now resolves the deepseek-official catalog,
which requests GET https://api.deepseek.com/models on the official
endpoint. With the smoke's fixture key that request answers 401 and the
route warns on stderr, so the test's empty-stderr assertion failed in the
e2e and snapshots lanes. Point the run at a loopback mock and assert it
received no model call.

* feat(llm): carry /models badges into the model catalog

The shared DeepSeek listing decoder reads an optional per-entry
`badge: { text, tone }`. Text is one literal or translations keyed by
lowercase language id with a required English entry, each trimmed to 1
to 12 characters on one line; tone is neutral, info, success, or
warning, and an absent or unrecognized tone reads as neutral. An absent
or null badge declares none; an unreadable one is dropped with a listing
problem and never hides its model.

resolveListedCatalog keeps the badges of offered models, both DeepSeek
routes attach them through catalogModelInfo, LlmRuntime.listModels
detaches them, and the Host model catalog forwards them as
ModelCatalogModel.badge. resolveModelInfo omits the badge, so no model
request, Session event, or model-facing tool carries it.

* feat(client): render catalog badges in the model selector

The composer model seat shows a catalog model's badge after its name in
the trigger, the root-menu value, and each option: a Tag in the badge's
tone whose text is resolved for the active locale through
ctx.locale.resolveText. A conversation.input.model.label contribution
replaces the name and badge together and receives the badge in its
owner props.

Remove ui-deepseek-model-label-1017, which hard-coded `1017` beside
DeepSeek-V4.1-Flash for two providers, and its Web bundle row. The badge
now comes only from the model listing, so the build ships none of its
own; the upgrade guide covers profile patches that still target the
removed row. The Web e2e replaces the fixed-badge case with replay
catalogs that declare literal and translated badges, and the replay
adapter validates and advertises a configured badge.

* feat(llm-deepseek): read model badges from client_metadata

Each /models entry now declares its selector badge under
client_metadata["deepseek-harness"].badge, keyed by the User-Agent product
token like client_requirements, so the public model fields stay unchanged
and later DeepSeek Harness fields join the same object. Every field under
client_metadata is ignorable: null at any level shows no badge, and a level
that is not an object drops only the badge with a diagnostic naming it.

* docs: record why the listing badge container is client_metadata

* fix(models): refresh listed badges in open selectors

* refactor(models): share listing cache lifecycle wiring

* docs(models): refresh listing event producer catalog

* fix(models): retain badges when listing refresh fails

* fix(models): validate badge text and preserve tags beside long names

* test(models): cover account listing automatic badge removal
2026-10-27 23:18:07 +08:00
imccyu 6a6e88754d Merge pull request #7429 from deepseek-harness/fix/ci-premerge-depth-window
fix(ci): deepen the pre-merge fetch when the time window strands the merge base
2026-10-27 23:09:35 +08:00
imccyu 18a559e7dd fix(ci): deepen the pre-merge fetch when the time window strands the merge base 2026-10-27 23:01:19 +08:00
imccyu 2de3029cc6 Merge pull request #7427 from deepseek-harness/fix/chat-landing
fix(chat): keep the transcript steady at Turn end and when steering lands
2026-10-27 22:16:47 +08:00
imccyu a1659120a3 fix(session-controller): retire a queued echo when its Inbox row is canceled
A queued echo now records its Inbox position like transcript and steering
echoes. Later splices move that position, so a canceled splice that removes
the row retires the echo even when no Inbox projection ever showed it, for
example when the Turn is stopped right after queueing. A claimed row still
waits for its admitted message, and the queue dock still hands the echo over
to the Host row it renders.

The fold-room e2e no longer samples the running status slide or the
`data-chat-tail-pinned` mark that this branch removed. It asserts that fold
room below a short answer takes the new Turn's growth without scrolling
through Turn end, that the room never grows back, and that the running
status only moves down.
2026-10-27 21:36:10 +08:00
imccyu ae07093132 fix(chat): keep the transcript steady at Turn end and when steering lands
Measured against 0.2.1-rc.4 and master with the frame-sampled motion matrix,
the transcript moved back and forth at Turn end and around input landing.
This keeps rc.4's landing behavior, master's steering layout, and fixes the
reversals that remain.

- Completion-time folding keeps the first visible surviving row still when
  work rows close at once, filling the gap with bottom space capped at half
  the scrollport, instead of letting the browser clamp the position.
- After the Session stops running, the running status keeps its tail slot
  hidden and silent until the Turn's actions row replaces it, so the tail
  does not shrink and grow back.
- A Turn's duration control takes its gap in one frame instead of sliding.
- A fold batch that takes over following for a content change absorbs
  growth outside the batch before its first frame.
- Steering lands at once under either fold timing: the running Turn keeps
  streaming above its echo, which a native animation would push down.
- The running status no longer slides below a Step's first output, and the
  column no longer carries `data-chat-tail-pinned`; the steering entrance
  keeps its animation.
- A queued prompt is accepted by its Inbox row as well as by its receipt,
  so the composer clears the draft in the frame the queue dock shows it.
- A queued echo waits for the Inbox projection instead of retiring on the
  durable splice, so the queue dock does not empty for a frame.
2026-10-27 20:44:52 +08:00
Kexin Liu 1d4b80f04e Merge pull request #7413 from deepseek-harness/perf/web-startup-assets
perf(web): 压缩动态插件资源以加快冷启动
2026-10-27 18:11:41 +08:00
Kexin Liu 72a84b24bd perf(web): 合并 Web Search 设置刷新中的重复凭据查询 (#7419)
* perf(web): coalesce Web Search credential reads

* fix(web): settle credential refresh races and startup budget checks
2026-10-27 18:06:38 +08:00
liukx0205 0bfa572080 test(web): retain minified bundles in real-host smoke coverage 2026-10-27 17:55:51 +08:00
Magolorand_Kerman fcca540841 refactor(session): unify runtime projection reads (#7347)
* refactor(session): bind runtime projection reads to one session

* refactor(session): expose session-bound readers on projection handles

---------

Co-authored-by: _Kerman <[email protected]>
2026-10-27 09:50:22 +00:00
Yichen Jiang 9890d095bb test(models): cover account listing automatic badge removal 2026-10-27 16:36:55 +08:00
Yichen Jiang 825945fd40 fix(models): validate badge text and preserve tags beside long names 2026-10-27 16:17:36 +08:00
liukx0205 4f851948a5 perf(web): minify dynamic client bundles 2026-10-27 15:42:21 +08:00
Yichen Jiang 67af01533e fix(models): retain badges when listing refresh fails 2026-10-27 15:26:08 +08:00
Yichen Jiang 049621b98b docs(models): refresh listing event producer catalog 2026-10-27 15:09:53 +08:00
Yichen Jiang a36745f6ee refactor(models): share listing cache lifecycle wiring 2026-10-27 15:01:54 +08:00
Yudong Han 6e7fae82ab Merge pull request #7407 from deepseek-harness/fix/ask-multi-recommended
fix(ui-user-questions): 多选题预选所有推荐选项
2026-10-27 14:57:06 +08:00
Yichen Jiang 8394a200b9 fix(models): refresh listed badges in open selectors 2026-10-27 14:53:23 +08:00
Yichen Jiang af086ad377 Merge remote-tracking branch 'origin/master' into worktree/pr-7027-badge-refresh
# Conflicts:
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	docs/persistence-catalog.i18n.yaml
#	docs/persistence-catalog.md
#	docs/persistence-catalog.zh.md
#	docs/persistence-schema.json
#	packages/client/ui-deepseek-model-label-1017/package.json
2026-10-27 14:38:36 +08:00
yudshj 185eb56a2b test(web): 新增多选题多推荐项预选的 Web snapshot 场景 2026-10-27 14:31:56 +08:00
yudshj 07c3c10d2c test(ui-user-questions): 覆盖多选预选的计时与回看卡片 2026-10-27 13:47:34 +08:00
yudshj bf8d8494ba fix(ui-user-questions): 多选题预选所有推荐选项 2026-10-27 13:43:39 +08:00
_Kerman 65032e4414 Merge pull request #7086 from deepseek-harness/xtr/trajectory-result-mode
feat(trajectory): 展示实时工具原始结果
2026-10-27 12:35:29 +08:00
Yifffan d4470e59dc Merge pull request #7089 from deepseek-harness/sidebar-spacing-pr1
fix(client): 统一侧边栏对齐原则,扩大操作热区,沉淀对齐方式 skills
2026-10-27 12:16:17 +08:00
_Kerman 9e2ec92f4a test(web): pace SVG replay under CI concurrency
Apply the focused replay-pacing fix from f9d31843e6c while preserving recording, fixtures, and connection assertions.
2026-10-27 12:14:34 +08:00
Huo Yaoyuan 09c2587b7e Merge pull request #7349 from deepseek-harness/feat/windows-administrator
fix(windows): remove LUA_TOKEN from the Windows ACL sandbox
2026-10-27 11:25:17 +08:00
_Kerman 89c5ea4d83 Merge latest master into xtr/trajectory-result-mode 2026-10-27 11:16:45 +08:00
Yichen Jiang 6300195071 Merge pull request #7378 from deepseek-harness/worktree/forge-diff-review-state
fix(forge): preserve diff review state and copy header paths
2026-10-27 11:00:22 +08:00
imccyu 3bb9682210 Merge pull request #7260 from deepseek-harness/worktree/sync-dsh-0.2.1-rc.4-to-master
release(dsh): sync 0.2.1-rc.4 to master
2026-10-27 11:00:01 +08:00
Yichen Jiang f73e140299 Merge remote-tracking branch 'origin/master' into worktree/forge-diff-review-state 2026-10-27 10:46:50 +08:00
Yichen Jiang 17b9e1536d fix(forge): release inactive diff subscriptions and menus 2026-10-27 10:46:40 +08:00
imccyu d534e65692 release(dsh): align master packages with 0.2.1-rc.4 2026-10-27 02:23:17 +08:00
imccyu 543219b909 Merge remote-tracking branch 'origin/master' into worktree/sync-dsh-0.2.1-rc.4-to-master 2026-10-27 02:21:20 +08:00
Yichen Jiangandimccyu e2c4d0538f ci: make reference and worktree tests portable (#7381)
* fix(forge): make reference and worktree tests portable

* test(worktree): cover error and text cancellation reasons

* test(forge): brace void arrow bodies in the reference lookup spec

* test(ci): expect the joined version script path in the pre-merge check

---------

Co-authored-by: imccyu <[email protected]>
2026-10-27 02:20:02 +08:00
imccyu 9c69007eca Merge pull request #7384 from deepseek-harness/feat/pre-merge-check
feat(ci): run one script for the merged tree in the pre-merge check
2026-10-27 01:44:02 +08:00
imccyu 5d0b96b5bd docs: state the pre-merge order the check runs, without a stray blank line 2026-10-26 23:52:18 +08:00
imccyu 3ddacfd725 fix(ci): narrow the version scan and keep a crashing check inside its mode
The scan now reads exactly the manifests the release tooling rewrites, names
every directory and file it cannot read, and refuses an empty corpus; the
command takes the root it checks so the exit code is tested, and the workflow
reports a crashed check as a warning in report mode.
2026-10-26 23:51:18 +08:00
imccyu 2b485ea1db fix(ci): run the version check as the first step of the pre-merge check
It reads the manifests without an install, so it opens the step list and a
mistyped release stops the check before anything is built.
2026-10-26 23:27:39 +08:00
imccyu 1e433706f4 refactor(ci): run the pre-merge steps as one ordered list
Each step is a spawn whose stdio reaches the job log, the first failure stops
the list, and the script decides from CHECK_MODE whether to annotate as an
error or a warning, so the workflow only calls it.
2026-10-26 23:24:20 +08:00
imccyu a518d10c95 fix(ci): keep the pre-merge check's failures honest
The missing-script branch now fails in block mode instead of passing, the shell
block parses, the version walk refuses a directory it cannot read or one that
holds no manifest, the install is watched before anything else can throw, and a
spec runs `bash -n` over every Policy shell block.
2026-10-26 23:12:29 +08:00
imccyu c3998dde7c feat(ci): run one script for the merged tree in the pre-merge check
`Policy / Pre-Merge Check` now runs `scripts/pre-merge-check.ts` under plain
Node and knows nothing else about the merged tree. That script installs the
dependencies, checks that every released manifest carries the root version
while the install runs, and builds the Host and Client contracts, reporting
which step failed.
2026-10-26 22:54:02 +08:00
Yichen Jiang f92fadd30c Merge remote-tracking branch 'origin/master' into worktree/forge-diff-review-state
# Conflicts:
#	packages/experimental/client-ui-forge/README.i18n.yaml
#	packages/experimental/client-ui-forge/README.md
#	packages/experimental/client-ui-forge/README.zh.md
2026-10-26 22:38:20 +08:00
Yichen Jiang f4a0bfe44a Merge pull request #7376 from deepseek-harness/worktree/forge-session-list
feat(forge): show worktree and PR status in the session list
2026-10-26 22:33:59 +08:00
Yichen Jiang c1b1d9b71f fix(forge): keep retained diffs from scroll anchoring on session switches 2026-10-26 22:08:57 +08:00
Yichen Jiang 57716b544f fix(forge): preserve diff review state and copy header paths 2026-10-26 21:44:38 +08:00
Yichen Jiang a941288b74 fix(forge): use the existing storage helper dependency classification 2026-10-26 21:35:20 +08:00
Yichen Jiang d82b72b3d7 test(forge): cover cancellation with shared list readers 2026-10-26 21:25:53 +08:00
Yichen Jiang db36fafc3a test(forge): preserve the failure backoff timing assertion 2026-10-26 21:25:20 +08:00
Yichen Jiang e99a597b16 fix(forge): drain list state streams and refresh reopened requests 2026-10-26 21:22:30 +08:00
Yichen Jiang a36d02a596 Merge master into worktree/forge-session-list 2026-10-26 21:17:53 +08:00
Yichen Jiang 9dd1ecc1f4 Merge pull request #5961 from deepseek-harness/worktree/forge-pr-worktree-panel
feat(forge): add the pull request panel and worktree Sessions
2026-10-26 12:40:20 +00:00
Yichen Jiang d1fa5a7a99 Merge pull request #5960 from deepseek-harness/worktree/forge-change-request-bindings
feat(forge): bind code host change requests to Sessions
2026-10-26 20:22:44 +08:00
Turtle 979f608f53 Merge pull request #7363 from deepseek-harness/turtle/link-boundary-fcb8291951da
fix(ui-primitives): end bare URLs before CJK punctuation
2026-10-26 20:13:09 +08:00
Yichen Jiang 3cec608656 Merge remote-tracking branch 'origin/worktree/forge-change-request-bindings' into worktree/forge-pr-worktree-panel 2026-10-26 20:09:46 +08:00
Yichen Jiang 71f0ec2905 test(web): record SVG connection termination counters 2026-10-26 20:08:52 +08:00
Yif 5a8c7c8f6b fix(client): prepare rail search before expanding the sidebar 2026-10-26 20:01:24 +08:00
std4453 7df7e1993c Merge pull request #7317 from deepseek-harness/feat/desktop-upload-edgeone-purge
feat(desktop): 上传固定安装包后刷新 EdgeOne 缓存
2026-10-26 19:53:53 +08:00
EricMoin 3b432c3f40 fix(client): stack mobile font-family settings (#7356)
* fix(client): stack mobile font-family settings

* fix(client): cover font layout and touch sizing

* test(bench): interleave scaling samples
2026-10-26 19:53:18 +08:00
Yichen Jiang b66c66a71a Merge remote-tracking branch 'origin/worktree/forge-change-request-bindings' into worktree/forge-pr-worktree-panel 2026-10-26 19:47:22 +08:00
Yichen Jiang e729b41395 test(web): remove temporary SVG transport instrumentation 2026-10-26 19:46:34 +08:00
Yichen Jiang 6a88a371be Merge remote-tracking branch 'origin/worktree/forge-change-request-bindings' into worktree/forge-pr-worktree-panel 2026-10-26 19:44:24 +08:00
Yichen Jiang 081249ca29 test(web): preserve the original socket spy implementation 2026-10-26 19:43:48 +08:00
imccyu 1ba07e14ec Merge pull request #7368 from deepseek-harness/revert-7362-turtle/files-pane-scroll-72ef67cde97a
Revert "fix(client): keep files tree view across session switches"
2026-10-26 19:41:22 +08:00
imccyu 6ff50a4890 Revert "fix(client): keep files tree view across session switches" 2026-10-26 19:40:44 +08:00
Yichen Jiang 5d8b823f7b Merge remote-tracking branch 'origin/worktree/forge-change-request-bindings' into worktree/forge-pr-worktree-panel 2026-10-26 19:37:59 +08:00
Yichen Jiang ed0fe80f2a test(web): wait for restored terminal output after reload 2026-10-26 19:37:09 +08:00
Yichen Jiang 573544b8b8 Merge remote-tracking branch 'origin/worktree/forge-change-request-bindings' into worktree/forge-pr-worktree-panel 2026-10-26 19:35:43 +08:00
Yichen Jiang 8a5cd2fb63 test(forge): align confined reference and recommendation fixtures 2026-10-26 19:34:55 +08:00
Yichen Jiang 2fa853870f fix(forge-panel): register all shipped locale dictionaries 2026-10-26 19:34:16 +08:00
Turtle 2d5e34d64f Merge pull request #7362 from deepseek-harness/turtle/files-pane-scroll-72ef67cde97a
fix(client): keep files tree view across session switches
2026-10-26 19:29:43 +08:00
Turtle f266121f10 test(ui-primitives): drop unnecessary template literals 2026-10-26 19:28:41 +08:00
Yichen Jiang dd4706fffe test(web): diagnose Host termination during SVG delivery 2026-10-26 19:14:57 +08:00
Turtle ca40dc544e fix(ui-primitives): end bare URLs before astral symbols and add Web snapshot 2026-10-26 19:10:55 +08:00
Turtle b84fea6768 fix(client): guard retained files trees by binding
Split the store's restore seed from start, accept a retained tree only
from the binding that currently owns the tab, drop the new unknown cast
in the body harness, document the watch lifetime limitation, and expect
the e2e Files directory to stay expanded after a Session round trip.
2026-10-26 18:58:55 +08:00
Yichen Jiang a6883f4038 test: calibrate npm layout budget for the expanded release graph 2026-10-26 18:54:40 +08:00
Turtle bdfc0c1d12 fix(ui-primitives): end bare URLs before CJK punctuation 2026-10-26 18:43:12 +08:00
Yichen Jiang f74e15ba08 Merge branch 'worktree/forge-bindings-stack-sync' into worktree/forge-pr-worktree-panel
# Conflicts:
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	packages/experimental/README.i18n.yaml
#	packages/preset/agent-preset/skills/cordis-composition-reference/references/packages.md
2026-10-26 18:39:10 +08:00
Turtle b4c2706c41 fix(client): keep files tree view across session switches
The Files tab's session-scoped store is released when the reader leaves
the Session, so returning reseeded a fresh tree at scroll offset 0 with
every directory collapsed. Retain each tab's last drawn tree in the face
per Session and tab, seed the next store with it, and replace the
earlier binding's watches.

Fixes #7361
2026-10-26 18:37:44 +08:00
Yichen Jiang 667ef04270 Merge remote-tracking branch 'origin/master' into worktree/forge-bindings-stack-sync
# Conflicts:
#	packages/experimental/README.i18n.yaml
2026-10-26 18:35:22 +08:00
Huo Yaoyuan 6970269f65 test(sandbox): label CIM availability by caller privilege
The probe now uses [Environment]::IsPrivilegedProcess and reports PASS when CIM availability matches the child's privilege, FAIL otherwise; both runner cases assert PASS.
2026-10-26 18:29:33 +08:00
Huo Yaoyuan 872ec48170 docs(windows): the restricted token does not use LUA_TOKEN
The Decision and How-the-restriction-works paragraphs now state the current flag set and that the derived token keeps the caller's group SIDs; the dated follow-up keeps the reason.
2026-10-26 18:18:48 +08:00
Yichen Jiang bbb031654e Merge remote-tracking branch 'origin/worktree/forge-change-request-bindings' into worktree/forge-pr-worktree-panel
# Conflicts:
#	docs/config-catalog.md
#	docs/config-catalog.zh.md
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	packages/experimental/forge-github/README.i18n.yaml
#	packages/experimental/forge-github/README.md
#	packages/experimental/forge-github/README.zh.md
#	packages/experimental/forge-github/src/provider.ts
#	packages/experimental/forge-session/README.i18n.yaml
#	packages/experimental/forge-session/README.md
#	packages/experimental/forge-session/README.zh.md
#	packages/experimental/forge-session/src/index.ts
#	packages/experimental/forge/README.i18n.yaml
#	packages/experimental/forge/README.md
#	packages/experimental/forge/README.zh.md
#	packages/experimental/forge/src/index.ts
2026-10-26 18:14:53 +08:00
zhc 3969c29fa5 refactor(desktop): simplify EdgeOne purge to submission only 2026-10-26 18:00:09 +08:00
Yichen Jiang 7fa3deaf82 fix(forge-session): preserve cancelled watches during registry updates 2026-10-26 17:59:54 +08:00
Yichen Jiang 1f54902e3b fix(forge-panel): drain worktree operations before releasing readers 2026-10-26 17:58:53 +08:00
Yichen Jiang 000583c135 fix(forge-panel): retain incomplete worktree recovery across reloads 2026-10-26 17:58:51 +08:00
Yichen Jiang 2384f52719 fix(forge-panel): hide the actual docked or floating tab 2026-10-26 17:57:14 +08:00
Yichen Jiang ccf509205f fix(forge-panel): release cached state with its Session scope 2026-10-26 17:57:12 +08:00
Yichen Jiang fa2dbbd8fd fix(forge-panel): format change counts in the selected locale 2026-10-26 17:57:11 +08:00
Yichen Jiang 04db213ad9 fix(forge-panel): pin merge confirmation to the reviewed head 2026-10-26 17:57:09 +08:00
Yichen Jiang 4969525f33 fix(worktree): remove only the missing target registration 2026-10-26 17:57:07 +08:00
Yichen Jiang b740b95424 fix(worktree): validate recorded restoration targets before mutation 2026-10-26 17:57:06 +08:00
Yichen Jiang e342b7cbf9 fix(forge-panel): label worktrees with their current HEAD 2026-10-26 17:52:33 +08:00
Yichen Jiang 3d29275e63 fix(forge-panel): cancel and join failed Git inspections 2026-10-26 17:52:32 +08:00
Yichen Jiang a5460dbe41 fix(forge): preserve refresh requests during active polling 2026-10-26 17:52:30 +08:00
Yichen Jiang dd0ed998e7 docs(forge): define and verify worktree request type 2026-10-26 17:52:30 +08:00
Yichen Jiang ac1b807e4f docs(worktree): explain generated name migration 2026-10-26 17:52:29 +08:00
Yichen Jiang 47a7694e6b docs(forge-panel): describe current worktree behavior 2026-10-26 17:52:28 +08:00
winewill d8b9cff130 Merge pull request #7326 from deepseek-harness/feat/intel-mac-packaging
feat(desktop): add Intel Mac packaging Action and align platform forms
2026-10-26 17:51:11 +08:00
Yichen Jiang 4bef9db77e fix(forge-github): validate byte budgets separately from timeouts 2026-10-26 17:49:52 +08:00
Yichen Jiang 7457789b98 fix(forge): reject backoff caps below the active interval 2026-10-26 17:49:26 +08:00
Yichen Jiang 1ebb31915b fix(forge-github): preserve HTTP remote ports 2026-10-26 17:49:24 +08:00
Yichen Jiang 8d4c8865dd fix(forge-session): resync watches when providers change 2026-10-26 17:48:19 +08:00
Yichen Jiang ae40382197 fix(forge-github): distinguish HTTP errors from GraphQL results 2026-10-26 17:45:20 +08:00
Yichen Jiang 3f53fe39fb fix(forge-session): cancel discovery when Sessions close 2026-10-26 17:43:59 +08:00
Yichen Jiang fbcfd80e21 fix(forge-github): enforce configured hosts before queries 2026-10-26 17:42:35 +08:00
Yichen Jiang cba0128268 fix(forge): drain provider calls before unregistering 2026-10-26 17:42:09 +08:00
Yichen Jiang 1abf0f55fe docs(forge): clarify discovery limits and provider contracts 2026-10-26 17:39:09 +08:00
1cc040b535 fix(client): clarify mobile native-open feedback (#6660)
* feat(ui-open-in-app): announce settled open outcomes app-wide

The shared file and directory opener reported a nullable failure, so it could
not tell a handoff the Host acknowledged from a gesture an adapter deliberately
dropped — a duplicate already in flight, or a controller disposed before the
gesture ran. Its notice also lived inside the control that asked for the open,
so it died with the panel, tab, or card the gesture started in.

Replace that with a discriminated outcome on both controllers and adapters, and
report each settled gesture into one `shell.overlay` entry that renders the
app-wide Toast: the Session-header command, the files-tab and Session-header
buttons, both document-preview controls, and both delivery-card controls.
`{ kind: 'skipped' }` announces nothing, because no handoff happened, so card
status and the toast never disagree about what settled.

Name the action for what it does on every surface — "Open on your computer" —
instead of after whichever application would take the handoff. The application
stays supplementary, in the tooltip beside the shortcut it runs under, which is
what the accessible name, visible text, and command label now share.

* fix(client): unify open-in-app feedback

* fix(client): report native open outcomes accurately

* fix(client): tighten open feedback outcomes and lifecycle tests

* test(ptc): synchronize hot-loop log capture

Observe a synchronous child marker before cancellation so the console retention assertion does not depend on completing Node startup within one second.

* test(bench): interleave scaling samples

* fix(client): scope native-open feedback to mobile

Explain native opens on another device for mobile and PWA clients while keeping desktop success silent. Preserve failure feedback after the initiating surface closes and ignore skipped requests.

Remove unrelated benchmark and PTC test changes and retain existing button labels.

Refs #6097

---------

Co-authored-by: EricMoin <[email protected]>
Co-authored-by: EricMoin <[email protected]>
2026-10-26 17:16:44 +08:00
_Kerman a2f822094b test(trajectory): update denied result section snapshot 2026-10-26 17:10:56 +08:00
_Kerman e63f5903f1 fix(trajectory): sync catalogs after hiding the result service type 2026-10-26 17:10:46 +08:00
_Kerman 5aa8416c11 refactor(trajectory): hide tool result service context type 2026-10-26 17:00:57 +08:00
_Kerman e72d36764e Merge pull request #7330 from deepseek-harness/xtr/fix-7327-ptc-abort
fix(ptc): classify caller cancellation as aborted
2026-10-26 16:53:02 +08:00
Turtle 884077bf72 Merge pull request #7043 from deepseek-harness/turtle/cross-session-path-link-9836d52f7a4d
fix(ui-conversation): decorate pasted file and folder paths as whole tokens
2026-10-26 16:45:25 +08:00
_Kerman 1576a9f7eb test(ptc): cover reviewed cancellation cases 2026-10-26 16:40:17 +08:00
Turtle deebbf3191 fix(ui-conversation): decorate pasted file paths as whole tokens
Closed @"…" paths and unquoted @ tokens with a separator or extension now decorate in full, matching the spans the sent message renders as references.
2026-10-26 16:33:21 +08:00
imccyu ce31187885 release(dsh): align master packages with 0.2.1-rc.4 2026-10-26 16:32:02 +08:00
imccyu 31693e2151 fix(ui-conversation): drop the unused more views label from the remaining locales 2026-10-26 16:31:53 +08:00
Huo Yaoyuan bdd75ca1a0 feat(windows): Remove LUA_TOKEN from Windows ACL sandbox.
This enables Administrator/WDAGUtilityAccount
2026-10-26 16:22:15 +08:00
imccyu 8c9c51d86d Merge worktree/release-dsh-0.2.1-rc.4 into the 0.2.1-rc.4 synchronization branch 2026-10-26 16:21:42 +08:00
imccyu 6a19d120c9 Merge master into DSH 0.2.1-rc.4 synchronization branch 2026-10-26 16:18:13 +08:00
imccyu 504a01a957 release(dsh): 0.2.1-rc.4 2026-10-26 16:15:51 +08:00
Turtle 93fefc4fbd Merge pull request #7153 from deepseek-harness/turtle/resume-blocked-goal-f1301e734784
fix(ui-goal): offer resume for a blocked goal
2026-10-26 16:12:15 +08:00
imccyu 199ffe37ba fix(ui): keep the more views tooltip fixed (#7342) 2026-10-26 16:12:05 +08:00
Yichen Jiang df4f3fd0c1 fix(forge-session): bind numeric PR references with explicit repositories 2026-10-26 16:10:51 +08:00
Turtle 9a6ab782fc Merge pull request #7120 from deepseek-harness/turtle/plugin-preview-image-e7b209c5fe5a
docs(plugin-market): redraw the feature preview after the real Discover tab
2026-10-26 16:08:21 +08:00
Turtle 699cecb83d Merge pull request #4719 from deepseek-harness/fix/toolcall-unused-args
fix(tools): tolerate unambiguous optional arguments
2026-10-26 16:06:45 +08:00
Dudu-0223 cf38ed3205 Merge pull request #7322 from deepseek-harness/fix/web-fetch-charset-tolerance
fix(web): recover malformed charset declarations with strict UTF-8
2026-10-26 15:56:04 +08:00
Xu Hanxiang 037ffa43d9 Merge pull request #6639 from deepseek-harness/worktree/experimental-client-ui-perf
feat(experimental): add client-ui-stats conversation view plugin
2026-10-26 15:48:06 +08:00
_Kerman 9e979a71e6 fix(ptc): align caller cancellation with native tools 2026-10-26 15:43:05 +08:00
mektpoy d729145a9c test(experimental): refresh web snapshots for Developer tools metadata 2026-10-26 15:29:15 +08:00
Dudu-0223 f6b8f49834 Merge pull request #7195 from deepseek-harness/fix/shared-subagent-tools
fix(subagent): share delegation tools within each preset revision
2026-10-26 15:27:42 +08:00
EricMoin 7333a38c7a fix(host): 修复 macOS 原生目录选择框初始灰态 (#7307)
Queue macOS directory-picker activation until the NSOpenPanel modal event loop is running. Preserve the Edit menu, selected-path output, and cancellation behavior.

Validate the script with same-service physical-mouse reversal tests and cover queued activation and selection results in the VM regression tests. Document the macOS validation range.

Fixes #7320
Refs #6685
2026-10-26 15:21:16 +08:00
Dudu-0223 b062e52bf6 test(web): strengthen charset recovery boundary coverage 2026-10-26 15:05:10 +08:00
_Kerman aa455f5a40 fix(ptc): preserve structured cancellation details 2026-10-26 15:03:19 +08:00
Dudu-0223 db38164d4c docs(subagent): clarify shared preset delegation tradeoffs 2026-10-26 15:02:10 +08:00
Yif 5616331997 fix(client): copy only declared drag-preview variables 2026-10-26 15:00:59 +08:00
mektpoy b2ddb38040 refactor(experimental): split the Statistics table renderers from the view entry 2026-10-26 14:57:16 +08:00
Turtle 093457deb0 Merge remote-tracking branch 'origin/master' into turtle/plugin-preview-image-e7b209c5fe5a
# Conflicts:
#	packages/experimental/plugin-market-bundle/docs/DETAIL.md
#	packages/experimental/plugin-market-bundle/docs/DETAIL.zh.md
2026-10-26 14:56:56 +08:00
_Kerman b9bf3bc4eb fix(trajectory): align live result states and JSON previews 2026-10-26 14:55:59 +08:00
Yichen Jiang d78dabef76 Merge pull request #6426 from deepseek-harness/worktree/composer-host-confirmed-echo
feat(web): show a message only after the Host accepts it
2026-10-26 14:44:20 +08:00
_Kerman b7849a5eea fix(ptc): render structured cancellation reasons readably 2026-10-26 14:40:33 +08:00
mektpoy 37afdaf3bb chore(release): cover the current dual-release resolution graph 2026-10-26 14:40:20 +08:00
Dudu-0223 0d7ae41c8f fix(web): constrain malformed charset recovery to strict UTF-8 2026-10-26 14:39:21 +08:00
mektpoy b7f14d3108 refactor(experimental): rename client-ui-perf to client-ui-stats 2026-10-26 14:30:51 +08:00
winewill 58cae53057 docs(desktop): match Windows packaging Action title 2026-10-26 14:26:40 +08:00
_Kerman 6287b3f387 feat(trajectory): stack raw and rendered tool results 2026-10-26 14:21:08 +08:00
Yif a28c3ec88a fix(client): limit workspace header motion to search interactions 2026-10-26 14:20:39 +08:00
winewill a578ca435f feat(desktop): add Intel Mac packaging workflow and align forms 2026-10-26 14:17:58 +08:00
zhc ad5dfe7658 fix(desktop): 修复 EdgeOne 轮询和发布恢复并补齐回归测试 2026-10-26 14:01:16 +08:00
Yichen Jiang 28f9c98108 Merge branch 'worktree/forge-bindings-stack-sync' into worktree/forge-pr-worktree-panel 2026-10-26 13:59:36 +08:00
Yichen Jiang 6abae82a41 Merge branch 'worktree/composer-host-confirmed-echo' into worktree/forge-bindings-stack-sync 2026-10-26 13:58:42 +08:00
Yichen Jiang 491a834d99 fix(web): keep mobile focus on the composer after sending 2026-10-26 13:57:39 +08:00
Dudu-0223 f673eec903 fix(web): decode a malformed multi-label charset declaration 2026-10-26 13:52:16 +08:00
mektpoy 1b1ba6d0f7 fix(experimental): wrap event scope badges on narrow tables 2026-10-26 13:48:47 +08:00
imccyuandimccyu df469ea6ff fix(ci): keep the pre-merge check green in report mode (#7311)
* fix(ci): keep the pre-merge check off the report-mode failures

* fix(ci): fail the pre-merge check in block mode without a base commit

* fix(ci): report every pre-merge failure the way its mode asks

The merge step now separates a base commit it cannot fetch from a merge it
cannot do, both failures print an annotation, and a spec pins the mode
wiring of the three Policy workflows.

* test(ci): type the Policy workflow assertions

---------

Co-authored-by: imccyu <[email protected]>
2026-10-26 13:39:57 +08:00
Dudu-0223 ae38375654 Merge pull request #7214 from deepseek-harness/fix/skill-root-isolation
fix(skills): 隔离技能根目录读取失败并保留可用技能
2026-10-26 13:35:02 +08:00
Yichen Jiang 64c81cb3e3 Merge branch 'worktree/forge-bindings-stack-sync' into worktree/forge-pr-worktree-panel 2026-10-26 13:30:55 +08:00
mektpoy e4a0262fb0 docs(experimental): describe the Statistics view in Developer tools metadata 2026-10-26 13:30:52 +08:00
Yichen Jiang 3cee25a3a5 Merge branch 'worktree/composer-host-confirmed-echo' into worktree/forge-bindings-stack-sync 2026-10-26 13:30:15 +08:00
Yichen Jiang b0e41f74ad Merge remote-tracking branch 'origin/master' into worktree/composer-host-confirmed-echo
# Conflicts:
#	packages/api/session-controller/README.i18n.yaml
#	packages/api/session-controller/src/client/sessions/session.ts
#	packages/client/ui-conversation/README.i18n.yaml
2026-10-26 13:29:33 +08:00
mektpoy 606bdf16f2 docs(experimental): record the recorded-session testing exemption 2026-10-26 13:26:11 +08:00
mektpoy ef0f768f06 feat(experimental): translate the Statistics view into five shipped locales 2026-10-26 13:26:11 +08:00
mektpoy 120c717035 fix(experimental): tighten paging guards and event payload typing 2026-10-26 13:26:11 +08:00
mektpoy 65b7e61b76 refactor(experimental): remove redundant token-usage validation 2026-10-26 13:26:11 +08:00
mektpoy 1a2f570d1f fix(experimental): single-flight older pages and keep retries explicit 2026-10-26 13:26:11 +08:00
mektpoy 476d7c9107 docs(experimental): align implementation notes with the shipped view 2026-10-26 13:26:10 +08:00
mektpoy 31a2defe6e refactor(experimental): require the standard projection seat and simplify fold access 2026-10-26 13:26:10 +08:00
mektpoy 1b9b924a05 fix(experimental): complete shipped-locale dictionaries and release expectations 2026-10-26 13:26:10 +08:00
mektpoy 8c311ed279 fix(experimental): complete plugin locale metadata and source path mapping 2026-10-26 13:26:10 +08:00
mektpoy f449996dbd feat(experimental): render the expanded model answer as Markdown 2026-10-26 13:26:10 +08:00
mektpoy 65dcfaefa5 fix(experimental): align package version and remove duplicated helpers 2026-10-26 13:26:10 +08:00
mektpoy 8a25bac0ab feat(experimental): explain retries, attempts, context refreshes, and approvals as event rows 2026-10-26 13:26:10 +08:00
mektpoy a5b5d3a865 feat(experimental): expand the full model answer from step details 2026-10-26 13:26:10 +08:00
mektpoy 310e943fe7 feat(experimental): ship the Statistics view through Developer tools and rename its tab 2026-10-26 13:26:10 +08:00
mektpoy d284f67c54 fix(experimental): keep event and step trigger text vertically centered 2026-10-26 13:25:52 +08:00
mektpoy 1ddc7e23ca feat(experimental): show the step start timestamp with date and milliseconds 2026-10-26 13:25:52 +08:00
mektpoy 8a26b3b06f feat(experimental): total the whole session from host projections 2026-10-26 13:25:52 +08:00
mektpoy 534c0845db chore(experimental): refresh translation pairing after rebase 2026-10-26 13:25:51 +08:00
mektpoy f234b091df feat(experimental): add non-step event rows and context delta switches 2026-10-26 13:25:51 +08:00
mektpoy a885fc7d5d feat(experimental): show compaction summary calls in the perf table 2026-10-26 13:25:51 +08:00
mektpoy 8dec878ecf fix(experimental): keep totals cumulative and switch token units to M 2026-10-26 13:25:51 +08:00
mektpoy 6df1a2c1e3 fix(experimental): dock the perf controls above the composer 2026-10-26 13:25:51 +08:00
mektpoy 9905e194b4 feat(experimental): show step timing beside each step and pin the footer 2026-10-26 13:25:51 +08:00
mektpoy f68f375bda feat(experimental): add totals row and context-basis controls 2026-10-26 13:25:51 +08:00
mektpoy 559c4c689d fix(experimental): weight turn cache hit by measured prompt tokens 2026-10-26 13:25:51 +08:00
mektpoy 1d751809a1 fix(experimental): complete sender JSDoc and trim README summaries 2026-10-26 13:25:51 +08:00
mektpoy 1a931813ec style(experimental): wrap perf headers and drop per-turn step counts 2026-10-26 13:25:51 +08:00
mektpoy 1c45663b8d style(experimental): left-align perf table values with their headers 2026-10-26 13:25:51 +08:00
mektpoy 9e081b7b6b feat(experimental): left-align perf headers and shorten the tool label 2026-10-26 13:25:51 +08:00
mektpoy e5ff00cc3e fix(experimental): frame team and subagent wake badges with readable senders 2026-10-26 13:25:51 +08:00
mektpoy 878b93f58a feat(experimental): rebalance perf columns and attribute wake senders 2026-10-26 13:25:51 +08:00
mektpoy dc63639968 feat(experimental): tighten the perf table columns and wording 2026-10-26 13:25:51 +08:00
mektpoy 3f4cb7b56d feat(experimental): preview prompt images from the performance table
Prompt nodes now carry the durable image references of a user message, not
just a count. The Question column renders an image-count pill in the trigger
badge's style; hovering or keyboard-focusing it opens a small preview strip,
and clicking a preview or the pill loads the image in the shared lightbox.
Image reads go through uiConversation.imageUrl, so the Conversation image
cache owns the browser URLs for the Session binding's lifetime.

Also bring the older-history specs in line with the surfaced failure state
already shipped at this branch tip, so the package suite is green again.
2026-10-26 13:25:51 +08:00
mektpoy f61130d203 fix(experimental): show the image count for image-only prompts 2026-10-26 13:25:51 +08:00
mektpoy 0960b5fbea fix(experimental): surface failed older-history pages 2026-10-26 13:25:51 +08:00
mektpoy f0d369053a fix(experimental): keep older-history loading retryable after a no-progress page 2026-10-26 13:25:51 +08:00
mektpoy 6d1822c449 fix(experimental): keep the sticky header flush with the scrollport 2026-10-26 13:25:51 +08:00
mektpoy a1707a4450 fix(experimental): opaque sticky header and prefetch the first older page 2026-10-26 13:25:51 +08:00
mektpoy 652ca2c3f8 feat(experimental): classify what opened each turn in the question column 2026-10-26 13:25:51 +08:00
mektpoy 6a932112e6 fix(experimental): hide compaction ghost turns and preview answers 2026-10-26 13:25:51 +08:00
mektpoy a43189a3cc feat(experimental): pretty-print and wrap the raw JSON inspector 2026-10-26 13:25:51 +08:00
mektpoy ccf7bec18c fix(experimental): ship Japanese metadata and the client style-gate conventions 2026-10-26 13:25:51 +08:00
mektpoy fa241b27d6 docs(experimental): document the fold helpers the builder exports 2026-10-26 13:25:51 +08:00
mektpoy 7c10f7c0e2 docs(experimental): record the paging and evidence behavior 2026-10-26 13:25:51 +08:00
mektpoy 5ebedd8e36 test(web): measure real timing columns in the panel golden 2026-10-26 13:25:51 +08:00
mektpoy 1671ea84f2 test(ui-trajectory): keep the bench off experimental view targets 2026-10-26 13:25:51 +08:00
mektpoy 6f545ff50e feat(experimental): scroll the performance table like Chat 2026-10-26 13:25:51 +08:00
mektpoy 2917b99d5c fix(experimental): attribute perf rows per Session and load history in place 2026-10-26 13:25:51 +08:00
mektpoy 9cd81a5dd3 fix(experimental): pass the client package-list and scrollbar gates 2026-10-26 13:25:51 +08:00
mektpoy c286ec70fa test(web): cover the opt-in Performance view in a real browser 2026-10-26 13:25:51 +08:00
mektpoy 65a8b625e2 chore(experimental): align client-ui-perf with the current base 2026-10-26 13:25:51 +08:00
mektpoy eab08fef4b test(client-ui-perf): reach the coverage bar for the performance view 2026-10-26 13:25:50 +08:00
Xu Hanxiang 160ba96793 feat(experimental): add client-ui-perf conversation view plugin 2026-10-26 13:25:50 +08:00
imccyu a3c3f107ef feat(storage): support compatible whole-unit versions (#6957)
* feat(storage): support compatible whole-unit versions

* fix(storage): contain failed writes

* test(storage): cover lock rejection and per-record version isolation

* docs(storage): align version rules with review findings

* fix(storage): unify versioned batch writes and deletion recovery
2026-10-26 13:22:25 +08:00
std4453 8e89080ba5 feat(desktop): 上传固定安装包后刷新 EdgeOne 缓存 2026-10-26 13:16:07 +08:00
Magolor 5e5b8bee6e fix(storage): unify versioned batch writes and deletion recovery 2026-10-26 13:04:41 +08:00
Magolor b412dc04ca docs(storage): align version rules with review findings 2026-10-26 13:04:41 +08:00
Magolor a5ce98cb82 test(storage): cover lock rejection and per-record version isolation 2026-10-26 13:04:41 +08:00
Magolor 88b0425dfc fix(storage): contain failed writes 2026-10-26 13:04:41 +08:00
Magolor 84210fa1e9 feat(storage): support compatible whole-unit versions 2026-10-26 13:04:41 +08:00
Dudu-0223 ef0e2b3461 fix(skills): clarify incomplete catalogs and pin root failure behavior 2026-10-26 13:02:08 +08:00
Yif 8668cf310a fix(client): address sidebar alignment review feedback 2026-10-26 12:57:11 +08:00
Turtle e409f6a7f8 Merge remote-tracking branch 'origin/master' into turtle/take-over-4719-fa0c7f5f8327
# Conflicts:
#	packages/fs/tool-fs-search/tests/integration.spec.ts
2026-10-26 12:56:58 +08:00
_Kerman faa2619933 Merge pull request #7045 from deepseek-harness/xtr/glob-prefix-fast-path
fix(fs): narrow glob traversal for literal directory prefixes
2026-10-26 12:52:32 +08:00
CreatixChu 6ccbacf0af Merge pull request #7218 from deepseek-harness/feat/cua-bundle-id-consent
feat(cua): 按 Bundle ID 或可执行路径保存 macOS 应用授权
2026-10-26 12:43:09 +08:00
_Kerman c51ec65921 docs(fs): clarify glob pruning safeguards and regression cases 2026-10-26 12:39:25 +08:00
creatixchu 886df319bc Merge origin/master into feat/cua-bundle-id-consent 2026-10-26 12:31:36 +08:00
Turtle 63a67f9ef1 Merge remote-tracking branch 'origin/master' into turtle/take-over-4719-fa0c7f5f8327 2026-10-26 12:31:19 +08:00
_Kerman af4ae072ce fix(trajectory): cap live tool results per session 2026-10-26 12:21:42 +08:00
imccyu 762418208a Merge pull request #7097 from deepseek-harness/worktree-ciapprove
feat(ci): run the merge checks from deepseek-harness-check
2026-10-26 12:16:11 +08:00
_Kerman f9ef03834a fix(fs): bound glob pruning with directory-name filters 2026-10-26 12:04:34 +08:00
_Kerman b1c8c9bbbc perf(fs): prune glob traversal using ripgrep directory filters 2026-10-26 12:04:34 +08:00
_Kerman 12f63eaf89 refactor(fs): resolve search workdir before building argv 2026-10-26 12:04:34 +08:00
_Kerman dfaf756199 docs(fs): make glob benchmark commands work on clean checkouts 2026-10-26 12:04:34 +08:00
_Kerman e8bdbbe823 fix(fs): preserve glob prefix path semantics 2026-10-26 12:04:34 +08:00
_Kerman 7253e123bd test(fs): benchmark glob through Native executor 2026-10-26 12:04:34 +08:00
_Kerman ef284800b9 fix(fs): narrow glob traversal for literal directory prefixes 2026-10-26 12:04:34 +08:00
_Kerman e3805406c4 Merge pull request #6763 from deepseek-harness/xtr/ptc-vm-script
feat(ptc-runtime-node): add VM Script execution with optional state retention
2026-10-26 11:52:18 +08:00
Turtle da3055de3e Merge pull request #7048 from deepseek-harness/turtle/plugin-detail-pages-5c09dd3d0a33
docs(experimental): move plugin detail text into descriptions
2026-10-26 11:39:55 +08:00
winewill 7484f5502a Merge pull request #6584 from deepseek-harness/feat/desktop-operation-diagnostics
feat(desktop): 启动故障诊断、报告获取与插件操作记录
2026-10-26 11:37:16 +08:00
_Kerman 315a1c7e47 style(test): align merged CUA scaffold options 2026-10-26 11:36:55 +08:00
imccyu 09b2bc7acf Merge pull request #6830 from deepseek-harness/design/floating-window-snap-back
feat(ui-dockkit): spring floating panels back inside the viewport
2026-10-26 11:34:38 +08:00
_Kerman 271a6143a1 Merge origin/master into xtr/ptc-vm-script 2026-10-26 11:34:32 +08:00
Turtle a9fe502214 Merge remote-tracking branch 'origin/master' into turtle/plugin-detail-pages-5c09dd3d0a33
# Conflicts:
#	snapshots/web/plugin-recommendations/session.v4.jsonl
2026-10-26 11:22:27 +08:00
_Kerman 5626ae2391 refactor(trajectory): own live result inspection in the feature plugin 2026-10-26 11:16:36 +08:00
winewill 5be33a66d2 test(desktop): isolate diagnostic fixtures and clarify record limits 2026-10-26 11:12:50 +08:00
winewill 6cbbc8caa5 Merge master and reconcile desktop diagnostic records 2026-10-26 11:05:45 +08:00
yudshj dcaaa11e5e fix(ui-dockkit): pick a panel up where it is drawn on a corner press too
A corner press during a return ended the return at the recorded
rectangle, so the panel jumped without pointer travel. The press now
picks the panel up where it is drawn for a resize as well: the resize
keeps the drawn origin and caps only the corner by the viewport, and the
release records the legal rectangle and springs the origin inside, as a
resize whose viewport shrank past its origin also does. Under reduced
motion a resize draws its legal rectangle and animates nothing.
2026-10-26 09:04:23 +08:00
yudshj 85812a9777 fix(ui-dockkit): settle float review findings on bodies and edge cases
- Move TabBody to its own module and draw DockSurface's docked bodies
  through it too, so divider and tab drags no longer re-render bodies.
- State in TabRenderer when the kit calls renderTab again and reuses a
  body.
- A corner press during a return ends it at the recorded rectangle; only
  a grip press picks the panel up where it is drawn.
- The resistance and its inverse keep the legal size, a resize origin
  stays inside a viewport that shrank mid-gesture, and a Windows root
  without a published caption height keeps the 20px floor.
- The Web e2e waits for the resting style before checking the clamp.
- Tests say "does not re-render" and also show that a new renderer
  re-renders the bodies.
2026-10-26 09:04:23 +08:00
yudshj 082b2b2c04 perf(ui-dockkit): keep tab bodies out of per-frame float re-renders
A float drag and its return update the preview through React state, so
TabLayout re-rendered every TabHost and called renderTab for every
retained body on each frame. Both renderers now draw bodies through
TabBody, memoized on the renderer and the tab record, so such a frame
re-renders only panel frames and headers.

Measured in the Web e2e scaffold under 4x CPU throttling (median of 5,
60-step drag past the corner, then the return): with a 4000-line file
preview floating, drag scripting fell from 592 ms to 100 ms and the
return's from 180 ms to 56 ms; with the default small preview, drag
scripting fell from 121 ms to 95 ms.
2026-10-26 09:04:23 +08:00
yudshj 22ea16f2e9 fix(ui-dockkit): clamp a resting panel's size into the viewport too
The resting .float rule now caps width at 100vw and height at the
viewport below the caption floor, so a stale viewport reading cannot
leave an oversized panel's header controls or resize corner off screen.
Vertical limits use 100dvh, which follows window.innerHeight as the
gesture hook does. The Web e2e publishes a far-off, oversized resting
rectangle and checks that the stylesheet alone draws it over the viewport.
2026-10-26 09:04:23 +08:00
yudshj 0fd4be4065 docs(notes): name the spring's rest predicate in the change scope 2026-10-26 09:04:23 +08:00
yudshj 38c1b9b835 fix(ui-dockkit): bound the edge return and keep clicks on projected panels
- A release that reaches the press's legal rectangle only raises, so a
  click on a panel drawn projected no longer records a move or resize.
- The return starts at the drawn panel's speed, and launchVelocity caps
  its outward part so the whole trajectory stays within 120 px.
- A panel larger than the viewport draws at the viewport's size, keeping
  its header controls and resize corner reachable.
- A return ends on an undo, dock or close of its panel, and at its target
  when another panel is pressed; a gesture uses the bounds of a window
  resized mid-gesture; drawn samples outside the velocity window drop.
- Gestures and returns may draw above the top floor; at rest the .float
  rule clamps every panel into the viewport and below the caption floor.
- Web e2e drags a Sidebar float past the right and bottom edges.
- The note is renamed to spring-based edge recovery, compressed, and
  cross-linked to the docking and retained-layout notes.
2026-10-26 09:04:23 +08:00
yudshj 7bfe4ad07f feat(ui-dockkit): spring floating panels back inside the viewport 2026-10-26 09:04:23 +08:00
yudshj 60ed3b2b74 docs(notes): propose force-directed edge recovery for floating dock panels 2026-10-26 09:04:23 +08:00
Turtle fbc30d138b Merge pull request #7266 from deepseek-harness/turtle/wrong-arch-dep-d8c6d0f056fb
fix(desktop): drop other targets' native packages from bundled pnpm
2026-10-26 05:09:04 +08:00
Turtle daf7a0cc43 refactor(scripts): share npm os/cpu matching for pnpm pruning
Move the release verifier's npm platform check into scripts/npm-platform.ts and use it when pruning pnpm's bundled packages, so negations, 'any' and invalid fields follow npm. Skip non-directories and name the manifest on invalid fields.
2026-10-26 03:25:12 +08:00
Turtle 44494c0c83 fix(desktop): drop other targets' native packages from bundled pnpm
pnpm's npm package carries reflink bindings for darwin-arm64, darwin-x64, win32-arm64 and win32-x64. The primary runtime copied all of them, so the Apple silicon app contained an x86_64 Mach-O module that triggers macOS's Intel app warning. Prune packages whose os/cpu fields exclude the target and bump the payload format.
2026-10-26 02:37:23 +08:00
imccyu 6d4faf5423 release(dsh): align master packages with 0.2.1-rc.4 2026-10-26 01:31:00 +08:00
imccyu 3014b0af0f Merge master into DSH 0.2.1-rc.4 synchronization branch 2026-10-26 01:29:12 +08:00
imccyu 341beb33c0 release(dsh): 0.2.1-rc.4 2026-10-26 00:57:37 +08:00
creatixchu bfc25c6cac fix(cua): expire handles after failed launch verification 2026-10-25 22:43:56 +08:00
creatixchu e2e18c340b fix(cua): describe missing process observations precisely 2026-10-25 21:38:24 +08:00
creatixchu de312c1e39 test(cua): separate deleted-app consent from OS operation failures 2026-10-25 21:30:04 +08:00
creatixchu 1f150869b3 docs(cua): regenerate optional-path API references 2026-10-25 21:25:27 +08:00
creatixchu d4a362a83b fix(cua): align optional paths with compiler interfaces 2026-10-25 21:24:30 +08:00
creatixchu 0ddd45c391 fix(cua): authorize bundle identities without executable paths 2026-10-25 21:22:40 +08:00
creatixchu d6a52d959c test(cua): exercise consent across standalone GUI launches 2026-10-25 20:52:38 +08:00
creatixchu a572bb4963 test(cua): use native normalized path for consent digest 2026-10-25 20:35:23 +08:00
creatixchu f63da6a3e6 feat(cua): allow persistent consent by executable path 2026-10-25 20:28:30 +08:00
Yichen Jiang 04b3164a27 Merge pull request #7211 from deepseek-harness/fix/model-collapse-preferred-width
fix(web): restore the composer model seat icon collapse
2026-10-25 20:26:49 +08:00
imccyu ca4c00de46 Merge pull request #7154 from deepseek-harness/session-stream-snapshot
refactor(session): separate history, state and Web follow implementations
2026-10-25 20:08:12 +08:00
imccyu 8b8d1a4942 test(session): call the renamed events read 2026-10-25 19:55:32 +08:00
imccyu e032fdc2a8 refactor(session): rename the history controller read to events 2026-10-25 19:54:41 +08:00
CreatixChu bf02b08da3 Merge pull request #5245 from deepseek-harness/feat/cua-permission-onboarding
feat(cua): 持久化应用授权并完善插件启用体验
2026-10-25 19:50:54 +08:00
imccyu 8cddf8b14c chore(docs): refresh event producer graph 2026-10-25 19:49:03 +08:00
creatixchu d8c9ac6a0f Merge master into feat/cua-permission-onboarding 2026-10-25 19:36:49 +08:00
imccyu 12a8c3adf7 docs(session): correct the events page and activation wording 2026-10-25 19:36:04 +08:00
creatixchu 06ebda4385 Revert "refactor(plugin-inventory): collect startup diagnostics concurrently"
This reverts commit c3d4f4bb70f685acdd289acb5d72472087e8377a.
2026-10-25 18:36:55 +08:00
creatixchu 075e9e4de7 refactor(plugin-inventory): collect startup diagnostics concurrently 2026-10-25 18:20:09 +08:00
imccyu 7c7b021766 docs(session): document events, deprecated page and activation order
Sync the Session README, implementation notes and follow upgrade guide,
then regenerate the API, subsystem, persistence and event-graph catalogs.
2026-10-25 18:14:32 +08:00
imccyu 99a6c14527 test(session): cover the split controllers and passive follow
Move the facade, listener, state, wait and history tests onto the new
controllers, and cover passive activation, omitted cursors and the
event subscription frames.
2026-10-25 18:14:28 +08:00
imccyu 985937265f refactor(ui-plan): read plans through the events page
A plan is a logged tool-call argument: read one page at the current log
end and page backwards from its cut instead of opening a follow snapshot.
2026-10-25 18:14:19 +08:00
imccyu 2f2caf88b3 feat(session): split history, state and web controllers
History, State and Web become ordinary classes sharing the Session scope.
Subscribe-before-read removes the tail repair, events and page expose the
cursor-bearing history read, observeEvent keeps a pure event subscription,
and follow stays passive unless activate is requested.
2026-10-25 18:14:18 +08:00
imccyu a1473ddec6 feat(gateway): expose pending remote events
Session state reads sample Gateway pending events; the Gateway contract now
provides getPendingRemoteEvents() for that read.
2026-10-25 18:14:17 +08:00
creatixchu 174b2dbe1b test(cua): clean up rejected launches before fixture readiness 2026-10-25 17:54:44 +08:00
creatixchu f7b2be051a fix(cua): recheck bundle metadata before launching approved apps 2026-10-25 17:51:48 +08:00
CreatixChu 7bc30c4cba fix(attachments): 复用完整图片引用,取消 Session 历史扫描 (#7021)
* test(api): measure historical image read costs

* fix(test): resolve image benchmark paths from its config

* fix(attachments): read stored images without scanning sessions

* test(attachments): complete by-id fixtures and access assertions

* fix(docs): implement image-id read on catalog fixture

* docs(attachments): refresh generated read contracts

* test(web): verify stored images across history and forks

* docs(api): clarify image read metadata and permission scope

* test(api): type benchmark instrumentation without unsafe returns

* test(api): retire unknown-cast allowances with removed history fixtures

* test(attachments): isolate browser cases and document store-wide access

* test(web): intercept the by-id thumbnail read path

* docs(perf): run generated diagnostic from its owning package

* refactor(attachments): reuse reference reads for conversation images

* docs(api): refresh complete image reference client contract

* test(ui-chat): assert complete image reference forwarding

* test(perf): measure image reads against current Session observations
2026-10-25 09:40:53 +00:00
creatixchu e876f69f56 fix(cua): launch the selected copy when bundle identifiers match 2026-10-25 17:10:11 +08:00
creatixchu d3f054c45b feat(cua): share macOS consent by bundle identifier 2026-10-25 16:46:31 +08:00
creatixchu e979223a76 fix(cua): scope detail separators to computer use 2026-10-25 16:38:11 +08:00
creatixchu 8860e05045 docs(cua): keep documentation changes focused on consent 2026-10-25 16:23:53 +08:00
creatixchu 31044bf469 revert(cua): remove unrelated Session presence recovery 2026-10-25 16:07:14 +08:00
creatixchu 57f2832347 revert(plugins): defer navigation scroll behavior 2026-10-25 16:01:24 +08:00
Dudu-0223 cb046fde2c fix(skills): isolate unreadable roots and publish available skills 2026-10-25 15:55:58 +08:00
creatixchu a87b643d87 style(cua): align permission separators and section spacing 2026-10-25 15:37:00 +08:00
Yichen Jiang bc5c2d4142 fix(web): cap the composer model seat at its preferred width
The composer collapses the model seat to its Models icon when the expanded
controls cannot share a row, but the seat's row-relative 45cqw cap made its
measured demand shrink with the row. The measured expanded state therefore
always fit and a narrow composer ellipsized the name instead of showing the
icon. Cap the seat at its 360px content width so the measurement sees the
demand, document the contract, and cover the collapse without the Plan chip.
2026-10-25 15:16:47 +08:00
Dudu-0223 c527f18146 test(subagent): use public composition APIs instead of scope primitives 2026-10-25 14:57:09 +08:00
Dudu-0223 6b24717b4a test(subagent): cover preset readiness and inherited restrictions 2026-10-25 14:30:53 +08:00
imccyu 10551d0e4e Merge pull request #7202 from deepseek-harness/worktree/sync-dsh-0.2.1-acp-stdout-tooltip-to-master
release(dsh): sync 0.2.1 ACP stdout and view-tab tooltip fixes back to master
2026-10-25 14:18:43 +08:00
creatixchu fc6e370457 Merge master after platform support landed 2026-10-25 13:41:24 +08:00
CreatixChu 620a185354 Merge pull request #6692 from deepseek-harness/feat/bundle-host-platforms
feat(plugins): 按 Host 平台限制组合包启用
2026-10-25 13:34:20 +08:00
imccyu 8ebf8a6218 Merge master into DSH 0.2.1 ACP stdout and view-tab tooltip sync
# Conflicts:
#	apps/cli/tests/profiles/acp/tests/stdout.expected.e2e.ts
#	packages/client/ui-conversation/README.i18n.yaml
#	packages/mcp/mcp-client/tests/protocol.spec.ts
2026-10-25 13:32:05 +08:00
Dudu-0223 3bf1435d7d docs(subagent): refresh module graph after scope dependency removal 2026-10-25 13:28:48 +08:00
Dudu-0223 90e398328d refactor(subagent): resolve preset references only when starting children 2026-10-25 13:22:35 +08:00
imccyu f425304f7b Merge pull request #7179 from deepseek-harness/fix/release-acp-account-stdout
fix(acp): keep account catalog diagnostics off stdout
2026-10-25 12:56:33 +08:00
Dudu-0223 0a618f13d5 test(subagent): scope preset round trips to their snapshot scenario 2026-10-25 12:24:04 +08:00
Dudu-0223 f78984a497 fix(subagent): share delegation tools within each preset revision 2026-10-25 12:17:00 +08:00
Tianyi Cui 940aafcadb test(acp): use shared cleanup for the stdout regression 2026-10-25 12:09:34 +08:00
Tianyi Cui 41736cdec5 fix(acp): keep account catalog diagnostics off stdout 2026-10-25 12:09:00 +08:00
imccyu cb7f0878e4 Merge pull request #7168 from deepseek-harness/worktree/acp-stdout-logging-audit-f95a78
fix(mcp): keep resource-listing diagnostics off ACP stdout
2026-10-25 12:05:36 +08:00
imccyu 72624bae95 fix(ui): show view tab tooltip only for overflowing labels (#7183)
Conversation view tabs added a tooltip to every label in 0.2.1-rc.3, including short labels that fit the 88px cell. Estimate the glyph width and only enable the tooltip when the label is expected to overflow; keep the More trigger tooltip.

Refs #7181
2026-10-25 12:05:02 +08:00
creatixchu cf65ee6e01 Merge master into bundle Host platform support 2026-10-25 11:58:20 +08:00
imccyu c38265a00e fix(ui): show view tab tooltip only for overflowing labels
Conversation view tabs added a tooltip to every label in 0.2.1-rc.3, including short labels that fit the 88px cell. Estimate the glyph width and only enable the tooltip when the label is expected to overflow; keep the More trigger tooltip.

Refs #7181
2026-10-25 02:48:52 +08:00
Yudong Han 92493ed592 Merge pull request #7164 from deepseek-harness/turtle/overlap-mask-b59b734b2a1a
fix(client-ui-conversation): fade overflowing header actions before the utilities
2026-10-24 22:52:27 +08:00
TurtleandYudong Han 2a2664cd21 chore(client-ui-conversation): regenerate slot catalog and README pairing
Co-authored-by: Yudong Han <[email protected]>
2026-10-24 22:27:58 +08:00
Yichen Jiang cc4446eecc fix(mcp): keep resource-listing diagnostics off ACP stdout
The MCP SDK answers resources/list and resources/templates/list for a
server without the resources capability with an empty page, but first
writes a console.debug line to stdout. Under the ACP profile that line
lands in the JSON-RPC stream whenever the model lists resources of a
client-supplied, tools-only MCP server. Return the same empty pages
before calling the SDK, and cover it with a unit assertion and an ACP
stdout regression test.
2026-10-24 22:25:48 +08:00
Yichen Jiang 5a1f5a657b fix(forge): follow the corner-shape rule and the README summary budget
- The checks dot in the Session list pairs its full-round radius with
  corner-shape: round, as the ui-theme stylesheet gate requires.
- The client-ui-forge README summary fits the 100-word budget again
  after the merge combined both branches' descriptions.
2026-10-24 22:12:18 +08:00
Yichen Jiang e4fd8a87ff Merge branch 'worktree/forge-pr-worktree-panel' into worktree/forge-session-list 2026-10-24 22:03:03 +08:00
Yichen Jiang 53d8a336f7 Merge commit 'bf1df01ae99' into worktree/forge-session-list
Brings the Session list branch up to date with the forge panel, whose
held messages now wait at inbox insertion.

- client-ui-forge keeps the Session list's remote-access import and the
  panel's messageOf; the agent-controller import is gone.
- The README and the forge panel Agent Note combine both sides: the
  forgeWorktree projection keeps its client view next to the inbox hold,
  and the Session list paragraphs stay.
- The config catalog is regenerated, and the README, forge subsystem
  page, and Agent Note pairings are recorded again.
2026-10-24 22:02:44 +08:00
TurtleandYudong Han 8b48ec1673 fix(client-ui-conversation): clip header actions without a scroll timeline
Fade overflowing actions inside the gap before the next header control and
clip with overflow: clip, so no animation stays attached to the header and
focus cannot scroll the title area.

Co-authored-by: Yudong Han <[email protected]>
2026-10-24 22:00:35 +08:00
Yichen Jiang bec0ac67ba Merge commit '45db0d28ce5' into worktree/forge-pr-worktree-panel 2026-10-24 21:45:54 +08:00
Yichen Jiang 3a52eb1993 Merge commit '861f5200ded' into worktree/forge-pr-worktree-panel
Master replaced the creation and plugin recommendation bundles with one
personalized recommendations bundle.

- The CLI manifest keeps master's bundle and the forge bundle.
- The plugin page expectation ranks the forge bundle before the held-back
  personalized recommendations bundle.
- The experimental README pairing is recorded again.
2026-10-24 21:45:41 +08:00
Yichen Jiang 91bd19ea98 Merge remote-tracking branch 'origin/worktree/composer-host-confirmed-echo' into worktree/forge-change-request-bindings 2026-10-24 21:42:23 +08:00
Yichen Jiang ba94a90da3 Merge commit '66a7e6cf07b' into worktree/forge-change-request-bindings
The experimental README pairing is recorded again for the merged
package rows.
2026-10-24 21:42:17 +08:00
Yichen Jiang b10ba84a0f Merge remote-tracking branch 'origin/master' into worktree/composer-host-confirmed-echo 2026-10-24 21:40:27 +08:00
Yichen Jiang 3764b60205 Merge commit '3dd445fe163' into worktree/composer-host-confirmed-echo
The session-controller README pairing is recorded again for the merged
sections.
2026-10-24 21:39:51 +08:00
TurtleandYudong Han 585fc2577a fix(client-ui-conversation): drop the header fade without scroll timelines
Co-authored-by: Yudong Han <[email protected]>
2026-10-24 20:48:24 +08:00
TurtleandYudong Han bf75167423 fix(client-ui-conversation): fade overflowing header actions before the utilities
Co-authored-by: Yudong Han <[email protected]>
2026-10-24 20:36:49 +08:00
Turtle 685b4fc5b3 test(ui-goal): pin the blocked goal strip and simplify resume visibility 2026-10-24 18:01:29 +08:00
Turtle 83644b6b37 fix(ui-goal): offer resume for a blocked goal
The goal service and /goal resume accept blocked goals, and editing keeps
the phase blocked, but the Web goal bar showed only edit and clear, so a
Web user could not continue a blocked goal after editing it.

Refs #1425
2026-10-24 17:26:02 +08:00
imccyu b9266aefd6 Merge pull request #7094 from deepseek-harness/worktree/ci-schedule
ci: cut PR check latency across the slow lanes
2026-10-24 14:58:40 +08:00
Turtle a2a5f832d8 Merge pull request #7137 from deepseek-harness/turtle/skip-option-repeat-a065821c4757
fix(tool-ask-user): tell the model a skipped question is answered
2026-10-24 14:08:58 +08:00
_Kerman e1e3f3a2c2 fix(trajectory): retain raw tool results only for live Sessions 2026-10-24 14:01:59 +08:00
Turtle d3da3b7640 fix(tool-ask-user): share skip guidance with the timed description
Refs #7134
2026-10-24 13:39:08 +08:00
Turtle 94ed636380 fix(tool-ask-user): tell the model a skipped question is answered
The blocking ask_user_question description did not explain that an answer
item with empty selected and no custom is a user skip, so models re-asked
the same question after the user clicked Skip.

Refs #7134
2026-10-24 13:23:53 +08:00
Turtle 376e00b639 docs(plugin-market): show the plus icon on the preview's Add button 2026-10-24 04:05:57 +08:00
Turtle e43acd19d1 docs(plugin-market): redraw the feature preview after the real Discover tab
The preview now shows an index URL being added and the Discover tab that lists its plugins, with the segmented Installed/Discover switch, index menu, search field, framed icons, and the Install, Installed, and Update available actions used by the real page.
2026-10-24 03:10:47 +08:00
Turtle 2099andTurtle c262a09fd0 fix(ui-primitives): keep backslashes in Windows-path Markdown links- #7098
* fix(ui-primitives): keep backslashes in Windows-path Markdown destinations

CommonMark reads \. in a link destination as an escaped dot, so
[x](D:\repo\.github\a.md) opened D:\repo.github\a.md. Destinations
that look like Windows paths now keep their authored text.

Fixes #7095

* fix(ui-primitives): cover relative paths and character references

Treat a backslash destination without a URL scheme as a Windows path
when it has no slash or a backslash before a letter or digit, keep
every backslash literal there, and still decode character references.
Pin the behavior in the Markdown file-links Web snapshot.

* chore: list micromark-util-decode-string in third-party notices

---------

Co-authored-by: Turtle <[email protected]>
2026-10-23 23:40:50 +08:00
Yif 88894be0db docs(skills): cover both alignment axes and adjacent headers 2026-10-23 23:22:00 +08:00
imccyu 92b011e57c Merge pull request #7105 from deepseek-harness/revert-6993-fix/summary-overflow-recovery
Revert "fix(compaction): retry oversized summaries with smaller prefixes"
2026-10-23 23:00:08 +08:00
imccyu 3081c71bab Revert "fix(compaction): retry oversized summaries with smaller prefixes" 2026-10-23 22:59:05 +08:00
CreatixChu 17b13383af fix(plugins): 默认隐藏 CUA 并保留已启用用户的管理入口 (#7068)
* fix(plugins): temporarily hide the CUA bundle

* test(plugins): update expectations for hidden CUA

* fix(cua): preserve management for enabled bundles

* test(cua): cover default-hidden activation and retained details

* test(cua): verify installed state survives disabling

* test(cua): use the native provider display name
2026-10-23 22:54:03 +08:00
Yif 6bd5d528e5 docs(skills): define shared vertical alignment references 2026-10-23 22:52:29 +08:00
1tops e54b1326f8 Merge pull request #6993 from deepseek-harness/fix/summary-overflow-recovery
fix(compaction): retry oversized summaries with smaller prefixes
2026-10-23 21:16:37 +08:00
Turtle b268c91066 docs(experimental): keep plugin descriptions to high-level behavior
Shorten the experimental plugin descriptions to what each plugin does and
how to use it, and keep the official-plugin catalogs unchanged.
2026-10-23 20:59:10 +08:00
Turtle faba7faa92 Merge remote-tracking branch 'origin/master' into turtle/plugin-detail-pages-5c09dd3d0a33 2026-10-23 20:49:27 +08:00
imccyu 22ddee3792 Merge pull request #7075 from deepseek-harness/worktree-chatgap
feat(chat): tighten the process-row gap to 4px
2026-10-23 19:48:59 +08:00
Yif 24137258f7 fix(client): nudge workspace toolbar controls left 2026-10-23 19:28:01 +08:00
Yif 86fb01a613 fix(client): align sidebar spacing and enlarge row action targets 2026-10-23 19:10:36 +08:00
_Kerman 22c2493322 feat(trajectory): remember raw result display mode 2026-10-23 18:40:21 +08:00
_Kerman 102b42d14a feat(session): persist canonical tool result values 2026-10-23 18:40:01 +08:00
ZiyaandZiyaZhang 0741cf26a5 fix(plugin): give personalized recommendations its own icon (#7071)
Co-authored-by: ZiyaZhang <[email protected]>
2026-10-23 10:16:43 +00:00
1tops f242b39905 test(build): clarify artifact dependency ordering 2026-10-23 18:13:05 +08:00
Yudong Han 02bffe2f6b Merge pull request #7054 from deepseek-harness/turtle/tab-scroll-animation-3b47b9bdbb50
fix(dockkit): continue the tab strip from its last offset on selection changes
2026-10-23 18:09:55 +08:00
imccyu 83a0433bc7 Merge pull request #7003 from deepseek-harness/session-baseline-split-6998
feat(typert): extract Session baseline cleanup and default resource resolvers
2026-10-23 18:06:28 +08:00
winewill efe44ee17f Merge pull request #6675 from deepseek-harness/perf/desktop-packaging-cache
perf(desktop): speed up signed packaging and align compression defaults
2026-10-23 17:56:11 +08:00
Turtle 4c4f88af86 fix(experimental): keep plugin risks in descriptions and bound skill catalogs
Add the remaining material risks to the experimental plugin descriptions,
serialize only English descriptions in the official-plugin catalogs so the
skill results stay below their previous size, and read description test
literals from the locale resources.
2026-10-23 17:43:22 +08:00
imccyu 048845f841 fix(typert): harden default resource declarations and sync contracts 2026-10-23 17:39:35 +08:00
winewill dba631dda6 docs(windows): preserve network settings when reinstalling runner 2026-10-23 17:38:55 +08:00
winewill a4aab8ec61 docs(desktop): consolidate cache lifecycle guidance 2026-10-23 17:18:48 +08:00
_Kerman 17ee469a1f test(ptc): refresh Script snapshots after merging master 2026-10-23 17:15:08 +08:00
winewill 0c3d6a1368 Merge remote-tracking branch 'origin/master' into perf/desktop-packaging-integration
# Conflicts:
#	.github/workflows/ci-windows.yml
#	apps/desktop/windows-signing/README.i18n.yaml
2026-10-23 17:10:54 +08:00
Turtle 91bc8630c4 test(snapshot): port unused-argument fixtures to current runtime context 2026-10-23 17:10:15 +08:00
_Kerman dc3754d252 Merge remote-tracking branch 'origin/master' into xtr/ptc-vm-script
# Conflicts:
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	packages/ptc-runtime/ptc-runtime-node/package.json
2026-10-23 17:09:31 +08:00
winewill 4007c4bd67 Merge pull request #7057 from deepseek-harness/fix/windows-runner-network-recovery
fix(desktop): 对齐发布 app ID 并恢复 Windows runner 网络依赖
2026-10-23 17:08:30 +08:00
Turtle d838d7ba79 Merge remote-tracking branch 'origin/master' into turtle/plugin-detail-pages-5c09dd3d0a33
# Conflicts:
#	packages/experimental/plugin-recommendations/README.i18n.yaml
#	packages/experimental/plugin-recommendations/README.md
#	packages/experimental/plugin-recommendations/README.zh.md
#	snapshots/web/creation-recommendations/session.v4.jsonl
#	snapshots/web/plugin-recommendations/session.v4.jsonl
2026-10-23 16:55:56 +08:00
winewill 46ce2b61e0 Merge remote-tracking branch 'origin/master' into perf/desktop-packaging-integration
# Conflicts:
#	docs/development.i18n.yaml
#	scripts/build.spec.ts
2026-10-23 16:53:18 +08:00
winewill cb60c52cea docs(desktop): clarify shared packaging cache maintenance 2026-10-23 16:48:11 +08:00
Turtle 7c385c957c Merge remote-tracking branch 'origin/master' into turtle/take-over-4719-fa0c7f5f8327
# Conflicts:
#	.agents/notes/implemented/feature/2026-07-19-model-facing-goal-tools.i18n.yaml
#	apps/web/tsconfig.json
#	packages/client/ui-tool/README.i18n.yaml
#	packages/client/ui-tool/README.md
#	packages/client/ui-tool/README.zh.md
#	packages/client/ui-tool/src/client/tool/models/raw-tool-call.ts
#	packages/client/ui-tool/tests/diff-card.client.spec.tsx
#	packages/client/ui-tool/tests/terminal-card.client.spec.tsx
#	packages/fs/tool-fs-search/README.i18n.yaml
#	packages/fs/tool-fs/README.i18n.yaml
#	packages/fs/tool-fs/README.md
#	packages/fs/tool-fs/README.zh.md
#	packages/fs/tool-fs/src/sandbox.ts
#	packages/goal/tool-goal/README.i18n.yaml
#	packages/goal/tool-goal/tests/tool-goal.spec.ts
#	snapshots/session/headless.snapshot.ts
#	tsconfig.host.json
2026-10-23 16:46:40 +08:00
1tops 8539e4d99c fix(build): align Host compiler heap with upstream 8 GiB limit 2026-10-23 16:38:14 +08:00
Xu Hanxiang bcbc3d37a5 Merge pull request #6748 from deepseek-harness/feat/liveshare-protocol
feat(liveshare-protocol): add the shared wire frames and identity contract
2026-10-23 16:37:26 +08:00
Yichen Jiang b33271247b Merge pull request #6102 from deepseek-harness/docs/long-table-filter
feat(docs): 为长表增加搜索、清空和固定表头
2026-10-23 16:34:21 +08:00
winewill 9c3e97676e fix(ci): report successful runner network fixture exit 2026-10-23 16:33:05 +08:00
Turtle 0b8eabaf66 fix(dockkit): record the strip offset when the strip unmounts
Reading scrollLeft in the layout cleanup also captures positions that fire no
scroll event, such as a restore the browser clamps to the current offset.

Refs #7049
2026-10-23 16:27:34 +08:00
imccyu 3c4a79cc1b chore(session): remove stray blank lines 2026-10-23 16:22:07 +08:00
imccyu b2b3bf4568 fix(typert): reject ambiguous and multiple default resource identities 2026-10-23 16:13:38 +08:00
mektpoy 09ccbf785f chore(release): carry liveshare-protocol to the rc.3 family version 2026-10-23 16:13:34 +08:00
mektpoy 65c20148c1 fix(liveshare-protocol): align the published files with the single entry
The package publishes only its root entry, so the constraint extras that
named the identity subpath and frame codec no longer describe its files.
2026-10-23 16:13:34 +08:00
mektpoy c682dfaa3b refactor(liveshare-protocol): publish one entry point
The root entry already re-exports the wire codecs, the identity contract, the
viewer carrier, and the viewer-state vocabulary, so `./protocol` and
`./identity` were aliases of subsets that every consumer had to mirror in the
tsconfig path list. Keep `.` alone, keep the mock provider in its source module
for tests, and bundle only the root.
2026-10-23 16:13:34 +08:00
mektpoy c34276ec5a refactor(liveshare-protocol): publish the mock identity provider on its own entry
A package that promises authentication-adjacent types must not hand production
callers a provider that fabricates principals from its public `./identity`
entry. The mock keeps its own `./identity/mock` export, the console face drops
it, and the entrypoint tests pin both halves.
2026-10-23 16:13:34 +08:00
mektpoy 774e618112 fix(liveshare-protocol): forward only the page's own origin
Matching a forwarded route on the pathname alone let any origin whose URL
carried `/api/present.host` or `/open-in-app/apps` reach the owner instead of
the destination it named. Resolve the request against the page base and forward
it only when the target origin is the page's own; every other request reaches
the fetch this carrier replaced.
2026-10-23 16:13:34 +08:00
mektpoy 0a0620abd5 docs(development): count the split compiler faces
The face layout paragraph still counted six split packages. The
liveshare-protocol split added a seventh, and this names which part of the
package stays on each leaf.
2026-10-23 16:13:34 +08:00
mektpoy 3f8f220ccd docs(experimental): align the liveshare ctx-key column
Every other row states the ctx key or an em dash; the protocol row said
'library and protocol — no ctx key' in both languages. It now uses the
same em dash as the other rows without a ctx key.
2026-10-23 16:13:34 +08:00
mektpoy fa579c64d4 docs(liveshare-protocol): point the viewer-state module at the package entry
The @module tag named a viewer-states subpath the package does not export.
The vocabulary is reached through the package root, so the tag now names
that entry.
2026-10-23 16:13:23 +08:00
mektpoy 81c3d89ab1 docs(liveshare-protocol): describe the current consumers and viewer entries
The summary claimed both packages import the package and named only
dsh-brand, while nothing imports it in this tree and cordis is a peer. It
now states the shared definitions and both dependencies without claiming
current importers. When to use it names the package root for the viewer
carrier and viewer-state vocabulary, and the Known Limitations anchor moves
ahead of its heading like the other sections.
2026-10-23 16:13:23 +08:00
mektpoy eab3bd4769 test(liveshare-protocol): round-trip the IPC fixtures with request tokens
The v1 host/worker fixtures carried no registrationToken or token, so a
legacy branch asserted them rejected and the optional-field case skipped
them. They now carry valid tokens and round-trip through their codecs. The
unknown access-mode fixture carries a token too, so its refusal is about
the mode rather than the missing identity. The unread required lists are
deleted; the optional lists now match what the parsers accept.
2026-10-23 16:13:23 +08:00
mektpoy 3a2a59a635 fix(liveshare-protocol): release a stream whose unreadable open reached the owner
A 200 open whose body was not a JSON object threw from readable() before the
abort listener was armed, so the page never sent stream-cancel and the owner
held the id until its own budget expired. The unreadable-open path now
cancels the id before rethrowing; the spec pins the cancel.
2026-10-23 16:13:23 +08:00
mektpoy 59ed57540b refactor(liveshare-protocol): name the offered capabilities in their type
LIVESHARE_CAPABILITIES was a runtime string array, so a peer could neither
check a capability name at compile time nor compare against the same union.
The tuple is now const and LiveshareCapability derives from it; the wire
values are unchanged.
2026-10-23 16:13:23 +08:00
mektpoy a64d6406f5 refactor(liveshare-protocol): decode a relay frame once
parseRelayFrame parsed the wire text for the lifecycle snapshot and again
for the envelope, and unsupportedFrameRefusal repeated both parses. One
JSON.parse now feeds the snapshot check, the envelope, and the refusal
branch, so the hot path decodes each frame exactly once.
2026-10-23 16:13:23 +08:00
mektpoy 6bebb9ef3f fix(liveshare-protocol): reject an empty tenant in the asserted actor
The wire parser accepted an empty tenant while the Relay's identity adapters
store a non-empty one and identityFromKey refuses an empty segment, so a
share ACL built from such an actor could never match a normalized entry.
Reject the unnormalized actor at both the Relay frame and IPC ingress.
2026-10-23 16:13:23 +08:00
mektpoy a4f851f028 test(liveshare-protocol): carry malformed share frames without unknown assertions 2026-10-23 16:13:23 +08:00
mektpoy ddd9cbc243 chore(build): split the liveshare protocol compiler faces
The package carried one Host-based tsconfig that both aggregates registered, so the browser carrier
was compiled under the Host face while the wire codecs that use Buffer were compiled under the same
config. It now follows the repository's double-face template: a solution-only package root, a Host
leaf (wire codecs, identity contract, shared vocabulary, and the root barrel that re-exports the
carrier), and a browser leaf for the viewer carrier and the pure types it shares. The two repository
aggregates register the matching leaf, and the carrier stays in the Host file list because the root
barrel re-exports it for the Relay's script generation.

The tsdown comment now states the two compiler faces and why only the Host face publishes runtime
modules.
2026-10-23 16:13:23 +08:00
mektpoy 202c196e00 fix(liveshare-protocol): declare the brand runtime dependency
The published declarations reference Branded from @deepseek-ai/dsh-brand, so an external consumer
must be able to resolve that package from the manifest. It moves from devDependencies to
dependencies; the lockfile records the importer under dependencies.
2026-10-23 16:13:23 +08:00
mektpoy 535c40d4b7 test(liveshare-protocol): gate the package at per-file coverage
The protocol package leaves the repository-wide coverage exemption and meets the same per-file 100%
gate as the other packages. The new specs cover the identity mock, the wire helpers and every
additive-field parser branch, both public entry points, and the carrier's forwarded-route paths; the
key parser drops an unreachable guard so its remaining branches are exercised.
2026-10-23 16:13:23 +08:00
mektpoy abb9d0c50f test(liveshare-protocol): neutralize provider-specific fixture subjects
The recorded frames no longer name a provider-specific subject prefix; the cases keep their shape
with a neutral subject id.
2026-10-23 16:13:23 +08:00
mektpoy e803c2647a fix(liveshare-protocol): reject an unparseable share expiry
An IPC share spec accepted any string as its deadline while the frame parser required a parseable
time, so a registration could carry an expiry no reader could enforce. Both parsers now apply the
same rule: a stated expiry must be a string Date.parse accepts.
2026-10-23 16:13:23 +08:00
mektpoy 90695b11a5 docs(liveshare-protocol): document identity normalization and the viewer entries
The key codec now states its precondition: a caller normalizes an identity first, because an
unnormalized empty tenant renders a segment the parser cannot read back. The viewer carrier and the
viewer-state vocabulary join the file map, reached through the package root rather than their own
subpaths, and the placeholder-tenant comment is one complete rule again.
2026-10-23 16:13:23 +08:00
mektpoy 605f4947e1 test(liveshare-protocol): reject invalid additive wire values
The new actor credential and share connection are untrusted wire inputs: a non-string credential and an unknown connection mode are refused, an absent connection parses as the relay default, and an absent credential keeps the Relay-asserted actor.
2026-10-23 16:13:23 +08:00
mektpoy 4800bbed30 feat(liveshare-protocol): reserve actor credentials and direct connections
Additive wire evolution: an actor may carry an owner-verifiable credential (type-checked here, verified by the owner, absent keeps today's Relay assertion), and a share spec may carry a connection that defaults to relay while a Relay that owns the data path refuses direct. Capabilities actor-credential and direct-connection declare support; the recorded frames carry both fields.
2026-10-23 16:13:22 +08:00
mektpoy a2c5d40e30 refactor(liveshare-protocol): share the permission facts between request and frame
TunnelShareSpec and ShareAccessStateFrame repeated the same six permission fields. They now extend one SharePermissions interface (the committed frame is the authority), with the upload switch optional in a request and required in a frame. The wire shape is unchanged, so the recorded frames replay byte for byte; the display-only and grouping fields plus the registration-token and request-id roles are documented in place.
2026-10-23 16:13:22 +08:00
mektpoy f1aca0df84 docs(liveshare-protocol): keep identity providers out of the protocol package
The identity contract and its fixtures named specific providers even though the package only describes the abstract triple. The JSDocs now say a deployment's adapter implements it, and every fixture and spec uses the neutral example provider while keeping the encoding, round-trip, and empty-field cases they pin.
2026-10-23 16:13:22 +08:00
mektpoy 590826f66d revert(liveshare-protocol): reset the wire protocol to version 1 and drop development history
The v2 upgrade guide and the fork-readonly normalization were development-period artifacts that never shipped, so the protocol returns to version 1: the guide triplet is deleted with its references, the withdrawn tier is no longer named anywhere (unknown tiers are rejected generically), and the README plus the version test state version 1. The file-upload capability and its additive field stay.
2026-10-23 16:13:22 +08:00
mektpoy 73df748c7f feat(liveshare-protocol): carry the viewer file-upload switch
The share spec gains an optional allowsFileUpload switch, the access-state frame always states it (an absent field reads as off), and the file-upload capability separates the field from the tier and fork switches. The wire version stays 2 because the change is additive; the upgrade guide says so and the recorded add-share frame names the new field.
2026-10-23 16:13:22 +08:00
mektpoy 8a5ee7bda9 fix(liveshare-protocol): keep the forking half of the withdrawn tier
Normalizing fork-readonly to view dropped the switch the withdrawn name implied when no allowsFork was present, so a legacy registration silently lost forking. Both parsers now set allowsFork true for that case, matching what the old name granted.
2026-10-23 16:13:22 +08:00
mektpoy 7139e5cd8d fix(liveshare-protocol): keep the protocol vocabulary off the client and normalize the withdrawn tier
viewer-states publishes only each condition's retry decision: HTTP statuses and error codes belong to the Relay and copy keys to the client, so the mismatched accessDeniedTitle key is gone with the wire discriminator. Both parsers read the withdrawn fork-readonly tier back as view while keeping allowsFork, matching the documented treatment, and the protocol version is pinned in a test. The coverage-exclusion comment now names this package's own deferred lane instead of another package's.
2026-10-23 16:13:22 +08:00
mektpoy eef30f8462 fix(liveshare-protocol): forward only real route segments
The carrier matched forwarded routes by substring, so /api/present.hostile and /x/open-in-app/apps-foo were posted to the owner as if they were the read routes. Match only where the route starts a path segment and ends at the pathname or a following slash, keeping the Relay base path prefix working; add hit, subpath, prefix-neighbour and query cases.
2026-10-23 16:13:22 +08:00
mektpoy d218ba88f0 fix(liveshare-protocol): keep the replaced fetch's receiver
The carrier called the captured page fetch as a plain function, so a browser fetch that checks its receiver would fail on every non-forwarded request. Call it with globalThis as the receiver, as the repository's other fetch wrappers do, and pin the receiver in a spec.
2026-10-23 16:13:22 +08:00
mektpoy ace985e94f fix(liveshare-protocol): move the wire protocol to version 2
The access-mode vocabulary changed from open|approval to open|selected without a protocol version change, so a peer could declare version 1 while refusing every value the other side sends. Version 2 states the break; the retired approval name stays refused rather than reinterpreted, and an upgrade guide records the peer upgrade order.
2026-10-23 16:13:22 +08:00
mektpoy a24cb73c34 refactor(liveshare-protocol): name the restricted access mode selected
The wire vocabulary now spells the allowlist-gated mode `selected` and the
closed enum is `open | selected`. The retired `approval` spelling falls outside
the enum, so both the share spec and the authority snapshot refuse it instead of
widening the share; the refused-frame fixture and the malformed-field case pin
that refusal.
2026-10-23 16:13:22 +08:00
mektpoy cecf36de05 chore(docs,lockfile): record the protocol package in the lockfile and generated catalogs
The lockfile gains the new workspace member, and the module graph and config
catalog list the package under its own experimental entry.
2026-10-23 16:13:22 +08:00
mektpoy 27ff469ea5 feat(liveshare-protocol): add the shared wire frames and identity contract
The owner bundle and the Relay must agree on the frames they exchange and on
how one viewer identity is keyed, without either depending on the other. This
package carries both halves: the owner/worker IPC frames, the gateway frames the
Relay forwards without interpreting, and the provider-neutral identity contract
with its loopback mock. `parseGatewayFrame` answers undefined for input this
build does not understand instead of failing the connection, and
`unsupportedFrameRefusal` turns such a frame into the refusal to send back.

The package is registered as a release member, a doc-standard library, a
tsconfig host and client program, and carries the coverage lane the wire codec
needs. Its Further Exploration list names the owner bundle and the Relay
without linking to packages this layer does not add yet.
2026-10-23 16:13:05 +08:00
gengruilin bc0069450f Merge remote-tracking branch 'origin/master' into docs/long-table-filter 2026-10-23 16:05:14 +08:00
imccyu 9d7cd67a51 test(session): cover cold attachment without workspace 2026-10-23 16:01:29 +08:00
imccyu 05f94de1f9 fix(typert): preserve parameter metadata in resource resolvers 2026-10-23 16:01:28 +08:00
imccyu 94464e2214 chore(session): sync baseline docs and test lint 2026-10-23 16:01:28 +08:00
imccyu fea3bcdbf4 feat(typert): support default resource resolvers 2026-10-23 16:01:28 +08:00
imccyu 0c23ad233d refactor(session-controller): remove unused inspection facade 2026-10-23 16:01:27 +08:00
Turtle c403ca4d77 fix(ui-conversation): end closed quoted folder tokens at the quote
A closed @"dir/" followed directly by text stays a folder reference, as in the sent-message rule; add a paste regression for the composer marks.
2026-10-23 15:57:24 +08:00
ZiyaandZiyaZhang d6d51be4cb feat(experimental): 把个性化页面和插件推荐合并为实验性插件「个性化推荐」 (#7034)
* feat(experimental): ship the two card experiments as one Personalized recommendations bundle

@deepseek-ai/dsh-personalized-recommendations inserts the action card
service, its browser half, Personalized pages and Plugin recommendations
with their card bodies under the existing row ids. As the only layer that
declares those ids, the Plugins page addresses and switches each row,
including the two shared card rows that were read-only while two bundles
inserted them.

The two experiment packages stop being bundles; the new bundle takes their
place in the official lists and the dsh installation, with the personalized
pages icon, localized title and description, and a feature preview of both
cards. The two feature rows describe what they do on the bundle page.

* fix(experimental): drop the model reason line from plugin recommendation cards

The reason follows the language of the user's question while the catalog
description below it follows the interface language, so the two lines could
differ in language and largely repeat each other; the answer already gives
the reason. The card keeps the catalog title and description, both in the
interface language, as its details target.

* test(web): refresh the card fixtures for the Personalized recommendations bundle

Both card replays enable the new bundle, so each Session lists both card
skills, and each skill's catalog lists Personalized recommendations in place
of the two former bundles. The plugin card goldens lose the reason line.

---------

Co-authored-by: ZiyaZhang <[email protected]>
2026-10-23 07:55:49 +00:00
imccyu 63021ce0b4 Merge pull request #7055 from deepseek-harness/fix/host-build-heap-8g
fix(build): raise Host compiler heap limit to 8 GiB
2026-10-23 15:53:25 +08:00
winewill a43a4bb9ee fix(desktop): supervise optional Windows runner network process 2026-10-23 15:50:31 +08:00
imccyu 0ce33089d9 fix(build): raise Host compiler heap limit to 8 GiB 2026-10-23 15:49:01 +08:00
Dudu-0223 92f6c1320b Merge pull request #7039 from deepseek-harness/fix/independent-session-forks
fix(session): 普通 fork 独立运行并保留子代理创建者语义
2026-10-23 15:35:17 +08:00
Turtle 7623307aaf fix(dockkit): continue the tab strip from its last offset on selection changes
Each tab host draws its own strip while selected, so every open, close, or
selection mounted a strip at offset 0 that then glided across the whole row
to the active chip. Record each pane's chip-box offset in TabLayout, jump a
new strip to it, and glide only the remaining distance; a first placement
with no recorded offset jumps.

Refs #7049
2026-10-23 15:33:11 +08:00
Turtle a3110727ca docs(experimental): keep material plugin risks in descriptions 2026-10-23 15:19:16 +08:00
Turtle 64f4efe31d fix(ui-conversation): decorate only whole folder tokens in the composer
A pasted file path such as @src/a.ts or @"dir/a b.md" styled its directory prefix as a folder reference.
2026-10-23 15:14:06 +08:00
Dudu-0223 6c4560253d docs(session): explain historical ordinary fork parents 2026-10-23 15:07:41 +08:00
winewill 02460a0eb4 fix(desktop): align release app ID examples with production 2026-10-23 15:06:25 +08:00
Turtle 39bc53fbce docs(experimental): move plugin detail text into descriptions
Experimental plugin detail documents keep only their feature preview; each
bundle's meta.description states what it does, how to use it, and its
material risks.
2026-10-23 15:03:51 +08:00
1tops a153557280 fix(snapshot): allocate long Windows workspace paths natively 2026-10-23 15:01:21 +08:00
Dudu-0223 cf072384b5 fix(headless): resume ordinary forks using subagent origin 2026-10-23 14:59:25 +08:00
Dudu-0223 30650824c9 Merge latest master and refresh persistence catalog 2026-10-23 14:55:33 +08:00
Xu Hanxiang a677aca248 Merge pull request #6986 from deepseek-harness/fix/agent-loop-series-start-prefix-cache
fix(agent-loop): keep the prompt prefix at a series start with unchanged text
2026-10-23 14:53:19 +08:00
Yichen Jiang 7fbdf4938a fix(forge): follow master's shipped languages and release version
- The panel and the forge bundle ship Spanish, French, German, Russian,
  and Portuguese titles and descriptions.
- Their versions follow the root release version 0.2.1-rc.3.
2026-10-23 14:51:41 +08:00
Yichen Jiang 6e9ff4c322 Merge remote-tracking branch 'origin/worktree/forge-change-request-bindings' into worktree/forge-pr-worktree-panel
The parent branch now carries master through #6426 again.

- Master moved the optional, experimental, and plugin page bundle lists
  into @deepseek-ai/dsh-official-bundles; the forge bundle joins the
  optional and experimental lists there.
- The CLI manifest, release families, and plugin page expectations keep
  both sides; the forge bundle ranks before the held-back recommendation
  bundles.
- The lockfile, the event matrix, and the experimental persistence
  catalog are regenerated, and the capability-seams and experimental
  README pairings are recorded again.
2026-10-23 14:51:28 +08:00
Dudu-0223 63d3504b14 test(web): assert ordinary fork identity without source parents 2026-10-23 14:50:06 +08:00
Dudu-0223 b096659245 fix(session): make ordinary forks independent of source lineage 2026-10-23 14:48:07 +08:00
Yichen Jiang 3159402734 fix(forge): follow master's shipped languages and release version
- The forge, forge-github, and forge-session packages ship Spanish,
  French, German, Russian, and Portuguese titles and descriptions.
- Their versions follow the root release version 0.2.1-rc.3.
2026-10-23 14:40:35 +08:00
Yichen Jiang 69e7682b6f Merge remote-tracking branch 'origin/worktree/composer-host-confirmed-echo' into worktree/forge-change-request-bindings
The parent branch now carries master through #6426.

- The root manifest keeps the forge packages and master's action cards.
- The event matrix and the experimental persistence catalog are
  regenerated, and the capability-seams and experimental README pairings
  are recorded again.
2026-10-23 14:40:17 +08:00
winewill 36dce6b596 fix(desktop): preserve operation identity and bound diagnostic shutdown 2026-10-23 14:36:04 +08:00
Yichen Jiang 948d572032 fix(web): follow master's locales and composer cases
- The Spanish, French, German, Russian, and Portuguese dictionaries name
  the sending composer.
- The device classification case expects the acceptance callback that a
  prompt submission carries.
2026-10-23 14:31:45 +08:00
Yichen Jiang 89538fb115 Merge remote-tracking branch 'origin/master' into worktree/composer-host-confirmed-echo
- The composer's primary button keeps master's rule that hides a denied
  action and draws the sending state inside it; a sending prompt stays
  interruptible through master's stopAction.
- The ui-conversation README pairing is recorded again for the merged
  sections.
2026-10-23 14:31:30 +08:00
1tops 70e678c492 fix(snapshot): bound padded workspace directory names 2026-10-23 14:22:41 +08:00
Magolor 1696816d2c refactor(time-context): restore browser zones with Projection (#6811)
* refactor(time-context): retain browser zones in Projection

* fix(time-context): clarify reads and recovery checks
2026-10-23 06:22:11 +00:00
creatixchu 205a465046 feat(plugins): hide unsupported bundles from default discovery 2026-10-23 14:10:57 +08:00
mektpoy 6e4eda77c8 Merge origin/master into fix/agent-loop-series-start-prefix-cache 2026-10-23 14:06:46 +08:00
Chinesezjc ab0116c629 Merge pull request #6200 from deepseek-harness/refactor/unify-remaining-time
refactor(time): fold the remaining duplicated time sites into dsh-util-time
2026-10-23 14:06:20 +08:00
mektpoy 6b9f7fcaa7 test(agent-loop): pin the unchanged-text series start in a recorded session
Add an authored headless scenario that appends a changed prompt and then replaces a surface node before the third step. The unchanged-rendering series start must keep both prompt versions and write no system/message event; the scenario fails against the pre-fix projection. Also tighten the tool-series-start assertions and make the prefix premise explicit.
2026-10-23 14:06:08 +08:00
mektpoy d5ee15acf2 docs(agent-loop): align series-start contracts with shipped behavior
Clarify the SystemPromptDecisionInput.startsSeries contract (a new request series no longer implies consolidation), record the tool-schema series-start exception consistently in the decision and consequences, and correct the complete-declaration attribution to the unsupported-route projection.
2026-10-23 14:06:07 +08:00
imccyu ff42d4c4ab Merge pull request #7016 from deepseek-harness/worktree/sync-dsh-0.2.1-rc.3-to-master
release(dsh): sync 0.2.1-rc.3 back to master
2026-10-23 13:52:29 +08:00
creatixchu 62b5391fe0 Merge remote-tracking branch 'origin/feat/bundle-host-platforms' into HEAD 2026-10-23 13:47:07 +08:00
creatixchu 4026b5f2b0 test(plugins): refresh the scoped recommendation recording 2026-10-23 13:46:34 +08:00
creatixchu cfb84472e8 test(plugins): bound recommendation replay to its voice-input fixture 2026-10-23 13:44:21 +08:00
winewill cce8300e15 fix(build): allow 6 GiB for host aggregate type checks 2026-10-23 13:40:20 +08:00
winewill 0c828117f8 fix(desktop): prioritize startup evidence in bounded reports 2026-10-23 13:39:57 +08:00
imccyu 5ba94b2c79 release(dsh): align master packages with 0.2.1-rc.3 2026-10-23 13:38:12 +08:00
imccyu 8a01b8360b Merge master into DSH 0.2.1-rc.3 synchronization branch 2026-10-23 13:38:11 +08:00
Ruilin Geng cd9ee0ee4c Merge pull request #6756 from deepseek-harness/fix/persistent-bash-input-mangling
fix(tools): disable history expansion in the persistent bash shell
2026-10-23 13:37:34 +08:00
Yudong Han ce333bc5ea Merge pull request #7004 from deepseek-harness/fix/shiki-go-backtracking
fix(client): 修复 Go 高亮正则回溯卡死
2026-10-23 13:32:47 +08:00
creatixchu 4f40057821 test(cua): locate the localized native provider switch 2026-10-23 13:28:16 +08:00
1tops 595f5a65f3 docs(compaction): clarify retry costs and safe integer validation 2026-10-23 13:27:31 +08:00
Yichen Jiang c4aa56ceee docs: record why the listing badge container is client_metadata 2026-10-23 13:26:17 +08:00
winewill 77ee0be32d fix(desktop): align diagnostic limits and recovery copy 2026-10-23 13:26:09 +08:00
creatixchu 3edd1935b9 test(cua): match sentence case setup dialog title 2026-10-23 13:25:39 +08:00
Yichen Jiang c92c81727c Merge remote-tracking branch 'origin/master' into worktree/model-badge-schema-fb132b 2026-10-23 13:23:05 +08:00
winewill dd746aa580 fix(desktop): preserve recovery failure while report is unavailable 2026-10-23 13:21:11 +08:00
creatixchu 8350194f86 Merge remote-tracking branch 'origin/feat/bundle-host-platforms' into HEAD 2026-10-23 13:20:22 +08:00
creatixchu d7e6274489 Merge platform review fixes and master into CUA permissions 2026-10-23 13:19:31 +08:00
creatixchu 2f94582263 fix(plugins): translate platform rejection in added locales 2026-10-23 13:17:32 +08:00
creatixchu 82bbf85894 fix(plugins): report unsupported bundle activation and address review 2026-10-23 13:15:05 +08:00
Turtle 09c74eca30 Merge pull request #7018 from deepseek-harness/turtle/docs/unify-plugin-lists-b09478de437d
docs(official-bundles): state the util data-list exception and list contracts
2026-10-23 13:14:44 +08:00
yudshj dbc7ae17dd fix(client): patch Go inline-struct rule at grammar load 2026-10-23 13:14:12 +08:00
Yichen Jiang 23c2ee5f73 feat(llm-deepseek): read model badges from client_metadata
Each /models entry now declares its selector badge under
client_metadata["deepseek-harness"].badge, keyed by the User-Agent product
token like client_requirements, so the public model fields stay unchanged
and later DeepSeek Harness fields join the same object. Every field under
client_metadata is ignorable: null at any level shows no badge, and a level
that is not an object drops only the badge with a diagnostic naming it.
2026-10-23 13:12:16 +08:00
creatixchu 2e0cc478e8 Merge master into bundle Host platform support 2026-10-23 13:12:09 +08:00
winewill 37d26dd098 merge: include upstream locale dictionary fixes 2026-10-23 13:05:17 +08:00
Ziya 0da2e7e48a Merge pull request #6631 from deepseek-harness/feat/plugin-recommendations
feat(experimental): 实验性插件「插件推荐」:在回答末尾推荐官方插件
2026-10-22 22:04:34 -07:00
winewill 1879b32f95 fix(desktop): preserve diagnostics across shipped locales 2026-10-23 13:03:30 +08:00
Turtle b11d3ac198 docs(official-bundles): limit the protected-list contract to its own refusal 2026-10-23 12:56:17 +08:00
imccyu edd79cc3b2 release(dsh): align master packages with 0.2.1-rc.3 2026-10-23 12:55:47 +08:00
imccyu 37faa05ffa Merge master into DSH 0.2.1-rc.3 synchronization branch 2026-10-23 12:55:47 +08:00
gengruilin 6e5cb19644 Merge remote-tracking branch 'origin/master' into fix/persistent-bash-input-mangling 2026-10-23 12:48:34 +08:00
Chinesezjc b001d7b275 Merge remote-tracking branch 'origin/master' into refactor/unify-remaining-time 2026-10-23 12:45:00 +08:00
Turtle 2d130c7e61 Merge pull request #5610 from deepseek-harness/turtle/image-cache-stale-0a243f674e12
fix(client): reload overwritten local Markdown images in new renders
2026-10-23 12:44:17 +08:00
Turtle 3264972ac3 docs(official-bundles): state the util data-list exception and list contracts
Group READMEs no longer place the bundle lists under boot/, util/ names its
data-only exception, PROTECTED_PLUGINS states what removing a name allows,
EXTENSION_BUNDLES is described as a ranking, and the package README records
the browser inlining constraint.
2026-10-23 12:42:11 +08:00
ZiyaZhang 935d9f178a Merge feat/creation-recommendations into plugin recommendation experiment
Takes master's move of the official plugin lists into
@deepseek-ai/dsh-official-bundles. The Plugins page files come from master,
and the plugin recommendations bundle follows the personalized pages bundle
in OPTIONAL_BUNDLES, MORE_BUNDLES and EXPERIMENTAL_BUNDLES.
2026-10-22 21:41:49 -07:00
ZiyaZhang beb23ac093 Merge remote-tracking branch 'origin/master' into pr-6635-ci
# Conflicts:
#	packages/client/ui-plugin-manager/src/client/PluginManagerPage.tsx
#	packages/client/ui-plugin-manager/src/client/presentation.ts
2026-10-22 21:37:10 -07:00
Turtle b6380c865b Merge pull request #7012 from deepseek-harness/turtle/unify-plugin-lists-b09478de437d
refactor(util): collect hardcoded official plugin lists in dsh-official-bundles
2026-10-23 12:31:19 +08:00
ZiyaZhang af711df6e4 test(web): refresh the card fixtures for the eight-language plugin catalog
The plugin skill's catalog lists every official plugin's title and
description in the eight shipped languages, and the page skill's catalog
lists the plugin recommendations entry with its new translations.
2026-10-22 21:30:04 -07:00
1tops 7444b201a1 Merge master locale fixes into summary overflow recovery 2026-10-23 12:27:55 +08:00
ZiyaZhang fee58ad80b feat(experimental): translate the plugin recommendation packages into the shipped languages
Master now ships Spanish, French, German, Russian and Portuguese, and its
locale gates require every package's plugin metadata and Client dictionaries
in those languages. Both plugin recommendation packages add them, the card
registers the new dictionaries, and the terms follow the card and Plugins
page translations.
2026-10-22 21:27:14 -07:00
ZiyaZhang 72c104e099 Merge feat/creation-recommendations into plugin recommendation experiment
Takes master 020a053a120 through the base, which ships Spanish, French,
German, Russian and Portuguese and requires them in every package. The page
card fixture takes the base's eight-language plugin catalog.
2026-10-22 21:26:27 -07:00
ZiyaZhang fd963e1cf3 test(web): refresh the page card fixture for the eight-language plugin catalog
The skill result lists official plugin titles and descriptions in every shipped language.
2026-10-22 21:22:06 -07:00
ZiyaZhang 00b5405e98 feat(experimental): translate the card packages into the shipped languages
Master now ships Spanish, French, German, Russian and Portuguese, and its locale gates require every package's plugin metadata and Client dictionaries in those languages. The four card packages add them and register the new dictionaries.
2026-10-22 21:22:05 -07:00
Turtle 7f324909a9 docs(official-bundles): describe the plugin manager lists precisely 2026-10-23 12:12:38 +08:00
Turtle 9b4aacb41a refactor(util): collect hardcoded official plugin lists in dsh-official-bundles
Move @deepseek-ai/dsh-official-bundles from packages/boot to packages/util
and move the plugin manager's protected plugin list and the Web plugin
page's hidden, ordered, and experimental bundle lists into it, so editing
any of these lists no longer touches app-boot or either plugin manager
package.
2026-10-23 12:12:38 +08:00
ZiyaZhang a34a4524e5 Merge remote-tracking branch 'origin/master' into pr-6635-ci 2026-10-22 21:12:11 -07:00
imccyu 5259c1e103 Merge master into DSH 0.2.1-rc.3 synchronization branch 2026-10-23 12:10:59 +08:00
imccyu b5fb03927e Merge pull request #7007 from deepseek-harness/07akioni/fix-master-locale-parity
fix: 同步 master 合并后的五种语言字典
2026-10-23 12:10:32 +08:00
07akioni dee4969d5f Merge branch 'master' into feat/desktop-operation-diagnostics 2026-10-23 12:03:55 +08:00
ZiyaZhang b5d4e93861 refactor(experimental): drop experimental from the plugin recommendation package names
The two packages keep their directories under packages/experimental and are
registered as experimental name exceptions:

- @deepseek-ai/dsh-plugin-recommendations
- @deepseek-ai/dsh-client-ui-plugin-recommendations

Loader row ids, the Remote service name, locale namespaces and the
enable_official_plugin action are unchanged. Generated catalogs are
regenerated, and both card Web fixtures record the renamed bundle in their
skills' plugin catalogs.
2026-10-22 21:02:23 -07:00
imccyu 00e06cfac9 release(dsh): align master packages with 0.2.1-rc.3 2026-10-23 12:02:11 +08:00
ZiyaZhang 30dafd2106 Merge feat/creation-recommendations into plugin recommendation experiment
Takes the card packages' new names and master up to e115cc98d65. The plugin
recommendation packages and their bundle rows now name
@deepseek-ai/dsh-action-cards and @deepseek-ai/dsh-client-ui-action-cards;
generated catalogs are regenerated, and both card Web fixtures record the
renamed bundles in their skills' plugin catalogs.
2026-10-22 20:58:11 -07:00
07akioni e1d9697f6c test: match the neutral task cleanup confirmation title 2026-10-23 11:57:29 +08:00
imccyu 14f0b2aafe Merge master into DSH 0.2.1-rc.3 synchronization branch 2026-10-23 11:56:34 +08:00
ZiyaZhang 57195d1934 refactor(experimental): drop experimental from the card package names
The four packages keep their directories under packages/experimental and
are registered as experimental name exceptions:

- @deepseek-ai/dsh-action-cards
- @deepseek-ai/dsh-client-ui-action-cards
- @deepseek-ai/dsh-creation-recommendations
- @deepseek-ai/dsh-client-ui-creation-recommendations

Loader row ids, plugin record names, message source kinds, the Remote
service name and locale namespaces are unchanged. Generated catalogs are
regenerated, and the page card Web fixture records the renamed bundle in
the skill's plugin catalog.
2026-10-22 20:48:18 -07:00
1tops dd87277afa docs(compaction): clarify summary overflow recovery contracts 2026-10-23 11:47:56 +08:00
07akioni 0926f361b2 fix: align task cleanup copy across locales 2026-10-23 11:46:17 +08:00
Chinesezjc 957bf0f925 Merge remote-tracking branch 'origin/master' into HEAD 2026-10-23 11:42:08 +08:00
ZiyaZhang 0d97af58b5 Merge commit 'e115cc98d65' into pr-6635-ci 2026-10-22 20:40:34 -07:00
1tops 237fbc02ac Merge master into fix/summary-overflow-recovery 2026-10-23 11:39:37 +08:00
07akioni 3ae19c15a9 fix: keep clear confirmation counts grammatically neutral 2026-10-23 11:33:25 +08:00
imccyu 1f81c047fc Merge pull request #6999 from deepseek-harness/worktree/release-dsh-0.2.1-rc.3
release: next version [email protected]
2026-10-23 11:29:36 +08:00
07akioni 84b028655b fix: synchronize new locales with current client dictionaries 2026-10-23 11:29:25 +08:00
imccyu b049cd257c Merge pull request #6980 from deepseek-harness/07akioni/more-locales
feat(i18n): add Spanish, French, German, Russian and Portuguese
2026-10-23 11:22:55 +08:00
Chinesezjc 7bf0640c13 Merge pull request #6734 from deepseek-harness/feat/schedule-clear-ended
feat(schedule): remove every ended task from the automation page
2026-10-23 11:20:28 +08:00
Yichen Jiang 439783d38f Merge remote-tracking branch 'origin/master' into worktree/model-badge-schema-fb132b 2026-10-23 10:37:32 +08:00
1tops b5b1b5f0e5 fix(snapshot): resolve native Windows paths before sizing cwd 2026-10-23 10:33:51 +08:00
Turtle b3189f6fad Merge pull request #6804 from deepseek-harness/turtle/subagent-picker-row-align-5fdaf8a5d12d
fix(web): align subagent switcher rows without a sidebar button
2026-10-23 10:31:22 +08:00
Turtle 918c01c1bf Merge pull request #6889 from deepseek-harness/turtle/menu-trigger-gap-a6ac85a85cd9
fix(ui-primitives): keep header menus next to their trigger on macOS desktop
2026-10-23 10:31:07 +08:00
Turtle 153b760fe8 Merge branch 'master' into turtle/subagent-picker-row-align-5fdaf8a5d12d 2026-10-23 10:19:27 +08:00
Turtle a0cbe9331c Merge pull request #6887 from deepseek-harness/turtle/trace-tab-indicator-clip-bede5ab93d0f
fix(ui-conversation): keep the active tab bar visible over Trajectory and widen tab targets
2026-10-23 10:14:02 +08:00
Turtle af5ab40ac5 Merge remote-tracking branch 'origin/master' into turtle/menu-trigger-gap-a6ac85a85cd9
# Conflicts:
#	packages/client/ui-primitives/README.i18n.yaml
#	packages/client/ui-primitives/README.md
#	packages/client/ui-primitives/README.zh.md
2026-10-23 10:01:13 +08:00
Turtle 424cde1de2 Merge pull request #6950 from deepseek-harness/turtle/preview-codemirror-slow-e4bae59874fa
fix(web): speed up lazy bundle loads in the Web preview
2026-10-23 09:56:19 +08:00
Shuhan Lin cd6187f901 test(compaction): stabilize overflow snapshots and cover entry points 2026-10-23 09:53:08 +08:00
Turtle 06f960d705 Merge pull request #6984 from deepseek-harness/turtle/hover-truncated-text-e74d638a44b5
feat(ui-chat): show cut-off composer statistics in a tooltip
2026-10-23 08:40:51 +08:00
imccyu d02ea370ee release(dsh): 0.2.1-rc.3 2026-10-23 01:11:35 +08:00
ZiyaZhang 63c46af8b4 test(web): refresh the plugin card fixture after the approval prompt wording change 2026-10-22 09:27:46 -07:00
ZiyaZhang 39fa48b074 Merge feat/creation-recommendations into plugin recommendation experiment
Takes current master through the base, including the runtime-context
approval sentence that recorded Web fixtures replay against.
2026-10-22 09:27:46 -07:00
ZiyaZhang 3801502461 test(web): refresh the page card fixture after the approval prompt wording change
Master now describes enabled approval prompts in the runtime context; the fixture records the new sentence.
2026-10-22 09:17:59 -07:00
ZiyaZhang 1e9600be15 Merge remote-tracking branch 'origin/master' into pr-6635-ci 2026-10-22 09:14:14 -07:00
ZiyaZhang b62dfa2d63 feat(experimental): invite to plugin cards only from the accepted call's result
The model sometimes wrote the skill's invitation sentence without calling
propose_action, so users read "click below" with no card. The plugin kind
now registers its invitation, which only an accepted call's result carries:
"Card shown below your answer. Do not restate it. End with at most one short
invitation in the user’s language to click Enable below; do not say the
plugin is already on." The skill drops its invitation sentence and says the
call's result tells how to end the answer. A test runs a real turn and checks
that the accepted result carries the invitation and a refused call does not;
the plugin Web fixture is refreshed for the new skill and result text.
2026-10-22 09:04:47 -07:00
ZiyaZhang d9b95adf93 Merge feat/creation-recommendations into plugin recommendation experiment
Takes the card invitation that only an accepted call's result carries. The
page card fixture keeps the base's skill and result text and the plugin
recommendations entry in its catalog. The plugin kind gains its invitation
in the next commit.
2026-10-22 09:03:38 -07:00
ZiyaZhang 969b4bd5f2 feat(experimental): give the card invitation only in the accepted call's result
The model sometimes wrote the skill's invitation sentence without calling
propose_action, so users read "click below" with no card. Each card kind
now registers its invitation, and only an accepted call's result carries
it: "Card shown below your answer. Do not restate it. <invitation>".
The personalized-pages skill drops its invitation sentence and says the
call's result tells how to end the answer; the Web fixture is refreshed
for the new skill and result text.
2026-10-22 08:57:29 -07:00
Tianyi Cui 5219b36110 Merge pull request #6498 from deepseek-harness/turtle/approval-never-wording-7e59b56e7f5c
fix(user-approval): describe approval policies by meaning in model-visible text
2026-10-22 23:47:16 +08:00
imccyu 3725e75edd Merge pull request #6991 from deepseek-harness/worktree-sync-dsh-0.2.1-to-master-1022
release(dsh): sync 0.2.1 navigation and model listing to master
2026-10-22 23:39:15 +08:00
imccyu 261f2db185 Merge master into DSH 0.2.1 navigation and model listing sync 2026-10-22 23:22:35 +08:00
ZiyaZhang 38c05d24b2 fix(experimental): confirm unconfirmed plugin cards before Not now
An enable cut off by a plugin reload can still land. The kind now uses the
shared reconcile step: before every action on an unconfirmed card, including
Not now, it checks the profile and records enabled when the plugin is on, so
the card is not dismissed or enabled again.
2026-10-22 08:17:48 -07:00
ZiyaZhang dbe0eb3aa2 Merge feat/creation-recommendations into plugin recommendation experiment
Takes the action-card fixes: kinds reconcile before every action, only live
roots get cards, the tool refuses while messages are queued, and bodies no
longer receive ready. The plugin card drops ready, its queued-card test goes
with the suppressed phase, and the shared-row limitation follows the base's
new limitations.
2026-10-22 08:16:20 -07:00
ZiyaZhang cb425d1384 fix(experimental): settle plugin cards from what enabling reports
- An enable that a plugin reload cuts off records unconfirmed; the card
  offers a retry and Not now, and a retry that finds the plugin on records
  enabled because this card's change landed.
- A rejected profile change records failed with operation-error and keeps
  the retry instead of failing the click; overridden and cancelled are gone
  because enabling a bundle only reports applied, restart-required or failed.
- A refused or lost catalog lookup keeps Not now and offers Try again, with
  separate copy for each; a settled card keeps its outcome after its plugin
  leaves the catalog. Only the clicked button shows Working, the details
  label is interpolated, and the reason and description stay its
  accessible description.
- The Host entry exports its inject list, so the configuration catalog lists
  its services; the skill keeps its rules without the examples.
- With both experiment bundles on, the shared action-card rows cannot be
  switched or configured on the Plugins page; the READMEs say so and a
  Loader test pins it while the bundles still switch.
- The unused intent fixture is removed and the evaluation set expects a
  create_personalized_page card for the counter.
2026-10-22 08:13:22 -07:00
Tianyi Cui 035fcb88dc Preserve SDK approval wording fixtures after rebase
Co-authored-by: Tianyi Cui <[email protected]>
2026-10-22 23:13:19 +08:00
TurtleandTianyi Cui 747cbf13b6 Describe the ask approval policy by its meaning
Replace the literal "Approval policy: ask." runtime-context sentence
with a statement parallel to the never-policy sentence, and refresh the
current-generation fixtures that persist it.

Co-authored-by: Tianyi Cui <[email protected]>
2026-10-22 23:13:19 +08:00
TurtleandTianyi Cui 43149cd905 Sync approval switch notice snapshot and docs
Update the Web permission-policy snapshot, the config catalog line
reference and README text for the new switch notice; pin both switch
directions in the unit test.

Co-authored-by: Tianyi Cui <[email protected]>
2026-10-22 23:13:19 +08:00
TurtleandTianyi Cui c29cda3777 Clarify approval policy switch notice wording
Name each policy by its meaning in the model-visible switch notice so a
bare "never" is not read as "approval is never granted".

Refs #6496

Co-authored-by: Tianyi Cui <[email protected]>
2026-10-22 23:13:19 +08:00
ZiyaZhang 9fd47804c2 fix(experimental): reconcile action cards before Not now and offer them to live roots only
- Kinds can reconcile a card before every user action, including Not now;
  the page kind confirms uncertain runs against the installed profile, so an
  installed page keeps Undo instead of being dismissed.
- Only a live root's direct skill load activates propose_action; calls from
  subagents or from code are refused.
- The tool refuses while queuedMessageLimit messages wait, so the model is
  never told a hidden card was shown; the suppressed phase is gone and the
  projection moves to state version 2.
- record() refuses actions no kind registered and the derived replaced phase.
- Not now dismisses an unstarted card whose kind is no longer registered.
- The unused ready prop and dead locale keys are removed, and the READMEs
  state the card and row rules as implemented.
2026-10-22 08:01:23 -07:00
Xu Hanxiang 70e2d31cc3 Merge pull request #6486 from deepseek-harness/feat/client-extension-points
feat(liveshare): add client and host seams a served read-only page needs
2026-10-22 22:57:27 +08:00
07akioni dcbcee9469 test(i18n): retain fallback coverage for unknown speech languages 2026-10-22 22:50:18 +08:00
Shuhan Lin cf7229e72a fix(compaction): retry oversized summaries with smaller prefixes 2026-10-22 22:43:32 +08:00
07akioni 07ef8a20b8 Merge master and sync plugin index translations 2026-10-22 22:24:15 +08:00
ZiyaZhang ad482a45ec test(web): replay the plugin card through propose_action
The plugin fixture now offers the card with a propose_action call, the
tool schema pin records the tool addition after the skill load, and the
card goldens name the region after the card title as the shared frame does.
The page fixture's skill catalog lists the plugin recommendation bundle.
2026-10-22 07:18:05 -07:00
ZiyaZhang fcb7340be1 refactor(experimental): register plugin recommendations as an action card kind
Plugin recommendations now proposes through the shared propose_action tool
instead of a dsh-action answer block. The Host registers the
enable_official_plugin kind with the action card service: the
official-plugins skill, details holding the plugin name, the Enable verb
with global access, and the enabled, already-enabled and failed phases,
whose data carries the application outcome and the management error code.
The package drops its own projection, Remote act, status notice and
persisted record; it keeps the describe Remote for card text. The bundle
inserts the shared card rows beside its own, as Personalized pages does.
The browser half registers a card body in the shared frame that keeps the
catalog text cache, the reserved loading lines, the single details target
and the Enable and Not now footer. The skill's submission section now asks
for one propose_action call.
2026-10-22 07:18:05 -07:00
mektpoy 76e39794eb revert(ui-session,ui-conversation): let the stop shortcut reach the Host
A reviewer blocked the one-shot boolean access read the stop shortcut used:
the client should not expose a render-time access answer as a boolean API.
The Esc-Esc sequence submits its cancel again, and a Host refusal surfaces as
the composer failure the Stop button would have prevented. Send keeps the same
treatment: the access snapshot withholds the buttons, the Host refuses the
request, and the composer reports it.
2026-10-22 22:16:06 +08:00
07akioni 1552f524f7 fix(i18n): address translation, installer and coverage review findings 2026-10-22 22:14:47 +08:00
mektpoy 80ce0669e9 revert(ui-conversation): leave a withheld submission to the Host
The access snapshot still withholds the Send button, Stop, and file intake, and
the Esc-Esc stop path still reads its own answer before sending a cancel. A
keyboard submission, however, once again reaches the Host, which refuses it and
reports the failure to the composer; gating that path needs a larger redesign
of the composer keyboard face.
2026-10-22 21:57:49 +08:00
ZiyaZhang c6d9a964d3 Merge feat/creation-recommendations into plugin recommendation experiment 2026-10-22 06:50:25 -07:00
Chinesezjc 583f6b529c test(ui-schedule): cover closing the cleanup menu 2026-10-22 21:46:09 +08:00
mektpoy d9c012551a revert(client): return the fork seam to master
ui-chat goes back to origin/master as a whole package: the footer branch forks
through ctx.sessions.fork with its own atSeq and analytics again, and Workspace
navigation takes no ForkSessionOptions. The package is identical to
origin/master, which drops the served-page branch assertions and README lines
added on this branch; they pinned behavior master already has.

ui-workspace restores master's forkSession signature and implementation
(allowMigration only, increaseTitle: true, selection untouched) and drops the
ForkSessionOptions type. uiSession leaves the inject list because the removed
session/rename probe was its only consumer. The row fork keeps passing
{ allowMigration: false }, which master already does, so forking from a shared
page never starts source-format migration.

Regenerate the client API catalog, restore the README fork contract (fork does
not navigate; allowMigration carries migration permission), re-record the
translation pairing, and return the ui-chat unknown-cast baseline count to 2.
2026-10-22 21:44:25 +08:00
mektpoy 58785b975e fix(ui-workspace): name every branch and open it on request
`increaseTitle` renames the child, not the source, and whoever may fork a Session also holds `session/rename` on the child it creates; the source's access answer therefore never decides the child's title. Remove the source-rename probe, the listed-branch wait, and its refresh-failure notice: `selected` opens the child immediately and `listed` leaves the selection alone. Update the public READMEs and the client API catalog.
2026-10-22 21:44:25 +08:00
mektpoy 0b72a81411 chore(docs): re-record the conversation README pairing after the master rebase 2026-10-22 21:44:25 +08:00
mektpoy 3ecac5aad4 chore(catalogs): follow the branch contract and Session access edits
Regenerate the client API catalog after the forkSession contract and the
ui-workspace gating changes.
2026-10-22 21:44:25 +08:00
mektpoy d985f44124 chore(ui-chat): correct the branch-rejection test comment
A rejected branch leaves the source view unchanged, as master does; the old
comment described the removed notice.
2026-10-22 21:44:25 +08:00
mektpoy 3bcb22f732 fix(ui-conversation): gate keyboard submission and stop
Enter, Mod+Enter, and accelerated queue steering now read the same prompt
answer as the Send button, and the fixed Esc-Esc sequence reads the live
session/cancel answer before it consumes a press. The composer locks for a
denied prompt, the README states both paths, and tests pin each gesture.
2026-10-22 21:44:25 +08:00
mektpoy cfc2fd926e fix(ui-workspace): route row gates through the Session service
Read the branch-naming answer through uiSession.access instead of the
cross-kind source, drop the unused openListedBranch method, give the branch
list refresh its own notice, and gate the search result restore control like
the archive row. The forkSession contract now states its navigation and throws
behaviour, present documents its 2x2 landing table, and the README describes
the real branch policy and row gating.
2026-10-22 21:44:25 +08:00
mektpoy f0fcd68b7b feat(ui-session): answer Session access for JS callers
The Session kind owns its remoteAccess source, so other plugins get one
non-Hook read from this service instead of reading the source themselves.
Navigation uses it for the branch-naming answer.
2026-10-22 21:44:25 +08:00
mektpoy b1f10381cb fix(ui-deliverables): return the package to master
The owner-baseline gate this branch added has no reader on the master access
API, and the reader page reads files through the Host route instead.
2026-10-22 21:44:25 +08:00
mektpoy 491515268a chore(docs): regenerate the cordis catalog after the frontend-static simplification 2026-10-22 21:44:25 +08:00
mektpoy c3f2fbbdad chore(client): follow the merged seam records in generated artifacts
The client inspect catalog follows the seam signatures, the unknown-cast
baseline drops the two casts this branch retired, and the README pairing
records match the merged prose.
2026-10-22 21:44:25 +08:00
mektpoy 55deb7a6c6 fix(ui-workspace): keep the per-resource access gates on the whenAccess client
Master's rows and menus carry no answer of their own, so the served-page seams
thread the Slot-standard access Hooks through the workspace browser and the
session actions: New Session, workspace rename/delete/reorder, and a Session's
rename, pin, archive, and reorder appear only while their own resource allows
them. The rows spec keeps master's pointer-armed rename helper and supplies the
allow-all answers as props.
2026-10-22 21:44:25 +08:00
imccyu 0d521bdd0d refactor(client): drop owner-era leftovers and gate sidebar New Session on session/create
- The Conversation binding tracking returns to master: the retainInfo subscription and the
  plugin-owned binding effects are a separate fix, not a served-page seam.
- The deliverables opener drops the ownerHostRoutes parameter no caller passes any more.
- Assertions on existing connection and HMR behavior, the menu-open-state spec, and the
  settings-general roster splice return to master.
- Workspace shortcuts no longer re-read Session access through source(); the Host refuses a
  denied rename, fork, or archive.
- The sidebar New Session button, the window-chrome New Session control, and the expanded
  brand shortcut render only while useRemoteAccess('session/create') allows it.
2026-10-22 21:44:25 +08:00
imccyu a154f70ab9 refactor(client): read composer access through useAccess and drop the branch-refusal notice
- The composer decides file intake from the Session-scope useAccess('fileUploads/upload')
  answer, like Send and Stop. The optional fileIntake hook seat, the InputHub fileIntake
  entries, and the ui-slots optional-hook typing are removed.
- The fixed Stop shortcut no longer re-reads session/cancel through a shared stop; the
  button stays hidden by useAccess and the Host refuses a withdrawn cancel.
- uiConversation.noticeBranchRefusal, branch.ts, ForkRefusal, and message.branchRefused are
  removed; a failed Chat branch leaves the source view unchanged, as on master.
2026-10-22 21:44:25 +08:00
mektpoy e30684cf9c fix(client): keep the served-page records on the master access API
The owner-baseline gating commits are superseded by master's whenAccess seams,
so the remaining records revert to the master shapes: the deliverables opener
takes no owner answer, the Japanese dictionaries carry the surviving copy, and
the test splice and owner assertions match the current services.
2026-10-22 21:44:25 +08:00
mektpoy 82ed6eee99 fix(ui-chat): narrow the injected Chat View without an unknown assertion
Read the slot injection through its real rest-parameter function type and invoke it directly, so the static gate sees no new assertion to unknown. The client inspect catalog's expected uiWorkspace signature follows the generated catalog after the fork options became ForkSessionOptions.
2026-10-22 21:44:25 +08:00
mektpoy b191c5c865 revert(session-controller): restore the package to master
P1 keeps only the client/host extension seams; the requestId deduplication,
the cancel recheck, and the named fork child are deferred to a later change.
The package, its README pair, and the persistence schema lines return to the
master state, so the branch adds no session-controller behaviour.
2026-10-22 21:44:25 +08:00
mektpoy 1624faeb23 fix(ui-conversation): keep the released-binding and shared-stop edges
Type the tracked binding disposers by their real shape and let a disposed
context's shared stop surface its own loud unavailable error instead of
reporting a withdrawn permission.
2026-10-22 21:44:25 +08:00
mektpoy fbc90909b1 fix(ui-conversation): gate the fixed stop shortcut through the shared stop
The button re-read the live session/cancel answer but the fixed shortcut
received the raw stop, so a withdrawal still sent Esc-Esc cancellation. Build
one gated stop used by both entry points and cover the shared gate.
2026-10-22 21:44:25 +08:00
mektpoy 938e82484f fix(ui-conversation): narrow a fork rejection before reading its code
A rejection may be null, undefined, or any non-object; reading rpcError through
it threw a TypeError and replaced the branch-refusal notice with a crash. Narrow
the value first and cover the non-object rejections.
2026-10-22 21:44:24 +08:00
mektpoy 15ab556c3e fix(ui-conversation): release each binding effect with its Session scope
The long-lived plugin effect wrapped every Session-scope effect, so a released
binding's Context stayed referenced until plugin unload. Track the binding-scope
effects in a map that the binding cleanup and the plugin unload both drain;
nothing long-lived closes over a released binding.
2026-10-22 21:44:24 +08:00
mektpoy 44f0996974 fix(session-controller): ask the concrete access decision before cancelling
The recheck used allows(), the list-filter view that never sees call arguments,
so a policy narrowing cancel to args.request refused a legitimate caller as
access-withdrawn forever. Ask decide() with the principal and the request, keep
the policy's own refusal reason, and cover both an args-independent role and an
args-dependent condition.
2026-10-22 21:44:24 +08:00
mektpoy 258ff28b72 fix(session-controller): finish the Workspace attach a named fork left pending
A first attempt that materialized the child and then failed at attachSession
was reported as failed, but its retry adopted the stored child and returned
success before attaching it, leaving the child ungrouped. The adoption path now
completes the missing attach and reports the same session/workspace-attach-failed
if that still rejects.
2026-10-22 21:44:24 +08:00
mektpoy 608f08ab31 refactor(session-controller): drop the prompt pre-delivery access recheck
The Gateway already admits each call; P1 no longer re-reads the caller's access
after prompt admission. Cancellation still refuses delivery when the caller
aborts during admission, and the requestId dedup/conflict, cancel recheck, and
fork naming behavior stay. The README pair follows.
2026-10-22 21:44:24 +08:00
mektpoy 4322b97840 refactor(ui-workspace): own the branch naming and listed presentation
The published UiWorkspace no longer requires openListedBranch: that wait is an
internal navigation helper, and the service forkSession takes the branch
boundary and presentation choice as optional inputs. The Chat branch surface
now supplies only its branch point and analytics and lets Workspace navigation
decide whether the child may be named and when it becomes presentable.
2026-10-22 21:44:24 +08:00
mektpoy 5ff6b3cdc5 test(ui-chat): pin the owner page branch-refusal notice
A refused branch now reports itself on the owner's own page: the Chat
injection writes the notice on the addressed Session and the composer bar
renders it as an alert, so the owner-visible change has an assertion at both
seams.
2026-10-22 21:44:24 +08:00
mektpoy 52b2bd064f refactor(ui-conversation): read file intake through one InputHub entry
The composer hook source and the drop gate now resolve the same live intake
answer from InputHub, so the policy and its two readers cannot drift; the
caller keeps ownership of the rejection copy.
2026-10-22 21:44:24 +08:00
mektpoy 3660cb0ed6 refactor(ui-conversation): make the composer file-intake hook optional
A bar registration that cannot prove live file-intake permission omits the
source; the bar then offers no file controls, matching the no-session
placeholder's fail-closed answer. PropsHooks keeps required hook seats intact
and lets an optional source leave its seat undefined.
2026-10-22 21:44:24 +08:00
mektpoy 59817cf0b3 refactor(ui-conversation): keep the notice store off the published SessionInput
The composer notice store has no production reader through the published
SessionInput seam: the bar renders it through ComposerBarInjected.hooks.notices
and the only other reader was a test. Leave the store on SessionInputShell,
which the hook wiring uses, and assert the bar's hook in the Chat spec so the
contract surface only publishes members with real consumers.
2026-10-22 21:44:24 +08:00
mektpoy 61ec76af1f docs(ui-workspace): state the connectWorkspace contract once
The model-visible catalog inherited two versions of the same JSDoc sentence,
one of which contradicted the other. Describe the blank reuse once: the first
reusable listed blank, the requested preset or its default, one shared attempt
per Workspace and preset. The generated client inspect catalog follows.
2026-10-22 21:44:24 +08:00
mektpoy bbccdd5141 test(api): cover the prompt access recheck and the named fork paths
Close the Session Controller's remaining per-file coverage debt: a named
fork adopting the child its earlier attempt stored, refusing a name another
Session holds or one seeded at a different cut, and the named-child
observation failures; prompt admission sharing one in-flight requestId and
refusing a duplicate from another caller; the retry read from a drained
splice; the access-withdrawn and aborted-admission delivery paths; cancel
after the caller's grant is gone; and the Assistant settlement that appends
past the last entry. Test-only: no production behavior changed.
2026-10-22 21:44:24 +08:00
mektpoy b9861ae39d feat(api): re-check prompt access and name fork children
The Gateway admits a prompt before the controller sees it, so a binding narrowed while the prompt waited must not still deliver: the Remote method passes a predicate that re-reads the caller's access after every await and immediately before delivery, and cancel re-reads it in the same synchronous step. One requestId still produces one message; a retry from another principal is refused as session/request-conflict. Fork callers may name childSessionId so an interrupted attempt resumes the child it stored; any other occupant of that id is refused as session/fork-conflict. The agent-controller admission pipeline this previously built on is not part of the current master, so the prompt keeps the direct admission path and the tool-execution turn carrier is gone.
2026-10-22 21:44:24 +08:00
mektpoy 6475f57942 feat(client): publish the branch, slot, and access seams a served page reads
The Workspace tree gains the Host-published branch projection plus the
access-snapshot reads its rows, menu entries, and shortcuts consult; the
Conversation package moves branch handling into its own module and exposes the
input and slot declarations a page-local plugin composes. Chat, sidebar,
deliverables, and open-in-app publish the slot surfaces those readers fill, and
the document-relative route and HMR endpoint seams keep the served page's own
base.
2026-10-22 21:44:24 +08:00
ZiyaZhang 0d333ddac3 test(web): refresh the page card fixture after the plugin index rename
The skill result in the fixture lists the custom plugin index bundle under its renamed title and description.
2026-10-22 06:36:52 -07:00
ZiyaZhang 2756d7b922 Merge remote-tracking branch 'origin/feat/creation-recommendations' into pr-6635-ci 2026-10-22 06:36:52 -07:00
ZiyaZhang 1fbebc433f test(web): replay the page card through propose_action
The creation fixture now offers the card with a propose_action call, the
tool schema pin records the tool addition after the skill load, and the
card goldens follow the shared frame and its Show more and Show less
controls.
2026-10-22 06:36:30 -07:00
ZiyaZhang 9b04d772b9 feat(experimental): propose personalized pages through action cards
Personalized pages registers the create_personalized_page card kind with
its create, keep, undo, cancel and restore handlers and drops its own
projection, Remote and status notice. The skill now ends with a
propose_action call. The Client package contributes only the page card
body. The persistence record moves from the page status source to the
shared action-cards source and decision record.
2026-10-22 06:36:30 -07:00
ZiyaZhang 9980af2d48 feat(experimental): add shared action cards behind a propose_action tool
action-cards owns the propose_action tool, the actionCards projection of
proposals and decisions, the act Remote and the card status notice; card
kinds register an action name, skill, details schema, verbs and handler.
The tool is admitted per Agent only after a registered kind's skill loads.
client-ui-action-cards draws the card frame below the completed answer,
lets kinds register a body per action and adds the propose_action
process row.
2026-10-22 06:36:30 -07:00
Chinesezjc 1c20d3fe36 fix(ui-schedule): align cleanup notices with the shared copy 2026-10-22 21:29:38 +08:00
ZiyaZhang a07485bdc7 refactor(experimental): drop the dsh-action answer block and its chat filter
Personalized pages no longer proposes cards through a fenced block in the
answer, so the chat stops hiding dsh- fences and the block parser package
goes away with its registrations.
2026-10-22 06:28:09 -07:00
mektpoy b65da56f6e docs(agent-loop,session): record the unchanged-text series-start rule
Update the capability decision rule, both READMEs, the EventMap JSDoc and generated catalogs. Add the bug-fix Agent Note that records the incident evidence, the decision, rejected alternatives and consequences, and cross-link it from the in-history decision note.
2026-10-22 21:25:32 +08:00
mektpoy 2676c67f32 fix(agent-loop): keep the prompt prefix at a series start with unchanged text
An in-history route carries prompt updates as later system nodes while node 0 stays stale. A surface replacement (compaction, tool-result prune, image offload) starts a new request series, and the projection used to re-baseline node 0 even when the rendering already matched the latest surviving node: it emptied that node and wrote identical text into node 0, so the request differed at its first message and the provider prefix cache missed from the first token (#6973).

Re-baseline node 0 only when the effective text must move: an incapable route, an empty rendering, or a series start whose latest surviving non-empty node differs from the rendering. A capable series start with matching text emits no system event; the loop still logs the request series and rebuilds declarations. Retry compaction still folds the prompt to the head when it shadows the latest node.
2026-10-22 21:25:29 +08:00
Chinesezjc d82234116b feat(ui-schedule): align bulk cleanup with the search-row More menu 2026-10-22 21:22:11 +08:00
mektpoy 71d5454904 test(agent-loop): reproduce series-start prompt rewrite on unchanged text
A compaction replacement after an appended in-history prompt starts a new request series, and the projection folds the prompt back into node 0 although the effective text is unchanged. The request moves the system segment to wire message 0, so the provider prefix cache misses from the first token (#6973).

The regression fails before the fix: the third request carries one system text ("prompt two") instead of keeping the appended node behind the cached prefix.
2026-10-22 21:19:24 +08:00
TurtleandZiyaZhang 701c0a41c5 refactor(ui-primitives): measure tooltip clipping when the bubble shows
Co-authored-by: ZiyaZhang <[email protected]>
2026-10-22 21:18:15 +08:00
ZiyaZhang b8e16bef91 Merge origin/master into personalized pages experiment 2026-10-22 06:17:15 -07:00
Turtle d10f417f13 Merge pull request #6979 from deepseek-harness/turtle/rename-plugin-index-again-2424dbab6ad1
fix(plugin-market): present custom plugin markets as custom plugin indexes
2026-10-22 21:10:29 +08:00
Yichen Jiang c48f8523f2 feat(llm): follow the official /models listing on the DeepSeek API-key route (#6944)
* refactor(llm-deepseek): share the /models listing machinery

Move the account route's `/models` request and decoding, its
credential-scoped listing cache, and listed-catalog resolution with the
version gate into the shared DeepSeek Messages library, so a second
route can follow the same listing. Names drop the account prefix, the
cache takes each access's endpoint settings with the call, and it gains
`invalidate()` for a forced reload. `isBuiltInCatalog()` recognizes the
catalog an uncustomized `models` setting resolves to.

The account route keeps its behavior; only its imports and the
override diagnostic wording ("model listing") change.

* feat(llm-deepseek-api-key): follow the official /models listing

While `models` equals the built-in catalog and the Messages origin is
https://api.deepseek.com, `deepseek-official` serves the official
`GET /models` listing, authenticated with the API key in `x-api-key`.
Loading, refresh, fallback, merging, and the client-version gate match
the account route: the first access waits up to `modelsTimeoutMs`, a
listing older than `modelsMaxAgeMs` refreshes in the background on the
next access, a failure keeps the last good listing or the built-in
catalog, and listed models this build cannot serve are refused before
network I/O.

A customized `models` list replaces the catalog and requests nothing.
Switching between a customized catalog and the listing discards the
cached listing, so Restore defaults reloads it on the next access.

Suites that boot the route on the official endpoint now answer the
listing themselves instead of reaching the network.

* docs: describe the official route model listing

Document when `deepseek-official` follows the official `/models`
listing, its timing fields, customization precedence, and the forced
reload after Restore defaults in the API-key README, the shared
transport README, and the provider guide. Record the decision in a new
Agent Note, keep the account note's statement of where the shared
listing code lives current, and regenerate the configuration catalog.

* test(llm-deepseek-api-key): pin the once-per-listing hidden-entry diagnostic

* docs: regenerate module and event graphs for the shared listing

* fix(llm-deepseek-api-key): report credential failures on the model request

On the official endpoint, `connect` waited for the listing view, whose
credential lookup rethrew a malformed key or a credential-store failure
before the agent loop recorded the prompt. A failed lookup now resolves
the connection from the current listing view, so `resolveAuth` reports
the failure on the model request and the turn keeps the prompt and the
failed attempt. A caller abort still rejects.

* docs(llm-deepseek-api-key): name which hidden listing entries are logged

* test: keep unit suites off the official model listing

Suites that boot the DeepSeek routes with a dummy or inherited key now
request `GET https://api.deepseek.com/models` on the official endpoint.
A unit-lane setup file rejects that request at once, so such a suite
serves the built-in catalog instead of reaching production or waiting
out the listing deadline on a network that never answers; suites that
exercise the listing answer `fetch` themselves. Other requests pass
through unchanged.

* feat(llm-deepseek): read tool_update from the model listing

Both DeepSeek routes now take a listed model's Messages tool update mode
from `api_capabilities.anthropic_messages.tool_update`, alongside the
system prompt mode. `in-history` and `addition-only` replace the
bundled mode; any other value reads as `leading-only` and removes it.
An absent or `null` mode keeps the bundled one, so a listing that does
not declare the field leaves `deepseek-flash` on `addition-only`.

`system_prompt_update: null` likewise keeps the bundled mode now; it
previously read as `leading-only`, which a serializer writing empty
values as null would have turned into a cache-unfriendly downgrade.

* fix(llm-deepseek): sync the built-in context window with the official listing

The official `/models` listing declares a 1,048,576-token context window
for `deepseek-flash` and `deepseek-v4-pro`; the built-in catalog said
1,000,000, a decimal-versus-binary mismatch. Both built-in entries now
carry the listed value, so offline starts and listing failures size the
context the same way the listing does. The Models page shows the exact
count for these rows, since 1,048,576 has no decimal K/M spelling.

`defaultContextWindow`, the fallback for model ids the catalog does not
describe, stays 1,000,000.

* test(llm-deepseek-api-key): reject the stubbed listing request with an Error

* test(cli): keep the built SDK smoke off the official model listing

The SDK profile's initialize now resolves the deepseek-official catalog,
which requests GET https://api.deepseek.com/models on the official
endpoint. With the smoke's fixture key that request answers 401 and the
route warns on stderr, so the test's empty-stderr assertion failed in the
e2e and snapshots lanes. Point the run at a loopback mock and assert it
received no model call.

* test(llm-deepseek): compose the malformed-key turn without the native executor

release/dsh-0.2.1 predates @deepseek-ai/dsh-tool-executor-native; its tool
runtime executes tools itself. Compose the official-route turn the way
account-routing.spec.ts does on this branch.

* fix(llm-deepseek-api-key): gate nothing when the credential lookup fails

The listing cache switches scope only after it reads a credential, so a
failed lookup left `connect` serving the previous credential's listing.
After a key whose listing refuses a model was replaced by a malformed one,
`admit` refused that model with CLIENT_UPGRADE_REQUIRED before the turn
recorded the prompt, instead of `resolveAuth` reporting the credential
error on the request. Serve the built-in catalog without listing gates
when the lookup fails.

* test(llm-deepseek-api-key): pin the once-per-listing diagnostic across request preparation

The hidden-entry diagnostic test repeated the same discovery assertion to
prove a second pass reports nothing new, which read as a duplicate. Assert
the single warning after the request preparations instead; they resolve
the same listing, so dropping the per-listing deduplication still fails
the test.

* docs(llm-deepseek-api-key): note that a list matching an earlier built-in catalog counts as customized

Customization is judged against the running version's built-in catalog,
so a saved `models` list equal to an earlier version's catalog stops
following the listing once the built-in catalog changes, until Restore
defaults. Record the consequence in the Agent Note and the README's known
limitations.
2026-10-22 20:42:19 +08:00
Turtle 77b2d919c4 docs(plugin-market): fix index wording left by the rename 2026-10-22 20:42:15 +08:00
TurtleandZiyaZhang 9ede8f237d feat(ui-chat): show cut-off composer statistics in a tooltip
Co-authored-by: ZiyaZhang <[email protected]>
2026-10-22 20:40:28 +08:00
07akioni 7f2f9b8856 fix(ci): allow larger tracked-file listings in static gates 2026-10-22 20:35:15 +08:00
07akioni 86c39abae0 fix(i18n): correct Portuguese units and Russian fold label 2026-10-22 20:28:39 +08:00
07akioni 72f814c5ca fix(i18n): correct French collapse label 2026-10-22 20:21:59 +08:00
Turtle 6c07e02afa fix(plugin-market): finish the plugin index wording and restore generated docs 2026-10-22 20:20:02 +08:00
Turtle 5b0e089f40 fix(plugin-market): present custom plugin markets as custom plugin indexes 2026-10-22 20:19:30 +08:00
07akioni b86f0cfd11 fix(i18n): align navigation and collapse terminology 2026-10-22 20:13:47 +08:00
winewill 5a407d497f fix(build): order bundlers before shared client output writes 2026-10-22 20:08:53 +08:00
imccyu 39d2903841 fix(ui): separate navigation pins from selection and show menu icons (#6883)
* fix(ui): separate navigation pins from selection and show menu icons

* fix(ui): simplify More menus and fold conversation views on resize

* test(ui): fix navigation CI regressions and refresh catalogs

* refactor(ui): render sidebar panel icons directly

* fix(ui): keep Chat visible and stop persisting navigation pins

* refactor(ui): drop unreachable pin validation from in-memory stores
2026-10-22 20:05:33 +08:00
07akioni b053d8a3f7 fix(i18n): refine common actions and thinking status copy 2026-10-22 20:01:21 +08:00
07akioni 84edeadc2b feat(i18n): add Spanish French German Russian and Portuguese 2026-10-22 19:53:52 +08:00
Chinesezjc 7302f003e2 test(storage): bind the sqlite exec spy without unbound references 2026-10-22 19:40:38 +08:00
winewill 5a45e0a448 fix(desktop): clarify signing prerequisites and portable fixtures 2026-10-22 19:37:19 +08:00
Chinesezjc 358d1c096d feat(schedule): lighten the bulk cleanup and clarify its copy 2026-10-22 19:35:23 +08:00
Chinesezjc a0d20b8af0 test(storage): use the supported throw assertion 2026-10-22 19:23:59 +08:00
winewill 6c5d7c918a fix(desktop): address packaging review follow-ups 2026-10-22 19:08:45 +08:00
Yichen Jiang 7ebceb3822 feat(client): render catalog badges in the model selector
The composer model seat shows a catalog model's badge after its name in
the trigger, the root-menu value, and each option: a Tag in the badge's
tone whose text is resolved for the active locale through
ctx.locale.resolveText. A conversation.input.model.label contribution
replaces the name and badge together and receives the badge in its
owner props.

Remove ui-deepseek-model-label-1017, which hard-coded `1017` beside
DeepSeek-V4.1-Flash for two providers, and its Web bundle row. The badge
now comes only from the model listing, so the build ships none of its
own; the upgrade guide covers profile patches that still target the
removed row. The Web e2e replaces the fixed-badge case with replay
catalogs that declare literal and translated badges, and the replay
adapter validates and advertises a configured badge.
2026-10-22 18:47:49 +08:00
Yichen Jiang 663aa618c5 feat(llm): carry /models badges into the model catalog
The shared DeepSeek listing decoder reads an optional per-entry
`badge: { text, tone }`. Text is one literal or translations keyed by
lowercase language id with a required English entry, each trimmed to 1
to 12 characters on one line; tone is neutral, info, success, or
warning, and an absent or unrecognized tone reads as neutral. An absent
or null badge declares none; an unreadable one is dropped with a listing
problem and never hides its model.

resolveListedCatalog keeps the badges of offered models, both DeepSeek
routes attach them through catalogModelInfo, LlmRuntime.listModels
detaches them, and the Host model catalog forwards them as
ModelCatalogModel.badge. resolveModelInfo omits the badge, so no model
request, Session event, or model-facing tool carries it.
2026-10-22 18:47:30 +08:00
winewill cda5c51b4f merge: reconcile desktop diagnostics with master 2026-10-22 18:47:16 +08:00
Chinesezjc ed7d73098f docs(storage): align unknown-state contracts and failure wording 2026-10-22 18:38:09 +08:00
Chinesezjc 35fce127c0 test(storage): guard the optional backupRecord probe 2026-10-22 18:33:38 +08:00
Chinesezjc 3c7cfb8cf8 fix(storage): propagate unknown-state through domains and clients 2026-10-22 18:31:12 +08:00
ZiyaZhang b0e5c20a29 test(web): re-record the plugin card prompt pin after the subagent guidance change 2026-10-22 03:25:45 -07:00
Yichen Jiang ae7c911ba0 Merge master into the model badge branch 2026-10-22 18:25:22 +08:00
ZiyaZhang ce3956729b Merge feat/creation-recommendations into plugin recommendation experiment 2026-10-22 03:25:05 -07:00
ZiyaZhang 8f6f0e77dc test(web): record plugin recommendations in the page skill catalog replay
Shipping plugin recommendations as an optional official bundle adds its entry
to the official-plugin catalog that the personalized-pages skill appends, so
the recorded skill result of the page card replay lists it.
2026-10-22 03:22:07 -07:00
Chinesezjc 0397bd3f04 fix(storage): quarantine domains after unknown-state batch failures 2026-10-22 18:21:16 +08:00
ZiyaZhang f997618f51 fix(experimental): keep recommended plugin titles in place while they load
Catalog text comes from an asynchronous Host lookup, so entering a Session
showed the package name and then moved the card when the title, description
and actions arrived. The browser contribution now keeps returned catalog text
by plugin name in a store owned by its UI lifetime, so a revisited card
renders it at once while its own lookup refreshes the selection, refusal or
lost reply. Until a plugin's first lookup returns, placeholder lines and the
disabled actions hold the title, description and footer in place.
2026-10-22 03:19:20 -07:00
imccyu 2ae65c16b7 Merge pull request #6966 from deepseek-harness/revert-6959-xtr/fix-web-agent-initialization
Revert "fix(session-controller): 在 Web 操作中等待本地 Agent 初始化"
2026-10-22 18:18:43 +08:00
imccyu f185b5fa6d Revert "fix(session-controller): 在 Web 操作中等待本地 Agent 初始化" 2026-10-22 18:18:01 +08:00
ZiyaZhang 51000d47c5 style(experimental): make the recommendation card one hover target
The card takes the deliverables card border and layer background. Its title,
reason and description stay one details button, which now highlights as a
whole on hover and keyboard focus with the interactive hover token instead of
underlining a separate "View details" hint; the hint copy is removed and the
Web goldens lose it from the button text.
2026-10-22 03:15:15 -07:00
ZiyaZhang 0bc0e53173 feat(experimental): route capability questions to the official-plugin skill
The official-plugins catalog entry now also triggers when the user asks how
to do or use something in this app, asks whether it can do something, or says
a capability is missing, and names tools, automation, access, badges and
signatures besides the earlier categories. At 495 characters it stays within
the skill catalog's 500-character limit; the previous 535-character entry lost
its exclusions to truncation. The Web replay fixture records the new catalog
line.
2026-10-22 03:13:23 -07:00
_Kerman a3d5e4f9e3 Merge pull request #6959 from deepseek-harness/xtr/fix-web-agent-initialization
fix(session-controller): 在 Web 操作中等待本地 Agent 初始化
2026-10-22 18:12:26 +08:00
ZiyaZhang a02bf55577 fix(experimental): port the page card fixes to plugin recommendations
The recommendation projection skips the events a forked Session inherits,
records the turn that admitted each proposal and publishes the proposals in
its client view. Card actions and descriptions look proposals and decisions up
by own keys only, so inherited member names such as toString are unknown ids.
The card reads its proposal from the projection view by turn and appears once
that turn ends for any reason; the answer parser and the hidden conversation
node are removed.
2026-10-22 03:11:07 -07:00
Chinesezjc ccdd737fd4 Merge remote-tracking branch 'origin/master' into feat/schedule-clear-ended
# Conflicts:
#	packages/client/ui-schedule/README.i18n.yaml
2026-10-22 17:59:12 +08:00
ZiyaZhang 62351681da test(web): re-record the page card prompt pin after the subagent guidance change 2026-10-22 02:58:18 -07:00
ZiyaZhang 6e4b45b17e Merge remote-tracking branch 'origin/master' into pr-6635-ci 2026-10-22 02:56:19 -07:00
Chinesezjc 6db5041812 fix(storage): keep batch deletion tracked and consistent 2026-10-22 17:54:21 +08:00
ZiyaZhang caa5535dee Merge feat/creation-recommendations into plugin recommendation experiment 2026-10-22 02:47:01 -07:00
Yichen Jiang 4b07c32b93 fix(forge): follow master's hover, tooltip, and plugin metadata rules
Master now limits CSS hover styles to hover-capable primary input, opens
tooltips from pointer events, requires a model on every Agent and a model
selection on agent/pre-step, and requires Japanese plugin metadata.

- The panel's hover styles move under @media (hover: hover); expanded,
  pressed, and focus-visible styles stay outside it.
- The hover tests enter with pointer events.
- The test Agent carries an unsupported model, and proposed steps carry a
  model selection.
- The panel and the forge bundle ship Japanese titles and descriptions.
2026-10-22 17:45:15 +08:00
Yichen Jiang cdc72eb229 Merge remote-tracking branch 'origin/worktree/forge-change-request-bindings' into worktree/forge-pr-worktree-panel
The parent branch now carries master through #6426, including the
agent-controller revert.

- Package manifests keep both sides: the ./types and ./locale/*.json
  exports of the forge packages, and the Web app's forge bundle and
  message quotes dependencies.
- The type manifest drops the removed agent-controller entries and keeps
  the forge panel entries.
- The event matrix and the capability-seams pairing are regenerated.
- The forge bundle stays on the main plugin list and now ranks before the
  held-back developer tools; the plugin page expectations follow.
2026-10-22 17:44:45 +08:00
_Kerman d3972b1af5 fix(session-controller): handle initialization join failures 2026-10-22 17:44:40 +08:00
ZiyaZhang b324796738 style(experimental): give the page card the deliverables card border 2026-10-22 02:36:17 -07:00
Chinesezjc 43f4c9a4ee Merge pull request #6789 from deepseek-harness/work/restore-subagent-end-turn
fix(prompt): end the turn while waiting on subagents
2026-10-22 17:35:12 +08:00
ZiyaZhang 80997ea36a Merge master into personalized pages experiment 2026-10-22 02:34:47 -07:00
Magolor a9dd195400 refactor(approval): persist policy and turn state in projection (#6816)
* refactor(approval): persist policy and turn state in projection

* fix(approval): require ready projection state and host-owned recovery

* chore(approval): retire obsolete test cast allowances

* docs(approval): make activation migration requirement explicit

* docs(session-projection): clarify recovery ownership and readiness

* docs(approval): keep projection skill in dedicated PR
2026-10-22 17:27:54 +08:00
winewill ab37218760 style(desktop): clarify operation diagnostics code 2026-10-22 17:19:47 +08:00
Yichen Jiang eea6d3b7ec feat(forge): hold a sent message until the Session's worktree is ready
The agent-controller admission hook was reverted on master, so the forge
panel now holds the next Turn with Agent maintenance started from the
agent/inbox/inserted notification. The message stays in the inbox until
the hold ends.

- A message sent before the requested worktree exists waits while the
  panel creates it. After a failed or stopped attempt the message keeps
  waiting until the card retries, through the new
  forgePanel.retryWorktree, or withdraws the request so the message goes
  on in the project directory.
- A message sent after the Session's worktree was removed waits on the
  missing card until the worktree is restored or the Session returns to
  the source checkout.
- Restoring a worktree reports its prepare and checkout steps. The card
  shows them in place of its actions, and the tab's restore action shows
  a spinner while it runs.
- The panel state names what a waiting message waits for and the running
  restoration; the agent-controller dependency is removed.
2026-10-22 17:17:55 +08:00
Chinesezjc 3b30f69c9f test(snapshot): carry the end-turn guidance into merged scenarios
The branch merge from master brought four scenarios added after the prompt
refresh (background-wait-steer, session-resume, model-switch-compaction,
web-search-unconfigured); their system-prompt sidecars still show the
guidance without the restored sentence, so the keyless replay fails. Apply
the same sentence to those six expected lines.
2026-10-22 17:17:06 +08:00
Shigma 1fd099f7e2 Merge pull request #6207 from deepseek-harness/feat/cordis-loader-resolver
feat(cordis): sync project-relative Loader resolution
2026-10-22 17:16:54 +08:00
_Kerman e5b68798b9 fix(session-controller): wait for initializing local agents 2026-10-22 17:04:51 +08:00
Chinesezjc 83a51c5d65 Merge branch 'master' into work/restore-subagent-end-turn 2026-10-22 16:59:48 +08:00
Chinesezjc f48596d2f7 Merge pull request #6863 from deepseek-harness/fix/weighted-approval-app-check
feat(ci): publish the approval decision label from a dedicated approval App
2026-10-22 16:47:28 +08:00
Shigma 90499e2e61 Merge branch 'master' into feat/cordis-loader-resolver 2026-10-22 16:35:55 +08:00
Chinesezjc 6f12142abb docs(ci): state how the App token fallback fails 2026-10-22 16:31:28 +08:00
Chinesezjc 1e25d64a2e fix(ci): select decision runs by the publishing app
The App token cannot update a run the job token created and vice versa, so
selecting runs by `app.slug === 'github-actions'` missed every App run: each
phase created another run, a published App success was never revoked, and a
head with an older job-token run made the App patch fail. Select runs by the
publishing app slug instead, pass `vars.DSH_APPROVAL_APP_SLUG`, and set the
external_id marker on the setup-phase run too.

Cover the App-owned update and the job-run coexistence, and align the note with
the measured earliest-suite rule and the App slug variable.
2026-10-22 16:31:01 +08:00
Chinesezjc 7cf0362dce fix(ci): re-record the note pairing and keep the issue App assertion
The dedicated-App edit changed the note's decision section without re-recording
its translation sidecar, and the client-id replacement also hit the issue
lifecycle workflow assertion. Re-record the pair and restore the issue App's
variable names in that test.
2026-10-22 16:29:07 +08:00
Chinesezjc 8cd82e8001 Merge branch 'master' into work/restore-subagent-end-turn 2026-10-22 16:21:42 +08:00
Magolor 88d2ab67b0 Merge pull request #6801 from deepseek-harness/projection-plugin-rules
docs(session-projection): 明确插件状态恢复与兼容规范
2026-10-22 16:21:36 +08:00
winewill 523ecd189b docs(desktop): record signing concurrency qualification 2026-10-22 16:18:07 +08:00
winewill f5caf31fb5 test(desktop): qualify signing concurrency with release files 2026-10-22 16:04:53 +08:00
Feiyu Xia 904a1caa2d Merge pull request #6664 from deepseek-harness/feat/mobile-input-viewport
fix(client): improve mobile input focus and caret scrolling
2026-10-22 15:58:15 +08:00
ZiyaZhang 7759bcc408 feat(experimental): voice the creation invitation in the first person 2026-10-22 00:58:04 -07:00
ZiyaZhang 9498be65dc feat(experimental): word the creation invitation around DeepSeek Harness 2026-10-22 00:53:43 -07:00
Yichen Jiang c1238b8792 Merge pull request #5263 from deepseek-harness/worktree/web-search-dynamic-tool-e26c2b
feat(web): withhold web_search and web_fetch from requests without credentials
2026-10-22 15:53:05 +08:00
Turtle 644eb3158e Merge pull request #6838 from deepseek-harness/turtle/market-install-allowbuilds-8b6f87a8284b
fix(plugin-manager): offer script approval for git-hosted packages
2026-10-22 15:52:14 +08:00
Turtle ca28551037 Merge remote-tracking branch 'origin/master' into turtle/preview-codemirror-slow-e4bae59874fa 2026-10-22 15:44:16 +08:00
Yichen Jiang fb378dacf2 fix(web): offer DeepSeek web tools whenever a credential exists
The DeepSeek search and Search/Fetch API providers now report readiness
from credential presence alone, on any model route: the account token
first on the deepseek-account route, then a literal apiKey or a
configured apiKeyEnv reference. A conversation on another provider's
model keeps web_search and web_fetch while a DeepSeek key is configured,
as on master, so the route refusal WEB_PROVIDER_ROUTE_DISABLED and its
upgrade guide are removed.

The pi-ai-conversation-updates and deepseek-messages-system-prompt
snapshots replay their master recordings again, the headless keyless
smoke points its search key reference at an unset variable, and the
readiness Agent Note records the rejected route and endpoint rules.
2026-10-22 15:38:16 +08:00
Turtle b70735089d docs(plugin-manager): keep pendingBuilds JSDoc on one line 2026-10-22 15:38:14 +08:00
Turtle a8bf3e7600 fix(plugin-manager): harden git allowBuilds key parsing
Capture git keys that contain spaces, skip keys whose git URL carries a
credential, and isolate Git configuration for the real pnpm test.
2026-10-22 15:38:14 +08:00
Turtle 220301c614 fix(plugin-manager): offer script approval for git-hosted packages
pnpm 11 refuses to prepare a git-hosted dependency with
ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED instead of reporting ignored build
scripts. Classify that error as build-blocked and record the allowBuilds
key pnpm suggests, so the existing approval-and-retry flow applies.

Fixes #6837
2026-10-22 15:37:59 +08:00
ZiyaZhang 00efaf376d test(web): expect the wider fold preview in the page card replay 2026-10-22 00:32:24 -07:00
ZiyaZhang 7a58410d66 fix(experimental): fold card text by display width instead of grapheme count 2026-10-22 00:30:03 -07:00
Turtle 7df9a5a93b Merge pull request #6943 from deepseek-harness/turtle/git-https-source-auth-b46e6aff6136
fix(plugin-manager): install self-hosted Git plugins over SSH when only SSH can read them
2026-10-22 15:26:48 +08:00
Turtle f313e7911b Merge pull request #6564 from deepseek-harness/turtle/plugin-sticky-header-29bfdb179e18
feat(plugin-manager): pin the Plugins page title bar while scrolling
2026-10-22 15:15:12 +08:00
Turtle 39d973322b test(web): compare the worker Buffer with an unmodified Node reference
Vitest resolves the shim's `buffer` import to Node's built-in module, so
the differential spec patched and then compared the same implementation.
Mock node:buffer with the npm package the Worker bundle uses. Also keep the
UTF-8 encoding check symmetric, call the package methods directly, and
document that the patch applies to every Buffer user in the Worker.
2026-10-22 15:13:05 +08:00
Yichen Jiang 4916ce9bff feat(forge): show worktree and change request state in the Session list
The branch label left the Session header, and finding which Sessions
work in their own worktrees, or which of their change requests need
attention, meant opening each Session's tab.

- A Session row shows a branch glyph while the Session works in the
  worktree the panel created for it, and the state glyph of its most
  urgent change request with a dot for its checks. The row's hover card
  names the worktree branch and its base, then up to three change
  requests with their state and checks.
- The forgeWorktree projection follows the Session's working directory;
  its client view is the created worktree while the Session works in
  it. The forgeChangeRequests projection gains a client view with the
  bindings, so the Session summary carries both without loading a
  Session.
- The forgePanel.changeRequestStates stream serves the states of the
  change requests every listed Session binds from one Host run. It
  polls each until it is merged or closed and stores every change in
  the forge_panel_change_requests storage domain, so a restart shows
  the last known states before the next poll.
- A new Agent Note records the decision; the READMEs, the forge
  subsystem page, and the catalogs follow.
2026-10-22 15:10:29 +08:00
ZiyaZhang f2ae73a473 feat(experimental): spell out the creation invitation in the page skill 2026-10-22 00:07:32 -07:00
winewill 2e62e83960 docs(desktop): extract recovery and diagnostics reference 2026-10-22 15:00:45 +08:00
Yichen Jiang a19aabc8ba feat(web): let a key of search's own serve other model routes
A user who gives DeepSeek search a key of its own has chosen what that key
pays for, so the route check now applies only to the shared
DEEPSEEK_API_KEY. A literal apiKey, or an apiKeyEnv other than
DEEPSEEK_API_KEY, opens every model route for Messages search and for the
Search and Fetch APIs; a configured endpoint does not, since the search
still sends the shared key. The Web search settings page saves its key
under the section's apiKeyEnv, DEEPSEEK_API_KEY by default, so a key saved
there keeps serving only the DeepSeek routes.

The route refusal now names the settings that give search a key of its
own, and the READMEs, the readiness Agent Note, and the upgrade guide
describe the exception.
2026-10-22 15:00:39 +08:00
lsdsjy da910572c5 Merge pull request #6948 from deepseek-harness/fix/sidebar-noop-drag-caret
fix(sidebar): 隐藏不改变标签顺序的拖拽定位条
2026-10-22 14:58:05 +08:00
Turtle b908e13271 Merge remote-tracking branch 'origin/master' into turtle/image-cache-stale-0a243f674e12
# Conflicts:
#	packages/api/session-controller/README.i18n.yaml
#	packages/api/session-controller/README.md
#	packages/api/session-controller/README.zh.md
#	packages/client/ui-primitives/README.i18n.yaml
2026-10-22 14:57:42 +08:00
Chinesezjc da7996de07 fix(schedule): report bulk removal outcomes from the page 2026-10-22 14:56:35 +08:00
Chinesezjc a6f92bfd79 Merge commit '8be1a8e63a48f357807d52070ea54db61912794e' into feat/schedule-clear-ended
# Conflicts:
#	packages/client/ui-schedule/src/client/index.ts
2026-10-22 14:53:21 +08:00
Turtle f837cdbfbf Merge remote-tracking branch 'origin/master' into turtle/plugin-sticky-header-29bfdb179e18
# Conflicts:
#	packages/client/ui-primitives/README.i18n.yaml
2026-10-22 14:50:11 +08:00
ZiyaZhang ac49bef0c2 Merge master into personalized pages experiment 2026-10-21 23:42:16 -07:00
lsdsjy 3e2e051bf1 docs(sidebar): 明确无效拖拽落点的释放语义 2026-10-22 14:41:06 +08:00
Yichen Jiang 587cf7a577 test(cli): keep the built SDK smoke off the official model listing
The SDK profile's initialize now resolves the deepseek-official catalog,
which requests GET https://api.deepseek.com/models on the official
endpoint. With the smoke's fixture key that request answers 401 and the
route warns on stderr, so the test's empty-stderr assertion failed in the
e2e and snapshots lanes. Point the run at a loopback mock and assert it
received no model call.
2026-10-22 14:39:17 +08:00
ZiyaZhang ea91bd7c16 feat(experimental): restore a removed page from its card 2026-10-21 23:38:49 -07:00
Yichen Jiang a3c0265456 test(snapshots): share web-fetch's tool schemas with web-search-unconfigured
web-search-unconfigured declares exactly the tool catalog that web-fetch
already pins, so it names web-fetch as its toolSchemasSource instead of
committing a second copy.
2026-10-22 14:36:38 +08:00
Turtle c063b70b85 Merge pull request #6891 from deepseek-harness/turtle/sidebar-first-open-flash-c291a96690d1
fix(ui-sidebar-right): slide the panel in on its first open
2026-10-22 14:33:13 +08:00
Turtle 4b1d4c814e fix(web): speed up lazy bundle loads in the Web preview
The Web preview Worker converted multi-megabyte combo scripts and source
maps with the buffer package's JavaScript UTF-8 codec, and the page
re-encoded each source map to Base64 in JavaScript before running the
bundle. Use TextEncoder/TextDecoder for whole-buffer UTF-8 conversions
and FileReader for the inline source-map data URL.
2026-10-22 14:32:35 +08:00
Turtle f228c3ffa6 Merge pull request #6064 from deepseek-harness/turtle/async-notice-efficiency
feat(jobs): deliver job output in completion notices and drop repeated notice text
2026-10-22 14:30:55 +08:00
winewill 39efcc36b5 fix(desktop): await remaining shutdown cleanup after rejection 2026-10-22 14:28:06 +08:00
lsdsjy f9ff5d0979 fix(sidebar): 隐藏不改变标签顺序的拖拽定位条 2026-10-22 14:27:54 +08:00
winewill 4db4345865 test(windows): compare signing concurrency with supervised probes 2026-10-22 14:27:28 +08:00
ZiyaZhang cb1e21f77b fix(experimental): keep the page skill entry within the catalog limit 2026-10-21 23:19:59 -07:00
turtle1999 6f4ce88207 Merge remote-tracking branch 'origin/master' into turtle/async-notice-efficiency
# Conflicts:
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
2026-10-22 14:12:42 +08:00
Turtle cce67394c9 docs(config-catalog): regenerate after merging master 2026-10-22 14:11:15 +08:00
ZiyaZhang 9209b12b9f fix(experimental): render page cards from the projection view and align fence hiding 2026-10-21 23:10:07 -07:00
Turtle 8255ebe3cc Merge remote-tracking branch 'origin/master' into turtle/git-https-source-auth-b46e6aff6136 2026-10-22 14:09:45 +08:00
Turtle 420a7797f6 fix(plugin-manager): harden Git transport probe prompts, fragments and attribution
Refs #6938
2026-10-22 14:07:10 +08:00
Turtle 1b7923db9d refactor(ui-sidebar-right): start the first-open slide with @starting-style
Replace the layout effect that forced a closed style with an opening marker derived during render. A @starting-style rule under the marker starts items mounted by the opening at the closed position. The marker clears when a transition ends, the panel closes, or the panel leaves the screen.
2026-10-22 14:07:05 +08:00
winewill 0f21742b25 fix(macos): apply ZIP compression when skipping notarization 2026-10-22 13:51:49 +08:00
imccyu b04c1c3261 Merge pull request #6895 from deepseek-harness/turtle/icon-tokens/icons-plugin-tokens-e20dec90761c
feat(client): let themes replace product icons through an SVG sprite
2026-10-22 13:50:35 +08:00
Yichen Jiang 1922f322aa fix(web): gate the DeepSeek web providers on the model route alone
Review found two faults in the endpoint and configuration checks. The
providers took the official chat route's endpoint from the first active
dsh-llm-deepseek-api-key Loader row, so a composition that isolates several
instances answered for an unrelated one: a correct route lost search, or a
gateway's key reached DeepSeek. And a key saved on the Web search settings
page lands under the default DEEPSEEK_API_KEY reference, so the inference
that search had its own configuration never saw it.

Both DeepSeek web packages now serve only the deepseek-account and
deepseek-official routes, whatever key they are configured with, and no
longer compare the chat endpoint with their own. ready() checks the route
and then credential presence as before; the API-key resolver repeats the
route check and refuses with WEB_PROVIDER_ROUTE_DISABLED. The packages no
longer read Loader rows or depend on dsh-llm-deepseek-api-key, so the
lockfile, the module graph, and the keyless smoke expectations that the
endpoint check had changed return to master's.

An upgrade guide covers conversations on other providers' models, which
lose DeepSeek web search and, with the Search and Fetch APIs selected,
web_fetch.
2026-10-22 13:48:53 +08:00
Yichen Jiang 8bef97a894 test(llm-deepseek-api-key): reject the stubbed listing request with an Error 2026-10-22 13:47:44 +08:00
Stevring 23a9cad68e Merge remote-tracking branch 'origin/master' into feat/mobile-input-viewport 2026-10-22 13:46:22 +08:00
ZiyaZhang 76be903074 feat(experimental): admit live views of the app's own data as a page signal 2026-10-21 22:43:11 -07:00
Turtle f59f0dfc67 fix(plugin-manager): install self-hosted Git plugins over SSH when only SSH can read them
Refs #6938
2026-10-22 13:42:56 +08:00
imccyu 73931626be feat(webserver): add resource routes and in-process readResource (#6901)
* feat(webserver): add resource routes and in-process readResource

A webserver route or the fallback seat may now answer through a resource,
a function from a Fetch Request to a Response, besides the existing
node:http handler. register accepts either route form and registerFallback
accepts a handler or { resource }; existing handler callers are unchanged.
HTTP GET and HEAD requests reach a resource; webServer.readResource(request,
peer) follows the same match order and reaches only resources, so handler
routes stay unreachable to in-process readers.

client-modules registers /plugins as the fetchBundle resource, and
frontend-static claims the fallback seat with a resource that renders the
index for a supplied Peer, or runs Connection's existing authorizeIndex for
an HTTP request and renders for the browser Peer.

* fix(webserver): flush resource headers and keep one drain listener per response

Resource responses send their status and headers before the first body
chunk, register one drain/close listener pair per response so the gzip
middleware's zlib stream does not accumulate listeners across backpressure
waits, and readResource drops the body of a HEAD read as HTTP does. HTTP
requests and readResource share one responder lookup.

* chore(scripts): prune the retired client-modules unknown-cast baseline entry

* feat(frontend-static): let browsers revalidate dist files except the index

Every dist file other than the rendered index now carries
cache-control: no-cache and a weak ETag from a SHA-1 of its bytes; a
request whose If-None-Match equals that ETag receives an empty 304. The
index keeps sending no cache headers. In-process reads through
webServer.readResource revalidate the same way.

* fix(webserver): stop reading Host for resource URLs and copy no held body

A resource request's URL is the request target resolved against the same
placeholder origin the route match uses, so a missing or unparsable Host no
longer turns a dist or /plugins request into 400. The webserver ends a
resource response itself and treats any earlier close as an abort, which
also covers the Web Worker tunnel marking an aborted response ended first.
client-modules bundle bodies and frontend-static file bodies are the held
bytes themselves instead of copies, and frontend-static no longer exports
its internal serveStatic helper.

* docs(webserver): document resource route types and the handler-or-resource fallback

Paste WebResource and WebResourceRoute beside WebRoute on the web-server
subsystem page and link the catalog signatures to it, replace the remaining
'fallback handler' wording now that the seat takes a handler or a resource,
and note why the drain listener removal is a no-op under gzip.
2026-10-22 13:41:18 +08:00
ZiyaZhang 8c780f4b7d fix(experimental): share the dsh-action fence scanner and harden the page projection 2026-10-21 22:40:50 -07:00
winewill 32bbe4fc8a fix(windows): check token public identity before signing 2026-10-22 13:38:57 +08:00
Turtleandimccyu 140496c750 Credit review by @imccyu
Co-authored-by: imccyu <[email protected]>
2026-10-22 13:37:32 +08:00
Yichen Jiang e3a485bd32 fix(llm-deepseek): sync the built-in context window with the official listing
The official `/models` listing declares a 1,048,576-token context window
for `deepseek-flash` and `deepseek-v4-pro`; the built-in catalog said
1,000,000, a decimal-versus-binary mismatch. Both built-in entries now
carry the listed value, so offline starts and listing failures size the
context the same way the listing does. The Models page shows the exact
count for these rows, since 1,048,576 has no decimal K/M spelling.

`defaultContextWindow`, the fallback for model ids the catalog does not
describe, stays 1,000,000.
2026-10-22 13:34:14 +08:00
Yichen Jiang 70b945901d feat(llm-deepseek): read tool_update from the model listing
Both DeepSeek routes now take a listed model's Messages tool update mode
from `api_capabilities.anthropic_messages.tool_update`, alongside the
system prompt mode. `in-history` and `addition-only` replace the
bundled mode; any other value reads as `leading-only` and removes it.
An absent or `null` mode keeps the bundled one, so a listing that does
not declare the field leaves `deepseek-flash` on `addition-only`.

`system_prompt_update: null` likewise keeps the bundled mode now; it
previously read as `leading-only`, which a serializer writing empty
values as null would have turned into a cache-unfriendly downgrade.
2026-10-22 13:25:06 +08:00
Turtle 5f77d80555 Merge remote-tracking branch 'origin/master' into turtle/icon-tokens/icons-plugin-tokens-e20dec90761c
# Conflicts:
#	packages/client/ui-primitives/README.i18n.yaml
2026-10-22 13:24:28 +08:00
imccyu 9fb3a064ea Revert "fix(client): 将模型发布标记改为蓝色纯文字" (#6936) 2026-10-22 13:21:40 +08:00
Turtle 2e0ce8d448 Merge pull request #6924 from deepseek-harness/turtle/extract-bundle-lists-349637056285
refactor(boot): move official bundle lists out of app-boot
2026-10-22 13:16:20 +08:00
winewill 863a731f3f Merge master and retain packaging priority check in Windows CI 2026-10-22 13:13:05 +08:00
ZiyaZhang 83d3c6f8e9 Merge master into personalized pages experiment 2026-10-21 22:08:51 -07:00
winewill 59b24da527 fix(ci): pin Windows packaging helpers to workflow revision 2026-10-22 13:08:12 +08:00
Yudong Han aeb1078126 Merge pull request #6875 from deepseek-harness/feat/document-zoom-controls
refactor(ui-primitives): 共享文档缩放控件
2026-10-22 13:01:22 +08:00
Yudong Han d2c2bc1d27 Merge pull request #6538 from deepseek-harness/fix/model-release-blue-text
fix(client): 将模型发布标记改为蓝色纯文字
2026-10-22 13:00:27 +08:00
Turtle bde671f984 test(web): ignore scroll-driven animations when waiting for settled layout
The model selector's clipped-text fade runs on a scroll timeline, which
never finishes, so document-wide waits never settled.
2026-10-22 12:48:28 +08:00
yudshj ca575ff573 refactor(client): fade clipped selector text with CSS only
Replace the FadeText measuring component with a self scroll timeline: it is
active only while a box overflows, so the end mask applies to clipped text
alone. Browsers without scroll-driven animations keep the ellipsis.
2026-10-22 12:48:28 +08:00
yudshj 652747423c fix(client): fade clipped model selector text instead of ellipsizing
Model and effort text in the composer trigger, root menu values, and option
rows now clips and fades over its last 24px while it overflows, following the
measured-clip mask used by dockkit tab titles and PathLabel.
2026-10-22 12:48:28 +08:00
yudshj a6aaac3ce6 fix(client): render model release as blue inline text 2026-10-22 12:48:28 +08:00
yudshj 3baadfb103 docs(client): record the zoom controls elevation follow-up
Mark the inherited literal shadow and neutral border with a TODO pointing at
the elevation token rules.
2026-10-22 12:46:06 +08:00
yudshj a740d8edc2 fix(client): let owner classes place zoom controls regardless of CSS order
Move the default bottom offset into a zero-specificity :where(.panel) rule and
correct the README description of which properties center and animate the
toolbar.
2026-10-22 12:46:06 +08:00
yudshj a2bfffad24 fix(client): drop stale shown zoom when the committed zoom changes
Reset the transient showZoom value during render on the first committed zoom
change, document the toolbar data attributes and the owner className scope,
and give the handle its own @param ref.
2026-10-22 12:46:06 +08:00
yudshj 0b5ff6ff02 refactor(client): address zoom controls review
Derive the displayed zoom during render instead of syncing the prop through
an effect, reference DOCUMENT_ZOOM directly in both packages, cover the
unfitted non-preset state, and list ZoomControls among localized primitives.
2026-10-22 12:46:05 +08:00
yudshj f70a511cca refactor(ui-primitives): share document zoom controls
Move the floating zoom controls from the document preview into
ui-primitives with the shared scale limits, so every document renderer
offers the same controls. Document preview renders them through its zoom
viewport with fit width as the optional fitting action.
2026-10-22 12:46:05 +08:00
Turtle f0ced1aa47 docs(boot): state which bundle lists app-boot applies 2026-10-22 12:44:00 +08:00
imccyu 4349b4e5f2 fix(ui-chat): clip horizontal overflow in the chat transcript (#6930) 2026-10-22 12:43:26 +08:00
imccyu 96f9a2f6c0 fix(ui-chat): clip horizontal overflow in the chat transcript 2026-10-22 12:21:11 +08:00
Yichen Jiang 103dac1b3c feat(web): gate web_fetch and the DeepSeek web APIs on readiness
The Web search switch selects the DeepSeek Search and Fetch APIs together,
and both reuse the DeepSeek chat key by default. Neither provider answered
readiness, so a step without a usable key was offered web_search and
web_fetch whose every call failed, and a conversation on another provider's
model or behind a gateway sent the chat key to DeepSeek.

dsh-web gives WebFetchProvider an optional ready(llmProvider) and adds
ctx.web.fetchReady(), which asks only the provider fetch() selects, as
searchReady() does for search. tool-web registers web_fetch with a step
activation condition on fetchReady(), so a step that cannot fetch leaves the
tool out of declarations, guidance, and PTC bindings and refuses a call to
it as UNKNOWN_TOOL; without web_fetch the search guidance uses its
search-only text. The shipped HTTP fetch provider has no ready() and stays
admitted.

web-deepseek-api applies the API-key rules of Messages search to both APIs:
without a literal apiKey, an apiKeyEnv other than DEEPSEEK_API_KEY, or a
baseURL other than the default, the chat key goes only to the DeepSeek
routes and only to an API base on the official chat route's origin. The
refusal sits in the key's only resolver, and both providers' ready()
answers presence in the order an operation resolves credentials. The
transport repeats these checks so the package still does not depend on
dsh-web-search-deepseek.
2026-10-22 12:10:21 +08:00
imccyu 1276a8e13a ci: split pull-request CI into Linux, Windows, and Python workflows (#6898)
* ci: run native Windows PR jobs in a separate workflow

GitHub re-runs failed jobs only after a whole run finishes, so slow Windows jobs blocked re-running failed Linux jobs. CI Windows now owns the native Windows jobs and the Windows x64 Python runtime target, with its own required 'windows checks passed' verdict.

Refs #6893

* ci: run Python PR jobs in a separate workflow

Python rarely fails while the Windows lanes run long, so the Python SDK suite and the Linux/Windows x64 runtime matrix move into CI Python. The three pull-request workflows can now be re-run without waiting for each other, and branch protection gains "python checks passed" beside the existing verdicts.

Refs #6893

* docs: record the three PR verdicts in the failover runbook

* test: pin the shared environment across the PR workflows

* test: read the python SDK lane from CI Python

* test(sdk): parse session JSONL frames in the keyless smoke

Node 24.19 returned only the first frame of a concatenated zstd log, so the single JSON.parse saw just the header; 24.22 returns every frame and the JSONL body broke the parse. Read frames explicitly and select the main session by its header so both versions agree.
2026-10-22 04:09:35 +00:00
Yichen Jiang ea0d24b05a test: keep unit suites off the official model listing
Suites that boot the DeepSeek routes with a dummy or inherited key now
request `GET https://api.deepseek.com/models` on the official endpoint.
A unit-lane setup file rejects that request at once, so such a suite
serves the built-in catalog instead of reaching production or waiting
out the listing deadline on a network that never answers; suites that
exercise the listing answer `fetch` themselves. Other requests pass
through unchanged.
2026-10-22 12:06:36 +08:00
Turtle f60cbc909b docs(boot): point remaining bundle-list references at official-bundles
Move the template value assertions into the official-bundles tests so a
template edit leaves app-boot tests untouched.
2026-10-22 12:05:04 +08:00
Yichen Jiang 115fd8017e docs(llm-deepseek-api-key): name which hidden listing entries are logged 2026-10-22 12:04:25 +08:00
Saafo 6b25107abf ♻️ Defer global viewport styles from mobile input fixes 2026-10-22 12:04:03 +08:00
Saafo da54bbf3b0 🔀 Merge master into mobile input adaptation 2026-10-22 12:01:42 +08:00
Yichen Jiang 542e0530ea fix(llm-deepseek-api-key): report credential failures on the model request
On the official endpoint, `connect` waited for the listing view, whose
credential lookup rethrew a malformed key or a credential-store failure
before the agent loop recorded the prompt. A failed lookup now resolves
the connection from the current listing view, so `resolveAuth` reports
the failure on the model request and the turn keeps the prompt and the
failed attempt. A caller abort still rejects.
2026-10-22 12:01:37 +08:00
Turtle e0e37bfd3d refactor(boot): move official bundle lists out of app-boot
Profile templates and the default, optional, on-demand, retired, and
installation-owned bundle lists move to the new data-only package
@deepseek-ai/dsh-official-bundles, so editing a bundle list no longer
touches app-boot.
2026-10-22 11:56:47 +08:00
Turtle 3e5deca05d Merge remote-tracking branch 'origin/master' into turtle/plugin-sticky-header-29bfdb179e18
# Conflicts:
#	packages/client/ui-primitives/README.i18n.yaml
2026-10-22 11:37:26 +08:00
Yichen Jiang ef2d457a53 docs: regenerate module and event graphs for the shared listing 2026-10-22 11:30:03 +08:00
Yichen Jiang db915a239d test(llm-deepseek-api-key): pin the once-per-listing hidden-entry diagnostic 2026-10-22 11:25:35 +08:00
Yichen Jiang 08fa1e4d5a docs: describe the official route model listing
Document when `deepseek-official` follows the official `/models`
listing, its timing fields, customization precedence, and the forced
reload after Restore defaults in the API-key README, the shared
transport README, and the provider guide. Record the decision in a new
Agent Note, keep the account note's statement of where the shared
listing code lives current, and regenerate the configuration catalog.
2026-10-22 11:23:52 +08:00
winewill 5110ca9153 perf(desktop): integrate Windows packaging and align compression defaults 2026-10-22 11:21:41 +08:00
Yichen Jiang e90f65c0d7 feat(llm-deepseek-api-key): follow the official /models listing
While `models` equals the built-in catalog and the Messages origin is
https://api.deepseek.com, `deepseek-official` serves the official
`GET /models` listing, authenticated with the API key in `x-api-key`.
Loading, refresh, fallback, merging, and the client-version gate match
the account route: the first access waits up to `modelsTimeoutMs`, a
listing older than `modelsMaxAgeMs` refreshes in the background on the
next access, a failure keeps the last good listing or the built-in
catalog, and listed models this build cannot serve are refused before
network I/O.

A customized `models` list replaces the catalog and requests nothing.
Switching between a customized catalog and the listing discards the
cached listing, so Restore defaults reloads it on the next access.

Suites that boot the route on the official endpoint now answer the
listing themselves instead of reaching the network.
2026-10-22 11:19:22 +08:00
Yichen Jiang a0324bc09f refactor(llm-deepseek): share the /models listing machinery
Move the account route's `/models` request and decoding, its
credential-scoped listing cache, and listed-catalog resolution with the
version gate into the shared DeepSeek Messages library, so a second
route can follow the same listing. Names drop the account prefix, the
cache takes each access's endpoint settings with the call, and it gains
`invalidate()` for a forced reload. `isBuiltInCatalog()` recognizes the
catalog an uncustomized `models` setting resolves to.

The account route keeps its behavior; only its imports and the
override diagnostic wording ("model listing") change.
2026-10-22 11:11:28 +08:00
Yichen Jiang 80eca619ef refactor(web-search): admit web_search through step activation conditions
Master's step-scoped conditional activation resolves each tool's condition
before a model step assembles, and both the declarations and dispatch read
that admitted set. Prompt assembly no longer carries tool declarations, so
tool-web's system-prompt/assemble listener could not remove web_search any
more, and removing a declaration there never stopped a repeated call from
running.

tool-web now registers web_search with a when condition that asks
ctx.web.searchReady() about the route of the step's model selection, and its
guidance section renders from the step's admitted set. A step that does not
admit the tool leaves it out of declarations, guidance, and PTC program
bindings, and refuses a call to it as UNKNOWN_TOOL without reaching a
provider. Tests that execute web_search directly now run inside a resolved
step, as the executor requires for conditional registrations.

Docs, the readiness Agent Note, and the event producer/consumer table follow
the new mechanism; the tools README gains an explicit conditional-tools
anchor for cross-locale links.
2026-10-22 10:57:09 +08:00
Yichen Jiang 6921551fc4 test(snapshots): refresh this branch's scenarios after merging master
Master declares exit_plan_mode only in plan mode, so this branch's own tool
schema sidecars for web-search-unconfigured and pi-ai-conversation-updates
drop it. The merge took master's pi-ai-conversation-updates session, whose
request headers still listed web_search; the refresh records the moonshotai
route without it again.
2026-10-22 10:56:56 +08:00
Yichen Jiang 19dcdca7b6 test(forge): compose the native tool executor in the session Loader test
Tool registration and scoped executors are now separate plugins, so a
composition without an executor cannot run the bash tool and the Agent
goes idle before its first model request. The forge-session Loader
composition now includes dsh-tool-executor-native, as the other real
composition tests do.
2026-10-22 10:54:25 +08:00
fz 1453c0ff86 Merge pull request #6864 from deepseek-harness/fix/cua-launch-readiness
fix(cua): wait for application launch readiness before reading PID
2026-10-22 10:51:30 +08:00
Yichen Jiang dd7e861007 fix(forge): ship plugin locale metadata for the forge packages
The Japanese plugin metadata gate requires every plugin package to carry
locale/en.json and locale/ja.json, and the three forge packages had no
locale directory. Each now has English, Chinese, and Japanese titles and
descriptions, exposed through exports and published through files like
its siblings.
2026-10-22 10:48:28 +08:00
Yichen Jiang 447fb0e1c0 Merge branch 'stack/pr-6426' into stack/pr-5960
# Conflicts:
#	docs/capability-seams.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
2026-10-22 10:41:17 +08:00
Yichen Jiang d373778fd8 Merge remote-tracking branch 'origin/master' into worktree/web-search-dynamic-tool-e26c2b
# Conflicts:
#	packages/web/tool-web/tests/tool-web.spec.ts
#	snapshots/session/pi-ai-conversation-updates/session.v4.jsonl
2026-10-22 10:32:41 +08:00
Yichen Jiang 67d5fd7b1e fix(web): add the Japanese label for a sending composer
The Japanese Conversation dictionary must carry every key, and the
locked composer's input.sending label had only Chinese and English
entries.
2026-10-22 10:31:35 +08:00
Yichen Jiang da864e3377 Merge remote-tracking branch 'origin/master' into stack/pr-6426 2026-10-22 10:30:26 +08:00
Stevring dc83ed11bc refactor(client): simplify and cache mobile device detection 2026-10-22 10:18:39 +08:00
ZiyaZhang 776f1ccf48 revert(experimental): keep the original descriptions of the other official plugins
Routing hints belong in the official-plugins skill, not in the
user-visible descriptions of thirteen other plugins; the plugin pages,
permission pages and the replay goldens return to their master text.
2026-10-21 13:08:09 -07:00
ZiyaZhang 2eb1b89cac refactor(experimental): share the dsh-action parser and drop the cross-experiment coupling
The fenced-block parser moves to the zero-dependency package
@deepseek-ai/dsh-util-action-block, which the client bundle inlines;
Personalized pages and Plugin recommendations both read answers through
it and no longer import or edit each other. Personalized pages yields:
a page block in an answer or turn that also carries another action's
block is logged but never offered, and a later other-action block
retracts this turn's undecided page card. Plugin recommendations never
yields and keeps one plugin card per turn of its own.
2026-10-21 13:08:06 -07:00
ZiyaZhang d4800ceca2 refactor(experimental): read answer blocks through the shared parser subpath
The recommendation card imports actionBlocksOf from the personalized pages
action-block subpath instead of keeping its own copy, and the Web replay
tool schema golden follows the merged master.
2026-10-21 11:10:10 -07:00
ZiyaZhang e11ce354c1 Merge feat/creation-recommendations into plugin recommendation experiment 2026-10-21 11:00:50 -07:00
ZiyaZhang fde2236683 build(experimental): publish the official-plugins skill file
The package files constraint lists per-package extras explicitly; the
skill provider reads skills/official-plugins.md at load time.
2026-10-21 10:58:36 -07:00
ZiyaZhang f837b90882 test(web): refresh the tool schema golden after merging master
master dropped exit_plan_mode from the shipped tool declarations.
2026-10-21 10:52:38 -07:00
ZiyaZhang 09373c62aa style(experimental): satisfy lint in the personalized pages block readers
Wraps two lines over the length limit and reads the regex group without a
non-null assertion.
2026-10-21 10:52:12 -07:00
ZiyaZhang 50be7d37c9 feat(experimental): recommend official plugins through dsh-action answer blocks
Replaces list_official_plugins, suggest_plugin and the routing prompt
section with the official-plugins skill, whose load appends the live
catalog, and an enable_official_plugin block at the end of the answer.
The card reads catalog text from the Host, Enable goes through the plugin
manager, and each outcome reaches the model as a status notice on the
next request. Adds the queuedMessageLimit option, en/zh/ja card copy and
a re-recorded persistence change and Web replay.
2026-10-21 10:50:48 -07:00
std4453 c7d282bfb0 Merge pull request #6615 from deepseek-harness/fix/image-drag-plugin-ownership
feat(file-drag): 独立插件交付原始文件并支持图片平移
2026-10-22 01:50:38 +08:00
ZiyaZhang 3c5111bffa fix(experimental): share the proposal block parser with the Client bundle
The Client card imports actionBlocksOf from the Host package's new
./action-block subpath instead of keeping a copy, and the client bundle
admits that pure parser as an inline-safe entry; the tool catalog test
no longer expects suggest_action, and over-long lines are wrapped.
2026-10-21 10:50:10 -07:00
ZiyaZhang 8efbfaf4e0 Merge master into personalized pages experiment 2026-10-21 10:39:35 -07:00
ZiyaZhang e4aebaded6 feat(experimental): keep one card per turn across both card actions
An answer's last valid card block, page or official plugin, is its card,
and the first answer with a card holds the turn. A page block that is not
the turn's card is logged as suppressed so the creation card stays hidden.
Exports the shared block reader for the official-plugin experiment, covers
the remaining uncovered branches, and marks the Client block reader as a
per-bundle copy.
2026-10-21 10:22:14 -07:00
Turtle 9009edb7c2 Merge remote-tracking branch 'origin/master' into turtle/icon-tokens/icons-plugin-tokens-e20dec90761c
# Conflicts:
#	packages/client/ui-primitives/src/icons/index.tsx
2026-10-22 01:13:40 +08:00
zhc 8eef248d1c Merge master into fix/image-drag-plugin-ownership 2026-10-22 01:10:50 +08:00
Turtle f58b6c41bb chore(extensions): regenerate the Cordis inspect catalog for icon replacements 2026-10-22 01:00:52 +08:00
Turtle 6470b39557 fix(client): harden theme icon replacements and add a real-composition test
Store a frozen copy of ThemeDefinition.icons, treat null as absent,
require replacement element ids under the symbol id, document the trust
level and ignored root attributes, and boot a theme plugin through the
Loader to check the sprite swaps and restores icons.
2026-10-22 00:59:21 +08:00
Magolor 926a4580a1 Merge pull request #6871 from deepseek-harness/model-order
feat(settings): 支持提供商内模型拖动排序
2026-10-22 00:51:26 +08:00
Turtle c422eac3b9 chore(extensions): regenerate the Cordis inspect catalog for theme icons 2026-10-22 00:46:06 +08:00
Turtle e3490f371a test(client): drop an unneeded cast in the icon sprite spec 2026-10-22 00:34:57 +08:00
Turtle c3eead771d feat(client): let themes replace product icons through an SVG sprite
Product icons drew fixed inline paths, so a theme could only recolor them.
Each glyph weight is now a dsw-icon-<glyph>-<weight> symbol: ui-primitives
keeps the default SVG documents and icons render one <use> of their symbol,
while ui-theme keeps a document sprite filled from the active theme's
ThemeDefinition.icons replacements or the defaults. Replacements may be
multi-color and paint with currentColor or --dsw-* tokens.

Refs #6894
2026-10-22 00:21:07 +08:00
Magolor 4527424c51 Merge master into model-order and resolve documentation pairing 2026-10-22 00:19:49 +08:00
zhc 98609cd7d7 Merge master into fix/image-drag-plugin-ownership 2026-10-22 00:18:25 +08:00
Yichen Jiang 2bda39dc3a Merge pull request #6024 from deepseek-harness/worktree/conditional-tools
feat(tools): add step-scoped conditional activation
2026-10-22 00:14:48 +08:00
zhc bc1bff3c82 test(web): pin message snapshot calendars across midnight 2026-10-22 00:14:00 +08:00
Shigma 230ac40b9f Merge pull request #6192 from deepseek-harness/refactor/cordis-config-access
refactor(cordis): unify configuration access and consumers
2026-10-22 00:11:57 +08:00
ZiyaZhang 7cafebe528 Merge feat/creation-recommendations into plugin recommendation experiment 2026-10-21 09:11:34 -07:00
Turtle a55c9c5ddf fix(ui-sidebar-right): keep the panel still on open for reduced motion
The reduced-motion rule only matched the closed selectors, so the more specific open rule kept its slide. Match the open selectors too.
2026-10-22 00:10:29 +08:00
ZiyaZhang ec1021b052 Merge master into plugin recommendation experiment 2026-10-21 09:09:40 -07:00
Yichen Jiang 8a6f773867 Merge pull request #6831 from deepseek-harness/worktree/composer-image-preupload
feat(web): upload composer images when they are added
2026-10-22 00:06:37 +08:00
zhc ea7c67887b Merge master into fix/image-drag-plugin-ownership 2026-10-21 23:52:53 +08:00
Yichen Jiang 47b6867566 fix(tools): settle cancelled admitted calls through cancellation
A step's tool set no longer empties when the step is cancelled. The shared
pipeline already settles unstarted calls as cancelled; keeping admission lets
those calls keep their finalizers, and a wrapper result completed at
cancellation now reports cancellation instead of UNKNOWN_TOOL.

exit_plan_mode checks for a missing Agent explicitly instead of asserting it.

Document the activation types on the tools subsystem page, the turn/end code
for a failed tool condition, the replacement and no-set consequences in the
upgrade guide, and the admission rule in the parallel tool-call note.
2026-10-21 23:51:22 +08:00
Turtle 36f0bae72e fix(ui-primitives): document the top-strip cap for hover cards
Name the Menu anchor gap once, describe the HoverCard exception in the
README and JSDoc, and cover a top-side menu from an anchor in the strip.
2026-10-21 23:45:37 +08:00
Kaige-Gaoandimccyu 0778dec09f feat(ui-user-questions): show answered batches as shared message bubbles (#6362)
* feat(ui-user-questions): show answered batches as shared message bubbles

* refactor(ui-primitives): share clipboard confirmation lifecycle

* test(web): include answered question bubbles in steering goldens

* fix(ui-user-questions): keep answered turns collapsible

* test(ui-user-questions): drop the removed compactAnswer expectation

master removed ChatTurnProcessPresentation.compactAnswer in aaeb344
("fix(chat): keep the reply gap on a folded final answer"); reply spacing
now comes from the adjacent-row rules. The rebase left this assertion
behind, so the parametrized replay/live/prepend cases failed on every CI
coverage lane even though typecheck stayed green: toMatchObject accepts
the stale property.

* fix(ui-user-questions): address answer bubble review feedback

* test(web): observe file card styles after both cards mount

---------

Co-authored-by: imccyu <[email protected]>
2026-10-21 23:44:02 +08:00
Yichen Jiang d8c99baa28 docs: record batch tool admission and fit the turn-flow summary
The tool executor merge added ToolBatchInput.toolSet; the subsystem page's type-equivalence block now matches it. The architecture turn-flow line names step-admitted tool schemas in one line so the page stays within its word budget.
2026-10-21 23:36:35 +08:00
Yichen Jiang 268d945470 test(web): size the oversized Markdown image past the new image limit
The /api/file route bounds files by maxImageBytes, whose default is now
50 MiB, so the 20 MiB + 1 byte fixture no longer exceeded it and the
route served it with 200 instead of 413.
2026-10-21 23:36:34 +08:00
Yichen Jiang ce24d53eb5 Merge remote-tracking branch 'origin/master' into worktree/dynamic-tool-activation-pr-64796e 2026-10-21 23:32:43 +08:00
Yichen Jiang 207b30d299 Merge remote-tracking branch 'origin/master' into worktree/dynamic-tool-activation-pr-64796e
# Conflicts:
#	docs/config-catalog.md
#	docs/config-catalog.zh.md
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	docs/persistence-catalog.i18n.yaml
#	docs/persistence-catalog.md
#	docs/persistence-catalog.zh.md
#	docs/persistence-schema.json
#	packages/core/agent-loop/src/agent.ts
#	packages/core/agent-loop/src/tool-calls.ts
#	packages/core/agent-loop/tests/tool-updates.spec.ts
#	packages/core/system-prompt/README.i18n.yaml
#	packages/core/system-prompt/README.md
#	packages/core/system-prompt/README.zh.md
#	packages/core/tool-executor-ptc/src/ptc.ts
#	packages/core/tool-executor-ptc/tests/ptc.spec.ts
#	packages/core/tools/README.i18n.yaml
#	packages/core/tools/README.md
#	packages/core/tools/README.zh.md
#	packages/core/tools/src/index.ts
#	packages/extensions/tool-cordis/src/api-catalog.ts
#	packages/plan/plan-mode/tests/plan-mode.spec.ts
#	snapshots/session/cordis-inspect-jsdoc/session.v4.jsonl
2026-10-21 23:32:15 +08:00
Turtle f2a52f6f3c test(ui-sidebar-right): pin the settling slide styles
Check that the slide rules list the same docked items as the opening effect and that the settling rule overrides the open rule. Name the shared selector list on both sides and describe the opening step in the module header.
2026-10-21 23:32:12 +08:00
Shigma 719870a489 Merge remote-tracking branch 'origin/master' into refactor/cordis-config-access
# Conflicts:
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	packages/client/ui-chat/tests/config.host.spec.ts
2026-10-21 23:30:16 +08:00
ZiyaZhang ac61d7ea5d Merge master into personalized pages experiment 2026-10-21 08:29:27 -07:00
ZiyaZhang 58dea020c0 feat(experimental): propose personalized pages with a dsh-action answer block
The proposal is a fenced dsh-action JSON object at the end of the final
answer instead of a suggest_action tool call. The projection reads the
last valid block of an assistant message, keeps one card per turn, and
marks a proposal written while queuedMessageLimit user messages are
queued as suppressed; the chat hides dsh- fences from rendered answers.
The Web replay fixture, tool catalog and READMEs follow the toolless
mechanism.
2026-10-21 08:27:41 -07:00
_Kerman 0d9036150e Merge pull request #6755 from deepseek-harness/xtr/agent-initialization-flow
refactor(agent-loop): 统一 Agent 初始化与回滚
2026-10-21 23:20:07 +08:00
Yichen Jiang dd568f76de Merge remote-tracking branch 'origin/master' into worktree/composer-image-preupload
# Conflicts:
#	packages/test-support/client-runtime/README.i18n.yaml
2026-10-21 23:17:57 +08:00
TurtleandShigma 4f7ebe8d34 test(web): cover conversation view tab hit testing in the browser
Co-authored-by: Shigma <[email protected]>
2026-10-21 23:15:02 +08:00
Turtle 3acbd08867 fix(ui-sidebar-right): slide the panel in on its first open
The first expansion of an empty column mounts the seeded tab already open, so it appeared in place instead of sliding in like later opens and every close.
2026-10-21 22:59:34 +08:00
zhc 8e7bce82fb Merge master into fix/image-drag-plugin-ownership 2026-10-21 22:53:25 +08:00
_Kerman 81a28b335b docs: regenerate module graph for CUA Node runtime peer 2026-10-21 22:51:52 +08:00
Turtle d343cd2162 fix(ui-primitives): keep header menus next to their trigger on macOS desktop
The desktop top clearance pushed every portalled overlay at least 68px from
the viewport top, so menus opened from Conversation header controls inside
that strip landed about 30px below their trigger. Cap the clearance at the
position directly below a visible anchor.

Refs #6886
2026-10-21 22:51:47 +08:00
TurtleandShigma d229d1cf75 docs(ui-conversation): state the tab press target comments exactly
Co-authored-by: Shigma <[email protected]>
2026-10-21 22:48:12 +08:00
TurtleandShigma 11e1580b82 fix(ui-conversation): keep the active tab bar visible over Trajectory and widen tab targets
Co-authored-by: Shigma <[email protected]>
2026-10-21 22:45:28 +08:00
Magolor be83795e6a chore(test): retire moved animation test cast allowance 2026-10-21 22:21:52 +08:00
_Kerman 4b2c8aed30 Merge master into xtr/agent-initialization-flow 2026-10-21 22:16:50 +08:00
_Kerman ded424132e Merge pull request #6619 from deepseek-harness/xtr/session-store-write-lifecycle
fix(session): 约束托管 Session 的事件发布时序
2026-10-21 22:10:32 +08:00
turtle1999 0d3beda4d2 test(snapshots): refresh background-wait-steer for the inline completion notice 2026-10-21 22:08:04 +08:00
zhc d1ad034443 fix(ui-primitives): dispose copy feedback on unmount 2026-10-21 22:03:53 +08:00
Magolor ec387796af fix(settings): address model reorder review findings 2026-10-21 21:57:08 +08:00
turtle1999 389b729ac8 Merge remote-tracking branch 'origin/master' into turtle/async-notice-efficiency
- tool-jobs: inline a completion notice only when the complete text fits inlineOutputBytes and the producer cap; otherwise point at job_output, whose result the spill policy bounds
- subagent: drop toolCallsThisStep; omit the closing text while the child's last logged tool call is its message to the parent
2026-10-21 21:44:47 +08:00
zhc 4e85a34e45 fix(file-drag): complete Japanese locale registration and metadata 2026-10-21 21:41:16 +08:00
_Kerman 1acbf46d53 fix(ui-workspace): preserve explicit navigation after startup failure 2026-10-21 21:36:49 +08:00
ZiyaZhang 13f46b9c71 fix(experimental): keep the manifest files order and cover proposal replacement branches 2026-10-21 06:36:09 -07:00
zhc a0b45086da Merge master into fix/image-drag-plugin-ownership
保留文本交互和文件拖放注入,合并依赖及生成目录。
2026-10-21 21:22:17 +08:00
ZiyaZhang 853a18cd87 fix(experimental): add Japanese bundle metadata and follow the More-list order
Both personalized page packages ship locale/ja.json beside en and zh, and
the browser package exposes its locale files like its siblings. The plugin
manager e2e expects the More list in the order the page declares. Refresh
the Web replay golden after the master merge.
2026-10-21 06:15:03 -07:00
ZiyaZhang d28efd25a2 Merge master into personalized pages experiment 2026-10-21 06:10:18 -07:00
ZiyaZhang b51e33d5ca feat(experimental): replace unstarted cards, limit queued turns, show content, tighten routing
A newer proposal in the same conversation replaces an unstarted card; the
next user message no longer expires it, so one unstarted card exists per
conversation (projection state version 7, phase replaced).

queuedMessageLimit (default 2) refuses a card while the user has that many
messages queued for later turns.

The card shows supplied initialContent as folded data below the plan.

The personalized-pages skill is rewritten around the chosen scenarios with
a shorter catalog entry that loads before any file reading. Paired sampling,
five runs per input on the same build: 63/90 to 81/90 cards on the
regression set with zero false cards; the log-tab inputs went from one to
four of five to five of five with two tool calls each.

Regenerated the config, cordis and plugin package catalogs, re-recorded
README pairing, and refreshed the Web replay golden.
2026-10-21 06:05:25 -07:00
winewill 69b446666b Merge pull request #6711 from deepseek-harness/fix/desktop-update-background-focus
fix(desktop): keep update dialogs in-window and follow app theme
2026-10-21 20:51:20 +08:00
Magolor 5d3ea87d28 refactor(web): share workspace drag behavior with model lists 2026-10-21 20:45:12 +08:00
Magolor f879017105 fix(tooling): 保留产品标识并修复 vendor 重命名检查 (#6716)
* fix(tooling): 保留产品标识并校验 vendored 包引用

* fix(tooling): preserve module rewrites in product-name exceptions
2026-10-21 20:42:35 +08:00
_Kerman 43cbcf083d Merge master into xtr/session-store-write-lifecycle 2026-10-21 20:29:48 +08:00
Magolor dae4bfa413 feat(settings): use a three-line model reorder handle 2026-10-21 20:11:39 +08:00
winewill 73574aff59 perf(windows): prepare Electron and primary runtime concurrently 2026-10-21 20:05:20 +08:00
Magolor dd466092de test(browser-use): 对齐 MCP 等待期限并完成失败清理 (#6394)
* test(browser-use): 对齐 MCP 等待期限并完成失败清理

* test(browser-use): fail promptly on unexpected process probe errors
2026-10-21 20:03:07 +08:00
_Kerman 20f757518d docs(ptc): migrate consumers to the shared program format type 2026-10-21 19:51:53 +08:00
_Kerman 67911ed176 fix(ptc): describe fresh Script failures without retained state 2026-10-21 19:51:52 +08:00
_Kerman 09c7036e05 fix(ptc): retain control transport errors in worker exit diagnostics 2026-10-21 19:51:51 +08:00
_Kerman c905726461 fix(ptc): allow local process declarations in function bodies 2026-10-21 19:51:50 +08:00
_Kerman 25ede01a8b fix(ptc): preserve compact declarations and native syntax diagnostics 2026-10-21 19:51:49 +08:00
fz 4fbe6e3f42 Merge pull request #6523 from deepseek-harness/feat/sdk-approval-rpc
SDK 支持将内部审批交由 stdio 客户端处理
2026-10-21 19:45:10 +08:00
Turtle a8e48df916 Merge pull request #6840 from deepseek-harness/turtle/focus-click-rename-9a2d9fb21ffb
fix(web): ignore window-activating click in session title double-click rename
2026-10-21 19:36:28 +08:00
winewill eda31bfc29 perf(windows): store installer uploads without recompression 2026-10-21 19:35:40 +08:00
_Kerman 04609dc0de Merge xtr/session-store-write-lifecycle into xtr/agent-initialization-flow 2026-10-21 19:33:26 +08:00
_Kerman fef2f3d06e Merge master into xtr/session-store-write-lifecycle 2026-10-21 19:27:56 +08:00
_Kerman 46fdc70d66 Merge master into xtr/session-store-write-lifecycle 2026-10-21 19:27:56 +08:00
Turtle 1203a3151e Merge remote-tracking branch 'origin/master' into turtle/plugin-sticky-header-29bfdb179e18
# Conflicts:
#	packages/client/ui-plugin-manager/src/client/index.ts
2026-10-21 19:21:21 +08:00
Turtle e19de3b6e5 Merge remote-tracking branch 'origin/master' into turtle/focus-click-rename-9a2d9fb21ffb 2026-10-21 19:20:54 +08:00
winewill dffe28972b docs(desktop): document retained caption and mandatory focus behavior 2026-10-21 19:17:44 +08:00
winewill 5f1413f49b fix(desktop): address update overlay review and startup coverage 2026-10-21 19:01:09 +08:00
Turtle c408ae1390 test(plugin-manager): pin the title-only header motion away from font-dependent widths
Linux CI lays the title-only header at 470px with the actions beside the
title, while macOS wraps them, so that width depends on the platform
font. The title-only case now records 1280, 600, and 390px; the tabs case
keeps all four widths.
2026-10-21 18:59:23 +08:00
ZiyaZhang 3a8c1daf66 Merge master into personalized pages experiment
Follow the tool executor refactor: the tool/ptc-dispatch event type now comes
from dsh-tool-executor-ptc, and the tests invoke tools through dsh-tool-testkit.
Regenerate the doc graphs, tool catalog and persistence schema after the merge
and re-record their translation pairing.
2026-10-21 03:50:29 -07:00
Tianyi Cui 2a48671b83 Merge pull request #6472 from deepseek-harness/worktree/session-copy-markdown
feat(session): copy sessions as Markdown with source paths
2026-10-21 18:49:57 +08:00
Magolor c10533a6ea feat(settings): support reordering provider models 2026-10-21 18:49:34 +08:00
imccyu ad340232fa Merge pull request #6792 from deepseek-harness/when-access
feat(remote-access): 插件注册跟随访问状态(whenAccess),index 按 Peer 注入访问快照
2026-10-21 18:42:23 +08:00
Yudong Han d70d52ceb2 Merge pull request #5609 from deepseek-harness/fix/question-composer-compact-details
fix(ui-user-questions): keep long questions answerable
2026-10-21 18:40:46 +08:00
winewill b6e387e5b3 fix(windows): normalize packaging step scheduling priority 2026-10-21 18:38:30 +08:00
ZiyaZhang baacca5658 test: refresh personalized page Web replay after master merge 2026-10-21 03:37:55 -07:00
winewill 1d41b28ed6 Merge master and fix desktop update CI integration 2026-10-21 18:37:44 +08:00
ZiyaZhang d7574c8d9a fix(experimental): add Japanese copy to personalized page cards 2026-10-21 03:35:33 -07:00
ZiyaZhang b6c3f57c6c Merge master into personalized pages experiment 2026-10-21 03:35:02 -07:00
Wenlu Wang 58b3dc70e8 Merge pull request #6820 from deepseek-harness/fix/6043-middle-click-close-tabs
feat(client): 支持右侧标签页中键关闭
2026-10-21 18:34:49 +08:00
winewill 4ff619c0e3 fix(desktop): remove cancel action from mandatory native fallback 2026-10-21 18:28:49 +08:00
winewill 8d66b7236b fix(desktop): use policy headline in native update fallback 2026-10-21 18:26:04 +08:00
_Kerman 7c86126642 Merge latest master into xtr/ptc-vm-script 2026-10-21 18:25:41 +08:00
winewill 34560ac294 fix(desktop): open policy download page from native update fallback 2026-10-21 18:23:42 +08:00
_Kerman 5058accc0d docs(agent-loop): refresh event references after base merge 2026-10-21 18:23:01 +08:00
_Kerman 0e7b3701cb Merge xtr/session-store-write-lifecycle into xtr/agent-initialization-flow 2026-10-21 18:22:35 +08:00
Turtle a3b26c896b Merge remote-tracking branch 'origin/master' into turtle/plugin-sticky-header-29bfdb179e18 2026-10-21 18:21:31 +08:00
_Kerman 1c6c558432 Merge pull request #6539 from deepseek-harness/xtr/tool-executor-plugins
refactor(tools): split native and PTC execution into scoped plugins
2026-10-21 18:17:35 +08:00
imccyu 31ebfa7aa9 fix(settings-account): 撤权时丢弃迟到的帧并清空登录状态 2026-10-21 18:16:28 +08:00
_Kerman b43a01e285 Merge master into xtr/session-store-write-lifecycle 2026-10-21 18:16:23 +08:00
_Kerman 7a8f417b5e Merge master into xtr/session-store-write-lifecycle 2026-10-21 18:16:23 +08:00
winewill 38816d8c84 fix(desktop): offer native recovery for mandatory view load failures 2026-10-21 18:13:53 +08:00
yudshj 0231bbccb8 Merge remote-tracking branch 'origin/master' into pr-5609-merge 2026-10-21 18:12:58 +08:00
Tianyi Cui 84cfd5f76d Merge master 98fb6ddd into session-copy-markdown
Integrate exact origin/master 98fb6dddb86a9f631054417d48bfc07c06e95a68. Adopt master's canonical narrow-layout overflow probe instead of the branch-local equivalent. Preserve Session Markdown copy, source-path metadata, and locale integration.
2026-10-21 18:10:46 +08:00
fz a275fdd789 fix(cua): retain application through launch readiness 2026-10-21 18:10:41 +08:00
Chinesezjc 4607c360ec Merge commit '98fb6dddb86a9f631054417d48bfc07c06e95a68' into feat/schedule-clear-ended
# Conflicts:
#	packages/session/message-quotes/src/client/locales.ts
2026-10-21 18:09:51 +08:00
kingwl 3d746bc794 fix(client): preserve tab drags during chorded middle clicks 2026-10-21 18:09:29 +08:00
kingwl 9c5958b07b fix(client): close sidebar tabs with middle click
Refs #6043
2026-10-21 18:09:29 +08:00
_Kerman 8569a3f798 fix(tools): add executor locale metadata 2026-10-21 18:04:42 +08:00
Tianyi Cui 0891effd68 test(web): probe locale dialog scroll container
Anchor the desktop-locale overflow probe to the locale selector's nearest horizontal scroll container instead of nav's next sibling. Master moved the settings header between nav and options, so the old positional target put the probe in a fixed header row and the negative control could not overflow.
2026-10-21 18:04:02 +08:00
fz 98bea53628 Merge origin/master after upstream multilingual CI fixes 2026-10-21 18:03:50 +08:00
Yichen Jiang 076d8a05f1 test(snapshots): declare exit_plan_mode only after plan-mode entry in background-wait-steer
The scenario arrived from master recorded with exit_plan_mode in a Session that never enters plan mode. Its fixture cannot be refreshed until #6710 lands, so the schema is removed by hand to match the replayed request.
2026-10-21 18:03:40 +08:00
Yichen Jiang 6dafa66dc6 docs(attachment): state the relaxed default image limits
The default per-image limit is 50 MiB and the default per-side limit is
65,535 pixels, but the admission summaries in the attachment subsystem
page and the attachment-local README still named 20 MiB and 8192 pixels.
The session-controller note on the /api/file byte limit and the tool-fs
note on read_image source caps also named the old 20 MiB maxImageBytes
default.
2026-10-21 18:02:38 +08:00
_Kerman d98025a089 Merge origin/master into xtr/tool-executor-plugins 2026-10-21 18:01:55 +08:00
Chinesezjc acc3f3a779 Merge remote-tracking branch 'origin/master' into fix/weighted-approval-app-check 2026-10-21 17:59:50 +08:00
Yichen Jiang 2f09a68329 Merge remote-tracking branch 'origin/master' into worktree/dynamic-tool-activation-pr-64796e 2026-10-21 17:58:24 +08:00
Turtle 16cf3316e6 Merge remote-tracking branch 'origin/master' into turtle/image-cache-stale-0a243f674e12
# Conflicts:
#	packages/api/session-controller/README.i18n.yaml
#	packages/client/ui-primitives/README.i18n.yaml
2026-10-21 17:58:20 +08:00
mektpoy a07e889dc1 test(ui-settings-account): share the access guard helpers in the revocation spec
Both revocation cases restated the same grant and revoke helpers; one module-level pair keeps the two
cases reading the same install and disposal order.
2026-10-21 17:57:53 +08:00
imccyu 7f7e1efdff fix(settings-account): 撤权时失效在途账号读取并清空账号快照 2026-10-21 17:57:52 +08:00
imccyu 0c619816c3 docs(plugin-manager): pluginNavigation 写明为可选读取的服务 2026-10-21 17:57:52 +08:00
imccyu d5d43d931d fix(remote-access): 按评审修正访问门控边界,插件导航改为可选依赖
- ui-entry-customization、voice-input 不再把 pluginNavigation 当必需服务:服务不在时隐藏「浏览插件」与「打开 / 查看详情」按钮,插件照常启动
- plugin-manager 每次授权时刷新已加载的列表
- settings-account 账号设置分区与 sign-in 一并按 account/watch 门控,revision 恢复 master 原样
- schedule 会话头部提醒另按会话 useAccess('schedule/list') 渲染
- 导出 staticAccessPlugin;补 whenAccess 归属、撤权与读者页启动测试
- 同步 README、web-server 子系统文档、webserver 包描述与 Agent Note
2026-10-21 17:57:52 +08:00
imccyu 09899f3a0f feat(remote-access): 插件注册跟随访问状态(whenAccess),index 按 Peer 注入访问快照
- remote-access 新增 whenAccess / whenResourceAccess,ui-session、ui-workspace 各自提供 whenAccess
- schedule、plugin-manager、settings-general、settings-account、directory-picker-native 的入口包进 global whenAccess;消息反馈按钮按会话 useAccess 渲染
- webserver/index-inject 带 Peer,renderIndex / collectIndexInjections 需要 Peer,remote-access 注入 snapshotFor(peer.principal)
- client-test-runtime 新增 StaticRemoteAccess、staticAccessPlugin 与 TestClientOptions.access
- implemented Agent Note 2026-10-21-when-access 与升级指南 webserver-index-peer

Refs #2940
2026-10-21 17:57:51 +08:00
Yichen Jiang 8246e568e7 Merge remote-tracking branch 'origin/master' into worktree/composer-image-preupload 2026-10-21 17:57:39 +08:00
imccyu 3e547bbd54 Merge pull request #6850 from deepseek-harness/07akioni/japanese-locale-completeness
fix(i18n): 补全日语元数据、授权提示和 Windows 安装器
2026-10-21 17:56:15 +08:00
Chinesezjc 1665223358 feat(ci): publish the approval decision label from a dedicated approval App
Mint the check-run token from a dedicated approval App instead of a shared
automation App, so the identity that owns the decision check suite is specific to
this gate. The workflow reads `vars.DSH_APPROVAL_APP_CLIENT_ID` and
`secrets.DSH_APPROVAL_APP_PRIVATE_KEY`, and the token stays scoped to
`checks: write`.
2026-10-21 17:50:27 +08:00
Tianyi Cui c7278aa4d5 fix(message-quotes): complete Japanese locale registration
Add the Japanese quote action dictionary and register it with the existing Chinese and English dictionaries. The package entered master alongside the Japanese locale lane without a Japanese entry.
2026-10-21 17:46:00 +08:00
07akioni b8c907dbd1 test(web): fix Japanese settings overflow probe 2026-10-21 17:44:32 +08:00
Tianyi Cui af4360620e test(snapshots): align background wait present schema
The SDK background-wait-steer fixture was authored on a branch that predates master's present description change (1ec651f2098). Refresh only its expected present tool description; committed Session generations and product behavior stay unchanged.
2026-10-21 17:41:05 +08:00
Chinesezjc ac465000d7 fix(client): complete the merged locale and snapshot fixtures 2026-10-21 17:33:43 +08:00
Yichen Jiang 85181cad37 fix(web): show image upload failures in a tooltip
A refused image thumbnail named its reason only through a native title,
which browsers show late or not at all, so hovering it showed nothing.
The refused and the failed image thumbnails now use the shared tooltip:
the Host's refusal opens on hover or keyboard focus and a tap or click
pins it, and the retry hint of a failed upload opens on hover or focus.
2026-10-21 17:25:57 +08:00
Tianyi Cui e4e1411a5c test(session-log-export): remove redundant label assertion
Keep the rendered Header and browser scenarios as the copy-action coverage, and describe the backend converter as used by the Session Markdown copy Host route.
2026-10-21 17:24:56 +08:00
Tianyi Cui 0ab1fcb674 fix(session-log-export): clarify Session Markdown copy label
Rename the Header menu item to Copy Session as Markdown (复制会话为 Markdown) so it states that the whole selected Session is copied. Update Header selectors, browser coverage, controller JSDoc, and the bilingual README; success toast wording stays unchanged.
2026-10-21 17:18:56 +08:00
Tianyi Cui 957379e8a9 test(session-log-export): adapt merged input actions
Add the master-introduced insertQuote input action to the isolated Header action fixture so the merged client aggregate typechecks.
2026-10-21 17:18:51 +08:00
Turtle c1eb1dac80 test(plugin-manager): drive merged header tests with real pointer movement
Master's tooltips react to pointer events. Locator hover scrolls its
target into view, and scrolling this list moves the header controls
from under the pointer, so the narrow-width tooltip checks now point
at each control once the resized header settles. After Tab moves focus
to the icon-only Add plugin button, the touch case expects that
button's focus tooltip to stay and checks that the hovered ones leave.
2026-10-21 17:13:32 +08:00
Chinesezjc c8d537db43 fix(client): complete the Japanese dictionaries after merging master 2026-10-21 17:12:57 +08:00
Yichen Jiang 752cc5c0c0 feat(plan): keep exit_plan_mode declared after the first plan-mode entry
Disclose the exit tool progressively instead of following every plan-mode
transition. The plan projection records whether a logged plan/mode ever
activated plan mode (stateVersion 4), and the tool's condition admits it
for a step whose selected state is active or once the Session has entered
plan mode. Leaving plan mode now changes only the guidance section, so an
addition-only route no longer drops a declaration from its cached prefix.

A call while plan mode is not committed still fails as unavailable outside
plan mode, and a Session that never entered plan mode does not list the
tool. The Web plan-review fixtures return to master's recording because the
request tool lists no longer change on exit.
2026-10-21 17:11:13 +08:00
Tianyi Cui 7a3c24738e Merge master b9bdd062 into session-copy-markdown
Integrate exact origin/master b9bdd062c8ffd3dc234ebea7f5dff55ff26d87ad. Preserve the Session Markdown copy lifecycle and dictionary while adopting the Japanese locale, regenerate the client slot catalog, and add the Japanese quote-removal string required by the merged locale type.
2026-10-21 17:10:56 +08:00
07akioni bec62b10f6 test(web): refresh localized translator component snapshots 2026-10-21 17:09:03 +08:00
Chinesezjc 482ce61890 Merge commit 'b9bdd062c8ffd3dc234ebea7f5dff55ff26d87ad' into feat/schedule-clear-ended
# Conflicts:
#	packages/client/ui-schedule/README.i18n.yaml
#	packages/client/ui-schedule/README.md
#	packages/client/ui-schedule/README.zh.md
2026-10-21 17:06:43 +08:00
winewill 081b562487 fix(desktop): preserve owner close and title behavior 2026-10-21 17:02:47 +08:00
07akioni 11295990b5 fix(i18n): cover nested metadata and repair locale CI regressions 2026-10-21 17:00:42 +08:00
Yichen Jiang c29fdd3bf6 Merge branch 'worktree/composer-image-preupload' into worktree/pr-6426-review-970262 2026-10-21 16:51:49 +08:00
yudshj c47d3285b5 test(snapshots): record the current present tool description
The background-wait-steer pinned tool schemas still carry the pre-reword present description, so the snapshot header no longer matches the shipped tool.
2026-10-21 16:50:06 +08:00
Yichen Jiang dbb2492c01 feat(attachment): relax the default image admission limits
Normalization already downscales long sides, so the default per-side
limit rises from 8192px to 65,535px, the largest side a JPEG carries,
and the decoded-pixel limit alone bounds admission. The default
per-image limit rises from 20 MiB to 50 MiB.
2026-10-21 16:48:03 +08:00
Yichen Jiang b22cf68e50 fix(web): explain refused image uploads and tighten image staging
Address the review of this pull request:
- A failed image upload keeps the Host's attachment refusal reason. The
  thumbnail of a refused image names the reason instead of offering a
  retry that cannot succeed, and Enter while uploads hold the send names
  the first failure; a transport failure still offers retry, and an
  image without a file name uses the pending-image label.
- An image upload stops reading at the smaller of the per-image and
  per-message byte limits.
- A carried image draft moving to a Session that sends images inline
  drops its upload instead of staging it again.
- The image upload ready state no longer carries an unread image ref.
- Docs describe stored-image admission and the command image path, and
  the attachment rail test builds typed props instead of casting through
  unknown.
2026-10-21 16:48:01 +08:00
winewill 291dd1f448 refactor(desktop): share shell theme initialization 2026-10-21 16:47:56 +08:00
_Kerman 5dcb912b1b refactor(cua): delegate retained execution settings to Node provider 2026-10-21 16:47:32 +08:00
_Kerman ac0816e573 refactor(ptc-runtime-node): centralize cross-realm exception formatting 2026-10-21 16:47:08 +08:00
Chinesezjc 5fc95b6213 feat(ci): publish the approval decision label from the repository App
The decision check run kept its job-token check suite out of the pull request's
merge box: GitHub associates check runs with a pull request through the check
suite of the workflow run, and a `pull_request_target` run is not associated.
Mint a token from the repository App scoped to `checks: write` and publish only
the check run with it, so the merge box lists the label; the commit status gate
keeps using the job token and is unaffected when the token or the write fails.

Pin the token step, its single secret reference, and the client split in the
workflow and approval tests, and record the measured association limit in the
Agent Note and review-ownership README.
2026-10-21 16:47:07 +08:00
_Kerman 73a5e63894 fix(ptc-runtime-node): report clearing only for retained executions 2026-10-21 16:46:56 +08:00
_Kerman 8d0277d029 test(ptc-runtime-node): allow platform-sized REPL diagnostics 2026-10-21 16:46:26 +08:00
Turtle da7a582491 Merge remote-tracking branch 'origin/master' into turtle/plugin-sticky-header-29bfdb179e18
# Conflicts:
#	apps/web/tests/plugin-manager.e2e.ts
#	apps/web/tsconfig.json
#	packages/client/ui-plugin-manager/README.i18n.yaml
#	packages/client/ui-plugin-manager/README.md
#	packages/client/ui-plugin-manager/README.zh.md
#	packages/client/ui-plugin-manager/src/client/PluginManagerPage.module.css
#	packages/client/ui-plugin-manager/src/client/PluginManagerPage.tsx
#	packages/client/ui-plugin-manager/tests/components.client.spec.tsx
#	packages/client/ui-primitives/README.i18n.yaml
#	tsconfig.host.json
2026-10-21 16:44:06 +08:00
07akioni 9ce74cda96 feat(desktop): localize Windows installer with per-language JSON resources 2026-10-21 16:43:19 +08:00
yudshj cf620a98df fix(message-quotes): register the Japanese dictionary
The quote plugin's dictionaries predate the Japanese locale merge, so the locale dictionary parity gate rejects its object-form dictionary without Japanese copy.
2026-10-21 16:35:29 +08:00
winewill 3f5dd8b931 fix(desktop): limit update card outline to dark mode 2026-10-21 16:32:01 +08:00
creatixchu 47e16be285 Merge updated platform base into CUA permissions 2026-10-21 16:27:33 +08:00
yudshj d12498798a fix(conversation): add missing Japanese quote removal label
The Japanese dictionary landed without the quote removal copy its English and Chinese dictionaries already carry, so the conversation dictionaries do not typecheck against the current master.
2026-10-21 16:26:54 +08:00
yudshj f9816f62eb Merge origin/master into fix/question-composer-compact-details
Re-record the merged Answering section's bilingual pairing hashes and add the new Expand/Collapse copy to the Japanese dictionary.
2026-10-21 16:26:39 +08:00
creatixchu 684bb206af Merge master into bundle platform support 2026-10-21 16:22:55 +08:00
07akioni 87c4de2e43 chore(i18n): limit added locale checks to JSON metadata 2026-10-21 16:22:53 +08:00
winewill c695c91485 fix(desktop): outline update cards with shared border token 2026-10-21 16:22:43 +08:00
fz 2efa355f65 fix(locale): register Japanese message quote dictionary 2026-10-21 16:22:21 +08:00
ZiyaZhang b6c967c984 Merge master into experimental personalized pages 2026-10-21 01:21:39 -07:00
yudshj fb290b388d fix(ui-user-questions): clip dropped lines from hit testing
A collapsed question or description keeps its full line budget and only
masked its dropped line, so that transparent line still took clicks: it
covered the first line of the question detail and the top edge of the
custom answer row. clip-path now removes the line from hit testing as
well as from view, and Web e2e hit tests pin both targets.

Expansions also record the question they belong to and apply only while
that question is shown, so a position change from another surface of the
same Session no longer carries them to a different question. `.title`
returns to its base margin; `.questionTitle` owns the flex sizing.
2026-10-21 16:20:04 +08:00
yudshj f8a094e465 fix(ui-user-questions): label every copy toggle Expand or Collapse
Option names made the toggle tooltips as long and unpredictable as the
option labels themselves. Every question and description toggle now uses
the same Expand and Collapse copy, and the question toggle, like the
description toggles, is described by the text it controls.
2026-10-21 16:20:04 +08:00
yudshj 5d5bd8eb41 test(web): re-check long question copy across chat and window widths
A long question with long, width-sensitive, and short option descriptions
is driven through the transcript width handle at its narrowest and widest
positions, an opened and collapsed right sidebar, and a smaller and
restored window. At each settled width every collapsed copy follows its
own overflow, the expanded description stays expanded, and the actions
stay reachable; collapsing the sidebar or restoring the window returns
the wide state exactly.
2026-10-21 16:20:03 +08:00
yudshj 012bfe03e2 style(ui-user-questions): expand long copy with chevron icon buttons
The expand and collapse controls reuse the composer's 24px icon button
with the existing downward and upward chevron icons instead of text. Each
carries a localized accessible name, which a portaled Tooltip shows on
hover. The fade at the end of clipped copy narrows to the 24px button.
2026-10-21 16:20:03 +08:00
yudshj 82b43da3dc revert(ui-primitives): drop HoverCard callout and click pinning
The question composer expands long copy in place, so no caller uses the
callout variant, openOnClick pinning, describeAnchor, or the
preview-to-anchor pointer handling this pull request added. HoverCard,
its stylesheet, tests, and README return to their merge-base state.
2026-10-21 16:20:03 +08:00
yudshj 45fd961413 refactor(ui-user-questions): expand long copy in place
A question shows in full up to three rendered lines and an option
description up to two. Longer copy keeps one line fewer, fades at the end
of that line, and offers a "More" text button that expands it in place;
expanded copy offers "Show less" on its own line.

CSS scroll timelines decide which copy overflows, so the composer no
longer measures layout in an effect. Each box keeps its full line budget
plus a 2px glyph allowance, and a negative margin returns the dropped
line, so the overflow reading never depends on the collapsed layout.
Navigation resets scroll and expansions from the navigation handler.
2026-10-21 16:20:03 +08:00
yudshj baa5cb2714 fix(client): retain hover previews when returning to anchors 2026-10-21 16:20:03 +08:00
yudshj 2e420e6fad test(client): verify responsive question disclosures after rebase 2026-10-21 16:20:03 +08:00
yudshj 2b6360c1d9 refactor(ui-user-questions): let CSS wrap option descriptions 2026-10-21 16:20:02 +08:00
yudshj f3bb36263d fix(client): complete hover-card coverage and steering golden 2026-10-21 16:20:02 +08:00
yudshj 50a9e31c2c fix(client): preserve question focus and dismiss hover cards 2026-10-21 16:20:02 +08:00
yudshj fbc2550f52 fix(ui-user-questions): preview long questions in three lines 2026-10-21 16:20:02 +08:00
yudshj 9d44ecefb7 style(ui-user-questions): fade truncated option details 2026-10-21 16:20:02 +08:00
yudshj 935a891a45 fix(ui-user-questions): preserve inline short option descriptions 2026-10-21 16:20:02 +08:00
yudshj 005bb7255a test(web): verify question callout in dark mode 2026-10-21 16:20:01 +08:00
yudshj 5b96747630 fix(ui-user-questions): refine option detail callouts 2026-10-21 16:20:01 +08:00
yudshj 1cbf50d03e test(web): stress long question layouts 2026-10-21 16:20:01 +08:00
yudshj aa099a9b40 fix(ui-user-questions): keep long questions answerable 2026-10-21 16:20:01 +08:00
_Kerman f04a1257f0 Merge latest master into xtr/ptc-vm-script 2026-10-21 16:14:49 +08:00
07akioni ed783066e6 fix(i18n): complete Japanese plugin metadata and guard locale coverage 2026-10-21 16:14:12 +08:00
fz 68da8d28ef test(sdk): refresh background-wait present tool schema 2026-10-21 16:12:50 +08:00
_Kerman f53d476790 chore(tools): drop unused testing export policy 2026-10-21 16:09:26 +08:00
_Kerman ab173e9d09 chore: remove unrelated changes from executor PR 2026-10-21 16:09:26 +08:00
Yif 3c26689899 fix(plugin-manager): make sticky header scrolling smooth
让插件主页面的吸顶过渡随滚动连续到位,而不是滚过 8px 后突然重排。

- 标题栏与列表共用滚动容器,保留展开态占位,避免收缩改变滚动范围
- 用不可交互的临时布局副本测量展开态和吸顶态,在同一组控件上按滚动距离更新位移和宽度
- 保持控件身份与焦点,补偿占空间的滚动条,防止水平偏移和临界位置来回抖动
- 小屏优先将 Tabs 与操作按钮排成一行,放不下时固定 Tabs 在上、按钮在下,吸顶前后不换序
- 仅 macOS 收起侧栏的吸顶态采用窗口安全区外 8px 留白,不再限制宽度;其他平台和状态不变
- 补充端点布局、滚动往返、短列表、滚动条和平台隔离回归

本次到插件主页面的吸顶动画与布局优化为止。实验性插件相关页面的吸顶体验留待后续单独处理。
2026-10-21 16:09:06 +08:00
fz e804a6025c fix(locale): restore required Japanese quote removal label 2026-10-21 16:06:47 +08:00
ZiyaZhang 9795925230 test(experimental): retain page routing cases from context reduction trials 2026-10-21 01:06:10 -07:00
fz 61a5014a74 Merge origin/master into feat/sdk-approval-rpc 2026-10-21 16:05:56 +08:00
Turtle 1e0553ef70 fix(web): register the rename disarm listener only on press
Also pin the second press of a real double-click in tests and correct the README and JSDoc.
2026-10-21 16:04:27 +08:00
fz 800805847d fix(sdk): route approvals by live runtime ownership 2026-10-21 16:03:18 +08:00
_Kerman 4b06d5bf80 refactor(ptc-runtime-node): remove unused bootstrap paths 2026-10-21 16:00:30 +08:00
_Kerman 2b7b2a0ee6 refactor(ptc): share the program format type 2026-10-21 15:59:56 +08:00
_Kerman 99c73e310a test(ptc): reuse shared Script snapshot configuration 2026-10-21 15:59:31 +08:00
_Kerman 302fc0119f fix(conversation): add missing Japanese quote removal label 2026-10-21 15:56:44 +08:00
_Kerman a53ecb2f3a Merge origin/master into xtr/tool-executor-plugins 2026-10-21 15:56:44 +08:00
Chinesezjc cf6684d8a4 Merge pull request #4484 from deepseek-harness/fix/multiline-display-math
fix(web): render multi-line display-dollar math
2026-10-21 15:53:22 +08:00
winewill a50412d71b fix(desktop): apply shared theme tokens to update dialogs 2026-10-21 15:51:32 +08:00
imccyu d4aa52c2be Merge pull request #6796 from deepseek-harness/07akioni/japanese-locale
feat(locale): 增加日语界面支持
2026-10-21 15:44:22 +08:00
Yichen Jiang 052186f89f Merge branch 'worktree/composer-image-preupload' into worktree/pr-6426-review-970262 2026-10-21 15:44:10 +08:00
Xu Hanxiang c818773a61 Merge pull request #6052 from deepseek-harness/fix/issue-5965-steer-background-wait
feat(jobs): release job_output waits on steered input
2026-10-21 15:44:05 +08:00
Yichen Jiang a2d35f381a fix(web): bound image receipts by the message limits and cancellation
Prompt admission counted stored images only toward the image count, so
two uploads that each fit alone could exceed maxMessageImageBytes once a
prompt cited both receipts. A staged image now records the byte length
of its upload, and admission counts stored and encoded images together
against both per-message limits before storing anything.

An image upload cancelled after its last chunk, or while its image was
stored, still received a receipt. The upload now checks cancellation
after the read and again before staging, as the file stream path does;
an image whose storage began stays stored, but no receipt is issued.
2026-10-21 15:43:55 +08:00
Chinesezjc 3d00d6faad test(ui-primitives): regenerate math-multiline-display DOM fixture for KaTeX 0.18 2026-10-21 15:40:19 +08:00
Yudong Han ef5f92c0c5 Merge pull request #6786 from deepseek-harness/fix/present-turn-end-delivery
fix(deliverables): present 说明交付在本轮结束后展示,并保留收尾回复之后的交付
2026-10-21 15:38:59 +08:00
Turtle ee95ae7ced Merge pull request #6724 from deepseek-harness/turtle/dark-hover-missing-33b8a5d72c4b
fix(client): show settings nav hover in dark theme
2026-10-21 15:36:06 +08:00
Turtle 2a871c2201 fix(web): ignore window-activating click in session title double-click rename
Refs #6835
2026-10-21 15:32:38 +08:00
Turtle ce403d850c Merge pull request #6815 from deepseek-harness/turtle/plugin-market-entry-8303d8c92886
feat(web): list custom plugin markets on the main experimental list
2026-10-21 15:32:32 +08:00
Yichen Jiang f507430f3f test(web): describe quote drafts under Host-confirmed sending
The quote composer specs and README from master described optimistic
sending: the draft cleared on Enter and a failed send restored it. Here
the draft and its quotes stay in the read-only editor until the Host
accepts the message, a failure leaves them in place, and the sink also
receives the acceptance callback.
2026-10-21 15:22:05 +08:00
Yichen Jiang 85b29bbcea Merge remote-tracking branch 'origin/master' into worktree/dynamic-tool-activation-pr-64796e 2026-10-21 15:21:24 +08:00
Turtle be43f4d846 Merge remote-tracking branch 'origin/master' into turtle/dark-hover-missing-33b8a5d72c4b
# Conflicts:
#	packages/client/ui-settings-general/src/client/SettingsRoot.module.css
2026-10-21 15:19:21 +08:00
_Kerman d692725db1 test(sdk): expect live model selection during resume 2026-10-21 15:18:50 +08:00
_Kerman bec0ccf10f test(sdk): expect live model selection during resume 2026-10-21 15:18:50 +08:00
Yichen Jiang 56f710bf5d Merge the earlier conditional-tools history into its rebuild on master
PR #6298 now fixes each step's model selection before prompt assembly and
agent/pre-step and passes it as modelSelection. This branch is rebuilt on
that master and keeps only conditional tool admission. The earlier agent/model
event, agent.step, step-boundary handling, prompt-assembly and
runtime-context reordering, and their snapshots are superseded, so this
merge records the earlier commits as ancestors and keeps the rebuilt tree.
2026-10-21 15:16:10 +08:00
Yichen Jiang faf62f4c1a Merge branch 'worktree/composer-image-preupload' into worktree/pr-6426-review-970262
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
#	packages/client/ui-conversation/src/client/input/facade.ts
#	packages/client/ui-conversation/src/client/input/hub.ts
2026-10-21 15:15:46 +08:00
Magolor a6a5c41b3b refactor(token-meter): 分离计量恢复信息与价格缓存 (#6728)
* refactor(token-meter): checkpoint measurement facts without pricing caches

* refactor(token-meter): reuse cached headers without new history reads

* fix(token-meter): isolate measurement recovery failures
2026-10-21 15:15:42 +08:00
Yichen Jiang 7f32f6c3cc test(snapshots): declare exit_plan_mode only in plan mode
Keyless session, SDK, ACP, and Web snapshots, the subagent-inheritance
expected output, and the Python dynamic-tools snapshot no longer list
exit_plan_mode outside plan mode, and the collapse-timing goldens count one
fewer added tool. In plan-review, the approved exit now removes the tool on
the next step: its header records a change that starts a series and a
tool-removal developer message, which the expanded Web timeline shows.
cordis-inspect-jsdoc records the new tools service signatures. The pwsh
tool-turn pins are edited by hand because pwsh is unavailable for a local
refresh; their only change is the removed schema.
2026-10-21 15:15:29 +08:00
Turtle 6db97a9b61 Merge pull request #6824 from deepseek-harness/turtle/make-clean-node-modules-3061242ba73e
fix(scripts): remove dangling pnpm links in clean
2026-10-21 15:12:49 +08:00
_Kerman 6085b6fb62 Merge latest master into xtr/ptc-vm-script 2026-10-21 15:12:47 +08:00
Turtle 589790d6e9 Merge remote-tracking branch 'origin/master' into turtle/plugin-market-entry-8303d8c92886
# Conflicts:
#	apps/web/tests/inspector-late-enable.e2e.ts
#	packages/experimental/inspector-profile/README.i18n.yaml
#	packages/experimental/inspector-profile/README.md
2026-10-21 15:12:43 +08:00
Yichen Jiang f3973d98d2 docs(web-search): fit the merged web-search-deepseek summary to its budget
The merge combined master's pointer to the DeepSeek Search and Fetch APIs
with the readiness sentence, which put the Summary at 118 words against the
100-word limit. Keep both facts and drop the restatements.
2026-10-21 15:12:24 +08:00
Yichen Jiang a623bdee60 test(web-search): pin the replay key only for replayed model calls
The web e2e scaffold pinned the snapshot replay DEEPSEEK_API_KEY for every
keyless run, so a UI-only run rendered a configured key. Master's web search
settings page golden records the page without one. Pin the placeholder only
when the scaffold replays recorded model calls, whose recordings ran with a
real key; other runs keep the launching environment, as their goldens do.

Refresh web-search-unconfigured after the merge: master now records the bash
call's job/registered and job/settled events.
2026-10-21 15:12:10 +08:00
mektpoy 184605873d docs(tool-jobs): mark the steer-release heuristic
The release trigger treats a user-sourced inbox insertion as a user steer and
only this wait responds to it. Both are known approximations for the
short-term fix: a shared steer signal should let every waiter decide locally
and let the input's producer own the decision. Mark both spots for the
follow-up and keep the public jobs contract unchanged.
2026-10-21 15:06:42 +08:00
mektpoy ea25277107 test(snapshots): declare the llm package the steering fixture imports
The recorded-session fixture is a scenario-owned plugin that imports
`@deepseek-ai/dsh-llm` at runtime. Declare the workspace package at the root
so the snapshot lane resolves it from a clean install instead of relying on a
local node_modules layout.
2026-10-21 15:06:42 +08:00
mektpoy 3ec8d15cf5 test(sdk): record the steered background wait
`snapshots/sdk/background-wait-steer` replays a keyless SDK session whose
`job_output(wait: true)` is released by a user steer: the fixture steers once
the waiter registers and the producer has written its barrier line, and the
scenario asserts the early return with `user steered`, the surviving producer,
and exactly one completion notice for the settlement that the released read
did not consume.

The authored session takes its job id from the live request with
`{{fromRequest:(bash-[0-9a-f]{16})}}`, so an opaque id needs no fixture edit.
2026-10-21 15:06:42 +08:00
Yichen Jiang 9d0c97b9d2 feat(tools): admit conditional tools per step
ctx.tools.register(definition, { when }) adds a synchronous or asynchronous
condition. AgentLoop resolves conditions once per proposed step with the
agent, the model selection that step preparation captured, and the turn
signal, before prompt assembly and agent/pre-step. The resulting
ResolvedToolSet governs tool declarations, Native dispatch, PTC bindings,
and nested calls for that step and its retries, and the turn disposes it
when the proposal is rejected or fails, when no step opens, and after the
step. A false condition removes the tool everywhere; a thrown condition
ends the turn with TOOL_ACTIVATION_FAILED. Calls whose registration was
replaced or removed during the step return UNKNOWN_TOOL.

system-prompt/assemble now transforms PromptPresentation without a tool
array, so tool declarations stay provider-owned. exit_plan_mode is declared
only for steps whose selected plan state is active, the same state that
renders plan guidance; it still rejects a call before the selection is
logged.

Model selection stays as master defines it; this change adds no model
selection event or Agent field.
2026-10-21 15:02:29 +08:00
Yichen Jiang 1b608982cc Merge remote-tracking branch 'origin/master' into worktree/composer-image-preupload
# Conflicts:
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	docs/persistence-catalog.i18n.yaml
#	docs/persistence-catalog.md
#	docs/persistence-catalog.zh.md
#	docs/persistence-schema.json
#	packages/client/ui-conversation/README.i18n.yaml
2026-10-21 15:02:15 +08:00
_Kerman d6dc8890b9 Merge github/master into xtr/session-store-write-lifecycle 2026-10-21 15:01:22 +08:00
_Kerman 8f0489dc06 Merge github/master into xtr/session-store-write-lifecycle 2026-10-21 15:01:22 +08:00
mektpoy e2a5eab020 test(tool-jobs): cover the released job_output wait
Registry-level cases pin the release claim, the settlement the read carries,
the withheld notice a cancelled read returns to delivery, parallel waits on one
job, and the model-killed settlement that must not repeat. A real AgentLoop
case covers the user steer releasing the wait while the producer survives.
2026-10-21 14:58:12 +08:00
mektpoy 01ea8ed877 docs(events): list tool-jobs as a consumer of agent/inbox/inserted
Generated by `pnpm exec tsx scripts/gen-doc-graphs.ts` after the wait-release
listener moved into the tool-jobs plugin.
2026-10-21 14:58:11 +08:00
mektpoy 7854778639 feat(tool-jobs): end job_output waits on a user steer
A `job_output(wait: true)` wait now ends when the user steers the current
step, returning the job's real state and the output already read; the
producer keeps running and the pending input stays queued. The wait is ended
by a controller the tool owns, so the registry's abort semantics — and every
other consumer of `wait` — stay unchanged. The model sees why a running job
came back early in the unchanged status line (`user steered`).

A settlement that lands between the release and this call's own read is
carried by that read instead of a second notice; if the read never commits,
the notice is delivered when the claim releases. Settlements already delivered
elsewhere (the model's own `job_kill` result or another waiter's collection)
are not repeated.
2026-10-21 14:57:28 +08:00
Yichen Jiang 2548bb4acf test(web-search): adapt fixtures to master's model selection and projections
Master moved the step's model route into the assembly context: the agent loop
fixes it per step and registers the prompt's provider variable from it, and
installModelSelection no longer takes a mutable selection. The tool-web
readiness fixture now registers that variable and passes the step's
modelSelection to each assembly.

AgentRegistry now needs the session projection registry, so the
web-search-deepseek initiator fixtures and the Loader composition in
chat-endpoint.spec.ts mount it, as master's own fixtures in this package do.
2026-10-21 14:53:05 +08:00
yudshj 17c8fc7076 fix(deliverables): 收窄 present 描述,并让行内引用包含收尾回复之后的写入
present 描述改为宿主可能只在本轮结束后展示交付文件,并只在等待用户回答的工具调用前建议改用链接。
本轮产生的文件与交付声明一样按整轮取,不再按收尾回复的 seq 截断;README 补充交付卡片的展示时机。

Refs #6769
2026-10-21 14:52:55 +08:00
Yichen Jiang 7a75f225c5 Merge remote-tracking branch 'origin/master' into worktree/web-search-dynamic-tool-e26c2b
# Conflicts:
#	apps/cli/tests/profiles/headless/tests/fixtures/cli.patch.yml
#	apps/cli/tests/profiles/sdk/keyless-smoke.e2e.ts
#	apps/web/tests/expected/plugin-config/official.expected.md
#	apps/web/tests/expected/plugin-manager/live-enabled.expected.md
#	apps/web/tests/expected/plugin-manager/manager.expected.md
#	apps/web/tests/expected/plugin-manager/refresh.expected.md
#	apps/web/tests/plugin-config.e2e.ts
#	apps/web/tests/plugin-manager.e2e.ts
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	docs/subsystems/web.i18n.yaml
#	docs/subsystems/web.md
#	docs/subsystems/web.zh.md
#	packages/client/ui-settings-web-search/README.i18n.yaml
#	packages/client/ui-settings-web-search/README.md
#	packages/client/ui-settings-web-search/README.zh.md
#	packages/web/tool-web/README.i18n.yaml
#	packages/web/tool-web/README.md
#	packages/web/tool-web/README.zh.md
#	packages/web/web-search-deepseek/README.i18n.yaml
#	packages/web/web-search-deepseek/README.md
#	packages/web/web-search-deepseek/README.zh.md
#	packages/web/web-search-deepseek/src/provider.ts
#	packages/web/web-search-deepseek/tests/deepseek.spec.ts
#	packages/web/web/src/index.ts
#	snapshots/session/web-search-endpoint-guidance/session.v4.jsonl
2026-10-21 14:47:47 +08:00
Saafo e98d49d094 Merge pull request #6669 from deepseek-harness/feat/narrow-settings-pages
fix(client): reflow settings and task fields for narrow screens
2026-10-21 14:44:48 +08:00
lsdsjyandlsdsjy 0e49f443dd feat(ui-conversation,ui-chat): 基于 user-text 支持富文本引用与草稿恢复 (#3206)
* feat(ui-chat,ui-conversation): 通过通用文本交互支持原文引用

* refactor(message-quotes): 插件化引用交互与文本扫描豁免

* test(message-quotes): 补齐插件装配预期与交互分发覆盖

* refactor(ui-conversation): 移除引用删除文案的语言切换刷新

* refactor(message-quotes): 移除引用扫描豁免并收窄文本交互接口

* fix(ui-chat): 修复引用浮层的滚动遮挡与恢复

* fix(ui-chat): 对齐 master 的聊天流注入与界面快照

* test(message-quotes): 补齐 Host 入口覆盖并固定子会话回放时序

* refactor(message-quotes): 清理文本依赖特例并修正引用按钮

* fix(ui-primitives): 让引用原文弹层按内容收缩

* fix(ui-primitives): 修复相邻引用悬停弹层重叠与误切换

* fix(message-quotes): 为引用悬停样式限定设备能力

---------

Co-authored-by: lsdsjy <[email protected]>
2026-10-21 14:41:41 +08:00
Turtle 94092349ab fix(scripts): clean links into removed packages and skip external node_modules
Links whose target this clean removes, or whose target loops or passes through a file, are residue. A node_modules directory resolving outside the repository is not scanned.
2026-10-21 14:40:16 +08:00
Saafo 5a028f151c 🔀 merge master into narrow settings pages 2026-10-21 14:29:12 +08:00
_Kerman b805300652 Merge pull request #6298 from deepseek-harness/xtr/session-model-selection
feat(agent): determine the request model before pre-step
2026-10-21 14:25:05 +08:00
Turtle 81ee28eebb fix(scripts): remove dangling pnpm links in clean
pnpm install keeps hoisted links to deleted workspace packages, and the Desktop development build fails reading their package.json.

Fixes #6823
2026-10-21 14:22:12 +08:00
CreatixChu 06ec84b259 Merge pull request #6806 from deepseek-harness/fix/cua-relative-999
fix(cua): 将相对坐标统一为 0–999
2026-10-21 14:22:06 +08:00
Turtle b1a0c733ab test(web): assert enabled Developer Tools returns to the main list 2026-10-21 14:11:15 +08:00
_Kerman 6bfe05c6f5 refactor(agent): name captured step selection modelSelection 2026-10-21 14:08:11 +08:00
Yichen Jiang 6edb11e306 test(web): keep Stop in the queue-actions sending golden
A running Session still shows Stop beside the read-only sending control
while the Host decides, so the golden keeps that button.
2026-10-21 14:07:04 +08:00
Yichen Jiang 40d5a3d0fb test(web): hold the prompt request in the browser for the admission e2e
master no longer has the agent-controller admission stage or the
session/prompt-rejected error, so the composer stops special-casing that
code. prompt-admission.e2e.ts now holds the prompt request in the browser
instead of through an admission listener and keeps its read-only sending,
no-echo, and acceptance assertions; queue-actions already covers a refused
prompt keeping its draft.
2026-10-21 14:05:24 +08:00
Yichen Jiang 114ab6395b Merge branch 'worktree/composer-image-preupload' into worktree/composer-host-confirmed-echo
Brings in master a8d675ecc92, which reverts the agent controller of #6418,
together with the composer's image upload on add.

The agent-controller message delivery Agent Note follows master's deletion.
The ui-conversation README keeps the read-only sending paragraph and takes
the image upload sentences. Service orchestration keeps both new sendSession
cases, and the refused-send reference case takes master's newSession
submission fact. README pairings are recorded again.
2026-10-21 14:05:09 +08:00
_Kerman 4b36aebb97 Merge remote-tracking branch 'origin/master' into xtr/session-model-selection 2026-10-21 13:57:15 +08:00
_Kerman 62a1075fe7 Merge remote-tracking branch 'origin/master' into xtr/session-model-selection
# Conflicts:
#	docs/persistence-schema.json
2026-10-21 13:56:41 +08:00
Yichen Jiang 2c2574ade6 feat(web): keep the composer read-only while a message is sent
The composer returns to a read-only sending phase. Enter captures the draft,
its reference chips, and its attachments; the editor and the attachment rail
stay read-only while Send shows progress; and the Host's acceptance clears
the draft and its attachments in one transaction before the echo renders. A
refusal or failure returns the draft unchanged. A running Session still
shows Stop beside the sending control, and Workspace selection still leaves
a draft whose message is in flight in its Session.

This reverts the editable-while-sending behavior of a7af0509ab5 and the
in-flight attachment accounting of e36c3f9a395, which only that behavior
needed.
2026-10-21 13:55:25 +08:00
_Kerman 051ac9154b Merge pull request #6688 from deepseek-harness/xtr/ptc-birpc-transport
refactor(ptc-runtime-node): carry the process control channel over birpc
2026-10-21 13:53:43 +08:00
Turtle 2839c53971 Merge pull request #6791 from deepseek-harness/turtle/plugin-sentence-case-299acab3ee89
fix(plugins): use sentence case for built-in plugin English copy
2026-10-21 13:53:27 +08:00
Yichen Jiang 4804ce4de4 Revert "fix(web): keep a sent message's attachments out of the next message's limits"
This reverts commit e36c3f9a395e4409b2bb2d29e75d2a9ccee1280d.
2026-10-21 13:52:00 +08:00
Yichen Jiang 9be99a0e28 feat(web): upload composer images when they are added
An ordinary Session's composer now uploads each image draft through the
background upload queue as soon as it is added. The raw upload route stages
the body as a prompt image when the request names its media type: the Host
reads at most the per-image byte limit, then validates, normalizes, and
stores the image as prompt admission would. The prompt cites the receipt in
a file part; prompt admission resolves it to the stored image, applies the
image-input check, and counts stored images toward maxImagesPerMessage.

Send waits for every image receipt, and a failed image upload turns its
thumbnail into a retry control. Commands and subagent continuations keep
encoding images inline.
2026-10-21 13:50:19 +08:00
07akioni 93b5d59229 fix(locale): translate current approval choices and preset guidance 2026-10-21 13:49:38 +08:00
07akioni 358c142716 Merge master for current locale keys 2026-10-21 13:47:53 +08:00
_Kerman a8013ddc35 Merge remote-tracking branch 'github/master' into xtr/session-store-write-lifecycle
# Conflicts:
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	docs/persistence-schema.json
2026-10-21 13:45:24 +08:00
_Kerman 28af21a294 Merge remote-tracking branch 'github/master' into xtr/session-store-write-lifecycle
# Conflicts:
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	docs/persistence-schema.json
2026-10-21 13:45:24 +08:00
07akioni 6238020202 fix(locale): type additional dictionaries and complete Japanese registration 2026-10-21 13:42:49 +08:00
Chinesezjc 500426d4bd test(schedule): cover a large ended set and repeated bulk removals 2026-10-21 13:38:20 +08:00
Turtle 11589ed104 feat(web): list custom plugin markets on the main experimental list
Developer Tools moves to the More page.
2026-10-21 13:34:13 +08:00
Turtle c7be617437 Merge remote-tracking branch 'origin/master' into turtle/plugin-sentence-case-299acab3ee89
# Conflicts:
#	packages/experimental/cua/src/application-access.ts
2026-10-21 13:28:18 +08:00
creatixchu f3accc8474 test(cua): pin thousandth coordinate guidance for all gestures 2026-10-21 13:27:33 +08:00
creatixchu e0b95e3e6e fix(cua): preserve thousandth scaling for relative coordinates 2026-10-21 13:25:51 +08:00
CreatixChu c266317763 Merge pull request #6650 from deepseek-harness/feat/cua-approval-format
feat(approval): 为 CUA 应用授权提供固定审批格式
2026-10-21 13:18:48 +08:00
Chinesezjc 783d048294 Merge remote-tracking branch 'origin/master' into fix/multiline-display-math
# Conflicts:
#	packages/client/ui-primitives/README.i18n.yaml
#	packages/client/ui-primitives/src/markdown/parse.ts
2026-10-21 13:14:53 +08:00
Turtle 4cb08ffb7a fix(web): match subagent switcher placeholder height to the sidebar button
Refs #6803
2026-10-21 13:11:16 +08:00
Turtle edde4a0667 Merge pull request #6795 from deepseek-harness/turtle/standard-plugin-manager-a5c4c80ab718
feat(preset): enable plugin_manager in standard mode
2026-10-21 13:09:08 +08:00
creatixchu 56f783f50a Merge remote-tracking branch 'origin/master' into feat/cua-approval-format
# Conflicts:
#	docs/persistence-schema.json
2026-10-21 12:48:44 +08:00
creatixchu fe77f964ca docs(cua): align relative-coordinate reference and limits 2026-10-21 12:48:06 +08:00
creatixchu a46f095f01 test(cua): refresh relative-coordinate prompt snapshots 2026-10-21 12:47:40 +08:00
creatixchu 7e8bf2808d fix(cua): normalize relative coordinates to 0–999 2026-10-21 12:46:20 +08:00
Turtle ca8cc62319 fix(web): align subagent switcher rows without a sidebar button
Refs #6803
2026-10-21 12:40:13 +08:00
imccyu 224516ec2f Merge pull request #6799 from deepseek-harness/worktree/sync-dsh-0.2.1-analytics-to-master
release(dsh): sync 0.2.1 entry analytics back to master
2026-10-21 12:38:54 +08:00
Turtle f7a69f8421 docs(preset): describe plugin management in Standard mode 2026-10-21 12:36:18 +08:00
Turtle 5a66e46c4f Merge pull request #6598 from deepseek-harness/persistence-schema-digest-keys
refactor(scripts): key persistence schema nodes by subgraph digest
2026-10-21 12:28:01 +08:00
Chinesezjc 3c5ab55d16 perf(storage): delete task rows as one durable batch 2026-10-21 12:21:11 +08:00
_Kerman 81a132d1c0 test(tools): align missing-runtime expectations with plugin activation 2026-10-21 12:13:58 +08:00
_Kerman 2be80e3619 fix(sdk): select tool executors through profile overlays 2026-10-21 12:13:58 +08:00
_Kerman e727a3b161 fix(tools): keep executor changes out of registry notifications 2026-10-21 12:13:57 +08:00
_Kerman 6c1ae74d16 refactor(ptc): separate client declarations from host policy types 2026-10-21 12:13:50 +08:00
ZiyaZhang 1551890afd test(experimental): use typed snapshot matcher without casts 2026-10-20 21:13:09 -07:00
TurtleandMagolor 472f0c8258 docs: formalize sentence case for English UI copy
Record the sentence-case rule for English UI copy and plugin titles, sync remaining README label references, and keep the session-inspector Chinese dictionary unchanged.

Refs #6790

Co-authored-by: Magolor <[email protected]>
2026-10-21 12:06:49 +08:00
ZiyaZhang 66078e79a8 Merge latest master into personalized pages experiment
# Conflicts:
#	packages/client/ui-plugin-manager/tests/components.client.spec.tsx
2026-10-20 21:06:46 -07:00
turtle1999 5b23990a0e Merge remote-tracking branch 'origin/master' into persistence-schema-digest-keys
# Conflicts:
#	docs/persistence-schema.json
2026-10-21 12:06:20 +08:00
imccyu 480562cdb9 Merge remote-tracking branch 'origin/master' into worktree/sync-dsh-0.2.1-analytics-to-master
# Conflicts:
#	packages/client/ui-agent-preset/README.i18n.yaml
#	packages/client/ui-sidebar/src/client/SidebarRoot.tsx
2026-10-21 11:56:51 +08:00
Chinesezjc d60699860d docs(subagent): record why the delegation guidance ends the turn
The README now states the delivery mechanism that makes ending the turn
safe, with links to the subagent notice behavior and the host wait loop.
An Agent Note records the #6057 observation and the controlled probe
(sleep 0/300 with the sentence, 29/300 without it) so a later prompt
trim does not remove the sentence again.
2026-10-21 11:54:22 +08:00
ZiyaZhang bdb211090f fix(experimental): clarify personalized page planning and handoff 2026-10-20 20:53:07 -07:00
ZiyaandZiyaZhang e819a7126b feat(web): hide persistent terminal bundle controls (#6753)
Co-authored-by: ZiyaZhang <[email protected]>
2026-10-21 03:52:24 +00:00
Chinesezjc b526b87520 Merge pull request #6729 from deepseek-harness/fix/weighted-approval-status-and-check
fix(ci): gate weighted approval through its commit status and label the conclusion
2026-10-21 11:51:22 +08:00
Turtle f9ec718a48 feat(preset): enable plugin_manager in standard mode 2026-10-21 11:47:26 +08:00
07akioni 3007a15cdd fix(locale): align Japanese copy with current plugin settings 2026-10-21 11:44:22 +08:00
07akioni 342965a75b Merge remote-tracking branch 'origin/master' into 07akioni/japanese-locale 2026-10-21 11:42:35 +08:00
07akioni 3f9f36e92d feat(locale): add Japanese client and desktop translations 2026-10-21 11:42:12 +08:00
zhc 9cf8c35179 refactor(file-drag): 统一 util 包命名与路径引用 2026-10-21 11:36:08 +08:00
Turtle b9d049d041 fix(plugins): use sentence case for built-in plugin English copy
Refs #6790
2026-10-21 11:28:26 +08:00
_Kerman c2c7ce2d2b docs: include acorn-walk in runtime notices 2026-10-21 11:25:08 +08:00
_Kerman bd9ce0e10b chore(tests): prune retired PTC cast baselines 2026-10-21 11:25:08 +08:00
_Kerman b5131b4ae1 test(ptc): cover defaults and align CUA fixtures with launch modes 2026-10-21 11:25:07 +08:00
_Kerman 38b9a095f3 Merge remote-tracking branch 'origin/master' into xtr/tool-executor-plugins
# Conflicts:
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	packages/web/tool-web/tests/integration.spec.ts
#	pnpm-lock.yaml
#	scripts/check-workspace-constraints.spec.ts
#	scripts/check-workspace-constraints.ts
2026-10-21 11:22:06 +08:00
675164957a feat(analytics): track navigation entries and preset selections (#6600)
* feat(analytics): track navigation entry clicks, preset switches, and new-session sends

Rebuilds the click events of #6600 on the region-owned More menus in
release/dsh-0.2.1. Exposure events, creator_mode_enter, and the
ui-workspace onOpened observer are not included.

- sidebar_menu_click gains position plus the new_session and more entries:
  New Session (sidebar button, brand, titlebar), Plugins and Automation
  rows, the left sidebar More button, and entries opened from its menu.
- more_menu_item_click records Browse other plugins and Create your plugin
  per region.
- agent_preset_switch reports applied or unbound staged picks; the seat
  controller now returns an explicit apply outcome.
- code_work_view_toggle reports a successful change of the setting.
- send_button_click adds is_new_session and the Session's preset category.

Co-authored-by: imccyu <[email protected]>

* test(analytics): cover navigation, preset switch, toggle, and send analytics

Co-authored-by: imccyu <[email protected]>

* docs(analytics): document click events and regenerate catalogs

Co-authored-by: imccyu <[email protected]>

* test(ui): reuse the developer tools row accessor without a new unknown cast

Co-authored-by: imccyu <[email protected]>

* fix(analytics): classify preset switches by preset id only

agent_preset_switch now maps the preset id the same way send_button_click does: cordis is creator, ptc, minimal and standard keep their ids, and any other id is other; a published preset name no longer changes the category.

Co-authored-by: imccyu <[email protected]>

---------

Co-authored-by: dingyifan <[email protected]>
Co-authored-by: imccyu <[email protected]>
2026-10-21 11:17:55 +08:00
_Kerman a027637c51 docs(plugins): refresh PTC runtime catalog 2026-10-21 11:12:33 +08:00
_Kerman b7904f24c2 test(ptc): cover Script profiles and CUA retention 2026-10-21 11:12:33 +08:00
_Kerman be1d8c3f02 fix(webworker-runtime): register unsupported REPL builtin 2026-10-21 11:12:33 +08:00
_Kerman 86fcf49a74 fix(ptc-runtime-node): validate Script evaluation and process lifecycle 2026-10-21 11:12:33 +08:00
gengruilin 967ba4c4b8 fix(tools): disable history expansion in the persistent bash shell
The persistent bash tool types each wrapped command into an interactive
bash, which applies history expansion before parsing. A `!` after a single
quote made bash reject the whole line with `event not found`, so neither
marker printed and the call waited for the full timeout; `!!` silently
substituted the previous history entry.

Disable history expansion when the shell is initialized, as OpenHands does
for its interactive terminal. Raw control characters other than newline and
carriage return still reach readline as typed keys; the README records that
limitation.
2026-10-21 11:00:34 +08:00
yudshj 7e23cec629 test(web): 交付卡片快照只截取卡片网格,排除与平台相关的桌面提示 2026-10-21 10:52:58 +08:00
creatixchu 433fd441eb Merge updated platform base into CUA permissions 2026-10-21 10:45:37 +08:00
_Kerman cdb3a76b6a test(agent): complete model ownership migration coverage 2026-10-21 10:43:20 +08:00
creatixchu be0c971766 Merge updated approval base into bundle platform support 2026-10-21 10:42:25 +08:00
creatixchu d605c90481 test(cua): wait for feature preview before measuring layout 2026-10-21 10:40:33 +08:00
creatixchu 0fa948bf16 Merge latest master into CUA approval format 2026-10-21 10:36:56 +08:00
CreatixChu 57fed3d7de Merge pull request #6680 from deepseek-harness/feat/cua-feature-preview
feat(cua): 更新 Computer Use 图标和详情页预览
2026-10-21 10:34:06 +08:00
yudshj 76d015b484 fix(deliverables): present 说明交付在本轮结束后展示,并保留收尾回复之后的交付
present 的工具描述说明宿主在本轮结束后展示交付文件,需要用户提前审阅时改为在消息中链接。
交付卡片不再按收尾回复的 seq 过滤,同一步给出文字并调用 present、之后没有文字的轮次也会显示卡片。

Refs #6769
2026-10-21 10:33:56 +08:00
_Kerman 96b32df5db Merge remote-tracking branch 'origin/master' into xtr/ptc-birpc-transport 2026-10-21 10:30:33 +08:00
_Kerman 94dae61fb5 fix(ptc-runtime-node): keep per-call binding waits pending when the host closes
Closing the control pipe after the result rejected outstanding birpc
calls; an unawaited binding promise then crashed the child and its trace
reached the result logs. Once the result is returned or the pipe closes,
binding calls now never settle, including those the closure rejects.
2026-10-21 10:30:31 +08:00
creatixchu 46910a859e Merge remote-tracking branch 'origin/master' into feat/cua-feature-preview 2026-10-21 10:21:45 +08:00
ZiyaandZiyaZhang 5e9bb51724 feat(plugins): Ralph 循环和会话搜索默认从 GUI 隐藏 (#6754)
* feat(plugins): hide Ralph and session search from default discovery

* fix: keep hidden bundled plugins explicitly activatable

---------

Co-authored-by: ZiyaZhang <[email protected]>
2026-10-20 10:26:31 -07:00
ZiyaZhang 6271657146 fix(experimental): refine personalized page routing and handoff 2026-10-20 10:01:42 -07:00
ZiyaZhang 421f589e48 fix(experimental): keep page routing policy in skill 2026-10-20 09:40:25 -07:00
_Kerman 16f0e2fc69 docs(ptc-runtime): explain Script retention and consumer migration 2026-10-21 00:37:56 +08:00
_Kerman cb6dcaedb0 fix(cua): use effective PTC state retention 2026-10-21 00:37:56 +08:00
_Kerman e82dddc45c fix(ptc-runtime-node): correct Script await handling and guidance 2026-10-21 00:37:56 +08:00
_Kerman c3473d1ea6 refactor(ptc-runtime-node): unify temporary and retained processes 2026-10-21 00:03:21 +08:00
_Kerman 399a74e808 refactor(ptc-runtime-node): simplify evaluator binding setup 2026-10-21 00:02:56 +08:00
_Kerman 36820a5462 refactor(ptc-runtime-node): share the fake child and fold result validation into the protocol
Move the identical in-memory child used by the per-call and persistent
host specs into tests/fake-child.ts, and move readProgramResult next to
the ProgramResult type it validates in protocol.ts.
2026-10-20 23:55:36 +08:00
winewill 9c622fad96 fix(windows): run packaging tasks at normal scheduling priority 2026-10-20 23:54:17 +08:00
_Kerman 7bab95f8da refactor(ptc-runtime-node): rely on birpc defaults for message handling
Keep the stream side owned here: line bounds, the leading-brace check,
the write-queue limit and closure. On the birpc side, drop the strict
envelope validation, falsy-rejection normalization and fatal UTF-8
decoding. Calls to methods an endpoint does not own are answered with
birpc's error and unknown messages are ignored; the resolver still runs
only own methods and none after the endpoint closes.
2026-10-20 23:28:27 +08:00
_Kerman 29f705cc86 Merge origin/master into xtr/ptc-vm-script 2026-10-20 23:18:49 +08:00
_Kerman b7ebde1cb1 fix(cua): recognize retained VM Script contexts 2026-10-20 23:16:35 +08:00
_Kerman 7745b3b9da feat(ptc-runtime-node): add VM Script execution with optional retained state 2026-10-20 23:16:30 +08:00
_Kerman 1f45e38279 feat(ptc-runtime): expose Script syntax and state retention 2026-10-20 23:16:24 +08:00
ZiyaZhang 466cf0b6cb fix(personalized-pages): route simultaneous viewing to sidebar 2026-10-20 08:14:29 -07:00
ZiyaZhang cb1ecfa706 fix(personalized-pages): refine planning and expire unused cards 2026-10-20 07:57:41 -07:00
_Kerman 55e45b52c9 test(session): cover preparation disposal directly 2026-10-20 22:39:43 +08:00
_Kerman 7289bee231 test(agent-loop): assert rollback closes before rejection 2026-10-20 22:39:38 +08:00
_Kerman 06e97edd60 docs(agent): clarify initialization cleanup timing 2026-10-20 22:39:28 +08:00
winewill 10c3dd5a3e chore(windows): record dependency install resources 2026-10-20 22:32:28 +08:00
_Kerman bf4ab8be26 refactor(agent-loop): unify agent initialization 2026-10-20 22:21:52 +08:00
_Kerman 364226ebd8 test(web): align model selection expectations with Auto 2026-10-20 22:13:58 +08:00
_Kerman 6f2aeff14d refactor(agent): simplify model selection access 2026-10-20 22:13:42 +08:00
_Kerman 200dedd494 chore: trim unrelated model-selection changes 2026-10-20 22:13:30 +08:00
ZiyaZhang a313fb106b fix(personalized-pages): scope discovery to reusable page planning 2026-10-20 06:48:51 -07:00
winewill f511e17dbd perf(windows): expose per-run installer compression 2026-10-20 21:34:54 +08:00
creatixchu 676a148082 test(plugins): scope platform navigation to the plugin panel 2026-10-20 21:20:30 +08:00
ZiyaZhang 36b0428c26 fix(personalized-pages): improve standalone discovery and minimal plans 2026-10-20 06:18:08 -07:00
creatixchu e182c88ff1 Merge latest bundle platform base into CUA permissions 2026-10-20 21:14:55 +08:00
creatixchu b2878c5722 merge: sync platform admission with updated approval base 2026-10-20 21:12:55 +08:00
zhc d6fdabd274 refactor(file-drag): 将传输常量归属通用文件包 2026-10-20 21:12:18 +08:00
winewill 122da6fcff perf(windows): isolate packaging temporary files 2026-10-20 21:10:29 +08:00
creatixchu 2a29b3f3b4 style(cua): restore the selected third headline position 2026-10-20 21:09:36 +08:00
_Kerman ba50aec4de fix(session): preserve automatic reasoning effort on selection 2026-10-20 21:09:29 +08:00
_Kerman a9c006f9c3 refactor(agent): own model selection on each agent 2026-10-20 21:09:09 +08:00
creatixchu 96e962bc12 Merge remote-tracking branch 'origin/master' into feat/cua-approval-format
# Conflicts:
#	packages/experimental/cua/src/client/mount.ts
2026-10-20 21:05:45 +08:00
_Kerman 639602e63a Merge origin/master into xtr/session-model-selection 2026-10-20 20:57:11 +08:00
zhc 0fc3f0b6bb refactor(markdown): 拆分图片导出名称表达式 2026-10-20 20:56:39 +08:00
creatixchu 7edaabb421 style(cua): align Latin lettering with the Chinese headline baseline 2026-10-20 20:49:52 +08:00
winewill 9ee94429b9 perf(windows): support isolated packaging workspaces 2026-10-20 20:47:05 +08:00
Yichen Jiang c817f21bf6 fix(web): mark the forge bundle experimental on the Plugins page
The Plugins page now shows the Experimental badge only for the official
bundles it lists, so the forge bundle joins that list and the More list
expectations.
2026-10-20 20:46:17 +08:00
Yichen Jiang a614375f36 Merge branch 'worktree/forge-change-request-bindings' into worktree/forge-pr-worktree-panel 2026-10-20 20:39:17 +08:00
Yichen Jiang 4f013673e5 Merge branch 'worktree/composer-host-confirmed-echo' into worktree/forge-change-request-bindings 2026-10-20 20:38:21 +08:00
CreatixChu 37c65e9d88 Merge branch 'master' into feat/cua-feature-preview 2026-10-20 20:38:07 +08:00
CreatixChu 78aeeae767 Merge pull request #6683 from deepseek-harness/fix/cua-service-toggle
fix(cua): 稳定插件启停期间的权限查询与页面布局
2026-10-20 20:35:42 +08:00
winewill a12c0dfe46 revert(windows): drop unproven packaging I/O pool default 2026-10-20 20:33:54 +08:00
ZiyaZhang 2bdbc66dba test(creation-recommendations): align catalog expectations with action tool 2026-10-20 05:32:42 -07:00
creatixchu 1a53ab50fb style(cua): use DSH headlines with serif lettering in previews 2026-10-20 20:31:09 +08:00
ZiyaZhang 7efa59274e docs: refresh personalized page dependency graph 2026-10-20 05:21:10 -07:00
creatixchu afa2ce20b3 test(cua): use plugin lifecycle wait budgets for repeated toggles 2026-10-20 20:18:02 +08:00
creatixchu e77640f24d test(web): align plugin snapshots with persistent progress status 2026-10-20 20:17:53 +08:00
creatixchu c8db8fa7eb test(plugins): verify retained details across profile invalidation 2026-10-20 20:17:53 +08:00
creatixchu beae0efd91 fix(plugins): retain displayed details during activation refresh 2026-10-20 20:17:53 +08:00
creatixchu 6172b26cab test(cua): cover wrapped loading and persistent diagnostics during row changes 2026-10-20 20:17:53 +08:00
creatixchu f8912d267f fix(plugins): retain independent row controls during query suspension 2026-10-20 20:17:53 +08:00
creatixchu 817d4ec740 fix(plugins): announce toggle progress through a persistent live region 2026-10-20 20:17:53 +08:00
creatixchu 3bc49f2825 fix(cua): retain permission guidance and known failures while loading 2026-10-20 20:17:53 +08:00
creatixchu 3509e723a1 fix(cua): own query pauses with plugin management operations 2026-10-20 20:17:53 +08:00
creatixchu 7d8a3fb86f test(cua): reject activation after controller disposal 2026-10-20 20:17:32 +08:00
creatixchu e057fe30d5 fix(cua): match empty application loading and settled heights 2026-10-20 20:17:31 +08:00
creatixchu a77e004dd1 fix(cua): reserve permission hint space during fresh queries 2026-10-20 20:17:31 +08:00
creatixchu b6ea723e9e test(cua): isolate toggle regression from consent storage changes 2026-10-20 20:17:31 +08:00
creatixchu 9c861f145e test(cua): retain screenshot directory setup import 2026-10-20 20:17:31 +08:00
creatixchu cf18b49144 fix(cua): pause permission queries while toggling the bundle 2026-10-20 20:17:31 +08:00
ZiyaZhang 1df9e40786 fix(experimental): avoid repeated discovery and honor declined recommendations 2026-10-20 05:11:51 -07:00
ZiyaZhang c97c9d4f8e fix(experimental): prioritize recommendation eligibility and refresh skill replay 2026-10-20 05:11:38 -07:00
Yif 2951b75ffe fix(plugin-manager): keep the compact bar clear of macOS window controls
With the sidebar open the native window controls live in the sidebar, so the compact
bar only needs its own 16px inset. Collapsing the sidebar moves those controls above
the page, and the compact bar then adds the frame's top clearance so its controls stay
below them instead of underneath.

The style-driven motion between the expanded and compact bars still needs work; it lands
in a follow-up.
2026-10-20 20:09:59 +08:00
Chinesezjc 9a7feb0df7 fix(ci): keep the decision label best effort and owned by the publisher
The check run is a label, not the gate, so a failed write is logged and leaves
the commit status untouched; a replayed workflow token without the checks
permission therefore cannot break the gate. Mark the runs the publisher creates
with an external_id so a same-named job check run from a pull request is not
adopted, and revoke only the publisher's own success.

Record the unchanged status behavior against the archived decision, clarify the
measured context name, and state the pending-write residual in the note.
2026-10-20 20:08:57 +08:00
winewill d0664c9a7c perf(windows): match runtime I/O pool to bounded readers 2026-10-20 19:58:51 +08:00
涂轶翔 185a9d63dd Merge pull request #6606 from deepseek-harness/fix/pi-system-reminders
fix(llm-pi-ai): 保留后续系统提醒的位置以复用前缀缓存
2026-10-20 19:54:07 +08:00
ZiyaZhang 50f21daa84 Merge master into personalized pages experiment 2026-10-20 04:52:17 -07:00
ZiyaZhang ea28b1199c Merge master into plugin recommendation experiment 2026-10-20 04:51:51 -07:00
creatixchu 920ef4bf5e docs(cua): describe preview content and remove duplicate layout prose 2026-10-20 19:49:44 +08:00
ZiyaZhang 7802f06d17 refactor(experimental): compose recommendation remote and reuse current catalog 2026-10-20 04:48:47 -07:00
ZiyaZhang d6ad08c758 refactor(experimental): load creation guidance through skill and compose remote 2026-10-20 04:48:26 -07:00
Chinesezjc 8d0540af50 fix(schedule): hand focus to the heading when a late readback lands 2026-10-20 19:47:46 +08:00
Chinesezjc 82461b7f33 fix(ci): keep the weighted approval status unchanged and add the decision check run
Only add the `weighted approval decision` check run: the required
`weighted approval` commit status keeps its `pending`, `success`, and `error`
states exactly as before, and branch rules keep requiring it. The check run
reports the same decision as a conclusion label and is not required, so it
never blocks a merge.

The Agent Note and README now state the status semantics as unchanged and the
measured reason a required check run is not available here.
2026-10-20 19:44:13 +08:00
creatixchu cb2f2bca47 feat(cua): replace the plugin icon with the supplied pointer artwork 2026-10-20 19:39:53 +08:00
Turtle 358ce64945 Merge pull request #6738 from deepseek-harness/revert-6418-worktree/session-agent-controller-refactor-ff892d
Revert "feat(agent): admit Web prompts through the agent controller"
2026-10-20 19:28:42 +08:00
Chinesezjc ac4f99652e fix(schedule): keep confirmation focus while its detail closes 2026-10-20 19:27:38 +08:00
imccyu b839823118 Merge pull request #6673 from deepseek-harness/fix/touch-tooltip-interactions
fix(client): align touch tooltips and hover affordances
2026-10-20 19:26:56 +08:00
winewill e722ee6869 fix(desktop): cancel diagnostic workers during shutdown 2026-10-20 19:21:31 +08:00
winewill 8c56ebbb3b perf(windows): overlap package and runtime preparation 2026-10-20 19:14:15 +08:00
Stevring 3fef66f140 test(client): use pointer events for entry menu tooltip 2026-10-20 19:12:59 +08:00
Chinesezjc 734812cdad fix(schedule): align the clear copy with the inactive status and harden focus 2026-10-20 19:11:42 +08:00
Turtle aa8bdfc79e Revert "feat(agent): admit Web prompts through the agent controller" 2026-10-20 19:09:02 +08:00
winewill c4f49704a1 fix(desktop): embed mandatory updates without native child windows 2026-10-20 18:58:04 +08:00
Stevring 65d4afafd6 fix(client): reveal menu shortcuts during keyboard navigation 2026-10-20 18:57:48 +08:00
Saafo 92d48e0579 Merge pull request #6663 from deepseek-harness/fix/sidebar-drag-preview
fix(client): isolate native sidebar drag previews
2026-10-20 18:54:51 +08:00
winewill aa59aceaee docs(desktop): replace manual diagnostic scripts with test guidance 2026-10-20 18:54:50 +08:00
Saafo fbc4bb02a0 🧪 test(client): compare settings scroll bounds at CSS pixel precision 2026-10-20 18:52:34 +08:00
Chinesezjc facfab5ce8 fix(schedule): return focus when a cleanup removes its own trigger 2026-10-20 18:48:46 +08:00
Chinesezjc 422aa724e3 fix(schedule): qualify the clear-ended copy and pin its tests 2026-10-20 18:41:33 +08:00
winewill e7a9e2683d perf(windows): bound parallel PE header discovery 2026-10-20 18:39:40 +08:00
winewill d148052e83 perf(windows): batch restored runtime signature verification 2026-10-20 18:30:35 +08:00
zhc 7c27a4aaf9 fix(file-drag): 补齐准备取消与文件名保真处理 2026-10-20 18:25:11 +08:00
Chinesezjc a2416dd357 feat(schedule): remove every ended task from the automation page 2026-10-20 18:24:49 +08:00
Yichen Jiang 2ee6dfb671 feat(forge): name changed files before their directories and drop the header branch label
The changes view cut each changed path from its end, so a long path lost
its file name, and the Session header's branch label crowded the actions
beside the title.

- A changed file in the files menu, a file's header, and the local
  changes card shows its name, then its directory in smaller subdued
  type. The directory shortens first, then the name shortens in the
  middle and keeps its end; hovering shows the complete path, and both
  paths of a renamed file. Assistive technology reads the complete path.
  A rename badge names the old file name, or the old path when only the
  directory changed.
- The checkout row draws a folder for the project directory and a
  branch for a worktree.
- The Session header no longer carries the branch label; the tab's
  checkout row names the branch, the kind of checkout, and its path.
- The README describes the collapsed strip of a large file, and the
  forge bundle README lists the gh commands and branches that bind
  change requests.
2026-10-20 18:15:10 +08:00
Yixiang Tu 84a6d2bcaf fix(llm-pi-ai): preserve later system reminders in request history 2026-10-20 18:13:24 +08:00
ZiyaZhang c078d6535f refactor(experimental): fold creation plans by content 2026-10-20 03:11:59 -07:00
winewill 48c0dfc98b docs(desktop): clarify diagnostic test package instructions 2026-10-20 18:09:39 +08:00
winewill 495977f9d7 fix(desktop): embed ordinary update dialogs in owner windows 2026-10-20 18:08:51 +08:00
Stevring 62f78d82f2 fix(client): preserve keyboard focus hints without hover 2026-10-20 18:07:55 +08:00
Saafo 5c12422a52 🐛 fix(client): preserve settings navigation and narrow desktop layouts 2026-10-20 18:07:52 +08:00
Saafo 2d3c9cb964 🐛 fix(client): preserve desktop settings chrome at narrow widths 2026-10-20 18:07:51 +08:00
Zrs000 2fa68cde12 fix(client): complete narrow settings viewport and hover layout 2026-10-20 18:07:51 +08:00
Zrs000 bc5f14f3b3 fix(ui-schedule): give the page's text fields a 16px touch step
iOS Safari zooms the page for a focused field below it.
2026-10-20 18:07:51 +08:00
Zrs000 aeb71f1061 feat(web): reflow the Settings panel at narrow widths
Below 768px the panel fills the viewport and the rail becomes the horizontal
section strip the Web product uses: the title shares the first row with the
header actions and close control, the registered section cells scroll sideways
on the second, and the active section fills the rest. Cells keep their labels
whole and the strip scrolls instead of truncating, so a registrant's label
length or a longer section roster cannot wrap or clip a name.

The panel places its four seats (title, header, navigation, options) on one
grid, so the desktop rail keeps its 188px track and 64px first row. Preference
rows drop their 48px control gutter and the Appearance cubes share one row at
that width.
2026-10-20 18:07:51 +08:00
Saafo 4470ca8910 🐛 fix(client): tie drag completion to the source row 2026-10-20 18:05:30 +08:00
Stevring eb8ef617c0 fix(client): isolate sidebar drag previews 2026-10-20 18:05:30 +08:00
Turtle e1a11ef09c Merge pull request #6222 from deepseek-harness/turtle/auto-review-cache-ffc00f27f914
fix(auto-review): keep review requests cacheable across calls
2026-10-20 18:05:26 +08:00
zhc 217a50e51e fix(file-drag): disclose MIME database runtime dependency 2026-10-20 18:02:17 +08:00
Turtle 450e81bbf3 Merge remote-tracking branch 'origin/master' into turtle/image-cache-stale-0a243f674e12
# Conflicts:
#	packages/client/ui-primitives/README.i18n.yaml
#	packages/client/ui-primitives/README.md
#	packages/client/ui-primitives/README.zh.md
#	packages/client/ui-primitives/tests/markdown-path-images.client.spec.tsx
2026-10-20 18:00:57 +08:00
Chinesezjc e34162881c fix(ci): gate weighted approval through its commit status and label the conclusion
The required check run introduced previously is not matched by this repository's
branch rules, so the gate could not be satisfied. Keep the required
`weighted approval` commit status as the gate and publish the
`weighted approval decision` check run as the label: action_required while the
policy is unmet, success when it holds, failure on setup or evaluation errors.
The status completes as success or failure instead of waiting as pending, so CI
watchers end.

Record the measured check-run limit, the revocation and recorded-conclusion
design, and the alternatives in the Agent Note; update the review-ownership
README, the workflow permissions, and the approval tests.
2026-10-20 17:59:41 +08:00
Stevring a8e6fe84f0 fix(client): restore touch workspace and search actions 2026-10-20 17:59:13 +08:00
winewill ec17e0127e perf(windows): seal runtime inventories with bounded reads 2026-10-20 17:55:55 +08:00
ZiyaZhang 6543e1c3e5 feat(experimental): refine personalized page placement and first-use notice 2026-10-20 02:54:53 -07:00
Turtle 681cf9b6ad fix(client): show settings nav hover in dark theme
The sidebar nav-item hover token resolved to the dark settings panel fill, so hovering a settings tab changed nothing. Use the interactive hover overlay that the main sidebar rows use and drop the now-unused token.

Refs #6717
2026-10-20 17:54:18 +08:00
Chinesezjc 690f987d3d Merge pull request #6722 from deepseek-harness/revert/weighted-approval-check-run
revert: restore the weighted approval commit status gate
2026-10-20 17:48:59 +08:00
Chinesezjc 4c293671ea Revert "Merge pull request #5440 from deepseek-harness/fix/weighted-approval-check-run"
This reverts commit 17c5ac9bac7eae975dde6596287deeb77cba20a4, reversing
changes made to c830699b0a3d21f993de79b2089f227da913e428.
2026-10-20 17:46:59 +08:00
TurtleandTuyixiang 427abde3da Credit review by @Tuyixiang
Co-authored-by: Tuyixiang <[email protected]>
2026-10-20 17:45:25 +08:00
zhc 9676ff7724 fix(file-drag): generalize transfers and address lifecycle review 2026-10-20 17:45:09 +08:00
Chinesezjc 5ae85515c0 Merge pull request #6670 from deepseek-harness/fix/bundle-self-client-resolution
fix(test-runtime): resolve a bundle own browser entry
2026-10-20 17:44:36 +08:00
Chinesezjc c60fee918a Merge pull request #6380 from deepseek-harness/xtr/session-projection-skill
docs: add session projection design skill
2026-10-20 17:42:58 +08:00
winewill b7f46b5739 perf(build): overlap client compilation with host bundling 2026-10-20 17:38:24 +08:00
_Kerman 8b79a1ddcc Merge origin/master into xtr/session-model-selection 2026-10-20 17:22:59 +08:00
_Kerman d22b5d9fff Merge remote-tracking branch 'origin/master' into xtr/session-store-write-lifecycle
# Conflicts:
#	packages/client/ui-workspace/README.i18n.yaml
#	packages/client/ui-workspace/README.md
#	packages/client/ui-workspace/README.zh.md
2026-10-20 17:19:39 +08:00
_Kerman deb8173dca Merge remote-tracking branch 'origin/master' into xtr/session-store-write-lifecycle
# Conflicts:
#	packages/client/ui-workspace/README.i18n.yaml
#	packages/client/ui-workspace/README.md
#	packages/client/ui-workspace/README.zh.md
2026-10-20 17:19:39 +08:00
Saafo 2e39b9c195 📝 docs(test-runtime): clarify bundle manifest lookup precedence 2026-10-20 17:12:52 +08:00
Stevring b7d8943cfa Merge origin/master into fix/touch-tooltip-interactions 2026-10-20 17:12:10 +08:00
Stevring 8fe2db10a0 fix(client): address touch interaction review findings 2026-10-20 17:07:57 +08:00
ZiyaZhang ab5dd06050 feat(experimental): explain creation cost and refine page suggestions 2026-10-20 02:06:03 -07:00
ZiyaZhang 03ff1010b6 fix(experimental): allow optional reusable-page suggestions 2026-10-20 02:05:22 -07:00
winewill bc3ef12560 fix(desktop): preserve background update overlay focus 2026-10-20 17:04:06 +08:00
_Kerman 6a06b72cdc docs: clarify projection initialization ordering 2026-10-20 16:58:25 +08:00
_Kerman 3540f621b9 docs: clarify session projection migration guidance 2026-10-20 16:58:25 +08:00
_Kerman 97b0d84d69 docs: add session projection design skill 2026-10-20 16:58:25 +08:00
Chinesezjc 294b8e9f4b test(snapshot): re-render expected prompts for the subagent guidance
The rendered tool guidance now ends with the end-turn sentence, so every
recorded system prompt and the bilingual package README fence that show it
carry the same text: 83 system-prompt sidecars and 2 README files.
2026-10-20 16:57:37 +08:00
creatixchu 23a9d4ca6b Merge branch 'feat/bundle-host-platforms' into fix/cua-scope-cleanup 2026-10-20 16:50:59 +08:00
Chinesezjc bc540720ec fix(prompt): end the turn while waiting on subagents
Restore the subagent guidance sentence that #6348 removed: when only a
delegated result remains, the model ends its turn instead of sleeping or
polling, because a finished subagent wakes the idle parent.

A controlled single-request probe (deepseek-v4-flash and deepseek-v4-pro,
150 samples per arm) measured a bash sleep in 0/300 samples with the
sentence and 29/300 without it; any waiting tool call in 7/300 versus
120/300. Unit expectations for the rendered guidance follow the restored
text.
2026-10-20 16:50:13 +08:00
Yichen Jiang 6752c858a7 Merge remote-tracking branch 'origin/master' into worktree/composer-host-confirmed-echo 2026-10-20 16:49:50 +08:00
zhc 31bd06d503 feat(image): restore pan and sharing with plugin-owned transfers 2026-10-20 16:46:23 +08:00
creatixchu 33c7f7ebed test(plugins): refresh inherited status-region snapshots 2026-10-20 16:46:09 +08:00
creatixchu 81c56450d8 test(plugins): observe settled progress in the persistent status region 2026-10-20 16:43:48 +08:00
Chinesezjc 9e6692931c Merge pull request #5440 from deepseek-harness/fix/weighted-approval-check-run
Publish weighted approval as a completed check run
2026-10-20 16:34:06 +08:00
imccyu cc724905a2 Merge pull request #6658 from deepseek-harness/feat/pwa-install-assets
feat(web): ship PWA install icons and standalone metadata
2026-10-20 16:31:07 +08:00
creatixchu ce20daa57c Merge branch 'feat/bundle-host-platforms' into fix/cua-scope-cleanup 2026-10-20 16:29:57 +08:00
ZiyaZhang 8ed706a686 test: refresh personalized page Web replay schema 2026-10-20 01:29:16 -07:00
creatixchu c9c79f4bf1 Merge remote-tracking branch 'origin/fix/cua-service-toggle' into feat/bundle-host-platforms 2026-10-20 16:29:13 +08:00
ZiyaZhang 578997c475 test: refresh plugin routing Web replay pins 2026-10-20 01:29:06 -07:00
creatixchu 224aea6ab4 test(plugins): verify retained details across profile invalidation 2026-10-20 16:27:01 +08:00
creatixchu f9bacf7b85 fix(plugins): retain displayed details during activation refresh 2026-10-20 16:23:28 +08:00
imccyu 8f47dd7e16 Merge pull request #6697 from deepseek-harness/worktree/sync-dsh-0.2.1-entry-customization-to-master
release(dsh): sync 0.2.1 entry customization back to master
2026-10-20 16:18:44 +08:00
ZiyaZhang 257c14743a fix: keep creation proposals scoped to one explicit need 2026-10-20 01:15:34 -07:00
ZiyaZhang 558d22481e fix: select one plugin recommendation before proposing 2026-10-20 01:14:35 -07:00
creatixchu cac8b5de62 Merge branch 'feat/bundle-host-platforms' into fix/cua-scope-cleanup 2026-10-20 16:11:19 +08:00
creatixchu e752f99fdd test(plugins): refresh configuration page live-region snapshots 2026-10-20 16:09:11 +08:00
creatixchu 1666a82f0c merge: inherit wrapped layout and live-region verification 2026-10-20 16:08:35 +08:00
_Kerman 427d62d4a7 fix(ptc-runtime-node): fail fast on mismatched bootstraps and late replies
- Reject a control line that does not start with "{", so another
  release's length-prefixed frame fails as a protocol error instead of
  waiting for a newline until the deadline.
- Bump SSH_PROTOCOL_VERSION to 3: executable helpers embed the PTC
  worker, so an older helper is now rejected while connecting.
- Send a small error instead of a late binding result from a finished
  persistent evaluation, so it cannot overflow the pipe shared with
  the next evaluation.
- Leave per-call binding waits pending after the result instead of
  rejecting them; unawaited calls crashed the child and leaked the
  trace into the result logs.
- Pin birpc to 4.2.0 so the host and the bundled child agree, and
  regenerate THIRD_PARTY_NOTICES.md.
- Update stale frame wording.
2026-10-20 16:06:50 +08:00
creatixchu d405e9546e Merge remote-tracking branch 'origin/fix/cua-service-toggle' into feat/bundle-host-platforms 2026-10-20 16:06:14 +08:00
creatixchu 66f0ee1fbb test(cua): align mount assertions with query ownership 2026-10-20 16:03:07 +08:00
creatixchu 2bb84a5974 test(cua): cover wrapped loading and persistent diagnostics during row changes 2026-10-20 15:59:56 +08:00
creatixchu 51dcb6f044 Merge branch 'feat/bundle-host-platforms' into fix/cua-scope-cleanup 2026-10-20 15:57:12 +08:00
creatixchu 5b8c1f4550 merge: retain current CUA toggle ownership with persistent consent 2026-10-20 15:57:09 +08:00
winewill 9738e49720 fix(desktop): preserve partial-write history and isolate journals 2026-10-20 15:56:15 +08:00
creatixchu 9b6a97d389 test(plugins): follow the experimental page for platform assertions 2026-10-20 15:56:08 +08:00
creatixchu b60d2f4861 Merge remote-tracking branch 'origin/fix/cua-service-toggle' into feat/bundle-host-platforms 2026-10-20 15:55:04 +08:00
Yichen Jiang 9220217bcd test(web): hold the opened-group fold's Turn with Agent maintenance
The composer publishes a message's echo only after the Host accepts it, so
holding the prompt request kept the echo from appearing and both cases timed
out. Agent maintenance keeps the accepted message in the inbox instead, so
the fold settles before the new Turn starts and the scroll assertions run
unchanged.
2026-10-20 15:52:01 +08:00
Yichen Jiang 317e56d108 fix(web): accept a prompt when its Inbox row arrives before the receipt
The Host's Inbox projection can reach the client before the prompt receipt.
Until the receipt published the echo, QueueDock showed the message as a
queued row, and in "On next message" mode the conversation followed the
grown tail and then folded back.

An Inbox row carrying the request id proves that the Host accepted the
prompt. The Session now publishes the echo and leaves the blank layout at
whichever of the receipt or the row arrives first, and calls the new
onAccept callback in that update. The composer settles inside that callback,
before the echo renders, so the fold starts from the shrunk composer instead
of depending on microtask order.
2026-10-20 15:51:53 +08:00
Yichen Jiang 899320122b fix(web): keep a sent message's attachments out of the next message's limits
While a message waits for the Host, its attachments stay in the draft until
the Host accepts it. The image intake counted them against the limits of the
next message, and the rail offered a remove button that the input refused
without feedback.

InputState now lists the attachments of the message being sent. The intake
pre-check counts only the images added for the next message, and the rail
shows the sent attachments without a remove action.
2026-10-20 15:51:35 +08:00
ZiyaZhang 29317a82c8 docs: clarify independent personalized page discovery 2026-10-20 00:47:57 -07:00
imccyu 14e5add505 Merge master into DSH 0.2.1 entry customization synchronization branch
Bring master 45d1196d3fe into the release/dsh-0.2.1 squash commit of #6247
so the release commit stays an ancestor of master.

Conflict resolutions:
- apps/web/tsconfig.json, tsconfig.host.json: keep the entry-customization
  e2e and master's Computer Use e2e entries.
- docs/subsystems/slots.{md,zh.md}: keep both added paragraphs; re-record the
  slots, ui-primitives, and ui-workspace README translation pairs.
- ui-sidebar SidebarRoot.tsx and locales.ts: keep the More menu imports and
  copy next to master's version-copy toast.
- apps/web/tests/agent-preset-selection.e2e.ts: keep the New Session
  blank-reuse assertions, read presets through master's
  sessionProjections.checkpoint() instead of the deprecated stateOf().

Follow-ups on master's side:
- ui-sidebar sidebar-root spec: give master's version-copy render the entry
  pin props that SidebarRootInjected now requires.
- snapshots/web/steering/trajectory.expected.md: add the More conversation
  views trigger to master's new Trajectory golden.
2026-10-20 15:47:29 +08:00
creatixchu 8126594af5 fix(plugins): retain independent row controls during query suspension 2026-10-20 15:46:43 +08:00
creatixchu 0178c000fd fix(plugins): announce toggle progress through a persistent live region 2026-10-20 15:43:16 +08:00
Chinesezjc ad0a0a9e5a fix(ci): revoke every passing conclusion and record it from main
A required check accepts success, neutral, and skipped, and any workflow in the
pushed branch can name a job after the required check, so revoke every passing
conclusion instead of only the success this publisher writes. Take the
conclusion writer from main() so tests no longer append to the runner's
GITHUB_OUTPUT file.

Record the measured naming risk, the edit and re-run migration paths, and the
boundary of each cancellation measurement in the decision note and README.
2026-10-20 15:42:03 +08:00
creatixchu d3db323c6f fix(cua): retain permission guidance and known failures while loading 2026-10-20 15:41:50 +08:00
creatixchu 3cfc042226 fix(cua): own query pauses with plugin management operations 2026-10-20 15:38:44 +08:00
creatixchu 7bfe826399 docs(cua): refresh event references after import cleanup 2026-10-20 15:37:24 +08:00
ZiyaZhang d773750c2f Merge master and retain recommendation scenarios with plugin detail pages 2026-10-20 00:37:08 -07:00
ZiyaZhang 420a4ae6fd feat(experimental): offer personalized pages independently of plugin recommendations 2026-10-20 00:35:51 -07:00
creatixchu 3a9e01b9bf Merge branch 'feat/bundle-host-platforms' into fix/cua-scope-cleanup 2026-10-20 15:34:19 +08:00
creatixchu 5b12f9a57f docs(plugins): refresh platform admission catalog references 2026-10-20 15:33:21 +08:00
creatixchu 1086d65492 Merge branch 'feat/bundle-host-platforms' into fix/cua-scope-cleanup 2026-10-20 15:32:04 +08:00
creatixchu 0dc9fba1c3 Merge branch 'feat/bundle-host-platforms' into fix/cua-scope-cleanup
# Conflicts:
#	apps/web/tests/cua-optin.e2e.ts
#	packages/client/ui-plugin-manager/README.i18n.yaml
#	packages/experimental/cua-bundle/README.i18n.yaml
2026-10-20 15:32:04 +08:00
creatixchu 14585c9b29 fix(plugins): include platform reason on bundle cards 2026-10-20 15:31:51 +08:00
creatixchu 97a571556e refactor(cua): remove redundant bundle metadata and build prewarm 2026-10-20 15:31:28 +08:00
_Kerman e7ffab7902 Merge remote-tracking branch 'origin/master' into xtr/session-store-write-lifecycle
# Conflicts:
#	apps/web/tests/new-session-refused.e2e.ts
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
2026-10-20 15:31:09 +08:00
_Kerman 939752f485 Merge remote-tracking branch 'origin/master' into xtr/session-store-write-lifecycle
# Conflicts:
#	apps/web/tests/new-session-refused.e2e.ts
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
2026-10-20 15:31:09 +08:00
creatixchu 8a1e9afae3 test(cua): limit native opt-in flow to supported Hosts 2026-10-20 15:29:31 +08:00
creatixchu c7b6fba276 feat(plugins): enforce bundle Host platform support 2026-10-20 15:28:55 +08:00
Stevring 5ea275ca2e test(client): verify blocked split pointer hit testing 2026-10-20 15:23:29 +08:00
5ff29190ed feat(ui): add navigation entry customization (#6247)
* fix(ui): avoid tooltip trigger overlap when a side fits

* feat(ui): add navigation entry customization

* fix(test): keep tooltip geometry independent of JSX types

* fix(ui): align customization catalogs and integration fixtures

* test(ui): complete customization lifecycle and release integration coverage

* fix(ui): return entry discovery to the installed plugin list

* docs: refresh module graph for entry customization

* build: refresh workspace links for the moved entry customization package

* docs: regenerate catalogs and pairing hashes for the moved entry customization package

* test(web): cover default-loaded entry customization in the Web e2e

* test(ui): describe entry customization as a browser plugin in the host entry spec

* build: order entry customization publication files as workspace constraints require

* test(release): drop the moved entry customization package from the experimental family list

* style(ui): use the shared focus ring color on entry customization triggers

* test(web): re-record goldens for the default-loaded More navigation menus

* test(web): add the More conversation views trigger to the remaining CI-reported goldens

* docs: record region-owned pins for plugin entry customization

* refactor(ui): rebuild entry customization on region-owned pins and More menus

- Each navigation region (left sidebar, Conversation header, right sidebar
  guide) persists its own unpinned list under
  dsh.slot.manual-order.<list slot key>.v1, filters its own launchers, and
  draws its own More button and Menu around its actions slot.
- ui-entry-customization only registers the shared menu rows (entries with
  pin buttons, Browse other plugins, Create your plugin) into the three
  actions slots and keeps no state.
- Remove ctx.layout.entryVisibility, the plugin store, left-sidebar drag
  reordering, and agentPresetNavigation; ui-layout and ui-agent-preset match
  release/dsh-0.2.1 again.
- Keep the release <nav> and Conversation tab-bar visibility conditions.
- Move the More glyph into ui-primitives as IconAppsAddOutline{Regular,Medium}.

* docs: compile the entry customization owner props example

* docs(ui): describe the wrapping Conversation tab row in the header comment

* fix(ui): return New Session to the Workspace blank and its unsent draft

- New Session (startSession without options) takes the blank-reuse path
  again instead of creating a fresh Session on every call, which hid the
  previous blank and its unsent draft since a5c9c2aaeef (#5775).
- A request that names an Agent preset skips blanks listing a different
  preset, so Creator entries no longer take over an ordinary draft.
- Record the change in a v0.2.1-rc.2 upgrade guide; the Host refresh of a
  stale loaded blank stays deferred (see the Agent Note).

* docs: regenerate the Cordis inspect catalog for New Session blank reuse

* fix(ui): keep the selected entry visible while unpinned

- Left-sidebar rows and View tabs keep the selected entry, with aria-current or aria-selected, until another entry is selected.
- Entry menu Creator action takes no region argument; the registration's region selects the prompt.
- Document the 2px panel-row and 32px View-tab gaps; drop commit hashes from the New Session Agent Note.

* fix(ui): leave a blank of another preset unstaged when starting Creator

startCreatorDraft stages cordis only when the main blank lists no preset or already lists cordis; navigation opens a separate Creator blank otherwise, so a Standard draft blank keeps its preset.

* feat(ui): choose whether a draft prompt replaces, appends to, or keeps an existing draft

- DraftInitializationOptions gains whenDraftExists ('replace' | 'append' | 'keep', default 'keep'); clearPreviousDraft stays and maps to 'replace' only when whenDraftExists is omitted.
- Append adds the prompt as new paragraphs after the existing text and references; attachments stay in every mode.
- The three Create your plugin entries append their regional prompt.

* test(web): align e2e with New Session blank reuse and the visible selected entry

* refactor(ui): share the draft paragraph writer between setDraft and appendDraft

---------

Co-authored-by: Yif <[email protected]>
Co-authored-by: imccyu <[email protected]>
2026-10-20 15:17:13 +08:00
_Kerman 82b23cf4d2 refactor(ptc-runtime-node): drop evaluation ids from the control protocol
One pipe delivers each evaluation's messages before its result, and the
child refuses calls from finished evaluations, so the per-message
evaluation id protected nothing. Remove it with the checks, counters and
helpers it required, measure outgoing messages only when posting, and
replace rethrowing validation blocks with early refusals.
2026-10-20 15:15:04 +08:00
Yichen Jiang 27c1b4c56f Merge pull request #6649 from deepseek-harness/xtr/fix-trajectory-input-order
fix(web): 修复停止补发后轨迹中的输入顺序
2026-10-20 15:14:49 +08:00
Saafo 0727eac568 🔀 merge master into mobile input viewport
Keep both automatic-focus and shared button style test imports so CI can run against the current base.
2026-10-20 15:10:30 +08:00
winewill e9e7722d53 fix(desktop): preserve safe operation diagnostics across failures 2026-10-20 15:09:37 +08:00
Saafo 9b029cdb38 🐛 fix(client): keep frame content inside vertical safe areas 2026-10-20 15:07:37 +08:00
EricMoin 7bc928c449 fix(web): strengthen install icon validation 2026-10-20 15:06:37 +08:00
Stevring 788c22d007 fix(client): retain composer position after mobile submission 2026-10-20 15:05:48 +08:00
winewill a247c4c04a fix(ci): resolve package caches after runner allocation 2026-10-20 15:04:24 +08:00
Chinesezjc d98ff5d9fa fix(ci): gate the setup failure step on the recorded conclusion
The evaluation failure title was preserved by matching details_url, a field
GitHub may rewrite for check runs created with the workflow token, so the
setup-failure step could replace the specific title after all. Record the
conclusion the publish step wrote in a step output and run the setup-failure
step only when no conclusion was recorded; a published decision now survives
a later failure or cancellation without relying on an API field.

Record the measured timeout cancellation path and the re-run migration limit in
the decision note, and scope the pre-push guidance to ACTION_REQUIRED so a
failure conclusion is not read as an approval wait.
2026-10-20 15:01:34 +08:00
winewill fb20e72e96 perf(windows): isolate package cache measurements and configure compression 2026-10-20 15:01:09 +08:00
Stevring d83d0a7330 docs(web): remove unavailable navigation drawer claim 2026-10-20 15:00:44 +08:00
winewill 03152d6ca4 perf(desktop): persist download and pnpm packaging caches 2026-10-20 14:56:55 +08:00
creatixchu 509829970f merge: inherit CUA disposal coverage
# Conflicts:
#	packages/experimental/cua/tests/controller.client.spec.ts
2026-10-20 14:53:46 +08:00
creatixchu 254aef2e2d test(cua): reject activation after controller disposal 2026-10-20 14:52:30 +08:00
_Kerman 534aafce7a refactor(ptc-runtime-node): carry the process control channel over birpc
Replace the length-prefixed JsonChannel and hand-written request/reply
correlation with typed birpc methods over newline-delimited JSON. The
host calls the child's run(); bindings call the host's invoke(); logs
and output-limit reports are notifications. The transport bounds
incoming lines and queued writes, validates UTF-8 and exposed methods,
and rejects outstanding calls when the pipe closes.

Drop logic made obsolete by the new transport, including the 32-bit
frame-length bound on maxMessageBytes.
2026-10-20 14:50:05 +08:00
Stevring 6fbd06d845 fix(client): preserve mobile model menu keyboard focus 2026-10-20 14:48:20 +08:00
Yichen Jiang 32fa63c4e7 fix(forge): draw merged change requests in violet and drafts as drafts
The panel colored merged change requests with the brand blue, which the
theme offered in place of the violet the panel design uses, and drew a
draft with failed checks, requested changes, or conflicts as needing
attention instead of as a draft.

- ui-theme adds the violet-400 and violet-600 static steps and the
  --dsw-alias-state-done-primary alias for finished work: violet-600 in
  the light palette and violet-400 in the dark palette.
- The merged glyph, the collapsed merged line, and the expanded status
  row of a change request card use the new alias.
- An open draft always draws the draft glyph. Only an open change
  request that is not a draft is drawn as needing attention, so a draft
  no longer lights the header button's red dot.
2026-10-20 14:46:39 +08:00
creatixchu 0f4986ebf3 merge: extract CUA toggle handling into dedicated PR 2026-10-20 14:43:12 +08:00
creatixchu 98badd548a fix(cua): match empty application loading and settled heights 2026-10-20 14:38:41 +08:00
_Kerman b3c1586897 chore(trajectory): prune retired unknown cast allowances 2026-10-20 14:38:29 +08:00
creatixchu 0930290367 fix(cua): reserve permission hint space during fresh queries 2026-10-20 14:37:08 +08:00
EricMoin b9e4a29210 fix(web): preserve detail in install icon exports 2026-10-20 14:35:00 +08:00
Chinesezjc 025723a7c7 fix(ci): keep the evaluation failure title the setup step would overwrite
The setup-failure step runs after an evaluation failure has already concluded the
check run as failure with a title that names the failure, so its generic title
replaced the specific one. Keep a failure that already points at the current run
and update every other conclusion.

Record the transition gaps and the filtered list count in the decision note: the
combined status resolves a context to its latest status, so the rollout retires
the earlier pending status instead of leaving it for CI watchers.
2026-10-20 14:34:22 +08:00
Stevring dbb0d2bd83 Merge origin/master into fix/touch-tooltip-interactions 2026-10-20 14:34:15 +08:00
creatixchu 2b17255d55 test(cua): isolate toggle regression from consent storage changes 2026-10-20 14:34:12 +08:00
Stevring 11e4b3f52f test(client): fix touch input and hover coverage checks 2026-10-20 14:34:03 +08:00
creatixchu 2052ca437c test(cua): retain screenshot directory setup import 2026-10-20 14:32:26 +08:00
Yichen Jiang e99272d04e feat(forge): bind change requests from gh pr view/edit/checkout and the checkout's branch
- forge-session recognizes `gh pr view`, `gh pr edit`, and `gh pr checkout`
  as binding evidence with the new `referenced` origin. It binds the URL
  given as the subcommand's first argument and the URL the output prints
  for that change request: gh's `url:` field, a URL alone on a line before
  a description, or a JSON `url` member. Links in a description do not
  bind. Pending calls carry this evidence, and the projection's state
  version moves to 2, so existing Sessions fold their logs again.
- forge-session adds `discoverBranch(session, remote, branch)`, the lookup
  that follows a push, for a branch that a caller reports.
- The panel reports the checkout's upstream branch when a provider
  recognizes its remote, once per remote and branch until a refresh, so a
  Session on a branch with an open change request shows it without a push.
- The empty panel names every way a change request becomes linked.
2026-10-20 14:31:10 +08:00
Yichen Jiang 891ea9bd33 feat(forge): show a collapsed large file as the strip of unchanged lines
A large file in the changes view started behind a notice and an outlined
button. It now starts as one strip drawn and opened like the collapsed
unchanged lines, which names its changed line count. Binary files and
files too large to show use the same strip without an action.
2026-10-20 14:30:46 +08:00
creatixchu e03ba51d58 fix(cua): pause permission queries while toggling the bundle 2026-10-20 14:30:34 +08:00
Chinesezjc 70b11f5f5f Merge remote-tracking branch 'origin/master' into fix/weighted-approval-check-run
# Conflicts:
#	.agents/notes/archived/process/2026-09-09-blocked-weighted-approvals-remain-pending.md
#	.agents/notes/archived/process/2026-09-09-blocked-weighted-approvals-remain-pending.zh.md
#	.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.i18n.yaml
#	.agents/notes/implemented/process/2026-09-11-production-blame-approval-weight.i18n.yaml
#	.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.i18n.yaml
2026-10-20 14:29:46 +08:00
Yichen Jiang 73883f5541 Merge remote-tracking branch 'origin/master' into worktree/composer-host-confirmed-echo 2026-10-20 14:27:41 +08:00
Turtle 37dfd82736 Merge pull request #6287 from deepseek-harness/turtle/reconnect-timer-1f7094496bf8
fix(ui-chat): pause the running clock while reconnecting
2026-10-20 14:20:50 +08:00
creatixchu ebe5e587eb feat(cua): show a localized feature preview in plugin details 2026-10-20 14:17:03 +08:00
winewill 4478a868ae fix(plugin-manager): retain refused install identity in diagnostics 2026-10-20 14:16:48 +08:00
Turtle 62e5926995 Merge pull request #6583 from deepseek-harness/turtle/copy-version-click-5d1aa11ad7b6
feat(web): copy the local-build version from the sidebar brand
2026-10-20 14:15:39 +08:00
Turtle c3e9b28845 Merge pull request #6354 from deepseek-harness/agent/web-fetch-fakeip-469be4db70d0
fix(web)!: let web_fetch reach non-public destinations
2026-10-20 14:13:24 +08:00
Turtle 8114a96ae4 Merge pull request #6623 from deepseek-harness/turtle/experimental-plugin-details-all
docs(experimental): add illustrated detail documents to the remaining experimental plugins
2026-10-20 14:11:59 +08:00
EricMoin 2138243df1 Merge branch 'master' into feat/pwa-install-assets 2026-10-20 14:10:47 +08:00
Turtle f8eafdc554 Merge pull request #6644 from deepseek-harness/turtle/discover-back-and-upgrade-0e7147938262
fix(plugin-manager): return to Discover from a bundle page opened there
2026-10-20 14:09:05 +08:00
imccyu 89b9e5b1e8 Merge pull request #6662 from deepseek-harness/fix/iphone-button-outline
fix(client): stabilize shared button outlines in Safari
2026-10-20 14:07:02 +08:00
winewill 8b70fa9af5 feat(ci): offer macOS ZIP compression choices in packaging workflow 2026-10-20 13:47:35 +08:00
winewill df12de44bb perf(desktop): default final macOS ZIP compression to level 3 2026-10-20 13:47:34 +08:00
winewill 51b324a198 perf(desktop): persist download and pnpm packaging caches 2026-10-20 13:47:18 +08:00
Stevring 16f1b7828f fix(client): preserve theme styles for detached documents 2026-10-20 13:45:24 +08:00
Stevring dc1bd1c28e fix(client): unify shared button inset outlines 2026-10-20 13:41:45 +08:00
Yichen Jiang d0fc1f6458 feat(forge): show the changes of a Session and of its change requests
The forge panel opens a changes view in the right sidebar for the
checkout's local changes and for the diff of each change request.

- ForgePanelController reads local diffs in the branch, uncommitted,
  unstaged, and staged scopes through Git in the Session's read-only
  sandbox, change request diffs through the forge provider, and the text
  of a changed file to expand unchanged lines. Change request patches stay
  cached by head commit and base branch within maxCachedDiffBytes. A local
  diff over maxDiffBytes fails as forgePanel/diff-too-large, and a provider
  read over its output limit fails as too-large, which the view explains
  with a link to the code host; a gh timeout fails as unavailable.
- forge adds the diff, fileText, and fileUrl provider methods and the
  too-large failure code, and forge-github implements them with gh.
- The panel shows a local changes card that opens the view and asks the
  Agent for a change request. The view collapses files past a line
  budget, draws lines side by side or in one column with optional
  wrapping, expands unchanged lines on request, and collapses or expands
  every file from its toolbar; file headers use a bare chevron.
- The checkout row copies the worktree or project directory path, the
  refresh glyph turns while a refresh runs, change request cards keep
  their actions while collapsed, and the header entry uses the shared
  compact button.
- client-ui-forge classifies changeRequestKey and FsError as
  peer-required Host imports, which keeps forge and dsh-fs as its peers.
2026-10-20 13:34:27 +08:00
ZiyaZhang ab9558f459 test: align CUA mode guidance with scenario description 2026-10-19 22:33:00 -07:00
creatixchu 1a7eae0d54 Merge remote-tracking branch 'origin/feat/cua-approval-format' into feat/cua-permission-onboarding 2026-10-20 13:31:30 +08:00
creatixchu e4b08c51d5 test(approval): refresh localized shortcut reference snapshots 2026-10-20 13:30:56 +08:00
Turtle 29959ed1ab Merge pull request #6537 from deepseek-harness/turtle/gemini-thought-signature-945bf702b56d
fix(llm-pi-ai): replay Gemini tool-call thought signatures over Chat Completions
2026-10-20 13:29:16 +08:00
Turtleandimccyu fc02fe9624 Credit review by @imccyu
Co-authored-by: imccyu <[email protected]>
2026-10-20 13:26:42 +08:00
Turtle 3937d433c1 Merge pull request #6213 from deepseek-harness/turtle/tilde-strikethrough-124c113089f2
fix(ui-primitives): keep single tildes literal in Markdown
2026-10-20 13:24:19 +08:00
Turtleandimccyu 0705cd69b5 Credit review by @imccyu
Co-authored-by: imccyu <[email protected]>
2026-10-20 13:22:51 +08:00
ZiyaZhang 6f3cdc62fd test: align plugin catalog scenarios and experimental labels 2026-10-19 22:17:05 -07:00
creatixchu 5fe3e753d5 test(cua): follow the experimental plugin page after sync 2026-10-20 13:16:45 +08:00
creatixchu 3c3e0583e5 Merge remote-tracking branch 'origin/feat/cua-approval-format' into feat/cua-permission-onboarding
# Conflicts:
#	apps/web/tests/cua-optin.e2e.ts
#	apps/web/tests/expected/plugin-config/official.expected.md
#	apps/web/tests/expected/plugin-manager/live-enabled.expected.md
#	apps/web/tests/expected/plugin-manager/manager.expected.md
#	apps/web/tests/plugin-manager.e2e.ts
#	packages/api/session-controller/README.i18n.yaml
#	packages/client/ui-plugin-manager/README.i18n.yaml
#	packages/client/ui-plugin-manager/src/client/presentation.ts
#	packages/experimental/cua-bundle/README.i18n.yaml
#	packages/experimental/cua-bundle/README.md
#	packages/experimental/cua-bundle/README.zh.md
#	packages/experimental/cua/tests/application-access.spec.ts
2026-10-20 13:13:41 +08:00
Yudong Han 5ef6d14b9b Merge pull request #6381 from deepseek-harness/feat/web-search-summary
feat(web): web_search 向模型提供 Search API 长摘要
2026-10-20 13:11:00 +08:00
creatixchu 4dc4da365c docs(approval): state label and icon source ownership 2026-10-20 13:10:03 +08:00
creatixchu f45859314e test(cua): cover fixed approval metadata and optional icon 2026-10-20 13:10:01 +08:00
creatixchu b2dd5b8d98 fix(approval): clarify limited-scope Enter action 2026-10-20 13:09:26 +08:00
winewill b7732a8870 test(desktop): accept applied bundle changes in restart fixture 2026-10-20 13:09:09 +08:00
ZiyaZhang 4287b2a9ce Merge master into plugin recommendations 2026-10-19 22:08:29 -07:00
creatixchu eb5150a87b fix(approval): identify invalid action and request constraints 2026-10-20 13:08:26 +08:00
creatixchu 29904ae2a5 test(cua): import question fixtures through package exports 2026-10-20 13:08:25 +08:00
creatixchu b7c9d1d7a5 fix(approval): keep generic question fallback answerable 2026-10-20 13:08:24 +08:00
ZiyaZhang 5eba1ec929 fix: guide capability discovery with scenario descriptions 2026-10-19 22:07:28 -07:00
creatixchu 6f6f889c95 Merge remote-tracking branch 'origin/master' into feat/cua-approval-format 2026-10-20 13:05:27 +08:00
Turtle a0ad6c5345 docs: regenerate module graph after dropping web-fetch-http's proxy dependency 2026-10-20 13:00:18 +08:00
yudshj 995eb90f85 test(web): refresh desktop PTC search output snapshot 2026-10-20 12:58:03 +08:00
Stevring 4995b2a11d test(web): align mobile dependency notices and font regression 2026-10-20 12:56:25 +08:00
winewill 4825f92a02 fix(desktop): include diagnostic worker in packaged archive 2026-10-20 12:52:14 +08:00
Stevring 94ffdd2377 fix(client): align touch tooltips and hover affordances 2026-10-20 12:50:31 +08:00
CreatixChu 082e4c3c3e Merge pull request #6653 from deepseek-harness/worktree/experimental-cua
fix(web): 将 CUA 移到实验性插件二级页并添加标签
2026-10-20 12:50:09 +08:00
Stevring 70500d38b1 docs(test-runtime): keep bundle resolution details in usage 2026-10-20 12:49:45 +08:00
Turtle 915792545f Name the version in the copied toast 2026-10-20 12:49:17 +08:00
Stevring e5132e269d fix(test-runtime): resolve a bundle own browser entry 2026-10-20 12:47:24 +08:00
yudshj ce79846fe6 refactor(web): keep search summary default internal 2026-10-20 12:46:14 +08:00
Yichen Jiang c4064a857a test(web): refresh system-prompt pins for the web_search summary field
Three Web snapshots pinned on master after this branch's original base still
listed the web_search source type without the optional summary member.
2026-10-20 12:46:14 +08:00
yudshj e715991ab9 feat(web): give the model Search API summaries
The DeepSeek Search API returns a short `snippet` for display and a longer
`summary` written for model reading. Carry `summary` on `WebSearchSource`,
map it from the Search API, and render it in place of the snippet in
`web_search` output while the search card keeps the snippet.

`tool-web` collapses summary whitespace and caps each summary at the new
`searchSummaryMaxChars` setting (800 characters by default, the upper end of
the guide's typical range; live summaries measured 1,000-2,300). The cut keeps
surrogate pairs intact and ends in an ellipsis. The PTC tool output type gains
the optional field, so the affected system-prompt sidecars are refreshed, and
the web search round now exercises a multi-line summary.
2026-10-20 12:46:14 +08:00
Turtleandturtle1999 2f01ad549c fix(web)!: let web_fetch reach non-public destinations
The HTTP fetch provider resolved each hostname, rejected any non-public
answer, and pinned the connection. That refused every page behind
TUN-mode proxies with fake-IP DNS and did not confine a model that has
shell network access. Requests now go through the installed proxy
dispatcher and the system resolver with no address restriction.

HttpFetchResolver and the HttpFetchProvider resolver parameter are
removed; the upgrade guide covers the migration.

Co-authored-by: turtle1999 <[email protected]>
2026-10-20 12:44:14 +08:00
Turtleandturtle1999 b5f3d3afc7 Credit review by @turtle1999
Co-authored-by: turtle1999 <[email protected]>
2026-10-20 12:43:55 +08:00
Turtle 2e527ca6f7 Merge pull request #6616 from deepseek-harness/turtle/session-search-id-7fe29ff5a47d
feat(client): 侧边栏搜索匹配 session id
2026-10-20 12:39:37 +08:00
Turtle 40e4107e25 Merge pull request #6577 from deepseek-harness/turtle/worktree-file-link-131c0891e002
fix(ui-chat): open relative file links from the current working directory
2026-10-20 12:38:52 +08:00
Turtle 1f63dc104d Merge pull request #6550 from deepseek-harness/turtle/tilde-path-expand-34ced8c370ab
fix(fs-local): 展开开头的 ~ 为主目录
2026-10-20 12:38:05 +08:00
Turtle 32dbb22170 Merge pull request #6561 from deepseek-harness/turtle/workspace-path-suffix-3291551d8870
feat(ui-workspace): show the shortest distinguishing path suffix for same-named Workspaces in the picker
2026-10-20 12:37:49 +08:00
ZiyaZhang 8145f4c05d fix(plugin-recommendations): settle actions safely across HMR and replay 2026-10-19 21:32:36 -07:00
turtle1999 23a3ba3039 Merge remote-tracking branch 'origin/master' into persistence-schema-digest-keys
# Conflicts:
#	docs/persistence-schema.json
2026-10-20 12:24:48 +08:00
Turtle adcc7fc933 Merge remote-tracking branch 'origin/master' into agent/web-fetch-fakeip-469be4db70d0 2026-10-20 12:19:37 +08:00
Turtle 5c4c743ff3 Merge pull request #6300 from deepseek-harness/turtle/model-picker-defaults-c7775e2d3040
feat(ui-settings-models): open model picker on the configured models
2026-10-20 12:17:05 +08:00
Stevring feaed5016b fix(client): retain mobile viewport and input behavior on the split base 2026-10-20 12:16:57 +08:00
creatixchu 968ceefd22 test(web): update CUA secondary-page expectations 2026-10-20 12:16:39 +08:00
creatixchu d779dda9ab fix(web): move CUA to experimental plugins page 2026-10-20 12:16:39 +08:00
Turtle e8fc11fcf7 fix(ui-primitives): keep single tildes literal in Markdown
Strikethrough now requires ~~, so ranges such as 10~20 stay plain text.

Refs #1508
2026-10-20 12:16:33 +08:00
creatixchu cc2caf9631 test(web): assert experimental labels for each offered bundle 2026-10-20 12:16:12 +08:00
Stevring f23d7801bd fix(client): dismiss mobile keyboard after submitting prompts
Dismiss focus when an accepted submission empties the mobile composer, preserving desktop focus and retained drafts. Rename the shared device predicate to isMobileDevice and update its consumers, documentation, and focus regressions.
2026-10-20 12:11:13 +08:00
Stevring c503b51a94 fix(client): preserve composer position above mobile keyboards
Patch Lexical viewport coordinates and cover keyboard panning in editor and browser regressions. Update the patch disclosure.
2026-10-20 12:10:15 +08:00
creatixchu 91c4ed3cd6 test(web): open workspace picker through the sidebar action 2026-10-20 12:10:07 +08:00
Stevring c29a313379 fix(client): avoid automatic picker focus on mobile
Use the shared device focus policy for command popups, schedule forms,
and the composer model picker while retaining user-requested focus.
2026-10-20 12:09:19 +08:00
Stevring c99d658a9d fix(ui-conversation): avoid automatic composer focus on mobile 2026-10-20 12:06:38 +08:00
_Kerman bb2a0197d4 fix(trajectory): preserve request prompt update ordering 2026-10-20 12:06:14 +08:00
creatixchu f305312e84 refactor(web): classify experimental badges with an explicit list 2026-10-20 12:05:57 +08:00
Stevring 671811627f fix(client): add opt-in inset button outlines 2026-10-20 12:03:57 +08:00
Yichen Jiang 35953a75de fix(forge): classify the panel's Host imports under the published dependency policy
verify-package-dependencies now covers experimental Client packages, and the
forge panel's Host face imported four workspace values that no policy entry
classified. appendPluginRecord writes through the Session class's private log,
and readers recognize a deadline's TimeoutReason only by instanceof, so both
need the provider's shared instance. pluginRecordOf and generatedWorktreeName
keep no state; classifying them as peer-required keeps the panel's existing
Session and worktree peer edges.

With the exports classified, the fixer keeps worktree, Session, and timeout as
the panel's only workspace peers, moves its type-only relationships to
devDependencies and dsh-typert-protocol to dependencies, and regenerates the
lockfile and the module graph.

verify-plugin-record-callers counts a string literal that spells
appendPluginRecord as a caller, so it now admits the dependency policy file,
which names the export to classify it and never calls it.
2026-10-20 12:02:57 +08:00
EricMoin 63b3d2ce4d test(web): remove native icon test decoding 2026-10-20 12:01:49 +08:00
ZiyaZhang 59fb9da656 Merge remote-tracking branch 'origin/master' into feat/plugin-recommendations
# Conflicts:
#	docs/capability-seams.i18n.yaml
2026-10-19 21:00:44 -07:00
EricMoin 5a31bcc3e0 feat(web): ship launcher-ready install icons
A launcher masks the icon it puts on the home screen, and the install manifest
advertised the bare whale favicon: an SVG mark drawn edge to edge on a
transparent canvas, with no tile and no background of its own. Handed to a
mask it loses its fins, and iOS reads no manifest icon at all, so an installed
Web App showed a generic tile.

Ship four rasters rendered from the desktop application tile: 192px and 512px
`any`, a 512px `maskable` whose artwork is scaled into the circle a platform
mask is guaranteed to show, and a 180px `apple-touch-icon`. Request
`display: standalone` so the phone keeps its status bar, and declare `png` in
the static MIME table so a launcher receives an image.

Agent Note: .agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md
2026-10-20 11:58:08 +08:00
_Kerman 7e405ec818 chore: drop unrelated browser harness documentation edits 2026-10-20 11:51:15 +08:00
Yichen Jiang 80254034c7 feat(forge): mark change requests ready and merge them from the panel
An expanded card now marks a draft ready for review and merges a change
request that the host would merge now:

- The forge seam gains provider actions `markReady` and `merge`, which
  return a typed `ForgeActionFailure`; the registry polls a watched change
  request at once after either settles. Snapshots carry `mergeReady` and
  the `mergeMethods` the repository allows, the user's usual one first.
- GitHub marks ready with `gh pr ready` and merges through the REST merge
  endpoint with the head SHA the card showed, so new commits refuse the
  merge; it never enables auto-merge, joins a merge queue, or deletes the
  head branch.
- The panel Remote acts only on visible bindings of the Session, and the
  card offers 标记为可评审 for drafts and a 合并 menu with the allowed
  methods and a confirmation, reporting each outcome in a toast.
- The forge panel e2e replaces the worktree branch before the ARIA
  capture normalizes durations, so a random suffix no longer breaks it.
2026-10-20 11:50:10 +08:00
Yichen Jiang 06d1616224 feat(forge): redesign the change request panel
Call every bound item a change request, whatever its code host calls it,
and rebuild the panel around the redesign:

- State glyphs for open, needs attention, draft, merged, closed, and
  unknown change requests; an open one with failed checks, requested
  changes, or conflicts needs attention, as the header dot does.
- A card whose title opens the change request, with a plain number line,
  a collapsed status summary, and expanded checks, review, merge, and diff
  rows; the checks row opens a list with failed checks first.
- The only open change request of a Session starts expanded.
- The checkout row names the branch or detached commit and the kind of
  checkout, with Restore for a removed worktree; the Host home shortens
  paths.
- Checkout inspection reports linked worktrees and the HEAD commit.
2026-10-20 11:50:10 +08:00
Yichen Jiang accd5b77c4 feat(worktree): name generated worktrees after their Session
Generated worktree names now join the dsh- prefix, the first eight
hexadecimal digits of the UUID in the Session id, and four random
hexadecimal digits, such as dsh-6b1f02c4-9a3e. A Session id without a
UUID gets eight random hexadecimal digits after the prefix. The worktree
runtime exports generatedWorktreeName, uses it for omitted
create_worktree names, and defaults namePrefix to dsh-. The forge panel
names its Session worktrees with the same function and defaults
worktreePrefix to dsh-, so checkouts no longer nest under a dsh/
directory.

The create_worktree name parameter asks for a few lowercase words joined
by hyphens that describe the task, and the tool description drops the
defaults that its parameters already state.
2026-10-20 11:49:51 +08:00
Yichen Jiang fc43a2d507 feat(forge): add the pull request panel and worktree Sessions
The forge panel shows the pull requests a Session created or pushed in a
right-sidebar tab, with a Session header button and branch label, and lets
a new Session start in its own Git worktree from the New Session options
row. The forge bundle ships the panel with the forge registry, the GitHub
provider, the Session bindings, and an isolated worktree service.

The panel listens to the agent-controller/admission stage. It holds the
first message of a Session with a worktree request while it creates and
enters the worktree as Agent maintenance, and refuses messages while the
worktree the Session works in is gone. A failed or stopped creation
returns the message to the composer: the card above the composer shows the
failure, sending again retries with a new branch name, and Continue
without a worktree withdraws the request. A removed worktree can be
checked out again or left for the source checkout. A message that enters
without admission still gets its worktree at the first step proposal.
2026-10-20 11:49:51 +08:00
Yichen Jiang 7c586e5b4c fix(forge): run every polling round on the forge service itself
A polling round started through a caller's ctx.forge runs on that caller's
cordis shadow receiver, and the loop schedule() -> setTimeout -> this.poll()
-> this.fetch() -> this.schedule() wrapped one more shadow around it on every
round. Within a few hundred rounds a write such as `this.timer = ...` in the
tail of fetch() overflowed the stack, the rejection went unhandled, and
app-boot exited the whole process with "fatal load failure". At the default
intervals that is a few hours of watching a change request without activity.

The timer now calls a class-field arrow function, which captures the instance
at construction, so each round starts from the instance and the receiver
chain no longer grows. A watch test polls 1,000 rounds through ctx.forge.
2026-10-20 11:49:25 +08:00
Yichen Jiang dcbfd8079a feat(experimental): bind code host change requests to Sessions
Add the experimental forge capability seam:

- dsh-experimental-forge: provider-neutral repository and change request
  references, a provider registry, and shared status polling. Watchers of
  one change request share one loop; each forge has one batched fetch in
  flight, and the cadence follows check, mergeability, and lifecycle state.
- dsh-experimental-forge-github: GitHub provider that runs the GitHub CLI
  on the host with a scrubbed environment and reads every due pull request
  of a host in one GraphQL query.
- dsh-experimental-forge-session: a host-only projection derives Session
  bindings from gh pr create and git push shell results and from
  plugin:forge-session/binding records; push results trigger head-branch
  discovery of open pull requests, and Session watchers poll bound change
  requests through ctx.forge.
2026-10-20 11:49:25 +08:00
_Kerman 4aaaec34ef test(web): pin trajectory preview workspace path length 2026-10-20 11:46:32 +08:00
Yichen Jiang f5f60ac47a Merge pull request #6418 from deepseek-harness/worktree/session-agent-controller-refactor-ff892d
feat(agent): admit Web prompts through the agent controller
2026-10-20 11:43:45 +08:00
creatixchu b63de2f36e Merge master into application approval format 2026-10-20 11:42:54 +08:00
creatixchu 0c2b90052b Merge remote-tracking branch 'origin/feat/cua-approval-format' into feat/cua-permission-onboarding 2026-10-20 11:41:09 +08:00
creatixchu 6df01745c5 docs(approval): align parameter docs and event references 2026-10-20 11:40:44 +08:00
creatixchu 067ca57b8a docs(approval): regenerate question intent type catalog 2026-10-20 11:40:18 +08:00
creatixchu 948f411a70 feat(cua): present application consent with fixed approval actions 2026-10-20 11:40:17 +08:00
_Kerman cc0e980e48 fix(session): align store-owned writes with lifecycle notifications 2026-10-20 11:33:44 +08:00
_Kerman 49a7a75d75 fix(session): align store-owned writes with lifecycle notifications 2026-10-20 11:33:44 +08:00
fz b42e32dbd4 Merge origin/master into feat/sdk-approval-rpc 2026-10-20 11:32:08 +08:00
_Kerman f56820cabf fix(web): preserve consumed input order in trajectory steps 2026-10-20 11:31:48 +08:00
imccyu 07a4a1713a Merge pull request #6618 from deepseek-harness/worktree-pluginassets
feat(client): serve plugin package assets by URL instead of data URLs
2026-10-20 10:51:26 +08:00
Yichen Jiang 32f4993cd6 Merge pull request #6628 from deepseek-harness/worktree/session-item-trailing-slot
feat(web): add trailing icon slots to session rows
2026-10-20 10:46:10 +08:00
Yichen Jiang 73a9e6c95c Merge branch 'refs/heads/worktree/session-agent-controller-refactor-ff892d' into worktree/composer-host-confirmed-echo
# Conflicts:
#	.agents/notes/implemented/architecture/2026-10-17-agent-controller-message-delivery.i18n.yaml
2026-10-20 10:41:58 +08:00
Yichen Jiang 43d6585375 refactor(agent-controller): recognize admission refusals by code
AgentAdmissionRejected now extends HarnessError with the fixed code
AGENT_ADMISSION_REJECTED. The Session Remote recognizes a refusal by that
code and imports only agent-controller types, so its mapping also holds for
an error from another copy of the module; its literals are typed by the
agent-controller constants, so a changed value fails the build.
2026-10-20 10:40:31 +08:00
Turtle 39d0232581 fix(plugin-manager): document the return tab and keep one origin per view 2026-10-20 10:40:26 +08:00
imccyu c23b1632dc Merge pull request #6638 from deepseek-harness/worktree-sendjump
fix(chat): keep the scroll and status steady when folding an opened group
2026-10-20 10:32:35 +08:00
Yichen Jiang 83ec1b3658 fix(web): preserve session row details across pointer and touch input 2026-10-20 10:29:22 +08:00
Turtle 00536d98a7 fix(plugin-manager): return to the opening tab from a bundle page
A bundle page opened from Discover now returns to Discover instead of
the Installed list.

Fixes #6643
2026-10-20 10:20:12 +08:00
imccyu dedf96f7d1 docs(client): settle the package asset resolver wording 2026-10-20 05:17:58 +08:00
imccyu 1823c6bd0e test(client): build an asset import end to end and keep the resolver call name stable 2026-10-20 04:55:29 +08:00
imccyu d7191fafb6 fix(client): keep plugin strings out of the asset resolver rewrite 2026-10-20 04:32:14 +08:00
imccyu 4fb0054448 fix(client): rewrite image-set urls and keep the route fixture valid on Windows 2026-10-20 04:00:23 +08:00
imccyu 7f3dd4ade0 fix(client): tolerate minified stylesheet placeholders and document the asset reference encoding 2026-10-20 03:40:00 +08:00
ZiyaZhang 7a2abfea95 test(plugin-recommendations): refresh collapsed experiment card count 2026-10-19 12:22:11 -07:00
ZiyaZhang 6906d12ac0 fix(plugin-recommendations): keep disabled experiment under More 2026-10-19 12:08:16 -07:00
imccyu 991b2635ad fix(client): keep package-asset references valid in CSS and pin preview placeholders 2026-10-20 03:07:59 +08:00
imccyu 299741d819 fix(client): address package asset review findings 2026-10-20 02:43:09 +08:00
ZiyaZhang 6c7f45624f fix(plugin-recommendations): preserve explicit execution limits after enabling 2026-10-19 11:38:56 -07:00
ZiyaZhang 89fc15aa0e Merge remote-tracking branch 'origin/master' into feat/plugin-recommendations 2026-10-19 11:36:11 -07:00
imccyu 8965a798c6 test(web): assert scroll continuity when folding an opened process group
Refs #6637
2026-10-20 02:35:11 +08:00
imccyu 62338ac27d fix(chat): keep the status slide while fold room takes growth
Fold room takes output growth before any scroll, like the empty space below content that fits, so the tail is pinned only when no room remains.

Refs #6637
2026-10-20 02:28:49 +08:00
imccyu 6c35093a4b fix(chat): finish a held row under a closed ancestor without another fold
A row whose ancestor finished closing sits under hidden="until-found", which keeps its own size but removes it from the flow. Finish such a row instead of driving it in a second batch that scrolls the view for content that occupies no space, and release held reveals when no closing member remains.

Refs #6637
2026-10-20 02:28:31 +08:00
ZiyaZhang 96300fb946 fix: keep activation continuation within requested scope 2026-10-19 11:17:28 -07:00
imccyu c1d9f141b9 feat(client): resolve package assets through require.asset so the WebWorker preview can map them 2026-10-20 02:14:33 +08:00
imccyu 43a80da55c Revert "feat(webworker-packer): serve package assets beside the preview page"
This reverts commit 6e28e787d564ee4933f7a560808fb05ed635e776.
2026-10-20 02:14:32 +08:00
imccyu 18065b8178 fix(client): follow master's plugin detail previews after rebase 2026-10-20 02:14:32 +08:00
imccyu 508567de89 feat(webworker-packer): serve package assets beside the preview page 2026-10-20 02:14:31 +08:00
imccyu 1ab28c1cb6 fix(client): keep watch-built assets, end cancelled asset streams quietly, and stat before reading bundles 2026-10-20 02:14:31 +08:00
imccyu 44dbf788fa docs: record the client plugin package asset decision and resync Chinese pairs 2026-10-20 02:14:25 +08:00
imccyu 0ecc1dec41 fix(client-modules): reread package manifests when Loader entries restart 2026-10-20 02:13:37 +08:00
imccyu ce84953e46 feat(client): serve plugin package assets by URL instead of data URLs 2026-10-20 02:13:37 +08:00
imccyu cd2d7dea0c docs(notes): serve client plugin package assets through the plugins route 2026-10-20 02:13:36 +08:00
ZiyaZhang 4c864e4ae5 fix: enforce standalone recommendation turn limit 2026-10-19 11:13:07 -07:00
imccyu d07ee15204 Merge pull request #6625 from deepseek-harness/xtr/session-projection-runtime-compat
fix(session-projection): 保留旧插件的运行时兼容调用
2026-10-20 01:47:15 +08:00
imccyu 806ee572b2 test(session-projection): type the async generator legacy yield 2026-10-20 01:28:06 +08:00
fz b88a1c2d14 Merge pull request #6351 from deepseek-harness/fix/sdk-desktop-alignment
fix(sdk): 补齐桌面工具、上下文和原生问答
2026-10-20 01:24:13 +08:00
imccyu 499c2e347f docs(session-projection): correct compatibility statements
Describe async generator yields, key-conflict reads, and macrotask timing as implemented, restore the API controller and mods bridge sentence in the upgrade guide, record the rejected plugin-disposal alternative, and pin the async generator behavior with a test.
2026-10-20 01:09:43 +08:00
ZiyaZhang 4f40d71a22 Pin clarified recommendation guidance in the Web replay 2026-10-19 10:04:30 -07:00
fz e69e8c398f fix(sdk): advertise question capability during lazy initialization 2026-10-20 01:03:31 +08:00
ZiyaZhang d0aa83dd00 Refresh recommendation tool guidance in the bilingual catalog 2026-10-19 10:01:08 -07:00
ZiyaZhang 34545f30d5 Align recommendation dependencies and borders with client policy 2026-10-19 09:59:07 -07:00
ZiyaZhang b207844a53 Clarify recommendation intent boundaries and add real-model cases 2026-10-19 09:46:01 -07:00
turtle1999 54f21361ca docs(experimental): name the Ralph loop and the terminals' persistent background and environment
The Ralph illustration headline is the product name. The persistent terminal page says that the terminal tools keep processes running in the background and carry the current directory and variables between tool calls.
2026-10-20 00:45:55 +08:00
imccyu 8f348ee760 docs(notes): fold projection compatibility into the registration handles note
Record the deprecated registration and keyed-read compatibility in the retained-reader decision instead of a separate note, and remove the 2026-10-19 compatibility note.
2026-10-20 00:42:33 +08:00
ZiyaZhang 0f239390af Polish recommendation cards and cover new plugin catalogs 2026-10-19 09:40:43 -07:00
imccyu 33101ad29f fix(session-projection): log unobserved legacy registration rejections
A legacy plugin may store the registration result as a disposer or ignore it,
so a failed registration became an unhandled rejection that ends a dsh process.
The registry now attaches its own handler: once the current turn ends, a
rejection nobody observed through then/catch/finally is logged through the
calling plugin's logger while the plugin stays active, and one whose owner is
no longer loading or active is dropped. Observed rejections are unchanged.
2026-10-20 00:31:55 +08:00
turtle1999 2738347d06 docs(experimental): make the agent team, terminal, Ralph, and plugin market details more direct
The team illustration shows the conversation whose title bar holds the Agent Team button. The terminal and Ralph pages say plainly that these are the agent's tool calls and what each round sees. The plugin market page describes a market as an index URL you add, without implying who owns it.
2026-10-20 00:22:49 +08:00
imccyu f415c83579 refactor(session-projection): simplify legacy registration compatibility
- register() returns the registration's effect disposer with then/catch/finally
  attached; the original body moves unchanged into a private acquire(), keeping
  synchronous installation and the asynchronous handle handoff.
- Legacy stateOf(session, key) logs one ctx.logger.warn per key per registry;
  registration and calls to its result no longer warn.
- Containing an ignored legacy registration failure is deferred and recorded
  under Known Limitations.
2026-10-20 00:18:25 +08:00
imccyu 87ca056c0d docs(upgrade): fold registration compatibility into the projection handles guide
Move the session projection handles guide from the released v0.2.1-alpha.3
directory to the current v0.2.1-rc.2 directory and fold in the callable
registration and deprecated keyed-read notes, replacing the separate
session-projection-registration guide.
2026-10-20 00:12:00 +08:00
fz f29deda51f perf(chat): avoid ancestor invalidation in history spacing 2026-10-20 00:09:46 +08:00
Shigma 7052192d4c fix(cordis): clarify resolver limits and isolate composition tests 2026-10-20 00:06:19 +08:00
Yichen Jiang fdf14b2bc9 docs(web): clarify session row slots and keyboard limits 2026-10-20 00:05:47 +08:00
turtle1999 116438bcd1 docs(experimental): add illustrated detail documents to the remaining experimental bundles
Ten experimental bundles get a user-facing detail document in English and Chinese. Each starts with one 640x300 SVG feature illustration drawn to the plugin preview standard, then covers how to use the plugin and what to know before turning it on.
2026-10-19 23:50:17 +08:00
ZiyaZhang fbe725a798 fix(plugin-recommendations): explain automatic hot activation 2026-10-19 08:49:42 -07:00
creatixchu 636ab61b34 Merge latest master plugin detail changes into CUA onboarding 2026-10-19 23:48:45 +08:00
creatixchu 2c7847a897 fix(cua): retain client closure checks across awaited consent 2026-10-19 23:43:52 +08:00
creatixchu e8b7565454 test(cua): type consent storage fixtures for lint 2026-10-19 23:41:04 +08:00
_Kerman b3b82021d5 fix(session-projection): handle canceled legacy registrations 2026-10-19 23:24:59 +08:00
Turtle c9eb8e9349 Merge pull request #6605 from deepseek-harness/turtle/experimental-plugin-details
refactor(plugin-manager): show built-in feature previews through bundle detail documents
2026-10-19 23:23:16 +08:00
_Kerman 5224454492 fix(session-projection): restore legacy keyed read materialization 2026-10-19 23:15:11 +08:00
ZiyaZhang 7983ae9d5c feat: open recommended plugin details from conversation cards 2026-10-19 08:14:08 -07:00
fz e7436013d1 fix(inspector): retain bootstrap request until cancellation settles 2026-10-19 23:10:30 +08:00
Yichen Jiang 9dbf907f93 Merge branch 'master' into worktree/session-item-trailing-slot 2026-10-19 23:07:23 +08:00
_Kerman b9c410d13f Merge master into scoped tool executors 2026-10-19 23:06:17 +08:00
Yichen Jiang b97a88f22e refactor(web): share the composer's failed-send settlement
Reference serialization and transport failures dispatched the same
sink-settled failure inline; route both through one failSink method so the
duplication gate passes.
2026-10-19 23:03:43 +08:00
ZiyaZhang 8613969e1f docs: name the experimental plugin group 2026-10-19 08:02:19 -07:00
turtle1999 b1b53672db fix(ui-primitives): separate the detail document with horizontal rules and keep its preview format checked 2026-10-19 23:00:32 +08:00
Yichen Jiang 9153978b36 feat(web): add trailing icon slots to session rows 2026-10-19 22:55:18 +08:00
_Kerman 746c2a4acd fix(session-projection): preserve legacy plugin runtime calls 2026-10-19 22:55:02 +08:00
Yichen Jiang 882b9dad5b test(web): wait for the Host to accept each queued prompt before the next
The composer stays editable while a prompt is sending, so waiting for an
editable composer no longer waits for the round trip. Consecutive sends in
the queue layout and queued image scenarios now wait until the composer
clears, which happens only after the Host accepted the previous prompt.
2026-10-19 22:53:14 +08:00
Yifffan 829c2d3913 Merge pull request #6610 from deepseek-harness/ux/schedule-empty-svg
feat(ui-schedule): 更新自动化任务空状态插画
2026-10-19 22:51:11 +08:00
ZiyaZhang 5b1ca75b7c Merge master and verify optional recommendation delivery 2026-10-19 07:47:57 -07:00
ZiyaZhang facceb8be9 feat(experimental): recommend official plugins after completed answers 2026-10-19 07:46:24 -07:00
Shigma f0700602fa docs(cordis): distinguish resolver sync from local patches 2026-10-19 22:45:44 +08:00
_Kerman 2cc5d541fb test(cua): use executor batches in permission flow 2026-10-19 22:45:01 +08:00
turtle1999 339ecc727f Merge remote-tracking branch 'origin/master' into persistence-schema-digest-keys 2026-10-19 22:44:03 +08:00
Turtle 9495663990 Merge pull request #6504 from deepseek-harness/worktree/web-fork-snapshot-order
test: stabilize fork clocks and image-only Office rendering
2026-10-19 22:43:26 +08:00
turtle1999 be10266430 fix(ui-primitives): keep the detail document scrollbar off its text 2026-10-19 22:41:11 +08:00
Yichen Jiang eb105d3471 Merge branch 'refs/heads/worktree/session-agent-controller-refactor-ff892d' into worktree/composer-host-confirmed-echo
# Conflicts:
#	packages/api/session-controller/README.i18n.yaml
2026-10-19 22:40:19 +08:00
creatixchu 0c59501534 Merge master with CUA onboarding and platform delivery updates 2026-10-19 22:39:36 +08:00
_Kerman 23357e500d merge master into scoped executor foundation 2026-10-19 22:39:29 +08:00
Turtle 85aac4d95c fix(plugin-manager): keep the narrow update note inside the page head
At narrow widths the non-shrinking toolbar grew to the update note's
width and pushed the add menu past the window edge; the toolbar now caps
at the head's width and the note wraps. The collapsed bar keeps the page
heading for assistive technology, the bar and body share the same inset
margins, and every head row reads one top-inset variable.
2026-10-19 22:36:08 +08:00
Yichen Jiang 55d00a08fc Merge remote-tracking branch 'origin/master' into worktree/session-agent-controller-refactor-ff892d
# Conflicts:
#	docs/capability-seams.i18n.yaml
2026-10-19 22:34:29 +08:00
CreatixChu a5773881db Merge pull request #6509 from deepseek-harness/feat/cua-basic-onboarding
feat(cua): 增加 macOS 插件入口与权限管理
2026-10-19 22:32:22 +08:00
Yichen Jiang 8f487bbb61 test(agent-controller): register session projections before the Agent registry
Master's async activation makes AgentRegistry inject sessionProjections and
makes createSessionTestRemote async. Install SessionProjectionRegistry in the
agent-controller and prompt-admission harnesses and await the test Remote in
the image admission tests.
2026-10-19 22:31:31 +08:00
Yichen Jiang 1d51715aca Merge remote-tracking branch 'origin/master' into worktree/session-agent-controller-refactor-ff892d
# Conflicts:
#	docs/config-catalog.md
#	docs/config-catalog.zh.md
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	docs/persistence-catalog.i18n.yaml
#	docs/persistence-catalog.md
#	docs/persistence-catalog.zh.md
#	docs/persistence-schema.json
#	packages/api/session-controller/README.i18n.yaml
#	packages/api/session-controller/tests/commands-upload-file.host.spec.ts
#	scripts/gen-cordis-catalog.ts
2026-10-19 22:25:55 +08:00
turtle1999 bbcee26cb5 fix(plugin-manager): gate detail documents and validate their publication path
verify-md-links and verify-md-wrap now scan packages/*/*/docs/*.md, the
pairing documentation records why detail documents stay outside the
pairing gate, and check-workspace-constraints reuses the runtime
meta.detail path rule, requires an existing file in a subdirectory, and
rejects malformed or missing declarations. The detail section drops its
border and inset padding so images meet the page's rounded corners.
2026-10-19 22:23:13 +08:00
TurtleandYifffan d04a96377a fix(plugin-manager): keep the collapsed bar when collapsing lets the list fit
Collapsing the page bar grows the list's scroll container. A list that
scrolled only a few pixels then fit, the browser clamped it back to the
top, and the bar reopened, so every scroll flipped the layout. The bar
now reopens at the top only while the list still scrolls, and an upward
wheel reopens it over a list that fits only under the collapsed bar.

Co-authored-by: Yifffan <[email protected]>
2026-10-19 22:19:24 +08:00
Turtle dbccedbc13 Merge remote-tracking branch 'origin/master' into turtle/plugin-sticky-header-29bfdb179e18
# Conflicts:
#	packages/client/ui-plugin-manager/README.i18n.yaml
#	packages/client/ui-plugin-manager/src/client/PluginManagerPage.tsx
#	packages/client/ui-primitives/README.i18n.yaml
2026-10-19 22:12:10 +08:00
creatixchu 33381c86c4 fix(release): preserve platform constraints in packed install verification 2026-10-19 22:11:22 +08:00
turtle1999 eeb996845c Merge remote-tracking branch 'origin/master' into persistence-schema-digest-keys 2026-10-19 22:07:58 +08:00
Turtle 5e99b92884 test(web): cover session id search in navigation e2e 2026-10-19 22:06:42 +08:00
Yif fbff97cd28 feat(plugin-manager): compact sticky header and responsive actions 2026-10-19 22:06:14 +08:00
turtle1999 3be4701a9d refactor(plugin-manager): show built-in feature previews through bundle detail documents 2026-10-19 22:04:01 +08:00
creatixchu 539716d112 test(cua): satisfy type-aware lint for permission and platform regressions 2026-10-19 22:00:42 +08:00
creatixchu 14a422021e Merge origin/master into feat/cua-basic-onboarding 2026-10-19 21:53:59 +08:00
_Kerman 7d0301fe05 Merge master into scoped tool executors 2026-10-19 21:53:41 +08:00
Turtle cae21f2ab7 feat(client): match session ids in sidebar search
Refs #6612
2026-10-19 21:50:44 +08:00
creatixchu 05713edefa refactor(cua): open system settings directly from allowed permission controls 2026-10-19 21:41:33 +08:00
Yif 773ce6f691 fix(ui-schedule): strengthen illustration palette contracts 2026-10-19 21:41:12 +08:00
creatixchu ea016fa3dd fix(manifest): describe optional dependency declarations 2026-10-19 21:39:38 +08:00
creatixchu 6d0ba11d2b test(cua): keep fixture opt-in coverage independent of host platform 2026-10-19 21:38:08 +08:00
creatixchu f85a71505f fix(boot): include installed optional packages in profile resolution 2026-10-19 21:37:05 +08:00
Turtle 854043a205 Merge pull request #6567 from deepseek-harness/fix/file-upload-resolver-dispose
fix(file-upload): release the Agent resolver when its owner reloads
2026-10-19 21:33:43 +08:00
creatixchu 35ba8ad66d test(cua): cover platform inference and disabled native provider in preview 2026-10-19 21:33:04 +08:00
creatixchu 587ee17853 test(cua): isolate package manager caches and narrow Loader importer 2026-10-19 21:31:32 +08:00
creatixchu 2e215f20f9 fix(cua): restrict native package delivery to macOS targets 2026-10-19 21:30:07 +08:00
creatixchu c2d384a4c1 style(cua): align permission controls and configured states 2026-10-19 21:29:50 +08:00
creatixchu a27c739204 Merge branch 'feat/cua-basic-onboarding' into feat/cua-permission-onboarding 2026-10-19 21:14:27 +08:00
creatixchu fdb0885ab0 test(cua): load the generated Remote type contribution in controller tests 2026-10-19 21:13:41 +08:00
creatixchu f9fe653ac5 merge(cua): keep single permission controls and consume Remote results directly 2026-10-19 21:12:39 +08:00
creatixchu 03d3cf9196 refactor(cua): consume the generated permission Remote directly 2026-10-19 21:11:13 +08:00
creatixchu 43aafb2e19 fix(cua): retain permission controls while stale errors refresh 2026-10-19 21:08:01 +08:00
creatixchu ecc90620a4 feat(cua): use one action per system permission row 2026-10-19 21:04:06 +08:00
Yif 2f348776b4 feat(ui-schedule): refresh automation empty-state illustration 2026-10-19 21:01:10 +08:00
fz ba4395d046 docs(sdk): clarify unknown user time zone 2026-10-19 20:40:09 +08:00
creatixchu cfa2a53818 merge(cua): retain approved dependency classification and add storage helper 2026-10-19 20:08:09 +08:00
creatixchu e49ef79884 build(cua): classify approved Host imports for the combined package 2026-10-19 20:06:23 +08:00
fz 3c45123daa fix(schedule): inject Web activation without blocking management 2026-10-19 19:59:05 +08:00
fz c4b3b4f82e refactor(deliverables): share file reference guidance as a plugin 2026-10-19 19:58:47 +08:00
creatixchu 9717153b3c merge(cua): align package publication inventory with basic onboarding 2026-10-19 19:55:46 +08:00
creatixchu 98f126e032 merge(cua): integrate combined Host and Client package from basic onboarding 2026-10-19 19:53:37 +08:00
creatixchu 630a693b70 fix(cua): align combined package publication inventory 2026-10-19 19:52:35 +08:00
creatixchu 57af330bf5 test(cua): keep Host marker checks out of the Client compiler 2026-10-19 19:48:35 +08:00
creatixchu 4da281c933 docs(cua): classify the combined package as a Host and Client plugin 2026-10-19 19:47:55 +08:00
creatixchu b40d335e3d refactor(cua): colocate permission UI and narrow package exports 2026-10-19 19:46:13 +08:00
turtle1999 b75f962674 refactor(scripts): address snapshot layout review
Report a missing digest prefix explicitly, normalize line endings for change-record
snapshots, make the key-rename test deterministic, align the cookbook and zh wording
with the node-table layout, and record the layout decision in its Agent Note.
2026-10-19 19:33:43 +08:00
Turtle b702109a8c Merge pull request #6279 from deepseek-harness/turtle/plugin-detail-readme
feat(plugin-detail): show Markdown plugin details on bundle and market entry pages
2026-10-19 19:27:33 +08:00
Turtle 145ff90076 Merge pull request #6591 from deepseek-harness/turtle/session-cwd-hover-c405e797102b
Show the working directory on Session hover cards
2026-10-19 19:27:04 +08:00
turtle1999 efde9a3019 Merge master into persistence-schema-digest-keys 2026-10-19 19:24:13 +08:00
creatixchu 5acc123d16 test(cua): keep native fixture override above plugin bundle ordering 2026-10-19 19:23:28 +08:00
creatixchu 0e6e6e1998 test(cua): avoid waiting for an unmounted panel in failure diagnostics 2026-10-19 19:21:17 +08:00
creatixchu 2cad88bb22 test(cua): report provider state when re-enabling stalls 2026-10-19 19:19:48 +08:00
creatixchu 435c7c16a6 test(cua): cover stale permission answers and durable write races 2026-10-19 19:15:23 +08:00
creatixchu a780875c95 test(cua): isolate HMR queue regression from provider restart coverage 2026-10-19 19:13:31 +08:00
turtle1999 9c8522bb64 feat(plugin-detail): show Markdown plugin details on bundle and market entry pages
Plugins bundle pages and Discover entry pages show the plugin's long
description first, as Markdown with images, in a height-limited region
without its own heading. The document is the file locale meta.detail
names, otherwise the package README, otherwise (market entries only)
the declared repository's README.

The new dsh-plugin-detail library selects documents and reads local
package directories and npm file CDNs without state. plugin-manager
reads installed and offered bundles through readBundleDetail; plugin-market
reads market entries through readEntryDetail, single-file APIs, and a
size-limited partial clone cache serialized across processes. Raw HTML is
omitted, relative links stay text, and relative images load from Host
routes with a sandbox CSP.
2026-10-19 19:13:22 +08:00
creatixchu fc76fb2ada test(cua): verify durable consent across complete provider restarts 2026-10-19 19:12:37 +08:00
Turtle 2099andturtle1999 14dcb78ee8 fix(ci): split the Wine Host compile to fit its heap budget (#6578)
The Wine gate compiled the Host face with one `tsc -b tsconfig.host.json`,
which holds the project-reference graph and the aggregate program in a
single V8 process and aborted at 4063.4 of 4088.3 MB. The supported build
splits those steps, and the native Windows build in the same run compiled
the references at 4096 and reached the aggregate without running out of
memory.

Compile the referenced projects first, then type-check the aggregate, both
at 4096. Assert the two commands and their order, and reject the fused
invocation.

Co-authored-by: turtle1999 <[email protected]>
2026-10-19 11:12:07 +00:00
creatixchu 5b4f3e3cfa test(cua): keep external permission writer source independent 2026-10-19 19:10:36 +08:00
creatixchu 39347a3f24 test(cua): await provider disposal before restoring permission state 2026-10-19 19:03:36 +08:00
creatixchu b788fd3add fix(cua): isolate application leases from permission storage reads 2026-10-19 18:58:55 +08:00
Turtle 8d00245b8e Merge pull request #6594 from deepseek-harness/turtle/plugin-market-enable-stay
fix(plugin-manager): keep the selected tab when enabling a bundle
2026-10-19 18:53:33 +08:00
creatixchu 9199e1a795 feat(cua): persist profile and conversation consent in domain storage 2026-10-19 18:47:55 +08:00
turtle1999 83d8119b3b refactor(scripts): avoid unknown cast when parsing snapshot text 2026-10-19 18:46:52 +08:00
Turtle ca735069a1 fix(plugin-manager): scroll the Plugins list in its own container under the page bar
A sticky bar let reverse keyboard navigation focus controls hidden under
it. The page bar and the More list's head now sit above a separate scroll
container, so focus and scrollIntoView always land in view. The tests read
the bar and body through data hooks, and the README pair documents the
pinned bar.
2026-10-19 18:38:25 +08:00
turtle1999 1edcd49192 refactor(scripts): key persistence schema nodes by subgraph digest
Store one node table keyed by the 16-hex digest prefix of each node's
canonical subgraph instead of per-root graphs with numeric indices, so a
type change rewrites only the changed nodes and their ancestors.

Refs #6597
2026-10-19 18:37:48 +08:00
Turtle 37d0b6d2b0 feat(plugin-manager): pin the Plugins page title bar while scrolling
The Plugins page title, its actions, and the Installed/Discover tabs now
stay at the top of the page while the cards scroll beneath them. The More
list's back-and-title row stays pinned the same way.
2026-10-19 18:37:19 +08:00
Turtleandturtle1999 fe93616859 Show the copied version as a success toast
The brand title stays unchanged; a successful copy shows the shared
success Toast instead.

Co-authored-by: turtle1999 <[email protected]>
2026-10-19 18:23:44 +08:00
Turtle 82a5ae1744 Merge pull request #6174 from deepseek-harness/feat/plugin-version-updates
feat(plugins): check and install newer registry versions of third-party bundles
2026-10-19 18:22:33 +08:00
Chinesezjc b72e83e915 revert(workspace): keep the platform instant rendering in the record writer
The shared formatter renders byte-identical strings for every epoch a
workspace record can receive, and the two inputs it would refuse (a
fractional epoch, an extended year) are unreachable from Date.now() and
from a SessionHeader epoch. Adopting it therefore added a module, a
dependency, and a writer test for an error branch that cannot occur, so
this reverts that increment and records the site as the fifth deliberate
exclusion in the Agent Note.

The session-format migration and its committed acceptance test remain.
2026-10-19 18:18:00 +08:00
winewill 957d2c1285 test(desktop): prepare reproducible diagnostic fault bundles 2026-10-19 18:17:47 +08:00
Turtle 9984cc1627 Pin Session hover-card directory through the browser, blank, and empty cases 2026-10-19 18:15:15 +08:00
turtle1999 0d9577c981 fix(plugin-manager): keep the selected tab when enabling a bundle
Enable now marks the added bundle for scrolling, and the page also
selected Installed for that mark, so enabling from Discover left the
market tab. The mark scrolls only to a card on the current page, and
activation guidance renders only beside the Installed list.
2026-10-19 18:12:26 +08:00
winewill 2163dec985 test(desktop): update disconnect fixture for startup diagnostics 2026-10-19 18:11:37 +08:00
Turtle f04d56a11e test(web): expect the normalized ./ image path 2026-10-19 18:09:50 +08:00
Chinesezjc f7ede17d88 test(session-format): pin the released-log instant profile, and drop a duplicate dependency
- validation.spec.ts now accepts the four-digit-year bounds and refuses
  each non-canonical spelling (year zero, 2026-02-30, a four-digit
  fraction, a numeric offset, surrounding whitespace) with the released
  error message, so the accepted set is guarded by a committed gate
  rather than by a review-time measurement
- the Agent Note cites that test instead of describing a differential
- session-format-v0-to-v1 declares dsh-util-time only in dependencies,
  matching dsh-util-values and the sibling migrations
- util/time lists the migration among its consumers
2026-10-19 18:09:16 +08:00
imccyu 85f4eca731 Merge pull request #6592 from deepseek-harness/revert-6530-feat/image-pan-and-share
Revert "feat(gui): 支持图片拖动平移与原图拖放分享"
2026-10-19 18:05:23 +08:00
imccyu a3debca9ab Revert "feat(gui): 支持图片拖动平移与原图拖放分享" 2026-10-19 18:04:17 +08:00
Turtle ec620b1449 Merge branch 'master' into fix/file-upload-resolver-dispose 2026-10-19 18:04:10 +08:00
winewill f6a6a3407f Merge pull request #6560 from deepseek-harness/fix/macos-update-fullscreen-recovery
fix(desktop): leave macOS fullscreen before update confirmation
2026-10-19 18:04:00 +08:00
std4453 54219b6288 Merge pull request #6530 from deepseek-harness/feat/image-pan-and-share
feat(gui): 支持图片拖动平移与原图拖放分享
2026-10-19 18:01:42 +08:00
Turtle 446e1ea400 Show the working directory on Session hover cards 2026-10-19 18:00:28 +08:00
turtle1999 b8c7fa91d5 fix(plugins): keep the Refresh label on the refresh button 2026-10-19 18:00:21 +08:00
Turtle 4fc8ac8dda Keep brand clicks bubbling and the copy target's width
The copy handler no longer stops propagation; the brand button skips New
Session for clicks inside the local-build fallbacks instead. The copied
label now overlays the 12px title row so the target keeps its width, and
repeated clicks during the feedback are ignored.
2026-10-19 17:55:10 +08:00
winewill d136ac9f6f fix(diagnostics): retain observed plugin versions for recovery 2026-10-19 17:54:46 +08:00
winewill b064af6396 Merge pull request #6566 from deepseek-harness/fix/windows-installer-rendering
fix(desktop): improve Windows installer rendering and caption controls
2026-10-19 17:54:07 +08:00
Turtle 8656e1c23d fix(ui-chat): reuse the tab for ./-prefixed links and state click-time resolution 2026-10-19 17:53:39 +08:00
Chinesezjc f7fbff4672 refactor(workspace): store record timestamps in the shared instant profile
createdAt and updatedAt were written with the platform's toISOString, which
truncates a fractional epoch and writes an extended year past 9999, so a
durable record could hold an instant the four-digit-year profile every
other boundary reads refuses. The writes now go through
formatFourDigitYearInstant and a local storedInstant that raises a
RangeError for the two inputs the profile cannot represent, which are
unreachable from Date.now() and from a SessionHeader epoch. Every
representable epoch renders byte-identically.
2026-10-19 17:49:30 +08:00
winewill 0596c66fbe fix(desktop): correlate Host failures with operation run identities 2026-10-19 17:47:54 +08:00
zhc 9485f9bf40 refactor: clarify image drag constant names 2026-10-19 17:46:59 +08:00
Yichen Jiang 9ab971fde5 Merge pull request #6568 from deepseek-harness/worktree/pi-ai-default-capacities
feat(llm): raise custom model defaults to 1M context and 128K output
2026-10-19 17:44:46 +08:00
zhc f0ed41c8dd fix: address image drag lifecycle and coverage review 2026-10-19 17:43:38 +08:00
zhc 54eed06c6f feat(gui): support image panning and original-file drag sharing 2026-10-19 17:43:38 +08:00
winewill 5d846caecb test(desktop): preserve plugin and runtime failure evidence 2026-10-19 17:41:31 +08:00
winewill 69207c4629 fix(desktop): harden installer rendering lifecycle and tests 2026-10-19 17:40:55 +08:00
turtle1999 53bf067fa1 Merge remote-tracking branch 'origin/master' into fix/file-upload-resolver-dispose 2026-10-19 17:39:33 +08:00
Turtle 93a8a77baf Merge remote-tracking branch 'origin/master' into turtle/worktree-file-link-131c0891e002 2026-10-19 17:37:57 +08:00
winewill dfbe5d8da3 feat(desktop): retain bounded plugin operation diagnostics 2026-10-19 17:35:18 +08:00
_Kerman 0fd34b3abb chore(packages): trim redundant executor dependencies 2026-10-19 17:34:49 +08:00
_Kerman 78b577faa4 refactor(profiles): select PTC execution through overlays 2026-10-19 17:34:49 +08:00
Turtle e5ba436f16 Re-record the ui-sidebar README translation pair 2026-10-19 17:33:35 +08:00
Turtle e934beb347 Copy the local-build version from the sidebar brand 2026-10-19 17:33:35 +08:00
ZiyaZhang 97551ceaaf style(plugins): place versions below included components 2026-10-19 02:33:13 -07:00
winewill be205ad819 Merge remote-tracking branch 'origin/master' into fix/windows-installer-rendering 2026-10-19 17:33:06 +08:00
Yichen Jiang 81963e0665 Merge remote-tracking branch 'origin/master' into worktree/pi-ai-default-capacities 2026-10-19 17:31:04 +08:00
Turtle ad99bdb0c6 Merge branch 'master' into turtle/workspace-path-suffix-3291551d8870 2026-10-19 17:30:01 +08:00
ZiyaZhang 24cbed2d92 fix(plugins): polish version updates UI and unify refresh 2026-10-19 02:29:54 -07:00
Turtle cac36ac075 Merge pull request #6573 from deepseek-harness/worktree/fix-collapse-clock-cleanup
fix(web): remove obsolete browser clock cleanup
2026-10-19 17:29:51 +08:00
Turtle 99dd3de77e Merge branch 'master' into turtle/workspace-path-suffix-3291551d8870 2026-10-19 17:29:02 +08:00
turtle1999 09d47d6a08 feat(plugins): check for updates from the refresh button and fix the merged version styles 2026-10-19 17:25:01 +08:00
Turtle 20b1297fe2 fix(ui-chat): resolve relative file links from the current working directory
Refs #6576
2026-10-19 17:23:02 +08:00
fz 909ffb6941 docs: remove standalone file reference upgrade guide 2026-10-19 17:21:17 +08:00
_Kerman fa8e928e4f fix(tools): require PTC runtime before activating executor 2026-10-19 17:18:08 +08:00
Yichen Jiang a82c4ccdd0 fix(web): remove obsolete browser clock cleanup 2026-10-19 17:17:41 +08:00
winewill f64dadb1dd test(web): match page-owned clock cleanup API 2026-10-19 17:14:18 +08:00
winewill 22542d831e Merge remote-tracking branch 'origin/master' into fix/macos-update-fullscreen-recovery 2026-10-19 17:13:00 +08:00
Yichen Jiang da80c3d9e6 fix(web): publish awaitingFirstTurn only after the Host accepted the prompt
The first-prompt flag is still set before the receipt, so a turn that
starts ahead of it clears the flag, but the Session snapshot reports
awaitingFirstTurn only once the Session left blank, which happens when the
Host accepted the prompt. A snapshot rebuilt while the prompt waits no
longer reports a first turn that nothing accepted yet.
2026-10-19 17:02:21 +08:00
winewill 798f002b19 test(desktop): preserve native transition error identity 2026-10-19 17:01:48 +08:00
Yichen Jiang ed99af1272 Merge remote-tracking branch 'origin/worktree/session-agent-controller-refactor-ff892d' into worktree/composer-host-confirmed-echo
# Conflicts:
#	.agents/notes/implemented/architecture/2026-10-17-agent-controller-message-delivery.i18n.yaml
#	packages/api/session-controller/README.i18n.yaml
2026-10-19 17:00:01 +08:00
fz 1861316e74 fix(sdk): defer harness development context alignment 2026-10-19 16:59:40 +08:00
Yichen Jiang f2e0d7db1b feat(llm): raise custom model defaults to 1M context and 128K output 2026-10-19 16:59:30 +08:00
turtle1999 67a4ab78be fix(file-upload): release the Agent resolver when its owner reloads
session-controller registers its resolver through `ctx.fileUploads`. Inside
that call Cordis returns a new proxy for every function-valued property read
from `this`, so the disposer's identity check never matched and the slot was
never cleared. Any restart of session-controller, such as a profile patch that
changes session-query-sqlite, then failed with "Agent resolver is already
registered" and left every sessionController dependent pending.

Store the resolver in a plain holder object, as the commands receipt resolver
already does, and test registration and disposal through a plugin fiber.
2026-10-19 16:59:18 +08:00
Yichen Jiang a472820428 fix(agent): wait for deliveries on disposal and keep image prompts in order
The Agent controller now finishes disposal only after every delivery it
stopped has settled, and a sender that cancels stops waiting at once, also
while its message waits behind a held one; the message keeps its place and
is dropped at its turn.

An image prompt takes the Session's image admission turn when its
preparation starts, in the Agent's delivery order, and keeps it until the
prompt settles. Image and text prompts therefore enter the inbox in arrival
order, and a model selection waits only for the image prompt being
admitted, not for one still queued.

The core page documents the delivery types, and the architecture overview
lists the controller and its admission event.
2026-10-19 16:58:59 +08:00
winewill 7f5007bca4 fix(desktop): localize fullscreen timeout recovery guidance 2026-10-19 16:54:25 +08:00
winewill b90327fb9e Merge pull request #6556 from deepseek-harness/fix/windows-installed-app-metadata
fix(desktop): correct Windows installed app publisher and name
2026-10-19 16:52:25 +08:00
Turtle 378d0c0cc7 Merge pull request #5989 from deepseek-harness/fix/coverage-fork-exit-order
test(ci): scope the fork exit ordering claim and pin the collapse-timing clocks
2026-10-19 16:51:47 +08:00
Chinesezjc 617f2a2a8c revert(time): keep the client UTC offset in the client plugin
The client bundle rejects a cross-plugin value import for any package it
does not list as inline-safe, and dsh-util-time is not on that list because
it imports the Temporal polyfill at module scope. The offset therefore
stays computed in packages/client/ui-settings-account, and the Agent Note
records it with the same standing as the other three sites that are not
copies of this vocabulary.

This reverts the localUtcOffsetSeconds entry, its consumers, its tests,
its README rows, and the two lockfile importer entries.
2026-10-19 16:49:33 +08:00
Chinesezjc f934ae8f37 refactor(time): fold the remaining duplicated time sites into dsh-util-time
- session-format-v0-to-v1 reads the shared canonical-instant profile
  through fourDigitYearEpoch instead of its own regular expression and
  round trip, with the accepted set pinned by a differential against the
  pre-change predicate (3,508 inputs, 0 divergences)
- localUtcOffsetSeconds holds the seconds-east conversion two clients had
  each written, so the offset a client reports has one home
- the Agent Note records the three sites that stay, and why each is not a
  copy of this vocabulary
2026-10-19 16:49:33 +08:00
Chinesezjc 9153fc8509 Merge pull request #5100 from deepseek-harness/refactor/unify-time-vocabulary
refactor(time): share the time vocabulary across its consumers
2026-10-19 16:49:29 +08:00
winewill e10b502e42 fix(desktop): preserve native fullscreen exceptions through executor 2026-10-19 16:45:49 +08:00
Turtle 06156ae7ca fix(ui-workspace): keep the picker path suffix visible on long titles
The title truncates before the suffix, the row tooltip shows the full path, root paths no longer get an empty suffix, and the labels are derived only when the list renders.
2026-10-19 16:45:02 +08:00
winewill 6895919d5e test(desktop): avoid unsafe manifest assignment in metadata checks 2026-10-19 16:37:46 +08:00
winewill 1c61949838 fix(desktop): bound fullscreen preparation and cover mandatory updates 2026-10-19 16:36:03 +08:00
winewill fb1bfd0e69 fix(desktop): smooth Windows installer rendering and caption controls 2026-10-19 16:32:48 +08:00
_Kerman e9784c073b fix(tools): settle executor calls after unload and validate configuration 2026-10-19 16:32:29 +08:00
_Kerman 3b978adcec test(tools): tidy migrated fixture imports and comments 2026-10-19 16:32:04 +08:00
winewill 2ac579e650 Merge remote-tracking branch 'origin/master' into fix/macos-update-fullscreen-recovery
# Conflicts:
#	apps/desktop/README.i18n.yaml
2026-10-19 16:29:09 +08:00
winewill 0d9a7f9abf fix(desktop): preserve copyright and verify installer metadata 2026-10-19 16:23:59 +08:00
Turtle a8a830f310 feat(ui-workspace): label same-named picker Workspaces with the shortest distinguishing path suffix
Refs #6555
2026-10-19 16:23:45 +08:00
winewill 669e24bbb9 fix(desktop): leave macOS fullscreen before update confirmation 2026-10-19 16:23:32 +08:00
Chinesezjc dbae54a7c9 Merge origin/master into refactor/unify-time-vocabulary
Resolutions:
- docs/config-catalog.md and .zh.md are regenerated from the merged package
  descriptions.
2026-10-19 16:22:37 +08:00
Yichen Jiang 7cf67a2dbf feat(web): keep the composer editable while a message is sent
Enter still sends one message at a time and waits for the Host, but the
editor stays editable meanwhile: typing, paste, completion, and new
attachments go to the next message, and only another send waits. Once the
Host accepts the message, its attachments leave the draft and its text
leaves the editor: the whole draft while it still equals the sent text,
only the sent text and the whitespace after it when the user added text
after it, and nothing when the user changed the sent text itself. A
running Session keeps Stop beside the sending control, and Workspace
selection leaves a draft with a submission in flight in its Session.
2026-10-19 16:08:53 +08:00
winewill f351c96d71 fix(desktop): set Windows publisher and uninstall display name 2026-10-19 16:01:50 +08:00
Turtle 32004176bf fix(fs-local): expand ~ only in requested paths
Directory entry names reach the same display-path helper, so expansion
moves to resolve and lstat. Replacing only the prefix keeps physical
parent traversal; ~\ expands only on Windows.

Refs #6548
2026-10-19 15:53:39 +08:00
creatixchu 4b7df02769 test(cua): separate Coding Tools and preset selection scenarios 2026-10-19 15:53:14 +08:00
_Kerman 2f113f54cf test: refresh Tools API inspection snapshot 2026-10-19 15:51:57 +08:00
creatixchu 36a2b4fba4 test(cua): preserve effective Coding Tools preferences 2026-10-19 15:49:36 +08:00
creatixchu 74e178cb03 refactor(cua): remove automatic Coding Tools configuration 2026-10-19 15:48:20 +08:00
_Kerman ac71b42560 Merge pull request #5966 from deepseek-harness/xtr/session-projection-registration-handles
refactor(session-projection): return retained handles from async registration
2026-10-19 15:47:04 +08:00
_Kerman a63c3ba39a test(sdk): resolve scheduler fixture from the runtime profile 2026-10-19 15:44:05 +08:00
creatixchu adde9215aa merge: cover CUA configuration error responses 2026-10-19 15:42:14 +08:00
creatixchu 4c966c65ea test(cua): cover runtime configuration transport failures 2026-10-19 15:41:44 +08:00
_Kerman 4439153073 fix(boot): require enabled tool executors at startup 2026-10-19 15:41:32 +08:00
_Kerman 2f69e7f181 test: finish executor fixture migration and await readiness 2026-10-19 15:41:31 +08:00
turtle1999 b10bd84dfe feat(plugins): check and install newer registry versions of third-party bundles 2026-10-19 15:40:51 +08:00
Turtle ae0dfd1003 fix(fs-local): expand a leading ~ to the home directory
Refs #6548
2026-10-19 15:35:14 +08:00
creatixchu 7e04470d19 test(cua): isolate explicit execution-mode changes 2026-10-19 15:32:47 +08:00
creatixchu b6d4829ecf test(cua): expect explicit runtime configuration copy 2026-10-19 15:29:15 +08:00
creatixchu 3e69193ce1 merge: verify explicit PTC runtime replacement 2026-10-19 15:27:46 +08:00
creatixchu aa52dc48b5 test(cua): assert the replaced runtime service instance 2026-10-19 15:27:05 +08:00
_Kerman d3f486e98e fix(plan-mode): reject requests before guidance is ready 2026-10-19 15:26:30 +08:00
_Kerman cc79cf553b fix(llm-retry): retain recovery requests during activation 2026-10-19 15:26:30 +08:00
_Kerman 10ec80ce48 fix(tmux-context): await projection readiness before querying 2026-10-19 15:26:30 +08:00
_Kerman 2f30c684ac fix(time-context): await projection readiness for initial requests 2026-10-19 15:26:30 +08:00
_Kerman 05f1066f8c fix(permission-presets): reject publication before initialization 2026-10-19 15:26:29 +08:00
_Kerman ab7bd24b60 fix(session-title): retain fallback requests during activation 2026-10-19 15:26:29 +08:00
_Kerman feff25e8f7 fix(subagent): initialize model selection before agent publication 2026-10-19 15:26:29 +08:00
creatixchu 751c5cc671 merge: include explicit PTC configuration from the CUA base 2026-10-19 15:25:46 +08:00
Turtle b235acebb2 test(llm-pi-ai): cover same-API signature stripping and stored replay signature 2026-10-19 15:24:47 +08:00
creatixchu 6100010e1e feat(cua): include explicit persistent PTC setup 2026-10-19 15:24:47 +08:00
creatixchu b524c7cc19 fix(cua): use the profile editor for persistent runtime changes 2026-10-19 15:24:37 +08:00
_Kerman 1db86655af test(sdk-minimal): expect the native tool executor 2026-10-19 15:24:06 +08:00
_Kerman ef9ed3fec1 test(agent-loop): preserve context after partial batch failure 2026-10-19 15:24:06 +08:00
_Kerman 8822a6d05f Merge remote-tracking branch 'origin/master' into xtr/tool-executor-plugins
# Conflicts:
#	pnpm-lock.yaml
2026-10-19 15:22:40 +08:00
_Kerman 7ebe78d231 test(tools): cover executor cancellation and model fixtures 2026-10-19 15:20:48 +08:00
_Kerman f0987e7f31 fix(gates): recognize scoped executor composition 2026-10-19 15:20:48 +08:00
_Kerman ebd6a9cf79 test(tools): migrate remaining executor smoke fixtures 2026-10-19 15:20:48 +08:00
_Kerman 5935b6b2cc fix(runtime): include the PTC executor in Python carriers 2026-10-19 15:20:48 +08:00
creatixchu 5e98e0e8f7 fix(cua): retain busy-state typing for both settings rows 2026-10-19 15:19:18 +08:00
creatixchu fdc14b5b84 merge: use the explicit persistent PTC setting in CUA 2026-10-19 15:17:28 +08:00
LogosVeritas 7d656c0511 Merge pull request #6369 from deepseek-harness/fix/sdk-minimal-checkpoints
为 sdk-minimal 默认启用已有的 session-checkpoint-policy,在模型请求、顶层工具执行和 step 前等待会话记录持久化,并在刷盘失败时阻止后续操作,将首次日志写入与原生写锁初始化提前到首个模型请求之前,降低短会话关闭时历史无法恢复的风险。同步补齐依赖、配置断言、回归测试及双语升级指南,并依据启用 checkpoint 后的 4 轮、20 个 CI 样本,将 profile-continuation 预算从 4,250 ms 重新校准为 7,000 ms,保留工作负载、完成数量及超预算检查。Fixes #6512。
2026-10-19 15:17:07 +08:00
Yichen Jiang 0afd709560 Merge remote-tracking branch 'origin/worktree/session-agent-controller-refactor-ff892d' into worktree/composer-host-confirmed-echo 2026-10-19 15:16:15 +08:00
Yichen Jiang d4aeef0352 Merge remote-tracking branch 'origin/master' into worktree/composer-host-confirmed-echo
# Conflicts:
#	docs/capability-seams.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	docs/persistence-catalog.i18n.yaml
#	docs/persistence-catalog.md
#	docs/persistence-catalog.zh.md
#	docs/persistence-schema.json
2026-10-19 15:16:01 +08:00
creatixchu 7d6a8f052f feat(cua): add an explicit persistent PTC setting 2026-10-19 15:14:09 +08:00
Chinesezjc adc27809f7 fix: map every zone rejection exhaustively and document the signed span
- timeZoneError and browserTimeZone switch over TimeZoneRejection instead of
  a ternary: each reason that shares a diagnostic is its own case, and the
  default arm assigns the reason to never so adding a reason fails the build
  (assertNever where the package already imports it). The unsupported cause
  is still forwarded.
- the request-zone suite now exercises the blank refusal, so every case the
  switch lists is covered rather than sharing one ternary branch.
- Duration documents that a span is signed: a negative count builds and
  decompose() refuses it, so the caller states its own sign policy.
2026-10-19 15:12:50 +08:00
Yichen Jiang 47d947d8b1 Merge remote-tracking branch 'origin/master' into worktree/session-agent-controller-refactor-ff892d
# Conflicts:
#	docs/capability-seams.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	docs/persistence-catalog.i18n.yaml
#	docs/persistence-catalog.md
#	docs/persistence-catalog.zh.md
#	docs/persistence-schema.json
2026-10-19 15:07:35 +08:00
_Kerman b907ff53d9 test(cli): probe the public tool batch entry in profile resolution 2026-10-19 15:06:41 +08:00
Turtle e8e43fc4c0 fix(llm-pi-ai): replay Gemini tool-call thought signatures over Chat Completions
Fixes #6536
2026-10-19 15:06:09 +08:00
Chinesezjc 19605806cb Merge origin/master into refactor/unify-time-vocabulary
Resolutions:
- packages/util/time/package.json keeps this branch's description and takes
  master's 0.2.1-rc.2 version.
- packages/util/README.i18n.yaml is re-recorded after the merge.
2026-10-19 15:05:12 +08:00
_Kerman 82417accb8 docs(tools): refresh merged catalog and translation records 2026-10-19 15:04:35 +08:00
_Kerman 4a84bc3f7e test(tools): migrate remaining executor fixtures and compatibility checks 2026-10-19 15:04:34 +08:00
_Kerman c0661b8408 fix(tools): retain the service subject while accepted calls settle 2026-10-19 15:04:34 +08:00
luogesi 13db84e624 test(bench): calibrate sdk-minimal checkpoint budget 2026-10-19 14:57:09 +08:00
_Kerman 31a49c6629 Merge remote-tracking branch 'origin/master' into xtr/tool-executor-plugins
# Conflicts:
#	docs/capability-seams.i18n.yaml
#	docs/tool-catalog.i18n.yaml
#	packages/bundle/base/package.json
#	packages/core/agent-loop/tests/serial-listener-review.spec.ts
#	packages/core/agent-tool-presentation/package.json
#	packages/experimental/auto-review/tests/auto-review.spec.ts
#	packages/lsp/tool-lsp/package.json
#	packages/subagent/tool-subagent/tests/list-models.spec.ts
#	scripts/check-workspace-constraints.ts
#	scripts/type-equiv.manifest.json
#	tsconfig.base.json
2026-10-19 14:53:33 +08:00
_Kerman ffe122ffd4 docs(tools): document executor plugins and migration 2026-10-19 14:50:00 +08:00
_Kerman 455686626d refactor(tools): separate scoped executors from tool registration 2026-10-19 14:49:55 +08:00
creatixchu b6fa39d3c2 Merge remote-tracking branch 'origin/feat/cua-basic-onboarding' into feat/cua-permission-onboarding
# Conflicts:
#	apps/web/tests/cua-optin.e2e.ts
2026-10-19 14:32:19 +08:00
imccyu d5b432ac2c Merge pull request #6519 from deepseek-harness/worktree-thinkfold
fix(chat): close a reader-opened process group from its height cap
2026-10-19 14:31:28 +08:00
creatixchu 69f5a19ba7 test(cua): await toggle completion under the browser deadline 2026-10-19 14:30:34 +08:00
Turtle f10ece9ada Merge pull request #6460 from deepseek-harness/turtle/update-teardown-fail-6d82d9119134
fix(desktop-host): exit after acknowledged teardown so updates install
2026-10-19 14:17:35 +08:00
creatixchu 64d1089f88 Merge remote-tracking branch 'origin/feat/cua-basic-onboarding' into feat/cua-permission-onboarding 2026-10-19 14:11:54 +08:00
creatixchu 8dc477498f Merge remote-tracking branch 'origin/master' into feat/cua-basic-onboarding 2026-10-19 14:11:34 +08:00
Turtleand07akioni 4449a2dc67 Credit review by @07akioni
Co-authored-by: 07akioni <[email protected]>
2026-10-19 14:06:22 +08:00
Shigma 23356d9c9c Merge pull request #6193 from deepseek-harness/chore/cordis-timer-1.1.4
chore(cordis): sync Timer 1.1.4 and reuse it in Client
2026-10-19 13:51:40 +08:00
fz 50fb069e7e fix(sdk): address approval interface review findings 2026-10-19 13:45:50 +08:00
Turtle 5a1ab4590b Merge pull request #6525 from deepseek-harness/docs/ai-usage-review-fixes
docs(skills): tighten AI Usage template checks
2026-10-19 13:36:36 +08:00
turtle1999 f24ef669af docs(skills): address AI Usage review comments
Extract the specification version from the skill in the template test,
check the exact placeholder badge URL, and drop duplicate marker and
old-format assertions.

Clarify the highest-level rule and table-header language, remove the
duplicate n/a rule, make the placeholder badge symmetric, and document
how to migrate an old Human and Agent Roles section.
2026-10-19 13:30:25 +08:00
imccyu 473b3d360c Merge pull request #6524 from deepseek-harness/docs/ai-usage-pr-section
docs(skills): present AI usage as a badged stage table
2026-10-19 13:27:40 +08:00
fz 0ff3d1ddd2 fix(sdk): preserve Python config positional arguments 2026-10-19 13:25:51 +08:00
Shigma 675e5de5f8 refactor(cordis): remove obsolete ClientTimerService export 2026-10-19 13:14:59 +08:00
turtle1999 31d8674d5e docs(skills): present AI usage as a badged stage table
Rename the PR description section to AI Usage and replace the level
legend and underlined stage lines with the AI-DECLARATION badge and a
stage table. Define AI involvement as the AI's participation in a
stage, not code volume or time.

The template policy test now checks the heading, marker block, badge,
table header, row order, and old-term removal.
2026-10-19 13:09:09 +08:00
fz 4f063cd2f4 docs(sdk): refresh approval dependency graph 2026-10-19 13:07:20 +08:00
fz 5d3dd1bbb0 feat(sdk): bridge internal approvals to stdio clients 2026-10-19 13:02:20 +08:00
Turtle 87a5f3719e Merge pull request #6513 from deepseek-harness/turtle/plugin-market-skip-private
feat(plugin-market-generator): skip packages and versions marked private
2026-10-19 13:01:58 +08:00
creatixchu b3031bcf8d Merge remote-tracking branch 'origin/feat/cua-basic-onboarding' into feat/cua-permission-onboarding 2026-10-19 13:01:17 +08:00
creatixchu 1aabce5547 test(cua): await responsive layout before measuring overflow 2026-10-19 13:00:45 +08:00
creatixchu b0e4907b10 test(cua): report permission panel overflow details 2026-10-19 12:58:28 +08:00
creatixchu e3b7123da1 Merge remote-tracking branch 'origin/feat/cua-basic-onboarding' into feat/cua-permission-onboarding
# Conflicts:
#	.agents/notes/implemented/architecture/2026-09-21-experimental-capabilities-as-optional-bundles.i18n.yaml
#	.agents/notes/implemented/architecture/2026-09-21-experimental-capabilities-as-optional-bundles.md
#	.agents/notes/implemented/architecture/2026-09-21-experimental-capabilities-as-optional-bundles.zh.md
#	packages/experimental/client-ui-cua/src/client/PermissionPanel.tsx
2026-10-19 12:57:19 +08:00
creatixchu b39ce22a09 test(cua): cover permission actions settling after disposal 2026-10-19 12:55:18 +08:00
creatixchu dd436a6c9c docs(cua): explain explicit package naming exceptions 2026-10-19 12:53:15 +08:00
creatixchu fe14a0972c docs(cua): disclose session consent cache retention 2026-10-19 12:52:38 +08:00
creatixchu 49794a9ad2 docs(cua): define preset filtering and cover late registration 2026-10-19 12:52:26 +08:00
creatixchu 2d16099200 fix(cua): localize complete permission instructions 2026-10-19 12:51:36 +08:00
creatixchu a86170da75 refactor(cua): remove unused permission observation pause controls 2026-10-19 12:51:17 +08:00
creatixchu d07ad00f86 fix(cua): keep the basic bundle independent of PTC configuration 2026-10-19 12:50:57 +08:00
creatixchu 5d0fe54309 test(cua): cover each private native operation once 2026-10-19 12:50:17 +08:00
creatixchu eaea1ec5b9 perf(cua): omit unused icons from application selection 2026-10-19 12:50:07 +08:00
creatixchu a2484cb02d fix(cua): keep permission guidance independent of the UI 2026-10-19 12:49:53 +08:00
creatixchu a1c0a68c80 docs(cua): place preset migration under the current release 2026-10-19 12:49:15 +08:00
creatixchu 373080de5e test(cua): remove unrelated split remnants 2026-10-19 12:48:58 +08:00
_Kerman b361989a45 test(benchmarks): balance user-text scaling sample volumes 2026-10-19 12:46:23 +08:00
creatixchu 302dead9fc Merge remote-tracking branch 'origin/feat/cua-basic-onboarding' into feat/cua-permission-onboarding
# Conflicts:
#	packages/experimental/cua-macos/tests/provider-lifecycle.spec.ts
2026-10-19 12:39:48 +08:00
creatixchu 6b471c6fee Merge remote-tracking branch 'origin/master' into feat/cua-basic-onboarding 2026-10-19 12:36:27 +08:00
_Kerman 679ec80a14 test(web): mount projections for search initiator fixtures 2026-10-19 12:24:35 +08:00
imccyu 1260d96ef3 fix(chat): close a reader-opened process group from its height cap 2026-10-19 12:23:46 +08:00
luogesi 1ddba8b3b0 fix(sdk-minimal): align review fixtures and upgrade guide 2026-10-19 12:13:57 +08:00
luogesi 96f4db3f7d test(sdk): align built profile and snapshot dependencies 2026-10-19 12:13:57 +08:00
luogesi f62182e577 fix(sdk-minimal): checkpoint sessions before dispatch 2026-10-19 12:13:57 +08:00
creatixchu 029f3d7a43 test(cua): avoid unsafe matcher assignment in preview assertions 2026-10-19 12:12:35 +08:00
Turtleandwinewill 3e84d5d38c Credit review by @winewill
Co-authored-by: winewill <[email protected]>
2026-10-19 12:11:21 +08:00
fz 1781189c31 test: sync desktop context and delivery prompt expectations 2026-10-19 12:07:48 +08:00
creatixchu ba96a9aaea test(cua): restore consent coverage and basic preview expectations 2026-10-19 12:04:57 +08:00
fz 61e6e132af docs(sdk): place migration guides under the current release 2026-10-19 11:48:04 +08:00
fz 8614ca9fb4 Merge origin/master into fix/sdk-desktop-alignment 2026-10-19 11:40:37 +08:00
fz b9fb31c2dd fix(sdk): own reminders and bridge native user questions 2026-10-19 11:35:30 +08:00
_Kerman 043071b874 test(cli): read subagent policy through a retained handle 2026-10-19 11:34:36 +08:00
creatixchu 0a21831883 test(cua): retain permission scenarios in click hold replay 2026-10-19 11:34:08 +08:00
winewill 147fee4186 Merge pull request #6350 from deepseek-harness/perf/windows-packaging
perf(desktop): batch Windows workspace tarball packing
2026-10-19 11:33:39 +08:00
creatixchu f722f6e376 test(cua): supply click hold bounds in permission fixtures 2026-10-19 11:31:23 +08:00
creatixchu ef15f31682 test(cua): include the configured click hold guidance 2026-10-19 11:30:56 +08:00
_Kerman c18f5bd3c0 Merge remote-tracking branch 'origin/master' into xtr/session-model-selection
# Conflicts:
#	docs/capability-seams.i18n.yaml
#	docs/capability-seams.md
#	docs/capability-seams.zh.md
#	docs/config-catalog.md
#	docs/config-catalog.zh.md
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	docs/persistence-catalog.i18n.yaml
#	docs/persistence-catalog.md
#	docs/persistence-catalog.zh.md
#	docs/persistence-schema.json
#	packages/api/session-controller/src/types.ts
#	packages/bundle/base/package.json
#	packages/bundle/headless/README.i18n.yaml
#	packages/bundle/headless/README.md
#	packages/bundle/headless/README.zh.md
#	packages/bundle/headless/package.json
#	packages/bundle/headless/tsconfig.json
#	packages/subagent/tool-subagent/tests/model-selection-settings.spec.ts
#	scripts/type-equiv.manifest.json
2026-10-19 11:30:55 +08:00
creatixchu 8a90e689d8 Merge origin/master into the basic CUA entry 2026-10-19 11:29:22 +08:00
_Kerman 8d23747fc0 Merge remote-tracking branch 'origin/master' into xtr/session-projection-registration-handles 2026-10-19 11:28:19 +08:00
turtle1999 4292020410 feat(plugin-market-generator): skip packages and versions marked private
A manifest with "private": true keeps its version out of the index. A Git
package that is private on the default branch is not listed at all, and the
generator warns when every bundle package of a repository is private. The
cache version is bumped so cached private releases are rebuilt.
2026-10-19 11:27:56 +08:00
winewill c39ce9e5e8 Merge pull request #6340 from deepseek-harness/fix/windows-runner-windowless
fix(desktop): launch Windows runner without a console
2026-10-19 11:23:33 +08:00
creatixchu 0d85981aba Merge basic CUA extraction while preserving final behavior 2026-10-19 11:22:45 +08:00
_Kerman 955e600031 Merge master into session projection registration handles 2026-10-19 11:21:16 +08:00
creatixchu ff2c6bb6be test(cua): observe the PTC provider lifecycle directly 2026-10-19 11:20:28 +08:00
creatixchu d4129cb83c test(cua): compare runtime identities without inspecting service proxies 2026-10-19 11:17:44 +08:00
creatixchu 26282b91d4 test(cua): match existing optional bundle presentation 2026-10-19 11:17:08 +08:00
CreatixChu 571ad5fda0 Merge pull request #6425 from deepseek-harness/feat/cua-click-hold
feat(cua): 支持可选的鼠标长按时长
2026-10-19 11:16:43 +08:00
creatixchu 8e726f5a39 refactor(cua): retain existing experimental package classification 2026-10-19 11:15:00 +08:00
creatixchu 9e0adb34ae docs(cua): align basic configuration catalog 2026-10-19 11:14:05 +08:00
creatixchu 8ad4e308a7 fix(cua): complete basic approval extraction 2026-10-19 11:12:33 +08:00
imccyu d9ae93d0c4 Merge pull request #6485 from deepseek-harness/projection-summary
perf(session-projection): serve session-list rows from a summary key set
2026-10-19 11:10:40 +08:00
creatixchu 1954e948cb feat(cua): add optional macOS setup with existing plugin interfaces 2026-10-19 11:10:31 +08:00
imccyu ab77d85f8e chore(docs): regenerate the persistence catalog 2026-10-19 10:53:13 +08:00
imccyu 76bc66b54c Merge pull request #6312 from deepseek-harness/worktree-keepreplies
fix(chat): keep every assistant reply visible when a turn folds
2026-10-19 10:50:20 +08:00
imccyu 99eacb881f fix(session-controller): drop a redundant projection cast and refresh doc graphs 2026-10-19 10:43:21 +08:00
creatixchu bf4d54aa67 test(cua): await cooperative helper shutdown before termination 2026-10-19 10:33:10 +08:00
creatixchu a44e171eeb test(cua): refresh configurable hold snapshots 2026-10-19 10:31:58 +08:00
creatixchu d0de935432 fix(cua): satisfy type-aware lint checks 2026-10-19 10:30:24 +08:00
creatixchu dc508374bd feat(cua): configure click hold limits and allow immediate release 2026-10-19 10:29:21 +08:00
creatixchu a928f614bc Merge origin/master into feat/cua-permission-onboarding 2026-10-19 10:29:08 +08:00
creatixchu faa6e20705 Merge remote-tracking branch 'origin/master' into feat/cua-click-hold 2026-10-19 10:23:21 +08:00
imccyu 6292808bf9 fix(session-controller): keep the list summary contract honest 2026-10-19 10:20:10 +08:00
Tianyi Cui b416e32847 test(office): scope render checks to the exported image 2026-10-19 04:11:05 +08:00
Tianyi Cui ce7414da14 test(office): report render routing failure evidence 2026-10-19 03:58:22 +08:00
Tianyi Cui 36a80c41b3 test(web): explain the adjacent fork clock pins 2026-10-19 03:35:05 +08:00
Tianyi Cui bde18e994e test(web): align fork snapshot clocks 2026-10-19 03:27:12 +08:00
imccyu af2fa50f8a docs(session-projection): stop describing a removed interlock 2026-10-19 03:00:08 +08:00
imccyu 8ff30ebd96 docs(session-projection): state the shipped summary behavior 2026-10-19 02:57:09 +08:00
Tianyi Cui 687aa118f1 feat(session-query): export backend Session Markdown converter
Expose the pure Host formatSessionMarkdown(events, sourcePath?) converter at the package entry. Persistence stat/list now report the selected materialized JSONL artifact path, and the Markdown copy route includes that exact optional path after the title. Frontend progressive display and Session format remain unchanged.
2026-10-19 01:46:27 +08:00
imccyu 76eba949bd chore(docs): regenerate the config catalog after the import shift
Adding the `ProjectionKeySelection` import to the projection cache moved its
`Config`/`inject` declaration down one line, so the config catalog's source
reference is regenerated. Only the source location changes.
2026-10-19 01:37:39 +08:00
imccyu a8f85009e5 test(web): skip blank bands between fully transparent fold rows
The fold blank matrix counted every band that no row box covered, including
a band whose rows on both sides had already faded to opacity 0. Such a band
shows the same background as those rows. Kept intermediate replies move with
the layout while closing groups stay in place, so that band could exceed the
transient limit in side/composer/steps without any visible difference.

A band now counts when a visible row borders it on either side, with the
viewport top and the composer treated as visible. A synthetic-DOM case keeps
a band beside a visible row failing the measurement.
2026-10-19 01:14:04 +08:00
imccyu f230fc2fc2 refactor(session-projection): review fixes for the summary key set
- Export `ProjectionKeySelection` (the read faces' key selection) and reuse it
  from the projection cache instead of restating the union twice. The Cordis
  catalog can now resolve the type its generated signatures reference.
- Make `wire.summary` a plain boolean: omitted means `true`, and a key whose
  map entry carries the summary mark declares `false` so the runtime key set
  and the client view type agree. The type no longer forbids a declaration.
- Describe the mark accurately: it has no runtime representation; `wire.view`
  and `viewSchema` are typed with `SessionProjectionViewValue`, which strips it,
  so the registration erase only widens the state type.
- Say plainly in the package README, the subsystem page, the client field
  comments, and the Agent Note that a list row carries the summary projection
  and never the full projection: a connected Session's store may hold more
  values, but those are outside the list contract.
- Document the summary vocabulary on the subsystem page with `ts type-equiv`
  blocks for the mark, its stripping, the excluded-key test, the summary value
  type, and the key selection; split the prose paragraph.
- Revert the behavior-neutral cache-double refactors in three specs, and record
  in the Agent Note why no upgrade guide accompanies the wire change and how a
  consumer migrates.
2026-10-19 01:13:24 +08:00
imccyu b5f0c91f3f docs(chat): describe reply visibility per Replies After Collapse choice
The group-tree JSDoc and the ui-chat README stated that every reply stays outside whole-Turn folding and named only the collapse-timing setting. They now distinguish All replies from Final answer only and link the new setting.
2026-10-18 23:18:59 +08:00
imccyu 31cca04e6c test(web): expect the 12px reply gap in seeded history
The seeded-history check measured the folded final answer 16px below the Turn control. Hidden process rows separate the two, so the answer now keeps the 12px reply gap.
2026-10-18 23:18:54 +08:00
imccyu 64a1d1e95f chore(docs): regenerate the event graph after the line shift
The producer/consumer matrix points at the declaring line of each event. Adding
the `imageLimits` JSDoc and map-entry wrap moved the `api-session/*` declarations
in `packages/api/session-controller/src/types.ts` down by four lines, so the
graph pair is regenerated. Only source locations change.
2026-10-18 23:12:27 +08:00
imccyu 8e2dd67de8 perf(session-controller): exclude the shared image limits from session summaries
`imageLimits` is a boot constant every Session shares: the unit stores no
per-Session state (`stateSchema: z.null()`, `apply: state => state`) and its
view returns `ctx.attachments.imageLimits`. A list row therefore repeated one
host-wide capability fact on every row. It now carries the summary mark like
the three log-derived units, so a summary read skips it while an opened Session
still receives it through `session/follow`, the control baseline, and
`session.projections`.

No other logic changes: the registration, its schema, its constant view, and
the host-side prompt validation keep reading `ctx.attachments` directly.
2026-10-18 23:04:29 +08:00
imccyu c458eab0a0 fix(chat): keep the reply gap on a folded final answer
A folded Turn gave its final answer the control's 16px clearance through
compactAnswer and data-turn-process-answer, because hidden process rows
keep the answer from matching the control's adjacent-row rule. With every
reply kept, the final answer no longer follows the control, so that 16px
only widened the gap above it. In both choices the margin also switched
from 12px to 16px when the closing rows became hidden at the end of a
next-message fold, moving the answer down about 4px.

A reply separated from the control by folded rows now keeps the 12px reply
gap in both choices; only a row directly following the control receives
16px, and no reply changes its spacing when a fold finishes.
2026-10-18 23:01:23 +08:00
imccyu 8598307c1e test(web): cover kept replies and the replies setting
Settings goldens gain the Replies After Collapse row; folded-Turn aria
goldens for plan review, goal turns, and turn-tail actions include their
intermediate replies. The folded layout checks measure the intermediate
reply that now follows the Turn control at the 12px reply gap.
2026-10-18 23:01:23 +08:00
imccyu 94ebbf124e fix(chat): keep every assistant reply visible when a turn folds
Whole-Turn folding chose the final answer by position, so a Turn that
continued after answering (a notice reply, record keeping, or a tool call)
hid the real answer with the process. A folded Turn now hides only its
process; every assistant-step reply row stays visible.

Settings -> General -> Replies After Collapse (ui-chat.foldedReplies,
default "all") switches back to the previous final-answer-only rule; the
choice applies immediately and is hidden in Verbose.
2026-10-18 23:01:23 +08:00
imccyu 4edfc1ad15 docs(chat): record keeping every assistant reply in folded turns 2026-10-18 23:01:22 +08:00
imccyu ef446fc85f docs(agent-notes): record the session-list summary projection decision
The note records why a Session-list row carries a summary key set instead of
every registered client view: the type-only exclusion mark with its
registration interlock, the derived stable key set reused through the existing
key-taking read faces, the nullable-wrapper rule for `todos`, and the estimated
payload effect of omitting `turnOutline`, `sessionStats`, and `todos`. It lists
the rejected alternatives (a summary-specific read face, a hardcoded key list,
post-hoc filtering, opt-in summaries, and lazy per-value reads) and the costs
the decision accepts.
2026-10-18 22:39:21 +08:00
imccyu 0e1ffcb3a6 chore(persistence): regenerate the event catalog after the line shift
The catalog pages embed the declaring source location of each documented type.
Adding the summary-exclusion import and map-entry JSDoc to
`packages/todo/tool-todo/src/types.ts` moved `TodoItem` and `todo/write` two
lines down, so both catalog languages and their pairing record are regenerated.
No type fingerprint changes: `verify-persistence-changes` still reports 65
roots matching 16 history records.

A second `gen-persistence-catalog` run is required after a schema change: the
page and the machine inventory are generated in one pass, and the page reads
the inventory as it was on disk.
2026-10-18 22:39:11 +08:00
imccyu 3a39d2b132 Merge pull request #6033 from deepseek-harness/docs/pr-declare-skill
docs(skills): add dsh-pr-description and slim the PR template
2026-10-18 22:24:11 +08:00
imccyu 0def172a02 chore(persistence): refresh source line references in the schema catalog
`docs/persistence-schema.json` records the declaring source location of every
persisted type. Adding the summary-exclusion import and map-entry JSDoc to
`packages/todo/tool-todo/src/types.ts` moved two declarations down by two
lines, so the generated catalog is regenerated. No persisted type, field, or
history record changes: `verify-persistence-changes` reports 65 roots matching
16 history records, and the release/format catalogs are unchanged.
2026-10-18 22:23:15 +08:00
imccyu 92f9e79fac perf(session-projection): serve session-list rows from a summary key set
A Session-list row no longer carries every registered client view: the three
units whose values a list row never renders (`turnOutline`, `sessionStats`,
and `todos`) are excluded, which removes the whole-log turn outline and the
figure and todo payloads from every row. An opened Session is unchanged —
`session/follow`, the `session/control` baseline, and `session.projections`
still serve every projected key.

The exclusion is one type-level mark plus one registration interlock:
`SessionProjectionSummaryExcluded<T>` marks a map entry, and
`ProjectionDefinition.wire` requires `summary: false` exactly on a marked key
and rejects it everywhere else, so the mark and the declaration cannot drift.
A nullable entry keeps `null` outside the wrapper, so `todos` stays
`TodoItem[] | null`. `summaryKeys()` returns the derived set, stable until a
registration or its disposal changes the key table, and the existing
key-taking read faces (`cachedSnapshot`, `viewCheckpoint`) accept that set
directly, so summaries need no separate read face and no per-read key copy.
`packages/api/session-controller/src/list.ts` passes the set to the live and
cold cached faces; `cachedPredecessorTitle` is untouched.

Client types only: `SessionSummary.projectionValues` narrows to
`SessionProjectionSummaryValues`, `SessionProjectionSnapshot.values` keeps
every view value with the mark stripped, and no wire field or client name
changed. The cache, subsystem page, and package README document the summary
rule and the two read faces.
2026-10-18 22:13:18 +08:00
imccyu c651566a49 docs(skills): calibrate dsh-pr-description levels and grading criteria
Set the autonomy ranges to hint 0-10, assist 15-25, pair 30-50, copilot
55-85, and auto 90-100 in the skill and the template level line, and
refill the example accordingly.

Sharpen the stage criteria where trial runs disagreed: grade design by
who supplied the adopted structure, count only the author in the first
four stages, ignore process and ordering instructions, define the main
change, keep review at n/a until a comment is handled, and separate
copilot from pair by whether every non-wording item got a human
response. Clarify the Changes categories, test evidence, disclosure
placement, language, and when to update the description.

Refs #6034
2026-10-18 22:02:42 +08:00
imccyu 85a17ec9af Merge pull request #6482 from deepseek-harness/ws-deflate
feat(api-gateway): compress Remote stream WebSocket with permessage-deflate
2026-10-18 21:57:41 +08:00
imccyu d3a6b8ba6f fix(api-gateway): state the compressed-write liveness contract
Correct the downlink-write activity contract for compressed frames: a write
that does not complete at once counts, and a frame above the threshold
deflates asynchronously so its completion always counts. The Host compresses
only its own outbound frames; uplink compression remains the peer's choice.

Stabilize the compressed-write liveness test with a 500 ms heartbeat interval
and assert the bounded half: after the downlink stops, missing Pongs still
terminate the socket.

Refs #6476
2026-10-18 20:57:55 +08:00
imccyu d2da953b5b docs(skills): address dsh-pr-description review findings
Start the template's review line as n/a, as the skill requires at PR
creation, and prompt for an Issue reference under Motivation. Read the
skill's stages only from its Stages and criteria section and reject a
stage name that does not pair one English word with a Chinese name.

Point the GIF disclosure row at record-browser-gif instead of copying
part of its list, merge the calibration notes into one TODO, replace the
design skeleton wording, and reduce the root AGENTS.md rule to a pointer.

Refs #6034
2026-10-18 20:56:28 +08:00
imccyu bf07dbc509 fix(api-gateway): drop maxPayload from the WebSocket compression change
Restore the `ws` default `maxPayload`: the pre-change mux passed no limit, so
the 1 MiB ceiling was an unrelated breaking change. Remove the exported
constant and the oversize-frame test, and drop the matching docs.

Pin the per-frame compression threshold and the accepted compressed-write
liveness consequence with regression tests, correct the queued-write
documentation, move the Agent Note to `implemented/`, and sync the Remote
duplex stream note with `websocketCompression`.

Refs #6476
2026-10-18 20:35:32 +08:00
imccyu 96adf85b5f docs(skills): rewrite pr-declare as the repository dsh-pr-description skill
Replace the pr-declare skill with dsh-pr-description, a repository-only
skill (user-invocable: false) that owns how deepseek-harness PR
descriptions are written and updated: section rules, Issue references,
test design, change-specific disclosures, and the Human and Agent Roles
section with its levels, stages, grading rules, and update timing. The
skill no longer fetches an external specification, asks the user for a
level, or adds badges.

Reduce the pull request template to a skeleton that points to the skill,
point root AGENTS.md at the skill, and make the template test check that
the template's role level line and stage placeholders match the skill.

Closes #6034
2026-10-18 20:30:14 +08:00
turtle1999 2f4bf25597 docs(skills): fetch processes and badges, re-read PR body before writing 2026-10-18 20:22:46 +08:00
turtle1999 420c4cdeee docs(skills): add pr-declare skill for PR AI-usage declarations 2026-10-18 20:22:46 +08:00
imccyu f881f61024 feat(api-gateway): compress Remote stream WebSocket with permessage-deflate
Add a `websocketCompression` Gateway config field (`'deflate'` by default,
`'none'` to disable) and negotiate `permessage-deflate` on `/api/remote.mux`.
Frames whose JSON text reaches 1024 characters are compressed in the mux send
path, because `ws` ignores its `threshold` option under the default context
takeover. `maxPayload` is bounded by `max(streamInboxBytes, 1 MiB)` so one
compressed frame cannot inflate past the fixed ceiling.

Fixes #6476
2026-10-18 20:07:13 +08:00
Tianyi Cui e16c52f610 fix(session-log-export): keep Markdown route path internal
Remove the unused SESSION_MARKDOWN_EXPORT_PATH package re-export while keeping the internal routes definition and Host import. Read the constant directly from routes in the same-package route test. SESSION_LOG_EXPORT_PATH remains public.
2026-10-18 18:28:26 +08:00
Tianyi Cui 8888db9545 docs(session-log-export): correct Markdown copy facts
Describe the direct session/title field read and same-click deferred clipboard request, cover Markdown copy in the selection guidance, and tighten the Chinese Blob and compaction wording. Re-record the README pair.
2026-10-18 18:05:37 +08:00
Tianyi Cui e0b32d1d90 docs(session-log-export): describe Markdown copy routes
Update the client and Host module descriptions and apply JSDoc to mention the authenticated Markdown copy route alongside ZIP download and feedback. Comment-only change.
2026-10-18 17:56:42 +08:00
Tianyi Cui e671c9e28d fix(session-log-export): address Markdown copy review
Preserve click activation by admitting a promised ClipboardItem write synchronously, own its preparation and write promises, and distinguish preparation from clipboard failures. Fold the title from the single readSession snapshot, preserve indentation, close unfinished assistant fences, and restore the shell.overlay props contract. Update the README pair and browser fixture inventory.
2026-10-18 17:52:54 +08:00
Tianyi Cui 36c89de7bd test(web): include copy Markdown fixture in inventory
The navigation-panes recorded-fixture inventory now lists the owner-local copy-markdown.expected.md file. No assertion was weakened; no snapshot or production code changed.
2026-10-18 17:13:54 +08:00
Tianyi Cui 6118b51fa8 test(session-log-export): remove unknown casts from fixtures
Type the Header and toast test fixtures with the standard Session/global props they render with, and read slot injections through a narrow object helper. Prune the four retired unknown-cast baseline entries. No product behavior changes.
2026-10-18 17:00:45 +08:00
Tianyi Cui ae22f461f3 feat(session-log-export): add Session Markdown copy
Add Copy as Markdown to the Session Header more-actions menu. A new authenticated /api/session.markdown route reads complete live-preferred history through sessionQuery.readSession and renders append-only user, assistant, and tool events as Markdown. Compaction surface replacements, reasoning, injected context, and request metadata stay out of the transcript. One plugin-wide copy operation owns the browser clipboard, with an overlay toast for success and failure.
2026-10-18 16:51:13 +08:00
Tianyi Cui aab314e40e Merge pull request #6465 from deepseek-harness/worktree/unsigned-desktop-local-branding
feat(desktop): use local branding for unsigned packages
2026-10-18 16:15:21 +08:00
imccyu 586e3a3f59 Merge pull request #6321 from deepseek-harness/worktree-chatwhalepos
feat(ui-chat): anchor running status to the running turn tail
2026-10-18 16:00:42 +08:00
Tianyi Cui 5d98573852 Merge remote-tracking branch 'origin/master' into worktree/unsigned-desktop-local-branding
# Conflicts:
#	docs/development.i18n.yaml
2026-10-18 15:53:01 +08:00
imccyu bde15f201b fix(ui-chat): keep echoes after a running Turn's empty control at the input gap
After a stop, steering still pending is claimed with the next prompt and the
AgentLoop logs it first. Once the first of those rows is durable, the
remaining echoes follow the new Turn's process control, which renders empty
while the Turn runs. The 16px clearance after that control applied to them
and moved each echo by 10px before its durable row replaced it. The clearance
now applies after a visible control to every row, and after any control only
to rows that are not local echoes, so the echoes keep the input gap.

Also state that a switch lifted within a move's duration plays the rest of
it, that the steering entrance approximates the status's previous position,
and that the runtime-context notice placement follows from the README rule.
2026-10-18 15:45:57 +08:00
Tianyi Cui b98127cf02 Merge pull request #6457 from deepseek-harness/worktree/session-build-generation-prototype
build: generate metadata before package compilation
2026-10-18 15:45:48 +08:00
ZiyaandZiyaZhang 114da79e42 fix(agent-instructions): budget the user-global and project chains separately (#6400)
* fix(agent-instructions): budget the user-global and project chains separately

The user-global and project instruction files shared one maxBytes and the user-global files were the broadest, so a large project chain omitted them first. Each chain now renders under maxBytes on its own and the results share one system-reminder frame: unchanged output when everything fits or one chain is empty, at most twice maxBytes otherwise. The project chain keeps its omit-broadest-first, truncate-last rule; reconciliation batches follow the same split; the maxBytes key and the baseline identity are unchanged.

Tests cover the fast paths, both chains fitting only separately, each chain over its own budget, the 2 × maxBytes boundary at exact and tiny limits, multibyte content, reconciliation batches, and a resumed single-budget baseline receiving the user-global file as one transition. The agent-instructions-budget snapshot is refreshed and agent-instructions-budget-chains is added. README, Config.maxBytes JSDoc, the configuration catalog, a new Agent Note, and an upgrade guide state the per-chain bound.

* fix(agent-instructions): keep loaded order in merged frames and note the unshared chain budget

A merged frame now lists sections, omissions and truncations in the loaded order, so a reconciliation batch renders its items in one order whether or not it fits a single budget. The guard comment states only the invariant and points to the Agent Note; the intro selection states its rule. The README names the unshared budget between chains.

---------

Co-authored-by: ZiyaZhang <[email protected]>
2026-10-18 00:45:39 -07:00
Tianyi Cui f4260fc2f7 test(desktop): cover unsigned packaging failure and cleanup 2026-10-18 15:31:23 +08:00
Turtle d2552daf45 Merge pull request #6152 from deepseek-harness/turtle/code-link-highlight-239ff7746b50
feat(client): fade the navigated line's highlight in document previews
2026-10-18 15:19:32 +08:00
imccyu 11bc10f621 test(web): refresh steering snapshots for the running status position
The running status now renders after the transcript rows and before the
steering echoes that are still waiting, so the mid-steer accessibility
snapshots list the status ahead of the interjection echo. Refreshed with
DSH_SNAPSHOT=refresh on the steering scenarios; no other golden changed.
2026-10-18 15:14:16 +08:00
imccyu 39323bbc5f fix(ui-chat): keep the steering entrance while the tail is pinned
A pinned tail zeroed both running status moves. Following scrolls absorb
output growth, so the status keeps its on-screen position for the first
output, but admitted steering still moves the status past its pending
echo below it: in a long transcript at the floor the status jumped the
height of that echo with no motion. The input-fold switch keeps zeroing
both moves; the pinned switch now repeats only the first-output slide's
structure, so the entrance after an admitted steering row keeps its
duration while the page scrolls by the added slot. Update the README and
the CSS check that the pin applies to the slide structure alone.
2026-10-18 15:14:16 +08:00
imccyu 3cea71bd94 fix(ui-chat): keep the running status moves from replaying when a switch lifts
The steering entrance and the first-output slide were selected through
`:not([data-input-fold]):not([data-chat-tail-pinned])`. Removing either
attribute added the animation name back while the structure still
matched, so a finished move replayed: scrolling away from a long running
transcript and back short of the floor, or the end of any fold batch,
faded the status in again and slid it one row. The structure alone now
names each move, wrapped in `:where()`, and the two switches only set
`animation-duration: 0s`; turning a switch off changes the duration of a
move that already finished, and turning it on completes a running one.

The tail pin now uses the test fold batches use for a tail landing:
follow intent with no unsampled reader movement, over content that
overflows the scrollport excluding fold room. A reader scroll keeps follow
intent until its 500ms sample settles while growth is not followed, so
the old pin claimed a held status there and skipped a needed move.
ChatReading recomputes the pin where follow intent and sampling settle,
the viewport recomputes it after scroll, scrollend, and each resize's own
follow scroll, reusing the scroll metrics or the resize's clean-layout
measurement, and the attribute is written only when the pin changes.

Record the decision to precompute the steering batch start in Definition
state with process-evidence and reader.previous as an Agent Note.
2026-10-18 15:14:16 +08:00
imccyu 8a3b0be658 fix(ui-chat): hold the running status still while the tail is pinned
The first-output slide set its 6px default through a selector that
outranked both variants, so a response (12px) and a new Turn's first
output after the empty process control (16px) travelled 6px and started
with a 6px or 10px jump. The default now sits on the single-class
`.running` rule, which every variant outranks.

When the transcript overflows the viewport and follows its bottom, the
follow scroll of the same resize absorbs new rows and the status keeps its
on-screen position; replaying a move there lifted the status 28-44px and
slid it back. The viewport now marks the column `data-chat-tail-pinned`
while the reader follows overflowing content, updated after each resize's
follow scroll and on scroll, and neither the steering entrance nor the
first-output slide plays under that mark. Update the README and add
viewport and CSS checks.
2026-10-18 15:14:16 +08:00
imccyu ed9a92d1a8 fix(ui-chat): slide the running status below a Step's first output
While a Step has no output yet, the running status sits directly below its
input. When the first process row, group header, or response appears, the
status now slides from that position into its slot below the row instead of
jumping. It travels the row's gap plus one font-scaled 24px row: 6px for a
process row or group header, 12px for a response, and 16px after the running
Turn's process control, which renders empty until the Turn closes. The move
reuses the steering entrance technique with the standard easing over 110ms,
runs once per input-to-output change, and is skipped under reduced motion and
during an input fold.

Group header titles take the process-row line height, so the travel matches
the inserted row in every display mode and font size. Update the README and
the CSS checks that keep the travel equal to the rows that cause it.
2026-10-18 15:14:16 +08:00
imccyu b4fcdd6e94 fix(ui-chat): keep queued steering echoes ahead of the opening submission
After a stop, steering still waiting in the next-step queue is claimed
together with the next idle submission, and the AgentLoop logs it first.
Echoes before the first transcript echo now render with that submission as
the next Turn's opening inputs above the running status, and move ahead of
the new Turn's empty process control together, so the echo order matches the
durable rows. Update the flow-tail spec and README for the echo order.
2026-10-18 15:14:15 +08:00
imccyu c538d65adb feat(ui-chat): anchor the running status to the running turn tail
Render the running status after the transcript rows and opening echoes and
before steering or Inbox echoes, so its position shows whether a waiting
steering message has been consumed. The status uses the completed Turn's
actions-row slot, its divider no longer takes layout space, and it hides once
the Turn tail is rendered.

The first steering row of each next-step claim keeps that 44px slot above it.
A state-only process-evidence Definition records each Step with durable
visible output, and the message Definition marks a claim's first human
message as a batch start when the latest earlier evidence belongs to the same
Turn. Rows claimed together stay 6px apart.
2026-10-18 15:14:15 +08:00
Tianyi Cui 091d6ed6be feat(desktop): use local branding for unsigned packages 2026-10-18 15:06:16 +08:00
Turtle 13896d6ea5 Merge remote-tracking branch 'origin/master' into turtle/auto-review-cache-ffc00f27f914
# Conflicts:
#	.agents/notes/implemented/feature/2026-08-28-auto-review.i18n.yaml
#	.agents/notes/implemented/feature/2026-08-28-auto-review.md
#	.agents/notes/implemented/feature/2026-08-28-auto-review.zh.md
#	packages/experimental/auto-review/README.i18n.yaml
#	packages/experimental/auto-review/src/index.ts
#	packages/experimental/auto-review/tests/auto-review.spec.ts
2026-10-18 15:05:52 +08:00
Turtle cd77df1e7b Merge remote-tracking branch 'origin/master' into turtle/model-picker-defaults-c7775e2d3040 2026-10-18 15:00:16 +08:00
Turtle 23fb3eadd1 test(desktop): cover a handle left open after Host teardown
Drive the built Host through the update handoff with a handle opened on
shutdown, and state why the exit skips startup failures.
2026-10-18 14:58:17 +08:00
Turtle ef0a2b2e3d Merge remote-tracking branch 'origin/master' into turtle/image-cache-stale-0a243f674e12
# Conflicts:
#	packages/client/ui-primitives/README.i18n.yaml
#	packages/client/ui-primitives/README.md
#	packages/client/ui-primitives/README.zh.md
2026-10-18 14:49:15 +08:00
Turtle 6252a29a20 fix(desktop-host): exit after acknowledged teardown
A handle left open after the application tree disposed kept the Host alive
until the shell's SIGTERM at the 10 s graceful deadline, so the update
handoff refused installation even though teardown had completed.

Refs #5492
2026-10-18 14:42:36 +08:00
Tianyi Cui 4d33f27225 Prepare metadata in public library builds 2026-10-18 14:29:23 +08:00
Tianyi Cui 1badf91762 Reuse source alias setup in metadata regression tests 2026-10-18 14:29:23 +08:00
Tianyi Cui 9b310b2b4b Generate source metadata before package compilation 2026-10-18 14:29:23 +08:00
Tianyi Cui dfa1482c75 Merge pull request #6456 from deepseek-harness/worktree/review-local-simplification
docs(review): test necessity before choosing feature ownership
2026-10-18 14:14:42 +08:00
Tianyi Cui 29a0861375 Merge pull request #6450 from deepseek-harness/worktree/headless-auto-review
feat(headless): support unattended Auto review
2026-10-18 11:59:31 +08:00
Tianyi Cui f38b890cc5 fix(auto-review): keep inherited creation prompts as facts 2026-10-18 11:44:14 +08:00
Tianyi Cui bdba350b47 test(auto-review): align Web checks with unified user authority 2026-10-18 11:44:14 +08:00
Tianyi Cui dee2989782 docs(headless): record remaining simplification proposals 2026-10-18 11:44:14 +08:00
Tianyi Cui 55fddb3424 refactor(auto-review): recognize existing user task sources 2026-10-18 11:44:14 +08:00
Tianyi Cui a8963e75a0 docs(headless): record preserved Auto policy on resume 2026-10-18 11:44:14 +08:00
Tianyi Cui e0dffac6fa fix(headless): refine Auto policy reporting and fixtures 2026-10-18 11:44:14 +08:00
Tianyi Cui 04e7e1b45d docs(headless): refresh approval dependency graph 2026-10-18 11:44:14 +08:00
Tianyi Cui 7181effc69 feat(headless): support unattended experimental Auto review 2026-10-18 11:44:14 +08:00
Tianyi Cui d6e6550caf test(auto-review): type the restore timestamp assertion 2026-10-18 11:44:13 +08:00
Tianyi Cui 928c3a872b test(auto-review): pin restore and live reload behavior 2026-10-18 11:44:13 +08:00
Tianyi Cui 4923f3cb4c fix(auto-review): preserve Auto during application shutdown 2026-10-18 11:44:13 +08:00
Tianyi Cui 6403904c3e test(headless): configure policy for the OAR Loader fixture 2026-10-18 11:43:32 +08:00
Tianyi Cui 4db9ed50f8 test(headless): assert parsed policy without config mirrors 2026-10-18 11:13:48 +08:00
Tianyi Cui 5c06a1b917 refactor(headless): read CLI permission selection directly 2026-10-18 11:13:48 +08:00
Tianyi Cui 3ca9c5e623 fix(headless): preserve explicit policy flags in custom profiles 2026-10-18 11:13:48 +08:00
Tianyi Cui 9db5b1e36a test(headless): use typed startup service without a cast 2026-10-18 11:13:48 +08:00
Tianyi Cui cec41e73e0 fix(headless): validate invocation policy across custom profiles 2026-10-18 11:13:48 +08:00
Tianyi Cui 29630d9fa6 feat(headless): support supervised worktree tasks 2026-10-18 11:13:48 +08:00
Tianyi Cui 82e5580545 test(sandbox): cover unsupported policy errors on every host 2026-10-18 11:10:00 +08:00
Tianyi Cui 86baf0c240 test(sandbox): accept bind-mount prevention of root replacement 2026-10-18 11:10:00 +08:00
Tianyi Cui dde160f092 test(sandbox): retain the session workspace regression boundary 2026-10-18 11:10:00 +08:00
Tianyi Cui 64ece4cac1 fix(sandbox): reject redirected and unsupported write grants 2026-10-18 11:10:00 +08:00
Tianyi Cui 07943e1b90 test(ssh): cover session identity with explicit write grants 2026-10-18 11:10:00 +08:00
Tianyi Cui 6b71dcad85 fix(sandbox): support explicit additional writable roots 2026-10-18 11:10:00 +08:00
Tianyi Cui e3518b0ac0 docs(review): clarify extension point and declaration ownership 2026-10-18 10:29:18 +08:00
Tianyi Cui a205710563 docs(review): test necessity before choosing feature ownership 2026-10-18 10:12:02 +08:00
Tianyi Cui e5cac772de Merge pull request #6325 from deepseek-harness/worktree/oar-experimental
feat: add standalone OAR subagent provider and tool
2026-10-18 00:48:15 +08:00
Tianyi Cui 427e3dcd69 feat: add standalone OAR subagent provider and tool 2026-10-18 00:28:33 +08:00
Tianyi Cui c12e805dc7 Merge pinned Pi upgrade base into worker context fix 2026-10-17 23:34:53 +08:00
Tianyi Cui f5fa6e8089 Merge pinned Pi upgrade master into Node 24 base 2026-10-17 23:29:30 +08:00
Tianyi Cui 6b14bac183 Merge pull request #6310 from deepseek-harness/worktree/upgrade-pi-ai-1.2.0
chore(deps): upgrade pi-ai to 1.3.0
2026-10-17 23:13:21 +08:00
Tianyi Cui 443b24cd1b Merge validated Node 24 base into worker context fix 2026-10-17 23:10:55 +08:00
Tianyi Cui 249fc3f9f3 Merge browser CI prerequisites into Node 24 base 2026-10-17 23:05:32 +08:00
Tianyi Cui 6643403536 docs: align Node 24 guide with current release 2026-10-17 23:01:48 +08:00
Tianyi Cui 79a5c00aec Merge master into worktree/oar-node24 2026-10-17 22:59:20 +08:00
Tianyi Cui b9e95fd1e5 docs(upgrade): target rc.2 for the unmerged pi-ai upgrade 2026-10-17 22:44:27 +08:00
Tianyi Cui d6398b3c86 chore(llm): upgrade pi-ai to 1.3.0 2026-10-17 22:44:26 +08:00
Tianyi Cui f9cb7ea9f5 fix(desktop): align runtime patch policy with pi-ai 1.2.0 2026-10-17 22:44:26 +08:00
Tianyi Cui 8fd91f311e chore(deps): upgrade pi-ai to 1.2.0 2026-10-17 22:44:26 +08:00
Tianyi Cui d849cfdd15 test(web): await terminal close before checking process cleanup 2026-10-17 22:43:54 +08:00
Tianyi Cui 9e59ef469f Merge pull request #6432 from deepseek-harness/worktree/fix-acp-account-stdout
fix(acp): keep account catalog diagnostics off stdout
2026-10-17 22:23:28 +08:00
Tianyi Cui ef055d25d0 test(preview): await renderer version effects 2026-10-17 22:09:37 +08:00
Tianyi Cui 64be7f033b docs(web): clarify sidebar hover setup 2026-10-17 22:09:36 +08:00
Tianyi Cui 09b83c3c53 test(web): clear hover before keyboard panel snapshots 2026-10-17 22:09:36 +08:00
Tianyi Cui 6c2c466214 test(web): scope settlement timing to the released question 2026-10-17 22:09:35 +08:00
Tianyi Cui 2f58a68c61 test(web): hold question streaming until the steering snapshot 2026-10-17 22:09:35 +08:00
Tianyi Cui 4edc3e5fcb test: stabilize browser readiness and workflow observations 2026-10-17 22:08:57 +08:00
Tianyi Cui c4bcce7329 test(acp): use shared cleanup for the stdout regression 2026-10-17 21:54:20 +08:00
Tianyi Cui 54af8907ca fix(acp): keep account catalog diagnostics off stdout 2026-10-17 21:54:20 +08:00
Tianyi Cui b1ce8a6e09 Merge pull request #6434 from deepseek-harness/worktree/fix-office-docx-validation
fix(office): stop routine DOCX package validation
2026-10-17 21:50:37 +08:00
Dudu-0223 09b389cf55 Merge pull request #6345 from deepseek-harness/fix/compact-before-model-switch
fix(compaction): condense history before model switches
2026-10-17 20:18:18 +08:00
Dudu-0223 9f1de3b514 Merge pull request #5275 from deepseek-harness/feat/subagent-fixed-model-tools
refactor(subagent): decouple model selection, discovery, and authorization
2026-10-17 20:17:37 +08:00
Tianyi Cui 6b68baff70 fix(office): stop routine DOCX package validation
Extract the focused Office guidance from PR 5374 and regenerate its recorded skill outputs against the pinned base. Keep the existing real-API content, formatting, and no-package-inspection assertions.
2026-10-17 20:03:50 +08:00
creatixchu 8b3d4f0b52 docs(cua): refresh composer registration catalog 2026-10-17 17:16:37 +08:00
creatixchu b4d5ccc416 refactor(cua): own approval presentation and preserve shared UI defaults 2026-10-17 17:12:49 +08:00
Yichen Jiang 2eb0df5628 feat(web): show a message only after the Host accepts it
The composer keeps a sent message, its references, and its attachments
read-only in a sending phase until session/prompt resolves, so an
admission listener can hold or refuse a prompt before it appears as sent.
The Session publishes the local echo and leaves the blank layout only
after acceptance; a refusal or failure keeps the draft, and
session/prompt-rejected with presented: true adds no toast.

This carries the client half of deepseek-harness/deepseek-harness#6178.
2026-10-17 17:09:44 +08:00
Yichen Jiang f34eaf7601 feat(agent): admit Web prompts through the agent controller
Add @deepseek-ai/dsh-agent-controller, composed in the base bundle, as the
entry through which a sender delivers a message from outside an Agent into
its inbox. deliver() runs the sender's content preparation in the Agent's
delivery order, asks the agent-controller/admission waterfall for a
decision, rechecks cancellation and registration, and appends the message
with followup() or steer(). dsh-agent and agent-loop are unchanged.

The Web session/prompt Remote delivers through it: the image model check,
upload receipt resolution, and attachment storage run as prepare, the
receipt binding runs as beforeInsert, and refusals map to
session/prompt-rejected. The Web client keeps its existing echo.

This replaces the Host half of deepseek-harness/deepseek-harness#6178,
whose admission stage lived in dsh-agent.
2026-10-17 17:09:29 +08:00
creatixchu 72047d6ecd docs(cua): document permissions, support limits and APIs 2026-10-17 16:35:13 +08:00
creatixchu c32a59cf89 build(cua): integrate source builds and product regressions 2026-10-17 16:35:13 +08:00
creatixchu 36ec182e33 feat(cua-ui): guide setup and manage application access 2026-10-17 16:35:13 +08:00
creatixchu 6ae9ce5e2f feat(cua): add the experimental Computer Use bundle 2026-10-17 16:35:13 +08:00
creatixchu aa786c98d4 feat(cua-macos): expose system permissions and responsible app 2026-10-17 16:35:13 +08:00
creatixchu 9802933c04 feat(cua): manage application grants and PTC session scopes 2026-10-17 16:35:13 +08:00
creatixchu 2d46f34701 feat(approval): share fixed-action approval cards 2026-10-17 16:35:13 +08:00
creatixchu ca081d5658 fix(session): restore presence when sessions return 2026-10-17 16:35:12 +08:00
creatixchu eb8efe84e4 feat(plugins): support platform-aware bundle activation 2026-10-17 16:35:12 +08:00
creatixchu a9f7ea7d52 fix(config): cancel settings writes waiting for HMR 2026-10-17 16:35:12 +08:00
creatixchu d53fff974a test(cua): refresh bounded hold guidance snapshots 2026-10-17 16:25:40 +08:00
creatixchu bffc4f193d fix(cua): bound click holds and test balanced cancellation 2026-10-17 16:24:17 +08:00
creatixchu 5a4137eff9 test(cua): observe windows on each cancellation connection 2026-10-17 16:14:50 +08:00
creatixchu fd829cb6c6 test(cua): verify interrupted holds through native sockets 2026-10-17 16:13:46 +08:00
creatixchu dc863c5882 test(cua): record hold requests and isolate pointer checks 2026-10-17 16:11:52 +08:00
creatixchu ade2d2b271 test(cua): compare serialized logged click arguments 2026-10-17 16:09:33 +08:00
creatixchu c45d558f7d feat(cua): support optional mouse hold duration 2026-10-17 16:08:08 +08:00
Dudu-0223 66d9f70255 Merge master and resolve subagent control package metadata 2026-10-17 15:55:44 +08:00
imccyu 687f282e86 Merge pull request #6408 from deepseek-harness/worktree/release-dsh-0.2.1-rc.2
release: next version [email protected]
2026-10-17 15:22:43 +08:00
imccyu d1b9c04067 release(dsh): 0.2.1-rc.2 2026-10-17 15:10:31 +08:00
Yudong HanandLegGasai fda98dadd2 feat(web): add DeepSeek Search and Fetch APIs behind a settings switch (#6103)
* fix(web-search-deepseek): disable thinking for auxiliary search requests

The provider discards the generated text of the auxiliary Messages turn,
so reasoning tokens have no consumer. DeepSeek enables thinking by default,
which adds latency and output tokens to every search.

Fixes #6099.

* feat(web): add opt-in traditional Search and Fetch APIs

* test(web): verify API settings persistence and refresh Session evidence

* fix(web): require an explicit Search API base with its route prefix

* docs(web): document direct Search and Fetch API decisions

* fix(web): keep Messages search default behind the new Web Search API switch

- Rename useTraditionalApi/traditionalBaseURL to useSearchApi/searchApiBaseURL and
  label the switch "Use the new Web Search API"; Messages search stays the default.
- Default the Search / Fetch API base to https://api.deepseek.com/v1/tools.
- Authenticate the Search API with an API key only: DeepSeek Account tokens
  returned an empty HTTP 404 on /v1/tools/* when probed on 2026-10-16.
- Remove the count setting, which tool-web's maxResults always overrides, and
  the undocumented DeepSeek-API-Version header.
- Name the active API's base URL field and the current settings path in
  endpoint guidance; re-record the endpoint guidance Session.
- Keep WebFetchResult.statusCode required: DeepSeek Fetch reports HTTP 200 and
  drops the page title, so the fetch-metadata widening and its upgrade guide
  are removed.

* fix(web): accept account sign-in on the live Search API

The public Search and Fetch routes went live on 2026-10-16 and accept the
DeepSeek Account token, so account-route Sessions send it to the Search API
as they do for Messages search, and the settings copy no longer says account
sign-in does not apply.

Record the live behavior in the README and Agent Note: summaries of
1,000-2,300 characters, an HTTP 500 for an unresolvable Fetch target, and
migration notes for gateways without /v1/tools routes. Test doubles now use
the live response fields and RFC 3339 offsets.

* feat(web): configure Search API result count and send the triggering question

- Add web-search-deepseek.count (1-50, default 10, the service default) and
  show it as "Results per search" in Search API mode, where "Max searches per
  request" applies only to Messages; a smaller request maxResults still wins.
- Send the initiating Session's latest human prompt as ori_question, with
  whitespace collapsed and cut to the API's 128-character limit; blank or
  absent prompts omit it, and sendOriginalQuestion: false disables it. The
  settings hint states that the question is sent.
- Match the web search double on the request's query field, since
  ori_question now carries every query in the prompt.

* fix(client): unify web search endpoint override controls

* feat(web): configure registered search and fetch providers through shared settings

* feat(web): include Exa and Perplexity in provider settings

* test(web): cover provider settings branches and refresh plugin snapshots

* revert(web): drop the provider dropdown, schema-driven form, and Exa/Perplexity settings

Revert aed8c5cb609, d88c47dcd80, and 036a5a337d3. The Web search settings
page returns to the DeepSeek toggle; the DeepSeek Search and Fetch API split
is reintroduced as one plugin in the following commit.

* feat(web): move the DeepSeek Search and Fetch APIs into web-deepseek-api

The new web-deepseek-api plugin registers deepseek-search-api and
deepseek-fetch-api over one credential and API base, and exports the
authentication and redirect-rejecting transport that web-search-deepseek now
reuses for Messages search only. The web service reads searchProvider and
fetchProvider for each operation, so the Web search settings switch selects
both APIs together on save and restores Messages search with local HTTP fetch
when turned off. The switch hint links to the DeepSeek search and fetch guide.

* refactor(client): keep the web search endpoint fields on the shared override controls

Drop the always-visible reset and the inherited-address placeholder from the
Web search page, restoring ui-primitives to its base. Each endpoint field shows
the selected section's effective value and offers Reset to default only for an
override, like every other settings field.

* refactor(web): keep Messages search independent of web-deepseek-api

Restore dsh-web-search-deepseek to its own credential and transport code,
keeping only the disabled-thinking request and the current settings guidance,
so the Messages plugin no longer depends on the Search and Fetch API package.
dsh-web-deepseek-api keeps its transport internal; the regions that repeat the
Messages helpers carry jscpd:ignore markers, with the rationale recorded in the
Search and Fetch API Agent Note.

* refactor(web): keep the web-deepseek-api request type module-private

---------

Co-authored-by: LegGasai <[email protected]>
2026-10-17 15:08:08 +08:00
Dudu-0223 f98a206b01 docs(subagent): align upgrade guide with released composition 2026-10-17 14:51:25 +08:00
Dudu-0223 1448360b66 refactor(subagent): make model discovery explicitly opt-in 2026-10-17 14:02:01 +08:00
Yichen Jiang ca15fe4f17 Merge pull request #6417 from deepseek-harness/worktree/deepseek-flash-1017-badge
feat(web): replace the DeepSeek Flash suffix with a temporary badge
2026-10-17 13:54:15 +08:00
Dudu-0223 50d57a5969 test(subagent): refresh CLI and Python control tool histories 2026-10-17 13:41:01 +08:00
Yichen Jiang 3c937cd0ba feat(web): display a temporary 1017 badge for DeepSeek Flash 2026-10-17 13:33:35 +08:00
Dudu-0223 abf5d650c8 docs(subagent): refresh shared control dependency graph 2026-10-17 13:32:53 +08:00
Dudu-0223 4df09d37d5 refactor(subagent): register shared control tools together 2026-10-17 13:28:17 +08:00
Yichen Jiang 32efe36a4e revert: restore the bundled DeepSeek Flash display name 2026-10-17 13:23:06 +08:00
Dudu-0223 590cfe168c Merge master and refresh tool catalog translation pairing 2026-10-17 12:03:03 +08:00
Yichen Jiang c6acc07232 Merge pull request #6406 from deepseek-harness/worktree/deepseek-flash-1017
fix(llm): append 1017 to the bundled DeepSeek Flash display name
2026-10-17 09:23:32 +08:00
Yichen Jiang 9404cd1790 Merge branch 'master' into worktree/deepseek-flash-1017 2026-10-17 09:13:01 +08:00
Yichen Jiang 7d844abc87 fix(llm): append 1017 to the bundled DeepSeek Flash display name 2026-10-17 09:06:36 +08:00
imccyu 1600d8a1e1 Merge pull request #6387 from deepseek-harness/worktree/release-dsh-0.2.1-rc.1
release: next version [email protected]
2026-10-16 21:46:57 +08:00
imccyu c6e89e6755 release(dsh): 0.2.1-rc.1 2026-10-16 21:39:18 +08:00
Shigma e821bc31d6 refactor(cordis): reuse TimerService in Client runner 2026-10-16 21:30:57 +08:00
imccyu ca912356fb Merge pull request #6388 from deepseek-harness/worktree/release-vendor-4.0.5
release(vendor): prepare cordis 4.0.5 stable packages
2026-10-16 21:19:31 +08:00
imccyu d9c04d6cc6 release(vendor): cordis 4.0.5, cosmokit 1.8.6, group 1.0.5, hmr 1.0.20, include 1.0.10, loader 1.0.6, logger-console 1.0.5, schemastery 3.18.5, timer 1.1.7 2026-10-16 20:53:44 +08:00
imccyu 1517a4dbca Merge pull request #6327 from deepseek-harness/worktree/release-dsh-0.2.1-alpha.5
release: next version [email protected]
2026-10-16 19:02:34 +08:00
imccyu a5e553a5ee Merge branch 'master' into worktree/release-dsh-0.2.1-alpha.5 2026-10-16 18:44:50 +08:00
lsdsjy 5ee0f50811 Merge pull request #6384 from deepseek-harness/feat/account-notice-stack
feat(account): 支持通用通知字段和多通知层叠展示
2026-10-16 18:40:03 +08:00
lsdsjy 00a1155f5a test(account): 修复通知动画测试的类型感知 lint 2026-10-16 18:26:26 +08:00
lsdsjy da6c16747e fix(account): 平滑过渡不同高度的通知卡片 2026-10-16 18:21:04 +08:00
Yichen Jiang 29a497ee5c Merge pull request #6357 from deepseek-harness/worktree/model-label-slot
feat(web): add provider-aware model label slot
2026-10-16 18:12:40 +08:00
lsdsjy 474d7b7989 docs(account): 同步通知元数据类型依赖图 2026-10-16 18:04:16 +08:00
winewill 911d9a5746 fix(desktop): preserve shell locale during loading page startup 2026-10-16 18:02:22 +08:00
lsdsjy 1f312b6282 fix(account): 保留未校验的通知元数据 2026-10-16 17:58:20 +08:00
lsdsjy 6ac5c103db fix(account): 修复通知空标题和过期队首处理 2026-10-16 17:52:44 +08:00
lsdsjy 2dcd031a8e feat(account): 支持通用通知字段和多通知层叠展示 2026-10-16 17:14:42 +08:00
_Kerman 1ec77846bb test(agent-loop): cover configured resume effort and missing route
A config-driven resume applies an explicit configured effort, and an
agent entry without a complete route keeps the recorded selection.
2026-10-16 17:07:37 +08:00
winewill c1be1579aa fix(desktop): retain confirmed locale for startup recovery 2026-10-16 16:57:43 +08:00
_Kerman 05adb37660 test(sdk): refresh the session-resume prompt after master's prompt restore
This branch added the scenario's system-prompt expectation, so master's
restore of the read and subagent guidance did not reach it.
2026-10-16 16:55:51 +08:00
fz f4fb5fbe1c test(python): align SDK runtime snapshots with desktop composition
Record the six added default SDK tools in the dynamic-tool history. Keep the existing custom snapshot, recovery, and restart compositions independent of the desktop defaults using the same disabled rows as the TypeScript legacy context patch. Validate the original failure and the following scenarios through the supported built CLI path.
2026-10-16 16:50:10 +08:00
_Kerman b34679d9de Merge remote-tracking branch 'origin/master' into xtr/session-model-selection 2026-10-16 16:40:11 +08:00
Yichen Jiang f87c08950a fix(web): memoize model label providers 2026-10-16 16:34:19 +08:00
_Kerman 3551b06833 test(cli): select the next-step effort before step preparation
agent/request now rejects reasoning-effort changes, so the keyless mock
records off effort with ctx.agentModel.select() after the first step's
tool call. The headless session expectation gains that model/selection
event, and the sdk-minimal built config dump lists dsh-agent-model.
2026-10-16 16:34:11 +08:00
_Kerman 393624a0b2 test(web): restore master's archive activity order
AgentRegistry no longer injects sessionProjections, so it loads at its
master position again and the archive dialog lists the turn before the
background job, as it does on master.
2026-10-16 16:34:08 +08:00
imccyu 0a49d91bb2 release: setup next release version for [email protected] 2026-10-16 16:17:09 +08:00
Yichen Jiang 1b385d6e2a fix(web): keep model menu positioning local 2026-10-16 16:01:04 +08:00
_Kerman 63bc92c3fd docs(module-graph): add the dsh-agent-model package 2026-10-16 15:56:59 +08:00
_Kerman b41a2fff61 docs(agent-model): file the upgrade guide under v0.2.1-alpha.4
Master released 0.2.1-alpha.4, so this unreleased break moves to the
new version directory and the new package takes the current version.
2026-10-16 15:50:44 +08:00
imccyu e28419f15b Merge pull request #6353 from deepseek-harness/fix/sidebar-pageup-shift
fix(web): clip the collapsed right sidebar so PageUp cannot shift the frame
2026-10-16 15:49:54 +08:00
winewill c6d01dcac0 feat(desktop): enrich startup crash diagnostics and reveal reports
Refs #4540. Capture startup state before disposal and make crash reports retrievable from native recovery.
2026-10-16 15:49:24 +08:00
fz 3d20a6269f test(sdk): cover desktop entrypoint and stabilize authoring snapshots 2026-10-16 15:48:08 +08:00
fz 89619e673a fix(sdk): share desktop tools and model context 2026-10-16 15:48:08 +08:00
fz d614ae2769 Merge pull request #6348 from deepseek-harness/fix/ptc-prompt-alignment
fix(prompts): restore compatible PTC guidance and SDK persona
2026-10-16 15:48:04 +08:00
_Kerman b670701e5a Merge remote-tracking branch 'origin/master' into xtr/session-model-selection 2026-10-16 15:42:50 +08:00
fz 839bd5131d refactor(tools): compose PTC instructions without prose replacement
Pass execution-specific phrases directly to the run_code flavor factory and render SDK instructions from explicit program-format branches. Keep all emitted text byte-identical while removing coupling to exact shared prose. Validate the owning tests with full scoped coverage, focused session/SDK replays, and a complete build.
2026-10-16 15:30:12 +08:00
_Kerman 7b112942ab refactor(agent-model): move Session model selection into its own package
Add @deepseek-ai/dsh-agent-model. It provides ctx.agentModel
(resolveForNextStep, select, applyConfiguredRoute) and owns the
modelSelection projection, which the Agent registry no longer registers.
dsh-agent-loop now injects agentModel; the base and SDK-minimal bundles
and the Python SDK runtime include it.

The provider keeps the projection registered until consumers that are
still unloading finish their cleanup. The model-switch notice becomes an
opt-in per-Agent plugin at @deepseek-ai/dsh-agent-model/notice, and
agent/request now also rejects reasoning-effort changes after step
preparation.

Mount the provider in every composition that loads dsh-agent-loop, and
update consumers, the switch-notice snapshot driver, docs, and the
upgrade guide.
2026-10-16 15:28:59 +08:00
fz c34a107428 test(sdk): apply cache E2E retry settings and expose provider failures
Set the required normal retry mode so the Loader accepts the full provider patch, and type-check the policy before JSON serialization. Assert the effective request settings and successful warm-up turns, report failed attempt causes immediately, and inherit suite retries for transient external API failures.
2026-10-16 15:16:35 +08:00
Yichen Jiang c78f3fd33d fix(web): keep model label menus inside the viewport 2026-10-16 15:16:21 +08:00
Dudu-0223 619559f9fd Merge branch 'master' into fix/compact-before-model-switch 2026-10-16 14:41:39 +08:00
Turtle 40b75d7ef8 fix(web): make fake-IP ranges an opt-in fetch config
Accepting 198.18.0.0/15 unconditionally opened that range to direct
connections on hosts without a fake-IP proxy. Operators now list their
proxy's ranges in fakeIpRanges (IPv4 or IPv6 CIDR, default empty);
invalid entries fail at load.
2026-10-16 14:40:06 +08:00
winewill 1bacc3db05 test(desktop): cover Windows pack preflight and relocate tuning docs 2026-10-16 14:31:39 +08:00
Dudu-0223 aa68564ff4 fix(compaction): address model-switch review details 2026-10-16 14:30:58 +08:00
Yichen Jiang 919aa5f400 feat(web): add provider-aware model label slot 2026-10-16 14:24:47 +08:00
Turtle a37a5ca395 fix(web): accept fake-IP DNS answers in web_fetch
TUN-mode proxies with fake-IP DNS (Clash/Mihomo, Surge, sing-box) answer
every hostname with an address in 198.18.0.0/15. web_fetch rejected those
answers as non-public, so every page failed. Treat a hostname answer in
that range like a proxied hop; an IP literal in the range stays blocked.
2026-10-16 14:24:01 +08:00
Shigma 1a33461f2b Merge pull request #6182 from deepseek-harness/docs/cordis-rc11-plan
docs(cordis): Cordis rc.11 vendor upgrade plan
2026-10-16 14:23:05 +08:00
imccyu 8953554b5e fix(web): clip the collapsed right sidebar so PageUp cannot shift the frame
While the right sidebar is collapsed, its docked hosts are translated past
the frame's right edge and widen the frame's scrollable overflow. Outside
macOS, PageUp in a text field moves the caret and reveals it against the
start edge of every scrollable ancestor, so the `overflow: hidden` frame
scrolled sideways and the whole UI shifted left.

Clip the panel's overflow. `overflow: clip` does not make the panel a scroll
container and does not clip the fixed-position floating panels, whose
containing block is the viewport; the DockLayout ancestor contract now says so.

Fixes #6341
2026-10-16 14:21:08 +08:00
winewill e7f030ee3e fix(desktop): preserve runner recovery after output drain timeout 2026-10-16 14:16:07 +08:00
winewill e826d5bf33 perf(desktop): batch Windows workspace tarball packing 2026-10-16 14:08:14 +08:00
Dudu-0223 f9761a140c Merge branch 'master' into fix/compact-before-model-switch 2026-10-16 13:30:07 +08:00
fz d6adc5d9b9 fix(prompts): restore compatible PTC guidance and SDK persona 2026-10-16 13:14:24 +08:00
winewill 012a25f8df fix(desktop): preflight local publication config before install 2026-10-16 12:42:29 +08:00
winewill cf0a621494 fix(desktop): contain runner launcher failure paths 2026-10-16 12:42:29 +08:00
winewill d0085c9642 docs(desktop): document runner and publication setup 2026-10-16 12:42:29 +08:00
winewill 9defbaf451 fix(desktop): launch Windows runner without a console 2026-10-16 12:42:29 +08:00
imccyu 085f3c6a58 Merge pull request #6339 from deepseek-harness/ci/master-baseline-canary-1016
test(web): keep page timers running under the pinned fixture day
2026-10-16 12:41:53 +08:00
imccyu 4cea5c36d2 test(web): sweep the Host clock distance in the pinned-clock regression
The page clock failure depended on the real-to-fixture distance modulo
2^32 ms, so a regression run on today's date alone passes against the old
fixture for half of every period. The test now shifts the Host clock across
eight phases of that period; the page.clock fixture fails every phase,
either with stopped page timers or with a Date that does not advance.
2026-10-16 12:23:10 +08:00
imccyu 81803f7ca5 test(web): keep page timers running under the pinned fixture day
pinHostClock replaces Date.now in the Vitest worker, where Playwright's
server also runs. page.clock.setFixedTime therefore recorded the fixture
day, and after a navigation the page clock replayed the gap to the real
day. The resulting negative timer delay was truncated to a 32-bit integer
and, from 2026-10-15T13:45:20Z, became a delay of weeks that stopped every
page timer, failing the steering and seeded-history suites.

pinBrowserClock now replaces only the page's Date so it reads the fixture
day while native timers and animation frames keep their real schedule; a
regression test navigates twice with both clocks pinned.
2026-10-16 12:07:47 +08:00
Dudu-0223 321d6362c5 fix(compaction): condense history before model switches 2026-10-16 12:02:31 +08:00
winewill 3f621c65c3 Merge pull request #6281 from deepseek-harness/feat/windows-runner-supervision
feat(desktop): supervise Windows runner and reject debug publications early
2026-10-16 10:47:39 +08:00
Yichen Jiang e412760f94 Merge pull request #6275 from deepseek-harness/fix/skill-menu-plugin-change
fix(ui-skill): 开关插件后刷新 `/` 菜单的 skill 列表
2026-10-16 10:38:32 +08:00
_Kerman 150a0d0842 docs(agent): clarify model-selection behavior 2026-10-16 10:16:18 +08:00
_Kerman d16b547843 test(web): follow archive activity listener order 2026-10-16 10:16:18 +08:00
_Kerman 0b7b214b3d refactor(user-questions): remove unreachable projection fallback 2026-10-16 10:16:18 +08:00
_Kerman b015d18e81 fix(agent-loop): identify rejected request route changes 2026-10-16 10:16:18 +08:00
_Kerman e7dd79a476 test(agent): migrate remaining model selection fixtures 2026-10-16 10:15:58 +08:00
Tianyi Cui 073033b4ea fix(webworker): preserve explicit empty async contexts 2026-10-16 02:28:18 +08:00
Tianyi Cui cfb65d3bb2 fix: validate the Node 24.9 runtime floor 2026-10-15 23:33:49 +08:00
Tianyi Cui c6a59a1ae9 build: require Node.js 24 or newer 2026-10-15 22:51:03 +08:00
Yichen Jiang 2d7f15a644 Merge pull request #6304 from deepseek-harness/worktree/terminal-session-list
fix(web): share terminal discovery per session
2026-10-15 22:42:21 +08:00
_Kerman ff731c72de refactor(session-projection): remove the host state map 2026-10-15 21:01:00 +08:00
TurtleandYichen Yan cc892fe5a0 Credit review by @oraluben
Co-authored-by: Yichen Yan <[email protected]>
2026-10-15 20:49:59 +08:00
Yichen Jiang 86a402200d Merge pull request #6301 from deepseek-harness/worktree/release-dsh-0.2.1-alpha.4
release: [email protected]
2026-10-15 20:22:44 +08:00
_Kerman 76acba476b fix(headless): retain explicit effort on same-route resume 2026-10-15 20:22:09 +08:00
_Kerman 8151a0271d fix(agent): persist explicit choices matching deployment defaults 2026-10-15 20:21:35 +08:00
gengruilin ba392ae0e7 docs(ui-skill): record the catalog gaps a plugin change leaves
State why every plugin manager operation drops every cached catalog, and
list two remaining gaps among the known limitations: on-disk SKILL.md
changes forward no event to an open session, and draft skill highlighting
waits for the next catalog fetch after an invalidation.
2026-10-15 20:17:59 +08:00
Turtle f150f2178d fix(ui-settings-models): keep model picker selection current and non-empty
Derive the picker's checked state from the list at render so a row drafted
during the fetch is not removed. Disable Update list when it would empty a
non-empty list, because an empty list serves the whole catalog. List a
repeated candidate id once and treat rows without an id as unconfigured.
2026-10-15 20:17:52 +08:00
Tianyi Cui 7f338ce3be Merge pull request #6272 from deepseek-harness/fix/codex-partial-answer-phase
fix(subagent-codex): accept the partial_answer agent message phase
2026-10-15 20:13:11 +08:00
Yichen Jiang 0ae278b935 fix(web): share terminal discovery per session 2026-10-15 20:12:52 +08:00
_Kerman e152f17bb2 refactor(token-meter): read pressure facts from retained state 2026-10-15 20:12:36 +08:00
_Kerman 5ee64b7005 refactor(agent): use a type-only SessionSeq import 2026-10-15 20:12:24 +08:00
_Kerman 491fa4a394 refactor(session-projection): clarify retained handle names 2026-10-15 20:12:09 +08:00
imccyu f2235d6c02 release(dsh): 0.2.1-alpha.4 2026-10-15 20:00:58 +08:00
_Kerman 7aaa6b3c5a refactor(agent): deprecate the agent/request hook 2026-10-15 20:00:19 +08:00
Turtle 72a607dfee fix(ui-settings-models): open model picker on the configured models
The fetched-model picker now starts with the configured models checked and
Update list adds newly checked candidates and removes unchecked configured
ones. Configured models the provider does not list stay. Select all and
Deselect all change only visible candidates so a hidden configured model is
not removed unseen.

Refs #6290
2026-10-15 19:52:23 +08:00
_Kerman d7a1aa839a docs: refresh module graph after projection dependency cleanup 2026-10-15 19:34:27 +08:00
_Kerman 0570efc157 test(session-title): mount agent registry in provider e2e 2026-10-15 19:34:21 +08:00
_Kerman e39b5f58c2 refactor(agent): persist model selection in session state 2026-10-15 19:05:09 +08:00
_Kerman d62915e72a chore(terminal-bash): remove unused projection peer dependency 2026-10-15 19:02:49 +08:00
_Kerman aedf8e9ee8 refactor(tmux-context): name the projection registration handle 2026-10-15 19:02:35 +08:00
_Kerman bad994ee01 docs(upgrade): consolidate projection migration in the current release guide 2026-10-15 18:29:20 +08:00
_Kerman 90014a7508 Merge remote-tracking branch 'origin/master' into xtr/session-projection-registration-handles
# Conflicts:
#	docs/tool-catalog.i18n.yaml
2026-10-15 18:26:22 +08:00
winewill b8f8abc5d6 fix(desktop): publish to administratively unversioned COS buckets 2026-10-15 18:19:27 +08:00
_Kerman c3efd4bff8 refactor(session-projection): keep state reads inside owning services 2026-10-15 18:18:58 +08:00
Turtle 0d26e2deef chore(cordis-client-runner): regenerate the client slot catalog 2026-10-15 18:06:02 +08:00
winewill af66022ace fix(desktop): default Windows packaging to signed 2026-10-15 17:52:13 +08:00
Turtle 585d20fc77 fix(ui-chat): pause the running clock while reconnecting
The Chat running indicator kept counting while the Client connection was
disconnected or reconnecting, which read as the agent still working. The
flow now reads the connection state and holds the clock until the
connection is back.
2026-10-15 17:47:02 +08:00
winewill 5e23825f15 fix(desktop): reject debug publications early and harden runner supervision 2026-10-15 17:40:38 +08:00
winewill cfa52b0bac feat(infra): supervise Windows packaging runner with Task Scheduler 2026-10-15 17:19:42 +08:00
Shigma f2f563c681 fix(cordis): route wrapper updates through the canonical Fiber 2026-10-15 16:59:58 +08:00
Shigma f10be1f63a docs(cordis): document Timer wrapper migration and local docs 2026-10-15 16:47:43 +08:00
ZiyaZhang e480bda8d2 fix(subagent-codex): call the payload the selected answer, not the final one
Address the ds-review-bot round on #6272: the README summary, completion
notice, token effect, and Known Limitations lines no longer call the
payload the final answer; the empty-result error reads "without an
answer"; the ordering spec also interleaves commentary; the partial_answer
branch cites its upstream source.
2026-10-15 01:44:26 -07:00
Yudong Han e44061c934 Merge pull request #6264 from deepseek-harness/fix/tool-exit-badge
fix(ui-tool): show command exits in neutral inline capsules
2026-10-15 16:41:15 +08:00
gengruilin 6910cd6e9c fix(ui-skill): refresh the slash menu after plugin manager changes
The browser skill source cached each Session's catalog until a preset
switch or connection reset, so an open Session kept listing the skills of
a plugin switched off on the Plugins page and missed those of a plugin
switched on. Clear every cached catalog on the forwarded
plugin-manager/changed event, which the Host emits after the operation's
reload.

Fixes #6268
2026-10-15 16:36:30 +08:00
yudshj 39d3849452 refactor(ui-tool): separate Host exit parsing fix 2026-10-15 16:23:48 +08:00
Yichen Jiang 0a11184ac3 Merge pull request #6261 from deepseek-harness/worktree/model-reasoning-level-config-2a9bbb
feat(models): edit reasoning levels of custom model API models
2026-10-15 16:21:44 +08:00
Yichen Jiang 2ee53e1cce test(python): admit the runtime context after the offloaded image in advanced snapshots
The release-shaped sdk-snapshot smoke records an image message that a
pre-step listener adds. The runtime-context snapshot now follows the
messages that agent/pre-step admitted, so it moves after that image and the
image/offload target seq changes from 13 to 12. The previous CI run stopped
at the authoring snapshot before reaching this one.

Re-recorded with --update-snapshots through apps/cli/lib/bin.js; the local
result.json diff matched the linux-x64 and win-x64 CI diffs exactly. The
scheduler-recovery, restart, minimal, minimal-in-history, and dynamic-tools
snapshots re-record unchanged.
2026-10-15 16:10:22 +08:00
yudshj 174c08563f test(snapshot): refresh nullable terminal exit descriptor 2026-10-15 16:08:44 +08:00
yudshj e8488f6b41 fix(shell): preserve missing exit codes in terminal presentation 2026-10-15 15:57:11 +08:00
Yichen Jiang 5d5ddeaff8 Merge remote-tracking branch 'origin/master' into worktree/dynamic-tool-activation-pr-64796e 2026-10-15 15:54:29 +08:00
Yichen Jiang a31a992113 Merge pull request #6267 from deepseek-harness/worktree/sad-jemison-fa352d
test(goal): disarm the seeded goal in the Loader e2e
2026-10-15 15:53:26 +08:00
Yichen Jiang 7d654c9d8a fix(models): drop the reasoning switched-off hint 2026-10-15 15:53:08 +08:00
yudshj 95e76236bf refactor(ui-tool): isolate exit badge and cover long descriptions 2026-10-15 15:46:03 +08:00
Yichen Jiang b542292bf9 fix(models): read reasoning levels from the pi-ai schema
The reasoning-level editor hardcoded pi-ai's seven levels, so a level a
pi-ai upgrade adds would be missing from the page and a stored key outside
the list would be dropped by the next edit. The levels now come from the
llm-pi-ai namespace schema, the key union of reasoningEfforts, read the
way protocolChoices reads the protocols, and travel explicitly from the
Models section and the provider editor through the model list to each row.
A stored key the schema does not list keeps its row and stays selectable.
The reasoning control now appears wherever that list is non-empty instead
of being inferred from a missing catalog provider.
2026-10-15 15:31:54 +08:00
yudshj 97a833471f fix(ui-tool): reserve inline space for exit capsules 2026-10-15 15:02:29 +08:00
Yichen Jiang 63e99ee2bf test(goal): disarm the seeded goal in the Loader e2e
The goal domain e2e runs over the shipped headless profile, whose base
bundle mounts goal-round-driver. GoalService.create() arms the goal the
fixture seeds, so the driver queued round 1 when the task turn went idle
and the test's headless driver disposed the context mid-round. The
assertions held only while the aborted round's turn/end missed the log
before disposal; an extra await in the agent loop's pre-step path (#6024)
let it land and the test failed deterministically.

Disarm the goal right after creating it. Activation is process-local, so
the persisted goal/change snapshot is unchanged and the driver starts no
round. Also assert a single turn/start, which records a started round
even when disposal aborts it before its turn/end is persisted.
2026-10-15 14:56:55 +08:00
Yichen Jiang a0a04158f8 fix(models): keep reasoning edits with their model and mirror pi-ai refusals
The reasoning-level editor kept the levels it switched off, its row order,
and typed text in component state, while the model list keys rows by
position, so deleting a row handed that state to the next model: turning
the survivor's switch on wrote the deleted model's levels into it. The
editor now renders only from the model; rows follow the stored order,
switching on always starts from the defaults, and only the focused field
keeps its typed text until the field is left.

The reasoning check moves out of the shared model validator into the two
cards whose rows edit it, so DeepSeek and catalog routes are untouched,
and it now mirrors every pi-ai refusal: a valueless field, a missing value
on a level besides off, and an empty value. A refused value marks its
field, and leaving that field repairs it with the level name.

The Providers guide now points custom model API users to the page before
the YAML, and both package READMEs describe the new behavior.
2026-10-15 14:53:56 +08:00
winewill fa13ef7a66 Merge pull request #6227 from deepseek-harness/fix/windows-installer-file-locks
fix(desktop): retry Windows installer directory locks and report failures
2026-10-15 14:37:39 +08:00
Yichen Jiang 399f9867cd test(snapshots): drop exit_plan_mode from master's cua scenarios
cua-native and cua-ptc were recorded on master while exit_plan_mode stayed
registered outside plan mode. With conditional admission the tool is
declared only in committed plan mode, so the refreshed tool schemas and
PTC prompt omit it; no other content changed.
2026-10-15 14:37:15 +08:00
winewill 58c8cef7dc Merge pull request #6262 from deepseek-harness/build/desktop-test-branches
feat(desktop): 支持 test 分支打包并中文化 Actions 表单
2026-10-15 14:22:24 +08:00
yudshj 00f897b048 fix(ui-tool): show command exits in neutral inline capsules 2026-10-15 14:20:22 +08:00
Yichen Jiang 4eed4557fb docs(upgrade-guide): move conditional tools to the v0.2.1-alpha.3 directory
master bumped the root version to 0.2.1-alpha.3 after alpha.3 shipped
without this change, so the alpha.2 directory is frozen and the guide
describes the upgrade from alpha.3.
2026-10-15 14:18:56 +08:00
Yichen Jiang 633bdd18ef Merge remote-tracking branch 'origin/master' into worktree/dynamic-tool-activation-pr-64796e
# Conflicts:
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	docs/persistence-catalog.i18n.yaml
#	packages/extensions/tool-cordis/src/api-catalog.ts
2026-10-15 14:18:40 +08:00
Yichen Jiang 91f2601cc6 test(python): admit the runtime context after the skill catalog in authoring snapshots
The release-shaped authoring smoke records the first request's user blocks.
The runtime-context snapshot now follows the messages that agent/pre-step
admitted, so the skill catalog precedes it. CI on the previous head reported
exactly this reorder for linux-x64 and win-x64; the content is unchanged.
2026-10-15 14:16:07 +08:00
Yichen Jiang d65835559c refactor(agent): commit plan selections in pre-step and drop agent/step-boundary
agent/pre-step already runs before step/start, and tool resolution and prompt
assembly now follow step/start, so plan-mode can keep committing pending
selections after downstream pre-step listeners accept the step. Tool
conditions and plan guidance still read the committed state, and a rejected
proposal keeps the selection pending as on master. No agent/model listener
depends on plan mode, so the separate serial event had no consumer left.

The loop now claims input, runs agent/model, and then agent/pre-step. The
agent/model order test replaces the boundary tests; plan-mode tests cover
the rejected-proposal case again. Docs, the Agent Note, the upgrade guide,
and generated catalogs follow.
2026-10-15 14:16:00 +08:00
ZiyaZhang 08d57291ad fix(subagent-codex): accept the partial_answer agent message phase
Codex app-server 0.162 adds `partial_answer` to the agentMessage phases
(stable answer text that later assistant output or tool calls may follow).
The wire treated every phase other than final_answer, commentary, and null
as fatal, so one such message failed the whole delegation. Record the
latest partial answer and select final, then partial, then unphased text;
unknown phases still fail the run. The pinned @openai/codex 0.153.4 payload
never emits the phase, so behavior with the shipped binary is unchanged.
2026-10-14 23:10:35 -07:00
winewill 29221675a2 fix(desktop): clarify source rejection and isolate Git fixtures 2026-10-15 14:03:50 +08:00
Yichen Jiang 02d25c695e docs(models): clarify the reasoning Off copy and level reset rule
The help tooltip now says that Off needs no value but keeps and sends a
value already written in the configuration, matching the disabled field
that shows it. Both READMEs add the rule that a custom value carries over
only between two thinking levels, so changing a row to or from Off resets
it. The Models settings e2e header comment is rewrapped.
2026-10-15 13:54:27 +08:00
Yichen Jiang 034f2cf8bd Merge pull request #5935 from deepseek-harness/worktree/command-delete-as-whole-21017f
feat(web): claim slash commands as atomic composer chips
2026-10-15 13:42:27 +08:00
winewill 61059baf61 feat(desktop): allow repository branches for test packaging 2026-10-15 13:39:28 +08:00
Yichen Jiang 6f8b55f3eb fix(web): restore a claimed command chip after reload
The persisted draft now records the leading command chip's claim token
(DraftSnapshot.command). Restoring the draft asks the trigger pipeline's
new resolveClaim, which polls the optional InputTriggerSource.matchClaim
hook: the command source awaits its catalog and answers only a
leadingInput claim, never opening a popup, running an action, or
executing the command. The input rebuilds the chip once the claim
resolves, only at the revision the restore produced and merged into the
seed's history entry; otherwise the literal token stays and Enter still
adjudicates it.
2026-10-15 13:18:01 +08:00
Yichen Jiang f61d9982f8 feat(models): edit reasoning levels of custom model API models
A model row on a hand-declared pi-ai route now edits its reasoningEfforts
from the Models settings page instead of only through cordis.patch.yml.
A switch turns the field on or off; when on, each row pairs a level
select with the API value sent for it, which defaults to the level name.
Off only turns reasoning off, so its value field is disabled and a value
already stored for it is kept. Saving refuses a list without a level
besides off, and a help tooltip beside the label explains the values.
Catalog routes and the DeepSeek card are unchanged.

Refs #6260
2026-10-15 12:56:11 +08:00
Yudong Han 755c52fd77 Merge pull request #6096 from deepseek-harness/fix/office-operation-guidance
fix(office): 回复不嵌入已删除预览,PDF 导出不查找阅读器
2026-10-15 12:23:26 +08:00
imccyu 5026df8fd1 Merge pull request #6250 from deepseek-harness/worktree-optbundles
feat(webworker): offer optional bundles in the preview and fetch lazy chunks on demand
2026-10-15 12:17:34 +08:00
yudshj dc9119c00b Merge remote-tracking branch 'origin/master' into pr6096-merge
# Conflicts:
#	packages/skill/skill-office/package.json
2026-10-15 12:04:01 +08:00
imccyu 6e43ad6bb1 Merge pull request #6164 from deepseek-harness/fix/subprocess-linux-probe-once
fix(subprocess-local): probe Linux native containment once and support systemd before v254
2026-10-15 11:32:27 +08:00
imccyu 18a22f73b2 Merge pull request #6228 from deepseek-harness/worktree-versionmismatch
feat(client-modules): prompt to reload when the host release changes
2026-10-15 11:31:54 +08:00
imccyu 93d2ad2d25 feat(webworker): boot the preview from the shipped profile and fetch lazy chunks on demand
The packer images the web template bundles, every optional bundle, and an installation manifest instead of a dumped composition, and the Worker boots that profile through app-boot like dsh web. Package-local lazy client chunks move into content-addressed archives that the VFS fetches on first asynchronous read; client-modules reads chunks asynchronously.

The Worker replaces node-addon-require-builtin with Node v24.18.0 internal-module interfaces and reports Node 24, so plugin metadata, the Cordis Loader, and HMR reach one cascaded loader backed by the Worker loader. Resolution failures carry Node error codes, the empty load cache lets HMR apply profile changes, and every optional bundle switches on and off live. Platform stubs throw ERR_FEATURE_UNAVAILABLE_ON_PLATFORM.
2026-10-15 11:30:45 +08:00
Turtle 15c245f2d8 Merge pull request #5633 from deepseek-harness/turtle/plugin-market-generator
feat(plugin-market-generator): add a reference index generator
2026-10-15 05:57:24 +08:00
turtle1999 14abe9543c feat(plugin-market-generator): record the source repository and parse options with commander
Copy each plugin's manifest repository as its web page and package
directory, converting shorthands and Git URLs with hosted-git-info. Parse
options and generate help with commander.
2026-10-15 05:37:54 +08:00
turtle1999 434dc116f0 feat(plugin-market-generator): add a reference index generator
Generate a validated format-1 plugin market index from a list of npm package
names and https Git repository URLs, without running package code.

- List every non-deprecated npm bundle version, and Git versions at release
  tags and the default branch, each with its engines.dsh range.
- Read display fields, keywords, homepage, and icons the way DSH reads
  installed plugins; inline small icons up to the format's 32 KiB limit.
- Cache results per immutable input and rewrite output only when it changes.
2026-10-15 05:37:54 +08:00
turtle1999 ed92c867ed feat(plugin-market): name the plugin and version in a Discover install
A Discover install passes the entry's localized title and version with its
spec. Every install dialog screen names the plugin and version, and shows the
spec, such as a pinned git+https URL, below in smaller monospace text.
2026-10-15 05:37:44 +08:00
turtle1999 b227127d62 fix(plugin-market): keep the Installed tab panel and list the markets bundle under More
The Installed tab panel now wraps its loading, unavailable, and empty states.
Custom plugin markets joins the bundles the More page holds back until the
profile enables them, and the repository field and its directory rule are
documented.
2026-10-15 05:37:44 +08:00
turtle1999 ab3bb74550 feat(plugin-market): carry a plugin's source repository
Indexes may give each plugin an optional repository: the web URL of its
source repository and the package directory inside it. The Discover entry
page links it as Source code, and the plugin_market tool returns it.
2026-10-15 05:37:44 +08:00
turtle1999 3de3a977e0 feat(plugin-market): add the Custom plugin markets bundle and Discover tab
Read configured plugin market indexes on the Host, search them with fuzzy
multilingual matching, and install a chosen version from the Plugins page.

- dsh-plugin-market-index validates format-1 indexes and builds install
  arguments; inline icons are capped at 32 KiB.
- The pluginMarket service fetches configured http(s) URLs, parses bodies in
  a Worker Thread, caches documents, and pages filtered, ordered results with
  installation status and engines.dsh compatibility.
- Discover is an opt-in experimental bundle; the Plugins page gains a
  plugins.tab slot and confirmed installs.
- The plugin_market tool lists indexes, plugins, and versions for Creator mode.
- Plugin compatibility reads engines.dsh before DSH peer dependencies.
2026-10-15 05:37:44 +08:00
imccyu bf1a2bf69c fix(inspector): serve the Client bootstrap while the Inspector Worker starts
A Client plugin loaded after the page starts fetches its bootstrap once. The Host now registers that route before starting the Worker and answers once the Worker is ready, so enabling Developer Tools on a running page opens the bottom DevTools without a reload. A failed start answers 503.

When the platform cannot run the Worker (ERR_FEATURE_UNAVAILABLE_ON_PLATFORM), the Host warns once and stays active with only the 503 bootstrap route; serve-static and open load after the Worker starts. Every other startup failure still fails the fiber.
2026-10-15 03:56:26 +08:00
imccyu 22c51728ed fix(dependencies): check experimental packages under the published dependency policy
verify-package-dependencies now covers every packages/*/* manifest, experimental included, and reads each package's tsdown build entries so Worker and child-process entries count as Host runtime sources. Experimental Client packages no longer declare browser and type relationships as peers.
2026-10-15 03:56:26 +08:00
imccyu b77cc1c0f2 feat(gates): reject module-scope await in package sources
Package modules that other code imports must not suspend evaluation at module scope. A syntax-aware gate scans packages/*/*/src and excludes only executables (a shebang or a package bin target). The SenseVoice worker, the SSH helper entry, and the Inspector worker entry now run an explicit main().
2026-10-15 03:56:25 +08:00
imccyu ba090a4b59 fix(ui-settings-general): focus Not now when the release prompt opens 2026-10-15 02:26:28 +08:00
imccyu a7a40f93d9 fix(client-modules): compare host releases before parsing the graph 2026-10-15 01:35:28 +08:00
imccyu f52abc04fa feat(client-modules): apply no plugin update from another host release 2026-10-15 00:51:20 +08:00
imccyu da5e23e8e8 refactor(subprocess-local): decide Linux containment in a testable selector
The provider's Linux decisions now live in LinuxScopeSelector, a state machine
over injected observations: the deep probe, the user-manager listener, the
manager confirmation, and the systemd-run binary identity. Each decision
carries its reason and probe trigger, the runtime logs it at debug level, and
table-driven scenarios cover every transition.

- A positive verdict probes again when the systemd-run found through PATH
  changes device, inode, size, or modification time, so an in-place upgrade
  past v254 cannot reuse options that no longer keep argv literal.
- The listener is read before the probe, so a manager that starts during a
  failed probe counts as a new listener.
- The kernel socket table is decoded as UTF-8, matching runtime directories
  outside ASCII.
- LinuxScopeNotStartedError drops its unused code field.
2026-10-15 00:51:05 +08:00
imccyu 71de43d8ee fix(subprocess-local): probe Linux again after a scope never starts or a new manager listens
A cached positive verdict let a hung user manager that still listens fail every
later command, and a cached negative verdict ignored a manager that started
later until the provider restarted.

- A scope launch whose bootstrap never consumed its launch request now fails
  with LinuxScopeNotStartedError, and the runtime clears the verdict it
  launched under, so the next eligible spawn probes again and records the
  fallback when that probe fails. A generation counter keeps launches from a
  superseded probe from forcing another probe.
- After a negative verdict, the runtime records the /proc/net/unix listener on
  $XDG_RUNTIME_DIR/systemd/private and probes again only when a different
  listener, meaning a new manager instance, appears.
2026-10-15 00:14:50 +08:00
Turtle f2a9545da0 test(auto-review): parse review JSON without unknown casts 2026-10-14 22:50:50 +08:00
Turtle ce5c139b05 fix(auto-review): send one message per review item so message-end caches hit
OpenAI GPT-5.6+ places implicit cache breakpoints only at message ends, and
Anthropic-style routes mark the final message. The review request was one
system prompt plus one growing user message, so those routes could reuse only
the policy and re-read the whole history on every review.

Send the environment, each retained item, and the pending call as separate
user messages. The pending call uses the history form, and a tool-schema item
precedes the first call that used each reviewed schema; the integration keeps
those schemas per live Session so later reviews repeat them in place. Each
review now begins with every message of the previous review.
2026-10-14 22:31:08 +08:00
07akioni 7ba5e3a01f Merge pull request #6197 from deepseek-harness/07akioni/desktop-environment-telemetry
feat(telemetry): attach cached desktop environment to product events
2026-10-14 21:29:33 +08:00
Yichen Jiang 571902028a Merge pull request #6241 from deepseek-harness/worktree/sync-dsh-0.2.1-alpha.3-to-master
release(dsh): sync 0.2.1-alpha.3 back to master
2026-10-14 21:15:15 +08:00
Yichen Jiang 5d7d13289c test(web): decouple plugin snapshots from release versions 2026-10-14 20:59:15 +08:00
Yichen Jiang 853b1d3609 Merge master into DSH 0.2.1-alpha.3 synchronization branch 2026-10-14 20:34:43 +08:00
winewill c7ab529c34 fix(desktop): preserve recovery paths in installer dialogs 2026-10-14 20:11:41 +08:00
winewill 06e5024577 fix(desktop): bound expanded directory failure details 2026-10-14 19:58:21 +08:00
imccyu 514e8893b5 test(subprocess-local): harden the socket-table checks and state their scope
The real-socket test rejects on a listen error instead of hanging, the table
fixture comment names the accepted-connection row it models, and the remaining
linux-scope module mocks provide confirmLinuxManager. The README notes that
the socket-table read matches only listeners in the same network namespace and
costs time proportional to the table size.
2026-10-14 19:53:13 +08:00
imccyu ac5a3e336c test(ui-settings-general): read the release prompt inject face without an unknown cast 2026-10-14 19:39:47 +08:00
winewill dcc8df8320 fix(desktop): preserve reported recovery state and label retry attempts 2026-10-14 19:38:42 +08:00
imccyu c97f840c89 fix(subprocess-local): confirm the user manager from its listening socket
systemd closes the private listening socket on shutdown without unlinking its
path, so the socket file outlives a manager stopped in place. The fast path now
reads /proc/net/unix, where the kernel lists a bound socket only while a
process holds it open, and accepts the manager only when a process listens on
$XDG_RUNTIME_DIR/systemd/private; otherwise the systemctl query decides. The
scope startup error names an unreachable manager as the cause, and the spawn
test again asserts that Windows launches never reach the fallback.
2026-10-14 19:34:14 +08:00
imccyu 055c7d7a74 fix(subprocess-local): confirm the user manager before each cached Linux scope launch
Without a per-spawn check, a user manager that stops after a positive probe,
such as after a logout without lingering, made every later scope launch fail.
Each eligible spawn now confirms the manager: its socket file at
$XDG_RUNTIME_DIR/systemd/private disappears when the manager stops, so its
presence answers with one stat; otherwise the restored systemctl --user show
query decides and the spawn falls back while it fails. A crashed or hung
manager that leaves its socket file still fails the scope launch with the
existing restart hint.
2026-10-14 19:14:29 +08:00
winewill fae05edca7 test(desktop): share retry trace reads with the installer 2026-10-14 19:13:46 +08:00
imccyu 7c9e54b3ef feat(client-modules): prompt to reload when the host release changes 2026-10-14 19:11:22 +08:00
imccyu f12bf31c4a docs(notes): record the host release change refresh prompt 2026-10-14 19:10:48 +08:00
winewill 14819e2eb4 fix(desktop): retry Windows installer directory locks and report failures 2026-10-14 19:08:02 +08:00
Turtle 5233bc770b docs(auto-review): scope the prefix-cache claim and record cache limitations 2026-10-14 19:00:17 +08:00
winewill fe3966734c Merge pull request #6212 from deepseek-harness/build/desktop-approval-summary
feat(desktop): show complete publication approval summary
2026-10-14 18:57:31 +08:00
Turtle bcc5e79c5c fix(auto-review): keep reviewer input append-only for prefix caching
Project instructions loaded mid-session were inserted into a section ahead of
the filtered history, so every nested AGENTS.md load changed the review prefix
and invalidated the provider's cached history. Render them in surface order
inside FILTERED_HISTORY with their constraint role, so each review's text
extends the previous review's text through its last history entry.
2026-10-14 18:46:29 +08:00
imccyu c218e79102 docs(subprocess-local): name the systemd 236 floor for --collect
The probe always passes --collect, which systemd-run rejects before v236, so those hosts use the fallback regardless of the --expand-environment=no retry.
2026-10-14 18:05:02 +08:00
Dudu-0223 4e4ac0eae8 Merge pull request #6208 from deepseek-harness/topic/agent-team-prompt
feat(agent-team): simplify team availability prompt
2026-10-14 17:55:36 +08:00
imccyu d73ad23353 fix(subprocess-local): name the recovery when a cached Linux scope fails to start
With the probe cached for the provider lifetime, a user manager that stops
after a positive probe makes every later scope launch fail. The startup error
now says to restart the provider to probe native containment again. The README
no longer lists older user-systemd among fallback hosts and notes the second
probe attempt for a systemd-run that rejects --expand-environment=no.
2026-10-14 17:50:29 +08:00
Ziya 075ebbb9f3 Merge pull request #6179 from deepseek-harness/ziya/builtin-plugin-preview-20261014
feat(plugins): 规范内置配图并补充双语语音示例
2026-10-14 02:46:15 -07:00
imccyu 39874e62ba docs(subprocess-local): state the provider-lifetime Linux probe and its cost
The README says the provider also keeps a failed or timed-out first probe and
names the user manager as reachable rather than live, since it is checked once.
The probe cache documents why no later spawn repeats the probe, and the spawn
test now starts two Windows commands to match its per-spawn Job recheck title.
2026-10-14 17:32:15 +08:00
ZiyaZhang d6e3df155e chore(plugins): preserve original preview work on the adopted frontend implementation 2026-10-14 02:25:24 -07:00
imccyu 0fffa2432a fix(subprocess-local): support systemd before v254 for native scopes
systemd-run before v254 rejects --expand-environment=no, so the scope probe
always failed and those hosts used the weaker fallback. Its scope mode never
expands argv, so the probe retries without the option only when systemd-run
reports it as unrecognized, and scope launches reuse the probed options.

systemctl before v252 rejects --kill-whom. Use --kill-who, the older
spelling, which newer systemctl accepts as an unambiguous prefix.
2026-10-14 17:24:14 +08:00
imccyu 404e64b435 fix(subprocess-local): probe Linux native containment once per provider
Every Linux spawn() and spawnTerminal() ran a synchronous spawnSync probe
before launching the command: the full systemd-run scope probe until it first
succeeded (a failed verdict was never cached), then a systemctl manager recheck.
spawnSync blocks the host event loop until the probe exits, so hosts without a
usable user manager paid the full probe on every grep, glob, and shell command.

Keep the first verdict, positive or negative, for the provider lifetime and
drop the per-spawn manager recheck.
2026-10-14 17:24:14 +08:00
ZiyaZhang c1269a8f75 feat(plugins): standardize built-in previews and illustrate voice input 2026-10-14 02:19:42 -07:00
winewill 1bd032be88 fix(desktop): align approval links with normalized upload destinations 2026-10-14 17:10:19 +08:00
winewill 24d73cae8d feat(desktop): show complete publication approval summary 2026-10-14 16:57:28 +08:00
ZiyaZhang ade9979185 fix(plugins): restrict feature previews to bilingual built-in SVGs 2026-10-14 01:57:03 -07:00
Dudu-0223 a04b8ef7e2 feat(agent-team): simplify team availability prompt 2026-10-14 16:36:16 +08:00
Shigma 3c15772738 feat(cordis): sync project-relative Loader resolution 2026-10-14 16:22:35 +08:00
07akioni 04c9489065 fix(telemetry): handle native metadata edge cases and scalar failures 2026-10-14 16:10:40 +08:00
ZiyaZhang 34252e34d0 docs(plugins): define built-in feature preview guidelines 2026-10-14 00:27:53 -07:00
Chinesezjc c949deb1ca refactor(util-time): publish only the span surface its consumers call
The package API carried members and result types ahead of any caller. The
span now publishes fromMilliseconds and decompose, the two entries
dsh-time-context calls; the seconds constructor, the zero identity, the
arithmetic and ordering operations, the millisecond getter, and the four
fractional conversions land with the caller that needs them, recorded in
the Agent Note and the README limitation.

The package entry names its exports instead of re-exporting both modules,
so the three result types and DurationParts, which no consumer names, are
no longer part of the published surface. Every remaining new name has a
named consumer: schedule imports the nine parser and bound names plus
four rejection types, and canonicalizeTimeZone has seven consumer files.
2026-10-14 15:27:15 +08:00
07akioni 4dee41f4bc feat(telemetry): attach cached desktop environment to product events 2026-10-14 15:23:58 +08:00
Shigma e199aa1bc7 refactor(cordis): 统一配置访问与消费方 2026-10-14 15:01:22 +08:00
Shigma 7618ccec69 chore(cordis): 同步 Timer 1.1.4 2026-10-14 15:01:18 +08:00
ZiyaZhang 37dbd15e0c feat(plugins): localize optional feature previews 2026-10-13 23:55:11 -07:00
TurtleandYifffan 7e68ab4f16 Credit review by @Yifffan
Co-authored-by: Yifffan <[email protected]>
2026-10-14 14:43:19 +08:00
Shigma c0bf2ac81d docs(cordis): plan the staged rc.11 vendor upgrade 2026-10-14 14:28:16 +08:00
ZiyaZhang fb752c7763 style(plugins): refine badge feature illustration 2026-10-13 23:07:32 -07:00
Dudu-0223 c5d227001e test(subagent): refresh fork runtime context ordering 2026-10-14 13:46:35 +08:00
Dudu-0223 e103591fd5 fix(subagent): address model selection review feedback 2026-10-14 13:40:25 +08:00
ZiyaZhang e527c04663 feat(plugins): support optional feature previews in bundle details 2026-10-13 22:22:43 -07:00
Turtleandimccyu 25ebc2450d Credit review by @imccyu
Co-authored-by: imccyu <[email protected]>
2026-10-14 13:12:34 +08:00
Dudu-0223 11bfb04081 Merge master and reconcile subagent preset and model selection 2026-10-14 13:09:34 +08:00
_Kerman 307d63792e Merge remote-tracking branch 'origin/master' into xtr/session-projection-registration-handles
# Conflicts:
#	benchmarks/session-corpus/projection-list.worker.ts
#	benchmarks/session-corpus/session-corpus.worker.ts
#	docs/config-catalog.md
#	docs/config-catalog.zh.md
#	packages/api/session-controller/README.i18n.yaml
#	packages/api/session-controller/src/index.ts
#	packages/api/session-controller/tests/list-scheduling.host.spec.ts
#	packages/client/ui-chat/README.i18n.yaml
#	packages/client/ui-chat/README.md
#	packages/client/ui-chat/README.zh.md
#	packages/client/ui-chat/src/client/chat/use-chat-viewport.ts
#	packages/experimental/client-ui-cot-translation/tests/activation.host.spec.ts
2026-10-14 11:12:24 +08:00
Yichen Jiang 2182e3ad09 Merge remote-tracking branch 'origin/master' into worktree/command-delete-as-whole-21017f
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
2026-10-14 10:58:07 +08:00
Turtle 7ab2ddd7a9 fix(client): drop the line mark when its preview reflows
A resized scrollport or a wrap toggle can move the marked line, so the band is
removed instead of marking another line. Document the exact timing and why
reduced motion keeps the opacity fade.
2026-10-13 22:11:53 +08:00
Turtle dbf7d54a18 feat(client): fade a navigated source line's highlight in the document preview
Opening a file link at a line now marks the landed line in both the code and
plain-text renderers with a band that holds briefly and fades out over two
seconds. The band lives in the scrollport, so syntax highlighting replacing
the code lines keeps it. The plain renderer's persistent target background is
removed.
2026-10-13 21:47:24 +08:00
_Kerman f50bab696a test(web): sample input folds on a controlled frame clock 2026-10-13 20:14:59 +08:00
_Kerman c25806f422 fix(ui-chat): settle folds with final sibling spacing 2026-10-13 20:14:43 +08:00
turtle1999 ac24256cdd refactor(subagent): keep closing-text state on local activations
- rename stepToolCalls to toolCallsThisStep
- move omitClosingText and toolCallsThisStep to the local activation resource, the only kind that sets them
2026-10-13 20:07:12 +08:00
turtle1999 99980c3a2e refactor(subagent): rename relayed to omitClosingText 2026-10-13 19:51:57 +08:00
_Kerman 6b4452b4ec Merge remote-tracking branch 'origin/master' into xtr/session-projection-registration-handles
# Conflicts:
#	packages/subagent/subagent-dsh-sdk/tests/fixtures/loader/scoped-tool-subagent.ts
#	packages/subagent/tool-subagent/src/index.ts
#	packages/subagent/tool-subagent/tests/harness.ts
#	packages/subagent/tool-subagent/tests/model-selection-settings.spec.ts
#	packages/subagent/tool-subagent/tests/tool-subagent.spec.ts
2026-10-13 17:44:01 +08:00
Yichen Jiang 47ea60e985 test(snapshots): align master's new scenarios with step decisions
subagent-agent-preset no longer declares exit_plan_mode outside plan mode, and the seeded Web fork admits its runtime-context snapshot after the time context.
2026-10-13 17:10:46 +08:00
Yichen Jiang 91d20a758b Merge remote-tracking branch 'origin/master' into worktree/dynamic-tool-activation-pr-64796e
# Conflicts:
#	docs/config-catalog.md
#	docs/config-catalog.zh.md
2026-10-13 17:05:40 +08:00
Yichen Jiang 7d00592802 test(snapshots): admit the runtime context after the step batch
The runtime-context snapshot now follows the messages that agent/pre-step admitted, so recorded sessions swap it with pre-step additions such as time context and skill notices. cordis-inspect-jsdoc records the new agent.step and agent/step-boundary declarations.
2026-10-13 17:05:03 +08:00
Yichen Jiang 4f36177740 feat(agent): publish step decisions as agent.step and add agent/step-boundary
Each proposed step now dispatches the serial agent/step-boundary event
after claiming input, then agent/model, and publishes the chosen model
selection as the optional agent.step until the step ends or its proposal
is rejected, fails, or is cancelled. agent/pre-step listeners, tool
conditions, prompt sections, and the provider and model variables read
agent.step, so the pre-step payload, AssembleContext, and
ToolActivationContext carry no model selection field.

Plan mode commits pending selections at the step boundary, before model
choice, and still narrates the switch in the first admitted step. The
system prompt assembles once after step/start, including runtime context,
and system-prompt/prepare is removed. The runtime-context snapshot is
admitted after the step's accepted messages, so a changed runtime context
alone no longer opens a step.
2026-10-13 17:04:55 +08:00
yudshj f12fa3bad1 fix(office): keep render replies and PDF checks within available files
- office-render: report inspection results in text and never link or embed
  removed previews. Desktop and Web replies otherwise embed deleted preview
  images, because their file-reference guidance encourages inline images.
- office-export-pdf: validate with the command result, nonempty output, and
  %PDF- signature; do not search for PDF readers unless the user requests
  deeper checks. The bundled runtime has none, so the old conditional made
  the model probe system Python and command-line tools.
- Assert that the PDF export e2e does not probe PDF reader tools.
- Replace stale "structural checks" wording in the Desktop Office plugin
  JSDoc and the skill package description.
2026-10-13 13:55:17 +08:00
gengruilin 761d5de69f fix(docs): 修复长表滚动与筛选评审问题 2026-10-13 13:40:29 +08:00
gengruilin e4804c305b test(docs): 精简长表热更新测试 2026-10-13 12:01:09 +08:00
Dudu-0223 26d038ae2b fix(subagent): complete discovery plugin metadata and catalog expectations 2026-10-13 11:56:32 +08:00
Dudu-0223 c05b3aedfb Merge master into subagent model selection refactor 2026-10-13 11:46:45 +08:00
Dudu-0223 8fa8e9167d refactor(subagent): separate model discovery and session authorization 2026-10-13 11:42:31 +08:00
turtle1999 92edf5bb0d Merge remote-tracking branch 'origin/master' into turtle/async-notice-efficiency 2026-10-13 11:33:31 +08:00
gengruilin 74a5818369 style(docs): 统一表格搜索框占位文字 2026-10-13 11:32:54 +08:00
_Kerman fc41f012a9 docs: refresh config catalog after merging master 2026-10-13 11:32:36 +08:00
_Kerman f8a84280aa Merge remote-tracking branch 'origin/master' into xtr/session-projection-registration-handles 2026-10-13 11:27:47 +08:00
Dudu-0223 84c3a61abf fix(subagent): keep fork model selection configurable 2026-10-13 11:16:53 +08:00
Yichen Jiang d6b8d3f278 Merge remote-tracking branch 'origin/master' into worktree/dynamic-tool-activation-pr-64796e 2026-10-13 11:14:28 +08:00
Yichen Jiang 28f196ffa1 docs(agent-loop): name agent/model as the effort override
A configured reasoning effort seeds the step's model selection, which agent/model may replace; agent/request can no longer change it.
2026-10-13 11:14:11 +08:00
Yichen Jiang 8bae86d49d test(snapshots): drop exit_plan_mode from master's office scenarios
Ordinary mode no longer declares exit_plan_mode, so the office scenarios that master recorded before this change pin the tool list and PTC SDK guidance without it.
2026-10-13 11:14:10 +08:00
Yichen Jiang d45d93f806 Merge remote-tracking branch 'origin/master' into worktree/dynamic-tool-activation-pr-64796e 2026-10-13 10:59:24 +08:00
Yichen Jiang 043539da05 feat(agent): choose provider, model, and effort through agent/model
Rename agent/route to agent/model and make its result a complete
ModelSelection, so each step fixes its provider, model, and reasoning
effort before prompt preparation, agent/pre-step, tool conditions, and
every request attempt. agent/request may change only sampling controls;
returning another provider, model, or reasoning effort fails the step.

installModelSelection installs one agent/model listener and
ModelSelectionRef drops assembled. The pre-step payload, AssembleContext,
and ToolActivationContext carry modelSelection. Webhook initial
selection and the CLI mock effort switch move to agent/model.
2026-10-13 10:58:57 +08:00
turtle1999 f7a0d4a8c9 Merge commit 'dae2c77f920' into turtle/async-notice-efficiency 2026-10-13 10:56:34 +08:00
gengruilin 568f12ea42 wip(docs): 保存长表筛选与固定表头实现 2026-10-13 01:53:50 +08:00
turtle1999 ed645016cd fix(jobs): read job output at settlement only for notices that inline it
- tool-jobs: inlining is decided before any read from the job's whole stream, so larger jobs keep their output and retention in the registry; a capped inline notice keeps the output's tail; job_output withdraws a pending notice from the agent now registered for the owner session
- subagent: track the relay through logged tool calls, which tool policy cannot skip
- snapshots: order the background-job-notice settlement with a sentinel file
2026-10-13 00:54:57 +08:00
Yichen Jiang 06d1caf82f feat(agent): choose the step route through agent/route
- Add the agent/route waterfall, dispatched once per proposed step before
  system-prompt/prepare and agent/pre-step; model selection captures its
  choice there, and a later selection applies from the next step.
- Pass the chosen StepRoute to prompt preparation and assembly, the
  agent/pre-step payload, and tool conditions; remove Agent.currentRoute().
- Run agent/request and prepareCall() for each attempt after step/start and
  prompt assembly; agent/request may change only call controls, and another
  provider or model fails the step.
- Resolve conditional tools and assemble the prompt once per step; retries
  reuse the route, tool set, and rendered assembly.
- Update documentation, Agent Notes, generated catalogs, tests, and the
  cordis-inspect-jsdoc snapshot.
2026-10-12 23:59:29 +08:00
turtle1999 111a6b4965 docs: regenerate event producer-consumer graph 2026-10-12 21:09:19 +08:00
turtle1999 3241fa9923 fix(subagent): keep the closing message when another tool ran beside the relay
- subagent: a send_message that shares its step with another tool call no longer omits the closing message
- tests: read settled job results through job_output and give job-owning test agents a mutable inbox
2026-10-12 21:03:04 +08:00
turtle1999 70eab5706e feat(jobs): deliver job output in completion notices and drop repeated notice text
- tool-jobs: a completion notice carries the job's unread output when it fits inlineOutputBytes (default 2048); job_output withdraws a notice still pending, and the label is its first line bounded to 120 characters
- tool-bash, tool-pwsh: the promotion result keeps only the still-running marker
- subagent: a settlement notice omits the closing message when the child's last action was a message to its parent
2026-10-12 20:40:48 +08:00
Dudu-0223 764a51d507 refactor(subagent): expose model selection only on settings-owning tools
Route fields and list_subagent_models are now registered only by the
tool-subagent instance configured with modelSelectionSettings, which keeps
one fixed definition per composition for Web presets while headless and SDK
profiles return to their previous schemas. Discovery registers with that
instance instead of a separate list-models plugin entry, so the delegation
description never references an absent tool. modelSelectionSettings on a
provider that inherits the parent conversation now fails the mount.
2026-10-12 18:37:26 +08:00
Chinesezjc 183cbf4df4 Merge origin/master into refactor/unify-time-vocabulary
Resolutions:
- packages/util/time/package.json keeps this branch's description and the
  polyfill dependency, with master's 0.2.1-alpha.2 version.
- docs/module-graph.md and .zh.md are regenerated: master's graph plus
  this branch's context/time-context and schedule edges to util-time.
2026-10-12 17:53:48 +08:00
Dudu-0223 56affefb19 refactor(subagent): shorten model-selection tool descriptions 2026-10-12 17:20:06 +08:00
Dudu-0223 155ab69fae fix(subagent): preserve fork model selection restriction 2026-10-12 16:29:01 +08:00
turtle1999 978d2b2237 Merge remote-tracking branch 'origin/master' into fix/coverage-fork-exit-order 2026-10-12 16:18:20 +08:00
_Kerman 5705bf12f0 refactor(agent-instructions): remove unused source type re-export 2026-10-12 15:41:37 +08:00
Dudu-0223 7609189262 fix(subagent): intersect model discovery with authorized catalog 2026-10-12 15:27:02 +08:00
Dudu-0223 0948cd9809 test(subagent): document non-Error adapter rejection fixture 2026-10-12 15:14:22 +08:00
Dudu-0223 080b921b5b docs(subagent): clarify exact-model lookup failures 2026-10-12 15:07:11 +08:00
Dudu-0223 48ab8620d9 Merge master and align SDK resume fixture with persistence plugin exports 2026-10-12 15:06:12 +08:00
Dudu-0223 41ea4eb6de fix(subagent): report model discovery errors per route 2026-10-12 15:00:27 +08:00
_Kerman 409ed33183 test(tool-present): import the built plugin namespace 2026-10-12 14:04:28 +08:00
Dudu-0223 ae19c6df8e test(web): align merged snapshots with shared subagent tools 2026-10-12 14:02:48 +08:00
Dudu-0223 ea5d070e37 test(subagent): refresh model tools in merged snapshot scenarios 2026-10-12 13:53:20 +08:00
Dudu-0223 26c86b129c Merge master into feat/subagent-fixed-model-tools 2026-10-12 13:46:15 +08:00
_Kerman 96784dae41 refactor(tool-goal): keep authority checks on the original event scan 2026-10-12 13:20:04 +08:00
_Kerman a9bbe0c220 fix(session-projection): always return a promise from handle disposal 2026-10-12 13:08:25 +08:00
_Kerman 5b401b27a5 refactor(tool-session-query): pass projection handles to operations 2026-10-12 13:08:18 +08:00
_Kerman 99f3660120 refactor(hooks-codex): simplify hook payload construction 2026-10-12 13:08:14 +08:00
_Kerman 2bdbdb4ae9 merge: integrate latest master with projection registration handles 2026-10-12 12:51:32 +08:00
_Kerman ce5c38791e docs: update projection lifecycle guidance and generated catalogs 2026-10-12 12:03:37 +08:00
_Kerman 7ac8f92b70 test: adapt application and benchmark fixtures to async activation 2026-10-12 12:03:37 +08:00
_Kerman 08e48f34b5 refactor(plugins): migrate projection consumers to retained handles 2026-10-12 12:03:36 +08:00
_Kerman 5ecf33aab1 refactor(session-projection): acquire retained readers during activation 2026-10-12 12:03:36 +08:00
Yichen Jiang aa0bc561df Merge origin/master into worktree/conditional-tools 2026-10-12 11:40:00 +08:00
Yichen Jiang b7ec97ed7d test(tools): refresh resumed-session tool inventories 2026-10-12 11:35:19 +08:00
Yichen Jiang 3ce7312b75 fix(tools): align conditional activation tests and snapshots 2026-10-12 11:09:00 +08:00
Yichen Jiang c4fc8c9dfc Merge master into conditional tools and reconcile PTC lifecycle 2026-10-12 10:36:42 +08:00
Yichen Jiang 9c3a6a34d4 feat(tools): resolve conditional tool admission per request 2026-10-12 10:30:08 +08:00
Yichen Jiang 5e3b3509b8 Merge remote-tracking branch 'origin/master' into worktree/command-delete-as-whole-21017f
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
#	packages/client/ui-conversation/README.md
#	packages/client/ui-conversation/README.zh.md
2026-10-11 17:11:17 +08:00
turtle1999 838c1e5f25 Merge remote-tracking branch 'origin/master' into fix/coverage-fork-exit-order 2026-10-11 15:04:18 +08:00
turtle1999 cde1086c0a test(coverage): name the missing worker-exit line in the ordering comment 2026-10-11 03:28:22 +08:00
turtle1999 4fe62777b3 test(web): pin the collapse-timing clocks to the fixture day
The scenario stamps its own prompt times from the Host wall clock, which
`support.ts` pairs with `pinHostClock`/`pinBrowserClock` and a fixture `createdAt`
so the calendar fact belongs to the scenario instead of the run, as
`seeded-history.e2e.ts` already does on the same fixture. With both clocks on the
fixture day the message and render instants cannot land on different days, so the
snapshot no longer depends on which side of local midnight the run falls.

Refs #5997
2026-10-11 00:55:07 +08:00
turtle1999 4ea4fc290e Merge remote-tracking branch 'origin/master' into fix/coverage-fork-exit-order 2026-10-11 00:33:13 +08:00
turtle1999 b66aa70051 test(coverage): scope the fork exit ordering claim to the clean exit report
Vitest reports an unexpected worker exit either as "Worker exited unexpectedly"
from the exit event or as "Worker coverage-forks emitted error" when the channel
closes under a send. The record describes the exit, and the error is logged
before it, so only the first form can order the record ahead of the report. The
previous assertion named one hardcoded message; #5900 widened it to whichever
form appeared and so claimed an order the error form cannot satisfy.

The case name no longer claims that order, and the report lookup relies on the
assertion above it instead of repeating the check.

Refs #5813
2026-10-10 13:32:36 +08:00
Yichen Jiang 75b761f87e Merge remote-tracking branch 'origin/master' into worktree/command-delete-as-whole-21017f
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
2026-10-10 13:02:14 +08:00
Yichen Jiang f20a0190ba Merge remote-tracking branch 'origin/master' into worktree/command-delete-as-whole-21017f
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
2026-10-09 16:24:05 +08:00
Yichen Jiang 510cd4cdcc fix(web): cover every deletion path in two-step chip deletion
Decide the command chip's selecting step on deletion input events as well
as the character-deletion command, so iOS ranged deleteContentBackward no
longer removes the chip in one press. A selected chip now clears only
after a keydown that produced no character deletion, which keeps platform
shortcuts such as macOS Ctrl+H working.

A failed command submission settles to the leading chip like every other
transition, so submit-settled no longer carries the draft. The claim event
reuses the same handler. The seat rule runs as chip and paragraph
transforms instead of walking the whole document on every update.

Document the reload limitation under Known Limitations and assert that a
chip ending its block does not grow the composer.
2026-10-09 14:52:21 +08:00
Yichen Jiang 98d93db18f feat(web): claim slash commands as atomic composer chips
A claimed command is now a CommandChipNode on the draft's leading seat
instead of styled literal text. The submit machine follows the chip: it
holds the claim while the chip leads the document, releases it when the
chip is deleted or displaced, and re-enters claimed when history
restores the chip. Deleting at the chip's edge takes two steps: the
first Backspace or Delete selects the chip, the next removes it.

The claim-token decoration transform and the text-reference precedence
rule are removed with the literal token.
2026-10-09 13:38:12 +08:00
Yichen Jiang 062dd72943 test(web-search): expect web_search withheld behind a local chat endpoint
The Python SDK profile keyless smoke, the web keyless CLI smoke, and the
Python runtime dynamic-tools snapshot point DEEPSEEK_BASE_URL at a local
mock. Since the endpoint check, the DeepSeek chat key of such a route is not
sent to the official search endpoint, and search there has no configuration
of its own, so the model request no longer offers web_search. CI reported the
three stale expectations in the e2e, web browser snapshot, and Python
runtime lanes.
2026-10-09 13:28:23 +08:00
Yichen Jiang 992a81d12b docs(web-search): state that only the key reference is compared with its default
A written apiKeyEnv: DEEPSEEK_API_KEY matches the shipped row and keeps the
DeepSeek-route and endpoint checks, but a written baseURL counts as search
configuration even when it names the default endpoint. The README limitation
and the Agent Note said "default values", which wrongly covered baseURL too.
2026-10-09 13:12:58 +08:00
Yichen Jiang fdd29b45b8 refactor(web-search): open search to every route once search has its own setup
Replace the `routes` allowlist with the user's own configuration. A search
whose key or endpoint the user configured (a literal apiKey, an apiKeyEnv
other than DEEPSEEK_API_KEY, baseURL, or $DEEPSEEK_SEARCH_BASE_URL) serves
every model route without the route or endpoint check. Without such
configuration the key is the DeepSeek chat key: only the DeepSeek account and
official routes may use it, and only toward a search endpoint on the official
chat route's origin. The account token rule stays as before.

Both refusals now name the settings that give search its own setup.
2026-10-09 13:04:22 +08:00
Yichen Jiang e2b2760589 feat(web-search): send the DeepSeek API key only for DeepSeek routes and their chat origin
DeepSeek native search is served by DeepSeek's endpoint, so two checks now
gate the API key, in ready() and again in the key resolver of every search:

- Route: a model request on a route other than deepseek-account,
  deepseek-official, and the new `routes` setting is refused with
  WEB_PROVIDER_ROUTE_DISABLED, so another provider's conversation does not
  bill DeepSeek unless a deployment lists the route.
- Endpoint: the official chat route's own key reference goes only to a
  search endpoint on that route's chat origin, read from the active
  dsh-llm-deepseek-api-key Loader row with that package's resolver;
  otherwise WEB_PROVIDER_ENDPOINT_MISMATCH. A literal key, a separate key
  reference, or a search endpoint on the chat origin passes.

The provider passes a resolver's own WebError through unchanged. A real
Loader composition pins the endpoint check. The pi-ai-conversation-updates
scenario gets its own tool sidecar without web_search, and the legacy
deepseek-messages route loses web_search in deepseek-messages-system-prompt.
2026-10-09 11:14:49 +08:00
Turtle 9e284509c8 Merge remote-tracking branch 'origin/master' into turtle/image-cache-stale-0a243f674e12
# Conflicts:
#	packages/client/ui-primitives/README.i18n.yaml
2026-10-08 20:37:19 +08:00
Yichen Jiang 5c54c34a82 revert(web-search): restore the settings page endpoint hint
The page no longer ships with the Web app, and review noted that the
web_search_20250305 requirement is not text a user should see. The hint
returns to master's wording, so this PR no longer changes the page's copy;
the endpoint requirement stays documented in web-search-deepseek.
2026-10-08 19:20:56 +08:00
Yichen Jiang 2377b272a4 feat(web-search): stop shipping the DeepSeek web search settings page
The provider's endpoint must serve DeepSeek's native web_search_20250305
search, so the shipped Web app no longer mounts ui-settings-web-search; the
web-search-deepseek section is configured only in cordis.patch.yml. The key
the page wrote is the same DEEPSEEK_API_KEY the Models page stores, so a
session on any route can still search once that key exists.

- web-app bundle: drop the page row; keep the package dependency so a
  deployment's Web profile patch can mount the page again. The package is
  registered as an optional product package.
- web-search-deepseek: endpoint failures no longer send the user to a
  settings page; they name DEEPSEEK_SEARCH_BASE_URL and
  web-search-deepseek.baseURL. The endpoint guidance snapshot is refreshed.
- Web e2e: the official group lists three pages, and a new case mounts the
  page through a patch overlay and checks its endpoint requirement hint.
2026-10-08 19:13:19 +08:00
Yichen Jiang add5ce9888 test(snapshots): refresh web-search-unconfigured after merging master
master added the working-directory runtime context and changed the headless
prompt and tool schemas. The scenario's session now matches tool-call-turn
again, and its pinned sidecars still differ from the default class only by
the withheld web_search schema and guidance.
2026-10-08 19:13:03 +08:00
Yichen Jiang d0dda93064 Merge remote-tracking branch 'origin/master' into worktree/web-search-dynamic-tool-e26c2b
# Conflicts:
#	.agents/notes/implemented/feature/2026-07-31-web-default-search.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	packages/web/tool-web/README.i18n.yaml
#	packages/web/web-search-deepseek/README.i18n.yaml
#	packages/web/web/README.i18n.yaml
#	snapshots/sdk/subagent-dsh-sdk-dynamic-route/session.1.v4.jsonl
#	snapshots/sdk/subagent-dsh-sdk-dynamic-route/session.v4.jsonl
2026-10-08 18:56:11 +08:00
Dudu-0223 3cd5599617 test(subagent): refresh Web model-selection tool snapshots 2026-10-08 16:52:30 +08:00
Dudu-0223 648eb70e90 test(subagent): refresh inherited parent tool list 2026-10-08 16:52:30 +08:00
Dudu-0223 7cf871d39a test(subagent): refresh PowerShell and Python SDK tool snapshots 2026-10-08 16:52:30 +08:00
Dudu-0223 11c9d75ca3 test(subagent): verify policy capture failure rolls back creation 2026-10-08 16:52:30 +08:00
Dudu-0223 7d640365b6 feat(subagent): share fixed model-selection tools across sessions 2026-10-08 16:52:29 +08:00
Chinesezjc 8987e6c45a docs: drop references to the removed invariant mechanism
origin/master removed the runtime invariant plugins and the package rule
that asked a README to justify the absence, so the text that cited that
mechanism no longer refers to anything:

- the Agent Note states the elapsed-rendering change without the reading
  invariant it used to cite
- the same note no longer says the durable reading format is one an
  invariant validates
- the util/time README no longer explains why no invariant companion is
  published, and its module JSDoc no longer claims the library formats
  nothing while listing a canonical instant renderer
2026-10-08 16:20:08 +08:00
Chinesezjc d51a000ee8 Merge origin/master into refactor/unify-time-vocabulary
Resolutions:
- packages/util/time/package.json keeps this branch's description and the
  polyfill dependency, with master's 0.2.1-alpha.1 version.
- packages/util/time/README.md and README.zh.md keep this branch's
  structure and content, with master's model-experience sentence about
  the zone instruction and timestamp. The re-recorded i18n sidecar
  follows.
- docs/module-graph.md and .zh.md carry master's graph plus this
  branch's context/time-context and schedule edges to util-time.
2026-10-08 16:02:22 +08:00
Turtle 9aad02e958 feat(session-controller): redirect file requests to cacheable versioned URLs
A request without the file's current version gets an uncached 302 to
api/file?path=…&v=<fs version>, served private, immutable. Each newly
mounted Markdown image still requests a unique URL, while the Host
decides whether the browser may reuse cached bytes.
2026-10-02 00:38:29 +08:00
Turtle 845c155faa fix(client): document image load tag contract and drop URL.parse 2026-10-01 22:32:50 +08:00
Turtle 3b0ad0aad4 fix(client): reload overwritten local Markdown images in new renders
Browsers reuse a same-URL image within one document despite no-store,
so a newly rendered reply showing an overwritten file kept the earlier
bytes. Tag each mounted rewritten local image URL with a per-mount load
number.
2026-10-01 22:12:35 +08:00
Chinesezjc a0ed3981fe fix(ci): revoke the approval decision through its conclusion
A check run that reached completed never returns to in_progress: GitHub
answers 200 and leaves the run completed, so the revoke write was a
silent no-op on every re-evaluation and a published success survived an
interrupted setup or history fetch. Revoke a published success by
concluding it action_required, start a head that has no decision in
progress, and drop the status-only check-run update.

The setup-failure step ran only when the revoke step succeeded, so a
failed or cancelled publisher left the earlier conclusion standing. It
now runs on failure and on a cancellation that precedes the decision.

The decision note, the review-ownership README, and the two related
notes record the measured transition matrix and the fallback condition.
2026-09-30 13:42:02 +08:00
turtle1999 094a8701fe Publish weighted approval as a completed check run
Unmet weighted approval conditions previously left a pending commit
status that CI watchers treat as running work, so gh pr checks --watch
and similar monitors never finished. Publish the decision as the
weighted approval decision check run instead: action_required blocks
merging while completing the check, and failure reports evaluation
errors.
2026-09-29 12:48:04 +08:00
Yichen Jiang 8b085b7e35 fix(web-search): check DeepSeek search readiness in the search's credential order
On the account route a search reads the account grant before any API key
and fails with WEB_PROVIDER_ERROR when that read throws; it never falls back
to the key. ready() swallowed the failed read and then accepted a literal,
stored, or environment key, so it answered true for a search that fails.

ready() now checks the account token first on the account route, answers
false when that read fails, and only then checks the literal key and the
credential reference. Tests pair the readiness answer with the real search
failure for a literal key and an environment key.
2026-09-28 12:17:36 +08:00
Yichen Jiang 7524c1ecd9 refactor(web): split web_search readiness across the web seam roles
Move per-request web_search withholding out of the DeepSeek provider:

- dsh-web: add the optional WebSearchProvider.ready(llmProvider) and
  ctx.web.searchReady(llmProvider?), which asks only the provider that
  search() would select; a provider without ready() and a selection
  failure answer true.
- dsh-web-search-deepseek: implement ready() with the existing credential
  check and drop the assemble listener together with its system-prompt,
  tools, and tool-web dependencies.
- dsh-tool-web: remove web_search and its tool:web_search section in a
  prepended system-prompt/assemble listener when searchReady() answers
  false for the route model selection assigned to the request.

A composition that selects another search provider no longer loses
web_search when DeepSeek has no credential. The Agent Note is renamed to
2026-09-27-web-search-offered-by-provider-readiness and records the split.
2026-09-27 17:00:33 +08:00
Yichen Jiang ad8c5f8c09 test(snapshots): pin the replay search credential in expected and web e2e runs
The subagent-inheritance expected test and the web e2e scaffold replay the
shipped compositions keylessly against sessions recorded while web_search
was offered. Both now supply REPLAY_CREDENTIAL_ENV, like the snapshot suites,
so their tool lists no longer depend on whether the runner exports a
DeepSeek key. Scaffolds that deliberately mask the key are unchanged.
2026-09-27 16:16:51 +08:00
Yichen Jiang 536e9932c3 test(snapshots): pin replay search credentials and cover a keyless search tool list
Snapshot replays now supply a placeholder DEEPSEEK_API_KEY through the shared
REPLAY_CREDENTIAL_ENV, so web_search visibility no longer depends on whether
the runner exports a key. The new web-search-unconfigured headless scenario
pins a request without web_search, the headless keyless smoke asserts the
same through the real Loader, and subagent-dsh-sdk-dynamic-route's keyless
dsh-sdk child now records its tool list without web_search.
2026-09-27 16:01:03 +08:00
Yichen Jiang 9bf6ba6298 feat(web): note the native search requirement under the search endpoint
The web search settings page's Endpoint hint now states that the endpoint
must support Anthropic's web_search_20250305 server-side search, since the
DeepSeek provider sends that native tool to whatever endpoint is configured.
2026-09-27 16:00:52 +08:00
Yichen Jiang 1fc808dbc9 feat(web-search): withhold web_search from requests without search credentials
The DeepSeek search provider now removes dsh-tool-web's web_search tool and
its prompt section from each model request whose search would have no
credential: no account token for the request's deepseek-account route and
search endpoint, no literal apiKey, and no configured apiKeyEnv credential.
A prepended system-prompt/assemble listener reads the route model selection
writes for the request, so a model switch or a newly stored key applies to
the next request through the existing dynamic tool updates. PTC
presentations and execution-time authentication are unchanged.
2026-09-27 16:00:32 +08:00
Chinesezjc 9facb51614 docs: state the coercion, changed samples, and charter files exactly
- the note says Duration overrides no coercion hook, matching the README
  and the class doc rather than claiming it has no toString
- the elapsed change names the two samples that move (NaN and +Infinity,
  not -Infinity, which the consumer still clamps to zero)
- the charter sentence names the files that keep the phrase
- the seconds-unit limitation separates a span from an epoch in seconds
2026-09-26 01:22:52 +08:00
Chinesezjc 830f66006e fix(util-time): enforce the span bound in arithmetic, and correct the record
- plus and minus construct through fromMilliseconds, so the documented
  bound has one enforcement point; the negative control reddens the new
  assertion
- the class and README say Duration overrides no coercion hook rather
  than having none, and decompose says which parts are floored
- the note no longer calls the differential a review probe, records that
  Schedule imports the bound constants, that both strict parsers refuse
  year 0, that Schedule's suites do assert a few message substrings, and
  that the util charter bans harness dependencies rather than external
  packages
2026-09-25 16:33:11 +08:00
Chinesezjc 0b5803cf0c fix(util-time): bound a span, and state the conversions as they are
- Duration rejects a count outside the exactly representable millisecond
  range, which is what makes decompose's whole-part identity exact; the
  postcondition is asserted over a sweep and a random sample
- the to* JSDoc no longer claims a round trip restores the count: each is
  one division, so it answers the nearest double
- a test rules out a Symbol.toPrimitive coercion hook, the README names
  both implementation modules, and the note records that the value type
  ships its full operation set before its second consumer exists
2026-09-25 15:14:50 +08:00
Chinesezjc 994b9e07bf fix: satisfy the lint and generated-catalog gates
- the immutability assertion checked coercion overrides instead of
  stringifying the instance, which no-base-to-string rejects
- the config catalog is regenerated for the package description
2026-09-25 14:55:58 +08:00
Chinesezjc 0939392e71 feat(util-time): add a millisecond Duration span
Duration holds one finite millisecond count behind a frozen class: a
class rather than a branded number because TypeScript erases a brand
through arithmetic, so only an instance can stop seconds and
milliseconds from being added. It adds, subtracts, orders, converts to
larger units fractionally, and splits a non-negative span into whole
days, hours, minutes, and seconds. There is no valueOf and no toString,
so a reader always states its unit.

dsh-time-context reads its elapsed line through it and keeps the
wording: a backward clock reading still clamps to zero there. The
rendered text is byte-identical for every finite sample, verified by a
differential over more than eight thousand inputs. A non-finite sample
now throws from the span constructor instead of rendering 'NaNs', which
the package's own reading invariant already refused.
2026-09-25 14:48:01 +08:00
Chinesezjc bbdac8febe docs(time): state the module contract and the zone mapper as they are
- the constants name one bound each
- resolveLocalInstant reuses an instance a caller already holds instead of
  copying it per candidate date
- the README entry-point paragraph drops its duplicate sentence
- the note replaces an invalid reason for the zone mapper and a
  review-vantage sentence with the facts
2026-09-25 01:01:26 +08:00
Chinesezjc 7edd3efe85 refactor(time): give the four-digit bound one owner
- isFourDigitYearInstant is the shared predicate; Schedule's five
  representability guards delegate to it instead of re-deriving the range
- the reason for a field outside its calendar range is out_of_range in
  both strict parsers, so year 0 no longer reads as a wall-clock problem
- LocalCalendarTime.year states which entries reject year 0 and which
  accept it at the UTC edges
- the note counts the five removed expressions and records that the zone
  mapper is a silent catch-all; the Chinese side mirrors the English
  paragraph order
2026-09-25 00:53:09 +08:00
Chinesezjc 92ee4e98f0 docs(time): state each entry's return precisely
- the README no longer claims resolveLocalInstant answers a string
- the accepted-input table records the widened plain date-time input
- canonicalClientTimeZone is named as the wire-boundary form rather than
  left outside a blanket claim about discriminated results
- the non-canonical scheduledAt diagnostic has one source
2026-09-25 00:41:31 +08:00
Chinesezjc 51a7572852 refactor(time): answer the shared resolver from one place
- resolveLocalInstant accepts an already-built plain date-time, so the
  recurrence walkers drop their private copy of the disambiguation and
  round-trip decision
- decodeInstant narrows its field explicitly and drops the assertion
- the model-input exhaustiveness fallbacks throw a plain Error, matching
  the timing-change fallback, while durable decoding keeps
  ScheduleLogError
- the library module and README state which entries answer a
  discriminated result
2026-09-25 00:31:56 +08:00
Chinesezjc 2d28a1dee7 docs: narrow the zone ignore and correct the time-vocabulary note
- the guarded ignore covers only the uncovered branch point
- the note says two consumers copied the profile, counts copies rather
  than consumers, and scopes the platform cause to the unsupported
  rejection
- the note names the remaining instant-profile copy in session-format
2026-09-24 22:19:01 +08:00
Chinesezjc 9f6696c2d5 Merge remote-tracking branch 'origin/master' into refactor/unify-time-vocabulary
# Conflicts:
#	packages/util/time/package.json
2026-09-24 21:56:53 +08:00
Chinesezjc 22f8be30a1 refactor(time-context): consume the shared zone vocabulary
time-context kept a third copy of the IANA profile: its own regular
expression and its own Intl canonicalization in request-zone.ts. It now
reads a request's reported zone through canonicalizeTimeZone and keeps
its stricter policy — an alias whose canonical answer differs from the
reported value is still refused, with the same three diagnostics.

The shared rejection now carries the platform error it caught, so the
unsupported refusal keeps its cause in both time-context and Schedule.
2026-09-24 21:54:34 +08:00
Chinesezjc d40567afaf Merge remote-tracking branch 'origin/master' into refactor/unify-time-vocabulary 2026-09-24 13:53:58 +08:00
Chinesezjc b42cae0c8b fix(schedule): mark the unreachable zone-rejection branch
The shared parser never answers not_canonical_shape, so a switch over the
union left that case uncovered and failed the per-file coverage gate.
2026-09-24 13:25:05 +08:00
Chinesezjc 0a89067fde docs: regenerate the module graph for the schedule to util-time edge 2026-09-24 13:15:06 +08:00
Chinesezjc 6c9c7cb69e docs(time): state the throwing preconditions and exact contracts
- Instant entries that take already-validated values throw on violated
  preconditions; the module header said every entry answers a result.
- formatFourDigitYearInstant also refuses a non-integral epoch.
- Schedule's reason mappers switch over the closed unions with a never
  default, so a new library reason fails the build instead of falling
  through to a catch-all.
- The Agent Note claimed the schedule suites pin these diagnostics; they
  assert codes only, and the note now records the added message assertion.
2026-09-24 13:08:06 +08:00
Chinesezjc 882a18c57b docs: drop a nonexistent path reference from the time-vocabulary note 2026-09-24 13:02:11 +08:00
Chinesezjc 7ee7dfde55 fix(schedule): preserve both local-calendar diagnostics
A calendar date the month does not have keeps the at diagnostic; only an
out-of-range clock field keeps the local at one. The extraction had
collapsed both onto the local at wording.
2026-09-24 13:00:40 +08:00
Chinesezjc c96d48d491 refactor(schedule): extract time vocabulary into dsh-util-time
Move Schedule's strict RFC 3339 instant, local-calendar, and IANA zone
parsing into @deepseek-ai/dsh-util-time, and have Schedule consume it.

The shared entries answer discriminated results and own no failure
vocabulary; Schedule keeps its error codes, diagnostics, durable record
shapes, and all recurrence arithmetic.
2026-09-24 12:55:36 +08:00
Chinesezjc bb181ccec9 Merge remote-tracking branch 'origin/master' into fix/multiline-display-math
# Conflicts:
#	packages/client/ui-primitives/README.i18n.yaml
#	packages/client/ui-primitives/src/markdown/parse.ts
2026-09-23 22:58:31 +08:00
Chinesezjc ac97f3328e Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-23 15:54:23 +08:00
Chinesezjc d5c9e60885 Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-23 15:17:56 +08:00
Chinesezjc 86cc2b9b5a Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-22 23:03:49 +08:00
Chinesezjc 8a3c7abfe9 Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-22 17:14:35 +08:00
Chinesezjc e8a3a5548c Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-22 17:10:31 +08:00
Chinesezjc 0dc8adfb4c Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-22 14:50:35 +08:00
Chinesezjc b8df5598eb Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-22 11:21:23 +08:00
Chinesezjc ba46f347cf Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-22 04:24:54 +08:00
Chinesezjc 17ddd3b2f4 Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-21 23:13:45 +08:00
Chinesezjc 70747fdba7 Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-21 18:12:27 +08:00
Chinesezjc 9fb5ea44a4 Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-21 17:33:57 +08:00
turtle1999 3ed9fe36ae test(snapshot): cover same-mode blank reasons in a dedicated scenario 2026-09-21 17:04:05 +08:00
turtle1999 b22f589943 revert(tools): keep existing tool descriptions 2026-09-21 16:57:47 +08:00
turtle1999 6c263e2e70 test(snapshot): preserve budgets after tool guidance changes 2026-09-21 16:14:51 +08:00
turtle1999 b7d0eba7bd chore: merge master for final review validation 2026-09-21 15:47:36 +08:00
turtle1999 4e4badb8f3 fix(tools): align optional argument presentation and guidance 2026-09-21 15:44:26 +08:00
Chinesezjc e719b4e628 Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-21 15:39:37 +08:00
turtle1999 ca50c2a224 chore: merge master before review fixes 2026-09-21 14:57:03 +08:00
Chinesezjc f44e7de90c Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-21 14:12:54 +08:00
Chinesezjc 589b3c816e Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-21 12:31:09 +08:00
turtle1999 9c0519085d chore: merge master for tool card compatibility 2026-09-21 12:19:55 +08:00
Chinesezjc 4a624284f5 Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-21 12:07:08 +08:00
turtle1999 89137a185d fix(web): preserve tool cards with blank escalation reasons 2026-09-21 12:04:58 +08:00
Chinesezjc 3c37cc91ef Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-21 11:42:15 +08:00
Chinesezjc 9dfe9b6c60 Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-21 00:13:39 +08:00
turtle1999 c3c2f73489 fix(fs): allow blank reasons without sandbox escalation 2026-09-20 21:41:38 +08:00
Chinesezjc 0c1e3adb60 Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-20 20:47:27 +08:00
Chinesezjc dbef698ef7 Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-20 18:23:52 +08:00
turtle1999 9dd781d935 chore: merge master into toolcall compatibility fix 2026-09-20 17:58:54 +08:00
turtle1999 080a2c60b3 fix(tools): tolerate empty filters and unused goal arguments 2026-09-20 17:50:02 +08:00
Chinesezjc 9d155141ea Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-20 17:02:38 +08:00
Chinesezjc 066145c926 Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-20 14:00:11 +08:00
Chinesezjc 0c11504970 test(web): assert the upstream-taken prose inside the math node 2026-09-20 13:20:19 +08:00
Chinesezjc 57fdf403c1 docs(web): correct the fence-meta example and pin the later-line trailing shape 2026-09-20 13:11:21 +08:00
Chinesezjc 6251cbadcb docs(ui-primitives): qualify the display-math fallbacks by line position 2026-09-20 13:01:31 +08:00
Chinesezjc d0228f0ec0 docs(ui-primitives): separate the display-math fallbacks from the verb fence rule 2026-09-20 12:17:14 +08:00
Chinesezjc c29f8b91d5 Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-20 12:11:53 +08:00
Chinesezjc ff0244f4b4 docs(ui-primitives): record the display-math limits in the package README 2026-09-20 12:10:08 +08:00
Chinesezjc a5a78b88ab docs(web): state the verb tail limitation and pin its comment case 2026-09-18 15:38:24 +08:00
Chinesezjc 9930a1da95 fix(web): keep a comment out of the verb delimiter branch 2026-09-18 15:23:16 +08:00
Chinesezjc 6c88876148 Merge remote-tracking branch 'origin/master' into fix/multiline-display-math 2026-09-18 15:17:13 +08:00
Chinesezjc c9c836ade1 fix(web): track the failed fence second marker and align the note 2026-09-18 15:07:24 +08:00
Chinesezjc d53bbe98fe fix(web): scope the verb delimiter tail to its line and unescaped backslash 2026-09-18 15:04:57 +08:00
Chinesezjc eeaac47630 fix(web): keep a lone dollar on a later line inside the block 2026-09-18 14:49:32 +08:00
Chinesezjc 6a2692c06b fix(web): type the consumed-tail helper for the parser code unit 2026-09-18 14:45:56 +08:00
Chinesezjc 33a6a06e17 fix(web): read the fence after verb as its delimiter
A `$$` right after `\verb` or `\verb*` is that command delimiter, so it no
longer closes the block: `$$\n\verb$$\nx\n$$` keeps the whole formula and the
paragraph after it, as the grammar this replaced did. The tokenizer also drops
a dead null guard and the blank line the earlier removal left behind.

Tests cover the two delimiter spellings, a failed fence inside a comment, and
a trailing lone dollar; coverage stays at 100% on both changed files.
2026-09-18 14:44:52 +08:00
Chinesezjc b0aac9fbad test(web): assert the surviving formula in the bare-percent case 2026-09-18 14:25:53 +08:00
Chinesezjc 072e3465f1 refactor(web): drop the hand-rolled verbatim scan from the dollar flow
The `\verb` state machine added for a `%` inside verbatim regions produced its
own defect class: its lookahead rescanned the line for every unterminated
candidate (58 KB of them took ~2 s), a `%` taken as a candidate delimiter lost
its comment state on the fallback, and control words, bare letters, and the
starred delimiter each needed a rule. It is gone; the comment gate alone keeps
`$$\verb|%|$$` and the rest of a 17-case parity set at base behavior.

What replaces it is the single-line rule the flow needs anyway: a `$$` that
failed to close continues scanning while it is still on the opening line, so a
later `$$` there still closes (`$$\verb*a$$a$$`), and it ends the candidate at
that line's ending (or immediately, on a later line), so `$$x$$ trailing` and
the repeated-candidate inputs keep the behavior earlier rounds pinned.

Net: 12 fewer branches in the tokenizer, linear parser work, and 132 tests with
100% statements, branches, functions, and lines.
2026-09-18 14:24:01 +08:00
Chinesezjc 2a4e1bcbfb fix(web): let a whitespace-only percent comment keep its fence
A comment whose text before the closing fence is empty or whitespace
(`95%$$`, `95 %$$`) no longer suppresses it, so a multi-line block whose last
line ends that way keeps its whole formula instead of falling back to upstream
and dropping its first line. A comment holding a non-whitespace character still
suppresses the fence, which keeps `\frac{1}{ % keep $$` at base behavior.

The note records the whitespace rule and its one consequence: a fence behind a
whitespace-only comment closes there, leaving a following lone `$$` as its own
empty block.
2026-09-18 14:06:04 +08:00
Chinesezjc 5804a9e863 fix(web): keep a second percent and a starred delimiter honest
A `%` inside an already open comment is comment text, so it no longer resets
the comment's content flag: `$$\n% 100%$$\n$$` and a formula whose comment
holds `95%` before the fence keep scanning instead of closing one line early,
which restores base behavior for both.

The starred `\verb*` form repeats any character, letters included, so only the
plain form continues to reject a letter delimiter (`\verbatim`); `$$\verb*a$$a$$`
renders as base does. The Agent Note records the comment gate as a sixth
residual shape and lists the conditions the new cases pin.
2026-09-18 14:04:43 +08:00
Chinesezjc 3e0a5c3af5 fix(web): read a percent followed straight by the fence as content
A `%` whose comment text is empty (`95%$$`, or a multiline block whose last
line ends that way) no longer suppresses the closing fence, so the whole
formula survives instead of falling back to upstream and dropping its first
line. A `%` with text before the fence still suppresses it, which keeps
`\frac{1}{ % keep $$` and the no-space spelling at base behavior.

The verbatim scan now follows LaTeX's own reader: a letter cannot be the
delimiter, one `*` marks the starred form, the region must close before the
line ending or its characters stay ordinary content, and a comment holds no
commands. The Agent Note states which state belongs to which construct.
2026-09-18 13:48:29 +08:00
Chinesezjc d9b2e06b46 fix(web): scope the comment exception to a later line
A bare `%` before a same-line closing fence (`$$\text{acc} = 95%$$`) stopped the
block from closing, so a display formula degraded to inline math. The comment
exception now applies only to a closing fence on a later line of the block,
which is where a multi-line block can hide one; a block that opens and closes
on one line keeps closing where it did before this flow spanned lines.

`\verb|%|` keeps its content reading, and the two comment cases are one test
with the annotation assertion on the first source only.
2026-09-18 13:30:28 +08:00
Chinesezjc 14a59d871e fix(web): keep a verbatim percent out of the comment state
`\verb|%|` inside a display formula made the comment tracking treat the `%` as
a TeX comment, so the block skipped its closing `$$`, swallowed the following
paragraph into a KaTeX error, and split the next formula. KaTeX renders
`\verb`, so this was a regression against base.

The value scanner now recognizes the `\verb` prefix and consumes its region
without touching the comment or escape state: `$$\verb|%|$$` closes where base
closes. The note records the verbatim rule and drops the numeric case counts
the earlier revision stated.
2026-09-18 13:29:03 +08:00
Chinesezjc 13ee2f421f fix(web): keep a TeX comment from closing a dollar block
An unescaped `%` inside a multi-line formula starts a TeX comment that runs to
the line ending, but the closing-fence check read a `$$` at the end of one as a
fence: `$$\n\frac{1}{ % keep $$\n2}\n$$` closed after the comment, produced a
KaTeX error for the truncated formula, and swallowed the paragraph after it
into a second block. The block now tracks the comment and ignores a fence
inside it, so base and this branch render the same value and keep the
following paragraph.

The Agent Note records the comment rule, states that every multi-line block
this flow owns is new (not only the ones on their own paragraph), and lists
the comment guard with the other seven.
2026-09-18 13:13:15 +08:00
Chinesezjc 6d538a9e4b docs(web): mirror the dollar note and record its fifth residual shape
The Chinese Agent Note had lost the bounded-work paragraph that the English
one keeps, so both now state it in the same place and the pair is 62 lines
each. Consequences also records the fifth shape the review found: a multi-line
block inside a container whose continuation line is de-indented gives upstream
the fence meta and an empty value, so the opening line disappears behind an
empty display block; keeping the continuation's indentation or moving the
block out of the list renders it. The Testing section now separates the two
cases that pin new behavior from the seven that pin behavior this change must
not alter, one condition each.
2026-09-18 13:01:44 +08:00
Chinesezjc 0913c023bb fix(web): end the dollar escape run at a line ending
A candidate line ending in an odd backslash kept the escape run alive across
the line ending and any blank lines after it, so the next line's leading `$$`
counted as escaped content and bypassed the closing-fence check. Base kept the
later block as its own display formula; this branch swallowed it into one
KaTeX error block, and repeating the pair made the parse quadratic (12.8 KB
took 1.4 s, 25.6 KB took 8.2 s).

The escape run now ends where the line ending is consumed, which is what a
trailing backslash escapes. The pair parses as base does and repeated
candidates stay linear (25.6 KB in ~76 ms).

A block whose opening line carries a solitary `$` and that must interrupt a
paragraph is unchanged from the grammar this replaces: both flows decline the
interruption, so the block stays in the paragraph. The Agent Note records that
fourth shape with its workaround, and its Testing section now states each
guard's condition instead of the revision that produced it.
2026-09-18 12:43:27 +08:00
Chinesezjc 760ad03842 fix(web): keep solitary dollars and interrupting paragraphs intact
Round two of review found the previous revision's decline rule too broad and
its interruption guard too eager:

- A solitary `$` inside a formula is content, not a failed closing fence, so
  `$$P = \begin{cases} 1 & \text{if $x>0$} \end{cases}$$` and
  `$$\text{a $x$ b}\tag{1}$$` stay display blocks. Only a full `$$` sequence
  that the line does not end after makes the flow decline the candidate.
- The dollar flow's interruption check now declines instead of claiming the
  interruption. Claiming it split `**before` / `$$a\$b` / `after**` into two
  paragraphs and exposed its `**` markers, because the candidate it claimed
  for then failed. Declining leaves the decision to upstream's own check,
  which short-circuits without consuming and claims only accepted fences.
- The guard stays scoped to the dollar flow: an unconditional decline dropped
  the multi-line `\[` blocks (`Prose line` followed by `\[`) that upstream has
  no counterpart for.

Guards cover the solitary dollar, the tagged formula, the escaped-dollar
paragraph, and the multi-line backslash block; the first three fail against
the previous revision.
2026-09-18 11:55:37 +08:00
Chinesezjc 14ead62052 fix(web): decline a dollar block whose closing fence carries text
Review of the multi-line change found three behaviors it had altered:

- `$$x$$ trailing\n\n$$y$$` scanned on from the failed same-line close and
  swallowed the second block's fence, rendering one broken math node where
  inline math, the prose, and the display block belong. A `$$` the flow
  cannot close now ends the attempt, as the single-line case already did.
- The paragraph-interruption guard sat in the shared tokenizer body, so an
  unclosed `\[` candidate (`**before\n\[literal]\nafter**`) split its
  paragraph and exposed the `**` markers. The guard applies to the dollar
  flow only, which is the one with a competing upstream construct.
- Repeated unclosed candidates (`$$a$b` per line) scanned to EOF from every
  line, making 9.6 KB of them take about four seconds in a synchronous
  parse. Declining the candidate on its opening line restores linear work
  (about 18 ms).

Each behavior gains a guard test that fails against the previous revision.
2026-09-17 23:31:34 +08:00
Chinesezjc f723892c98 fix(web): render multi-line display-dollar math
A `$$math` block whose opening line carries math and whose closing `$$` sits
at the end of the last math line parsed as an unclosed flow:
micromark-extension-math reads the opening line's text as fence meta and
accepts a closing fence only on a line whose content is exactly `$$`. The
flow then consumed the following document, and KaTeX rendered the block plus
that swallowed text as one error span. Fence meta was also silently dropped
because the renderer reads only the math node's value.

The compatibility layer's dollar flow now owns every `$$` fence, spans
multiple lines, and stops its paragraph-interruption check before the line
ending, as the upstream flow does; registering it after `math()` is what
gives it that precedence in micromark.
2026-09-17 22:49:01 +08:00
7685 changed files with 344162 additions and 697715 deletions

No files matched your search

@@ -0,0 +1,2 @@
2026-10-14-built-in-plugin-feature-previews.md: ae9b32340d9b1e77a99465445a05ccee05df0e79
2026-10-14-built-in-plugin-feature-previews.zh.md: 9229b0835a23a0eefffa1bd157efece333aebfa9
@@ -1,6 +1,7 @@
# Agent Note: Built-in plugin feature previews
Status: implemented
Archived: 2026-10-19
English | [中文](2026-10-14-built-in-plugin-feature-previews.zh.md)
@@ -1,6 +1,7 @@
# Agent Note: 内置插件功能配图
Status: implemented
Archived: 2026-10-19
[English](2026-10-14-built-in-plugin-feature-previews.md) | 中文
+6
View File
@@ -2020,6 +2020,9 @@
"feature/2026-09-28-desktop-login-shell-environment.i18n.yaml": "sha256:6129edeb0a7ecf087911193ca5a15223a730ad01e30d2ead7739d01f589fafa7",
"feature/2026-09-28-desktop-login-shell-environment.md": "sha256:cd133b1ef1a178828d30b9db176a91597eedf9e67802551356f34d3e0f80206d",
"feature/2026-09-28-desktop-login-shell-environment.zh.md": "sha256:1422667a44aacc51736f31b328a8e171a39cb6b5168d6993e980e4324b23b98f",
"feature/2026-10-14-built-in-plugin-feature-previews.i18n.yaml": "sha256:4769fd7fa4881a38fa8e2682b2ea62ed5a5ec6588e87f225c2b106e1bd308200",
"feature/2026-10-14-built-in-plugin-feature-previews.md": "sha256:093e00da4cebf732861c1e5c47465fb00359eb89a86dc212f3f90b43f9b80cec",
"feature/2026-10-14-built-in-plugin-feature-previews.zh.md": "sha256:56adb70945f68b073357c437bbec9e69f110587cf302291f2f135d9fcb5537e7",
"process/2026-06-11-doc-sync-enforcement.i18n.yaml": "sha256:33b6d5874427bd7a2bd82e7e2f4f482b12448b2464aef15a9c57975edb48554d",
"process/2026-06-11-doc-sync-enforcement.md": "sha256:aa2fe83d519fc30d48dff19e596e83c8922aacc9e063e14fe2cc35b769b9100e",
"process/2026-06-11-doc-sync-enforcement.zh.md": "sha256:698017bd35f030fdea3eac51df9e43138c48140f504739d687b7251d13fced2b",
@@ -2077,6 +2080,9 @@
"process/2026-07-06-generated-config-catalog.i18n.yaml": "sha256:48e64ccfc7622559a9b964e2741ad82a9fa37243a0d5c472f0482d9c748d7739",
"process/2026-07-06-generated-config-catalog.md": "sha256:c8654a4e084bfbf4f7d8eb78239dc1d5d62e1384b2aed309fa2526a4d6de0f04",
"process/2026-07-06-generated-config-catalog.zh.md": "sha256:5ab8d964f12e0cf9a04251e988f0b2a114d7ae8ea7ed337c00f625213cb1dc94",
"process/2026-07-06-node-engine-floor.i18n.yaml": "sha256:65d73bf7a6c2fb7609ff3a8f18a435e83bb488e005365d065d184942958bfb35",
"process/2026-07-06-node-engine-floor.md": "sha256:b62c8e8dd008b0ab7874b5d57c20c629ffb71e4b4a62922afaf6a1039d25898e",
"process/2026-07-06-node-engine-floor.zh.md": "sha256:3cd685e59f913ef700b3e2ed3b941c2266bb2de706810cbe570f4832a26a5034",
"process/2026-07-06-parallel-github-ci-gates.i18n.yaml": "sha256:0f6ece268d9a51bc20cb8eb929f26d8838761603a64eb08dc24521198f10da36",
"process/2026-07-06-parallel-github-ci-gates.md": "sha256:6249bd7396ae7f2d0dc671879ce21cefab33a47ace6ef17a25a70e8650b815af",
"process/2026-07-06-parallel-github-ci-gates.zh.md": "sha256:cf7edb9bcf97ab1d4e452330c0df0b127a664509ec3f11597ace3eabeb663a5b",
@@ -0,0 +1,3 @@
# Frozen bilingual archive consistency record: Git blob hashes after archive metadata insertion.
2026-07-06-node-engine-floor.md: 4e399b2a71bb4a76a28bc4b643a761ce65c55865
2026-07-06-node-engine-floor.zh.md: abd4913688cc4b3af02fa2f0b06d4b8206652d13
@@ -1,6 +1,7 @@
# Agent Note: Raise the Node LTS engine floor to 22.19
Status: implemented
Archived: 2026-10-15
English | [中文](2026-07-06-node-engine-floor.zh.md)
@@ -1,6 +1,7 @@
# Agent Note: 将 Node LTS 引擎下限提升至 22.19
Status: implemented
Archived: 2026-10-15
[English](2026-07-06-node-engine-floor.md) | 中文
@@ -9,8 +9,8 @@
en: c8b932b02a4e1c80
zh: 96c466be1a521f3f
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/decision:
en: b57eb0f268033ff1
zh: 4143382a50181414
en: 2e08d801099cbe18
zh: 8c5ab162ada9cc41
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/package-topology:
en: 5ea3ddfae82df181
zh: 2d05d95e51081696
@@ -18,14 +18,14 @@
en: b432f80a424c3e49
zh: faa26925fdd98387
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/provider-availability-and-selection:
en: dff78c46432311a1
zh: 58d5db4c3c85b886
en: 4e4ec068c2a22383
zh: b7030d0f00bfdb74
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/search-request-and-result-schema:
en: ec3703b7e7c6b23d
zh: 325755e6e90092da
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/fetch-request-and-result-schema:
en: f734fdc57a6546c9
zh: 027c28cbc584a8c2
en: 6dba3b9a51c28b6d
zh: 58a93f6ba2d269ea
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/tool-consumer-behavior:
en: 62127883453dd79c
zh: 01503a8177a0ddab
@@ -55,19 +55,16 @@
zh: 64032ea6ab61b836
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/alternatives-considered/mirror-claude-code-s-url-prompt-webfetch-shape:
en: 56a080de39e96056
zh: ae4d070aa7657503
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/alternatives-considered/validate-dns-and-then-call-an-ordinary-fetch:
en: aa6c1e7e6374a875
zh: 045c8af6cd44c3e1
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/alternatives-considered/block-private-looking-hostname-strings-without-pinning-resolved-addresses:
en: 3d906f3056733479
zh: d652723eb5372007
zh: 937336a73de70518
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/alternatives-considered/restrict-fetch-to-public-addresses:
en: af3014c0de7ff66f
zh: 509040c9183327fe
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/alternatives-considered/require-per-call-approval-before-public-fetches:
en: f8d1fc8e9c9659cf
zh: da5792520e35cba0
en: 3ea94ffb142d144a
zh: 89645f68412f6769
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/consequences:
en: 12fd20ba54818215
zh: c1ca000b13666701
en: 9774448fd6b21c41
zh: 8411c590ad70d5c4
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/deferred-work:
en: 48b7b8e7310cd56b
zh: d406cd6a8a649e58
@@ -32,7 +32,7 @@ Search and fetch are separate tools but one web-access seam. `ctx.web` owns prov
- A tool is never unregistered merely because its selected provider is missing, misconfigured, missing credentials, ambiguous, or temporarily unavailable.
- The provider is resolved at execution time, and a structured `WebError` is returned when the selected capability cannot run.
This keeps the model schema stable without making plugin load order, credential state, or HMR timing part of the model-facing contract. If web search is enabled but no usable search provider exists, `web_search` remains visible and execution fails with a structured `WebError` such as `WEB_PROVIDER_UNAVAILABLE` or `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`. If a provider appears after `dsh-tool-web`, the next execution can use it without changing the schema. If a provider disappears mid-call, execution fails with a structured `WebError` instead of silently choosing another provider or falling through to `UNKNOWN_TOOL`.
This keeps the model schema stable without making plugin load order, credential state, or HMR timing part of the model-facing contract. If web search is enabled but no usable search provider exists, `web_search` remains visible and execution fails with a structured `WebError` such as `WEB_PROVIDER_UNAVAILABLE` or `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`. `dsh-tool-web` narrows this per model request: it [withholds `web_search` while the selected provider reports that the request's search cannot authenticate](2026-09-27-web-search-offered-by-provider-readiness.md). If a provider appears after `dsh-tool-web`, the next execution can use it without changing the schema. If a provider disappears mid-call, execution fails with a structured `WebError` instead of silently choosing another provider or falling through to `UNKNOWN_TOOL`.
The seam deliberately exposes no observation surface — no registry-change event and no aggregated capability-status query. Unavailability is a fact a caller observes by executing: `search()`/`fetch()` resolve the provider at call time and throw the structured `WebError` that names what failed. [The observation-surface Agent Note](../../archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md) records that judgment: derived-on-call selection and enablement-based registration leave no consumer that needs a change signal or an availability probe distinct from executing and routing the error, and a future provider-status panel reintroduces the smallest signal or query it actually consumes.
@@ -60,6 +60,7 @@ flowchart LR
exa["@deepseek-ai/dsh-web-search-exa"] -->|registerSearchProvider| web["@deepseek-ai/dsh-web / ctx.web"]
perplexity["@deepseek-ai/dsh-web-search-perplexity"] -->|registerSearchProvider| web
deepseek["@deepseek-ai/dsh-web-search-deepseek"] -->|registerSearchProvider| web
deepseekApi["@deepseek-ai/dsh-web-deepseek-api"] -->|registerSearchProvider / registerFetchProvider| web
fetchLocal["@deepseek-ai/dsh-web-fetch-http"] -->|registerFetchProvider| web
toolWeb["@deepseek-ai/dsh-tool-web"] -->|search/fetch| web
toolWeb -->|ctx.tools.register| webSearch["tool: web_search"]
@@ -151,6 +152,8 @@ The "single provider auto-selects" rule is for tests, demos, and simple deployme
Operational overrides feed the same explicit selection path: `DSH_WEB_SEARCH_PROVIDER=perplexity` is equivalent to config `searchProvider: perplexity`, not a hidden priority chain inside `dsh-tool-web`.
Each search or fetch reads the configured id when it runs, so a saved settings change selects another provider for the next operation without remounting `dsh-web`. The Web search settings switch relies on this to move both capabilities to the [DeepSeek Search and Fetch APIs](../feature/2026-10-15-deepseek-search-fetch-api.md) at once.
`ctx.web.search()` and `ctx.web.fetch()` resolve the provider at execution time using the selection rules above. If the selected capability is unavailable, they throw `WebError` with a structured code such as `WEB_PROVIDER_UNAVAILABLE`, `WEB_PROVIDER_CONFIGURED_MISSING`, `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`, or `WEB_PROVIDER_AMBIGUOUS`. If no provider is explicitly configured and no usable provider exists, the execution error is the generic `WEB_PROVIDER_UNAVAILABLE` case; there is deliberately no diagnostic summary of every unavailable provider.
## Search request and result schema
@@ -199,7 +202,7 @@ Full page retrieval remains the job of `web_fetch(url)`. Search snippets are dis
## Fetch request and result schema
The `web_fetch` implementation is an anonymous public HTTP(S) fetch provider, `http`. It fetches bytes from a concrete URL, resolves and pins public destinations, applies the transport hygiene below, decodes textual content, and returns only the minimal model-useful result: final URL, status code, body, and truncation. It carries no browser cookies, editor credentials, git credentials, internal auth tokens, or implicit access to private services.
The `web_fetch` implementation is an anonymous HTTP(S) fetch provider, `http`. It fetches bytes from a concrete URL, applies the transport hygiene below, decodes textual content, and returns only the minimal model-useful result: final URL, status code, body, and truncation. It carries no browser cookies, editor credentials, git credentials, or internal auth tokens. It does not restrict destination addresses.
The seam request stays smaller than OpenCode's model-facing tool:
@@ -235,14 +238,12 @@ The provider owns safe resource retrieval: URL validation, HTTP transport, redir
The fetch provider's resource controls:
- Only `http:` and `https:` URLs are accepted; credentials in URLs are rejected.
- A literal address or the complete result of one hostname lookup must contain only globally reachable unicast IPv4 or IPv6 destinations. IPv6 resolution also discovers the active DNS64 prefix and rejects NAT64 addresses that translate to non-public IPv4. Loopback, private, link-local, carrier-grade NAT, multicast, reserved, transition, translation, and private IPv4-mapped IPv6 addresses are rejected.
- The request retains that validated address set in an Undici lookup callback instead of resolving the hostname again. The original hostname remains the HTTP Host and TLS SNI value, while DNS rebinding cannot replace the connection destination after validation.
- Maximum URL length, response byte cap, decoded body character cap, timeout, and redirect hop cap are enforced.
- Abort signals propagate through network fetches and expensive decoding.
- Only same-origin redirects are followed automatically; each followed hop performs a fresh public-address lookup and pins its own connection. A cross-origin redirect fails with `WEB_REDIRECT_BLOCKED`, requiring a fresh tool call and fresh public-address validation. (Claude Code's WebFetch uses this same model — it does not auto-follow a cross-host redirect; it returns the redirect target to the model for a fresh call.)
- Only same-origin redirects are followed automatically; each followed hop repeats URL validation. A cross-origin redirect fails with `WEB_REDIRECT_BLOCKED`, requiring a fresh tool call. (Claude Code's WebFetch uses this same model — it does not auto-follow a cross-host redirect; it returns the redirect target to the model for a fresh call.)
- Requests carry an explicit product user agent rather than silently impersonating a browser.
The provider rejects an entire DNS answer set when any address is not public instead of silently filtering the unsafe members. This fail-closed rule prevents connection-family selection or fallback from reaching an address that did not satisfy the public-network policy.
Requests use the process-wide dispatcher, so the installed outbound proxy policy and the system resolver decide the connection. Loopback, private-network, and cloud metadata destinations are reachable.
## Tool consumer behavior
@@ -310,17 +311,13 @@ Rejected for the first version. Those providers often return extracted or summar
Rejected for the seam. `prompt` turns fetch into LLM summarization and couples public-web retrieval to a model provider. The harness seam should fetch and decode deterministically; `dsh-tool-web` can later offer summaries as a presentation mode without making `ctx.web` depend on `ctx.llm`.
### Validate DNS and then call an ordinary fetch
### Restrict fetch to public addresses
Rejected because an ordinary fetch resolves the hostname again when it opens the connection. An attacker can return a public address during validation and a private address during the second lookup. Passing the validated answer set through the connection's lookup callback closes that rebinding interval while preserving hostname-based HTTP and TLS behavior.
### Block private-looking hostname strings without pinning resolved addresses
Rejected because hostname syntax does not establish the connection destination: an arbitrary public-looking name can resolve to loopback, a private range, or a cloud metadata address. Address classification belongs after resolution, and every address available to connection fallback must pass it.
Rejected. Resolving each hostname, rejecting any non-public answer, and pinning the connection to the validated set does not confine the model, which reaches the same networks through mounted shell tools, and it refuses every page on hosts whose DNS answers with non-public addresses, such as TUN-mode proxies with fake-IP DNS. Deployments that must keep the model away from private destinations need network confinement, a `tools/pre-execute` policy, or no fetch tool.
### Require per-call approval before public fetches
Rejected for the shipped presets. Public-address validation blocks SSRF destinations, while per-call confirmation would interrupt ordinary browsing without controlling public data egress reliably: a model can reach the same public network through mounted shell tools. Deployments that require a dedicated confirmation step can add a `tools/pre-execute` policy or disable `web_fetch`.
Rejected for the shipped presets. Per-call confirmation would interrupt ordinary browsing without controlling data egress reliably: a model can reach the same networks through mounted shell tools. Deployments that require a dedicated confirmation step can add a `tools/pre-execute` policy or disable `web_fetch`.
## Consequences
@@ -332,7 +329,7 @@ Rejected for the shipped presets. Public-address validation blocks SSRF destinat
**Provider state can change after startup.** A tool can be visible in the request assembled at step start and lose its provider before execution. The execution path resolves again and fails with a structured error.
**Fetch is a network boundary, not just a read-only tool.** Public-address validation and connection pinning prevent `web_fetch` from reaching non-public destinations, but a model can still disclose data through a public URL and fetched text remains untrusted model input. The shipped `cordis`, `code`, and `standard` presets expose `web_fetch` in every sandbox and approval mode without per-call confirmation.
**Fetch is a network boundary, not just a read-only tool.** `web_fetch` reaches any destination the host can, including loopback and private-network services, a model can disclose data through a URL, and fetched text remains untrusted model input. The shipped `cordis`, `code`, and `standard` presets expose `web_fetch` in every sandbox and approval mode without per-call confirmation.
**Large web content can damage context quality.** Providers enforce byte/character caps and report `truncated`; `tool-web` formats bounded model output with clear continuation or follow-up guidance.
@@ -32,7 +32,7 @@ Web 访问是一个一等能力 seam,遵循[能力 seam Agent Note](2026-06-13
- 工具绝不会仅仅因为其选定的提供方缺失、配置错误、缺少凭证、存在歧义或暂时不可用就被注销。
- 提供方在执行时解析,当选定的能力无法运行时返回结构化的 `WebError`。
这使模型 schema 保持稳定,而不将插件加载顺序、凭证状态或 HMR(热模块替换)时序纳入面向模型的约定。如果 web 搜索已启用但不存在可用的搜索提供方,`web_search` 仍然可见,执行时以结构化的 `WebError`(如 `WEB_PROVIDER_UNAVAILABLE` 或 `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`)失败。如果某个提供方在 `dsh-tool-web` 之后出现,下一次执行即可使用它而无需更改 schema。如果某个提供方在调用过程中消失,执行以结构化的 `WebError` 失败,而不是静默选择另一个提供方或回退到 `UNKNOWN_TOOL`。
这使模型 schema 保持稳定,而不将插件加载顺序、凭证状态或 HMR(热模块替换)时序纳入面向模型的约定。如果 web 搜索已启用但不存在可用的搜索提供方,`web_search` 仍然可见,执行时以结构化的 `WebError`(如 `WEB_PROVIDER_UNAVAILABLE` 或 `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`)失败。发行版的 `dsh-tool-web` 按模型请求收窄了这一点:选中的提供方报告该请求的搜索无法鉴权时,它会[撤下 `web_search`](2026-09-27-web-search-offered-by-provider-readiness.zh.md)。如果某个提供方在 `dsh-tool-web` 之后出现,下一次执行即可使用它而无需更改 schema。如果某个提供方在调用过程中消失,执行以结构化的 `WebError` 失败,而不是静默选择另一个提供方或回退到 `UNKNOWN_TOOL`。
该 seam 刻意不暴露任何观察面——没有注册表变更事件,也没有聚合的能力状态查询。不可用性是调用方通过执行观察到的事实:`search()`/`fetch()` 在调用时解析提供方,并抛出命名了失败原因的结构化 `WebError`。[观察面 Agent Note](../../archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md) 记录了这一判断:基于调用的派生选择与基于启用的注册使得没有消费方需要变更信号或独立于执行和错误路由的可用性探测;未来的提供方状态面板会重新引入它实际消费的最小信号或查询。
@@ -60,6 +60,7 @@ flowchart LR
exa["@deepseek-ai/dsh-web-search-exa"] -->|registerSearchProvider| web["@deepseek-ai/dsh-web / ctx.web"]
perplexity["@deepseek-ai/dsh-web-search-perplexity"] -->|registerSearchProvider| web
deepseek["@deepseek-ai/dsh-web-search-deepseek"] -->|registerSearchProvider| web
deepseekApi["@deepseek-ai/dsh-web-deepseek-api"] -->|registerSearchProvider / registerFetchProvider| web
fetchLocal["@deepseek-ai/dsh-web-fetch-http"] -->|registerFetchProvider| web
toolWeb["@deepseek-ai/dsh-tool-web"] -->|search/fetch| web
toolWeb -->|ctx.tools.register| webSearch["tool: web_search"]
@@ -151,6 +152,8 @@ interface WebRuntime {
运维覆盖走同一条显式选择路径:`DSH_WEB_SEARCH_PROVIDER=perplexity` 等同于配置 `searchProvider: perplexity`,而非 `dsh-tool-web` 内部的隐式优先级链。
每次搜索或抓取在执行时读取配置的 id,因此保存的设置变更无需重新挂载 `dsh-web` 即对下一次操作选择另一个提供方。网页搜索设置页的开关借此把两种能力同时切换到 [DeepSeek Search 与 Fetch API](../feature/2026-10-15-deepseek-search-fetch-api.zh.md)。
`ctx.web.search()` 和 `ctx.web.fetch()` 在执行时按上述选择规则解析提供方。如果选定的能力不可用,它们抛出带有结构化代码的 `WebError`,如 `WEB_PROVIDER_UNAVAILABLE`、`WEB_PROVIDER_CONFIGURED_MISSING`、`WEB_PROVIDER_CONFIGURED_UNAVAILABLE` 或 `WEB_PROVIDER_AMBIGUOUS`。如果未显式配置提供方且不存在可用提供方,执行错误是通用的 `WEB_PROVIDER_UNAVAILABLE` 情况;刻意不提供对每个不可用提供方的诊断汇总。
## 搜索请求与结果 schema
@@ -199,7 +202,7 @@ Exa 搜索将提供方扁平 `results[]` 的每一项映射为 `WebSearchSource`
## Fetch 请求与结果 schema
`web_fetch` 的实现是一个匿名公开 HTTP(S) fetch 提供方 `http`。它从具体 URL 获取字节,解析并固定公开目的地址,应用下述传输卫生措施,解码文本内容,并仅返回最小的模型可用结果:最终 URL、状态码、正文和截断标志。它不携带浏览器 cookie、编辑器凭证、git 凭证、内部认证令牌,也不隐式访问私有服务。
`web_fetch` 的实现是一个匿名 HTTP(S) fetch 提供方 `http`。它从具体 URL 获取字节,应用下述传输卫生措施,解码文本内容,并仅返回最小的模型可用结果:最终 URL、状态码、正文和截断标志。它不携带浏览器 cookie、编辑器凭证、git 凭证或内部认证令牌。它不限制目的地址。
seam 请求比 OpenCode 的面向模型工具更小:
@@ -235,14 +238,12 @@ export type WebFetchBody =
fetch 提供方的资源控制:
- 仅接受 `http:` 和 `https:` URL;拒绝 URL 中的凭证。
- 字面 IP 地址或 hostname 一次解析得到的完整结果只能包含全球可达的单播 IPv4 或 IPv6 目的地址。IPv6 解析还会发现当前 DNS64 前缀,并拒绝转换到非公开 IPv4 的 NAT64 地址。loopback、私有、link-local、运营商级 NAT、多播、保留、过渡、转换和映射到私有 IPv4 的 IPv6 地址都会被拒绝。
- 请求通过 Undici lookup 回调保留这一组已验证地址,不会再次解析 hostname。原 hostname 仍作为 HTTP Host 与 TLS SNI 值,而 DNS rebinding 无法在验证后替换连接目的地址。
- 强制执行最大 URL 长度、响应字节上限、解码正文字符上限、超时和重定向跳数上限。
- Abort 信号传播到网络获取和高开销解码。
- 仅自动跟随同源重定向;每个跟随的跳转都会重新解析公开地址,并把自己的连接固定到解析结果。跨源重定向以 `WEB_REDIRECT_BLOCKED` 失败,要求一次新的工具调用和新的公开地址校验。(Claude Code 的 WebFetch 使用同样的模型——它不自动跟随跨主机重定向,而是将重定向目标返回给模型以发起新调用。)
- 仅自动跟随同源重定向;每个跟随的跳转都会重复 URL 校验。跨源重定向以 `WEB_REDIRECT_BLOCKED` 失败,要求一次新的工具调用。(Claude Code 的 WebFetch 使用同样的模型——它不自动跟随跨主机重定向,而是将重定向目标返回给模型以发起新调用。)
- 请求携带显式的产品 User-Agent,而非静默伪装浏览器。
只要 DNS 完整解析结果中存在任一非公开地址,提供方就会拒绝整个结果,而不是静默过滤不安全成员。该 fail-closed 规则可防止连接的地址族选择或回退触及未满足公开网络策略的地址。
请求使用进程级 dispatcher,因此由已安装的出站代理策略与系统解析器决定连接。loopback、内网与云 metadata 目的地址都可访问。
## 工具消费方行为
@@ -310,17 +311,14 @@ fetch 提供方的资源控制:
在 seam 层面否决。`prompt` 将 fetch 变成 LLM 摘要,并将公开 web 获取耦合到模型提供方。harness seam 应当确定性地获取和解码;`dsh-tool-web` 日后可以将摘要作为展示模式提供,而无需让 `ctx.web` 依赖 `ctx.llm`。
### 验证 DNS 后调用普通 fetch
<a id="restrict-fetch-to-public-addresses"></a>
### 只允许抓取公开地址
否决,因为普通 fetch 在打开连接时会再次解析 hostname。攻击者可以在验证时返回公开地址,在第二次解析时返回私有地址。把已验证解析结果通过连接的 lookup 回调传入,可以在保留基于 hostname 的 HTTP 与 TLS 行为的同时关闭这一 rebinding 时间窗口。
### 只阻断看起来像私网的 hostname 字符串,不固定解析地址
否决,因为 hostname 语法无法确定连接目的地址:任意看似公开的名称都可能解析到 loopback、私有网段或云 metadata 地址。地址分类必须在解析后执行,连接回退可使用的每个地址都必须通过校验。
否决。解析每个 hostname、拒绝任何非公开解析结果并把连接固定到已验证地址,并不能约束模型,因为模型可以通过已挂载的 shell 工具访问相同网络;同时在 DNS 返回非公开地址的主机上(例如使用 fake-IP DNS 的 TUN 模式代理),这会拒绝所有页面。需要阻止模型访问私有目的地址的部署应使用网络隔离、`tools/pre-execute` 策略,或不挂载抓取工具。
### 在公开抓取前要求逐次审批
已交付的 preset 不采用这一方案。公开地址校验会阻断 SSRF 目的地址,而逐次确认会打断普通浏览,却不能可靠控制公开数据出站:模型可以通过已挂载的 shell 工具访问同一公开网络。要求专门确认步骤的部署可以添加 `tools/pre-execute` 策略或禁用 `web_fetch`。
已交付的 preset 不采用这一方案。逐次确认会打断普通浏览,却不能可靠控制数据出站:模型可以通过已挂载的 shell 工具访问相同网络。要求专门确认步骤的部署可以添加 `tools/pre-execute` 策略或禁用 `web_fetch`。
## 后果
@@ -332,7 +330,7 @@ fetch 提供方的资源控制:
**提供方状态可能在启动后变化。** 一个工具可能在步骤开始时组装的请求中可见,但在执行前失去其提供方。执行路径重新解析并以结构化错误失败。
**Fetch 是网络边界,不仅仅是只读工具。** 公开地址校验与连接固定可防止 `web_fetch` 触达非公开目的地址,但模型仍可通过公开 URL 泄露数据,抓取文本也仍是不受信任的模型输入。已交付的 `cordis`、`code` 与 `standard` preset 会在所有 sandbox 和审批模式下暴露 `web_fetch`,无需逐次确认。
**Fetch 是网络边界,不仅仅是只读工具。** `web_fetch` 可以访问主机能访问的任何目的地址,包括 loopback 与内网服务;模型可以通过 URL 泄露数据,抓取文本也仍是不受信任的模型输入。已交付的 `cordis`、`code` 与 `standard` preset 会在所有 sandbox 和审批模式下暴露 `web_fetch`,无需逐次确认。
**大量 web 内容可能损害上下文质量。** 提供方强制执行字节/字符上限并报告 `truncated`;`tool-web` 格式化有界的模型输出,附带清晰的继续或后续引导。
@@ -21,11 +21,11 @@
en: 985e0eb63f3d1fd6
zh: 337cc44fadc6227e
/agent-note-single-file-executable-sdk-runtime-distribution-single-exe/decision/build-pipeline-and-artifacts:
en: 921085cb9d894688
zh: 12d99fe153d36738
en: 9a4ba22142a7be24
zh: 8553b02a923641a3
/agent-note-single-file-executable-sdk-runtime-distribution-single-exe/decision/python-sdk-distribution-two-carriers-exe-for-production-node-for-development:
en: 02e6b36f9f92c83a
zh: edd8b2db7a787483
en: b2a740a1ab7a5002
zh: a48be6ca6fc65912
/agent-note-single-file-executable-sdk-runtime-distribution-single-exe/decision/naming-lineage:
en: 31a5aecb8bf828bb
zh: 2dffb6fd2e0f8194
@@ -48,13 +48,13 @@ The [Office kit](2026-09-14-independent-libreoffice-kit.md) and its installed de
### Build pipeline and artifacts
[`scripts/build-exe-for-python-sdk.ts`](../../../../scripts/build-exe-for-python-sdk.ts): runtime closure verification → `pnpm run build` → (after clearing) `pnpm --filter dsh-python-runtime-closure deploy --legacy --prod --config.allow-unused-patches=true --config.node-linker=hoisted --config.auto-install-peers=false --config.link-workspace-packages=true --config.hoist-workspace-packages=false` **directly into** `python/sdk-runtime/src/deepseek_harness_runtime/runtime/node/`, including [`python/sdk-runtime/runtime-bootstrap.mjs`](../../../../python/sdk-runtime/runtime-bootstrap.mjs) as the carrier-root `runtime-bootstrap.mjs` → restore direct workspace packages omitted by legacy deploy and reject any remaining manifest gap → replace staged dependency symlinks with their target bytes, remove package-manager `.bin` links, and fail if any symlink remains → verify the deployed bootstrap and inject pkg configuration with that bin plus assets covering dynamic profile, bundle, frontend, preset, native-library, and configuration reads → stage the target `node-pty` addon → invoke `pkg --sea` once per target → write `deepseek-harness-sdk-runtime-<platform>-<arch>` under `dist-exe/` and copy it with its lightweight resource manifest and Office skill assets into the runtime directory. The Python runtime owns that bootstrap. It calls the public CLI export for ordinary launches and dispatches a provider-private selection to the same `@deepseek-ai/dsh-subprocess-local/runner` core without changing CLI grammar or adding another executable; the [native-containment reference](../../../../packages/subprocess/subprocess-local/README.md) owns that private path. Linux CI rebuilds `pty.node` inside the matching manylinux 2.28 container because legacy deploy omits that install side effect. Every target copies its native `@vscode/ripgrep` binary beside the executable as the required `-rg` sidecar; pkg runtimes select that sidecar through `process.pkg`, while ordinary Node execution uses `@vscode/ripgrep` directly. macOS uses its target prebuild and also emits the required `-spawn-helper`. The deploy flags preserve the measured runtime layout: `--legacy` is the mandatory path with inject-workspace-packages off; hoisted supplies package directories that staging makes symlink-free; staging removes nested workspace copies only when pnpm records the same dependency identity at the payload root, preserving singleton services; disabling workspace hoisting excludes unrelated workspace source packages; disabling automatic peer installation prevents undeclared peers from expanding the closure; link-workspace-packages selects direct workspace dependencies. [`pnpm-workspace.yaml`](../../../../pnpm-workspace.yaml) overrides the transitive `@deepseek-ai/cosmokit` and `@deepseek-ai/schemastery` semver requests to the pinned vendor sources so legacy deploy never resolves those unpublished names from a registry. Production deployment permits unused workspace patches because development-only tools can be absent from its closure; included-package patch failures still fail deployment. Workspace installs retain strict unused-patch validation.
[`scripts/build-exe-for-python-sdk.ts`](../../../../scripts/build-exe-for-python-sdk.ts): runtime closure verification → `pnpm run build` → (after clearing) `pnpm --filter dsh-python-runtime-closure deploy --legacy --prod --config.allow-unused-patches=true --config.node-linker=hoisted --config.auto-install-peers=false --config.link-workspace-packages=true --config.hoist-workspace-packages=false` **directly into** `python/sdk-runtime/src/deepseek_harness_runtime/runtime/node/`, including [`python/sdk-runtime/runtime-bootstrap.mjs`](../../../../python/sdk-runtime/runtime-bootstrap.mjs) as the carrier-root `runtime-bootstrap.mjs` → restore direct workspace packages omitted by legacy deploy and reject any remaining manifest gap → replace staged dependency symlinks with their target bytes, remove package-manager `.bin` links, and fail if any symlink remains → verify the deployed bootstrap and inject pkg configuration with that bin plus assets covering dynamic profile, bundle, frontend, preset, native-library, and configuration reads → stage the target `node-pty` addon → invoke `pkg --sea --compress Brotli` once per target, where the vendored pkg patch pins Brotli's quality to 6: pkg passes no options, so its default quality 11 measured 132.8 s against 3.2 s at quality 6 for an archive 12% larger → write `deepseek-harness-sdk-runtime-<platform>-<arch>` under `dist-exe/` and copy it with its lightweight resource manifest and Office skill assets into the runtime directory. The Python runtime owns that bootstrap. It calls the public CLI export for ordinary launches and dispatches a provider-private selection to the same `@deepseek-ai/dsh-subprocess-local/runner` core without changing CLI grammar or adding another executable; the [native-containment reference](../../../../packages/subprocess/subprocess-local/README.md) owns that private path. Linux CI rebuilds `pty.node` inside the matching manylinux 2.28 container because legacy deploy omits that install side effect. Every target copies its native `@vscode/ripgrep` binary beside the executable as the required `-rg` sidecar; pkg runtimes select that sidecar through `process.pkg`, while ordinary Node execution uses `@vscode/ripgrep` directly. macOS uses its target prebuild and also emits the required `-spawn-helper`. The deploy flags preserve the measured runtime layout: `--legacy` is the mandatory path with inject-workspace-packages off; hoisted supplies package directories that staging makes symlink-free; staging removes nested workspace copies only when pnpm records the same dependency identity at the payload root, preserving singleton services; disabling workspace hoisting excludes unrelated workspace source packages; disabling automatic peer installation prevents undeclared peers from expanding the closure; link-workspace-packages selects direct workspace dependencies. [`pnpm-workspace.yaml`](../../../../pnpm-workspace.yaml) overrides the transitive `@deepseek-ai/cosmokit` and `@deepseek-ai/schemastery` semver requests to the pinned vendor sources so legacy deploy never resolves those unpublished names from a registry. Production deployment permits unused workspace patches because development-only tools can be absent from its closure; included-package patch failures still fail deployment. Workspace installs retain strict unused-patch validation.
CI: [`.github/workflows/build-exe-for-python-sdk.yml`](../../../../.github/workflows/build-exe-for-python-sdk.yml) runs [installed-wheel validation](../testing/2026-08-23-installed-python-wheel-black-box-ci.md) on Linux/Windows x64 for pull requests and Linux ARM64 plus both macOS architectures for master pushes. The [public publication workflow](../../archived/process/2026-08-11-python-publication-workflow.md) calls it for all five targets; `workflow_dispatch` can still select a subset. Native builds run on linux-x64 / linux-arm64 (`ubuntu-24.04-arm`) / macos-arm64 / macos-x64 (`macos-15-intel`) / win-x64 (`windows-2025`), with `~/.pkg-cache` cached where applicable, and pkg handles macOS ad-hoc signing. Each leg installs the release-shaped SDK and runtime wheels into a clean venv outside the checkout, verifies their package and executable origin, then drives the complete keyless scenario set through the public SDK and direct NDJSON JSON-RPC. Trusted pull requests and master pushes additionally run a real DeepSeek two-turn tool smoke on their selected targets; fork and Dependabot heads receive no key. Linux inspects the executable and native addon's GLIBC requirements and runs an additional manylinux 2.28 smoke, while macOS checks the runtime, ripgrep, and PTY helper architectures and verifies that all three deployment targets fit the wheel tag. A full five-target run retains six artifacts, each containing one release file: the platform-independent SDK wheel and five native runtime wheels; a subset dispatch retains the SDK wheel and selected runtime wheels. Bare executables and source bundles remain intermediate test inputs. [`.gitlab-ci.yml`](../../../../.gitlab-ci.yml) accepts `python-v<repository-version>` tag pipelines whose version matches the root `package.json`, builds one SDK wheel and five native runtime wheels, then a single serialized job checks and publishes all six to the project PyPI registry. The [`python/sdk-runtime` README](../../../../python/sdk-runtime/README.md) owns the Windows target and the explicit exclusion of Windows arm64.
### Python SDK distribution: two carriers, exe for production, node for development
The Python SDK lives at [`python/`](../../../../python/README.md): `python/sdk` is the client and `python/sdk-runtime` is the runtime carrier package. The runtime package's data directory holds the build-injected platform executable with its required `-rg` sidecar, lightweight download metadata and Office skills, and optional macOS helper, plus the build-injected `runtime/node/` closure tree for repository development. `resolve_bundled_launch_args()` selects the executable by default; explicit `DSH_RUNTIME_MODE=node` runs `runtime/node/runtime-bootstrap.mjs` on system Node 22.19 or newer. The node carrier never enters wheel distributions, and neither carrier uses a checked-in complete `cordis.yml`.
The Python SDK lives at [`python/`](../../../../python/README.md): `python/sdk` is the client and `python/sdk-runtime` is the runtime carrier package. The runtime package's data directory holds the build-injected platform executable with its required `-rg` sidecar, lightweight download metadata and Office skills, and optional macOS helper, plus the build-injected `runtime/node/` closure tree for repository development. `resolve_bundled_launch_args()` selects the executable by default; explicit `DSH_RUNTIME_MODE=node` runs `runtime/node/runtime-bootstrap.mjs` on system Node 24.9 or newer. The node carrier never enters wheel distributions, and neither carrier uses a checked-in complete `cordis.yml`.
[`scripts/build-python-release.py`](../../../../scripts/build-python-release.py) reads the authoritative `X.Y.Z` or prerelease version from the repository root `package.json`, converts prereleases to their PEP 440 spelling, and stages both packages at that wheel version, with `deepseek-harness-sdk` depending exactly on the matching `deepseek-harness-runtime-bin`. An optional `python-v<repository-version>` release tag is a consistency assertion and is rejected when it differs from the repository version; the source `pyproject.toml` development sentinel never determines a release version. Staging also carries the repository license into both wheels and the third-party notices into the bundled runtime wheel. The SDK is a `py3-none-any` wheel; each wheel-only runtime package contains one exe, lightweight resource metadata and skills, and its architecture-matched ripgrep sidecar, and the macOS wheel also contains its architecture-matched spawn helper. Runtime wheels use `py3-none-manylinux_2_28_x86_64`, `py3-none-manylinux_2_28_aarch64`, `py3-none-macosx_14_0_arm64`, `py3-none-macosx_14_0_x86_64`, or `py3-none-win_amd64`; the Hatch hook rejects sdists, universal tags, mixed-platform payloads, missing or extra sidecars, and unsupported platforms. Both macOS tags deliberately declare a conservative 14.0 installation floor: the packaged Node 24 executables declare macOS 13.5, and the x64 PTY helper declares 10.7, but release validation proves the complete payload only against the 14.0 wheel claim rather than promising each observed component minimum as a supported host. The two macOS wheels remain architecture-specific; no universal2 wheel is published.
@@ -48,13 +48,13 @@ exe 的 VFS 内是**构建产物形态的真实包树**(各包的 `lib/` + 真
### 构建流水线与产物
[`scripts/build-exe-for-python-sdk.ts`](../../../../scripts/build-exe-for-python-sdk.ts):运行时闭包校验 → `pnpm run build` →(清空后)`pnpm --filter dsh-python-runtime-closure deploy --legacy --prod --config.allow-unused-patches=true --config.node-linker=hoisted --config.auto-install-peers=false --config.link-workspace-packages=true --config.hoist-workspace-packages=false` **直接写入** `python/sdk-runtime/src/deepseek_harness_runtime/runtime/node/`,其中包含作为载体根目录 `runtime-bootstrap.mjs` 的 [`python/sdk-runtime/runtime-bootstrap.mjs`](../../../../python/sdk-runtime/runtime-bootstrap.mjs) → 恢复 legacy deploy 遗漏的直接工作区包,并拒绝剩余的 manifest 缺口 → 将暂存依赖中的符号链接替换为目标文件内容,删除包管理器的 `.bin` 链接,并在仍有任何符号链接时失败 → 验证已部署的 bootstrap,并注入以该文件为 bin 的 pkg 配置及覆盖动态读取 profile、bundle、前端、preset、原生库与配置文件的 assets → 暂存目标平台的 `node-pty` addon → 每个构建目标调用一次 `pkg --sea` → 将 `deepseek-harness-sdk-runtime-<platform>-<arch>` 写入 `dist-exe/`,并将它与轻量资源清单及 Office skill 资源一同拷回运行时目录。该 bootstrap 由 Python runtime 拥有;普通启动时它调用公开 CLI export,而提供方私有选择会分派到同一个 `@deepseek-ai/dsh-subprocess-local/runner` 核心,不改变 CLI 语法,也不增加另一个可执行文件;[原生 containment 参考](../../../../packages/subprocess/subprocess-local/README.zh.md)负责这条私有路径。Linux CI 会在匹配的 manylinux 2.28 容器中重新构建 `pty.node`,因为 legacy deploy 会遗漏这一安装副作用。每个目标都会把对应的原生 `@vscode/ripgrep` 二进制复制到可执行文件旁,作为必需的 `-rg` 伴随文件;pkg 运行时通过 `process.pkg` 选择该伴随文件,普通 Node 执行则直接使用 `@vscode/ripgrep`。macOS 使用对应目标的预构建产物,并额外生成所需的 `-spawn-helper`。部署标志都有实测依据:未启用 `inject-workspace-packages` 时必须使用 `--legacy`;`hoisted` 提供包目录,再由暂存步骤消除符号链接;只有 pnpm 在载荷根目录记录了相同依赖标识时,暂存步骤才删除对应的嵌套工作区副本,以保留服务单例;关闭工作区提升可排除无关的工作区源码包;关闭对等依赖自动安装可防止未声明的对等依赖扩大闭包;`link-workspace-packages` 选择直接工作区依赖。[`pnpm-workspace.yaml`](../../../../pnpm-workspace.yaml) 将传递的 `@deepseek-ai/cosmokit` 与 `@deepseek-ai/schemastery` semver 请求覆盖到固定的 vendor 源码,使 legacy deploy 不会从注册表解析这些未发布名称。 生产部署允许未使用的工作区补丁,因为开发工具可能不属于运行时闭包;闭包内包的补丁应用失败仍会中止部署。工作区安装继续严格校验未使用的补丁。
[`scripts/build-exe-for-python-sdk.ts`](../../../../scripts/build-exe-for-python-sdk.ts):运行时闭包校验 → `pnpm run build` →(清空后)`pnpm --filter dsh-python-runtime-closure deploy --legacy --prod --config.allow-unused-patches=true --config.node-linker=hoisted --config.auto-install-peers=false --config.link-workspace-packages=true --config.hoist-workspace-packages=false` **直接写入** `python/sdk-runtime/src/deepseek_harness_runtime/runtime/node/`,其中包含作为载体根目录 `runtime-bootstrap.mjs` 的 [`python/sdk-runtime/runtime-bootstrap.mjs`](../../../../python/sdk-runtime/runtime-bootstrap.mjs) → 恢复 legacy deploy 遗漏的直接工作区包,并拒绝剩余的 manifest 缺口 → 将暂存依赖中的符号链接替换为目标文件内容,删除包管理器的 `.bin` 链接,并在仍有任何符号链接时失败 → 验证已部署的 bootstrap,并注入以该文件为 bin 的 pkg 配置及覆盖动态读取 profile、bundle、前端、preset、原生库与配置文件的 assets → 暂存目标平台的 `node-pty` addon → 每个构建目标调用一次 `pkg --sea --compress Brotli`,其中 vendor 的 pkg 补丁把 Brotli 质量固定为 6:pkg 不传参数时默认质量 11,同一载荷实测 132.8 s,质量 6 为 3.2 s,产物大 12% → 将 `deepseek-harness-sdk-runtime-<platform>-<arch>` 写入 `dist-exe/`,并将它与轻量资源清单及 Office skill 资源一同拷回运行时目录。该 bootstrap 由 Python runtime 拥有;普通启动时它调用公开 CLI export,而提供方私有选择会分派到同一个 `@deepseek-ai/dsh-subprocess-local/runner` 核心,不改变 CLI 语法,也不增加另一个可执行文件;[原生 containment 参考](../../../../packages/subprocess/subprocess-local/README.zh.md)负责这条私有路径。Linux CI 会在匹配的 manylinux 2.28 容器中重新构建 `pty.node`,因为 legacy deploy 会遗漏这一安装副作用。每个目标都会把对应的原生 `@vscode/ripgrep` 二进制复制到可执行文件旁,作为必需的 `-rg` 伴随文件;pkg 运行时通过 `process.pkg` 选择该伴随文件,普通 Node 执行则直接使用 `@vscode/ripgrep`。macOS 使用对应目标的预构建产物,并额外生成所需的 `-spawn-helper`。部署标志都有实测依据:未启用 `inject-workspace-packages` 时必须使用 `--legacy`;`hoisted` 提供包目录,再由暂存步骤消除符号链接;只有 pnpm 在载荷根目录记录了相同依赖标识时,暂存步骤才删除对应的嵌套工作区副本,以保留服务单例;关闭工作区提升可排除无关的工作区源码包;关闭对等依赖自动安装可防止未声明的对等依赖扩大闭包;`link-workspace-packages` 选择直接工作区依赖。[`pnpm-workspace.yaml`](../../../../pnpm-workspace.yaml) 将传递的 `@deepseek-ai/cosmokit` 与 `@deepseek-ai/schemastery` semver 请求覆盖到固定的 vendor 源码,使 legacy deploy 不会从注册表解析这些未发布名称。 生产部署允许未使用的工作区补丁,因为开发工具可能不属于运行时闭包;闭包内包的补丁应用失败仍会中止部署。工作区安装继续严格校验未使用的补丁。
CI 使用 [`.github/workflows/build-exe-for-python-sdk.yml`](../../../../.github/workflows/build-exe-for-python-sdk.yml):[安装后 wheel 包验证](../testing/2026-08-23-installed-python-wheel-black-box-ci.zh.md)在拉取请求上运行 Linux/Windows x64,在 master 推送上运行 Linux ARM64 与两种 macOS 架构。[公开发布工作流](../../archived/process/2026-08-11-python-publication-workflow.md)调用它构建全部五个目标;`workflow_dispatch` 仍可选择部分目标。linux-x64、linux-arm64(`ubuntu-24.04-arm`)、macos-arm64、macos-x64(`macos-15-intel`)与 win-x64(`windows-2025`)分别进行原生构建,并在适用平台缓存 `~/.pkg-cache`;macOS 的 ad-hoc 签名由 pkg 处理。每个平台都把发布形态的 SDK wheel 包与运行时 wheel 包安装到 checkout 外的干净 venv,证明包与可执行文件来源,再通过公开 SDK 与直接 NDJSON JSON-RPC 运行完整 keyless 场景。可信拉取请求与 master 推送还会在各自选定的目标上运行真实 DeepSeek 双轮工具冒烟测试;fork 与 Dependabot head 不会获得密钥。Linux 会检查可执行文件和原生 addon 各自的 GLIBC 依赖,并额外运行 manylinux 2.28 冒烟测试;macOS 则检查 runtime、ripgrep 与 PTY helper 的架构,并验证三个载荷的部署目标都符合 wheel 包标签。完整构建五个目标时保留 6 个产物,每个产物只含一个发布文件:平台无关的 SDK wheel 包与 5 个原生运行时 wheel 包;手动选择部分目标时保留 SDK wheel 与所选运行时 wheel。裸 exe 与源码包只作为测试中间输入。[`.gitlab-ci.yml`](../../../../.gitlab-ci.yml) 只接受版本与根目录 `package.json` 匹配的 `python-v<repository-version>` 标签流水线,构建一个 SDK wheel 包和 5 个原生运行时 wheel 包,再由单个串行任务校验并将这 6 个文件发布到项目的 PyPI 注册表。[`python/sdk-runtime` README](../../../../python/sdk-runtime/README.zh.md)负责 Windows 目标及对 Windows arm64 的明确排除。
### Python SDK 分发:双载体,exe 用于生产,`node` 用于开发
Python SDK 位于 [`python/`](../../../../python/README.zh.md):`python/sdk` 是客户端,`python/sdk-runtime` 是运行时载体包。运行时包的数据目录包含构建注入的平台可执行文件及其必需的 `-rg` 伴随文件、轻量下载元数据与 Office skills 和可选的 macOS helper,以及供仓库开发使用的构建注入 `runtime/node/` 闭包树。`resolve_bundled_launch_args()` 默认选择可执行文件;显式设置 `DSH_RUNTIME_MODE=node` 会在系统 Node 22.19 或更高版本上运行 `runtime/node/runtime-bootstrap.mjs`。node 载体从不进入 wheel 分发,两种载体都不使用检入的完整 `cordis.yml`。
Python SDK 位于 [`python/`](../../../../python/README.zh.md):`python/sdk` 是客户端,`python/sdk-runtime` 是运行时载体包。运行时包的数据目录包含构建注入的平台可执行文件及其必需的 `-rg` 伴随文件、轻量下载元数据与 Office skills 和可选的 macOS helper,以及供仓库开发使用的构建注入 `runtime/node/` 闭包树。`resolve_bundled_launch_args()` 默认选择可执行文件;显式设置 `DSH_RUNTIME_MODE=node` 会在系统 Node 24.9 或更高版本上运行 `runtime/node/runtime-bootstrap.mjs`。node 载体从不进入 wheel 分发,两种载体都不使用检入的完整 `cordis.yml`。
[`scripts/build-python-release.py`](../../../../scripts/build-python-release.py) 从仓库根目录的 `package.json` 读取权威的 `X.Y.Z` 或预发布版本,把预发布版本转换为 PEP 440 写法,并以该 wheel 包版本暂存两个包,让 `deepseek-harness-sdk` 精确依赖匹配版本的 `deepseek-harness-runtime-bin`。可选的 `python-v<repository-version>` 发布标签只是一项一致性断言,与仓库版本不同时会被拒绝;源码 `pyproject.toml` 中的开发占位版本从不决定发布版本。暂存过程还会把仓库许可证放入两个 wheel 包,并把第三方声明放入内置运行时 wheel 包。SDK 是 `py3-none-any` wheel 包;每个只提供 wheel 包的运行时包都包含一个 exe、轻量资源元数据与 skills 及架构匹配的 ripgrep 伴随文件,macOS wheel 包还包含与其架构匹配的 spawn helper。运行时 wheel 包使用 `py3-none-manylinux_2_28_x86_64`、`py3-none-manylinux_2_28_aarch64`、`py3-none-macosx_14_0_arm64`、`py3-none-macosx_14_0_x86_64` 或 `py3-none-win_amd64`;Hatch 钩子拒绝 sdist、通用标签、混合平台载荷、伴随文件缺失或多余,以及不支持的平台。两个 macOS 标签都会特意声明保守的 14.0 安装下限:打包后的 Node 24 可执行文件声明 macOS 13.5,x64 PTY helper 声明 10.7,但发布验证只按照 14.0 wheel 声明证明完整载荷,不会把各组件实测的最低版本承诺为受支持宿主。两个 macOS wheel 包仍按架构分别发布,不发布 universal2 wheel 包。
@@ -24,8 +24,8 @@
en: 26e4b71366bebd1b
zh: 10598e12b28c586e
/agent-note-cooperative-tool-cancellation-at-the-registry-boundary/verification:
en: fbd101d4576afa92
zh: 35f5d3c2f6cad6a8
en: 983da6d0c09f5c10
zh: ca7c86117a1a4d5f
/agent-note-cooperative-tool-cancellation-at-the-registry-boundary/alternatives-considered:
en: c4d86feb135d2ac4
zh: 67c56c0bc2213949
@@ -46,7 +46,7 @@ This decision requires cancellation at the tool invocation boundary only. Making
## Verification
[`execution-signal-types.spec.ts`](../../../../packages/core/tools/tests/execution-signal-types.spec.ts) proves the required exact signal types, readonly observer and tool views, mutable-but-required around-dispatch view, and `defineTool()` inference. [`tools.spec.ts`](../../../../packages/core/tools/tests/tools.spec.ts) covers pre-aborted materialization, phase skipping, policy and wrapper races, body invocation classification, caller-signal fusion, error precedence, context retention, and quiescent drainage. [`tool-calls.spec.ts`](../../../../packages/core/agent-loop/tests/tool-calls.spec.ts) and [`contract-regressions.spec.ts`](../../../../packages/core/agent-loop/tests/contract-regressions.spec.ts) cover balanced durable results for undispatched siblings. [`ptc.spec.ts`](../../../../packages/core/tools/tests/ptc.spec.ts) and first-party integration suites cover explicit forwarding, while [`timeout-policy.spec.ts`](../../../../packages/guard/timeout-policy/tests/timeout-policy.spec.ts) preserves timeout ownership.
[`execution-signal-types.spec.ts`](../../../../packages/core/tools/tests/execution-signal-types.spec.ts) proves the required exact signal types, readonly observer and tool views, mutable-but-required around-dispatch view, and `defineTool()` inference. [`tools.spec.ts`](../../../../packages/core/tools/tests/tools.spec.ts) covers pre-aborted materialization, phase skipping, policy and wrapper races, body invocation classification, caller-signal fusion, error precedence, context retention, and quiescent drainage. [`tool-calls.spec.ts`](../../../../packages/core/agent-loop/tests/tool-calls.spec.ts) and [`contract-regressions.spec.ts`](../../../../packages/core/agent-loop/tests/contract-regressions.spec.ts) cover balanced durable results for undispatched siblings. [`ptc.spec.ts`](../../../../packages/core/tool-executor-ptc/tests/ptc.spec.ts) and first-party integration suites cover explicit forwarding, while [`timeout-policy.spec.ts`](../../../../packages/guard/timeout-policy/tests/timeout-policy.spec.ts) preserves timeout ownership.
No registry test can prove that arbitrary third-party same-process code observes the signal or stops in bounded time. Capability tests continue to prove cancellation and quiescence at the boundary that owns each side effect.
@@ -46,7 +46,7 @@ Status: implemented
## 验证
[`execution-signal-types.spec.ts`](../../../../packages/core/tools/tests/execution-signal-types.spec.ts) 证明必填的精确信号类型、观察者与工具的只读视图、环绕调度可替换但不可删除的视图,以及 `defineTool()` 推断。[`tools.spec.ts`](../../../../packages/core/tools/tests/tools.spec.ts) 覆盖进入时已中止的物化与阶段跳过、策略和包装层竞态、工具主体调用分类、调用方信号融合、错误优先级、上下文保留和完全停稳。[`tool-calls.spec.ts`](../../../../packages/core/agent-loop/tests/tool-calls.spec.ts) 与 [`contract-regressions.spec.ts`](../../../../packages/core/agent-loop/tests/contract-regressions.spec.ts) 覆盖为未调度的同批调用补齐持久化结果。[`ptc.spec.ts`](../../../../packages/core/tools/tests/ptc.spec.ts) 和第一方集成测试覆盖显式转发,[`timeout-policy.spec.ts`](../../../../packages/guard/timeout-policy/tests/timeout-policy.spec.ts) 保持超时归属。
[`execution-signal-types.spec.ts`](../../../../packages/core/tools/tests/execution-signal-types.spec.ts) 证明必填的精确信号类型、观察者与工具的只读视图、环绕调度可替换但不可删除的视图,以及 `defineTool()` 推断。[`tools.spec.ts`](../../../../packages/core/tools/tests/tools.spec.ts) 覆盖进入时已中止的物化与阶段跳过、策略和包装层竞态、工具主体调用分类、调用方信号融合、错误优先级、上下文保留和完全停稳。[`tool-calls.spec.ts`](../../../../packages/core/agent-loop/tests/tool-calls.spec.ts) 与 [`contract-regressions.spec.ts`](../../../../packages/core/agent-loop/tests/contract-regressions.spec.ts) 覆盖为未调度的同批调用补齐持久化结果。[`ptc.spec.ts`](../../../../packages/core/tool-executor-ptc/tests/ptc.spec.ts) 和第一方集成测试覆盖显式转发,[`timeout-policy.spec.ts`](../../../../packages/guard/timeout-policy/tests/timeout-policy.spec.ts) 保持超时归属。
任何注册表测试都无法证明任意第三方同进程代码会观察信号或在有界时间内停止。各能力的测试仍需在拥有相应副作用的边界证明取消与完全停稳。
@@ -21,20 +21,20 @@
en: 0a4240d88cca240a
zh: 8f5f1527caf571cd
/agent-note-client-plugin-loading-lazy-factories-cordis-lifecycle-and-hot-reload/decision/the-loading-flow-end-to-end:
en: 7dac236d6add3c3c
zh: 2a43a923a0646bb0
en: 15fb56befd328339
zh: 0ffb10ada7e52805
/agent-note-client-plugin-loading-lazy-factories-cordis-lifecycle-and-hot-reload/decision/live-graph-reconciliation:
en: 37e2fa1fe5cb2b95
zh: 80e513db87709cb4
/agent-note-client-plugin-loading-lazy-factories-cordis-lifecycle-and-hot-reload/decision/hot-reload-one-driver-plugin-self-watched-bundles:
en: f8b591d4c41ef492
zh: bc70f1cd540b7753
en: c1fdcb3e6f074a7a
zh: b08c7b1ff25acb09
/agent-note-client-plugin-loading-lazy-factories-cordis-lifecycle-and-hot-reload/package-ownership:
en: 94ca0d09e6fd1e3a
zh: d5949a2bdec795ba
/agent-note-client-plugin-loading-lazy-factories-cordis-lifecycle-and-hot-reload/consequences:
en: 6a96d9ac06c222e5
zh: b2814ab58348e831
en: 16e1f2467c559315
zh: a714ec6ff2722cf1
/agent-note-client-plugin-loading-lazy-factories-cordis-lifecycle-and-hot-reload/alternatives-considered:
en: b8f036f50cff7cd8
zh: 6d623c4a1f4314b1
@@ -56,7 +56,7 @@ What happens between `dsh web` starting and the UI appearing? Three stages: the
1. The composing app (`apps/cli`) ships the roster as ordinary rows in its `cordis.yml` config tree — client plugin packages are entry rows like every host plugin, including the always-mounted `client-hmr` row. `auditStartupEntries` reports failed imports, activation errors with their original stacks, and pending dependencies; optional entries warn and required entries reject startup ([startup policy](2026-09-09-consumer-owned-startup-strictness.md)).
2. The `dsh-client-modules` node half (the package is dual-face: its browser half is the module table) resolves each live Loader entry through the same `name` and owning-tree `baseUrl` inputs that imported its Host face, then reads the nearest owning package.json `dsh.client` declaration and composes `window.__DSH_BOOT__`: `{ rev, entries: [{ id, url, rev, inject?, immediately?, external? }], batches: [{ phase, url, rev, entries }] }`. The manifest package name is the browser module identity even when an overlay names a relative source or built entry file. Distinct active Loader sources resolving to one package name fail composition; after one source unloads, the surviving source supplies the row without a fiber restart. The row's three optional fields come from manifests, never hand-copied. Composition orders requested dynamic rows before their consumers, rejects synchronous request cycles, and assigns every row to exactly one initial batch. It refuses declared plugins without built `./client` bundles and groups their package/path rows under one required source-build instruction; malformed declaration fields also fail activation, and the Host audit reports either error from the FAILED fiber.
3. Scanning is incremental per package — there is no full-rescan code path. Each cordis `internal/plugin` emission marks the fiber's entry name dirty (entry-less fibers drop O(1)); a microtask flush reconciles each dirty name against live loader entries, with package metadata (including the negative "not a client package" verdict) cached per entry name and owning-tree base URL for the process lifetime and changed bundle publication reachable only through `rebuilt(id)`. The activation pass seeds the same dirty set from current entries and flushes synchronously, so first scan and steady state share one implementation. Initial rows hash their pre-read filesystem metadata; startup combo revisions hash the ordered row identities and revisions, and the rows plus batch descriptors hash into `graph.rev`. The graph types are single-sourced in the modules package's `./client` export — the webserver knows nothing about the graph, while modules registers the combo route and contributes structured index-injection rows.
3. Scanning is incremental per package — there is no full-rescan code path. Each cordis `internal/plugin` emission marks the fiber's entry name dirty (entry-less fibers drop O(1)); a microtask flush reconciles each dirty name against live loader entries, rereading the package manifest and bundle metadata on each reconciliation, so an entry restart publishes a changed declaration or bundle and an unchanged one publishes nothing; a bundle rebuilt without an entry restart is published through `rebuilt(id)` ([package asset decision](2026-10-19-client-plugin-package-assets.md)). The activation pass seeds the same dirty set from current entries and flushes synchronously, so first scan and steady state share one implementation. Initial rows hash their pre-read filesystem metadata; startup combo revisions hash the ordered row identities and revisions, and the rows plus batch descriptors hash into `graph.rev`. The graph types are single-sourced in the modules package's `./client` export — the webserver knows nothing about the graph, while modules registers the combo route and contributes structured index-injection rows.
Why is the roster yml rows and not a scan? Because which plugins compose into a deployment is a composition decision, not a package property — a package declaring `dsh.client` in the repo does not mean this deployment mounts it, so discovery-by-scan cannot make that call; the node half scans only what the tree actually mounted.
@@ -94,7 +94,7 @@ On the browser side, the transport delegates code replacement to the same module
Bootstrap replacement is rejected before invalidation or teardown: the module system retains its initial exports, so remounting its old code would reset consumers without applying the requested revision. The page reports the required reload and preserves the bootstrap fiber. Every non-bootstrap plugin shares the replacement semantics; an `immediately` row reloads exactly like a lazy one. Dependency cascade costs zero client code: a fiber's activation epoch strings its service providers' uids, so replacing a foundational provider such as connection re-loads every dependent through cordis itself — correct, if heavy.
Reload creates a fresh fiber and component state; it does not preserve React state inside the replaced plugin. Static assembly libraries and the shell require a rebuilt page. Failed imports or activation remain diagnosable and retryable without rolling back unrelated plugins. Self-reload closes the old SSE channel and opens a new one; its full snapshot repairs missed graph changes. Boot, graph updates and rebuilt frames share the same queue, so a code replacement cannot overlap initial module arrival.
Reload creates a fresh fiber and component state; it does not preserve React state inside the replaced plugin. Static assembly libraries and the shell require a rebuilt page; when the Host itself serves another release, the page holds plugin replacement and asks whether to reload ([Host release change prompt](../feature/2026-10-14-host-release-change-refresh-prompt.md)). Failed imports or activation remain diagnosable and retryable without rolling back unrelated plugins. Self-reload closes the old SSE channel and opens a new one; its full snapshot repairs missed graph changes. Boot, graph updates and rebuilt frames share the same queue, so a code replacement cannot overlap initial module arrival.
## Package ownership
@@ -102,7 +102,7 @@ The current package inventory and build forms live in the [client shell layering
## Consequences
One governance implementation runs on both sides of the wire; the browser-specific layer is one module system plus one reload plugin. Dynamic packages have one artifact form, so the purity check covers them all. Cordis dependencies, module requests, and the boot tier live with their owners — the manifests — while the composing app holds only the roster. Host graph validation and recursive request arrival keep synchronous factory dependencies explicit. Browser-native script loading preserves the standard mapping among plugin network resources, generated bundles, and TypeScript/TSX sources, while the module system keeps only one replaceable `loadBundle` hook.
One governance implementation runs on both sides of the wire; the browser-specific layer is one module system plus one reload plugin. Dynamic packages have one artifact form, so the purity check covers them all. Cordis dependencies, module requests, and the boot tier live with their owners — the manifests — while the composing app holds only the roster. Host graph validation and recursive request arrival keep synchronous factory dependencies explicit. Browser-native script loading preserves the standard mapping among plugin network resources, generated bundles, and TypeScript/TSX sources, while the module system keeps two replaceable carrier hooks: `loadBundle` for bundle bytes and `resolveAsset` for package-asset references.
Costs accepted: the vendored Loader carries idle machinery in the browser (EntryTree persistence is a no-op, groups/isolation unused); every plugin edit in dev pays a bundle rebuild plus fiber remount; graph `inject` rows guide factory arrival but service availability remains the activation authority, so a mismatch appears at the settled sweep; the static UI libraries keep direct value exports; every bundle gains a source-map artifact; and external-script failures provide only coarse URL diagnostics instead of the HTTP status available to an explicit fetch. The Host retains bundle snapshots and lazy response plans for the current graph and one prior startup generation. Script and map bodies are cached after their first `GET`, so memory grows with bundle snapshots and requested response bodies. Each materialized response stays fixed at its URL; a prior-generation map first requested after a rebuild reads the current map file.
@@ -56,7 +56,7 @@ Host 会快照每个已构建插件入口 bundle,并把每个调度阶段的
1. 负责组合的 app(`apps/cli`)把名册作为普通行放进它的 `cordis.yml` 配置树——client 插件包与每个 host 插件一样是 entry 行,包括无条件挂载的 `client-hmr` 行。`auditStartupEntries` 报告 import 失败、带原始 stack 的激活错误,以及待满足的依赖;optional entry 输出 warning,required entry 则使启动失败([启动策略](2026-09-09-consumer-owned-startup-strictness.zh.md))。
2. `dsh-client-modules` 的 node 半(该包是双面的:浏览器半就是模块表)使用 Host face import 时相同的 `name` 与所属 tree `baseUrl` 解析每个 live Loader entry,再读取最近归属 package.json 的 `dsh.client` 声明并组合出 `window.__DSH_BOOT__`:`{ rev, entries: [{ id, url, rev, inject?, immediately?, external? }], batches: [{ phase, url, rev, entries }] }`。即使 overlay 指向相对的 source 或 built entry 文件,manifest 包名仍是浏览器模块身份。若不同的 active Loader source 解析到同一包名,组合会失败;一个来源卸载后,仍存活的来源无需重启 fiber 即可提供该 row。Row 的三个可选字段都来自 manifest,永不人肉抄写。组合会把被请求的动态图 row 排到消费者之前、拒绝同步请求环,并把每个 row 恰好分配给一个初始批次。它会拒绝没有已构建 `./client` bundle 的已声明插件,并把它们的 package/path 行归到一条源码构建要求下;畸形声明字段同样会让激活失败,Host 检查会从 FAILED fiber 报告这两类错误。
3. 扫描是单包增量——不存在全量重扫代码路径。每次 cordis `internal/plugin` 发射把该 fiber 的 entry 名标脏(无 entry 的 fiber O(1) 丢弃);微任务 flush 把每个脏名对账 live loader entries,包元数据(含「非 client 包」的否定结论)按 entry 名与所属 tree base URL 缓存至进程结束,变化后 bundle 的发布只经 `rebuilt(id)` 可达。激活趟从当前 entries 灌同一脏集合并同步 flush,初扫与稳态共享一条实现。初始 row 对读取前的文件系统元数据求哈希;启动 combo revision 对有序 row 的身份与 revision 求哈希,row 与批次描述再共同哈希进 `graph.rev`。图类型单源在 modules 包的 `./client` 出口——webserver 对图一无所知;modules 会注册 combo 路由并贡献结构化 index 注入行。
3. 扫描是单包增量——不存在全量重扫代码路径。每次 cordis `internal/plugin` 发射把该 fiber 的 entry 名标脏(无 entry 的 fiber O(1) 丢弃);微任务 flush 把每个脏名对账 live loader entries,每次对账都重新读取包 manifest 与 bundle 元数据,因此条目重启会发布变化后的声明或 bundle,未变化时不发布;没有伴随条目重启的 bundle 重建经 `rebuilt(id)` 发布([包资源决策](2026-10-19-client-plugin-package-assets.zh.md))。激活趟从当前 entries 灌同一脏集合并同步 flush,初扫与稳态共享一条实现。初始 row 对读取前的文件系统元数据求哈希;启动 combo revision 对有序 row 的身份与 revision 求哈希,row 与批次描述再共同哈希进 `graph.rev`。图类型单源在 modules 包的 `./client` 出口——webserver 对图一无所知;modules 会注册 combo 路由并贡献结构化 index 注入行。
为什么名册是 yml 行而不是扫描?因为哪些插件组合进一次部署是组合决策,不是包属性——一个在仓库中声明了 dsh.client 的包,不代表这次部署要挂载它,扫描发现无从替人做这个决定;node 半只扫描配置树实际挂载了的东西。
@@ -94,7 +94,7 @@ Host SSE 适配器转发现有图变化通知,并在连接时发送当前完
Bootstrap 替换会在失效或卸载前被拒绝:模块系统保留其初始导出,重新挂载旧代码会重置消费者,却无法应用请求的 revision。页面会报告需要刷新,并保留 bootstrap fiber。所有非 bootstrap 插件都共享同一套替换语义;`immediately` 行的重载与 lazy 行分毫不差。依赖级联不花一行 client 代码:fiber 的激活纪元串接着它各服务提供方的 uid,因此替换 connection 等基础 provider 的 fiber 时,每个依赖方都会经 cordis 本身重新装载——行为正确,但代价较高。
重载会创建新的 fiber 和组件状态,不保留被替换插件内部的 React 状态。静态组装库与应用壳需要重建后的页面。导入或激活失败仍可诊断和重试,不会回滚无关插件。自重载关闭旧 SSE 通道并打开新通道;其完整快照补齐遗漏的图变更。启动、图更新和重建帧共用同一队列,因此代码替换不会与初始模块到达重叠。
重载会创建新的 fiber 和组件状态,不保留被替换插件内部的 React 状态。静态组装库与应用壳需要重建后的页面;Host 本身换成另一个 release 时,页面会暂停插件替换并询问是否刷新([Host release 变化提示](../feature/2026-10-14-host-release-change-refresh-prompt.zh.md))。导入或激活失败仍可诊断和重试,不会回滚无关插件。自重载关闭旧 SSE 通道并打开新通道;其完整快照补齐遗漏的图变更。启动、图更新和重建帧共用同一队列,因此代码替换不会与初始模块到达重叠。
## 包归属
@@ -102,7 +102,7 @@ Bootstrap 替换会在失效或卸载前被拒绝:模块系统保留其初始
## Consequences
Wire 两侧运行同一份治理实现;浏览器特有层只包含一套模块系统和一个重载插件。动态包只有一种产物形态,因此纯度检查覆盖全部动态包。Cordis 依赖、模块请求与启动档位都与其所有者——manifest——同住,负责组合的 app 只握名册。Host graph 校验与递归请求到达使同步 factory 依赖保持显式。浏览器原生 script 装载保留插件网络资源、生成 bundle 与 TypeScript/TSX 源码之间的标准映射,模块系统也只保留一个可替换的 `loadBundle` 钩子。
Wire 两侧运行同一份治理实现;浏览器特有层只包含一套模块系统和一个重载插件。动态包只有一种产物形态,因此纯度检查覆盖全部动态包。Cordis 依赖、模块请求与启动档位都与其所有者——manifest——同住,负责组合的 app 只握名册。Host graph 校验与递归请求到达使同步 factory 依赖保持显式。浏览器原生 script 装载保留插件网络资源、生成 bundle 与 TypeScript/TSX 源码之间的标准映射,模块系统只保留两个可替换的载体钩子:负责 bundle 字节的 `loadBundle` 和负责包资源引用的 `resolveAsset`。
接受的代价:vendored Loader 在浏览器里背着闲置机件(EntryTree 持久化是 no-op,分组/隔离未用);开发期每次修改插件都要付一次 bundle 重建加 fiber 重挂;graph `inject` row 指导 factory 到达,但服务可用性仍是激活权威,因此不匹配会在 settled 扫描时浮出;静态 UI 库保留直接实体导出;每个 bundle 多出一份 sourcemap 产物,外部 script 失败也只能给出粗粒度 URL 诊断,不能像显式 fetch 那样报告 HTTP 状态。Host 为当前图及上一代启动图保留 bundle 快照与惰性响应计划。脚本和 map body 在首次 `GET` 后缓存,因此内存随 bundle 快照和已请求的响应 body 增长。每份已物化响应在其 URL 下保持固定;若上一代 map 在重建后才首次被请求,则会读取当前 map 文件。
@@ -6,8 +6,8 @@
en: 55846ac57e07eeda
zh: 3de03730d4b28863
/agent-note-dsh-source-launch-through-the-tsx-esm-hook/problem:
en: 68385f7e47d3ac1f
zh: 285ca290832e2939
en: d13aa56d01c1181d
zh: 837427ed6963c39d
/agent-note-dsh-source-launch-through-the-tsx-esm-hook/decision:
en: 12adf8ed56b4d5c3
zh: fbbb9add935730c0
@@ -8,7 +8,7 @@ English | [中文](2026-07-29-dsh-source-launch-tsx-esm.zh.md)
## Problem
The [archived native source-launch decision](../../archived/architecture/2026-07-28-dsh-native-typescript-source-launch.md) ran `apps/cli/src/bin.ts` under `node --experimental-transform-types` with a resolve-only paths loader, so Node owned TypeScript transformation. Node 26.0.0 removed `--experimental-transform-types` (the process rejects the flag with `bad option`), keeping only strip mode, and strip mode rejects syntax this source graph requires: vendored Cordis parameter properties (`constructor(private ctx: Context)`), the `@Inject` decorators in `vendor/hmr`, and runtime enums/namespaces throughout `vendor/` and `packages/workflow`. The repository's engines range (`^22.19.0 || >=24.0.0`) includes Node 26, so the native launch chain could not start at all there — and no CI job executed the real launch vector, so the incompatibility shipped silently.
The [archived native source-launch decision](../../archived/architecture/2026-07-28-dsh-native-typescript-source-launch.md) ran `apps/cli/src/bin.ts` under `node --experimental-transform-types` with a resolve-only paths loader, so Node owned TypeScript transformation. Node 26.0.0 removed `--experimental-transform-types` (the process rejects the flag with `bad option`), keeping only strip mode, and strip mode rejects syntax this source graph requires: vendored Cordis parameter properties (`constructor(private ctx: Context)`), the `@Inject` decorators in `vendor/hmr`, and runtime enums/namespaces throughout `vendor/` and `packages/workflow`. The supported engines range includes Node 26, so the native launch chain could not start at all there — and no CI job executed the real launch vector, so the incompatibility shipped silently.
Startup latency also mattered: the off-thread `module.register()` hooks worker serialized every resolution across threads (~440ms of `makeSyncRequest` wait during TUI boot), and the full tsx default (`--import tsx`) pays ~0.4s in its CJS hook's resolution amplification.
@@ -8,7 +8,7 @@ Status: implemented
## 问题
[已归档的原生源码启动决策](../../archived/architecture/2026-07-28-dsh-native-typescript-source-launch.md)让 `apps/cli/src/bin.ts` 在 `node --experimental-transform-types` 下运行,配合一个只做解析的 paths loader,由 Node 负责 TypeScript 转换。Node 26.0.0 移除了 `--experimental-transform-types`(进程以 `bad option` 拒绝该 flag),只保留 strip 模式,而 strip 模式无法接受这个源码图必需的语法:vendor Cordis 中的参数属性(`constructor(private ctx: Context)`)、`vendor/hmr` 中的 `@Inject` 装饰器,以及遍布 `vendor/` 与 `packages/workflow` 的运行时 enum/namespace。仓库的 engines 范围(`^22.19.0 || >=24.0.0`)包含 Node 26,因此原生启动链在其上完全无法启动——且没有任何 CI 任务执行过真实启动向量,这一不兼容悄然发布。
[已归档的原生源码启动决策](../../archived/architecture/2026-07-28-dsh-native-typescript-source-launch.md)让 `apps/cli/src/bin.ts` 在 `node --experimental-transform-types` 下运行,配合一个只做解析的 paths loader,由 Node 负责 TypeScript 转换。Node 26.0.0 移除了 `--experimental-transform-types`(进程以 `bad option` 拒绝该 flag),只保留 strip 模式,而 strip 模式无法接受这个源码图必需的语法:vendor Cordis 中的参数属性(`constructor(private ctx: Context)`)、`vendor/hmr` 中的 `@Inject` 装饰器,以及遍布 `vendor/` 与 `packages/workflow` 的运行时 enum/namespace。受支持的 engines 范围包含 Node 26,因此原生启动链在其上完全无法启动——且没有任何 CI 任务执行过真实启动向量,这一不兼容悄然发布。
启动延迟同样是问题:off-thread 的 `module.register()` 钩子工作线程把每次解析都跨线程序列化(TUI 启动期间约 440ms 的 `makeSyncRequest` 等待),而完整的 tsx 默认形态(`--import tsx`)会因其 CJS 钩子放大解析开销而多花约 0.4s。
@@ -33,8 +33,8 @@
en: a244f6a88fdabf3d
zh: e4feedb7297f4865
/agent-note-typert-gateway-targeted-method-calls/three-artifact-kinds-and-two-typescript-programs:
en: c5bcd6eb76bf3d42
zh: 9df2fbb364643186
en: 4479d41466723100
zh: 3a7f398e0d0070a5
/agent-note-typert-gateway-targeted-method-calls/the-remote-package-entry:
en: 75e977fe0fb71d59
zh: 00760c79da173cb7
@@ -207,17 +207,9 @@ Client Program
`remote-client` is the Host Program's second emitter, not a third Program or the Client's local face. It contains no Host Cordis merge, Service class, Context class, or implementation code, and it does not enter the Host-local reflection registry.
The Host lib build performs strict Host analysis and emits both the Host-local and Remote consumer artifacts. The Client lib then consumes the Remote DTS. The complete order is:
Source-only Host analysis writes the Host-local and Remote consumer artifacts before package compilation. [The generation-order decision](../process/2026-10-18-source-first-metadata-builds.md) owns placement and its tradeoffs; [Development](../../../../docs/development.md#typescript-project-layout) owns the command sequence.
```text
Host lib build
→ 生成 typert.host.{js,d.ts}
→ 生成各业务包 lib/typert.remote-client.{js,d.ts,d.ts.map}
→ 完成 Client lib 和 typert.client 产物
→ Vite 构建 Web
```
The top-level `build` runs the step graph in `scripts/build.ts`, which completes the Host bundle and the Remote artifacts before starting Client TypeScript compilation. `build:lib` remains the complete serial lib build for `build:bench`, and its Host half `build:lib:host` serves the vendor release workflows. `typecheck` writes the Remote artifacts through `scripts/generate-typert.ts`, beside Host compilation, before Client TypeScript compilation. A clean build must not depend on stale `.d.ts` files from an earlier build.
The top-level `build`, declaration-only `typecheck`, and serial `build:lib` share metadata preparation before package compilation. `build:lib` remains the complete serial lib build for `build:bench`; its Host half `build:lib:host` serves the vendor release workflows and retains aggregate checking. A clean build must not depend on stale `.d.ts` files from an earlier build.
Compiler-backed repository gates that resolve the consumer surface have the same prerequisite even when their primary inputs are source files. The public `typecheck`, `lint`, and `doc-typecheck` commands run the Host contract pass first. The gate scheduler may use their `*:contracts-ready` variants only after an explicit Typert-contract or complete-build dependency, so parallel lanes neither read missing declarations nor run concurrent generators against the same outputs.
@@ -207,17 +207,9 @@ Client Program
`remote-client` 是 Host Program 的第二个 emitter,不是第三个 Program,也不是 Client 本地 face。它不包含 Host Cordis merge、Service class、Context class 或实现代码,不进入 Host 本地 reflection registry。
Host lib 构建负责完成严格 Host 分析并产出 Host 本地 artifact 与 Remote 消费端 artifact;Client lib 随后消费 Remote DTS。完整顺序为:
仅源码的 Host 分析在包编译之前写出 Host 本地 artifact 与 Remote 消费端 artifact。[生成顺序决策](../process/2026-10-18-source-first-metadata-builds.zh.md)负责生成位置及其取舍;[开发指南](../../../../docs/development.zh.md#typescript-project-layout)负责命令序列。
```text
Host lib build
→ 生成 typert.host.{js,d.ts}
→ 生成各业务包 lib/typert.remote-client.{js,d.ts,d.ts.map}
→ 完成 Client lib 和 typert.client 产物
→ Vite 构建 Web
```
顶层 `build` 运行 `scripts/build.ts` 中的步骤图,先完成 Host bundle 与 Remote artifact,再启动 Client TypeScript 编译。`build:lib` 仍是 `build:bench` 使用的完整串行 lib 构建,其 Host 部分 `build:lib:host` 供 vendor 发布 workflow 使用。`typecheck` 与 Host 编译并行,经 `scripts/generate-typert.ts` 写出 Remote artifact,再启动 Client TypeScript 编译。一次干净构建不能依赖上次残留的 `.d.ts`。
顶层 `build`、仅生成声明的 `typecheck` 与串行 `build:lib` 都在包编译之前准备元数据。`build:lib` 仍是 `build:bench` 使用的完整串行 lib 构建;其 Host 部分 `build:lib:host` 供 vendor 发布 workflow 使用,并保留 aggregate 检查。一次干净构建不能依赖上次残留的 `.d.ts`。
即使主要输入是源文件,需要通过编译器解析消费方 surface 的仓库门禁也有相同的前置条件。公共 `typecheck`、`lint` 和 `doc-typecheck` 命令会先执行 Host 约定 pass。门禁调度器仅可在显式的 Typert 约定依赖或完整构建依赖完成后使用对应的 `*:contracts-ready` 变体,使并行 lane 既不会读取缺失的声明,也不会针对同一输出并发运行多个生成器。
@@ -27,14 +27,14 @@
en: d5d0d6a57a4fa581
zh: 265973b157f957d9
/agent-note-session-history-control-state-and-remote-event-transport/decision/session-controller:
en: e338ac27490a380f
zh: 12600011ec742e5b
en: f432480a52414d56
zh: 15a3f175b69cdb53
/agent-note-session-history-control-state-and-remote-event-transport/decision/session-controller/activation-policy:
en: 534dcc62a26d305e
zh: 03d64abc0a46bbec
en: 03e24cdee8519a92
zh: 82dc02eb26d7d7f4
/agent-note-session-history-control-state-and-remote-event-transport/decision/session-controller/session-journal:
en: bb710f8cf35f2028
zh: aae2cfb8cd848069
en: 4de2965717900b96
zh: 1d129003824fa6bd
/agent-note-session-history-control-state-and-remote-event-transport/decision/session-controller/session-live-control:
en: a11d627cd68650c3
zh: 9aff565b9f712853
@@ -139,11 +139,11 @@ If a page request is canceled with its physical carrier generation, the journal
`packages/api/session-controller` provides Host `ctx.sessionController` and the generated `ctx.remote.session` namespace.
It owns Session list, search, create, selectModel, rename, fork, prompt, attachment, updateQueue, cancel, page, follow, and control. The Host-generation model catalog is exposed separately through `session/modelCatalog` because it is not Session-specific.
It owns Session list, search, create, selectModel, rename, fork, prompt, attachment, updateQueue, cancel, `events`, the deprecated `page`, `follow`, `control`, plus `observeEvent`, `projections`, `observeProjections`, `state`, `observeState`, and `wait`. The Host-generation model catalog is exposed separately through `session/modelCatalog` because it is not Session-specific.
The package separates agent, commands, control, history, and list controllers internally, but Session identity resolution, activation policy, subagent ownership, and Remote error projection have one public owner.
Other Host Remote namespaces reuse the same identity rules through `ctx.sessionController.inspect()` or `resolveAgent()`; they do not retain a second Session resolver.
Other Host Remote namespaces reuse the same resume-capable identity rules through `ctx.sessionController.resolveAgent()`; they do not retain a second Session resolver.
#### Activation policy
@@ -157,7 +157,8 @@ Each method explicitly selects a cold inspection, live-only lookup, or resume-ca
| `session.page(address)` | attached Session or persistence log | Never resumes an Agent |
| `session.follow(address)` | one live or prepared observation carrying the opening page and projections | Publishes the snapshot first, then promotes an ordinary cold Session once in the background |
| `session.control()` | current attached Agents, pending registry, and process-local registries | Baseline and reconnect do not resume an Agent |
| `session.attachment`, fork source read | authorized durable Session data | A read does not resume an Agent |
| Fork source read | authorized durable Session data | A read does not resume an Agent |
| `session.attachment` | attachment store under the [image access rule](2026-10-23-store-scoped-image-reads.md) | A read does not resume an Agent |
| `session.updateQueue` | live Agent or ordinary persisted Session | Resumes an ordinary cold Session before mutating its Inbox |
| `session.cancel` | only the current live Agent | Does not resume vanished state |
| `models`, `selectModel`, `rename`, `prompt` | command resolves the target Session | Resumes only when the method explicitly permits it |
@@ -173,9 +174,9 @@ Reading titles, lists, and projections does not require an Agent. An observation
#### Session journal
`session.page` returns a history window clipped on message boundaries with contiguous internal sequence numbers. Every request must carry an explicit `throughSeq`; this value comes from the corresponding `session.follow` generation's opening cursor and fixes the read at the same log cut. A tail page without `beforeSeq` must end exactly at `throughSeq`, where `-1` denotes an empty log. `beforeSeq` only selects an older page before that cut and cannot replace the synchronization cursor. `maxMessages` limits user/assistant message count without dropping chunks, tools, or state events between those messages.
`session.events` returns a history window clipped on message boundaries with contiguous internal sequence numbers. `throughSeq` is optional: when omitted, the read selects the observed log end and returns it as the page's inclusive `cursor`; a supplied value fixes the read at that cut. A page without `beforeSeq` ends exactly at the selected cut, where `-1` denotes an empty log. `beforeSeq` only selects an older page before that cut and cannot replace the synchronization cursor. `maxMessages` limits user/assistant message count without dropping chunks, tools, or state events between those messages. The deprecated `session.page` entry returns the same type.
The tail page also carries a projection baseline no later than `throughSeq`; older pages carry only historical entries. The Client merges pages and subsequent live control updates by projection watermark.
A page carries only its records, `hasMore`, and inclusive `cursor`; a projection baseline comes from a follow opening snapshot or the control stream, never from a page. The Client merges pages and subsequent live control updates by projection watermark.
Ordinary Sessions and direct subagents use one `SessionAddress` protocol. A direct-subagent address carries parent Session, child Session, and mode; a cold Host read verifies durable ownership and descriptor rather than authorizing access from the child id alone.
@@ -139,11 +139,11 @@ repair 期间旧 window 保持可读;page 与期间积累的 live entries 拼
`packages/api/session-controller` 提供 Host `ctx.sessionController` 与生成的 `ctx.remote.session` namespace。
它拥有 Session list、search、create、selectModel、rename、fork、prompt、attachment、updateQueue、cancel、page、follow 与 control。Host generation 的 model catalog 通过独立的 `session/modelCatalog` 公开,因为它不属于特定 Session。
它拥有 Session list、search、create、selectModel、rename、fork、prompt、attachment、updateQueue、cancel、`events`、已弃用的 `page`、`follow`、`control`,以及 `observeEvent`、`projections`、`observeProjections`、`state`、`observeState` 与 `wait`。Host generation 的 model catalog 通过独立的 `session/modelCatalog` 公开,因为它不属于特定 Session。
包内的 agent、commands、control、history 与 list controller 分开实现,但 Session 身份解析、激活策略、subagent ownership 和 Remote 错误投影只有一个公开 owner。
其他 Host Remote namespace 通过 `ctx.sessionController.inspect()` 或 `resolveAgent()` 复用同一身份规则,不保留第二份 Session resolver。
其他 Host Remote namespace 通过 `ctx.sessionController.resolveAgent()` 复用同一套允许恢复 Session 的身份规则,不保留第二份 Session resolver。
#### 激活策略
@@ -157,7 +157,8 @@ Session Remote 方法传递 `SessionId` 或 `SessionAddress`,不靠参数类
| `session.page(address)` | attached Session 或 persistence 日志 | 永不恢复 Agent |
| `session.follow(address)` | 一份携带 opening page 与 projection 的 live 或 prepared observation | 先发布 snapshot,再在后台把普通冷 Session 提升一次 |
| `session.control()` | 当前 attached Agent、pending registry 与进程内 registry | baseline 与重连不恢复 Agent |
| `session.attachment`、fork 源读取 | 已授权的持久 Session 数据 | 读取不恢复 Agent |
| Fork 源读取 | 已授权的持久 Session 数据 | 读取不恢复 Agent |
| `session.attachment` | 按[图片访问规则](2026-10-23-store-scoped-image-reads.zh.md)读取附件存储 | 读取不恢复 Agent |
| `session.updateQueue` | live Agent 或普通持久 Session | 修改 Inbox 前恢复普通冷 Session |
| `session.cancel` | 仅命中当前 live Agent | 不为已消失状态恢复 Agent |
| `models`、`selectModel`、`rename`、`prompt` | 命令解析目标 Session | 仅按方法约定显式恢复 |
@@ -173,9 +174,9 @@ Session Remote 方法传递 `SessionId` 或 `SessionAddress`,不靠参数类
#### Session 日志
`session.page` 返回一段按消息边界裁剪、内部 seq 连续的历史窗口。每个请求必须显式携带 `throughSeq`;该值来自对应 `session.follow` generation 的 opening cursor,并把本次读取固定在同一个日志切点。无 `beforeSeq` 的 tail page 必须精确结束于 `throughSeq`,其中 `-1` 表示空日志;`beforeSeq` 只选择该切点之前的更早页面,不能替代同步 cursor。`maxMessages` 限制 user/assistant 消息数,不丢弃这些消息之间的 chunk、tool 或状态事件。
`session.events` 返回一段按消息边界裁剪、内部 seq 连续的历史窗口。`throughSeq` 可省略:省略时选取观察到的日志末尾,并把该切点作为页面的 inclusive `cursor` 返回;显式提供时把本次读取固定在该切点。无 `beforeSeq` 的 page 精确结束于所选切点,其中 `-1` 表示空日志;`beforeSeq` 只选择该切点之前的更早页面,不能替代同步 cursor。`maxMessages` 限制 user/assistant 消息数,不丢弃这些消息之间的 chunk、tool 或状态事件。已弃用的 `session.page` 入口返回同一类型。
tail page 同时携带不晚于 `throughSeq` 的 projection baseline;旧页只携带历史 entries。Client 以 projection watermark 合并 page 与后续 live control 更新。
page 只携带记录、`hasMore` 和 inclusive `cursor`;projection baseline 来自 follow 的开场快照或 control 流,不来自 page。Client 以 projection watermark 合并 page 与后续 live control 更新。
普通 Session 与 direct subagent 使用同一个 `SessionAddress` 协议。direct subagent 地址同时携带父 Session、子 Session 与 mode,Host 冷读时验证持久 ownership 和 descriptor,不能只凭 child id 越权读取。
@@ -6,14 +6,14 @@
en: 41f30d473ff53ce2
zh: 6851c4414f5331c1
/agent-note-session-projections-as-a-required-reader-seam/problem:
en: c0c779c717cad546
zh: 5d2916ac86bf5d84
en: fca00eeb7a8d9abd
zh: d7a9766f5322dff9
/agent-note-session-projections-as-a-required-reader-seam/decision:
en: a1c50d2f43334a88
zh: d7736994733454b4
en: 8d38ed9356085c7e
zh: e15ede51415192f3
/agent-note-session-projections-as-a-required-reader-seam/alternatives-considered:
en: aea3da939c90d240
zh: 21e91c481cdd23b7
/agent-note-session-projections-as-a-required-reader-seam/consequences:
en: 73325513ec70fbb2
zh: 610bf34b9f9db420
en: ab45eb87f7c22cdb
zh: e65a97991a45164f
@@ -6,19 +6,17 @@ English | [中文](2026-08-19-session-projection-mandatory-seam.zh.md)
## Problem
An optional projection registry lets a plugin whose host behavior reads projected state activate without that state. Unless the reader rejects the missing registry or key, the host behavior or subagent catalog fields can silently disappear. Batch-only reads also materialize every client view when a consumer needs one host value. Some contributor sites intentionally retain optional `ctx.inject` registration, so their readers need an explicit missing-state rule.
An optional projection registry lets a plugin whose host behavior reads projected state activate without that state. Unless the reader rejects the missing registry or key, the host behavior or subagent catalog fields can silently disappear. Batch-only reads also materialize every client view when a consumer needs one host value.
## Decision
This decision builds on the split between host projection state and client views in [Session projection state and client views](../../archived/architecture/2026-08-19-session-projection-state-and-client-views.md).
Every host reader treats the projection registry and its required key as mandatory state. A plugin either declares `sessionProjections` as a required injection or resolves the registry and key explicitly and throws on the first dependent access. Official compositions mount the registry before those plugins. `ApiProxyService` follows the required-injection form; the lower-level `createApiProxy` factory remains tolerant for isolated tests and diagnostics.
Every host state owner treats the projection registry and its required key as mandatory state. Owners declare `sessionProjections` as a required injection and await acquisition before exposing service methods; other consumers depend on those services. Official compositions mount the registry before those plugins. `ApiProxyService` follows the required-injection form; the lower-level `createApiProxy` factory remains tolerant for isolated tests and diagnostics.
A domain contributor may register its unit through `ctx.inject(['sessionProjections'], ...)`. Optional registration controls the child lifecycle only; it does not authorize a reader to substitute a default when the registry or key is absent.
Owners acquire state through [awaited registration and retained handles](2026-10-12-session-projection-registration-handles.md). That decision supersedes optional first-access acquisition and returning a bare disposer. It preserves the required-state rule here: a missing registry cannot silently produce default host state. Client views still contain only consumed fields; business service methods expose the host data each consumer needs.
The registry provides `stateOf(session, key)` for one typed host state and keeps `snapshot()` for batch carriers. Client views contain only consumed fields; host readers use `stateOf` for richer state.
`onChanged` publishes client-visible value changes only. Unit registration and removal remain effect-scoped registry lifecycle; `register()` returns the exact Cordis disposer so a composite domain owner can finish cleanup against projected state before removing its unit. Registration changes do not create a second Host event stream or client tombstone protocol. A later authoritative history or list baseline reflects the active key set.
`onChanged` publishes client-visible value changes only. Registration changes do not create a second Host event stream or client tombstone protocol. A later authoritative history or list baseline reflects the active key set.
## Alternatives considered
@@ -30,6 +28,6 @@ The registry provides `stateOf(session, key)` for one typed host state and keeps
## Consequences
- Missing projection composition fails either during activation or at the first dependent host access; it never degrades to a default value.
- Missing projection composition fails during activation; it never degrades to a default value.
- Host consumers avoid repeated whole-registry snapshots and log scans.
- Wire payloads exclude host-only fields and per-key watermark wrappers; ordinary baselines communicate the active key set.
@@ -6,19 +6,17 @@ Status: implemented
## 问题
可选的投影注册表会让 host 行为读取投影状态的插件在缺少该状态时仍然激活。除非读取方拒绝缺失的注册表或 key,否则 host 行为或 subagent 目录字段可能静默消失。只有批量读取也会在消费方只需要一个 host 值时物化每个客户端 view。部分贡献位置有意保留可选的 `ctx.inject` 注册形式,因此其读取方需要明确的缺失状态规则。
可选的投影注册表会让 host 行为读取投影状态的插件在缺少该状态时仍然激活。除非读取方拒绝缺失的注册表或 key,否则 host 行为或 subagent 目录字段可能静默消失。只有批量读取也会在消费方只需要一个 host 值时物化每个客户端 view。
## 决策
本决策建立在[会话投影的 host 状态与客户端视图](../../archived/architecture/2026-08-19-session-projection-state-and-client-views.md)所定义的拆分之上。
每个 host 读取方都把投影注册表及其所需 key 视为必需状态。插件要么把 `sessionProjections` 声明为必需注入,要么显式解析注册表与 key,并在第一次依赖它们的访问时抛错。正式组合在这些插件之前挂载注册表。`ApiProxyService` 采用必需注入形式;较低层的 `createApiProxy` factory 对隔离测试和诊断保持容错。
每个 host 状态所有者都把投影注册表及其所需 key 视为必需状态。所有者将 `sessionProjections` 声明为必需注入,并在提供服务方法前等待获取完成;其他消费方依赖这些服务。正式组合在这些插件之前挂载注册表。`ApiProxyService` 采用必需注入形式;较低层的 `createApiProxy` factory 对隔离测试和诊断保持容错。
领域贡献方可以通过 `ctx.inject(['sessionProjections'], ...)` 注册单元。可选注册只控制子功能生命周期;它并不允许读取方在注册表或 key 缺失时替换为默认值。
所有者通过[等待注册与保留句柄](2026-10-12-session-projection-registration-handles.zh.md)获取状态。该决策取代可选首次访问获取和返回裸释放函数的方式,保留这里的必需状态规则:缺少注册表不能静默产生默认 host 状态。客户端视图仍只包含消费字段;业务服务方法提供各消费方所需的 host 数据。
注册表提供 `stateOf(session, key)` 来读取一个类型化 host 状态,并为批量 carrier 保留 `snapshot()`。客户端 view 只包含消费方使用的字段;host 读取方通过 `stateOf` 取得更丰富的状态。
`onChanged` 只发布客户端可见值的变化。单元注册和移除仍是绑定 effect 的注册表生命周期;`register()` 返回 Cordis 的原始 disposer,使组合式领域 owner 可以先依据投影状态完成清理,再移除自身单元。注册变化不会创建第二条 Host 事件流或客户端 tombstone 协议。后续权威 history 或 list 基线会反映活跃 key 集。
`onChanged` 只发布客户端可见值的变化。注册变化不会创建第二条 Host 事件流或客户端 tombstone 协议。后续权威 history 或 list 基线会反映活跃 key 集。
## 考虑过的替代方案
@@ -30,6 +28,6 @@ Status: implemented
## 后果
- 缺少投影组合会在激活期间或第一次依赖它的 host 访问时失败,绝不会降级为默认值。
- 缺少投影组合会在激活期间失败,绝不会降级为默认值。
- host 消费方避免重复的全注册表快照和日志扫描。
- 协议负载排除 host 内部字段和逐 key 水位包装;普通基线会传达活跃 key 集。
@@ -51,8 +51,8 @@
en: 51f99cfca9c4c808
zh: ce95bab241d306f7
/agent-note-client-derived-presentation-from-raw-session-tool-events/host-design:
en: 16fd2b3e545ede21
zh: 4d28f52132495441
en: d5e5d1376dc42558
zh: c2f302b2a5d1de39
/agent-note-client-derived-presentation-from-raw-session-tool-events/host-design/performance-constraints:
en: 102b14e3eb9367ff
zh: d391099dbc41db24
@@ -195,9 +195,9 @@ The Client narrows `meta` locally at runtime. Renaming `presentationMeta` to mor
## Host Design
After obtaining source events, `SessionHistoryController.page()` performs only pagination and the existing projection-baseline calculation. Attached Sessions use the projection registry snapshot; detached Sessions use its restore path over the inspected log. History does not mount a preset to change the registered projection set.
After obtaining source events, `HistoryController.history()` performs only pagination and requests projections only for a subagent address that needs descriptor validation. Attached Sessions use the projection registry snapshot; detached Sessions use its restore path over the inspected log. History does not mount a preset to change the registered projection set.
`SessionHistoryController.follow()` retains listener-first setup, opening cursors, gap-free replay, live buffering, cancellation, and teardown. It maintains no additional state for tool events.
`WebController.follow()` retains listener-first setup, opening cursors, gap-free replay, live buffering, cancellation, and teardown. It maintains no additional state for tool events.
The controller has no `presenterScopeFor()`, `viewFor()`, `backscanArgs()`, `parseToolCall()`, or `jsonView()` path. Page state contains no presenter scope or argument resolver; follow state contains no `openCalls`, `fallbackEvents`, or presentation argument resolver. Each page/follow event is wrapped only as `{event}` while addressing, ownership, cursor, sequence, and projection logic remains intact.
@@ -195,9 +195,9 @@ Client 对 `meta` 做局部运行时收窄。是否把 `presentationMeta` 改名
## Host 端设计
`SessionHistoryController.page()` 在取得 source events 后只执行分页与现有 projection baseline 计算。attached Session 使用 projection registry snapshot;detached Session 使用该 registry 对 inspected log 的 restore 路径。history 不通过挂载 preset 改变已注册的 projection 集合。
`HistoryController.history()` 在取得 source events 后只执行分页,并且只有需要校验 descriptor 的 subagent 地址才请求投影。attached Session 使用 projection registry snapshot;detached Session 使用该 registry 对 inspected log 的 restore 路径。history 不通过挂载 preset 改变已注册的 projection 集合。
`SessionHistoryController.follow()` 保留 listener-first、opening cursor、gap-free replay、live buffering、取消和 teardown;它不为工具事件维护额外状态。
`WebController.follow()` 保留 listener-first、opening cursor、gap-free replay、live buffering、取消和 teardown;它不为工具事件维护额外状态。
Controller 不存在 `presenterScopeFor()`、`viewFor()`、`backscanArgs()`、`parseToolCall()` 或 `jsonView()` 路径。page state 不含 presenter scope 或参数 resolver;follow state 不含 `openCalls`、`fallbackEvents` 或 presentation 参数 resolver。每个 page/follow event 只包装成 `{event}`,地址、ownership、cursor、seq 与 projection 逻辑保持完整。
@@ -15,17 +15,17 @@
en: 480284ae1a6ce210
zh: d42c72ada9f019f8
/agent-note-session-observations-and-projection-owned-client-state/decision/observation-is-the-point-read-unit:
en: fac92f8547482a49
zh: 60240c47fbe4611f
en: 283136ae36512767
zh: e342b446c77ae300
/agent-note-session-observations-and-projection-owned-client-state/decision/source-resolution-and-lifetime:
en: e550ae9892474276
zh: fb6a2d9e91ebd4d9
en: bced4b90cb03525d
zh: b0c1ca4bf1064f89
/agent-note-session-observations-and-projection-owned-client-state/decision/projection-execution-boundary:
en: 148de86941b90f41
zh: c065a8e96e28b9d7
en: b3b235fa0250868a
zh: 0bb5eedf14cdafee
/agent-note-session-observations-and-projection-owned-client-state/decision/read-audiences:
en: 888f62659f4d868d
zh: e74491e8c85046ee
en: e394e9480340f69c
zh: 7eea994ee9fa380d
/agent-note-session-observations-and-projection-owned-client-state/decision/replayable-client-facts-are-projection-owned:
en: efded09f0cd30a8e
zh: 5e16580a39ff3359
@@ -48,11 +48,11 @@
en: 8d699dfc22ab5c5f
zh: 8dcd9fc6bd1594db
/agent-note-session-observations-and-projection-owned-client-state/verification:
en: df17217023668428
zh: 88f7da4deffc6bf9
en: 29f364d1031b7d85
zh: 2b193ceb52fb0406
/agent-note-session-observations-and-projection-owned-client-state/alternatives-considered:
en: a25138b4298cda16
zh: 92799a9bd8867d47
en: 90574351058d5d8c
zh: 6aa514cf47495ed2
/agent-note-session-observations-and-projection-owned-client-state/consequences:
en: e1f8bd8fa68de603
zh: 5afcd06f2611bc1e
@@ -24,7 +24,7 @@ The two ownership rules meet at the observation's projection snapshot. Lightweig
flowchart LR
List["list / search"] --> Corpus["SessionQuery corpus"]
Follow["follow"] --> Observe["observeSession"]
Page["page / attachment / fork"] --> Observe
Page["page / fork"] --> Observe
Subagent["subagent list / continuation"] --> Observe
Corpus --> Cache["projection cache hints"]
Cache --> ClientList["Client Session list"]
@@ -46,7 +46,7 @@ flowchart LR
`SessionQueryEngine.observeSession(sessionId, options)` returns a disposable `SessionObservation` containing one source kind, header, contiguous event prefix, cursor, optional projection snapshot, and the durable revision for a prepared source. An attached Session wins. Otherwise the reader's own prepared cache — keyed by `stat().revision` and pinned by observation leases — serves the cold Session, sharing one persistence read (`open(id, 'read')` + `read`) across concurrent observations, including an in-flight cold load.
Every owner disposes its observation. `retain()` creates another lease over the same cut, which lets `session.follow` publish a snapshot and then transfer that exact prepared source to background Agent promotion without rereading the log. A live Session that appears during cold resolution wins before publication; a disappeared live source is retried as cold.
Every owner disposes its observation. `retain()` creates another lease over the same cut, which lets `session.follow` transfer that exact prepared source to background Agent promotion before delivering the snapshot, without rereading the log. A live Session that appears during cold resolution wins before publication; a disappeared live source is retried as cold.
### Source resolution and lifetime
@@ -56,7 +56,7 @@ Live preference is checked both before and after a cold borrow. The second check
Persistence absence maps to Session-not-found only after no attached Session exists. Durable corruption, source-identity conflict, cancellation, and operational persistence failure remain distinct `SessionQueryError` outcomes so API owners can preserve their own public error vocabulary without duplicating source detection.
The observation owns no mutation authority. Its event array is an immutable prefix, and its prepared Session remains unpublished. Promotion is an explicit ownership transfer performed by the Session Controller after it has emitted the opening snapshot; other readers cannot turn an observation into a live Agent.
The observation owns no mutation authority. Its event array is an immutable prefix, and its prepared Session remains unpublished. Promotion is an explicit ownership transfer the Session Controller starts once the opening is computed, before delivering it; other readers cannot turn an observation into a live Agent.
Projection work is deliberately `all | none`. `all` computes every registered projection at the observation's event cursor; `none` leaves projection state untouched. There is no per-key preparation state, `projectionKeys` mode, or cached `viewedState`/`viewedValue` layer. A publisher may filter the completed values for an audience, but the underlying observation is never partly projected.
@@ -72,7 +72,7 @@ The registry owns fold state; each domain owns its `init`, `apply`, `view`, sche
Corpus listing remains a separate lightweight operation. `listSessions()` returns live-preferred headers without materializing every log. Session list first uses live projection state or durable projection-cache rows. Subagent discovery reads parent-catalog projections through exact observations. Session list may take one complete observation for an individually stored artifact within its configured small-log limit when cached metadata cannot establish whether it is blank; a large or unreadable cache miss remains visible with unknown hints.
`session.follow` publishes a required opening snapshot containing header, cursor, the initial event window, and a complete projection baseline. Reconnect replaces the previous generation from another complete snapshot. `session.page` is reserved for older-history reads and gap repair. Observation-only reads never activate an Agent; only an ordinary follow may retain its prepared observation and request promotion after the opening snapshot has been delivered.
`session.follow` publishes a required opening snapshot containing header, cursor, the initial event window, and a complete projection baseline. Reconnect replaces the previous generation from another complete snapshot. `session.page` supports current or fixed-cut history reads, older-history reads, and gap repair. Observation-only reads never activate an Agent; only an activation-enabled ordinary Follow may retain its prepared observation and request promotion once the opening snapshot is computed, before it is delivered. The [snapshot and subscription decision](2026-10-21-session-history-stream-and-snapshot.md) defines the Controller composition and passive subscription semantics.
### Read audiences
@@ -82,9 +82,10 @@ Each public operation chooses one query and projection policy. The choice is par
|---|---|---|---|
| `session.list` | Corpus headers, live state, and cached rows; bounded small-log fallback | Partial hints, or one full small-log observation | Never |
| `session.search` | Corpus authorization plus the configured search provider | None for result listing | Never |
| `session.follow` | One exact observation | All, carried in the opening snapshot | Ordinary cold Session only, after snapshot delivery |
| `session.follow` | One exact observation | All, carried in the opening snapshot | Only with `activate: true`, for an ordinary cold Session once the opening is computed, before delivery |
| `session.page` | One exact observation | None, except projection-backed subagent authorization | Never |
| Attachment and fork source | One exact observation | None unless authorization requires it | Never for the source |
| Fork source | One exact observation | None unless authorization requires it | Never for the source |
| Attachment | Direct store read under the [image access rule](2026-10-23-store-scoped-image-reads.md) | None | Never |
| Subagent list and continuation | Parent-catalog observations for listing; target observations for continuation | All; listing consumes catalog values, continuation consumes identity and inherited values | Never for listing; continuation follows its explicit command semantics |
### Replayable Client facts are projection-owned
@@ -171,7 +172,7 @@ These rules apply to new Session-derived Client state even when a direct event s
## Verification
Persistence and SessionQuery tests pin shared cold loading, cancellation, live-source races, retained observations, disposal, and all-or-none projection calculation. Session Controller and Gateway tests pin snapshot-first opening, replacement reconnect, older-page reads, gap repair, list-cache hints, bounded small-log fallback, and promotion after snapshot delivery.
Persistence and SessionQuery tests pin shared cold loading, cancellation, live-source races, retained observations, disposal, and all-or-none projection calculation. Session Controller and Gateway tests pin snapshot-first opening, replacement reconnect, older-page reads, gap repair, list-cache hints, bounded small-log fallback, and promotion once the opening is computed.
Client tests pin higher-sequence-wins projection storage, title updates, model catalog and selection readiness, preset roster refresh and Session-specific selection, and subagent loading without transient offline presentation. Subagent tests pin parent-catalog order, recursive traversal, branch diagnostics, cancellation, fork isolation, and no Agent activation during listing.
@@ -179,7 +180,7 @@ Client tests pin higher-sequence-wins projection storage, title updates, model c
**Keep source resolution in each consumer.** Rejected because every caller would continue to implement its own live race, persistence error mapping, preparation lifetime, cancellation, and projection cut, allowing both duplicate work and inconsistent results.
**Activate an Agent for every exact read.** Rejected because list, history, attachment, search, and subagent inspection are read operations. Activation loads plugins and changes process state, and it has no natural retirement point for pagination or catalog reads.
**Activate an Agent for every exact read.** Rejected because list, history, search, and subagent inspection are read operations. Activation loads plugins and changes process state, and it has no natural retirement point for pagination or catalog reads.
**Prepare only requested projection keys.** Rejected because a partially projected Session creates another lifecycle state that every cache, restore, plugin-registration, and caller path must track. Projection units are pure and few; computing all registered units for an exact observation is simpler than maintaining `O(E*k)` partial state instead of `O(E*P)` complete state.
@@ -24,7 +24,7 @@ Session 精确读取使用可保留的 `SessionObservation`,向 Client 暴露
flowchart LR
List["list / search"] --> Corpus["SessionQuery corpus"]
Follow["follow"] --> Observe["observeSession"]
Page["page / attachment / fork"] --> Observe
Page["page / fork"] --> Observe
Subagent["subagent list / continuation"] --> Observe
Corpus --> Cache["projection cache hints"]
Cache --> ClientList["Client Session list"]
@@ -46,7 +46,7 @@ flowchart LR
`SessionQueryEngine.observeSession(sessionId, options)` 返回可 dispose(资源释放)的 `SessionObservation`,其中包含同一份 source kind、header、连续事件前缀、cursor、可选 projection snapshot,以及 prepared source 的持久化 revision。已挂载 Session 优先;否则由读取方自己的 prepared cache——以 `stat().revision` 为键、由 observation lease 固定——提供冷 Session,让并发 observation 共享同一次持久化读取(`open(id, 'read')` + `read`),包括尚未完成的冷加载。
每个 owner 都会 dispose 自己的 observation。`retain()` 为同一切面创建另一份 lease,使 `session.follow` 能够先发布 snapshot,再把完全相同的 prepared source 转交给后台 Agent promotion,而无需重读日志。冷解析期间出现的 live Session 会在发布前胜出;已经消失的 live source 会按 cold source 重试。
每个 owner 都会 dispose 自己的 observation。`retain()` 为同一切面创建另一份 lease,使 `session.follow` 能把完全相同的 prepared source 转交给后台 Agent promotion,并在交付 snapshot 前启动它,而无需重读日志。冷解析期间出现的 live Session 会在发布前胜出;已经消失的 live source 会按 cold source 重试。
### 数据源解析与生命周期
@@ -56,7 +56,7 @@ flowchart LR
只有在不存在已挂载 Session 后,persistence absence 才映射为 Session-not-found。持久数据损坏、source identity 冲突、取消和 persistence 操作失败分别保留不同的 `SessionQueryError`,API owner 因而可以维持自身公开错误词汇,而不用重复数据源判定。
Observation 不拥有任何 mutation 权限。其事件数组是不可变前缀,prepared Session 保持未发布。Promotion 是 Session Controller 在 opening snapshot 发出后执行的显式 ownership transfer;其他读方不能把 observation 变成 live Agent。
Observation 不拥有任何 mutation 权限。其事件数组是不可变前缀,prepared Session 保持未发布。Promotion 是 Session Controller 在 opening 计算完成后、交付前启动的显式 ownership transfer;其他读方不能把 observation 变成 live Agent。
Projection 工作明确只有 `all | none` 两种模式。`all` 在 observation 的事件 cursor 上计算所有已注册 projection;`none` 完全不触碰 projection 状态。系统不存在按 key preparation 的状态、`projectionKeys` 模式或额外的 `viewedState`/`viewedValue` cache。发布方可以按 audience 筛选已完成的值,但底层 observation 不会处于只算完部分 projection 的状态。
@@ -72,7 +72,7 @@ Registry 拥有 fold state;各领域拥有自己的 `init`、`apply`、`view`
语料库 list 仍是独立的轻量操作。`listSessions()` 返回 live-preferred header,而不物化每份日志。Session list 先读取 live projection 状态或持久 projection-cache row。Subagent 发现通过精确 observation 读取父目录 projection。当 cache 无法判断 Session 是否为空,且该 Session 拥有的独立产物未超过配置的小日志限制时,Session list 可以执行一次完整 observation;大型或不可读的 cache miss 仍以 hints 未知但 row 可见的方式返回。
`session.follow` 发布必需的 opening snapshot,其中包含 header、cursor、首个事件窗口和完整 projection baseline。重连使用另一份完整 snapshot 替换上一 generation。`session.page` 仅用于旧历史读取与 gap repair。只读 observation 不激活 Agent;只有普通 follow 可以保留 prepared observation,并在 opening snapshot 已交付后请求 promotion。
`session.follow` 发布必需的 opening snapshot,包含 header、cursor、初始事件窗口与完整 projection baseline。重连通过另一份完整快照替换之前的 generation。`session.page` 支持当前或固定切点的历史读取、旧历史分页及缺口修复。只读 Observation 不激活 Agent;只有显式启用激活的普通 Follow 可以在开场计算完成后、交付之前 retain prepared observation 并请求提升。[快照与订阅决策](2026-10-21-session-history-stream-and-snapshot.zh.md)定义 Controller 组合和纯订阅语义。
### 读取 audience
@@ -82,9 +82,10 @@ Registry 拥有 fold state;各领域拥有自己的 `init`、`apply`、`view`
|---|---|---|---|
| `session.list` | Corpus header、live state 和 cached row;有界小日志 fallback | 部分 hints,或一次完整小日志 observation | 从不 |
| `session.search` | Corpus 鉴权加已配置 search provider | 结果列表不计算 | 从不 |
| `session.follow` | 一份精确 observation | 全算,并由 opening snapshot 携带 | 仅普通 cold Session,且在 snapshot 交付后 |
| `session.follow` | 一份精确 observation | 全算,并由 opening snapshot 携带 | 仅 `activate: true` 的普通 cold Session,且在开场计算完成后、交付前 |
| `session.page` | 一份精确 observation | 不计算,但 projection-backed subagent 鉴权除外 | 从不 |
| Attachment 与 fork source | 一份精确 observation | 鉴权不要求时不计算 | source 从不激活 |
| Fork source | 一份精确 observation | 鉴权不要求时不计算 | source 从不激活 |
| Attachment | 按[图片访问规则](2026-10-23-store-scoped-image-reads.zh.md)直接读取存储 | 不计算 | 从不 |
| Subagent list 与 continuation | Listing 观察父目录;continuation 观察目标 | 全算;listing 消费目录值,continuation 消费身份与继承值 | Listing 从不;continuation 遵循显式命令语义 |
### 可回放的 Client 事实归 projection 所有
@@ -171,7 +172,7 @@ Client 本地交互状态也继续留在本地:loading 和 error 状态、打
## 验证
Persistence 与 SessionQuery 测试固定共享冷加载、取消、live-source race、retained observation、dispose 和 all-or-none projection 计算。Session Controller 与 Gateway 测试固定 snapshot-first opening、replacement reconnect、旧分页读取、gap repair、list-cache hints、小日志有界 fallback,以及 snapshot 交付后的 promotion。
Persistence 与 SessionQuery 测试固定共享冷加载、取消、live-source race、retained observation、dispose 和 all-or-none projection 计算。Session Controller 与 Gateway 测试固定 snapshot-first opening、replacement reconnect、旧分页读取、gap repair、list-cache hints、小日志有界 fallback,以及开场计算完成后的 promotion。
Client 测试固定 higher-sequence-wins projection store、title 更新、model catalog 与 selection readiness、preset roster refresh 与 Session 专属选择,以及不会短暂展示离线状态的 subagent loading。Subagent 测试固定父目录顺序、递归遍历、分支诊断、取消、fork 隔离,以及 listing 期间不激活 Agent。
@@ -179,7 +180,7 @@ Client 测试固定 higher-sequence-wins projection store、title 更新、model
**由各消费方继续解析数据源。** 否决,因为每个调用方都需要重复实现 live race、persistence error mapping、preparation lifetime、cancellation 和 projection cut,既会重复工作,也会产生不一致结果。
**每次精确读取都激活 Agent。** 否决,因为 list、history、attachment、search 与 subagent inspection 都是读取操作。Activation 会加载插件并改变进程状态,也没有适合分页或 catalog 读取的自然退出点。
**每次精确读取都激活 Agent。** 否决,因为 list、history、search 与 subagent inspection 都是读取操作。Activation 会加载插件并改变进程状态,也没有适合分页或 catalog 读取的自然退出点。
**只 prepare 被请求的 projection key。** 否决,因为只完成部分 projection 的 Session 会增加一种生命周期状态,所有 cache、restore、plugin registration 和调用路径都必须追踪它。Projection unit 数量少且为纯函数;精确 observation 计算全部已注册 unit,比为了 `O(E*k)` 而维护部分状态、取代 `O(E*P)` 完整状态更简单。
@@ -6,17 +6,17 @@
en: b19652a0e1cc1826
zh: 4d3511e4f0db5b9d
/agent-note-one-outbound-proxy-policy-installed-before-anything-can-request/problem:
en: 70e5f3ed33010c98
zh: 5437d943953f5a40
en: 6379f0a2522f4c46
zh: c45ffeb5089be274
/agent-note-one-outbound-proxy-policy-installed-before-anything-can-request/decision:
en: efe23161a54f70c5
zh: e24ee097918b2724
en: 6435c336ab53ca0f
zh: 57070799a7061f85
/agent-note-one-outbound-proxy-policy-installed-before-anything-can-request/alternatives-considered:
en: df44eb15557b95fe
zh: b757e53da4c0a99f
en: df6c078f239a11f5
zh: 914da26836aa373b
/agent-note-one-outbound-proxy-policy-installed-before-anything-can-request/consequences:
en: 6a4acbab915f1bd1
zh: 2c8ffd62ba74ff96
en: ff1898a64e5db79c
zh: 0a77d02385fb3506
/agent-note-one-outbound-proxy-policy-installed-before-anything-can-request/testing:
en: 0d7eaa666c94a5cd
zh: 210fa098f154d137
en: fddb0be6abbd809e
zh: 88843f0f3ea60555
@@ -10,7 +10,7 @@ Node's built-in `fetch` ignores `HTTP_PROXY` and `HTTPS_PROXY`. Every other tool
The repository had briefly had an answer and lost it without noticing. PR #971 set `NODE_USE_ENV_PROXY=1` in `bin/dsh`; eleven days later the “cleanup: remove managed source installer” change deleted that launcher wholesale, taking the flag with it. What survived was one sentence in `apps/cli/reference/README.md` telling the reader to set a variable that nothing consumed any more.
That sentence could not have worked anyway, for three measured reasons. `NODE_USE_ENV_PROXY` samples the environment at process start, while `loadLayeredEnv()` merges the `.env` layers afterwards, so a proxy declared in `$DSH_HOME/.env` is invisible to it. It reaches Node 24.0+ and, on the 22 line, only 22.21+ — while `engines` admits `^22.19.0`, where the variable does not exist and setting it warns about nothing. And it does not reach `web-fetch-http` at all: that provider passes its own `dispatcher` to `fetch`, and an explicit dispatcher overrides the global one whatever the flag says.
That sentence could not have worked anyway, for three measured reasons. `NODE_USE_ENV_PROXY` samples the environment at process start, while `loadLayeredEnv()` merges the `.env` layers afterwards, so a proxy declared in `$DSH_HOME/.env` is invisible to it. It reaches Node 24.0+ and, on the 22 line, only 22.21+ — the runtime floor at adoption was Node 22.19, where the variable did not exist and setting it warned about nothing. And it does not reach `web-fetch-http` at all: that provider passes its own `dispatcher` to `fetch`, and an explicit dispatcher overrides the global one whatever the flag says.
## Decision
@@ -36,7 +36,7 @@ This keeps `proxyForUrl()` and the dispatcher answering from one set of values.
**Rejection is quiet, and never reroutes the refused scheme.** A slot the user filled and this package refused keeps that scheme direct rather than falling through to `ALL_PROXY` or the HTTP proxy, so the diagnostic and the route agree. A SOCKS URL, an unparseable string, or an unsupported scheme is reported on stderr and skipped — the variable may have been exported for other tools, and a typo in it must not stop the agent from starting. The environment is the only source, so no configuration surface exists where `AGENTS.md`'s fail-loud rule would apply instead.
**Through a proxy, `web_fetch` stops resolving and pinning.** The provider validates a public address set and pins the connection to it. Through a proxy there is nothing to pin — the proxy performs the origin's DNS — and a pinned direct connection would bypass the proxy entirely. So a proxied hop skips resolution, and configuring a proxy is a statement that the proxy is trusted with destination selection. A hop the policy bypasses, which includes every loopback and every `NO_PROXY` entry, takes the resolved-and-pinned path unchanged. Kimi Code and Claude Code reached this same conclusion independently.
**`web_fetch` uses the installed dispatcher.** The provider sends every request through the process-wide dispatcher, so a proxied hop goes through the proxy, which performs the origin's DNS, and a hop the policy bypasses, which includes every loopback and every `NO_PROXY` entry, connects directly. The provider does not restrict destination addresses ([web capability seam](2026-06-24-web-capability-seam.md#restrict-fetch-to-public-addresses)).
The URL-level policy is untouched: `http(s)` only, no embedded credentials, the length cap, and the cross-origin redirect refusal all still apply on every hop.
@@ -48,17 +48,17 @@ This accepts a documented seam. Such a context matches bypass entries by Node's
**SDK transports need independent verification.** The OTLP exporter selects `http-exporter-transport` on Node, which posts through `node:http` and bypasses the global fetch dispatcher; a `globalThis.fetch` reference in its browser implementation does not prove Node routing. The [E2B removal](../simplification/2026-09-11-remove-e2b-providers.md) retires a second SDK transport integration without changing this requirement.
**Telemetry stays direct on purpose.** Routing it needs one of two things, and both cost more than the channel is worth. An `http.Agent` reads the environment through `proxyEnv`, which arrived in Node 22.21 and 24.5 — inside the engines range, so 22.19, 22.20, and 24.0–24.4 would stay direct regardless, and the proxy package would have to keep a `createNodeHttpAgent` export for a path that works on some runtimes. Replacing the transport with the SDK's `fetch` delegate covers every runtime, but that delegate has no compression, and the shipped `base` bundle enables gzip: a realistic OTLP batch measures 6.4x smaller with it. An attempt that refused `exporter.compression` instead broke every test that boots the shipped bundle, and one that gzipped at the serializer worked but put transport code in a telemetry plugin to keep it working.
**Telemetry stays direct on purpose.** Routing it needs one of two things, and both cost more than the channel is worth. An `http.Agent` reads the environment through `proxyEnv`, which arrived in Node 22.21 and 24.5 — above the runtime floor at adoption, so Node 22.19, 22.20, and 24.0–24.4 stayed direct regardless, and the proxy package would have to keep a `createNodeHttpAgent` export for a path that worked on some runtimes. The Node 24.9 floor removes this API gap; routing still needs independent transport integration and must preserve compression. Replacing the transport with the SDK's `fetch` delegate covers every runtime, but that delegate has no compression, and the shipped `base` bundle enables gzip: a realistic OTLP batch measures 6.4x smaller with it. An attempt that refused `exporter.compression` instead broke every test that boots the shipped bundle, and one that gzipped at the serializer worked but put transport code in a telemetry plugin to keep it working.
Weighed against that, telemetry is the one outbound channel whose loss costs the user nothing: no tool, no model request, and no session depends on it, and an export that cannot connect is already dropped silently. A user behind a mandatory proxy is left exactly where they were before this change rather than regressed. `egress.spec.ts` now asserts the exclusion — an SDK upgrade that moved the exporter onto `fetch` would start routing telemetry through a proxy silently, and that case is what makes it visible.
**Every call site carries an egress test.** Each owning package's `egress.spec.ts` drives its actual transport through a fake proxy and checks the observed route. These tests cover search backends, pi-ai discovery, MCP over HTTP, child Node processes, worker threads and telemetry's direct-route exception. They detect dependency transport changes that a static call-site check cannot observe.
**A gate keeps the defect from returning.** `verify-no-bare-dispatcher` parses the TypeScript AST — `scripts/AGENTS.md` requires syntax-aware discovery, and a line-wise regex missed both the `{ dispatcher }` shorthand this repository already uses and a `new Alias(...)` behind a renamed import. It rejects an undici agent construction and an explicit `dispatcher` option outside the owning package. `proxyRouteFor(url)` is the sanctioned replacement, and the one call site that genuinely owns its transport — `web-fetch-http`, pinning a request to addresses it validated — says so with a `proxy-exempt:` comment. The rule exists because `web-fetch-http`'s original `new Agent` was entirely reasonable when it was written — proxying simply did not exist yet, and nothing would have caught it.
**A gate keeps the defect from returning.** `verify-no-bare-dispatcher` parses the TypeScript AST — `scripts/AGENTS.md` requires syntax-aware discovery, and a line-wise regex missed both the `{ dispatcher }` shorthand this repository already uses and a `new Alias(...)` behind a renamed import. It rejects an undici agent construction and an explicit `dispatcher` option outside the owning package. `proxyRouteFor(url)` is the sanctioned replacement, and a call site that genuinely owns its transport says so with a `proxy-exempt:` comment. The rule exists because `web-fetch-http`'s original `new Agent` was entirely reasonable when it was written — proxying simply did not exist yet, and nothing would have caught it.
## Alternatives considered
**Document `NODE_USE_ENV_PROXY=1` and stop.** Rejected on three measurements, above: invisible to `$DSH_HOME/.env`, absent on the lowest supported Node, and bypassed by `web-fetch-http` regardless. It is also what the repository already claimed to do.
**Document `NODE_USE_ENV_PROXY=1` and stop.** Rejected at adoption on the three measurements above: invisible to `$DSH_HOME/.env`, absent on the Node 22.19 floor, and bypassed by `web-fetch-http` regardless. The environment-snapshot and explicit-dispatcher limitations remain on the current runtime floor.
**Thread a policy value to every call site.** DeepSeek-Reasonix does this across 98 sites, buying a per-provider opt-out. Rejected: that opt-out exists for a need this harness does not have, and nine sites changed by hand means the tenth is forgotten — Pi's changelog records OAuth and Bedrock as two separate after-the-fact fixes of exactly that kind. The isolation argument for it is real, and is answered instead by handling worker threads explicitly and by proving disposal restores the previous dispatcher.
@@ -78,8 +78,6 @@ A user who exports `HTTPS_PROXY`, or writes it into `$DSH_HOME/.env`, is proxied
Because the operating system's settings are not read, the user-facing documentation is now load-bearing rather than supplementary: a user who only toggled "system proxy" in a proxy application gets nothing and no diagnostic. `docs/user/guide/network-proxy.md` therefore states which variables to export and why a browser is proxied when a terminal is not — the three-mechanism confusion is the single most common report, and it is not specific to this harness.
`web_fetch`'s safety story now has two shapes, and its README says so: direct hops keep address validation and pinning, proxied hops delegate destination selection to a proxy the operator configured. This is the one outward-facing security promise the change alters.
Reaching Node's built-in `fetch` from a userland undici depends on both writing the legacy `Symbol.for('undici.globalDispatcher.1')` slot. That is an implicit cross-version coupling rather than a contract — corepack#834 records it breaking — so `tests/install.spec.ts` drives a real request through a loopback proxy. A version bump that breaks the coupling fails there instead of in the field.
The suite is hermetic against the developer's own environment: every Vitest configuration runs `scripts/test-proxy-environment.ts`, which clears all eight proxy names in both casings before any test, and `install.spec.ts` restores the machine's values around each case that sets its own. It has to. An exported lowercase `all_proxy` decided a test's outcome during development, because resolution reads lowercase first.
@@ -88,7 +86,7 @@ The suite is hermetic against the developer's own environment: every Vitest conf
`packages/util/http-proxy` holds 84 tests at 100% per-file coverage. Resolution covers precedence, the `ALL_PROXY` fallback, blank-shadowing, the SOCKS and malformed diagnostics, and the HTTPS-only environment that leaves `http:` direct; routing covers the whole loopback range structurally, and bypass matching covers suffixes, ports, both IPv6 spellings, and the CIDR entry that deliberately does not match. Installation drives a real loopback proxy and asserts the absolute-form request arrives, that a bypassed target does not, and that disposal restores the dispatcher, the policy, and the environment. Every case installs through `installProxyFromEnvironment`, so no test can assert a policy object a real launch could not produce.
`packages/web/web-fetch-http/tests/proxy.spec.ts` asserts the decision that matters most: under a proxy the public-address resolver is never called, while a bypassed hop still calls it exactly once, and the cross-origin redirect refusal survives on the proxied path.
`packages/web/web-fetch-http/tests/proxy.spec.ts` asserts that a proxied hop reaches the proxy, a bypassed hop connects directly, and the cross-origin redirect refusal survives on the proxied path.
`verify-no-bare-dispatcher.spec.ts` proves the gate rejects the exact shape this package was introduced to fix, accepts `proxyRouteFor`, accepts an annotated exemption, and passes on the current tree.
@@ -10,7 +10,7 @@ Node 内置的 `fetch` 会忽略 `HTTP_PROXY` 与 `HTTPS_PROXY`。开发者运
仓库曾短暂拥有过答案,又在无人察觉时弄丢了。PR #971 在 `bin/dsh` 里设置了 `NODE_USE_ENV_PROXY=1`;十一天后的“cleanup: remove managed source installer”改动整体删除了那个启动器,把该标志一并带走。留下的只有 `apps/cli/reference/README.md` 里的一句话,让读者去设置一个已经无人消费的变量。
即便照做,那句话也不可能生效,原因有三条且都经过实测。`NODE_USE_ENV_PROXY` 在进程启动时对环境取快照,而 `loadLayeredEnv()` 是在之后才合并 `.env` 层,因此写在 `$DSH_HOME/.env` 中的代理对它不可见。它只覆盖 Node 24.0+,在 22 线上只覆盖 22.21+——而 `engines` 允许 `^22.19.0`,那里根本没有这个变量,设置了也不会有任何警告。它也完全触及不到 `web-fetch-http`:该提供方向 `fetch` 传入自己的 `dispatcher`,而显式 dispatcher 无论标志如何都会覆盖全局的那个。
即便照做,那句话也不可能生效,原因有三条且都经过实测。`NODE_USE_ENV_PROXY` 在进程启动时对环境取快照,而 `loadLayeredEnv()` 是在之后才合并 `.env` 层,因此写在 `$DSH_HOME/.env` 中的代理对它不可见。它只覆盖 Node 24.0+,在 22 线上只覆盖 22.21+——采用此决策时的运行时下限为 Node 22.19,那里根本没有这个变量,设置了也不会有任何警告。它也完全触及不到 `web-fetch-http`:该提供方向 `fetch` 传入自己的 `dispatcher`,而显式 dispatcher 无论标志如何都会覆盖全局的那个。
## Decision
@@ -36,7 +36,7 @@ Node 内置的 `fetch` 会忽略 `HTTP_PROXY` 与 `HTTPS_PROXY`。开发者运
**拒绝是静默的,且绝不为被拒协议改道。** 用户填写而被本包拒绝的槽位,会让该协议保持直连,而不是继续回退到 `ALL_PROXY` 或 HTTP 代理,从而让诊断与实际路由一致。SOCKS URL、无法解析的字符串或不受支持的协议,会在 stderr 上报告并跳过——该变量可能是为其他工具导出的,它的笔误不应阻止 agent 启动。环境是唯一来源,因此不存在一个本应适用 `AGENTS.md` 「配置错误必须响」规则的配置面。
**经由代理时,`web_fetch` 不再解析与固定地址。** 该提供方会校验一组公网地址并把连接固定到其上。经由代理时没有可固定的对象——origin 的 DNS 由代理执行——而固定后的直连会彻底绕开代理。因此代理转发的一跳跳过解析,配置代理即表示信任该代理进行目的地选择。被策略绕过的一跳,包括每一个 loopback 与每一条 `NO_PROXY` 条目,仍走原有的解析并固定路径。Kimi Code 与 Claude Code 各自独立得出了同一结论。
**`web_fetch` 使用已安装的 dispatcher。** 该提供方的每个请求都经过进程级 dispatcher,因此代理转发的一跳经过代理,由代理执行 origin 的 DNS;被策略绕过的一跳,包括每一个 loopback 与每一条 `NO_PROXY` 条目,则直接连接。该提供方不限制目的地址([web capability seam](2026-06-24-web-capability-seam.zh.md#restrict-fetch-to-public-addresses))。
URL 层策略未受影响:仅 `http(s)`、禁止内嵌凭据、长度上限与跨域重定向拒绝在每一跳上依然生效。
@@ -48,17 +48,17 @@ URL 层策略未受影响:仅 `http(s)`、禁止内嵌凭据、长度上限与
**SDK 传输需要独立验证。** OTLP 导出器在 Node 上选择 `http-exporter-transport`,通过 `node:http` 投递并绕过全局 fetch dispatcher;其浏览器实现中的 `globalThis.fetch` 引用不能证明 Node 路由。[E2B 移除决策](../simplification/2026-09-11-remove-e2b-providers.zh.md)撤下另一项 SDK 传输集成,但不改变这一要求。
**遥测的直连是有意为之。** 要让它走代理只有两条路,代价都超过这条通道本身的价值。`http.Agent` 通过 `proxyEnv` 读取环境,而该选项自 Node 22.21 与 24.5 才有——落在 engines 范围之内,因此 22.19、22.20 与 24.0–24.4 无论如何仍是直连,而代理包还得为一条只在部分运行时生效的路径保留 `createNodeHttpAgent` 导出。改用 SDK 的 `fetch` delegate 替换传输可以覆盖所有运行时,但该 delegate 没有压缩能力,而随附的 `base` bundle 启用了 gzip:实测一批真实规模的 OTLP 数据启用后体积只有 1/6.4。曾有一版转而在加载期拒绝 `exporter.compression`,结果凡是启动随附 bundle 的测试全部失败;另一版在 serializer 处 gzip 确实能跑通,但代价是把传输层代码塞进了遥测插件。
**遥测的直连是有意为之。** 要让它走代理只有两条路,代价都超过这条通道本身的价值。`http.Agent` 通过 `proxyEnv` 读取环境,而该选项自 Node 22.21 与 24.5 才有——高于采用此决策时的运行时下限,因此 Node 22.19、22.20 与 24.0–24.4 无论如何仍是直连,而代理包还得为一条只在当时部分运行时生效的路径保留 `createNodeHttpAgent` 导出。Node 24.9 下限消除了这一 API 缺口;代理路由仍需要独立集成传输,并保留压缩。改用 SDK 的 `fetch` delegate 替换传输可以覆盖所有运行时,但该 delegate 没有压缩能力,而随附的 `base` bundle 启用了 gzip:实测一批真实规模的 OTLP 数据启用后体积只有 1/6.4。曾有一版转而在加载期拒绝 `exporter.compression`,结果凡是启动随附 bundle 的测试全部失败;另一版在 serializer 处 gzip 确实能跑通,但代价是把传输层代码塞进了遥测插件。
与之相比,遥测是唯一一条丢失了对用户毫无代价的出网通道:没有任何工具、模型请求或会话依赖它,而连不上的导出本就被静默丢弃。处在强制代理后的用户,只是停留在本次改动之前的状态,而不是被弄坏。`egress.spec.ts` 现在断言这一排除——若某次 SDK 升级把导出器挪到 `fetch` 上,遥测就会开始静默走代理,而该用例正是让这件事暴露出来的东西。
**每个出网点都配有出网测试。** 各所属包中的 `egress.spec.ts` 通过假代理驱动实际传输,并检查观察到的路由。这些测试覆盖搜索后端、pi-ai 发现、走 HTTP 的 MCP、子 Node 进程、worker 线程和遥测的直连例外。它们可发现静态调用点检查无法观察到的依赖传输变化。
**用门禁防止该缺陷复现。** `verify-no-bare-dispatcher` 解析 TypeScript AST——`scripts/AGENTS.md` 要求 source-ownership 门禁使用语法感知发现,而逐行正则漏掉了本仓库已在使用的 `{ dispatcher }` 简写,以及重命名导入后的 `new Alias(...)`。它在所属包之外拒绝 undici agent 构造与显式 `dispatcher` 选项。`proxyRouteFor(url)` 是受支持的替代;唯一一处确实自有传输的调用点——`web-fetch-http`,它把请求钉在已校验的地址上——用 `proxy-exempt:` 注释说明。这条规则之所以存在,是因为 `web-fetch-http` 里原本那行 `new Agent` 在写下时完全合理——那时根本还没有代理这回事,也没有任何机制会拦下它。
**用门禁防止该缺陷复现。** `verify-no-bare-dispatcher` 解析 TypeScript AST——`scripts/AGENTS.md` 要求 source-ownership 门禁使用语法感知发现,而逐行正则漏掉了本仓库已在使用的 `{ dispatcher }` 简写,以及重命名导入后的 `new Alias(...)`。它在所属包之外拒绝 undici agent 构造与显式 `dispatcher` 选项。`proxyRouteFor(url)` 是受支持的替代;确实自有传输的调用点用 `proxy-exempt:` 注释说明。这条规则之所以存在,是因为 `web-fetch-http` 里原本那行 `new Agent` 在写下时完全合理——那时根本还没有代理这回事,也没有任何机制会拦下它。
## Alternatives considered
**只写文档,让用户设 `NODE_USE_ENV_PROXY=1`。** 基于上文三条实测被否决:对 `$DSH_HOME/.env` 不可见、在最低支持的 Node 上不存在、且无论如何被 `web-fetch-http` 绕过。而这恰恰是仓库此前声称的做法。
**只写文档,让用户设 `NODE_USE_ENV_PROXY=1`。** 采用此决策时基于上文三条实测被否决:对 `$DSH_HOME/.env` 不可见、在 Node 22.19 下限上不存在、且无论如何被 `web-fetch-http` 绕过。环境快照和显式 dispatcher 的限制在当前运行时下限上仍然存在。
**把策略值传递到每一个调用点。** DeepSeek-Reasonix 在 98 处这样做,换来每提供方的 opt-out。被否决:该能力服务于本 Harness 并不具备的需求,而手工改九处意味着第十处会被遗忘——Pi 的变更日志正记录了 OAuth 与 Bedrock 两次事后补漏。它关于隔离性的论点确实成立,本方案改为显式处理 worker 线程、并以「dispose 后还原前一个 dispatcher」的断言来回应。
@@ -78,8 +78,6 @@ URL 层策略未受影响:仅 `http(s)`、禁止内嵌凭据、长度上限与
由于不读取操作系统设置,面向用户的文档从补充材料变成了承重件:仅在代理软件里拨了「系统代理」开关的用户什么也得不到,且没有诊断。因此 `docs/user/guide/network-proxy.md` 说明了要导出哪些变量,以及为什么浏览器走代理而终端不走——这个「三套机制」的困惑是最常见的报障,且并非本 Harness 特有。
`web_fetch` 的安全叙述现在有两种形态,其 README 已如实说明:直连的一跳保留地址校验与固定,代理转发的一跳把目的地选择交给运维方配置的代理。这是本次变更唯一改动的对外安全承诺。
userland undici 能触及 Node 内置的 `fetch`,依赖于两者都会写入 legacy 的 `Symbol.for('undici.globalDispatcher.1')` 槽位。那是跨版本的隐式耦合而非约定——corepack#834 记录了它失效的实例——因此 `tests/install.spec.ts` 会驱动一次真实请求穿过 loopback 代理。破坏该耦合的版本升级会在那里失败,而不是流到线上。
测试套件对开发者自身的环境免疫:每份 Vitest 配置都会先运行 `scripts/test-proxy-environment.ts`,在任何测试之前清除全部八个代理变量名的两种大小写形式;`install.spec.ts` 则在每个自行设值的用例前后还原本机的值。这是必需的。开发过程中,一个已导出的小写 `all_proxy` 曾决定了某个测试的结果,因为解析优先读取小写。
@@ -88,7 +86,7 @@ userland undici 能触及 Node 内置的 `fetch`,依赖于两者都会写入 l
`packages/util/http-proxy` 有 84 个测试,per-file 覆盖率 100%。解析覆盖优先级、`ALL_PROXY` 兜底、空值遮蔽、SOCKS 与畸形值诊断,以及只设 https 变量时 `http:` 保持直连;路由以结构化方式覆盖整个 loopback 网段,绕过匹配覆盖后缀、端口、两种 IPv6 写法,以及刻意不匹配的 CIDR 条目。安装驱动一个真实的 loopback 代理,断言绝对形式的请求确实抵达、被绕过的目标不抵达,且 dispose 会还原 dispatcher、策略与环境。所有用例一律经 `installProxyFromEnvironment` 安装,因此没有测试能断言一次真实启动无法产生的策略对象。
`packages/web/web-fetch-http/tests/proxy.spec.ts` 断言了最关键的那个决定:经由代理时公网地址解析器完全不被调用,而被绕过的一跳仍恰好调用一次,且跨域重定向拒绝在代理路径上依然成立。
`packages/web/web-fetch-http/tests/proxy.spec.ts` 断言代理转发的一跳抵达代理、被绕过的一跳直接连接,且跨域重定向拒绝在代理路径上依然成立。
`verify-no-bare-dispatcher.spec.ts` 证明该门禁能拒掉本包所要修复的那种写法、接受 `proxyRouteFor`、接受带注释的豁免,并在当前代码树上通过。
@@ -30,8 +30,8 @@
en: 9d2d50cb76754b74
zh: 9ceed3652475f18d
/agent-note-historical-session-reads-prepare-before-write-publication/decision/follow-and-agent-promotion:
en: 692c29db3e7ab07e
zh: 7da0cfdbc80f407d
en: 46da45b9b63e7298
zh: 3d55099479a80bd9
/agent-note-historical-session-reads-prepare-before-write-publication/problem-to-solution-mapping:
en: e067e187cd4d0a8c
zh: b0c79b635a637296
@@ -102,7 +102,7 @@ No external caller can append before the handle exists. `append`, `flush`, and `
### Follow and Agent promotion
`session.follow` opens history through the read path, restores the Session and projections, emits the opening snapshot, and then starts Agent promotion. Agent resume uses write open, so it waits for publication before the Agent accepts a new turn. History visibility and write readiness are separate timing points without introducing an unpublished append state.
`session.follow` opens history through the read path, restores the Session and projections, starts Agent promotion from the separately retained Observation when the caller requests activation, and then delivers the opening snapshot. Agent resume uses write open, so it waits for publication before the Agent accepts a new turn. History visibility and write readiness are separate timing points without introducing an unpublished append state.
## Problem-to-solution mapping
@@ -102,7 +102,7 @@ Handle 返回前,外部 caller 无法 append。因此 `append`、`flush` 与 `
### Follow 与 Agent promotion
`session.follow` 通过 read path 打开历史、恢复 Session 与 projections、发出 opening snapshot,然后启动 Agent promotion。Agent resume 使用 write open,因此会在 Agent 接收新一轮对话前等待 publication。历史可见与写入就绪成为两个明确时间点,同时不引入 unpublished append state。
`session.follow` 通过 read path 打开历史、恢复 Session 与 projections,在调用方请求激活时先用单独 retain 的 Observation 启动 Agent promotion,再交付 opening snapshot。Agent resume 使用 write open,因此会在 Agent 接收新一轮对话前等待 publication。历史可见与写入就绪成为两个明确时间点,同时不引入 unpublished append state。
## 问题与方案对照
@@ -39,8 +39,8 @@
en: ef015f605f7b00ab
zh: eee9c79faebf3dff
/agent-note-workspace-file-service/alternatives-considered:
en: 8eee658526db4465
zh: 4a655accf6a5c08a
en: c750d88e0e746a99
zh: 9e904ad4172fda49
/agent-note-workspace-file-service/consequences:
en: 246bb7aed62f6c9b
zh: debf6d4129ee477b
@@ -117,7 +117,7 @@ The [resource model](2026-09-05-client-resource-model.md) owns `ctx.resources`,
**Serving navigable workspace documents.** Rejected on origin grounds; the [binary transfer](2026-09-17-workspace-file-binary-transfer.md) returns multipart data while HTML remains isolated in a Blob iframe.
**Log-reachable or workspace-contained authorization for file reads.** The one precedent that sends file content over the wire, command attachments, authorizes only files that appear in the session log. That excludes typed paths, while workspace containment excludes readable files elsewhere on the Session backend. The [workspace file read authority](2026-09-09-workspace-file-read-authority.md) instead makes the backend's read decision authoritative and keeps containment only for workspace-shaped operations.
**Log-reachable or workspace-contained authorization for file reads.** Requiring a Session-log reference excludes typed paths, while workspace containment excludes readable files elsewhere on the Session backend. The [workspace file read authority](2026-09-09-workspace-file-read-authority.md) makes the backend's read decision authoritative and keeps containment only for workspace-shaped operations. Image attachment reads separately follow the [store-level access rule](2026-10-23-store-scoped-image-reads.md).
**Whole-file read and slice for the byte window.** The interim form of `readBytes` read the file from its start to the window's end through `readBytes(target, signal, offset + length)` and sliced. It cannot read a window of a file longer than that end — the seam refuses such a file as too large — so no window could ever report `eof: false`, which contradicts the reason the method exists. Rejected in favour of the `readByteRange` seam, whose bound is the window.
@@ -117,7 +117,7 @@ Client 导出向 `ctx.resources` 注册一个 `ResourceProvider<'file'>`,存
**提供可导航的工作区文档。** 因源隔离问题不采用;[二进制传输](2026-09-17-workspace-file-binary-transfer.zh.md)返回 multipart 数据,HTML 仍隔离在 Blob iframe 中。
**文件读取采用“日志可达”或工作区包含授权。** 唯一把文件内容送过线路的先例——命令附件——只授权出现在会话日志里的文件,这会排除手输路径;工作区包含则会排除 Session 后端其他位置的可读文件。[工作区文件读取权限](2026-09-09-workspace-file-read-authority.zh.md)改为以后端读取决策为准,只对工作区形态的操作保留包含限制。
**文件读取采用“日志可达”或工作区包含授权。** 要求 Session 日志引用会排除手输路径;工作区包含则会排除 Session 后端其他位置的可读文件。[工作区文件读取权限](2026-09-09-workspace-file-read-authority.zh.md)以后端读取决策为准,只对工作区形态的操作保留包含限制。图片附件读取另行遵循[存储范围的访问规则](2026-10-23-store-scoped-image-reads.zh.md)。
**为字节窗口整文件读取再切片。** `readBytes` 的临时形态经 `readBytes(target, signal, offset + length)` 从文件开头读到窗口末端再切片。它读不了比该末端更长的文件的窗口——seam 会以过大拒绝这样的文件——因此没有任何窗口能报告 `eof: false`,与该方法存在的理由相悖。被否,改为以窗口为界的 `readByteRange` seam。
@@ -6,8 +6,8 @@
en: 982ac05aa02da2f0
zh: 225f89c994efb1c4
/agent-note-awaited-per-agent-initialization/problem:
en: 03e6b75365b29406
zh: cc880a33cf2303d0
en: 6e9393e78ff71286
zh: b931a7e606cad37e
/agent-note-awaited-per-agent-initialization/decision:
en: 48cdb570d3feb222
zh: 904a1169540e3111
@@ -15,5 +15,5 @@
en: 466a9ebbb13f68c6
zh: ab88fb34786d2c2b
/agent-note-awaited-per-agent-initialization/consequences:
en: d8cfe8d88ccacfc8
zh: 8d2aa21408e6e40e
en: 3fc2b36a94ace3ba
zh: 65bb386b89ff89ce
@@ -6,7 +6,7 @@ English | [中文](2026-09-09-awaited-agent-creation.zh.md)
## Problem
Shared presets install tools and prompt sections separately for each Agent. That installation can await plugin activation, and external SessionStart hooks can produce context asynchronously. A creator must know that these contributions have finished before the Agent's first model request.
Agent-specific plugins can install tools asynchronously, and external SessionStart hooks can produce context asynchronously. A creator must know that these contributions have finished before the Agent's first model request.
## Decision
@@ -28,4 +28,6 @@ This decision owns asynchronous creation timing. The [scope runtime reference](.
Creation latency includes asynchronous plugin and SessionStart work. Initializer failures become caller-visible creation failures, and cancellation relies on listeners settling cooperatively. Notifications already delivered cannot be undone; rollback pairs them with disposal notifications.
Scoped installer tests verify rollback, lifecycle tests verify ordered completion and cancellation, and the SDK serial-created scenario verifies that asynchronous prompt context reaches the first request. Hook tests cover awaited context injection and subprocess disposal.
Lifecycle tests verify initialization rollback, ordered completion and cancellation, and the SDK serial-created scenario verifies that asynchronous prompt context reaches the first request. Hook tests cover awaited context injection and subprocess disposal.
Delegation tools use [shared preset registrations](../bug-fix/2026-10-25-shared-preset-delegation-tools.md); they do not require per-Agent installation. This event still owns asynchronous Agent initialization.
@@ -6,7 +6,7 @@ Status: implemented
## 问题
共享 preset 为每个 Agent(智能体)分别安装工具与提示词段。安装可能要等待插件激活,外部 SessionStart 钩子也可能异步产生上下文。创建方必须知道这些贡献已完成,才能允许 Agent 发起首次模型请求。
针对单个 Agent(智能体)的插件可能异步安装工具,外部 SessionStart 钩子也可能异步产生上下文。创建方必须知道这些贡献已完成,才能允许 Agent 发起首次模型请求。
## 决策
@@ -28,4 +28,6 @@ AgentLoop 在 setup 与创建分发期间保留现有的维护活动。初始化
创建延迟包括异步插件与 SessionStart 工作。初始化器失败成为调用方可见的创建失败,取消依赖监听器协作结算。已经送达的通知无法撤回;回滚会为其配对销毁通知。
作用域安装器测试验证回滚,生命周期测试验证有序完成与取消,SDK serial-created 场景验证异步提示词上下文进入首次请求。钩子测试覆盖需等待的上下文注入与子进程 dispose。
生命周期测试验证初始化回滚、有序完成与取消,SDK serial-created 场景验证异步提示词上下文进入首次请求。钩子测试覆盖需等待的上下文注入与子进程 dispose。
委派工具使用[预设共享注册](../bug-fix/2026-10-25-shared-preset-delegation-tools.zh.md),无须按 Agent 安装。此事件仍负责异步 Agent 初始化。
@@ -9,8 +9,8 @@
en: 6bb727a659ba1503
zh: d2ebf5c8164a807e
/agent-note-current-profile-plugin-management-shares-cli-transactions/decision:
en: 863b4369c39f3933
zh: 7dd44a91ccaaef8f
en: d2e13562b5aef903
zh: 07face30189c07fc
/agent-note-current-profile-plugin-management-shares-cli-transactions/alternatives-considered:
en: 30c609de89084351
zh: 27f2747ad9d9b752
@@ -16,7 +16,7 @@ Configuration watches use Chokidar write stabilization by default. Its ordinary
Profile files remain the persisted state: entry toggles edit only `disabled` in the last override matching the entry id and any module-name assertion, appending when none matches, and bundle toggles edit the ordered string list. Dependency updates do not reactivate retained disabled bundles. A service removal first applies the composition without the bundle and waits for old fibers to finish before deleting the dependency. Saved configuration, pnpm completion and runtime activation have separate outcomes; a failed removal preserves the actual partial state and a diagnostic path, while a failed or cancelled installation restores the profile files it snapshotted.
This extends the [profile bundle composition decision](2026-08-05-profile-plugin-bundles.md). Profiles without HMR keep their process composition, and Desktop package management remains shell-owned. Web controls and explicitly enabled agent tools call the same service. Management operations return results to callers without adding messages to live Agents. The agent tool is enabled in Creator mode and disabled by default in the base bundle and other shipped presets. The browser-only worker preview has no host package installer; its module-proxy table refuses `execa` calls explicitly while retaining the management module for inventory discovery.
This extends the [profile bundle composition decision](2026-08-05-profile-plugin-bundles.md). Profiles without HMR keep their process composition, and Desktop package management remains shell-owned. Web controls and explicitly enabled agent tools call the same service. Management operations return results to callers without adding messages to live Agents. The agent tool is enabled in Standard and Creator modes and disabled by default in the base bundle and other shipped presets. The browser-only worker preview has no host package installer; its module-proxy table refuses `execa` calls explicitly while retaining the management module for inventory discovery.
CLI calls inherit the terminal and authentication environment; service calls retain the subprocess credential scrub and bounded diagnostics. Management records carry error codes and parameters for locale-owned Web presentation. Reconciliation compares entry identity, fiber identity, configuration and diagnostics before and after updating: unchanged inactive entries remain warnings, while newly affected failures reject the operation. Explicit enablement targets must activate.
@@ -16,7 +16,7 @@ Web 和 Agent 控件需要修改运行中的 profile,同时避免另建包安
profile 文件保持为持久状态:条目开关只修改最后一条符合条目 id 及模块名称断言的覆盖项中的 `disabled`,没有匹配项时追加,组合包开关修改有序字符串列表。更新依赖不会重新激活保留的已停用组合包。service 删除组合包时,先应用去掉该组合包的配置,等待旧 fiber 完成卸载后再删除依赖。已保存配置、pnpm 完成状态与运行时激活分别报告;失败的删除保留实际的部分状态与诊断路径,失败或被取消的安装则恢复它快照的 profile 文件。
这扩展了[profile 组合包决策](2026-08-05-profile-plugin-bundles.zh.md)。startup profile 保留进程组合,Desktop 包管理仍由 shell 持有。Web 控件与显式启用的 Agent 工具调用同一 service。管理操作向调用方返回结果,不向存活 Agent 添加消息。创造模式启用该 Agent 工具;base 组合包和其他内置预设默认禁用。纯浏览器 worker 预览没有宿主包安装器;其模块代理表明确拒绝 `execa` 调用,同时保留管理模块用于清单发现。
这扩展了[profile 组合包决策](2026-08-05-profile-plugin-bundles.zh.md)。startup profile 保留进程组合,Desktop 包管理仍由 shell 持有。Web 控件与显式启用的 Agent 工具调用同一 service。管理操作向调用方返回结果,不向存活 Agent 添加消息。标准模式和创造模式启用该 Agent 工具;base 组合包和其他内置预设默认禁用。纯浏览器 worker 预览没有宿主包安装器;其模块代理表明确拒绝 `execa` 调用,同时保留管理模块用于清单发现。
CLI 调用继承终端和认证环境;service 调用保留子进程凭据清理与有界诊断。管理结果提供错误码和参数,由 Web 词典呈现文案。重载前后比较 entry、fiber、配置与诊断:未变化的已有故障保留为警告,本次影响到的新故障使操作失败。显式启用的目标必须成功激活。
@@ -9,8 +9,8 @@
en: edefff7cd986a509
zh: a270839fa86976a2
/agent-note-guided-plugin-installation/decision:
en: 30f1e636973df06a
zh: 0f874b88a5d4d7f5
en: 5448b9f8aaa9a569
zh: 9024c5328c5da3bd
/agent-note-guided-plugin-installation/alternatives-considered:
en: be64b541f4d81e7c
zh: 8600531c08ab4288
@@ -20,7 +20,7 @@ The install dialog put a spec straight into `pnpm add` and showed pnpm's termina
**Cancellation is the manager's, not the signal's.** The dialog generates a request id for each run; the Host streams the run's output and phases under it, and `cancelInstall` answers only after pnpm exited and the files are back. The dialog waits for that answer before offering the spec again; an aborted RPC or a lost connection is not a confirmation. Only the check takes a trailing `AbortSignal`: going back or closing drops a registry lookup, whose settlement nothing waits for.
**Enabling comes after the fact.** The run installs with `enabled: false`; the finished screen offers **Enable now** for the bundle it added, and the dialog closes and the list scrolls to it. Nothing is enabled before the person has seen what was installed.
**Enabling comes after the fact.** The run installs with `enabled: false`; the finished screen offers **Enable now** for the bundle it added; the dialog closes, and the list scrolls to it when its card is on the current page. Nothing is enabled before the person has seen what was installed.
**Outcomes of the moment are toasts.** A change that waits for the next start, one a higher layer overrides, a cancelled run, and a refused action each toast and retire; nothing stays on the page.
@@ -20,7 +20,7 @@ Status: implemented
**取消属于管理器,不属于信号。** 对话框为每次运行生成一个 request id;宿主在它之下流式转发运行的输出与阶段,`cancelInstall` 只在 pnpm 退出且文件恢复后才答复。对话框等到这个答复才把 spec 重新交回;中止的 RPC 或断开的连接都不算确认。只有检查末尾接受 `AbortSignal`:返回编辑或关闭会丢掉一次注册表查询,没有人等它的结果。
**启用在事后。** 运行以 `enabled: false` 安装;完成画面为它新增的组合包提供**立即启用**,对话框关闭,列表滚动到它。在人看到装了什么之前,不会启用任何东西。
**启用在事后。** 运行以 `enabled: false` 安装;完成画面为它新增的组合包提供**立即启用**,对话框关闭;其卡片在当前页面时,列表滚动到它。在人看到装了什么之前,不会启用任何东西。
**当下的结果是 toast。** 要等下次启动的变更、被更高层覆盖的变更、被取消的运行、被拒绝的操作,各弹一条 toast 后自行消失;页面上不留任何东西。
@@ -9,8 +9,8 @@
en: 44615a7a0939054e
zh: b8efa721321ba0d7
/agent-note-creator-mode-installs-persistent-plugin-bundles/decision:
en: c0d74db1b2b16576
zh: b8713a6aadf007f9
en: 38ecabc170c418c7
zh: d380f53fa508328e
/agent-note-creator-mode-installs-persistent-plugin-bundles/alternatives-considered:
en: af527d8aabdfd59c
zh: 34388da992d2ab56
@@ -12,6 +12,8 @@ Agents need to install capabilities and use them during the same conversation. G
Creator mode enables the existing `plugin_manager` tool. Agents author packages and Loader YAML patches as workspace files, then install them through `install_bundle`. An MCP connection is a configuration-only bundle inserting the installed `dsh-mcp-client`; a UI bundle includes a Host entry and a Client artifact. Profile locking, package installation, enablement and HMR remain owned by the existing manager. The entire management tool requires `danger-full-access` or approval for one call: profile changes can load code with host permissions and affect other sessions. Each execution, including inventory reads, uses the shared sandbox escalation helper and approval service before accessing the manager. Under lower sandbox modes, `ask` requests approval and `never` denies; a full-access session needs no additional approval. A grant does not change session permissions, but its profile changes persist. This keeps shell-equivalent Host access explicit without requiring a permanent session-wide elevation. Human-operated Web and CLI controls retain their existing behavior.
The default Standard mode enables the same tool under the same profile condition and approval gate. Without it, Standard sessions did not install plugins directly and spent extra turns and tokens working around the missing tool; the change also moves Standard and Creator toward one capability set. A full-access Standard session can therefore install Host code without a separate prompt, as its shell already can. PTC and Minimal keep the tool disabled.
The model sees two read-only Cordis inspection tools. Generated-code define/run/stop/undefine and dynamic self-inspection tool APIs are absent. Existing runtime and Client consumers retain their services; historical session cards remain readable. This supersedes only the model-facing mutation workflow in the [self-referential toolset reference](../../../../packages/extensions/cordis-host-runner/README.md); its runtime ownership and sandbox rationale remain independently relevant. The [profile transaction decision](2026-09-14-current-profile-plugin-management.md) continues to govern locking, package installation, and partial failures.
Visual creation requests default to an installed Client plugin rendered in the current Web page unless the user names another destination. The development skill supplies a minimal package and effect-owned Client registration. Discovery ends when the required APIs are known; a working first version is installed before optional visual refinement. Verification uses the connected page where available. Browser authentication or operating-system setup is not a prerequisite for plugin installation, and a mock preview cannot establish an in-app result.
@@ -12,6 +12,8 @@ agent 需要安装能力并在同一段对话中使用。生成代码工具在
创造模式启用现有的 `plugin_manager` 工具。agent 将包和 Loader YAML patch 写入工作区文件,再通过 `install_bundle` 安装。MCP 连接是插入已安装 `dsh-mcp-client` 的纯配置组合包;UI 组合包包含 Host 入口和 Client 产物。profile 锁、包安装、启停和 HMR 继续由现有管理器负责。 整个管理工具要求 `danger-full-access` 或单次调用的批准:profile 变更能以宿主权限加载代码,并影响其他会话。每次执行(包括查询列表)都会先使用共享沙箱提权函数和审批服务,再访问管理器。在较低沙箱模式下,`ask` 请求审批,`never` 拒绝;完整权限会话无需额外审批。批准不改变会话权限,但本次操作的 profile 变更会持久化。这让与 shell 等价的宿主访问需要明确授权,同时不必永久提升整个会话的权限。由用户直接操作的 Web 和 CLI 控件保持原有行为。
默认的标准模式以相同的 profile 条件和审批门槛启用同一工具。缺少该工具时,标准模式会话不会直接安装插件,而要额外花费轮次和 token 绕过;此改动也让标准模式和创造模式朝同一能力集收敛。因此,完整权限的标准模式会话无需单独审批即可安装 Host 代码,与其 shell 已有的能力一致。PTC 和 Minimal 仍禁用该工具。
模型可见两个只读 Cordis 检查工具,不再提供生成代码的 define/run/stop/undefine 和动态自省工具 API。现有运行时和 Client 消费者保留其服务;历史会话卡片仍然可读。这仅取代[自引用工具集参考](../../../../packages/extensions/cordis-host-runner/README.zh.md)中的模型侧变更流程;其运行时所有权和沙箱依据仍有独立价值。[profile 事务决策](2026-09-14-current-profile-plugin-management.zh.md)继续规定锁、包安装和部分失败行为。
除非用户指定其他目标,视觉创建请求默认通过已安装的 Client 插件显示在当前 Web 页面。开发 skill 提供最小包和由 effect 管理的 Client 注册示例。已知所需 API 后结束探查,在可选视觉优化之前先安装能工作的初版。有条件时使用已连接页面验证。浏览器认证或操作系统设置不是安装插件的前提,mock 预览不能证明应用内结果。
@@ -72,8 +72,8 @@
en: 607a1afd89892d15
zh: a0aa85124340458c
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/decision/security-and-validation:
en: 46a0c0b3ce1e5671
zh: 7743707de42b1d1d
en: 2426665452a6a37b
zh: 282572d66e17a237
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/decision/in-process-and-worker-carriers:
en: df4ae60d8403cdbb
zh: 70c6024a2a9a1e84
@@ -102,8 +102,8 @@
en: 26dcd8bebdc8d974
zh: e74bcef7c549e70c
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/placement:
en: ecfaff2b9b2ef021
zh: 85b392d79364e0c3
en: 40c838816d0941ed
zh: 4f5428a040b33b74
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/key-sequences:
en: 130e4f9ec7296a10
zh: 2a5aa1302a8db61a
@@ -309,6 +309,7 @@ Credit frames wait until a consumer that needs sustained high-volume uplink appe
| Peer | Admitted at upgrade as one `PeerScope`; `invocation.peer` of every stream on that socket is that Peer |
| Item validation | With an uplink codec, every item is decoded strictly, and a failure fails the whole stream with the existing `gateway/input-invalid` (details `{ endpoint, field: 'uplink' }`); without a codec, only JSON-safety validation applies |
| Frame size | A single frame keeps the `maxPayload` of `ws`; the inbox limit bounds the accumulated total |
| Frame compression | Each connection negotiates `permessage-deflate` when the Gateway config `websocketCompression` is `'deflate'` (the default); frames below 1,024 characters are sent uncompressed. See [the compression decision](2026-10-18-websocket-permessage-deflate.md) |
### In-process and worker carriers
@@ -392,6 +393,8 @@ export type RemoteStreamOpener = (
// index.ts Config addition
/** Uplink inbox limit per logical stream, in UTF-8 bytes of frames (default 262144). */
readonly streamInboxBytes?: number
/** Per-message WebSocket compression; 'none' disables the extension (default 'deflate'). */
readonly websocketCompression?: 'none' | 'deflate'
```
`prepareInvocation`:
@@ -453,7 +456,7 @@ cancel : missing → ignore; otherwise control.abort(new Error('Remote stream ca
| `dsh-typert-registry` | Load-time validation that `uplink.codec` is a valid strict codec |
| `dsh-typert-generator` (`model.ts`, `analyzer.ts`, `emitter.ts`) | `remoteResultType` recognizes `RemoteStream<Out, In>` and generates the `uplink` boundary; the descriptor emits `uplink: { codec }`; the generated Client signature returns `RemoteStreamHandle<Out, In>`; the parameter loop accepts only business parameters and a final `signal` |
| `dsh-client-connection` (`peer.ts`, `rpc.ts`, `rpc-host.ts`, `index.ts`) | The `Peer` class; `connection.browserPeer` and `admit`; the `/api` and upgrade routes obtain the Peer through `admit`; the optional `uplink` of `rpc.open` |
| `dsh-api-gateway` Host (`stream-protocol.ts`, `stream-server.ts`, `types.ts`, `index.ts`) | `item` / `end` frames; `UplinkInbox`; `Config.streamInboxBytes`; `GatewayInvocation` and `extend({ invocation })`; `UplinkDecoder`; `handleUpgrade(req, socket, head, peer)` |
| `dsh-api-gateway` Host (`stream-protocol.ts`, `stream-server.ts`, `types.ts`, `index.ts`) | `item` / `end` frames; `UplinkInbox`; `Config.streamInboxBytes` and `Config.websocketCompression`; `GatewayInvocation` and `extend({ invocation })`; `UplinkDecoder`; `handleUpgrade(req, socket, head, peer)` |
| `dsh-api-gateway` Client (`client/index.ts`, `client/stream-client.ts`) | `invokeStream` returns the handle; the uplink pump is driven by the handle's queue |
| `dsh-webworker-runtime`, `dsh-remote-mock` | The worker tunnel's two uplink frames and the `serveStream` assembly; `StreamHandle.uplink`; the direct proxy returns the real handle |
| Documentation | The READMEs of gateway, the api group, connection, protocol, and generator; `docs/api-gateway`; the type-equiv blocks of `docs/subsystems/typert`; the config and Cordis catalogs |
@@ -309,6 +309,7 @@ credit 帧留待需要持续大流量上行的消费者出现时再加;inbox
| Peer | 升级时接纳为一个 `PeerScope`,该 socket 上所有流的 `invocation.peer` 都是它 |
| 项校验 | 有 uplink codec 时逐项严格解码,失败即整条流以既有的 `gateway/input-invalid` 失败(details `{ endpoint, field: 'uplink' }`);无 codec 时只做 JSON 安全校验 |
| 帧大小 | 单帧沿用 `ws` 的 `maxPayload`;inbox 上限限制累计量 |
| 帧压缩 | Gateway 配置 `websocketCompression` 为 `'deflate'`(默认)时,每条连接协商 `permessage-deflate`;少于 1024 个字符的帧不压缩。见[压缩决定](2026-10-18-websocket-permessage-deflate.zh.md) |
### 进程内与 worker 载体
@@ -392,6 +393,8 @@ export type RemoteStreamOpener = (
// index.ts Config addition
/** Uplink inbox limit per logical stream, in UTF-8 bytes of frames (default 262144). */
readonly streamInboxBytes?: number
/** Per-message WebSocket compression; 'none' disables the extension (default 'deflate'). */
readonly websocketCompression?: 'none' | 'deflate'
```
`prepareInvocation`:
@@ -453,7 +456,7 @@ cancel : missing → ignore; otherwise control.abort(new Error('Remote stream ca
| `dsh-typert-registry` | 加载校验 `uplink.codec` 为有效的严格 codec |
| `dsh-typert-generator`(`model.ts`、`analyzer.ts`、`emitter.ts`) | `remoteResultType` 识别 `RemoteStream<Out, In>` 并生成 `uplink` boundary;描述符输出 `uplink: { codec }`;生成的 Client 签名返回 `RemoteStreamHandle<Out, In>`;参数循环只认业务参数与末位 `signal` |
| `dsh-client-connection`(`peer.ts`、`rpc.ts`、`rpc-host.ts`、`index.ts`) | `Peer` 类;`connection.browserPeer` 与 `admit`;`/api` 与升级路由经 `admit` 取 Peer;`rpc.open` 的可选 `uplink` |
| `dsh-api-gateway` Host(`stream-protocol.ts`、`stream-server.ts`、`types.ts`、`index.ts`) | `item` / `end` 帧;`UplinkInbox`;`Config.streamInboxBytes`;`GatewayInvocation` 与 `extend({ invocation })`;`UplinkDecoder`;`handleUpgrade(req, socket, head, peer)` |
| `dsh-api-gateway` Host(`stream-protocol.ts`、`stream-server.ts`、`types.ts`、`index.ts`) | `item` / `end` 帧;`UplinkInbox`;`Config.streamInboxBytes` 与 `Config.websocketCompression`;`GatewayInvocation` 与 `extend({ invocation })`;`UplinkDecoder`;`handleUpgrade(req, socket, head, peer)` |
| `dsh-api-gateway` Client(`client/index.ts`、`client/stream-client.ts`) | `invokeStream` 返回句柄;上行泵由句柄的队列驱动 |
| `dsh-webworker-runtime`、`dsh-remote-mock` | worker 隧道的两种上行帧与 `serveStream` 组装;`StreamHandle.uplink`;直接代理返回真句柄 |
| 文档 | gateway、api 组、connection、protocol、generator 的 README;`docs/api-gateway`;`docs/subsystems/typert` 的 type-equiv 块;config 与 Cordis 目录 |
@@ -15,5 +15,5 @@
en: b78785f2a603eba8
zh: b8c268228da7e2e7
/agent-note-active-tool-headers-and-stateful-request-projection/consequences:
en: 93c32b795b71a1cc
zh: d42b5c563341c898
en: 60d7bf28fa5a8fd1
zh: a17b222e7aca01e2
@@ -30,7 +30,7 @@ DeepSeek translates projected updates into system-role `tool_addition` and `tool
## Consequences
Tool lists and logical update events are capability-independent; only outgoing declarations and messages vary. Calls to removed retained tools fail through the existing `UNKNOWN_TOOL` path. Changed definitions and incomplete auxiliary prefixes sacrifice prefix reuse for coherent declarations; unchanged restoration keeps the cached prefix on `in-history` routes only while the request series continues. Routes supporting tool updates and in-history system prompts preserve earlier messages when tools are added alongside a changed prompt or after an earlier prompt update. Routes without tool-update support still consolidate on tool changes; explicit series starts and surface replacements still consolidate on every route. No persisted fields or Session format revision are added.
Tool lists and logical update events are capability-independent; only outgoing declarations and messages vary. Calls to removed retained tools fail through the existing `UNKNOWN_TOOL` path. Changed definitions and incomplete auxiliary prefixes sacrifice prefix reuse for coherent declarations; unchanged restoration keeps the cached prefix on `in-history` routes only while the request series continues. Routes supporting tool updates and in-history system prompts preserve earlier messages when tools are added alongside a changed prompt or after an earlier prompt update. Routes without tool-update support still start a new series on tool changes; explicit series starts and surface replacements consolidate the prompt only when the effective text differs, and otherwise leave the surviving prompt node in place. No persisted fields or Session format revision are added.
The authored [headless prompt/tool scenario](../../../../snapshots/session/dynamic-tool-prompt-updates/snapshot.yml) pins an appended prompt beside a deferred tool addition and verifies that the first request's messages stay unchanged. It shares the stable-prompt scenario's tool lifecycle and schema expectations.
@@ -30,7 +30,7 @@ DeepSeek 把投影后的更新转换为 system 角色的 `tool_addition` 和 `to
## Consequences
工具列表和逻辑更新事件不依赖能力;只有发出的声明和消息不同。调用已移除但保留声明的工具时,通过现有 `UNKNOWN_TOOL` 路径失败。定义变化和不完整辅助前缀为保证声明一致而放弃前缀复用;仅当请求序列继续时,未变化的恢复才在 `in-history` 路由上保留缓存前缀。同时支持工具更新和历史内系统提示词的路由,在工具添加与提示词变化同时发生或晚于此前提示词更新时,保留此前消息。不支持工具更新的路由仍在工具变化时归并提示词;所有路由仍在显式新序列和 surface 替换时归并。不增加持久字段或 Session 格式版本。
工具列表和逻辑更新事件不依赖能力;只有发出的声明和消息不同。调用已移除但保留声明的工具时,通过现有 `UNKNOWN_TOOL` 路径失败。定义变化和不完整辅助前缀为保证声明一致而放弃前缀复用;仅当请求序列继续时,未变化的恢复才在 `in-history` 路由上保留缓存前缀。同时支持工具更新和历史内系统提示词的路由,在工具添加与提示词变化同时发生或晚于此前提示词更新时,保留此前消息。不支持工具更新的路由仍在工具变化时开启新序列;显式新序列和 surface 替换只在有效文本不同时归并提示词,否则保留存活的提示词节点。不增加持久字段或 Session 格式版本。
手写的[无界面提示词/工具场景](../../../../snapshots/session/dynamic-tool-prompt-updates/snapshot.yml)钉住追加提示词与延迟工具添加同时发生的行为,并验证首次请求的消息保持不变。它复用稳定提示词场景的工具生命周期和 schema 期望值。
@@ -9,11 +9,11 @@
en: 2ba6c43b20a3d347
zh: 5647179ddaa38ad9
/agent-note-ship-experimental-capabilities-as-optional-bundles/decision:
en: 6c8583bdefc5e36c
zh: 0928b4f2876bbfe4
en: 191ca8bffb101db2
zh: 1365bef5cd830a5c
/agent-note-ship-experimental-capabilities-as-optional-bundles/alternatives-considered:
en: d6f40bf02df37ab7
zh: e5b85ce9d41e8754
/agent-note-ship-experimental-capabilities-as-optional-bundles/consequences:
en: 38d27583533cd028
zh: 9bf11f5489c1aa43
en: 3064da835e892958
zh: bfcf967b6acc6f85
@@ -10,14 +10,16 @@ The Web plugin page offered two optional bundles, Agent Teams and voice input. A
## Decision
`OPTIONAL_BUNDLES` owns the installation's default-off selections, including maintained optional capabilities and experimental capabilities. Every bundle declares `icon` and exports `./locale/*.json` with `meta.title` and `meta.description`; the Official group displays that metadata. A bundle's presentation does not determine its [experimental status](../../../../packages/experimental/README.md#status). The isolation check validates admission and still rejects experimental dependencies of nonexperimental bundles.
`OPTIONAL_BUNDLES` owns the installation's default-off selections, including maintained optional capabilities and experimental capabilities. Every bundle declares `icon` and exports `./locale/*.json` with `meta.title` and `meta.description`; the [plugin manager page](../../../../packages/client/ui-plugin-manager/README.md#use-this-package) displays that metadata for eligible bundles and retains explicit activation for default-hidden ones. A bundle's presentation does not determine its [experimental status](../../../../packages/experimental/README.md#status). The isolation check validates admission and still rejects experimental dependencies of nonexperimental bundles.
An optional bundle's dependencies are downloaded with every `dsh` installation. [Reasoning translation](2026-10-05-anonymous-reasoning-translation.md) uses three experimental packages without adding third-party runtime dependencies. Admission therefore considers installation cost and whether a switch supplies a usable composition without an additional configuration form. Browser and computer providers remain explicit compositions because they add substantial runtimes or require an external executable. Stagehand requires model credentials; Python PTC replaces a core provider and conflicts with TypeScript workflow consumers. Demonstrative hooks, webhooks, and mods stay outside the list. [Session Inspector](../feature/2026-09-24-session-inspector.md) owns the Inspector's distinct activation behavior.
An optional bundle's dependencies are downloaded with every `dsh` installation. [Reasoning translation](2026-10-05-anonymous-reasoning-translation.md) uses three experimental packages without adding third-party runtime dependencies. Admission therefore considers installation cost and whether a switch supplies a usable composition without an additional configuration form. External browser and Cua Driver providers remain explicit compositions because they add substantial runtimes or require an external executable. Stagehand requires model credentials; Python PTC replaces a core provider and conflicts with TypeScript workflow consumers. Demonstrative hooks, webhooks, and mods stay outside the list. [Session Inspector](../feature/2026-09-24-session-inspector.md) owns the Inspector's distinct activation behavior.
Bundles insert ordinary Host rows; optional model tools register in the global tool layer, so every preset, including minimal, receives them. Plugin management, profile composition, and reload need no bundle-specific behavior. Search, Ralph, and terminals supply their own isolated services in a Host group. Search opens one in-memory index on demand; the Host search policy stays unchanged. Badge skills and title providers act on the Host. Switching a bundle registers or removes its tools for live Agents, and later user patch layers retain precedence. A replacement title provider may register once the previous provider's disposal starts; the previous provider's late results never commit.
Claude Code and Codex install on demand through the separate `ON_DEMAND_BUNDLES` catalog; the [on-demand native-bundle decision](2026-10-05-official-on-demand-bundles.md) owns the cost, version, and offline-discovery trade-offs.
Computer Use is a shipped optional bundle in the experimental group. Its bundle uses the explicit naming exception `@deepseek-ai/dsh-cua-bundle`, alongside `@deepseek-ai/dsh-cua`. These names preserve profile references when the capability moves package groups; the experimental classification still controls isolation and promotion. Its repository-owned native component is prepared when the provider loads, without an external Cua Driver runtime.
## Alternatives considered
**Ship every provider as an optional bundle.** Composes and displays correctly, but grows every install by provider runtimes for a capability most installations never switch on; voice input's `sherpa-onnx-node` is the one accepted precedent.
@@ -31,3 +33,5 @@ Claude Code and Codex install on demand through the separate `ON_DEMAND_BUNDLES`
## Consequences
Official discovery does not imply experimental status or a different plugin lifecycle. Switching a bundle off keeps its installed files; failed activation retains the existing saved-selection behavior. Bundle rows are ordinary Host rows with their usual switches. Individual and combined composition tests cover the Host rows and every preset's tool catalog; live switching and recorded Sessions cover activation and model-visible behavior.
Computer Use keeps its permission service and native provider on the Host and registers tools in Agent scopes whose bound preset is `ptc`. Existing Agent events and `createScope()` maintain current, new and resumed Agents. Preset declarations remain unchanged. Only the explicit detail-page action changes the new-session default through the existing settings form; the activation switch does not configure PTC execution.
@@ -10,14 +10,16 @@ Web 插件页只提供两个可选 bundle:Agent Teams 与语音输入。Auto r
## 决策
`OPTIONAL_BUNDLES` 管理安装随附且默认关闭的选项,包括维护中的可选能力与实验性能力。每个 bundle 声明 `icon`,并导出带 `meta.title` 与 `meta.description` 的 `./locale/*.json`;官方分组显示这些元数据。Bundle 的展示方式不决定其[实验性状态](../../../../packages/experimental/README.zh.md#status)。隔离检查校验准入,并继续拒绝非实验 bundle 的实验性依赖。
`OPTIONAL_BUNDLES` 管理安装随附且默认关闭的选项,包括维护中的可选能力与实验性能力。每个 bundle 声明 `icon`,并导出带 `meta.title` 与 `meta.description` 的 `./locale/*.json`;[插件管理页](../../../../packages/client/ui-plugin-manager/README.zh.md#use-this-package)为符合展示条件的组合包显示这些元数据,并保留默认隐藏项的显式启用入口。Bundle 的展示方式不决定其[实验性状态](../../../../packages/experimental/README.zh.md#status)。隔离检查校验准入,并继续拒绝非实验 bundle 的实验性依赖。
可选 bundle 的依赖随每次 `dsh` 安装下载。[思考过程翻译](2026-10-05-anonymous-reasoning-translation.zh.md)使用三个实验包,不增加第三方运行时依赖。因此准入需要考虑安装成本,以及开关能否在不增加配置表单的情况下提供可用组合。浏览器与电脑提供方仍使用显式组合,因为它们会增加较大的运行时或要求外部可执行文件。Stagehand 需要模型凭据;Python PTC 替换核心提供方,并与 TypeScript workflow 消费方冲突。演示性钩子、webhook 与模组不进入名单。[Session Inspector](../feature/2026-09-24-session-inspector.zh.md) 管理 Inspector 特有的激活行为。
可选 bundle 的依赖随每次 `dsh` 安装下载。[思考过程翻译](2026-10-05-anonymous-reasoning-translation.zh.md)使用三个实验包,不增加第三方运行时依赖。因此准入需要考虑安装成本,以及开关能否在不增加配置表单的情况下提供可用组合。外部浏览器与 Cua Driver 提供方仍使用显式组合,因为它们会增加较大的运行时或要求外部可执行文件。Stagehand 需要模型凭据;Python PTC 替换核心提供方,并与 TypeScript workflow 消费方冲突。演示性钩子、webhook 与模组不进入名单。[Session Inspector](../feature/2026-09-24-session-inspector.zh.md) 管理 Inspector 特有的激活行为。
Bundle 插入普通 Host 行;可选模型工具注册到全局工具层,因此包括 minimal 在内的所有预设都会获得它们。插件管理、profile 组合与重载无需针对 bundle 的特殊行为。搜索、Ralph 与终端在 Host 组内提供自己的隔离服务。搜索按需打开一个内存索引;Host 搜索策略保持不变。徽章技能与标题提供方作用于 Host。切换 bundle 会为运行中的 Agent 注册或移除其工具,后续用户 patch 层继续优先。前一个标题提供方开始清理后,替代提供方即可注册;前一个提供方的迟到结果不会提交。
Claude Code 与 Codex 通过独立的 `ON_DEMAND_BUNDLES` 目录按需安装,其成本、版本与离线发现取舍由[按需原生 bundle 决策](2026-10-05-official-on-demand-bundles.zh.md)管理。
Computer Use 是 experimental 包组内随安装提供的可选组合包。组合包与 `@deepseek-ai/dsh-cua` 一起采用显式命名例外 `@deepseek-ai/dsh-cua-bundle`,在能力移动分组时保留配置引用。依赖隔离和晋升仍由实验性分类约束。提供方加载时准备仓库自有的本机组件,无需外部 Cua Driver 运行时。
## 考虑过的替代方案
**把所有提供方都做成可选 bundle。** 组合与展示都正确,但会为多数安装从不启用的能力把提供方运行时塞进每次安装;语音输入的 `sherpa-onnx-node` 是唯一被接受的先例。
@@ -31,3 +33,5 @@ Claude Code 与 Codex 通过独立的 `ON_DEMAND_BUNDLES` 目录按需安装,
## 后果
官方发现不表示实验性状态,也不引入不同的插件生命周期。关闭 bundle 保留已安装文件;激活失败保留现有的已保存选择行为。Bundle 行是普通 Host 行,使用常规开关。独立与组合测试覆盖 Host 行及每个预设的工具目录;实时切换与已记录 Session 覆盖激活和模型可见行为。
Computer Use 将权限服务和原生提供方保留在 Host,在已绑定 `ptc` preset 的 Agent 作用域中注册工具。现有 Agent 事件和 `createScope()` 维护当前、新建和恢复的 Agent,preset 声明保持不变。只有详情页的显式操作通过现有设置表单修改新会话默认模式,启用开关不配置 PTC 执行方式。
@@ -15,8 +15,8 @@
en: eced414858f08ef9
zh: 40639593b0d5bb6f
/agent-note-fatal-diagnostics-and-desktop-crash-reports/relationship-to-the-desktop-logging-design:
en: 7dc438912d10574a
zh: 07d05f9f17043d67
en: ca4a912437c7b02e
zh: 2c7953ce5e8c196f
/agent-note-fatal-diagnostics-and-desktop-crash-reports/testing:
en: 864a8e71e3b3378b
zh: a71807db24d9409c
@@ -32,7 +32,7 @@ This partially supersedes the [native Desktop fatal recovery](2026-09-15-desktop
## Relationship to the Desktop logging design
The Desktop local logging design (a Worker-owned rolling JSONL log with a closed event vocabulary, redaction, and rate limits) is a continuous-log design and remains future work. It stated that Host stderr and renderer console output are never persisted and that errors reach the log only as fixed categories. This decision carves out one exception: the one-time crash report of a fatal failure keeps the raw error and the retained stderr and console tails, because that raw content is what both field diagnoses needed and the report is bounded (the error section is cut at 256 KiB, the Host diagnostic at 64 KiB, the renderer console tail at 64 KiB), local, owner-only, and written only when the application is already showing its fatal dialog. The dialog names the file so a user knows what they would be sharing. The continuous-log privacy posture is unchanged for anything that is not a crash report.
Desktop continuously persists a bounded plugin-management journal with whitelisted facts, two rotating segments and a bounded write queue; its budgets and failure guarantees belong to the [Desktop README](../../../../apps/desktop/README.md). It excludes raw installation specs, error text and package-manager output. The broader Worker-owned continuous log remains deferred. Fatal crash reports independently retain raw errors and bounded stderr and renderer-console tails because these contain the field-diagnosis evidence; files are local and owner-only where supported. An isolated Worker appends bounded management history after the basic report is saved, preserving access to the basic report when collection fails or is still running. Recent operations provide context, not causal attribution.
## Testing
@@ -32,7 +32,7 @@ macOS 上,启动审计把一组 Web 客户端行列为 `import failed (see con
## 与 Desktop 日志体系设计的关系
Desktop 本地日志设计(Worker 独占写入的滚动 JSONL 日志、封闭事件词汇、脱敏与限流)是持续日志设计,仍是未来工作。它规定 Host stderr 与渲染进程 console 输出永不持久化、错误只以固定分类进入日志。本决定划出一个例外:致命失败的一次性崩溃报告保留原始错误以及保留的 stderr 与 console 尾部,因为两次现场定位需要的正是这些原始内容,而报告有界(error 段截至 256 KiB、Host 诊断 64 KiB、渲染进程 console 尾部 64 KiB)、本地、仅所有者可读,且只在应用已经弹出致命对话框时写入。对话框标明文件名,用户知道自己会分享什么。任何非崩溃报告的内容仍遵循持续日志的隐私姿态。
Desktop 持续保存有界的插件管理日志,使用事实白名单、两个轮换文件段和有界写入队列;预算及失败保障由 [Desktop README](../../../../apps/desktop/README.zh.md) 定义。日志不包含原始安装 spec、错误文本或包管理器输出。更广泛的 Worker 独占持续日志仍属延期工作。致命崩溃报告独立保留原始错误以及有界的 stderr 和渲染进程 console 尾部,因为这些内容包含现场定位证据;文件保存在本地,并在平台支持时仅所有者可读。独立 Worker 在基本报告保存后追加有界管理历史,收集失败或尚未结束时仍可获取基本报告。近期操作提供上下文,不构成因果归因。
## Testing
@@ -9,11 +9,11 @@
en: e42db40b879fdbf4
zh: 6017da9c4da17835
/agent-note-node-ptc-execution-modes-and-in-memory-state/decision:
en: d2d08c54c6520f02
zh: 520a9b3421ff5c65
en: 7b5808a31493ac1e
zh: 8d9c698c41dbf18e
/agent-note-node-ptc-execution-modes-and-in-memory-state/alternatives-considered:
en: 396157a9fc293198
zh: a061f05b4423e26f
/agent-note-node-ptc-execution-modes-and-in-memory-state/consequences:
en: 9b9172f4aafe907f
zh: 022a6bd52d7caf05
en: 56fa39e2958afdff
zh: d04f539437d0c00d
@@ -12,7 +12,7 @@ Interactive automation retains application objects, imported modules and helper
The existing `@deepseek-ai/dsh-ptc-runtime-node` plugin selects its execution mode through `executionMode`. The default `per-call` mode starts a fresh process under the configured file policy. Opt-in `persistent-in-memory` mode keeps a Node REPL process per explicit, object-identity state owner. Both modes provide the same `ctx.ptcRuntime` service. PTC uses the live Agent as owner and awaits release during its context disposal. Changing the configuration reloads the provider and releases its processes. The [sandboxed Node decision](2026-09-11-sandboxed-node-ptc-runtime.md) owns per-call confinement; the [PTC foundation](../feature/2026-06-15-ptc.md) owns tool dispatch and logged model requests.
Mode names describe state retention rather than a process implementation or source syntax. `per-call` retains state for one invocation; `persistent-in-memory` retains it across invocations until the process or owner ends. `durable` names the deferred ability to save and restore execution state across restarts. It is not an accepted configuration value: a future implementation must define its recovery point, recoverable values and external-resource handling before exposing that mode. The [retained-process permission decision](2026-10-08-persistent-ptc-sandbox.md) owns confinement, policy changes and temporary escalation.
The `per-call` and `persistent-in-memory` names describe their fixed state-retention policies: one invocation or across invocations until the process or owner ends. The [Script evaluation decision](2026-10-20-ptc-script-evaluation-and-retention.md) extends these modes with independently configured syntax and retention. `durable` names the deferred ability to save and restore execution state across restarts. It is not an accepted configuration value: a future implementation must define its recovery point, recoverable values and external-resource handling before exposing that mode. The [retained-process permission decision](2026-10-08-persistent-ptc-sandbox.md) owns confinement, policy changes and temporary escalation.
One npm package and one plugin entry keep existing installation and loading paths stable. Communication, binding proxies, JSON encoding and output limits remain package-local modules. The Node provider delegates retained process ownership to an internal execution object, which registers no additional Cordis service. Per-call and in-memory execution keep their distinct lifecycle rules. `programFormat` describes source evaluation independently of retention: a REPL need not retain state. The runtime owns syntax, completion and retention instructions through `executionInstructions`; tools own call arguments, SDK declarations and image attachment. Workflow uses `programFormat` to wrap its program and refuses restricted file policies when the selected runtime cannot enforce them.
@@ -34,4 +34,4 @@ A realm inside the Harness process shares process lifetime with model code and c
Selecting `persistent-in-memory` retains execution state; the [permission decision](2026-10-08-persistent-ptc-sandbox.md) defines its file restrictions. Nested tools keep their normal authorization and logging. Memory and native handles may disappear without a Harness restart. The model must recover from reference or resource errors; the runtime never retries a program automatically.
An idle owner retains a process with no expiration timer. Callers release owners explicitly; ownerless service calls use temporary processes. Programs use REPL statements and a final expression rather than top-level return, and calls for one retained owner cannot overlap. Tool schemas and program guidance follow the selected mode.
An idle owner retains a process with no expiration timer. Callers release owners explicitly; ownerless service calls use temporary processes. The `persistent-in-memory` mode uses REPL statements and a final expression rather than top-level return, and calls for one retained owner cannot overlap. Tool schemas and program guidance follow the selected mode.
@@ -12,7 +12,7 @@ Status: implemented
现有 `@deepseek-ai/dsh-ptc-runtime-node` 插件通过 `executionMode` 选择执行模式。默认的 `per-call` 模式按配置的文件策略启动新进程。显式启用的 `persistent-in-memory` 模式为每个以对象身份标识的状态所有者保留一个 Node REPL 进程。两种模式都提供同一个 `ctx.ptcRuntime` 服务。PTC 使用存活的 Agent 作为所有者,在其上下文销毁时等待释放。修改配置会重载提供方并释放其进程。[Node 沙箱决策](2026-09-11-sandboxed-node-ptc-runtime.zh.md)负责逐次执行的文件约束,[PTC 基础决策](../feature/2026-06-15-ptc.zh.md)负责工具分派与模型请求日志。
模式名称描述状态保留方式,不绑定进程实现或代码语法。`per-call` 只为一次调用保留状态;`persistent-in-memory` 跨调用保留状态,直到进程或所有者结束。`durable` 用于命名未来跨重启保存与恢复执行状态的能力。它不是可接受的配置值:实现必须先明确恢复点、可恢复的值以及外部资源的处理方式,才能公开这个模式。[常驻进程权限决策](2026-10-08-persistent-ptc-sandbox.zh.md)负责文件约束、权限切换与临时提权。
`per-call` 和 `persistent-in-memory` 名称描述各自固定的状态保留策略:只保留一次调用,或跨调用保留至进程或所有者结束。[Script 求值决策](2026-10-20-ptc-script-evaluation-and-retention.zh.md)在这两种模式之外增加独立配置的语法与保留方式。`durable` 用于命名未来跨重启保存与恢复执行状态的能力。它不是可接受的配置值:实现必须先明确恢复点、可恢复的值以及外部资源的处理方式,才能公开这个模式。[常驻进程权限决策](2026-10-08-persistent-ptc-sandbox.zh.md)负责文件约束、权限切换与临时提权。
一个 npm 包和一个插件入口保持现有安装与加载路径稳定。通信、绑定代理、JSON 编码和输出限制继续作为包内模块复用。Node 提供方将保留进程的所有权交给内部执行对象,该对象不注册额外的 Cordis 服务。逐次执行和内存保留各自管理不同的生命周期。`programFormat` 描述代码求值形式,与状态保留独立:REPL 不必跨调用保留状态。运行时通过 `executionInstructions` 负责语法、完成值和状态保留指引;工具层负责调用参数、SDK 声明和图片附加。工作流根据 `programFormat` 包装程序;所选运行时无法执行受限文件策略时,工作流拒绝执行。
@@ -34,4 +34,4 @@ Harness 进程内的独立执行上下文与模型代码共享进程生命周期
选择 `persistent-in-memory` 会保留执行状态;[权限决策](2026-10-08-persistent-ptc-sandbox.zh.md)定义其文件限制。嵌套工具仍遵循原有授权和日志规则。即使 Harness 没有重启,内存和原生句柄也可能丢失。模型需要从引用或资源错误中恢复,运行时不会自动重试程序。
空闲所有者保留一个没有过期计时器的进程。调用方显式释放所有者,没有所有者的服务调用使用临时进程。程序使用 REPL 语句和末尾表达式,不支持顶层 return;同一保留所有者的调用不能重叠。工具 schema 和程序指引跟随所选模式。
空闲所有者保留一个没有过期计时器的进程。调用方显式释放所有者,没有所有者的服务调用使用临时进程。`persistent-in-memory` 模式使用 REPL 语句和末尾表达式,不支持顶层 return;同一保留所有者的调用不能重叠。工具 schema 和程序指引跟随所选模式。
@@ -9,8 +9,8 @@
en: 2eeb916eb65c66e7
zh: 7bf5c0abb8bb61e4
/agent-note-deepseek-account-models-come-from-the-account-listing-and-gate-by-client-version/decision:
en: 42e800c4cd3b6ebe
zh: 06fabe5a9681fa27
en: 0125e33ba966c8ac
zh: bf6af389336999af
/agent-note-deepseek-account-models-come-from-the-account-listing-and-gate-by-client-version/alternatives-considered:
en: 7c7880fdb0cc95f6
zh: 2b8a539192626789
@@ -12,8 +12,8 @@ The account route's catalog was deployment configuration: a build advertised the
- [`llm-deepseek-account`](../../../../packages/llm/llm-deepseek-account/README.md) loads the listing lazily for each credential, waits for it once up to `modelsTimeoutMs`, refreshes it in the background after `modelsMaxAgeMs`, keeps the last good listing when a load fails or returns no model, and serves the bundled catalog before any listing loads. The cache lives in memory and is keyed by endpoint and token fingerprint, so per-account rollouts stay separate.
- A listed model is available when its `client_requirements["deepseek-harness"].min_version` is at most the current DeepSeek Harness version in semantic-version order. Without that declaration, only ids the built-in catalog knows are available; the others stay hidden, because a client cannot tell which future version supports them. A request for a refused listed model fails before network I/O with `CLIENT_UPGRADE_REQUIRED` or `MODEL_UNSUPPORTED`.
- Listed capabilities replace bundled ones field by field; the bundled entry keeps client-side knowledge such as tool-update mode and image budgets; `modelOverrides` applies last and reuses the `models` entry fields and validation. `max_output_tokens` bounds the request default instead of becoming it.
- The shared transport gains only two provider hooks, `connect` and `admit`; listing, gating, and merging stay in the account package.
- Listed capabilities replace bundled ones field by field; the bundled entry keeps client-side knowledge the listing does not declare, such as image budgets and a protocol mode the listing omits or sets to `null`; `modelOverrides` applies last and reuses the `models` entry fields and validation. `max_output_tokens` bounds the request default instead of becoming it.
- The shared transport gains only two provider hooks, `connect` and `admit`. Listing, gating, and merging live in the shared transport, which the [official route](2026-10-22-deepseek-official-model-listing.md) also uses.
## Alternatives considered
@@ -12,8 +12,8 @@ Status: implemented
- [`llm-deepseek-account`](../../../../packages/llm/llm-deepseek-account/README.zh.md) 按凭据懒加载列表,首次访问最多等待 `modelsTimeoutMs`,超过 `modelsMaxAgeMs` 后在后台刷新;获取失败或返回的列表中没有模型时保留上一份,还没有加载到任何列表时使用内置目录。缓存只在内存中,以端点和 token 指纹为键,因此按账号灰度互不干扰。
- 列出的模型在其 `client_requirements["deepseek-harness"].min_version` 按 semver 顺序不高于当前 DeepSeek Harness 版本时可用。没有这个声明时,只有内置目录认识的 id 可用,其余保持隐藏,因为客户端无法判断将来哪个版本会支持它们。请求被拒绝的已列出模型时,在网络请求之前以 `CLIENT_UPGRADE_REQUIRED` 或 `MODEL_UNSUPPORTED` 失败。
- 列表中的能力逐字段覆盖内置能力;内置条目保留工具更新模式、图片预算等客户端知识;`modelOverrides` 最后叠加,复用 `models` 条目的字段与校验。`max_output_tokens` 只限制请求默认值,不直接成为默认值。
- 共享传输层只新增 `connect` 与 `admit` 两个提供方钩子;列表、门控与合并都留在账号包中。
- 列表中的能力逐字段覆盖内置能力;内置条目保留列表没有声明的客户端知识,例如图片预算,以及列表没有给出或设为 `null` 的协议模式;`modelOverrides` 最后叠加,复用 `models` 条目的字段与校验。`max_output_tokens` 只限制请求默认值,不直接成为默认值。
- 共享传输层只新增 `connect` 与 `admit` 两个提供方钩子。列表、门控与合并位于共享传输包中,[官方路由](2026-10-22-deepseek-official-model-listing.zh.md)也使用它们。
## 考虑过的替代方案
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-09-24-shared-time-vocabulary.md (docs/i18n/README.md): per heading
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-09-24-shared-time-vocabulary.md
/agent-note-shared-time-vocabulary-for-wall-clock-selectors:
en: ae04747237709d0c
zh: a3731d98d5e940f9
/agent-note-shared-time-vocabulary-for-wall-clock-selectors/problem:
en: abd0263d14861926
zh: e1736cd08e87f795
/agent-note-shared-time-vocabulary-for-wall-clock-selectors/decision:
en: fdd39b7e8fa89d70
zh: 4a0dfc81509e5485
/agent-note-shared-time-vocabulary-for-wall-clock-selectors/alternatives-considered:
en: 1737484c099fab52
zh: 303b144c52d167bd
/agent-note-shared-time-vocabulary-for-wall-clock-selectors/consequences:
en: 79d8d66071453d1f
zh: f940f0b6d74148d6
@@ -0,0 +1,53 @@
# Agent Note: Shared time vocabulary for wall-clock selectors
Status: implemented
English | [中文](2026-09-24-shared-time-vocabulary.zh.md)
## Problem
`dsh-util-time` existed for one job and one caller shape: `canonicalClientTimeZone` validated a caller-reported zone at a wire boundary and answered `undefined`, which `dsh-api-session-controller` and `dsh-subagent` mapped onto their own domain errors. Two other consumers had grown parts of the same vocabulary privately. Schedule held a strict RFC 3339 instant parser, a local-calendar parser, a four-digit-year bound and canonical formatter, a local wall-clock resolver, and its own IANA zone canonicalizer in `packages/schedule/schedule/src/domain.ts`, throwing `ScheduleInputError` instead of answering a result. `dsh-time-context` held a second copy of the IANA zone profile in `packages/context/time-context/src/request-zone.ts`, with its own regular expression and its own `Intl.DateTimeFormat` canonicalization, to enforce the stricter policy that a request may only name a zone another process resolves back to the same identity. The IANA profile and the `Intl` canonicalization step therefore existed three times in all, so a change to that profile had to be made and verified once per copy, and the next consumer of a wall-clock selector had no shared entry point to reach for.
## Decision
`dsh-util-time` owns the time vocabulary that boundaries accept. `packages/util/time/src/instant.ts` holds the parsers, `src/duration.ts` holds the span type, and `src/index.ts` re-exports both and keeps `canonicalClientTimeZone` as the `string | undefined` wrapper the existing wire boundaries consume.
The library answers discriminated results and carries no failure vocabulary. `parseOffsetInstant`, `fourDigitYearEpoch`, `parseLocalCalendar`, and `canonicalizeTimeZone` answer an `accepted` arm or a `rejected` arm naming a stable reason (`not_rfc3339`, `out_of_range`, `not_a_calendar_date`, `invalid_numeric_offset`, `malformed`, `blank`, `padded`, `not_area_location`, `unsupported`, `not_canonical_shape`). Each caller maps a reason onto its own code and diagnostic, so the library never writes model-facing text. `formatFourDigitYearInstant` returns the canonical instant text or `undefined` outside the bound, `isFourDigitYearInstant` answers whether an epoch is representable at all, and `resolveLocalInstant` answers epoch milliseconds or `undefined` for a wall-clock value inside a forward transition's gap. The reason vocabulary is parallel between the two strict parsers: the same condition is `out_of_range` in both, so year 0 is refused by `parseOffsetInstant` and `parseLocalCalendar` alike, while `resolveLocalInstant` accepts any year a real `Temporal` date carries because its callers derive local dates at the UTC edges. `LocalCalendarTime.year` records that split.
Schedule consumes it and keeps every domain decision. `canonicalizeTimeZone`, `decodeInstant`, `parseAtInput`, and `futureInstant` remain Schedule entry points with their existing codes and diagnostics; they now call the shared parser and map its reason. Schedule still owns the durable record shapes, the fold and transition authority, the injection-resistant framing, and all recurrence arithmetic: the cron engine, the daily and weekly date walks, the latest-due search, and the schedule-specific accepted-decision bound.
`dsh-time-context` consumes it too and keeps its stricter policy. `deriveBrowserTimeZoneContext` reads each user-rpc zone through `canonicalizeTimeZone` and refuses an alias whose canonical answer differs from the reported value, so the request boundary still requires an identity another process resolves back to the same name. Time-context owns its three refusals and their messages; the shared entry only answers the canonical name or a reason, and its `unsupported` rejection carries the platform error the library caught, so that refusal keeps its `cause` in both consumers.
`Duration` holds one span as a single millisecond count, and it is a class rather than a branded number because TypeScript's arithmetic erases a brand: adding two differently branded numbers answers an unbranded `number`, so a brand cannot stop seconds and milliseconds from being mixed, while an instance can. The unit is milliseconds because every producer and consumer in this repository already uses it (`Date.now()`, `SessionEvent.time`, the session log, the client), and because a double holds every millisecond of the whole `Date` range exactly, so no precision is spent on the unit. Instances are frozen, no coercion hook is overridden, and a span is bounded by the exactly representable millisecond range, which is what makes the split exact. The published surface is `fromMilliseconds` and a whole-part `decompose()`, the two entries the migrated consumer calls.
The last copy of the canonical instant profile moves too. `packages/session/session-format-v0-to-v1/src/payload-validation.ts` validated a stored schedule instant with its own regular expression and round trip; the shared `fourDigitYearEpoch` owns that same profile, so the migration now asks it and keeps its own error type and message. Because that migration reads released logs, its accepted set is a compatibility promise: `packages/session/session-format-v0-to-v1/tests/validation.spec.ts` accepts the four-digit-year bounds and refuses each spelling a regular expression alone would admit (`0000`, `2026-02-30`, a four-digit fraction, a numeric offset, surrounding whitespace).
The maintained `@js-temporal/polyfill` 0.5.1 becomes a runtime dependency of `dsh-util-time` as well as of `dsh-schedule`. Local resolution continues to use it, so the Host's IANA data still decides gaps and overlaps and no caller owns offset sampling. `util/time`'s own description and README drop the `zero-dependency` phrase as ambiguous: the group's charter forbids harness dependencies so any group may depend on util, and does not ban external packages, which is why `packages/README.md` and the `util/` line in `AGENTS.md` keep the phrase while `packages/util/README.md` and this package's own description name what it loads.
Observable behavior does not change. Every accepted input resolves to the same epoch, every refused input raises the same `ScheduleInputError` or `ScheduleLogError` code and the same message, and the stored `scheduledAt` profile is unchanged, so no durable record and no recorded snapshot is re-recorded. Schedule's suites assert error codes and a few unrelated message substrings, so the assertion this change adds to `packages/schedule/schedule/tests/domain.spec.ts` is the only place that pins the three local-calendar message strings. `packages/util/time/tests/instant.spec.ts` covers the new entries directly, per-file 100% included.
## Alternatives considered
**Keep the shared module private to Schedule.** A Schedule-local `time` module beside `domain.ts` would shrink that file without touching a published package, its lockfile entry, its README, or its dependency set. It was rejected because the duplication this change exists to remove is the duplicate *profile*, not the duplicate line count: the zone regular expression, the canonical `Intl` step, and the instant profile would still exist twice, and the next boundary that accepts a wall-clock selector would copy a third.
**Give the library its own error type.** A `TimeInputError` carrying the reason would let callers rethrow instead of mapping, shortening each call site. It was rejected because `util/time`'s existing contract is that each boundary owns its refusal vocabulary, and Schedule's messages reach the model through the tool result `{ code, message }` and the client's message card; a shared error class would put one caller's wording into another caller's surface.
**Have the library return `undefined` instead of reasons.** That is the shape `canonicalClientTimeZone` already has, and it was the smaller change. It was rejected because Schedule must distinguish a malformed instant from a non-existent calendar date from an out-of-range clock field to keep its existing diagnostics, and `canonicalizeTimeZone` must distinguish a bad name from a locally unresolvable one; collapsing those to one falsy value would have forced Schedule to re-derive the distinction with its own regular expressions, restoring the duplication.
**Brand the millisecond count instead of building a class.** `Branded<number>` already exists in this repository for opaque identities, and a branded count is smaller than a class. It was rejected because the brand does not survive the operation that matters: TypeScript accepts `brandedA + brandedB` and answers an unbranded `number`, so a caller who adds a span to an epoch silently loses the unit, which is the exact mistake the type exists to prevent. A class makes that addition a type error, and it also gives the whole-part split one home.
**Hold the span in nanoseconds.** `Temporal` carries nanoseconds internally as a `bigint`, so a nanosecond span is representable. It was rejected for the same reason the boundaries stay in milliseconds: the count would have to be a `bigint`, which no `number` arithmetic, no `Date`, and no `JSON.stringify` accepts, so every consumer and every durable record would need a string or binary hop to move it. A nanosecond span as a `number` would instead be quantized to roughly 256 ns at the current epoch, which is a different value wearing the same name.
**Add formatting and DST arithmetic for other consumers too.** No current consumer needs model-facing formatting or offset arithmetic from this library: `dsh-time-context` owns its own formatter and zone policy text. Adding them now would give the library a public operation set with no consumer evidence, against the package rule that ties each abstraction to a current owner and need.
## Consequences
The time profile now has one home. A change to the accepted instant profile or the zone profile is made once and covered once, and a new boundary imports it instead of copying it. Schedule's `domain.ts` loses the five regular expressions that spelled the instant, calendar-date, local-time, and zone profiles, the `Intl` canonicalization, and the hand-rolled calendar conversion. It still imports the library's two bound constants for its date arithmetic at the UTC edges and delegates every representability guard to `isFourDigitYearInstant`, so the range itself has one owner.
The cost is a wider published surface and a heavier dependency graph: `dsh-util-time` gains a runtime dependency on `@js-temporal/polyfill`, so a consumer that imports only `canonicalClientTimeZone` now loads that module too. The library is also no longer a pure "validation and canonicalization only" package, which its README and package description had to state and which its Known Limitations section now records. Schedule keeps a thin mapping layer at each entry. The local-calendar and instant mappers switch over the library's closed reason unions with a `never` default, so a newly added reason fails the build there instead of silently taking a catch-all. The zone mapper instead branches only on `not_canonical_shape` — a reason the current IANA data never produces, so it is guarded by a `v8 ignore` rather than a `never` default — and folds `blank`, `padded`, `not_area_location`, and `unsupported` into one diagnostic. A reason added to `TimeZoneRejection` later would therefore compile there and silently take that generic message.
A value type publishes what its consumers call, so the span exposes the constructor and the split that `dsh-time-context` uses and nothing else. A conversion, an arithmetic operation, or a `zero` identity lands with the caller that needs it; the seconds-valued fields at `dsh-schedule` and the DeepSeek file store are the spans that will ask for a second-unit conversion, and the bound and the freeze the class already carries are what make that addition a small one. The package README records the un-migrated boundaries as a known limitation.
The elapsed reading keeps its split in the library and its wording in the consumer: `dsh-time-context` still decides that a backward clock reading clamps to zero and how the compact line reads, while `Duration.decompose()` answers the whole parts. That swap is byte-identical for every finite sample, measured over more than eight thousand inputs including negatives, fractions, and values past a year. That measurement is not a committed test: the consumer's own suite pins the readings it renders (`1d 1h 1m 1s`, `1m 1s`, `4m 2s`, and the clamped zero). Two samples do change: `NaN` rendered `NaNs` and `+Infinity` rendered `Infinityd NaNs`, and both now throw from the span constructor, so a reading that is not a finite number fails where it is produced instead of rendering text. A `-Infinity` sample is unchanged, because the consumer clamps it to zero before the span is built.
Time-context keeps the two policies this change did not touch: its ISO-shaped timestamp rendering in `packages/context/time-context/src/timestamp.ts`, which is that package's durable reading format, and the elapsed-duration wording in its own plugin. The browser client keeps its own localized date rendering and its own offset reading for display, which serve a different purpose (a reader's locale) from a boundary value; unifying those means deciding whether the shared library should also own display formatting, and which package then owns the durable reading format. Five time sites keep their own code, each for a different reason. `packages/experimental/tool-session-query/src/input.ts` parses a wider profile than the shared entry — seconds optional, any number of fractional digits, and the digits below the millisecond kept so two instants can be compared exactly — so substituting the shared parser would narrow what it accepts, and moving that profile into the library is a design step of its own. The `Date.parse(record.scheduledAt)` reads in `dsh-schedule` parse a value the write path already validated, so a discriminated decode would add a rejection branch that cannot occur in practice and change what a corrupt record reports. The `everySeconds`, `afterSeconds`, and `refreshMarginSeconds` multiplications each sit beside a validation that raises the package's own error, so routing them through a constructor that throws its own type would change the failure a caller sees. The UTC offset a client reports is computed in `packages/client/ui-settings-account/src/client/client-metadata.ts` instead of shared: the client bundle admits a cross-plugin value import only for the packages it lists as inline-safe, `dsh-util-time` is not one of them, and it imports the `Temporal` polyfill at module scope, so inlining it would ship that polyfill to the browser. The Desktop host computes the same offset from the shared library, where no such rule applies. `dsh-workspace` writes its record timestamps with the platform's `toISOString`, which is the canonical rendering for every representable epoch: the extended-year and fractional-truncation cases a shared writer would refuse are unreachable from `Date.now()` and from a `SessionHeader` epoch, so adopting the shared formatter there would add a module and a dependency without changing a byte.
@@ -0,0 +1,53 @@
# Agent Note: 面向墙钟选择器的共享时间词汇
Status: implemented
[English](2026-09-24-shared-time-vocabulary.md) | 中文
## Problem
`dsh-util-time` 此前只承担一项职责、面向一种调用方:`canonicalClientTimeZone` 在协议边界校验调用方所报时区并回答 `undefined`,由 `dsh-api-session-controller` 与 `dsh-subagent` 映射成各自的域错误。同一套词汇的其余部分被另外两个消费方各自私下养大。Schedule 在 `packages/schedule/schedule/src/domain.ts` 里持有严格的 RFC 3339 时刻解析器、本地日历解析器、四位年份边界与规范格式化、本地墙钟解析,以及自己的 IANA 时区规范化实现——它抛 `ScheduleInputError` 而不是回答结果。`dsh-time-context` 在 `packages/context/time-context/src/request-zone.ts` 里持有第二份 IANA 时区剖面,配有自己的正则与自己的 `Intl.DateTimeFormat` 规范化,用来执行更严格的策略:一轮请求只能报出另一个进程会解析回同一标识的时区。因此 IANA 剖面与那一步 `Intl` 规范化合计存在三份,该剖面的任何变更都要逐份改、逐份验;而下一个需要墙钟选择器的消费方没有共享入口可用。
## Decision
`dsh-util-time` 拥有各边界所接收的时间词汇。解析器位于 `packages/util/time/src/instant.ts`,时间段类型位于 `src/duration.ts`,`src/index.ts` 转出两者,并保留 `canonicalClientTimeZone` 作为既有协议边界消费的 `string | undefined` 包装。
本库回答带判别值的结果,不持有失败词汇。`parseOffsetInstant`、`fourDigitYearEpoch`、`parseLocalCalendar` 与 `canonicalizeTimeZone` 回答 `accepted` 分支,或回答携带稳定原因的 `rejected` 分支(`not_rfc3339`、`out_of_range`、`not_a_calendar_date`、`invalid_numeric_offset`、`malformed`、`blank`、`padded`、`not_area_location`、`unsupported`、`not_canonical_shape`)。每个调用方把原因映射成自己的错误码与诊断文案,因此本库从不撰写面向模型的文本。`formatFourDigitYearInstant` 返回规范时刻文本,越界时返回 `undefined`;`isFourDigitYearInstant` 回答某个纪元是否可表示;`resolveLocalInstant` 回答纪元毫秒,对落在前向跳变缺口内的墙钟值回答 `undefined`。两个严格解析器的原因词汇彼此对称:同一条件在两者中都叫 `out_of_range`,所以年份 0 被 `parseOffsetInstant` 与 `parseLocalCalendar` 同样拒绝;而 `resolveLocalInstant` 接受真实 `Temporal` 日期携带的任何年份,因为它的调用方需要在 UTC 边界推导本地日期。`LocalCalendarTime.year` 记录了这一分工。
Schedule 消费它,并保留全部域内决策。`canonicalizeTimeZone`、`decodeInstant`、`parseAtInput` 与 `futureInstant` 仍是 Schedule 的入口,保留既有错误码与诊断文案;它们现在调用共享解析器并映射其原因。Schedule 仍拥有持久记录结构、折叠与状态转移权威、抗注入的框架文本,以及全部重复规则运算:cron 引擎、每日与每周日期遍历、最近到期搜索,以及 Schedule 特有的决策时刻边界。
`dsh-time-context` 同样消费它,并保留自己更严格的策略。`deriveBrowserTimeZoneContext` 通过 `canonicalizeTimeZone` 读取每条 user-rpc 时区,并拒绝规范答案与所报值不同的别名,因此请求边界仍然要求另一个进程能解析回同一名称。三条拒绝与它们的文案归 time-context 所有;共享入口只回答规范名称或一个原因;被拒绝的结果中只有 `unsupported` 会携带本库捕获到的平台错误,使该拒绝在两个消费方都保留其 `cause`。
`Duration` 以单一毫秒计数保存一段时间;它做成类而不是 branded number,是因为 TypeScript 的算术会抹掉 brand:两个不同品牌的数字相加得到的是无品牌的 `number`,所以 brand 挡不住秒与毫秒的混用,而实例可以。单位选毫秒,是因为本仓库的每个生产者与消费者本来就用它(`Date.now()`、`SessionEvent.time`、Session 日志、客户端),也因为 double 能精确表示 `Date` 全范围内的每一个毫秒,这个单位不消耗任何精度。实例被冻结,不覆盖任何强制转换钩子,且时间段受可精确表示的毫秒范围约束——这正是拆分精确的前提。公开面就是 `fromMilliseconds` 与整数量拆解 `decompose()`,即已迁移消费方所调用的两个入口。
最后一份规范时刻剖面副本也一并迁移。`packages/session/session-format-v0-to-v1/src/payload-validation.ts` 原本用自己的一段正则与往返比对校验持久记录里的调度时刻;共享的 `fourDigitYearEpoch` 拥有同一剖面,因此该迁移改为询问它,并保留自己的错误类型与文案。由于这个迁移要读取已发布的日志,其接受集是兼容性承诺:`packages/session/session-format-v0-to-v1/tests/validation.spec.ts` 接受四位年份两端,并拒绝只靠正则会放进来的每种拼写(`0000`、`2026-02-30`、四位小数、数字偏移、首尾空白)。
受维护的 `@js-temporal/polyfill` 0.5.1 在成为 `dsh-schedule` 运行时依赖之外,也成为 `dsh-util-time` 的运行时依赖。本地解析继续使用它,因此缺口与重叠仍由宿主 IANA 数据决定,也没有调用方需要自己实现偏移采样。`util/time` 自身的描述与 README 去掉了 `zero-dependency` 这一说法,因为它容易被误读:该组的约定禁止的是 harness 依赖(以便任何组都能依赖 util),并不禁止外部包;因此 `packages/README.md` 与 `AGENTS.md` 里的 `util/` 一行保留该说法,而 `packages/util/README.md` 与本包自己的描述写明它加载了什么。
可观察行为不变。每个被接受的输入解析出相同的纪元值,每个被拒绝的输入抛出相同的 `ScheduleInputError` 或 `ScheduleLogError` 错误码与相同的消息,存储的 `scheduledAt` 剖面不变,因此没有持久记录或录制快照需要重录。Schedule 的测试断言错误码以及少量无关的消息子串,因此本次在 `packages/schedule/schedule/tests/domain.spec.ts` 新增的断言是唯一固定这三条本地日历消息的地方。`packages/util/time/tests/instant.spec.ts` 直接覆盖新增入口,包含逐文件 100% 覆盖。
## Alternatives considered
**把共享模块保持为 Schedule 私有。** 在 `domain.ts` 旁抽出一个 Schedule 私有的 `time` 模块可以缩小它,而不触碰已发布包、其 lockfile 条目、README 或依赖集合。之所以否决,是因为本次要消除的重复是重复的**剖面**,不是重复的行数:时区正则、`Intl` 规范化步骤与时刻剖面仍会存在两份,下一个接收墙钟选择器的边界仍会复制出第三份。
**让本库自带错误类型。** 一个携带原因的 `TimeInputError` 可以让调用方直接重抛而不做映射,缩短每个调用点。之所以否决,是因为 `util/time` 既有契约规定每个边界拥有自己的拒绝词汇,而 Schedule 的消息会通过工具结果的 `{ code, message }` 与客户端的消息卡片到达模型;共享错误类会把一个调用方的措辞带进另一个调用方的界面。
**让本库回答 `undefined` 而不是原因。** 这正是 `canonicalClientTimeZone` 已有的形态,也是改动更小的做法。之所以否决,是因为 Schedule 必须区分格式错误的时刻、日历上不存在的日期与越界时钟字段,才能保留既有诊断;`canonicalizeTimeZone` 也必须区分名称本身不合法与本地无法解析。把二者压成一个假值,会迫使 Schedule 用自己的正则重新推导这层区分,从而恢复重复。
**给毫秒计数加 brand,而不是造类。** 本仓库已有用于不透明标识的 `Branded<number>`,branded 计数也比类更小。之所以否决,是因为 brand 撑不过真正要紧的那步运算:TypeScript 接受 `brandedA + brandedB` 并回答无品牌的 `number`,于是把时间段加到纪元上的调用方会静默丢掉单位,而这正是该类型要防的错误。类会把这种相加变成类型错误,也让整数量拆解只有一个归宿。
**用纳秒保存时间段。** `Temporal` 内部以 `bigint` 携带纳秒,所以纳秒时间段是可表示的。之所以否决,理由与边界保持毫秒相同:计数必须变成 `bigint`,而 `number` 算术、`Date` 与 `JSON.stringify` 都不接受它,于是每个消费方与每条持久记录都要多一次字符串或二进制转换。若改用 `number` 表示纳秒,在当前离线纪元量级上它会被量化到约 256 ns,那是同一个名字下的另一个值。
**同时为其他消费方加入格式化与 DST 运算。** 目前没有消费方需要本库提供面向模型的格式化或偏移运算:`dsh-time-context` 自己拥有格式化器与时区策略文本。现在加入只会给本库一个没有消费方证据的公开操作集合,违背"每项抽象都绑定当前所有者与需求"的包规则。
## Consequences
时间剖面现在只有一个归宿。时刻剖面或时区剖面的变更只做一次、只覆盖一次,新边界导入它而不是复制它。Schedule 的 `domain.ts` 去掉了为时刻、日历日期、本地时间与时区剖面命名的五段正则、`Intl` 规范化与手写日历换算。它仍然导入本库的两个边界常量用于其 UTC 边界日期运算,并把每处可表示性守卫委托给 `isFourDigitYearInstant`,因此范围本身只有一个所有者。
代价是更宽的已发布接口与更重的依赖图:`dsh-util-time` 增加了对 `@js-temporal/polyfill` 的运行时依赖,因此只导入 `canonicalClientTimeZone` 的消费方也会加载该模块。本库也不再是纯"只做校验与规范化"的包——其 README 与包描述必须说明这一点,其"已知限制"一节现在也记录了它。Schedule 在每个入口保留一层薄映射。本地日历与时刻映射函数以 `never` 默认分支穷尽 switch 本库的封闭原因联合,因此新增原因会让这两个调用点构建失败,而不是静默走兜底分支。时区映射函数则只对 `not_canonical_shape` 分支——当前 IANA 数据不会产生该原因,因此它由 `v8 ignore` 守护而不是 `never` 默认分支——并把 `blank`、`padded`、`not_area_location` 与 `unsupported` 合并成一条诊断。因此日后给 `TimeZoneRejection` 新增原因时会通过编译并静默套用那条通用文案。
值类型只发布消费方会调用的部分,因此这个时间段只暴露 `dsh-time-context` 用到的构造入口与拆解,其余不发布。某个换算、某个算术运算或 `zero` 恒等元都随第一个需要它的调用方落地;`dsh-schedule` 与 DeepSeek 文件存储里以秒为单位的字段就是将来会要求第二个单位换算的那些跨度,而类已经携带的范围约束与冻结正是让这次添加保持很小的原因。包 README 把这些尚未迁移的边界记为已知限制。
耗时读数的拆分上收到库、措辞留在一侧:`dsh-time-context` 仍然决定回退的时钟读数钳到零、以及那行紧凑文本怎么写,而 `Duration.decompose()` 回答整数量。这次替换对每个有限样本都逐字不变,用一份覆盖八千多个输入(含负数、小数与一年以上的值)的差分测得。该测量不是提交进仓库的测试:消费方自己那套测试固定了它渲染出的读数(`1d 1h 1m 1s`、`1m 1s`、`4m 2s`,以及被钳到零的那次)。有两个样本会变:`NaN` 原本渲染出 `NaNs`、`+Infinity` 原本渲染出 `Infinityd NaNs`,现在两者都由时间段构造函数抛出,于是非有限的读数在产生它的地方失败,而不是渲染出一段文本。`-Infinity` 样本不变,因为消费方在构造时间段之前已把它钳到零。
本次未触及、仍归 time-context 的两项:`packages/context/time-context/src/timestamp.ts` 中的 ISO 形状时间戳渲染(那是该包的持久读数字格式),以及其插件内的耗时措辞。浏览器客户端也保留自己的本地化日期渲染与用于展示的偏移读取——它们服务的目的(读者的区域设置)与边界值不同;统一它们意味着先决定共享库是否也要拥有展示格式化,以及届时由谁拥有持久读数字格式。有五处时间代码各自保留实现,原因各不相同。`packages/experimental/tool-session-query/src/input.ts` 解析的剖面比共享入口更宽——秒可省略、小数位不限、并保留下毫秒数字以便精确比较两个时刻——因此换成共享解析器会收窄它接受的输入,而把那条剖面搬进本库是另一项独立的设计。`dsh-schedule` 里的 `Date.parse(record.scheduledAt)` 读的是写入路径已经校验过的值,换成带判别值的解码会在每个读取点新增一个实际不可能发生的拒绝分支,并改变损坏记录的报告方式。`everySeconds`、`afterSeconds` 与 `refreshMarginSeconds` 三处乘法旁边都紧跟一条抛本包自有错误的校验,因此把它们改走会抛自身错误类型的构造函数,会改变调用方看到的失败。客户端上报的 UTC 偏移由 `packages/client/ui-settings-account/src/client/client-metadata.ts` 自行计算而不共享:客户端 bundle 只允许对列入 inline-safe 的包做跨插件值导入,`dsh-util-time` 不在其中,而且它在模块顶层静态导入 `Temporal` polyfill,内联它等于把该 polyfill 打进浏览器;Desktop 的宿主侧则用共享库计算同一个偏移,那里没有这条限制。`dsh-workspace` 用平台的 `toISOString` 写记录时间戳,对每个可表示的纪元那已是规范渲染:共享写入器会拒绝的扩展年份与小数截断两种情形,从 `Date.now()` 与 `SessionHeader` 的纪元出发都不可达,因此在那边采用共享格式化器只会增加一个模块与一条依赖,却不会改变任何字节。
@@ -9,11 +9,11 @@
en: b32a7ce6483b9b48
zh: ec0938ad032b99d8
/agent-note-application-consent-for-native-computer-use/decision:
en: 92247ff599c1103f
zh: 26845420a9711c83
en: b34d24e39879277e
zh: 03363665d4682ddb
/agent-note-application-consent-for-native-computer-use/alternatives-considered:
en: 4693dcd217c4fedb
zh: 1dc9bf9067314006
en: 18b2c124f1d4e601
zh: 621a6c9512d5612b
/agent-note-application-consent-for-native-computer-use/consequences:
en: 0146a69ab2086edc
zh: 4257bc1ab4432c3f
en: 4bda3366af65250a
zh: 7c83561b4db07284
@@ -10,29 +10,36 @@ macOS privileges authorize an execution host, not a user's choice of target appl
## Decision
The CUA executor owns application consent for every object-API and native-tool dispatch. It uses the existing User Questions service for human decisions and the provider's existing profile configuration for saved grants. General tool approval and operating-system privileges remain separate requirements. Arbitrary persistent PTC code and separately authorized shell tools retain their own execution authority.
The CUA executor owns application consent for every object-API and native-tool dispatch. It uses the existing User Questions service for human decisions and profile-scoped domain storage for saved grants. Its fixed approval presentation reuses the tool-approval card without adopting the session's tool-approval policy. Native icons and names are presentation metadata, excluded from saved identities and model results. General tool approval and operating-system privileges remain separate requirements. Arbitrary persistent PTC code and separately authorized shell tools retain their own execution authority.
A saved identity combines the canonical executable path with a verified designated signing requirement, represented by a SHA-256 digest. The path prevents authorization from following a copied application, and the signing requirement distinguishes replacements while allowing matching signed updates. Unverifiable and ad-hoc applications receive task access only, tied to a process instance and Agent connection lifetime. Labels do not establish identity.
System-permission buttons call the native helper on the DSH host and remain available to remote browsers. The [usage documentation](../../../../packages/experimental/cua/README.md#use-this-package) explains where to complete authorization; the page has no persistent remote-access notice.
Cold application selection prepares an executable identity and handle without launching. The same consent decision guards the private launch continuation, and the helper checks the executable again before launch and binds the resulting process afterward. Signed selections retain the normal designated-requirement identity; an unverifiable executable uses a content identity for this pending selection and cannot create a saved grant. Preparing metadata before asking avoids starting an application the user refuses.
Application identity is owned by [bundle-ID consent](2026-10-25-cua-bundle-id-consent.md), which supersedes this note's original path/signature choice. That original choice prevented copied or replaced applications from inheriting access, at the cost of separate grants per installation and no saved grants for unsigned applications. Enforcement and human decision ownership remain as described here.
Selections of the same application within one task are serialized, so a pending question does not produce competing decisions. Saved-grant additions read the current configuration inside one mutation queue. Refusals prevent another prompt within the task; an explicitly restored saved grant takes precedence at dispatch.
Cold application selection prepares an executable identity and handle without launching. The same consent decision guards the private launch continuation, and the helper checks the executable again before launch and binds the resulting process afterward. Selections with a bundle identifier retain that identity after launch; an application without one uses its canonical executable path before and after launch and can save that grant. Preparing metadata before asking avoids starting an application the user refuses.
Removing a saved grant invalidates Agent connections retaining the application's handles and cancels active operations; Agent/provider disposal awaits native connection cleanup. Reauthorization uses a fresh connection, so old app and window snapshots cannot become valid again. The [native object decision](2026-09-17-experimental-cua-object-api.md) continues to own process identity, coordinate semantics, and separation from the evaluator.
Conversation decisions survive provider disposal and DSH restart according to [profile consent storage](2026-10-19-cua-profile-consent-storage.md). That decision owns persistence, migration and cross-process revocation. The executor continues to serialize application selections within a conversation, refuse repeated prompts after denial, and give an explicit long-term grant precedence over conversation refusals.
Revocation invalidates the Agent connections that retain the application's handles, then awaits native cleanup. Reauthorization uses a fresh connection, so old app and window snapshots cannot become valid again. The [native object decision](2026-09-17-experimental-cua-object-api.md) continues to own process identity, coordinate semantics, and separation from the evaluator.
## Alternatives considered
**Trust model instructions or hide controls.** Retained objects and direct tool calls can still reach native operations. The executor must enforce the decision at dispatch.
**Persist application names or unsigned paths.** A replacement can reuse both. Durable grants require a verifiable signing identity; task-only access supports unsigned development applications without that promise.
**Persist application names or unsigned paths.** A replacement can reuse both. [Bundle-ID consent](2026-10-25-cua-bundle-id-consent.md) owns the identity choices and the decision to share permission between installations.
**Add an approval database or Session control stream.** Profile settings already own user configuration, and User Questions already owns interactive answers.
**Add an approval database or Session control stream.** User Questions already owns interactive answers. The existing domain service provides durable state without introducing a dedicated database or Session control stream; storage tradeoffs are owned by the profile consent decision.
**Disable permission buttons based on the browser address.** A user may operate the DSH host through screen sharing and complete authorization there. Port forwarding and proxies make the address an unreliable indication of whether the browser and DSH run on the same computer.
**Use the nearest GUI ancestor as the permission target.** A source-built Desktop app can sit between Terminal and the helper while macOS still attributes Accessibility and Screen Recording to Terminal. Process responsibility supplies the system’s attribution; ancestry alone does not. The optional SPI adds compatibility risk, so lookup failure omits the application instead of guessing.
**Keep the native context after revocation.** Its application lookup can return an earlier handle and retain window snapshots. Closing the Agent connection discards its native context through existing transport cleanup; preserving unrelated handles would require a native per-application retirement protocol.
## Consequences
Revoking one application also invalidates other application handles on the same Agent connection. Those applications require fresh selection; saved grants remain reusable, while task grants expire. Other tasks with independent connections remain unaffected unless they also hold access to the revoked application.
Refusing or revoking one application also invalidates other application handles on the same Agent connection. Those applications require fresh selection; session and saved grants remain reusable. Other sessions with independent connections remain unaffected unless they also hold access to the revoked application.
Explicit enablement may start the shared helper for a read-only permission probe. The helper captures the application identified by macOS process responsibility before readiness releases the launcher, retaining its name and icon after reparenting. The optional native query is dynamically resolved; unavailable attribution uses generic system-prompt guidance. The macOS prompt remains authoritative. Enabling does not request macOS authorization or inspect application content.
Application decisions and failure results are exercised through policy regressions and the recorded CUA scenario. Desktop binary distribution, signing, and packaged-host permissions remain outside the source-launch workflow.
@@ -10,29 +10,36 @@ macOS 权限授权执行宿主,不能表达用户允许操作哪些目标应
## 决策
CUA 执行器在每次对象 API 和原生工具调用时检查应用授权。人工决定复用 User Questions 服务,长期许可保存在 provider 的现有 profile 配置中。通用工具审批和操作系统权限仍是独立要求。任意 persistent PTC 代码和另行授权的 shell 工具保留各自的执行权限。
CUA 执行器在每次对象 API 和原生工具调用时检查应用授权。人工决定复用 User Questions 服务,长期许可保存在按 profile 隔离的存储域中。通用工具审批和操作系统权限仍是独立要求。任意 persistent PTC 代码和另行授权的 shell 工具保留各自的执行权限。 固定审批呈现复用工具审批卡片,同时保留独立于会话工具审批策略的授权规则。本机图标和名称仅用于展示,不进入保存的身份信息或模型结果。
长期身份由规范化可执行文件路径和经过验证的签名指定要求组成,以 SHA-256 摘要表示。路径阻止许可跟随应用副本,签名要求区分替换程序,并允许符合相同签名要求的更新。签名不可验证或使用 ad-hoc 签名的应用只能获得任务授权,许可绑定进程实例和 Agent 连接生命周期。显示名称不用于确定身份。
系统权限按钮调用运行 DSH 的电脑上的原生 helper,远程访问时仍可使用。授权地点写在[使用说明](../../../../packages/experimental/cua/README.zh.md#use-this-package)中,页面不常驻远程访问提示。
选择尚未运行的应用时,先准备可执行文件身份和句柄,不立即启动。同一授权决定保护内部启动续接,helper 在启动前再次核对可执行文件,并在启动后绑定实际进程。已签名应用沿用签名指定要求身份;签名不可验证的程序使用本次待启动选择的内容身份,不能生成长期许可。先准备元数据再询问,可以避免启动用户拒绝的应用。
应用身份由[Bundle ID 授权](2026-10-25-cua-bundle-id-consent.zh.md)负责,取代本记录原先的路径和签名选择。原选择防止应用副本或替换程序继承许可,代价是各安装副本必须分别授权,未签名应用无法保存长期许可。执行检查和用户决定的所有权仍由本记录规定。
同一任务对同一应用的选择串行执行,避免未完成的问题产生相互冲突的决定。长期许可的新增在同一写入队列内读取当前配置。拒绝会阻止任务内的再次询问;用户显式恢复的长期许可在执行检查时优先于旧拒绝记录。
选择尚未运行的应用时,先准备可执行文件身份和句柄,不立即启动。同一授权决定保护内部启动续接,helper 在启动前再次核对可执行文件,并在启动后绑定实际进程。有 Bundle ID 的应用在启动后沿用该身份;没有 Bundle ID 的程序在启动前后使用规范化可执行路径,并可保存该许可。先准备元数据再询问,可以避免启动用户拒绝的应用。
删除长期许可会使保留目标应用句柄的 Agent 连接失效,并取消活动操作;Agent 或提供方销毁时等待对应的原生连接清理。重新授权使用新连接,因此旧应用对象和窗口快照不能重新生效。[原生对象决策](2026-09-17-experimental-cua-object-api.zh.md)继续负责进程身份、坐标语义和与执行器分离的设计。
会话决定按[profile 授权存储](2026-10-19-cua-profile-consent-storage.zh.md)的规则,在提供方卸载和 DSH 重启后保留。该记录负责持久化、迁移和跨进程撤销。执行器继续将同一对话对同一应用的选择串行化,拒绝后不再次询问,显式长期许可优先于会话拒绝。
撤销使保留目标应用句柄的 Agent 连接失效,并等待原生清理完成。重新授权使用新连接,因此旧应用对象和窗口快照不能重新生效。[原生对象决策](2026-09-17-experimental-cua-object-api.zh.md)继续负责进程身份、坐标语义和与执行器分离的设计。
## 考虑过的替代方案
**依赖模型指引或隐藏控件。** 保留的对象和直接工具调用仍能到达原生操作。执行器必须在派发时检查用户决定。
**保存应用名称或未签名路径。** 替换程序可以复用二者。长期许可需要可验证的签名身份;任务授权支持未签名的开发应用,但不承诺跨进程复用。
**保存应用名称或未签名路径。** 替换程序可以复用二者。身份选择和在安装副本之间共用许可的决定由[Bundle ID 授权](2026-10-25-cua-bundle-id-consent.zh.md)负责。
**新增审批数据库或 Session 控制流。** Profile settings 已负责用户配置,User Questions 已负责交互回答。
**增加审批数据库或 Session 控制流。** User Questions 已经负责交互式回答。现有域服务提供持久状态,无须增加专用数据库或 Session 控制流;存储取舍由 profile 授权存储决策负责。
**根据浏览器访问地址禁用权限按钮。** 用户可能通过屏幕共享操作运行 DSH 的电脑,并在该电脑上完成授权。端口转发和代理使访问地址无法可靠判断浏览器与 DSH 是否运行在同一台电脑上。
**把最近的 GUI 祖先当作授权对象。** 源码构建的 Desktop 可以位于 Terminal 和 helper 之间,但 macOS 仍将辅助功能和屏幕录制归属于 Terminal。责任进程记录提供系统认定的归属,仅凭父进程链无法确定。可选内部接口有兼容性风险,因此查询失败时省略应用信息,不再猜测。
**撤销后保留原生状态。** 原生应用查找可能返回旧句柄并保留窗口快照。关闭 Agent 连接可以通过现有传输清理释放其原生状态;保留其他应用的句柄则需要新增按应用销毁状态的原生协议。
## 影响
撤销一个应用也会使同一 Agent 连接中其他应用的句柄失效。这些应用需要重新选择;长期许可仍可复用,任务许可随之失效。其他任务使用独立连接,除非也持有被撤销应用的许可,否则不受影响。
拒绝或撤销一个应用也会使同一 Agent 连接中其他应用的句柄失效。这些应用需要重新选择;会话许可和长期许可仍可复用。其他会话使用独立连接,除非也持有被撤销应用的许可,否则不受影响。
显式启用可以启动共享 helper 进行只读权限检查。helper 在发布就绪状态、让启动器退出之前,记录 macOS 责任进程对应的应用,因此父进程改变后仍能提供应用名称和图标。可选的本机查询通过动态加载解析,无法取得归属时显示通用系统弹窗指引。授权归属仍以 macOS 提示为准。启用不会申请 macOS 授权或检查应用内容。
授权决定和失败结果由策略回归测试及 CUA 录制会话验证。桌面安装包的二进制分发、签名和打包宿主权限不属于源码启动流程。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-09-27-web-search-offered-by-provider-readiness.md (docs/i18n/README.md): per heading
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-09-27-web-search-offered-by-provider-readiness.md
/agent-note-offer-web-search-and-web-fetch-only-while-the-selected-provider-is-ready:
en: 9163af91ea0e030d
zh: 92e44043b999d32f
/agent-note-offer-web-search-and-web-fetch-only-while-the-selected-provider-is-ready/problem:
en: 6fac3c73a4415c83
zh: f65b61cc1705348b
/agent-note-offer-web-search-and-web-fetch-only-while-the-selected-provider-is-ready/decision:
en: 458416ed7e47f644
zh: 8b4dd820228e9dfd
/agent-note-offer-web-search-and-web-fetch-only-while-the-selected-provider-is-ready/alternatives-considered:
en: b731254983be656d
zh: 5dee2b09f7faa537
/agent-note-offer-web-search-and-web-fetch-only-while-the-selected-provider-is-ready/consequences:
en: 798faeaa0b406eac
zh: 97bbfdaebc84b1d4
@@ -0,0 +1,51 @@
# Agent Note: Offer web_search and web_fetch only while the selected provider is ready
Status: implemented
English | [中文](2026-09-27-web-search-offered-by-provider-readiness.zh.md)
## Problem
`dsh-tool-web` registers `web_search` whenever search is enabled, and the [web capability seam decision](2026-06-24-web-capability-seam.md) keeps it visible while the selected provider cannot run. The shipped search provider, `dsh-web-search-deepseek`, authenticates a search with the DeepSeek account token only for a model request on the `deepseek-account` route; every other route needs a configured API key. A Session on an API-key route without a search key was therefore offered a tool whose every call failed with `WEB_PROVIDER_CREDENTIAL_MISSING`. Sessions in one Host can use different routes, a Session can switch routes between requests, and the Web app registers `web_search` once per agent preset, so no registration decision matches every request. The Search and Fetch APIs of `dsh-web-deepseek-api`, which the Web search settings switch selects together, need that credential for both operations, so the same Session was also offered a `web_fetch` whose every call failed.
## Decision
Each role of the web capability seam owns one part of a per-step check:
- **Service Definition.** `WebSearchProvider` and `WebFetchProvider` have an optional `ready(llmProvider)` that answers whether an operation made for a model request on that chat route has a credential to send. It checks configuration only and makes no network calls. `ctx.web.searchReady(llmProvider?)` and `ctx.web.fetchReady(llmProvider?)` resolve the provider with the same rules as `search()` and `fetch()` and return its answer. A provider without `ready()` counts as ready, and a selection failure answers true because the operation reports that failure as a structured `WebError`.
- **Service Provider.** `dsh-web-search-deepseek` answers true when the route is `deepseek-account` and `ctx.deepseekAccount.resolveToken()` returns a token for the configured search endpoint, when the section carries a literal `apiKey`, or when `ctx.credentials.describe()` reports the `apiKeyEnv` reference configured (without that service, when the launching environment carries it). It checks credentials in the order `search()` resolves them, and a failed account-token or credential-store read answers false, because `search()` fails on that read instead of trying the next credential. The Search and Fetch API providers of `dsh-web-deepseek-api` share one credential and answer the same way for their API base; their transport repeats these checks, so neither package depends on the other. The providers name no tool or prompt section.
- **Consumer.** `dsh-tool-web` registers `web_search` and `web_fetch` with [step activation conditions](../../../../packages/core/tools/README.md#conditional-tools) that ask `ctx.web.searchReady()` and `ctx.web.fetchReady()` about the route of the step's model selection. The agent loop resolves conditions before it assembles the step, so the declared tools, the `tool:web_search` and `tool:web_fetch` prompt sections, PTC program bindings, and dispatch all read the same admitted set.
The route decides only whether the account token is checked before the API key, which happens on the `deepseek-account` route; an API key counts on every route, so a conversation on another provider's model is offered the DeepSeek web tools whenever a DeepSeek key is configured. The providers do not compare the chat route's endpoint with their own: each operation sends the configured key, `DEEPSEEK_API_KEY` by default, to the configured endpoint, even when the official route chats through a gateway.
Only presence decides: no API key value is read and no endpoint is contacted, so a wrong key or an endpoint that does not serve the native `web_search_20250305` server tool keeps the tool admitted. A step that does not admit a web tool also refuses calls to it as `UNKNOWN_TOOL`, so a model that repeats an earlier call reaches no provider; an operation made outside a model step resolves its credential when it runs and fails with `WEB_PROVIDER_CREDENTIAL_MISSING` when there is none.
The request header records the assembled tools, so the agent loop announces each change through tool-registry updates. A route switch, a stored key, or a removed key changes the next request; DeepSeek routes load a returning tool as a deferred addition, and routes without tool-update support receive the complete tool list.
## Alternatives considered
**Let the DeepSeek provider remove `web_search` in its own assemble listener.** The seam stays unchanged, but the provider then names another package's tool, prompt section, and PTC transport, and it removes the tool even when the deployment selects another search provider, such as Exa, whose searches would succeed.
**Extend `available()`.** It is synchronous and takes no request, while this check reads an asynchronous credential store and depends on the request's route. A route-dependent `available()` would also make provider selection depend on the conversation.
**Read the route from the Session's latest `request/context` event instead of passing it.** That event records the previous request, so a new Session's first request and the first request after a model switch would be decided for the wrong route or for none.
**Register or unregister `web_search` in `dsh-tool-web`.** Registration is shared by every Session of an agent preset, while credentials depend on each request's route. Unregistering would also make `toolOrder` entries and subagent `toolFilter` restrictions that name `web_search` fail.
**Remove `web_search` from the assembled request.** A `system-prompt/assemble` listener could drop the declaration and its section, but the tool would stay executable, so a model that repeats an earlier call would still run it, and PTC programs would still bind it. Prompt assembly also no longer carries tool declarations.
**Offer search only on the DeepSeek account route.** Account tokens are issued only for the official endpoint, so a gateway key could never reach DeepSeek. But every shipped profile defaults to the `deepseek-official` route, so search would be off by default, including the official API-key case.
**Withhold the tools from other providers' conversations unless search has a key of its own.** A search from another model's conversation bills the DeepSeek key, but DeepSeek search is one of the services that key pays for, and a user who configured it expects search to work whatever model chats. The Web search settings page saves its key under `apiKeyEnv`, `DEEPSEEK_API_KEY` by default, which the chat route also reads, so a key entered there could never serve another model; a `routes` setting that opens them would be one more thing a user has to find.
**Compare the official chat route's endpoint with the search endpoint.** Withholding the tools or the key when the two endpoints differ in origin keeps a gateway's key away from DeepSeek, but the providers would have to find the chat route's configuration themselves. Taking the first active `dsh-llm-deepseek-api-key` Loader row answers for an unrelated instance when a composition isolates several, which disables a correct route or lets a gateway key through; only the route's owner knows which endpoint serves a request, and an endpoint field in `dsh-llm` would reach the browser through the Remote `listProviders()`. The check would also withhold search from users who chat through a proxy or development endpoint with a key the official search endpoint accepts. Without it, a gateway user's search fails at the configured endpoint.
**Keep the tool offered and fail at execution.** The model spends a call on a search that cannot authenticate, and the failure repeats on every attempt.
**Probe the key or endpoint before offering the tool.** A network request per model request adds latency and cost; the user controls configuration, not its validity at that moment.
## Consequences
`dsh-web` gains public members: the optional `ready()` of both provider interfaces, `WebRuntime.searchReady()`, and `WebRuntime.fetchReady()`. Each readiness method is the smallest query its one consumer needs, which is how the [observation-surface decision](../../archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md) lets a status query return; neither reports availability or selection failures. The Exa, Perplexity, and HTTP fetch providers implement no `ready()`, so their behavior is unchanged, and `web_fetch` stays admitted whenever the switch leaves HTTP fetch selected. A step that does not admit a web tool leaves it out of PTC program bindings too. An endpoint that cannot serve the native server tool keeps the tool admitted and fails each call. Removing a tool on an unchanged route changes the declared tools and misses the prompt cache once; a model switch already invalidates it. Searches and fetches from other providers' conversations bill the configured DeepSeek key, and a deployment whose official route chats through a gateway sends the gateway's key to the configured endpoint, the official one by default, where each operation fails unless that endpoint accepts the key. The DeepSeek web packages read no Loader rows and depend on no LLM package.
Unit tests pin each role: `packages/web/web/tests/web.spec.ts` covers which provider `searchReady()` and `fetchReady()` ask and their answer on selection failure, `packages/web/web-search-deepseek/tests/deepseek.spec.ts` and `packages/web/web-deepseek-api/tests/readiness.spec.ts` cover each credential case of `ready()` on the account route and on other routes, and `packages/web/tool-web/tests/tool-web.spec.ts` resolves step admission of both tools through the real prompt, tool, and executor services, including the `UNKNOWN_TOOL` refusal of a call in a step that withheld the tool. In real compositions, the shipped headless keyless smoke test and the `web-search-unconfigured` recorded-session snapshot, whose search key references are unset, pin the withheld search tool. Snapshot replays supply a placeholder DeepSeek key, so their tool lists do not depend on the runner's environment; with it, the `pi-ai-conversation-updates` conversation on a pi-ai route keeps `web_search`.
@@ -0,0 +1,51 @@
# Agent Note: 仅在选中的提供方就绪时提供 web_search 与 web_fetch
Status: implemented
[English](2026-09-27-web-search-offered-by-provider-readiness.md) | 中文
## Problem
`dsh-tool-web` 在启用搜索时总会注册 `web_search`,而 [web 能力 seam 决策](2026-06-24-web-capability-seam.zh.md)让它在选中的提供方无法运行时仍然可见。发行版的搜索提供方 `dsh-web-search-deepseek` 只对 `deepseek-account` 路由上的模型请求使用 DeepSeek 账号 token 鉴权;其他路由都需要已配置的 API 密钥。因此,API 密钥路由上没有搜索密钥的会话会获得一个每次调用都以 `WEB_PROVIDER_CREDENTIAL_MISSING` 失败的工具。同一 Host 中的会话可以使用不同路由,一个会话可以在请求之间切换路由,而 Web 应用按 agent preset 只注册一次 `web_search`,因此任何注册层面的决定都无法与每个请求相符。Web 搜索设置开关会同时选中 `dsh-web-deepseek-api` 的 Search 与 Fetch API,两个操作都需要这份凭据,因此同一会话还会获得一个每次调用都失败的 `web_fetch`。
## Decision
web 能力 seam 的每个角色各负责逐步骤检查的一部分:
- **Service Definition。** `WebSearchProvider` 与 `WebFetchProvider` 有一个可选的 `ready(llmProvider)`,回答为该对话路由上的模型请求发起的操作是否有可发送的凭据。它只检查配置,不发起网络调用。`ctx.web.searchReady(llmProvider?)` 与 `ctx.web.fetchReady(llmProvider?)` 按与 `search()`、`fetch()` 相同的规则解析提供方并返回其回答。没有 `ready()` 的提供方视为就绪;选择失败时回答 true,因为对应操作会把该失败报告为结构化 `WebError`。
- **Service Provider。** `dsh-web-search-deepseek` 在以下任一情况下回答 true:路由为 `deepseek-account` 且 `ctx.deepseekAccount.resolveToken()` 为配置的搜索端点返回 token;配置段带有字面 `apiKey`;或 `ctx.credentials.describe()` 报告 `apiKeyEnv` 引用已配置(没有该服务时,启动环境中存在该变量)。它按 `search()` 解析凭据的顺序检查凭据,读取账号 token 或凭据存储失败时回答 false,因为 `search()` 会在该次读取上失败,而不会尝试下一种凭据。`dsh-web-deepseek-api` 的 Search 与 Fetch API 提供方共用一份凭据,并对其 API 基础地址以同样方式回答;它们的传输层重复了这些检查,因此两个包互不依赖。提供方不点名任何工具或提示词段落。
- **Consumer。** `dsh-tool-web` 注册 `web_search` 与 `web_fetch` 时各带一个[步骤激活条件](../../../../packages/core/tools/README.zh.md#conditional-tools),以该步骤模型选择的路由询问 `ctx.web.searchReady()` 与 `ctx.web.fetchReady()`。agent loop 在组装该步骤之前解析各条件,因此声明的工具、`tool:web_search` 与 `tool:web_fetch` 提示词段落、PTC 程序绑定与派发都读取同一个准入集合。
路由只决定是否在 API 密钥之前检查账号 token,这只发生在 `deepseek-account` 路由上;API 密钥在任何路由上都算数,因此只要配置了 DeepSeek 密钥,使用其他提供方模型的对话也会获得 DeepSeek Web 工具。提供方不比较聊天路由的地址与自己的端点:即使官方路由经网关聊天,每次操作也把配置的密钥(默认 `DEEPSEEK_API_KEY`)发往配置的端点。
只看凭据是否存在:不读取 API 密钥的值,也不访问任何端点,因此错误的密钥,或不提供原生 `web_search_20250305` 服务器工具的端点,仍会让该工具保持准入。未准入某个 Web 工具的步骤也会以 `UNKNOWN_TOOL` 拒绝对它的调用,因此模型重复先前的调用也到达不了任何提供方;在模型步骤之外发起的操作在运行时解析凭据,没有凭据时以 `WEB_PROVIDER_CREDENTIAL_MISSING` 失败。
请求头记录组装后的工具,因此 agent loop(智能体循环)通过工具注册表更新宣告每次变化。路由切换、存储密钥或删除密钥都会改变下一个请求;DeepSeek 路由以延迟加载的新增方式载入重新提供的工具,不支持工具更新的路由则接收完整工具列表。
## Alternatives considered
**让 DeepSeek 提供方在自己的 assemble 监听器中移除 `web_search`。** seam 保持不变,但提供方因此要点名另一个包的工具、提示词段落与 PTC 传输工具;而且即使部署选择了另一个搜索成功的提供方(例如 Exa),它也会移除该工具。
**扩展 `available()`。** 它是同步的且不接收请求,而这项检查要读取异步凭据存储,并取决于请求的路由。依赖路由的 `available()` 还会让提供方选择取决于对话。
**从会话最近的 `request/context` 事件读取路由,而不是传入路由。** 该事件记录的是上一个请求,因此新会话的首个请求,以及模型切换后的首个请求,会按错误的路由判断,或根本没有路由可用。
**在 `dsh-tool-web` 中注册或注销 `web_search`。** 注册由同一 agent preset 的所有会话共享,而凭据取决于每个请求的路由。注销还会使点名 `web_search` 的 `toolOrder` 条目和 subagent `toolFilter` 限制失败。
**从组装好的请求中移除 `web_search`。** `system-prompt/assemble` 监听器可以删掉该声明及其段落,但工具仍可执行,因此模型重复先前的调用时仍会运行它,PTC 程序也仍能绑定它。提示词组装也已不再携带工具声明。
**只在 DeepSeek 账号路由上提供搜索。** 账号 token 只对官方端点签发,因此网关的密钥不可能到达 DeepSeek。但所有发行 profile 的默认路由都是 `deepseek-official`,搜索会因此默认关闭,连官方 API 密钥的情况也一并关闭。
**除非搜索有自己的密钥,否则不向其他提供方的对话提供这些工具。** 其他模型对话中的搜索会由 DeepSeek 密钥计费,但 DeepSeek 搜索本就是该密钥付费使用的服务之一,配置了它的用户也期望无论用哪个模型对话,搜索都能用。Web 搜索设置页把密钥存在 `apiKeyEnv` 指向的引用下,默认是聊天路由也在读取的 `DEEPSEEK_API_KEY`,因此在那里输入的密钥永远无法服务其他模型;再加一个开放这些路由的 `routes` 配置项,只会让用户多找一样东西。
**比较官方聊天路由的端点与搜索端点。** 两个端点不同源时撤下工具或不发送密钥,能让网关的密钥远离 DeepSeek,但提供方得自己找出聊天路由的配置。取第一条激活的 `dsh-llm-deepseek-api-key` Loader 行时,如果组合隔离了多个实例,回答的可能是无关的实例,从而关掉正确的路由,或放过网关的密钥;只有路由的所有者知道哪个端点服务某个请求,而在 `dsh-llm` 加地址字段会让它经 Remote 方法 `listProviders()` 进入浏览器。这项检查还会让经代理或开发端点对话、而密钥能被官方搜索端点接受的用户失去搜索。不做这项检查时,网关用户的搜索会在配置的端点上失败。
**保持工具可见,在执行时失败。** 模型会把一次调用花在无法鉴权的搜索上,并且每次尝试都会重复失败。
**提供工具前探测密钥或端点。** 每个模型请求都发起一次网络请求会增加延迟与成本;用户控制的是配置本身,而不是它在那一刻是否有效。
## Consequences
`dsh-web` 增加以下公共成员:两个提供方接口上可选的 `ready()`、`WebRuntime.searchReady()` 与 `WebRuntime.fetchReady()`。每个就绪方法都是其唯一消费方所需的最小查询,这正是[观测接口决策](../../archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md)允许状态查询回归的方式;它们既不报告可用性,也不报告选择失败。Exa、Perplexity 与 HTTP 抓取提供方没有实现 `ready()`,其行为不变;只要开关让 HTTP 抓取保持选中,`web_fetch` 就始终准入。未准入某个 Web 工具的步骤,其 PTC 程序绑定中也没有它。无法提供原生服务器工具的端点仍会让工具保持准入,并使每次调用失败。在路由不变时移除工具会改变声明的工具,并使提示词缓存失效一次;模型切换本身已经使缓存失效。其他提供方对话中的搜索与抓取由配置的 DeepSeek 密钥计费;官方路由经网关对话的部署会把网关的密钥发往配置的端点(默认是官方端点),除非该端点接受这个密钥,否则每次操作都会失败。DeepSeek Web 包不读取 Loader 的行,也不依赖任何 LLM 包。
单元测试分别固定每个角色:`packages/web/web/tests/web.spec.ts` 覆盖 `searchReady()` 与 `fetchReady()` 询问哪个提供方以及选择失败时的回答,`packages/web/web-search-deepseek/tests/deepseek.spec.ts` 与 `packages/web/web-deepseek-api/tests/readiness.spec.ts` 覆盖 `ready()` 在账号路由与其他路由上的每种凭据情况,`packages/web/tool-web/tests/tool-web.spec.ts` 通过真实的提示词、工具与执行器服务解析两个工具的步骤准入,包括在撤下该工具的步骤中调用它时以 `UNKNOWN_TOOL` 拒绝。在真实组合中,发行版 headless 无密钥冒烟测试与 `web-search-unconfigured` 录制会话快照的搜索密钥引用都未设置,二者固定了搜索工具的撤下行为。快照回放提供占位 DeepSeek 密钥,因此其工具列表不依赖运行环境;借助该密钥,`pi-ai-conversation-updates` 中 pi-ai 路由上的对话保留 `web_search`。
@@ -33,5 +33,5 @@
en: 939cedceed200cb2
zh: c7bb02d0da400c8d
/agent-note-refresh-runtime-resolution-after-package-operations-and-expire-hmr-package-configuration/consequences:
en: 1b9e31b4e773fac5
zh: c140ce07fc64bd55
en: 5a0120a6a0d48912
zh: 649e7bc118bda1a2
@@ -113,5 +113,5 @@ Tests need no API key and make no model calls.
- With HMR, supported package operations publish the updated private dependencies and remove unused mappings after plugins stop. Without HMR, a pending startup-bundle disablement defers later publications until restart, preserving running plugins' mappings.
- In the supported thread, package.json configuration reads use the changed fields; JSON modules retain their own reload behavior. The separate module, loader-thread, and Worker limitations above still apply.
- Package configuration below expired directories is parsed in JavaScript and must keep the native reader's field and error semantics; tests compare it with the native reader on Node 22, 24, and 26.
- Package configuration below expired directories is parsed in JavaScript and must keep the native reader's field and error semantics; tests compare it with the native reader on Node 24 and 26.
- The implementation depends on several Node internal interfaces, and these tests must run again for Node upgrades.
@@ -113,5 +113,5 @@ binding 的 `getNearestParentPackageJSON` 只被 `package_json_reader` 自己调
- 有 HMR 时,支持的包操作发布更新后的私有依赖,并在插件停止后移除不再需要的映射。没有 HMR 时,待生效的启动 bundle 停用会将后续发布延迟到重启,保留运行中插件的映射。
- 在支持的线程内,package.json 配置读取使用更新后的字段;JSON 模块保留自己的重载行为。上述模块、loader 线程及 Worker 的独立限制仍然适用。
- 包配置读取改由 JS 解析 package.json,需要与原生读取保持字段和错误语义一致;测试在 Node 22、24、26 上与原生读取逐项比较。
- 包配置读取改由 JS 解析 package.json,需要与原生读取保持字段和错误语义一致;测试在 Node 24、26 上与原生读取逐项比较。
- 依赖多个 Node internal 接口,Node 升级时需要重跑这些测试。
@@ -9,14 +9,14 @@
en: 6aa396e12264ee48
zh: 56849a18acaf830a
/agent-note-initial-draft-content-and-structured-reference-restoration/decision:
en: 9cf2afd9d5e663e9
zh: ad59b1bec6ec4666
en: 1a370f23ee32b6a4
zh: fb969ec929a6c7c9
/agent-note-initial-draft-content-and-structured-reference-restoration/decision/data-model:
en: 7f33895364a9f5cc
zh: f1c79ea15f5c151e
/agent-note-initial-draft-content-and-structured-reference-restoration/decision/classes-and-methods:
en: ca81423a57f898a0
zh: 98962cc1c6bce113
en: e57394545d9d93ff
zh: 5ab142e661750b82
/agent-note-initial-draft-content-and-structured-reference-restoration/decision/data-flow:
en: dd57e639e9328f1d
zh: 5e16cb2e3a73597c
@@ -12,7 +12,7 @@ New-task entry points need to place unsent content in the composer. A draft cont
Keep the existing `SessionInputShell`, `DraftEditorRuntime`, Conversation store, and Session reference lifetime. Plain text and structured drafts enter through the input model. Initial import finishes before `InputHub.shellFor()` returns, without React effects restoring text. Existing input objects are not restored again. React mounts the same editor and binds the existing persistence callback.
The second parameter of `startSession(workspaceId?, options?)` contains `prompt` and `clearPreviousDraft`. `prompt` accepts only a string; structured drafts remain internal to restoration and Workspace transfer. Omitting options preserves the existing flow. Without clearing, existing content is preserved and only an empty draft adopts the prompt. Explicit clearing replaces the target Session's text document, without deleting attachments or changing other Sessions. Prefilling does not send, execute commands, or change `blank`.
The second parameter of `startSession(workspaceId?, options?)` contains `prompt`, `whenDraftExists`, and `clearPreviousDraft`. `prompt` accepts only a string; structured drafts remain internal to restoration and Workspace transfer. Omitting options preserves the existing flow. An empty draft adopts the prompt. When the target already has text or attachments, `whenDraftExists` decides: `keep` (the default) preserves it, `replace` replaces the target Session's text document with the prompt or clears it without one, and `append` adds the prompt as new paragraphs after the existing text and references. Neither deletes attachments or changes other Sessions. `clearPreviousDraft: true` acts as `replace` only when `whenDraftExists` is omitted. Prefilling does not send, execute commands, or change `blank`.
### Data model
@@ -41,7 +41,7 @@ The existing `dsh.conversation.<sessionId>` key remains. Readers accept both leg
There is no new draft service, storage key, general task framework, or Session creation entry point. SlotRenderer, Host Session logs, command execution, and file-discovery RPCs are unchanged. `InputActions` remains the shared input-action entry; components receive no separate content-initialization callback.
The call parameters are `startSession(workspaceId?: WorkspaceId, options?: StartSessionOptions)`, `requestDraftInitialization(binding: SessionBinding, options: DraftInitializationOptions)`, `SessionInputShell.requestDraftInitialization(options)`, and the internal `SessionInputShell.setDraft(input: string | DraftSnapshot)`. `StartSessionOptions` aliases `DraftInitializationOptions`, whose fields are `prompt?: string` and `clearPreviousDraft?: boolean`. A shallow copy captures these values before asynchronous creation, and the result addresses only the retained target binding. Initialization distinguishes applied, preserved, and submission-blocked outcomes. Refusal does not clear first; superseded navigation does not write a draft.
The call parameters are `startSession(workspaceId?: WorkspaceId, options?: StartSessionOptions)`, `requestDraftInitialization(binding: SessionBinding, options: DraftInitializationOptions)`, `SessionInputShell.requestDraftInitialization(options)`, and the internal `SessionInputShell.setDraft(input: string | DraftSnapshot)`. `StartSessionOptions` extends `DraftInitializationOptions`, whose fields are `prompt?: string`, `whenDraftExists?: 'replace' | 'append' | 'keep'`, and `clearPreviousDraft?: boolean`. A shallow copy captures these values before asynchronous creation, and the result addresses only the retained target binding. Initialization distinguishes applied, preserved, and submission-blocked outcomes. Refusal does not clear first; superseded navigation does not write a draft.
### Data flow
@@ -12,7 +12,7 @@ Status: implemented
保留现有 `SessionInputShell`、`DraftEditorRuntime`、Conversation store 和 Session 引用生命周期。普通文字与结构化草稿统一经过输入模型导入;初始导入在 `InputHub.shellFor()` 返回对象前完成,不由 React effect 恢复文字。已存在的输入对象不重新恢复。React 只挂载同一编辑器,并绑定现有持久化回调。
`startSession(workspaceId?, options?)` 的第二参数保留 `prompt` 与 `clearPreviousDraft`。`prompt` 仅接受字符串;结构化草稿只用于内部恢复和工作区携带。无选项时维持原流程。不清空时保留已有内容,空稿才采用 prompt;显式清空作用于目标 Session 的文字文档,不删除附件,也不修改其他 Session。预填不自动发送、执行命令或改变 `blank`。
`startSession(workspaceId?, options?)` 的第二参数包含 `prompt`、`whenDraftExists` 与 `clearPreviousDraft`。`prompt` 仅接受字符串;结构化草稿只用于内部恢复和工作区携带。无选项时维持原流程。空稿直接采用 prompt。目标已有文字或附件时由 `whenDraftExists` 决定:`keep`(默认)保留原稿,`replace` 用 prompt 替换目标 Session 的文字文档、无 prompt 时清空,`append` 在原有文字与引用之后另起段落追加 prompt。三者都不删除附件,也不修改其他 Session。只有未传 `whenDraftExists` 时,`clearPreviousDraft: true` 才等同 `replace`。预填不自动发送、执行命令或改变 `blank`。
### 数据模型
@@ -41,7 +41,7 @@ Status: implemented
不新增草稿服务、存储 key、通用任务框架或另一套 Session 创建入口;不改 SlotRenderer、Host Session 日志、命令执行和文件发现 RPC。`InputActions` 保留统一输入操作入口,组件不单独注入另一条内容初始化回调。
调用参数依次为 `startSession(workspaceId?: WorkspaceId, options?: StartSessionOptions)`、`requestDraftInitialization(binding: SessionBinding, options: DraftInitializationOptions)`、`SessionInputShell.requestDraftInitialization(options)`,以及内部的 `SessionInputShell.setDraft(input: string | DraftSnapshot)`。`StartSessionOptions` 直接引用 `DraftInitializationOptions`,字段为 `prompt?: string` 和 `clearPreviousDraft?: boolean`;进入异步创建前浅拷贝这些值,结果只作用于最终保留的 binding。初始化结果区分已应用、保留原稿和提交期间拒绝;拒绝不先清空,导航被后续操作取消时不写稿。
调用参数依次为 `startSession(workspaceId?: WorkspaceId, options?: StartSessionOptions)`、`requestDraftInitialization(binding: SessionBinding, options: DraftInitializationOptions)`、`SessionInputShell.requestDraftInitialization(options)`,以及内部的 `SessionInputShell.setDraft(input: string | DraftSnapshot)`。`StartSessionOptions` 扩展 `DraftInitializationOptions`,字段为 `prompt?: string`、`whenDraftExists?: 'replace' | 'append' | 'keep'` 和 `clearPreviousDraft?: boolean`;进入异步创建前浅拷贝这些值,结果只作用于最终保留的 binding。初始化结果区分已应用、保留原稿和提交期间拒绝;拒绝不先清空,导航被后续操作取消时不写稿。
### 数据流
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-10-09-composer-command-chip.md (docs/i18n/README.md): per heading
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-10-09-composer-command-chip.md
/agent-note-claimed-commands-as-atomic-composer-chips:
en: 66d4f149698760c8
zh: de219f394dfd4a4c
/agent-note-claimed-commands-as-atomic-composer-chips/problem:
en: 831c97e0de131639
zh: ea35b4b179ff33f0
/agent-note-claimed-commands-as-atomic-composer-chips/decision:
en: 6ac53f6cd1307897
zh: 2f1dbe762b52f18e
/agent-note-claimed-commands-as-atomic-composer-chips/alternatives-considered:
en: 9c694a21237d7772
zh: b35ba7bf8b73b679
/agent-note-claimed-commands-as-atomic-composer-chips/consequences:
en: 089365d6a4be0ebb
zh: b30384c717a7ea1d
@@ -0,0 +1,41 @@
# Agent Note: Claimed commands as atomic composer chips
Status: implemented
English | [中文](2026-10-09-composer-command-chip.zh.md)
## Problem
The [Lexical composer decision](../../archived/architecture/2026-08-20-web-composer-lexical-editor.md) kept a claimed command as literal leading text. A TextNode transform styled the token and split typed overflow back out of it, and the submit machine held the claim while the draft string started with the token. Every edit at the token boundary ran through that transform. Lexical merges same-style neighbours before the overflow node's own transform runs, so the split and the merge looped until Lexical aborted the update with error #14 and the DOM diverged from the editor state (issue #4490). PR #5480 cleared the copied style for the reported gesture; pasting an image after a claim and IME composition reach the same transform and remained unverified. The plain-text reference entity also needed a precedence rule to leave the claimed token alone.
## Decision
A claim replaces the leading trigger span with `CommandChipNode`, an atomic inline `DecoratorNode`, followed by the token's argument separator as an ordinary text node.
- The node carries the `CommandClaim`, including its submit closure. History restores nodes with their fields, so undoing a chip deletion restores the claim. JSON export writes the token text; JSON import yields a plain text node because the closure has no JSON form.
- The detect projection counts the chip as one U+FFFC, so trigger detection never re-reads the command name. The clipboard projection is the token without its separator. The chip is not a reference occurrence. `EditorProjection.command` is the claim of a chip that opens the document.
- Outside a submit transaction, the machine's claim follows the document: `draft-changed` carries the leading chip's claim, so deleting or displacing the chip releases the claim and a history restore re-enters claimed. A failed command submission settles to the same rule, so it returns to claimed exactly when a chip still leads; a claim adjudicated from typed text at Enter has no chip and returns to plain.
- Node transforms keep the chip on the leading seat. A chip with a preceding sibling or inside a later block becomes its literal token, which releases the claim as typing before the old token did. The chip's own transform sees sibling insertions and moves between blocks; a paragraph transform sees a block inserted before the chip's block, so no update walks the whole document.
- Deletion takes two steps. A character deletion that reaches the chip from a collapsed caret marks it selected (`data-selected` on its host; the root's `data-composer-chip-selected` hides the caret), and the next deletion in the same direction removes it whole. Deletions are decided both on the character-deletion command and on deletion input events, because iOS applies a ranged `deleteContentBackward` without that command. A key whose dispatch produced no character deletion, a click, blur, or a caret that leaves the chip clears the mark, so platform deletion shortcuts such as macOS Ctrl+H keep it. The editor selection stays a collapsed range and `isKeyboardSelectable()` stays false for the reason recorded on reference chips. Escape still does not release a claim.
- The persisted draft records the leading chip's claim token (`DraftSnapshot.command`), never the claim. On restore the input asks the trigger pipeline's `resolveClaim`, which polls the optional source hook `matchClaim`: it awaits the source's catalog and answers only a claim, never invoking or executing the command, unlike Enter's `matchEnter`. The chip is rebuilt only at the revision the restore produced and merged into the seed's history entry; an earlier edit, an unclaimed token, or a catalog failure keeps the literal token, which Enter still adjudicates.
- `claim-decor.ts` and the text-reference precedence rule are deleted.
The `slash/input-*` events, `CommandClaim`, `InputState.claim`, and trigger sources keep their fields. `CommandClaim.token` still ends with the separator, and command arguments are still read from the clipboard text after it.
## Alternatives considered
**Keep literal text and repair each gesture.** Every gesture that reaches the boundary node needs its own proof, as #5480 showed, and the precedence rule with text references remains.
**Reuse `ReferenceChipNode`.** Reference chips are occurrences serialized through their owner codec at submit; a command is not model content and must not enter `occurrences`.
**Put the separator inside the chip text.** Lexical appends a managed line break after an inline decorator that ends its block, so the blank-arguments hint rendered by `p:last-child::after` would move to the next line. A separate text node keeps the hint on the chip's line.
**Select the chip with a NodeSelection.** The plain-text binding's arrow, typing, and deletion handlers ignore non-range selections, so each of those gestures would need its own handling at the chip.
**Keep claims in a shell map keyed by NodeKey.** The map duplicates the field the node already carries and needs pruning whenever history is cleared.
## Consequences
- Backspace no longer edits the command name character by character to reopen the menu. At the chip's edge, two Backspace or Delete presses remove the chip and leave the arguments as plain text.
- A reload restores the chip after the command catalog loads; until then, and when restoration does not apply, the draft shows the literal token.
- Whole-chip deletion through a real key event is observable only in the browser lane because jsdom lacks `Selection.modify`; `lifecycle-chrome.e2e.ts` pins the separator, select, and delete presses.
@@ -0,0 +1,41 @@
# Agent Note: 已认领命令作为输入框原子 chip
Status: implemented
[English](2026-10-09-composer-command-chip.md) | 中文
## 问题
[Lexical 输入框决策](../../archived/architecture/2026-08-20-web-composer-lexical-editor.md)把已认领命令保留为行首字面文本。一个 TextNode transform 给 token 加样式,并把输入进去的溢出文字拆出来;提交状态机在草稿字符串以 token 开头时保持认领。token 交界处的每次编辑都会经过这个 transform。Lexical 会在溢出节点自身的 transform 执行前合并样式相同的相邻节点,于是拆分与合并循环往复,直到 Lexical 以错误码 14 中止这次更新,DOM 与编辑器状态随之分叉(issue #4490)。PR #5480 针对报告的手势清除了被复制的样式;认领后粘贴图片和 IME 组字会进入同一个 transform,仍未验证。纯文本引用实体也需要一条优先级规则来避开已认领的 token。
## 决策
认领把行首触发区间替换为 `CommandChipNode`(一个原子 inline `DecoratorNode`),其后是作为普通文本节点的 token 参数分隔符。
- 节点携带 `CommandClaim`,包括其 submit 闭包。历史记录恢复节点时连同字段一起恢复,因此撤销 chip 删除会恢复认领。JSON 导出写入 token 文本;闭包没有 JSON 形式,因此 JSON 导入得到纯文本节点。
- 检测投影把 chip 计为一个 U+FFFC,触发检测不会再读到命令名。剪贴板投影是不含分隔符的 token。chip 不是引用 occurrence。`EditorProjection.command` 是位于文档开头的 chip 所携带的认领。
- 在提交事务之外,状态机的认领跟随文档:`draft-changed` 携带行首 chip 的认领,因此删除或挤开 chip 会释放认领,历史恢复会重新进入 claimed。命令提交失败后按同一规则结算,只要行首仍有 chip 就回到 claimed;Enter 时从输入文本裁决出的认领没有 chip,回到 plain。
- 节点 transform 让 chip 保持在行首位置。前面有兄弟节点或位于后续块中的 chip 会变回字面 token,并像在旧 token 前输入那样释放认领。chip 自身的 transform 覆盖兄弟节点插入和跨块移动;段落 transform 覆盖在 chip 所在块之前插入块的情况,因此没有任何更新需要遍历整个文档。
- 删除分两步。从折叠光标出发、方向触及 chip 的字符删除先把它标记为选中(宿主元素上的 `data-selected`;根元素的 `data-composer-chip-selected` 隐藏光标),同方向的下一次删除才整体移除它。字符删除命令和删除类输入事件都会参与判断,因为 iOS 处理带范围的 `deleteContentBackward` 时不经过该命令。分发过程中没有产生字符删除的按键、点击、失焦或光标离开 chip 会清除标记,因此 macOS Ctrl+H 这类平台删除快捷键会保留它。编辑器选区保持为折叠的 range,`isKeyboardSelectable()` 仍为 false,原因与引用 chip 记录的相同。Escape 仍不释放认领。
- 持久化的草稿记录行首 chip 的认领 token(`DraftSnapshot.command`),从不保存认领本身。恢复时输入框调用触发管线的 `resolveClaim`,它轮询可选的 source 钩子 `matchClaim`:等待 source 的目录就绪,只应答认领,与 Enter 的 `matchEnter` 不同,从不调用或执行命令。chip 只在恢复产生的那个版本上重建,并合入种子的历史条目;在此之前发生的编辑、无人认领的 token 或目录加载失败都会保留字面 token,Enter 仍会对其裁决。
- 删除 `claim-decor.ts` 和文本引用的优先级规则。
`slash/input-*` 事件、`CommandClaim`、`InputState.claim` 与触发源的字段保持不变。`CommandClaim.token` 仍以分隔符结尾,命令参数仍从其后的剪贴板文本读取。
## 考虑过的替代方案
**保留字面文本并逐个修复手势。** 如 #5480 所示,每个触及交界节点的手势都需要单独证明,与文本引用之间的优先级规则也仍然存在。
**复用 `ReferenceChipNode`。** 引用 chip 是在提交时经 owner codec 序列化的 occurrence;命令不是模型内容,不能进入 `occurrences`。
**把分隔符放进 chip 文本。** inline decorator 位于块末尾时,Lexical 会在其后追加受管换行,由 `p:last-child::after` 渲染的空参数提示会移到下一行。独立的文本节点让提示留在 chip 所在行。
**用 NodeSelection 选中 chip。** 纯文本绑定的方向键、输入和删除处理器都会忽略非 range 选区,这些手势在 chip 处都需要单独处理。
**在 shell 中用按 NodeKey 索引的表保存认领。** 该表重复了节点已携带的字段,并且每次清空历史时都需要修剪。
## 影响
- Backspace 不再逐字编辑命令名并重新打开菜单。在 chip 边缘按两次 Backspace 或 Delete 会删除 chip,参数保留为普通文字。
- 刷新后,命令目录加载完成即恢复 chip;在此之前以及恢复不适用时,草稿显示字面 token。
- 通过真实按键整体删除 chip 只能在浏览器 lane 中观察,因为 jsdom 缺少 `Selection.modify`;`lifecycle-chrome.e2e.ts` 固定了删除分隔符、选中、删除这三次按键。
@@ -9,8 +9,8 @@
en: 84858bc0e1e268ab
zh: 39ebb2979968d6cc
/agent-note-chat-group-tree-positions-and-exchanges/decision:
en: ca3153ae96994baa
zh: de6c7d925559efbb
en: 043e48fc04003f7d
zh: 15842c3db3fc83f4
/agent-note-chat-group-tree-positions-and-exchanges/why-mark-the-opening-rather-than-the-notice:
en: db8d77cd773ad646
zh: ab2f03b8fc58e952
@@ -14,7 +14,7 @@ A notice is neither body text nor a user message: folding and bottom advancement
- **Group-tree positions.** `ChatConversationViewNode.groupTree` declares a Node's position in the group tree, typed `ChatGroupTree = 'exchange' | 'input' | 'turn' | 'process'` and ordered outermost first; a Node at a position also has the effect of every inner position. `exchange` opens an exchange. `input` makes the Node an input of its Turn: before the Turn's process it is the opening input, after it an interleaved input that keeps the whole Turn open. `turn` keeps the Node outside the Turn's process disclosure, `process` keeps it inside the disclosure but outside step runs, and a Node without a position joins the surrounding step run. `isAtOrOutside(node, position)` is the single reader of that order: process grouping reads `process`, whole-Turn folding reads `turn`, Turn-input detection reads `input`, and exchanges read `exchange`.
- **Exchanges.** A Turn holding an `exchange` Node opens an exchange, which lasts until the next opening Turn. The business message Definition marks only human input claimed in Step 1 (`user` or `steering`) as `exchange`; later human input and trigger notices stand at `input` and never open an exchange.
- **Built-in positions.** `turn-error`, `turn-max-tokens`, `turn-tail`, and the `turn-process` control stand at `turn`; `model-retry` stands at `process`. Two placement rules stay with ui-chat's own `assistant-step` Node: its reasoning row joins the step run while its reply row stands outside it, and the Turn's process specification keeps the finalized answer's reply outside the disclosure because which step answers changes as later steps arrive.
- **Built-in positions.** `turn-error`, `turn-max-tokens`, `turn-tail`, and the `turn-process` control stand at `turn`; `model-retry` stands at `process`. Two placement rules stay with ui-chat's own `assistant-step` Node: its reasoning row joins the step run while its reply row stands outside it, and with the default Replies After Collapse choice every reply row also stays outside the whole-Turn disclosure ([keep every Assistant reply](../feature/2026-10-15-chat-keep-assistant-replies.md)).
- **Mechanism.** `GroupTreeSegments(position)` splits the loaded Turns at the Turns holding a Node at that position. The latest segment starts at the last opening Turn, or at the first loaded Turn when the window holds none. It counts per Turn incrementally without scanning loaded Nodes, and a start move returns only the Turns that entered or left the latest segment. The Chat Builder's `bottomSource` is "the Node is visible and belongs to the latest exchange".
As a result, a notice-woken Turn no longer folds earlier Turns, so no room is reserved; the human's next ordinary message folds the whole exchange through the own-input path.
@@ -14,7 +14,7 @@ Status: implemented
- **分组树位置。** `ChatConversationViewNode.groupTree` 声明节点在分组树中的位置,类型为 `ChatGroupTree = 'exchange' | 'input' | 'turn' | 'process'`,由外到内有序;站在某个位置的节点同时具有所有内层位置的效果。`exchange` 开启一个交互段。`input` 表示节点是所在 Turn 的输入:出现在该轮过程之前即为开场输入,之后出现即为中途输入,使整个 Turn 保持展开。`turn` 让节点留在 Turn 的整轮折叠之外,`process` 让节点留在整轮折叠之内、步骤组之外,没有位置的节点并入相邻的步骤组。`isAtOrOutside(node, position)` 是读取这一顺序的唯一入口:过程分组读 `process`,整轮折叠读 `turn`,轮次输入判断读 `input`,交互段读 `exchange`。
- **交互段。** 含 `exchange` 节点的 Turn 开启一个交互段,交互段持续到下一个开启 Turn。业务层的 message Definition 只给 Step 1 中被认领的真人输入(`user` 或 `steering`)标 `exchange`;之后的真人输入和触发通知位于 `input`,不开启交互段。
- **内置位置。** `turn-error`、`turn-max-tokens`、`turn-tail` 与 `turn-process` 控件位于 `turn`;`model-retry` 位于 `process`。ui-chat 自己的 `assistant-step` 节点保留两条放置规则:推理行并入步骤组、回复行在步骤组之外;该轮的过程规格让最终答案的回复留在整轮折叠之外,因为哪一步是最终答案会随后续步骤到来而变化。
- **内置位置。** `turn-error`、`turn-max-tokens`、`turn-tail` 与 `turn-process` 控件位于 `turn`;`model-retry` 位于 `process`。ui-chat 自己的 `assistant-step` 节点保留两条放置规则:推理行并入步骤组、回复行在步骤组之外;在默认的「收起后显示的回复」选项下,每个回复行也留在整轮折叠之外([整轮折叠保留全部 Assistant 回复](../feature/2026-10-15-chat-keep-assistant-replies.zh.md))。
- **机制。** `GroupTreeSegments(position)` 按含该位置节点的 Turn 切分已加载的 Turn,最新一段从最后一个开启 Turn 开始,窗口中没有开启 Turn 时从第一个已加载 Turn 开始。它按 Turn 增量计数,不扫描已加载节点;起点移动时只返回进入或离开最新一段的 Turn。Chat Builder 的 `bottomSource` 为"节点可见且属于最新交互段"。
结果是:通知唤醒的 Turn 不再折叠前面的 Turn,没有折叠就没有补白;真人下一次普通输入时,整个交互段经由自己输入的路径一起折叠。
@@ -27,17 +27,17 @@
en: a01ac57fc71a84f0
zh: 3e488ef3da5e4b8c
/agent-note-peer-principals-and-method-level-remote-access-control/decision/method-level-access-control:
en: a55516339b0d49dd
zh: 3281df9ea7878fc0
en: 725a2d4145c0a75e
zh: b98a64532cadeaba
/agent-note-peer-principals-and-method-level-remote-access-control/decision/method-level-access-control/correspondence-with-rbac:
en: a78a2410eb52f777
zh: a868bf1b0e690c9f
/agent-note-peer-principals-and-method-level-remote-access-control/decision/method-level-access-control/resources-and-resource-readers-deepseek-ai-dsh-typert-protocol-deepseek-ai-dsh-typert-registry:
en: 625b20552962e268
zh: 1136c0fd7cdc6901
en: d50ae22978165c57
zh: 8c1fcfb7dc2d3367
/agent-note-peer-principals-and-method-level-remote-access-control/decision/method-level-access-control/endpoint-resources:
en: ec1677d8e338e115
zh: 6bedd163f208eaa9
en: 582f5f527ecc58c8
zh: 4a4b68361de2dcf0
/agent-note-peer-principals-and-method-level-remote-access-control/decision/method-level-access-control/roles-bindings-and-policies-deepseek-ai-dsh-api-remote-access:
en: e57f396239ea9f56
zh: ddb26278b1521ceb
@@ -45,8 +45,8 @@
en: 3c911f57f0f6ca4b
zh: 1509781eeda4c922
/agent-note-peer-principals-and-method-level-remote-access-control/decision/method-level-access-control/host-access-check-order:
en: 6c58dc8777e107ef
zh: bc14605c96e4d8f3
en: 2d97a251cc4c9db0
zh: df072d5a96a9e82f
/agent-note-peer-principals-and-method-level-remote-access-control/decision/method-level-access-control/forwarded-events:
en: 5fe34ee5ec31942a
zh: ef39b020b0539b8f
@@ -54,8 +54,8 @@
en: 7865e159528e2d30
zh: 1399d54632f4d926
/agent-note-peer-principals-and-method-level-remote-access-control/decision/method-level-access-control/access-snapshot-stream:
en: 41d7ac1faf2448d7
zh: 6d523895a9abae68
en: a928a63e8c36177c
zh: c943c1fdc8c4bb45
/agent-note-peer-principals-and-method-level-remote-access-control/decision/method-level-access-control/client-face:
en: 7be28cd7e19e5377
zh: 30b5aee957271d6c
@@ -75,14 +75,14 @@
en: 305ec45a7cd85ed7
zh: d2cd4209a56b1d0c
/agent-note-peer-principals-and-method-level-remote-access-control/alternatives-considered:
en: 130df2981ba550df
zh: e9a0a89af6fb50a3
en: 60da38bca6e9c3d6
zh: 61ea578fe8185b61
/agent-note-peer-principals-and-method-level-remote-access-control/consequences:
en: 89452c3c3f042e75
zh: 9bcad00a60c65303
/agent-note-peer-principals-and-method-level-remote-access-control/consequences/verification:
en: 048067eb02e6bf8a
zh: 12508b59f6b206b8
en: d7d0909974609247
zh: 6ebf20714f8d545b
/agent-note-peer-principals-and-method-level-remote-access-control/consequences/costs-and-limits:
en: 1e254cec680b1a37
zh: 06a31e057ba965aa
@@ -131,7 +131,7 @@ The single read point is `this.ctx.invocation.peer.principal`; an owner test is
### Method-level access control
Resource types and the `resources` readers of `@Remote` live in `@deepseek-ai/dsh-typert-protocol`; resource kinds live in typert beside lookups and Contexts: `TypertResourceMap` declares the kinds, `typert.resources` holds each kind's default wire field, and lookups and Host Context adapters declare the resource kind their wire identity names. The Gateway resolves one call or forwarded event to the resources it touches. Decisions, list filtering, and the access snapshot live in the new package `@deepseek-ai/dsh-api-remote-access` (`packages/api/remote-access`), which reads no argument field. The Gateway and domain packages depend on remote-access only for types and inject `remoteAccess`; their only value dependency is `dsh-typert-protocol`, so `package-dependency-policy` gains no remote-access entry. Business packages define and register roles, bindings, and policies; liveshare is the first user. Apart from the built-in `global`, no resource-kind name appears in remote-access or the Gateway, which follows the [`packages/api` carries generic orchestration only](../../../../docs/defensive-patterns/remote-and-api.md) review rule.
Resource types and the `resources` readers of `@Remote` live in `@deepseek-ai/dsh-typert-protocol`; resource kinds live in typert beside lookups and Contexts: `TypertResourceMap` declares the kinds, `typert.resources` holds each kind's default wire field or resolver, and lookups and Host Context adapters declare the resource kind their wire identity names. The Gateway resolves one call or forwarded event to the resources it touches. Decisions, list filtering, and the access snapshot live in the new package `@deepseek-ai/dsh-api-remote-access` (`packages/api/remote-access`), which reads no argument field. The Gateway and domain packages depend on remote-access only for types and inject `remoteAccess`; their only value dependency is `dsh-typert-protocol`, so `package-dependency-policy` gains no remote-access entry. Business packages define and register roles, bindings, and policies; liveshare is the first user. Apart from the built-in `global`, no resource-kind name appears in remote-access or the Gateway, which follows the [`packages/api` carries generic orchestration only](../../../../docs/defensive-patterns/remote-and-api.md) review rule.
#### Correspondence with RBAC
@@ -165,7 +165,7 @@ export interface TypertResourceMap {}
// @deepseek-ai/dsh-workspace/types: workspace: TypertResource<WorkspaceId>
// Runtime registries in typert
ctx.typert.resources.register(kind, { field }): TypertDisposer
ctx.typert.resources.register(kind, { field } | { resolve }): TypertDisposer
ctx.typert.lookups.register(key, { parameter, wire, resource?, … }) // resource: the kind its wire id names
ctx.typert.contexts.registerHost(key, { wire, resource?, … }) // a TypertHostContextAdapter
@@ -213,11 +213,11 @@ export function RemoteScope<This extends object, Args extends unknown[], Result>
| Rule | Content |
|---|---|
| Syntax | `@Remote({ name: 'page', resources: { session: (request) => request.address.kind === 'session' ? request.address.sessionId : request.address.childSessionId } })`; a Context method writes `@RemoteScope('agent')` and gets its Session resource from the Agent Context's `resource: 'session'`, and writes `@RemoteScope('agent', { resources: { workspace: (request) => request.workspaceId } })` when it needs another resource. Most methods write no `resources` and get their resources from automatic resolution |
| Syntax | `@Remote({ name: 'prompt', resources: { session: (request) => request.sessionId } })` when a method must name its resource by hand; `session/page` and `session/follow` instead rely on the registered `session` default resolver; a Context method writes `@RemoteScope('agent')` and gets its Session resource from the Agent Context's `resource: 'session'`, and writes `@RemoteScope('agent', { resources: { workspace: (request) => request.workspaceId } })` when it needs another resource. Most methods write no `resources` and get their resources from automatic resolution |
| Resource kinds | The lower-level packages that declare the identity types merge the resource kinds: `session: TypertResource<SessionId>` in `@deepseek-ai/dsh-session/types` and `workspace: TypertResource<WorkspaceId>` in `@deepseek-ai/dsh-workspace/types`. session-controller references the commands package, so a merge in session-controller would make commands reference session-controller and form a project-reference cycle. remote-access builds in `global`, which has no id and stays out of `TypertResourceMap`; typert reserves the name, and `typert.resources.register('global', …)` throws |
| Default fields and declarations | The domain that owns a kind registers its default wire field when its controller is constructed: the `SessionController` constructor calls `typert.resources.register('session', { field: 'sessionId' })` and the `WorkspaceController` constructor registers `workspace` → `workspaceId`; registering a kind twice throws. The Agent, Session, and `workspaceFileScope` lookups declare `resource: 'session'`, and the agent package's Agent Context declares `resource: 'session'` |
| Resolution precedence | Each resource kind takes, in order: ① that kind's reader in the method's `resources`; ② every lookup parameter that declares that kind as its `resource` contributes its wire identity, and an omitted optional lookup parameter contributes none; when the Context of a Context method declares that kind as its `resource`, the Context's wire identity contributes too; ③ that kind's default field: a top-level named argument of that name when one exists, otherwise that field of the first object argument. A default field names one identity, so an array there is an invalid value |
| Argument values | Resource resolution and the `args` of `decide()` read the named arguments validated against their Typert-compiled types: each JSON argument is decoded once by its codec, which drops object fields its type does not declare, and dispatch reuses that value, so a caller cannot change the resources with an undeclared field; lookup parameters and the Context identity keep their wire identities, and no lookup or Agent is resolved at that point |
| Default extraction declarations | The owning domain registers either `{ field }` or `{ resolve }`, never both. SessionController registers `resolveSessionResource`; WorkspaceController registers `workspaceId`. Duplicate kinds are rejected. Lookup and Host Context resource declarations remain independent of these defaults |
| Resolution precedence | Per kind: explicit method reader, then lookup and Host Context identities, then the registered default field or resolver. A field reads the named top-level parameter or the first object argument. A resolver receives validated named `args` and ordered parameter descriptions once before lookup resolution. Defaults return one non-empty string or undefined; arrays are invalid |
| Argument values | Compiled codecs strip undeclared object fields; SRC fallback checks JSON safety without stripping them. Resolution and dispatch use the same validated values. The Session resolver rejects a request object containing both sessionId and address, even when equal. Lookup and Context identities remain wire values until after authorization |
| Resolution result | The resources of every kind together are the resources the call touches; with none, the call touches `[global]`; when resources resolve, only those are used and `global` is not added |
| Readers | Called with the method's positional arguments: the named arguments mapped by the descriptor back to the method's parameter order (the validated value in a JSON position, the wire identity in a lookup position, and the call's signal in the cancellation position); return one id, an id array (every one must be allowed), or `undefined` (no resource of that kind) |
| Invalid values | A reader that throws or returns an empty array, or a read value (including a Context's wire identity) that is not a non-empty string, makes the resources invalid: the Gateway refuses the call with `remote-access/denied` (reason `resource-invalid`) without asking `decide()` |
@@ -228,7 +228,7 @@ export function RemoteScope<This extends object, Args extends unknown[], Result>
#### Endpoint resources
The table lists the endpoints #3854 viewers use and where their resources come from. Only `session/page` and `session/follow` need explicit `resources`; the rest rely on a lookup's `resource` or the default field:
The table lists the endpoints #3854 viewers use and where their resources come from. Session history endpoints use the registered Session resolver; special method readers can override defaults:
| Endpoint | Resource | Resolution source | Filtering inside the method |
|---|---|---|---|
@@ -241,16 +241,18 @@ The table lists the endpoints #3854 viewers use and where their resources come f
| `llm/listProviders` | `global` | no resource | no |
| `commands/list` | session: `args.agentId` (the Agent lookup's wire field) | Agent lookup `resource: 'session'` | no |
| `goals/get` | session: `args.agentId` | Agent lookup `resource: 'session'` | no |
| `session/attachment` | session: `args.request.sessionId` | default field | no |
| `skills/list` | session: `args.request.sessionId` | default field | no |
| `session/fork` | session: `args.request.sessionId` | default field | no |
| `session/prompt` | session: `args.request.sessionId` | default field | no |
| `session/cancel` | session: `args.request.sessionId` | default field | no |
| `session/projections` | session: `args.request.sessionId` | default field | no |
| `session/page` | session: `args.request.address.sessionId` for a Session address; `args.request.address.childSessionId` for a subagent address | `resources` | no |
| `session/follow` | same as above | `resources` | no |
| `session/attachment` | session: `args.request.sessionId` | default resolver | no: a grant on one Session permits reads from the whole attachment store |
| `skills/list` | session: `args.request.sessionId` | default resolver | no |
| `session/fork` | session: `args.request.sessionId` | default resolver | no |
| `session/prompt` | session: `args.request.sessionId` | default resolver | no |
| `session/cancel` | session: `args.request.sessionId` | default resolver | no |
| `session/projections` | session: `args.request.sessionId` | default resolver | no |
| `session/page` | session: `args.request.address.sessionId` for a Session address; `args.request.address.childSessionId` for a subagent address | default resolver | no |
| `session/follow` | same as above | default resolver | no |
| `remoteAccess/follow` | `global` | no resource; remote-access allows it for every principal | no |
For `session/attachment`, `sessionId` identifies the resource for the Gateway check; it does not constrain which image may be read. Once that check permits the call, an image may belong to another Session or no Session, and the handler does not check whether the supplied Session exists. Policy authors must treat this action as access to the whole attachment store, as defined by the [image access rule](2026-10-23-store-scoped-image-reads.md).
A subagent address reads the child transcript, so its resource is the child Session. A Client rereads the projections of known Sessions after a reconnect, including the parent of a subagent Session; a reader without a grant on that parent has its projection read refused. Whether a grant on a parent Session covers its subagent children is the policy's decision, that is, whether it gives a binding for the child Session; the generic layer derives no parent resource.
Methods that need no non-owner access declare nothing, and a method no role lists is refused. The request fields of subagent `prompt` are `parentSessionId` / `childSessionId`, so automatic resolution finds no Session and resolves it to `global`; it must therefore stay out of every `global` role. `session/search` filters its hits per Session inside the method. `subagents/list` is not a Remote endpoint (`SubagentRuntime.list()` carries no `@Remote`).
@@ -392,7 +394,7 @@ Every call path (HTTP `/api`, WebSocket streams, in-process `fetch(request, peer
| 1 | Admission yields the Peer: network paths from Connection `admit()` (`browserPeer`), in-process paths from the caller. The Gateway checks `peer.signal` before it reads the wire payload | Failed network admission answers HTTP 401 / 403 without entering the Gateway; a disposed Peer: `gateway/peer-disposed` |
| 2 | Resolve the endpoint and validate the argument field set (`resolveDescriptor`, `assertExactArguments`), then validate the JSON arguments against their Typert-compiled types, decoding each once | `gateway/invocation-unavailable`, `gateway/ambiguous-endpoint`, `gateway/definition-unavailable`; extra or missing fields: `gateway/arguments-invalid`; a JSON argument failing validation: `gateway/input-invalid` (for owners and non-owners alike, before the access check) |
| 3 | Owner pass-through: for `principal.kind === 'owner'` go straight to step 5 | — |
| 4 | Non-owner: the Gateway resolves resources from the arguments validated in step 2 (`resources` readers, then lookup parameters and the `resource` a Context declares, then default fields; lookup and Context identities are wire identities, and neither a lookup nor the Context is resolved), then asks `remoteAccess.decide({ principal, action: endpoint, resources, args })` with the validated arguments as `args`; invalid resources are refused with `resource-invalid` without asking `decide()`; a `deny` decision becomes the thrown error | `remote-access/denied` with details `{ action, reason }` |
| 4 | Non-owner: the Gateway resolves resources from the arguments validated in step 2 (`resources` readers, then lookup parameters and the `resource` a Context declares, then the registered default field or resolver; lookup and Context identities are wire identities, and neither a lookup nor the Context is resolved), then asks `remoteAccess.decide({ principal, action: endpoint, resources, args })` with the validated arguments as `args`; invalid resources are refused with `resource-invalid` without asking `decide()`; a `deny` decision becomes the thrown error | `remote-access/denied` with details `{ action, reason }` |
| 5 | Dispatch: `resolveReceiverContext` (which can activate an Agent), lookup resolution (JSON arguments reuse the step 2 values), and the method call | Existing codes unchanged, such as `gateway/lookup-not-found`, `gateway/context-not-found`, `gateway/input-invalid`, `gateway/service-unavailable`, `gateway/method-unavailable`, and business errors |
The access check (steps 3–4) precedes lookup and Context resolution, so a refused call activates no Session and causes no other side effect. The only new Gateway error code is `gateway/peer-disposed`; remote-access declares the refusal code `remote-access/denied`.
@@ -446,7 +448,7 @@ export const REMOTE_ACCESS_SNAPSHOT_KEY = '__DSH_REMOTE_ACCESS__'
| Behavior | Detail |
|---|---|
| First snapshot | `snapshotFor(principal)` computes a page's opening baseline. remote-access adds the global `REMOTE_ACCESS_SNAPSHOT_KEY`, set to the owner baseline `{ type: 'baseline', owner: true }`, to every index the webServer renders, through `webserver/index-inject`. A business package that renders a non-owner page (such as liveshare on the owner Host) places an assignment of that global to `snapshotFor(principal)` after every index injection row; the later assignment wins, and the Client reads the global when it mounts |
| First snapshot | `snapshotFor(principal)` computes a page's opening baseline. The webServer renders each index for the Peer the page speaks for, and `webserver/index-inject` passes that Peer to listeners; remote-access adds the global `REMOTE_ACCESS_SNAPSHOT_KEY`, set to `snapshotFor(peer.principal)`, to every index, so the owner's page receives the owner baseline `{ type: 'baseline', owner: true }` and a page rendered for a non-owner Peer (such as a liveshare viewer page on the owner Host) receives that principal's grants. The Client reads the global when it mounts |
| Subscription | Each Peer subscribes to one access snapshot stream, whose baseline replaces the injected one |
| Computation | All bindings of the policy for the principal's kind are merged by equal resource; each resource's `actions` are the endpoint actions of those bindings' roles that the same decision as a call allows (one resource, without args, conditions called without args); only a resource with at least one allowed endpoint action gets a grant. Decisions and snapshots share one source: for the same principal, action, and resource (without arguments), the snapshot answer equals `decide()` |
| Endpoint actions only | `event:<name>` actions do not enter the snapshot; the frontend decides entry visibility from endpoint actions only |
@@ -600,7 +602,7 @@ The review rule's home is the "`@Remote` marks capability methods" section of [R
**Refusal reasons from conditions only.** Without a binding there is no condition to give a reason, so a viewer page could not tell "share expired" from "share not writable"; `explain` gives a reason only after the decision is already a refusal and never changes it.
**A global resource-resolver registry (domain packages register recognizers by field name).** Field recognition separates from the method, nothing flags a method signature change, and same-named fields in other packages are misread; resource resolution uses a default convention on the method plus per-method `resources`, and a domain package registers only its own kind's default field.
**A global resource-resolver registry (domain packages register recognizers by field name).** Field recognition separates from the method, nothing flags a method signature change, and same-named fields in other packages are misread; resource resolution uses a default convention on the method plus per-method `resources`, and a domain package registers only its own kind's default field or resolver.
**Dot-path resource declarations (`'request.address.sessionId'`).** Types cannot constrain a path; the parameter types of `resources` readers are inferred from the decorated method, so a typo fails compilation.
@@ -635,10 +637,10 @@ The review rule's home is the "`@Remote` marks capability methods" section of [R
| Check order | The owner calls any method normally without reading a policy; a method no role lists refuses non-owners with `remote-access/denied` (`no-allow`); the access check precedes lookups, so a refused call activates no Session | Gateway host spec |
| Policy registration | One policy per non-owner principal kind, and a second registration throws; once the disposer withdraws it, that kind is refused with `no-allow`; it is withdrawn with its fiber | remote-access host spec |
| Decisions | Every resource needs a matching binding (AND); a role without the action, an unequal resource, or a refusing condition does not allow; the refusal reason follows condition > `explain` > `no-allow`; `explain` never changes the decision; conditions receive args for calls but not for snapshots and list filtering; a throwing `bindings()`, condition, or `explain()` refuses with `policy-failed`, each policy is logged once between `invalidate()` calls, and on the event path the owner stream and the shared event source continue; `remoteAccess/follow` is allowed for every principal | remote-access host spec, Gateway host spec |
| Resource resolution | A default field (top-level or first object argument) yields its resource; a lookup parameter that declares that kind as its `resource` yields its wire identity; a Context method gets its Session resource from the Agent Context identity in args without activating the Agent, and may declare other resources through `resources`; a `resources` reader overrides its kind, an array yields several resources, and `undefined` none; with nothing the call is `[global]`; a throw, an empty array, or a value that is not a non-empty string (including a Context identity) is invalid, refused by the Gateway with `resource-invalid` without asking `decide()`; a Context that declares no `resource` resolves by the ordinary rules; resources and `args` read the validated arguments, so an undeclared nested field changes no resource and no condition sees it, and a JSON argument failing validation returns `gateway/input-invalid` before the access check | Gateway host spec (gateway-access) |
| Resource registry | `typert.resources` registers one default field per kind, rejects a second registration and `global`, and withdraws the registration with its fiber; a configured lookup resolver keeps the provider's `resource`; a Host Context adapter keeps its `resource` | typert-registry spec |
| Resource resolution | A default field or resolver yields one resource; a default resolver returning an array is invalid; a lookup parameter that declares that kind as its `resource` yields its wire identity; a Context method gets its Session resource from the Agent Context identity in args without activating the Agent, and may declare other resources through `resources`; a `resources` reader overrides its kind, an array yields several resources, and `undefined` none; with nothing the call is `[global]`; a throw, an empty array, or a value that is not a non-empty string (including a Context identity) is invalid, refused by the Gateway with `resource-invalid` without asking `decide()`; a Context that declares no `resource` resolves by the ordinary rules; resources and `args` read the validated arguments, with undeclared fields stripped only by compiled codecs, and a JSON argument failing validation returns `gateway/input-invalid` before the access check | Gateway host spec (gateway-access) |
| Resource registry | `typert.resources` registers one default field or resolver per kind, rejects a second registration and `global`, and withdraws the registration with its fiber; a configured lookup resolver keeps the provider's `resource`; a Host Context adapter keeps its `resource` | typert-registry spec |
| Forwarded events | A non-owner `$events` stream receives an event, whole, only when `event:<name>` is granted on its resources; an event that declares no resources is decided on `global`; an event whose reader returns `undefined` does not reach non-owners, and `decide()` is not asked; the owner receives all | Gateway host spec (gateway-access) |
| First snapshot | `snapshotFor()` returns the owner baseline or the principal's grants; remote-access injects the owner baseline into every rendered index; the Client starts from the injected baseline and rejects a malformed one | remote-access host and client specs |
| First snapshot | `snapshotFor()` returns the owner baseline or the principal's grants; remote-access injects the snapshot of the Peer each index speaks for; the Client starts from the injected baseline and rejects a malformed one | remote-access host and client specs |
| Type inference | The parameter types of `resources` readers are inferred from the method; lookup positions have wire types; a missing property, more parameters than the method has, and a `global` key fail compilation; a binding whose resource kind differs from its role's fails compilation | typert-protocol spec, remote-access spec (type tests) |
| List filtering | `session/list`, `session/search`, `session/control`, and `workspace/follow` return non-owners only items for which `allows()` is true; `workspace/follow` sends a `replace` increment when a delivered Workspace loses visibility, and the Workspace reappears once access returns; owner results are unchanged | session-controller / workspace-controller host spec |
| Access snapshots | The allowed set in a snapshot equals the Host decision for the same principal, action, and resource without arguments; it contains endpoint actions only; a resource with no allowed action gets no grant, and one whose allowed set becomes empty is removed; `invalidate()` and policy registration or removal push increments; a wait that a change ends leaves no abort listener behind; unloading the service ends every open snapshot stream; the owner receives only `{ owner: true }` | remote-access host spec |
@@ -131,7 +131,7 @@ export function principalOf(invocation: RemoteInvocation | undefined): Principal
### 方法级访问控制
资源类型与 `@Remote` 的 `resources` 读取函数在 `@deepseek-ai/dsh-typert-protocol`;资源种类在 typert 中与 lookups、Contexts 并列:`TypertResourceMap` 声明种类,`typert.resources` 保存每个种类的默认 wire 字段,lookup 与 Host Context adapter 声明其 wire 身份所属的资源种类。Gateway 把一次调用或一个转发事件解析为它访问的资源。判定、列表过滤与访问快照在新包 `@deepseek-ai/dsh-api-remote-access`(`packages/api/remote-access`),该包不读取任何参数字段。Gateway 与领域包对 remote-access 只做 type-only 依赖并 inject `remoteAccess`,值依赖只有 `dsh-typert-protocol`,不为 remote-access 新增 `package-dependency-policy` 条目。role、binding 与 policy 由业务包定义和注册,liveshare 为首个使用方。除内置的 `global` 外,remote-access 与 Gateway 中不出现任何资源种类名,符合 [`packages/api` 只承载通用编排](../../../../docs/defensive-patterns/remote-and-api.zh.md) 的评审规则。
资源类型与 `@Remote` 的 `resources` 读取函数在 `@deepseek-ai/dsh-typert-protocol`;资源种类在 typert 中与 lookups、Contexts 并列:`TypertResourceMap` 声明种类,`typert.resources` 保存每个种类的默认 wire 字段或 resolver,lookup 与 Host Context adapter 声明其 wire 身份所属的资源种类。Gateway 把一次调用或一个转发事件解析为它访问的资源。判定、列表过滤与访问快照在新包 `@deepseek-ai/dsh-api-remote-access`(`packages/api/remote-access`),该包不读取任何参数字段。Gateway 与领域包对 remote-access 只做 type-only 依赖并 inject `remoteAccess`,值依赖只有 `dsh-typert-protocol`,不为 remote-access 新增 `package-dependency-policy` 条目。role、binding 与 policy 由业务包定义和注册,liveshare 为首个使用方。除内置的 `global` 外,remote-access 与 Gateway 中不出现任何资源种类名,符合 [`packages/api` 只承载通用编排](../../../../docs/defensive-patterns/remote-and-api.zh.md) 的评审规则。
#### 与 RBAC 的对应
@@ -165,7 +165,7 @@ export interface TypertResourceMap {}
// @deepseek-ai/dsh-workspace/types: workspace: TypertResource<WorkspaceId>
// Runtime registries in typert
ctx.typert.resources.register(kind, { field }): TypertDisposer
ctx.typert.resources.register(kind, { field } | { resolve }): TypertDisposer
ctx.typert.lookups.register(key, { parameter, wire, resource?, … }) // resource: the kind its wire id names
ctx.typert.contexts.registerHost(key, { wire, resource?, … }) // a TypertHostContextAdapter
@@ -213,11 +213,11 @@ export function RemoteScope<This extends object, Args extends unknown[], Result>
| 规则 | 内容 |
|---|---|
| 写法 | `@Remote({ name: 'page', resources: { session: (request) => request.address.kind === 'session' ? request.address.sessionId : request.address.childSessionId } })`;Context 方法写 `@RemoteScope('agent')`,由 Agent Context 的 `resource: 'session'` 得到会话资源,需要其他资源时写 `@RemoteScope('agent', { resources: { workspace: (request) => request.workspaceId } })`。多数方法不写 `resources`,由自动解析得到资源 |
| 写法 | 方法必须手工点名资源时写 `@Remote({ name: 'prompt', resources: { session: (request) => request.sessionId } })`;`session/page` 与 `session/follow` 则依赖已注册的 `session` 默认 resolver;Context 方法写 `@RemoteScope('agent')`,由 Agent Context 的 `resource: 'session'` 得到会话资源,需要其他资源时写 `@RemoteScope('agent', { resources: { workspace: (request) => request.workspaceId } })`。多数方法不写 `resources`,由自动解析得到资源 |
| 资源种类 | 资源种类由声明 id 类型的底层包合并:`session: TypertResource<SessionId>` 在 `@deepseek-ai/dsh-session/types`,`workspace: TypertResource<WorkspaceId>` 在 `@deepseek-ai/dsh-workspace/types`。session-controller 引用 commands 包,若在 session-controller 合并,commands 要引用 session-controller,形成 project reference 环。`global` 由 remote-access 内置,没有 id,不进 `TypertResourceMap`;typert 保留该名,`typert.resources.register('global', …)` 抛错 |
| 默认字段与声明 | 拥有该种类的领域在控制器构造时登记默认 wire 字段:`SessionController` 构造函数调用 `typert.resources.register('session', { field: 'sessionId' })`,`WorkspaceController` 构造函数登记 `workspace` → `workspaceId`;同一种类重复登记即抛错。Agent、Session 与 `workspaceFileScope` lookup 声明 `resource: 'session'`,agent 包的 Agent Context 声明 `resource: 'session'` |
| 解析优先级 | 对每个资源种类依次取:① 该方法 `resources` 中该种类的读取函数;② 声明了该种类 `resource` 的每个 lookup 参数贡献其 wire 身份,省略的可选 lookup 参数不贡献;Context 方法的 Context 声明了该种类 `resource` 时,其 wire 身份同样贡献;③ 该种类的默认字段:命名参数中有同名顶层参数就读它,否则读第一个对象参数的同名字段。默认字段只表示一个身份,读到数组即为非法值 |
| 参数来源 | 资源解析与 `decide()` 的 `args` 读取经 Typert 编译类型校验后的命名参数:每个 JSON 参数由其 codec 解码一次,类型未声明的对象字段被移除,分派复用该值,因此调用方无法用未声明字段改变资源;lookup 参数与 Context 身份取 wire 身份,此时不解析 lookup、不激活 Agent |
| 默认提取声明 | 资源所属领域登记 `{ field }` 或 `{ resolve }`,两者互斥。SessionController 登记 `resolveSessionResource`;WorkspaceController 登记 `workspaceId`。重复种类被拒绝;lookup 和 Host Context 的资源声明独立于默认规则 |
| 解析优先级 | 每个种类依次采用方法显式 reader、lookup 与 Host Context 身份、注册的默认字段或 resolver。字段读取同名顶层参数或首个对象参数。resolver 在 lookup 解析前调用一次,接收已校验的命名 `args` 及有序参数描述。默认规则返回一个非空字符串或 undefined,数组非法 |
| 参数来源 | 编译 codec 移除未声明的对象字段;SRC fallback 只检查 JSON 安全性,不移除字段。解析和分派使用相同的已校验值。Session resolver 拒绝同时包含 sessionId 与 address 的请求对象,即使两者相同。lookup 和 Context 身份在授权完成前保持 wire 值 |
| 解析结果 | 各种类的资源合在一起即本次调用访问的资源;一个都没有即 `[global]`;解析出资源时只用这些资源,不附加 `global` |
| 读取函数 | 以方法的位置参数调用:按描述符把命名参数映射回方法参数顺序(JSON 位为校验后的值,lookup 位为 wire 身份,取消参数位为调用的 signal);返回一个 id、id 数组(每个都须被允许)或 `undefined`(该种类无资源) |
| 非法值 | 读取函数抛错或返回空数组,或读到的值(含 Context 的 wire 身份)不是非空字符串即资源非法:Gateway 不询问 `decide()`,直接以 `remote-access/denied`(reason `resource-invalid`)拒绝调用 |
@@ -228,7 +228,7 @@ export function RemoteScope<This extends object, Args extends unknown[], Result>
#### 端点的资源
下表列出 #3854 读者用到的端点及其资源来源。只有 `session/page` 与 `session/follow` 需要显式 `resources`,其余靠 lookup 的 `resource` 或默认字段:
下表列出 #3854 读者用到的端点及其资源来源。Session 历史端点使用注册的 Session resolver;特殊方法可用显式 reader 覆盖默认规则:
| 端点 | 资源 | 解析来源 | 方法内过滤 |
|---|---|---|---|
@@ -241,16 +241,18 @@ export function RemoteScope<This extends object, Args extends unknown[], Result>
| `llm/listProviders` | `global` | 无资源 | 否 |
| `commands/list` | session:`args.agentId`(Agent lookup 的 wire 字段) | Agent lookup `resource: 'session'` | 否 |
| `goals/get` | session:`args.agentId` | Agent lookup `resource: 'session'` | 否 |
| `session/attachment` | session:`args.request.sessionId` | 默认字段 | 否 |
| `skills/list` | session:`args.request.sessionId` | 默认字段 | 否 |
| `session/fork` | session:`args.request.sessionId` | 默认字段 | 否 |
| `session/prompt` | session:`args.request.sessionId` | 默认字段 | 否 |
| `session/cancel` | session:`args.request.sessionId` | 默认字段 | 否 |
| `session/projections` | session:`args.request.sessionId` | 默认字段 | 否 |
| `session/page` | session:Session 地址读 `args.request.address.sessionId`;subagent 地址读 `args.request.address.childSessionId` | `resources` | 否 |
| `session/follow` | 同上 | `resources` | 否 |
| `session/attachment` | session:`args.request.sessionId` | 默认 resolver | 否:一条 Session 上的授权允许读取整个附件存储 |
| `skills/list` | session:`args.request.sessionId` | 默认 resolver | 否 |
| `session/fork` | session:`args.request.sessionId` | 默认 resolver | 否 |
| `session/prompt` | session:`args.request.sessionId` | 默认 resolver | 否 |
| `session/cancel` | session:`args.request.sessionId` | 默认 resolver | 否 |
| `session/projections` | session:`args.request.sessionId` | 默认 resolver | 否 |
| `session/page` | session:Session 地址读 `args.request.address.sessionId`;subagent 地址读 `args.request.address.childSessionId` | 默认 resolver | 否 |
| `session/follow` | 同上 | 默认 resolver | 否 |
| `remoteAccess/follow` | `global` | 无资源;remote-access 对任何 principal 放行 | 否 |
对于 `session/attachment`,`sessionId` 指定 Gateway 检查的资源,不限制可读取哪张图片。该检查放行后,图片可以属于其他 Session 或不属于任何 Session,处理函数也不检查请求中的 Session 是否存在。policy 作者必须把此 action 视为整个附件存储的读取权限,具体规则见[图片访问规则](2026-10-23-store-scoped-image-reads.zh.md)。
subagent 地址读取的是子会话转录,因此其资源是子会话。Client 在重连后会重新读取已知会话的 projections,其中包括 subagent 会话的父会话;没有该父会话授权的读者,其 projection 读取被拒。父会话的授权是否覆盖其 subagent 子会话由 policy 决定,即是否为子会话给出 binding;通用层不推导父资源。
不需要非 owner 访问的方法不做任何声明,没有 role 列出它即被拒。subagent `prompt` 的请求字段为 `parentSessionId` / `childSessionId`,自动解析不到会话,解析为 `global`,因此不得放进任何 `global` 级 role;`session/search` 在方法内按会话逐项过滤命中。`subagents/list` 不是 Remote 端点(`SubagentRuntime.list()` 未标 `@Remote`)。
@@ -392,7 +394,7 @@ const childPrompt = child => ({ action, args }) =>
| 1 | 准入得到 Peer:网络路径由 Connection `admit()` 给出(`browserPeer`);进程内路径由调用方传入。Gateway 在读取 wire payload 之前检查 `peer.signal` | 网络准入失败返回 HTTP 401 / 403(不进入 Gateway);Peer 已 dispose:`gateway/peer-disposed` |
| 2 | 解析端点并做参数字段集校验(`resolveDescriptor`、`assertExactArguments`),再按 Typert 编译类型校验 JSON 参数,每个参数解码一次 | `gateway/invocation-unavailable`、`gateway/ambiguous-endpoint`、`gateway/definition-unavailable`;字段多余或缺失:`gateway/arguments-invalid`;JSON 参数校验失败:`gateway/input-invalid`(owner 与非 owner 相同,先于访问检查) |
| 3 | owner 直通:`principal.kind === 'owner'` 时直接进入第 5 步 | — |
| 4 | 非 owner:Gateway 从第 2 步校验后的参数解析资源(`resources` 读取函数、其次 lookup 参数与 Context 声明的 `resource`、再次默认字段;lookup 与 Context 身份取 wire 身份,不解析 lookup 与 Context),再以校验后的参数为 `args` 询问 `remoteAccess.decide({ principal, action: endpoint, resources, args })`;资源非法时不询问 `decide()`,直接以 `resource-invalid` 拒绝;`deny` 结论变为抛出的错误 | `remote-access/denied`,details `{ action, reason }` |
| 4 | 非 owner:Gateway 从第 2 步校验后的参数解析资源(`resources` 读取函数、其次 lookup 参数与 Context 声明的 `resource`、再次注册的默认字段或 resolver;lookup 与 Context 身份取 wire 身份,不解析 lookup 与 Context),再以校验后的参数为 `args` 询问 `remoteAccess.decide({ principal, action: endpoint, resources, args })`;资源非法时不询问 `decide()`,直接以 `resource-invalid` 拒绝;`deny` 结论变为抛出的错误 | `remote-access/denied`,details `{ action, reason }` |
| 5 | 分派:`resolveReceiverContext`(可能激活 Agent)、lookup 解析(JSON 参数复用第 2 步的值)、调用方法 | 既有错误码不变,如 `gateway/lookup-not-found`、`gateway/context-not-found`、`gateway/input-invalid`、`gateway/service-unavailable`、`gateway/method-unavailable` 与业务错误 |
鉴权(第 3–4 步)位于 lookup 与 Context 解析之前,未授权调用不会激活会话或产生其他副作用。Gateway 新增的错误码只有 `gateway/peer-disposed`;拒绝码 `remote-access/denied` 由 remote-access 声明。
@@ -446,7 +448,7 @@ export const REMOTE_ACCESS_SNAPSHOT_KEY = '__DSH_REMOTE_ACCESS__'
| 行为 | 说明 |
|---|---|
| 首份快照 | `snapshotFor(principal)` 计算页面打开时的 baseline。remote-access 经 `webserver/index-inject` 为 webServer 渲染的每个 index 加入全局变量 `REMOTE_ACCESS_SNAPSHOT_KEY`,值为 owner baseline `{ type: 'baseline', owner: true }`。渲染非 owner 页面的业务包(例如 owner Host 上的 liveshare)在所有 index 注入行之后放置一条把该全局变量赋值为 `snapshotFor(principal)` 的语句;后写的赋值生效,Client 在挂载时读取该全局变量 |
| 首份快照 | `snapshotFor(principal)` 计算页面打开时的 baseline。webServer 按页面所代表的 Peer 渲染 index,`webserver/index-inject` 把该 Peer 传给监听方;remote-access 为每个 index 加入全局变量 `REMOTE_ACCESS_SNAPSHOT_KEY`,值为 `snapshotFor(peer.principal)`,owner 页面因此得到 owner baseline `{ type: 'baseline', owner: true }`,为非 owner Peer 渲染的页面(例如 owner Host 上的 liveshare 读者页)得到该 principal 的 grant。Client 在挂载时读取该全局变量 |
| 订阅 | 每个 Peer 订阅一条访问快照流,其 baseline 取代注入的快照 |
| 计算 | 取该 principal kind 的 policy 的全部 binding,按资源相等归并;每个资源的 `actions` 为这些 binding 的 role 中、经与调用相同的判定(单个资源、不带 args,condition 以无 args 调用)放行的端点 action;只为至少放行一个端点 action 的资源给出 grant。判定与快照同源:同一 principal、action 与资源(不带参数)上,快照答案与 `decide()` 一致 |
| 只含端点 action | `event:<name>` action 不进入快照;前端只据端点 action 决定入口显隐 |
@@ -600,7 +602,7 @@ GlobalStandardProps.useWorkspaceAccess: (workspaceId: WorkspaceId, action: Remot
**拒绝原因只由 condition 给出。** 没有 binding 时没有 condition 可以给出原因,读者页面无法区分「分享已过期」「分享不允许写入」等情况;`explain` 只在结论已为拒绝后给出原因,不改变结论。
**全局资源 resolver 注册表(领域包按字段名注册识别函数)。** 字段识别与方法分离,方法改签名时无人提醒,跨包同名字段也会被误认;资源解析采用方法上的默认约定加 per-method `resources`,领域包只登记自己种类的默认字段。
**全局资源 resolver 注册表(领域包按字段名注册识别函数)。** 字段识别与方法分离,方法改签名时无人提醒,跨包同名字段也会被误认;资源解析采用方法上的默认约定加 per-method `resources`,领域包只登记自己种类的默认字段或 resolver。
**按点路径声明资源(`'request.address.sessionId'`)。** 路径无法由类型推导约束;`resources` 读取函数的参数类型从被装饰方法推断,拼错即编译失败。
@@ -635,10 +637,10 @@ GlobalStandardProps.useWorkspaceAccess: (workspaceId: WorkspaceId, action: Remot
| 鉴权顺序 | owner 调用任何方法照常分派且不读取 policy;没有 role 列出的方法对非 owner 以 `remote-access/denied`(`no-allow`)拒绝;访问检查早于 lookup,被拒调用不激活会话 | Gateway host spec |
| policy 注册 | 每个非 owner principal kind 一个 policy,重复注册抛错;disposer 撤回后该 kind 以 `no-allow` 拒绝;随 fiber 撤回 | remote-access host spec |
| 判定 | 每个资源都需要匹配的 binding(AND);role 不含该 action、资源不相等或 condition 拒绝都不放行;拒绝原因按 condition > `explain` > `no-allow` 取;`explain` 不改变结论;condition 在调用时收到 args,在快照与列表过滤时不收;`bindings()`、condition 或 `explain()` 抛错以 `policy-failed` 拒绝,每个 policy 在两次 `invalidate()` 之间只记录一次日志,事件路径上 owner 流与共享事件源照常;`remoteAccess/follow` 对任何 principal 放行 | remote-access host spec、Gateway host spec |
| 资源解析 | 默认字段(顶层或首个对象参数)得到对应资源;声明了该种类 `resource` 的 lookup 参数得到其 wire 身份;Context 方法从 args 中的 Agent Context 身份得到其 session 资源且不激活 Agent,另可经 `resources` 声明其他资源;`resources` 读取函数覆盖该种类,数组为多个资源,`undefined` 为无;都没有为 `[global]`;抛错、空数组或非非空字符串(含 Context 身份)为非法,Gateway 以 `resource-invalid` 拒绝且不询问 `decide()`;未声明 `resource` 的 Context 按普通规则解析;资源与 `args` 读取校验后的参数,未声明的嵌套字段不改变资源、condition 也读不到它,JSON 参数校验失败在访问检查前返回 `gateway/input-invalid` | Gateway host spec(gateway-access) |
| 资源注册表 | `typert.resources` 每个种类登记一个默认字段,拒绝重复登记与 `global`,并随 fiber 撤回;配置的 lookup resolver 保留 provider 的 `resource`;Host Context adapter 保留其 `resource` | typert-registry spec |
| 资源解析 | 默认字段或 resolver 得到一个资源,默认 resolver 返回数组为非法;声明了该种类 `resource` 的 lookup 参数得到其 wire 身份;Context 方法从 args 中的 Agent Context 身份得到其 session 资源且不激活 Agent,另可经 `resources` 声明其他资源;`resources` 读取函数覆盖该种类,数组为多个资源,`undefined` 为无;都没有为 `[global]`;抛错、空数组或非非空字符串(含 Context 身份)为非法,Gateway 以 `resource-invalid` 拒绝且不询问 `decide()`;未声明 `resource` 的 Context 按普通规则解析;资源与 `args` 读取校验后的参数,只有编译 codec 才移除未声明字段,JSON 参数校验失败在访问检查前返回 `gateway/input-invalid` | Gateway host spec(gateway-access) |
| 资源注册表 | `typert.resources` 每个种类登记一个默认字段或 resolver,拒绝重复登记与 `global`,并随 fiber 撤回;配置的 lookup resolver 保留 provider 的 `resource`;Host Context adapter 保留其 `resource` | typert-registry spec |
| 转发事件 | 非 owner 的 `$events` 流只在 `event:<name>` 于其资源上被授权时整条收到事件;未声明资源的事件按 `global` 判定;读取函数返回 `undefined` 的事件不送达非 owner,且不询问 `decide()`;owner 全部收到 | Gateway host spec(gateway-access) |
| 首份快照 | `snapshotFor()` 返回 owner baseline 或该 principal 的 grant;remote-access 为渲染的每个 index 注入 owner baseline;Client 从注入的 baseline 起步并拒绝格式错误的快照 | remote-access host 与 client spec |
| 首份快照 | `snapshotFor()` 返回 owner baseline 或该 principal 的 grant;remote-access 为每个 index 注入其所代表 Peer 的快照;Client 从注入的 baseline 起步并拒绝格式错误的快照 | remote-access host 与 client spec |
| 类型推断 | `resources` 读取函数的参数类型从方法推断;lookup 位为 wire 类型;访问不存在的属性、参数多于方法参数与 `global` 键编译失败;binding 的资源种类与 role 不符编译失败 | typert-protocol spec、remote-access spec(类型测试) |
| 列表过滤 | `session/list`、`session/search`、`session/control`、`workspace/follow` 对非 owner 只返回 `allows()` 为 true 的项;已下发工作区失去可见性时 `workspace/follow` 发送 `replace` 增量帧,访问恢复后该工作区重新出现;owner 结果不变 | session-controller / workspace-controller host spec |
| 访问快照 | 快照中的允许集合与 Host 对同一 principal、action、资源(不带参数)的判定一致;只含端点 action;没有放行 action 的资源不出 grant,放行集合变空时推送 `remove`;`invalidate()` 与 policy 注册、撤回后推送增量;被变化结束的等待不残留 abort 监听器;卸载服务即结束所有打开的快照流;owner 只收 `{ owner: true }` | remote-access host spec |
Loaded 100 of 7685 files, more files were not shown because too many files have changed in this diff. Show more