Compare commits

...
Author SHA1 Message Date
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
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
imccyu f2235d6c02 release(dsh): 0.2.1-alpha.4 2026-10-15 20:00:58 +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
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
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 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
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
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
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
Yichen Jiang 2fbf4359b8 Merge pull request #6234 from deepseek-harness/worktree/release-dsh-0.2.1-alpha.3
release(dsh): 0.2.1-alpha.3
2026-10-14 19:48:55 +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
Yichen Jiang 36acfb1e88 release(dsh): 0.2.1-alpha.3 2026-10-14 19:38:37 +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
Yichen Jiang 02128b3a31 Merge pull request #6218 from deepseek-harness/worktree/release-vendor-0.2.1-alpha.3
release(vendor): prepare alpha.2 packages for DSH 0.2.1-alpha.3
2026-10-14 19:28:00 +08:00
Yichen Jiang 7c8cbea696 release(vendor): cordis 4.0.5-alpha.2, cosmokit 1.8.6-alpha.2, group 1.0.5-alpha.2, hmr 1.0.20-alpha.2, include 1.0.10-alpha.2, loader 1.0.6-alpha.2, logger-console 1.0.5-alpha.2, schemastery 3.18.5-alpha.2, timer 1.1.7-alpha.2 2026-10-14 19:22:48 +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
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
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
Turtle 4b3cb7629e Merge pull request #6206 from deepseek-harness/feat/builtin-plugin-preview-hardcoded
feat(plugin-manager): show a built-in feature preview for the badge skill
2026-10-14 17:07:10 +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
imccyu a941635df3 Merge pull request #5945 from deepseek-harness/worktree/client-control/operator-connect
feat(connection): scope the connect handshake to one operator
2026-10-14 16:56:53 +08:00
turtle1999andZiyaZhang a2141868de fix(plugin-manager): reset a failed preview by source instead of a key
Co-authored-by: ZiyaZhang <[email protected]>
2026-10-14 16:55:21 +08:00
turtle1999andZiyaZhang aac50c7923 docs(notes): record the built-in plugin feature preview decision
Co-authored-by: ZiyaZhang <[email protected]>
2026-10-14 16:41:59 +08:00
Dudu-0223 a04b8ef7e2 feat(agent-team): simplify team availability prompt 2026-10-14 16:36:16 +08:00
turtle1999andZiyaZhang 7fcaf07051 feat(plugin-manager): show a built-in feature preview for the badge skill
Plugin Manager hardcodes feature previews by package name, like its
experimental bundle lists, instead of reading a package manifest field.
The DSH badge skill bundle shows an English or Chinese illustration
below its description. Client bundles embed imported SVG, PNG, and
WebP files as data URLs through the shared tsdown preset, which
replaces the account package's own asset loader.

Co-authored-by: ZiyaZhang <[email protected]>
2026-10-14 16:22:56 +08:00
07akioni 04c9489065 fix(telemetry): handle native metadata edge cases and scalar failures 2026-10-14 16:10:40 +08:00
Yichen Jiang fedaed7139 Merge pull request #6190 from deepseek-harness/worktree/conversation-hero-options
feat(web): add the New Session options slot
2026-10-14 15:57:04 +08:00
lsdsjy 328584875a Merge pull request #6184 from deepseek-harness/fix/shared-account-avatar
fix(account): 共享头像加载结果并在打开设置时重试
2026-10-14 15:47:50 +08:00
ZiyaZhang 34252e34d0 docs(plugins): define built-in feature preview guidelines 2026-10-14 00:27:53 -07:00
07akioni 4dee41f4bc feat(telemetry): attach cached desktop environment to product events 2026-10-14 15:23:58 +08:00
Tianyi Cui d0286d55d5 Merge pull request #5934 from deepseek-harness/docs/cordis-cleanup-audit
docs(cordis): audit cleanup dependency lifetimes as P0
2026-10-14 15:14:19 +08:00
CreatixChu f9c634d092 Merge pull request #4456 from deepseek-harness/feat/experimental-dsh-cua
feat(cua): 支持 macOS 原生 Computer Use
2026-10-14 14:57:42 +08:00
ZiyaZhang 37dbd15e0c feat(plugins): localize optional feature previews 2026-10-13 23:55:11 -07:00
lsdsjy 3df5eca5df Merge master into fix/shared-account-avatar 2026-10-14 14:47:18 +08:00
Yichen Jiang 3f1d5dfcfa feat(ui-conversation): add the New Session options slot
conversation.hero.options seats compact Session options at the right end of
the New Session row, after the workspace and agent-preset controls. It is a
session-scoped list slot rendered only while a Session exists, so plugins can
add choices such as a worktree toggle without displacing the shipped controls.
2026-10-14 14:45:32 +08:00
creatixchu 5da34fb5ae test(cua): cover capability projection on every platform 2026-10-14 14:45:14 +08:00
imccyu c233ca3df5 fix(typert): recognize Cordis Context across built copies 2026-10-14 14:44:01 +08:00
imccyu 1d0ff1d2e4 test(snapshot): let inspect fixture receive events 2026-10-14 14:44:01 +08:00
imccyu b679a3aa32 docs(typert): include Peer interaction participation in type excerpt 2026-10-14 14:44:01 +08:00
imccyu fd1b8bd095 test(gateway): supply interaction mode for stream Peer fixture 2026-10-14 14:44:01 +08:00
imccyu 10672c4c24 Integrate Peer interaction scope with Remote access 2026-10-14 14:44:01 +08:00
imccyu d3de1613b6 Bundle shared Clients separately for browser and Node 2026-10-14 14:44:00 +08:00
imccyu 88ee3d2961 test(connection): cover operator lifecycle failures and update fixtures 2026-10-14 14:44:00 +08:00
imccyu 0f7ac58213 feat(connection): isolate API operators without changing browser identity 2026-10-14 14:44:00 +08:00
imccyu 30d48c4d49 test(client): share dynamic import bundle fixture 2026-10-14 14:44:00 +08:00
imccyu e137ebaaa1 Build shared Client bundles independently for Node and browser 2026-10-14 14:43:59 +08:00
lsdsjy 2a7ba15ee1 fix(account): 同步头像渲染失败并完善恢复验证 2026-10-14 14:37:22 +08:00
lsdsjy b2ea1d1b71 Merge pull request #6169 from deepseek-harness/fix/account-failure-stderr
fix(account): 账号接口仅失败时输出 stderr 日志
2026-10-14 14:24:37 +08:00
creatixchu aeada33774 test(cua): verify window listing invalidates stale image coordinates 2026-10-14 14:18:51 +08:00
lsdsjy d0b0bfcd79 fix(account): 保持取消静默并合并失败诊断 2026-10-14 14:11:13 +08:00
ZiyaZhang fb752c7763 style(plugins): refine badge feature illustration 2026-10-13 23:07:32 -07:00
lsdsjy 521c7c000f fix(account): 共享头像加载结果并在打开设置时重试 2026-10-14 14:06:32 +08:00
creatixchu 43391ef048 fix(cua): use the supported Zod issue type 2026-10-14 14:06:10 +08:00
creatixchu bde699d225 test(cua): refresh prompts after syncing master 2026-10-14 13:57:05 +08:00
creatixchu 220cd03bea Merge master into macOS CUA 2026-10-14 13:51:30 +08:00
creatixchu 12b3fa5304 test(cua): verify readable errors within PTC exception output 2026-10-14 13:35:45 +08:00
creatixchu 8ed497a02d test(cua): pin readable validation errors and coordinate guidance 2026-10-14 13:33:35 +08:00
creatixchu b01b889632 fix(cua): report readable argument errors and clarify coordinate targets 2026-10-14 13:32:36 +08:00
lsdsjy cb18185c9d Merge pull request #5532 from deepseek-harness/fix/bonus-notice-onboarding
fix(account): 延后赠金通知并隔离身份补齐通知
2026-10-14 13:27:37 +08:00
ZiyaZhang e527c04663 feat(plugins): support optional feature previews in bundle details 2026-10-13 22:22:43 -07:00
winewill 7ebaa991e9 Merge pull request #6172 from deepseek-harness/build/desktop-manual-publish
feat(desktop): publish retained packages independently of packaging
2026-10-14 13:21:44 +08:00
lsdsjy 5102f4f715 fix(account): 保留浏览器赠金通知并补齐身份订阅说明 2026-10-14 13:17:17 +08:00
Turtle 88d90d5883 Merge pull request #6122 from deepseek-harness/turtle/ask-question-exclusive-1fa495b41a5b
fix(ui-user-questions): show one selection when a single-select custom field is focused
2026-10-14 12:44:05 +08:00
Turtle bae0b78718 Merge pull request #5860 from deepseek-harness/turtle/stop-reason-visible-6a05d9dbbed1
fix(ui-chat): show a stopped notice for turns interrupted by a DSH stop
2026-10-14 12:41:24 +08:00
Turtle 6e765e5fed Merge pull request #6167 from deepseek-harness/turtle/workspace-cancel-adds-8a3aebe529b2
fix(directory-picker-native): ignore macOS panel selection on cancel
2026-10-14 12:36:54 +08:00
Turtle 56c5cf84a1 Merge pull request #6170 from deepseek-harness/turtle/rescue-profile-overwrite-2aaffdd2f31b
Make --from-default-profile replace an existing custom profile
2026-10-14 12:35:08 +08:00
Magolor 7110b615eb fix(llm): 生成提供方元数据并显示凭据检测状态 (#6106)
* fix(llm): generate provider metadata and clarify credential hints

* fix(settings): shorten credential hints and mark detected sources in green

* fix(llm): verify staged provider metadata and document regeneration

* fix(provider-metadata): reconcile authentication hints and key controls

* test(user-text): calibrate ratio headroom across unmatched workloads

* test(gates): include provider metadata in hygiene expectations

* fix(models): hide authentication hints for entered keys
2026-10-14 12:29:31 +08:00
winewill ce64289dbd fix(desktop): validate publication dispatch and package job identity 2026-10-14 12:22:27 +08:00
Turtle 6bf5668e79 Harden profile replacement against case aliases and Windows rename errors
Reject case variants of shipped profile names, detect a concurrent winner by
the target's state instead of errno, warn instead of failing when staging
cleanup fails after a successful replacement, and cover the restore paths with
deterministic tests. Update the profile Agent Note, reference recovery path,
and upgrade-guide migration steps.
2026-10-14 12:12:52 +08:00
winewill 60084a5b59 feat(desktop): publish retained packages through manual dispatch 2026-10-14 12:10:00 +08:00
Turtle 8843c37e37 Make --from-default-profile replace an existing custom profile
Repeated rescue runs such as `dsh rescue --from-default-profile web` failed
because the target profile already existed. The option now assembles a fresh
template copy in a staging directory, moves it over the target, and deletes the
previous profile; a failed move restores it.
2026-10-14 11:56:17 +08:00
TurtleandMagolor d3b465da91 Credit review by @Magolor
Co-authored-by: Magolor <[email protected]>
2026-10-14 11:54:57 +08:00
lsdsjy c9d56d5073 fix(account): 仅在账号接口失败时输出 stderr 日志 2026-10-14 11:52:55 +08:00
Turtle 570c52d202 fix(directory-picker-native): ignore macOS panel selection on cancel
NSOpenPanel keeps its current directory in URLs after Cancel, so a cancelled pick added a workspace. Read the selection only on an OK response.
2026-10-14 11:28:50 +08:00
lsdsjy 6514255930 Merge origin/master into fix/bonus-notice-onboarding 2026-10-14 11:20:42 +08:00
creatixchu 7bfefd3118 test(cua): record unread paste failure and observed recovery 2026-10-14 11:13:03 +08:00
creatixchu e587c71600 test(cua): exercise paste recovery before closing fixture transport 2026-10-14 11:11:36 +08:00
creatixchu 688137b0a2 refactor(cua): derive screenshot metadata from protocol schema 2026-10-14 11:09:42 +08:00
creatixchu 537a1f5047 docs(cua): explain unread paste recovery and foreground evidence 2026-10-14 11:09:20 +08:00
creatixchu 0f83b644c8 fix(cua): bound clipboard consumption waits with actionable failure 2026-10-14 11:08:00 +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
imccyu a2486e8243 Merge pull request #6160 from deepseek-harness/worktree-listperf
perf(session): speed up @ session candidates and session listing
2026-10-14 10:52:25 +08:00
imccyu d1f4261356 Merge pull request #6158 from deepseek-harness/worktree-foldmidscroll
fix(ui-chat): fold next-message work details on one frame clock
2026-10-14 10:52:10 +08:00
ZiyaandZiyaZhang ecebe2a2b3 fix(plugin): clarify DSH badge skill descriptions in English and Chinese (#6156)
* fix(plugin): clarify DSH badge skill descriptions

* fix(plugin): remove redundant badge attribution explanation

---------

Co-authored-by: ZiyaZhang <[email protected]>
2026-10-13 19:51:03 -07:00
imccyu a6e9071b82 fix(ui-chat): continue batch members from their current size
A revealing row that ran on its own CSS transition and was then taken
over by a batch entered the landing twice: the measured starting layout
held the height it had already reached, and the growth amount added its
full height again while the batch restarted it from zero. The extra
height raised the tail target above the real floor and stayed as bottom
room. Rows revealed while a batch ran kept their own transitions and were
taken over this way by the next batch.

- A row that starts revealing while a batch runs waits at zero size like
  a held closing row; the next flush drives it in a batch or starts its
  own transition when nothing closes.
- A batch continues every member from its current size: closing rows
  shrink from the height they have now, and revealing rows grow from the
  height they reached, so only the remaining growth enters the landing.
2026-10-14 03:38:35 +08:00
imccyu 57fa64db18 fix(ui-chat): fold rows that close mid-batch in the next batch
A row that started closing while a fold batch ran kept its own CSS
transition and outlived the batch. During that window following moved to
the floor while reclaimBelow held still, so the bottom room was followed
in full instead of being consumed by the content that arrived, and it
stayed under the reader afterwards.

- A row that closes while a batch runs keeps its size without a
  transition until the batch settles; the next batch starts at once and
  drives it. Without a driver at that flush the held row finishes at once.
- A batch takes over a closing row at its current size and finishes a
  row that has no size left.
- The tail landing reads the committed layout when it finishes: it lands
  at the larger of the target and the real floor and keeps only the room
  that target needs.
2026-10-14 03:13:00 +08:00
imccyu 7f7940f0e2 fix(ui-chat): address second review on single fold motion
- A tail request counts only for closing rows that can start a batch,
  using the same membership rule as the batch, and detaching clears it.
- A batch keeps the reader landing while reading movement awaits
  sampling, so an unsampled scroll away from the tail is not undone.
- Reader scrolling during a reader landing moves the reference row's
  recorded position, so the final correction keeps only layout changes.
- The blank-area matrix releases a partly opened world, fails a case on
  cleanup errors, mounts only the provider catalog for the Turn that fails
  before its model call, names its gap as a layout gap, and labels the
  steering case as a no-fold control.
- The note records the pending-sampling rule and the opacity of a row
  that joins the travelling set after fading.
2026-10-14 01:53:16 +08:00
imccyu 33c37200f8 perf(token-meter): keep only system prompts in the context breakdown state
contextBreakdown kept one entry per retained surface node, so its
checkpoint grew with the message count (7.13 MB across 836 local Sessions,
up to 103 KB for one) and every cached Session listing validated it. The
state now holds the surviving system prompts, one surface total, and at
most one shadow-price claim. Appends add their price; a replacement
subtracts the adjacent compaction/summary or compaction/prune price and
drops the system prompts its shadowedSeqs name; a system-prompt rewrite
subtracts the retained prompt price. Other unmetered replacements fold at
zero delta, as in contextPressure. State version 6 discards positional
rows. Folding all 836 local logs with both versions gives identical
figures after every event; retained state drops to 110 KB.
2026-10-14 01:32:16 +08:00
imccyu 768473d755 perf(session-reference): resolve label services once per candidate listing
Every `@` candidate query labeled each listed Session through three
service lookups, and each lookup and method read through a Cordis service
proxy allocates wrappers, so the labeling step grew with the Session count.
The resolver now resolves sessions, projections, and the projection cache
once per call, indexes attached Sessions in one map, and binds
cachedSnapshot once for cold Sessions.
2026-10-14 01:32:15 +08:00
imccyu 6aa157a05a perf(session-persistence-jsonl): bound listing reads with a shared concurrency limit
list(), the root encoding check, and historical corpus fingerprints read
project directories, Session directories, headers, and stats one at a time.
On a network filesystem every listing paid the summed round trips. These
reads now run through one p-limit limiter per backend (listConcurrency,
default 3), shared by concurrent listings; results and the first reported
failure keep the sequential order.
2026-10-14 01:32:14 +08:00
imccyu 06829b67b6 test(web): bound the fold matrix's live-Turn waits
The queued and steer cases held the model call at a global call index and
waited without bounds, so any shifted or missing call stalled the case
until the 240 s test timeout. The hold now targets the first call in the
case's Session that streams the case's marker text, every wait has an
explicit timeout whose error lists the observed calls and the Session's
latest events, scroll settling is bounded, and the cases use the web
lane's default timeout.
2026-10-14 00:51:05 +08:00
imccyu 25a69605ad fix(ui-chat): address review on single fold motion
- A tail request counts only while a fold batch runs or is scheduled,
  so rows still closing on CSS transitions cannot strand own input.
- A reader-content-still landing adds the reader's own scrolling between
  frames to the position it keeps still.
- A newer tail request that takes over a running batch recomputes which
  closing rows travel with the content.
- Symmetric cancel and resume guards; Set-based travelling bookkeeping.
- The blank-area matrix holds the subagent continuation route for child
  sends, writes per-frame evidence only on failure, and labels the parent
  message case as a no-fold control.
- The group-tree note now links the single fold motion for input without
  an echo on this page.
2026-10-14 00:32:00 +08:00
imccyu a4426261ab fix(ui-chat): fold next-message work details on one frame clock
Folding on the next message reserved the whole closing height as bottom
room and kept scrollTop whenever the input had no transcript echo on this
page: a message from another tab, a queued message delivered after the
previous Turn, or a send from a subagent child in the right Sidebar. A
reader below the fold was left on an empty viewport.

Every batch of closing rows now runs on the viewport's frame-clock driver.
It lands at the tail for this client's own input or a following reader and
otherwise keeps the reader's content still, installing only the room the
landing lacks. Subagent child sends register a local echo and pass its
request id to subagents.prompt, so the child's recorded message retires it.
2026-10-14 00:31:59 +08:00
imccyu 0af3e0dbc3 Merge pull request #6090 from deepseek-harness/worktree-remoteaccess
feat(remote-access): carry caller principals on peers and gate remote methods
2026-10-14 00:27:05 +08:00
imccyu b632f50330 docs: regenerate cordis catalogs 2026-10-14 00:02:56 +08:00
imccyu 529706d163 fix(remote-access): drop buffered events on revoke, filter search, declare workspace file resources 2026-10-13 23:55:05 +08:00
imccyu 7e335ee608 style(remote-access): wrap the request args JSDoc 2026-10-13 23:13:36 +08:00
imccyu 0c4a7e3e8c fix(api-gateway): resolve access resources from typert-validated arguments 2026-10-13 23:03:16 +08:00
imccyu a18488955d docs: sync subsystem docs and regenerate catalogs 2026-10-13 23:03:15 +08:00
imccyu b967475c66 feat(client-ui): add remote access hooks 2026-10-13 23:01:38 +08:00
imccyu 2640d3df82 feat(api): resolve endpoint resources and filter lists by access 2026-10-13 23:01:38 +08:00
imccyu d7bd68ee7a feat(remote-access): add rbac policies and gate non-owner calls and events 2026-10-13 23:01:37 +08:00
imccyu 9c6374909b feat(typert): declare remote method and event resources 2026-10-13 23:01:37 +08:00
imccyu d6e408d957 feat(session-controller): record non-owner principals on user message sources 2026-10-13 23:01:37 +08:00
imccyu f6316536ea feat(client-connection): carry caller principals on explicit peers 2026-10-13 23:01:37 +08:00
imccyu f6e4b5026c docs(notes): record peer principals and rbac remote access 2026-10-13 23:01:37 +08:00
imccyu 3203ee3393 Merge pull request #5607 from deepseek-harness/feat/hmr-stale-package-reload
feat(hmr): replace plugins whose package files changed during profile recomposition
2026-10-13 22:36:27 +08:00
turtle1999 879ad372ac feat(hmr): replace plugins whose package files changed during profile recomposition
With HMR, installing, enabling, disabling and removing bundles take effect live. Each profile recomposition compares
the loaded modules below the profile directory and its linked targets with disk, stops the plugins whose files
changed, and recomposes the profile through app-boot's recomposeProfile(): the rows the next generation drops are
removed first, the latest runtime resolution is published once, and the generation is applied and judged. Afterwards
HMR evicts the modules below the mappings the recomposition dropped.

- app-boot: recomposeProfile(); PluginPackages.resolution; ResolutionRestartRequired; the resolver accepts in-place
  version changes and relinks, and relocates Node's cached link real paths.
- plugin-manager: with HMR, installBundle refuses an installed package (already-installed), so an upgrade is a
  removal and an installation; every restart-required result carries a restartReason; removal uses the registry the
  package was installed from.
- ui-plugin-manager: the Official Update removes and then installs; restart prompts name their reason.
2026-10-13 22:15:57 +08:00
creatixchu 0cca141695 test(cua): pin foreground paste guidance 2026-10-13 21:41:50 +08:00
creatixchu ef12e53d58 docs(cua): require foreground paste for ChatGPT and Codex 2026-10-13 21:40:17 +08:00
creatixchu ec85b94309 test(cua): update screenshot object guidance snapshots 2026-10-13 21:33:05 +08:00
creatixchu e5b0b492d8 fix(cua): preserve screenshot format and dimensions in object API 2026-10-13 21:30:47 +08:00
ZiyaandZiyaZhang 9f5bc9669b fix(schedule): 简化自动化任务标题,去除时间与执行备注 (#6105)
* fix(schedule): keep timing and execution notes out of task titles

* fix(schedule): avoid repeating task title guidance

* fix(schedule): keep title guidance examples in English

---------

Co-authored-by: ZiyaZhang <[email protected]>
2026-10-13 05:38:34 -07:00
creatixchu ac0a449378 test(cua): expect screenshot pixels in shared loader output 2026-10-13 19:51:34 +08:00
creatixchu 6e554ab609 docs(cua): describe encoded screenshot results consistently 2026-10-13 19:49:21 +08:00
creatixchu 6e9db1ba17 test(cua): wait for overlay hit testing and retain successful hover replay 2026-10-13 19:24:15 +08:00
creatixchu 57bd5e5438 test(cua): cover pointer-transparent overlays and record input decisions 2026-10-13 19:18:42 +08:00
Turtle 2b302a1814 Merge pull request #6058 from deepseek-harness/turtle/plugin-enable-redundant-override
fix(plugin-manager): drop profile switches the bundles already imply
2026-10-13 19:16:41 +08:00
creatixchu ff1864c6cf fix(cua): retain other window mappings during scoped observations 2026-10-13 19:07:22 +08:00
creatixchu 06e671f143 fix(cua): complete coordinate serialization and projection references 2026-10-13 19:02:50 +08:00
creatixchu 5658751da0 feat(cua): map screenshot pixels and optional relative coordinates 2026-10-13 18:59:05 +08:00
creatixchu fd3868adc6 feat(cua): reveal alternate hover UI before moving the system pointer 2026-10-13 18:21:50 +08:00
creatixchu 379ad9d485 fix(cua): use mouse hit testing for system hover 2026-10-13 18:17:11 +08:00
Yichen Jiang 6e82f79e95 Merge pull request #6125 from deepseek-harness/ui/pin-more-plugins-to-main-list
fix(web): 在「更多」页开启的实验性插件固定到插件主页列表
2026-10-13 17:59:36 +08:00
Wenlu Wang 18be29bfbe Merge pull request #6055 from deepseek-harness/fix/user-text-benchmark-sampling
test(benchmarks): stabilize user-text timing samples
2026-10-13 17:42:27 +08:00
winewill f094f04c96 Merge pull request #6116 from deepseek-harness/build/desktop-cos-publish
feat(desktop): 支持原机器产物复用与审批式 COS 发布
2026-10-13 17:42:10 +08:00
Yudong Han a29f5ee1e3 Merge pull request #6108 from deepseek-harness/fix/empty-response-text
fix(llm): 保留跨协议完整历史并过滤 DeepSeek 请求空文本
2026-10-13 17:11:43 +08:00
turtle1999 e776e2b3d2 refactor(plugin-manager): pass the profile to writePluginEnabled 2026-10-13 17:09:18 +08:00
winewill e637eecaa9 test(desktop): cover channel and source packaging inputs 2026-10-13 16:58:18 +08:00
lsdsjy fc17ae9145 Merge pull request #5381 from deepseek-harness/feat/web-account-login
feat(web): 在 Web UI 开放 DeepSeek 账号登录
2026-10-13 16:33:49 +08:00
Turtle 8013c1cd07 Merge pull request #6121 from deepseek-harness/turtle/credentials-unknown-key-4538f7b4e481
fix(credentials-local): ignore unknown top-level keys with a warning
2026-10-13 16:29:09 +08:00
Yichen Jiang f98cfe3cf3 Merge pull request #6126 from deepseek-harness/fix/macos-directory-picker-edit-menu
fix(directory-picker-native): install the macOS panel's Edit menu
2026-10-13 16:23:30 +08:00
TurtleandLegGasai b4f3f60ced Credit review by @LegGasai
Co-authored-by: LegGasai <[email protected]>
2026-10-13 16:17:11 +08:00
winewill fdbe2bce47 feat(desktop): separate update channels and confirm releases before approval 2026-10-13 16:14:41 +08:00
dsh-portal[bot] f31da59d83 fix(directory-picker-native): install the macOS panel's Edit menu
The macOS chooser ran AppleScript's `choose folder`, whose panel belongs to an
osascript helper that has no application menu. A native panel resolves
⌘C/⌘V/⌘A/⌘X as main-menu key equivalents rather than as behavior of the
focused field editor, so Go to Folder, Search, and Tags could not be pasted
into even though the panel's own ⌘⇧G worked.

Run one JXA script that installs the standard Edit menu and then opens
NSOpenPanel with the previous configuration; untargeted items let AppKit
enable them against the responder chain that owns the panel's field editor.
The prompt stays argv data, and a cancel now prints an empty line, so the
stderr "User canceled" sniffing and its helper are gone.

The README pair re-records its consistency record for the two edited sections.
2026-10-13 16:11:06 +08:00
lsdsjy 32f81a511b test(web): use account menu in workspace hover tests 2026-10-13 16:06:53 +08:00
Turtle acaed1b782 docs: regenerate config catalog 2026-10-13 15:49:34 +08:00
yudshj 5ba6b2da82 test(llm): 空文本历史真实 API 用例固定使用官方根地址
与同文件其他真实 API 用例一致,boot() 不再接受 baseURL 参数,
该用例不再读取 DEEPSEEK_BASE_URL。
2026-10-13 15:49:00 +08:00
lsdsjy 2009b90d7e Merge remote-tracking branch 'origin/master' into feat/web-account-login
# Conflicts:
#	apps/web/tests/sidebar-terminal.e2e.ts
2026-10-13 15:48:48 +08:00
Yichen Jiang a611c4e891 Merge pull request #6117 from deepseek-harness/ui/header-actions-band-order
fix(web): 页头操作区顺序改为 预设 → 子智能体 → 后台任务
2026-10-13 15:39:01 +08:00
Turtle 004aa05085 fix(credentials-local): keep foreign big integers exact and cover reload warnings
Re-serializing a document with a foreign integer beyond 2^53 no longer rounds it;
the reload warning is tested; the GITLAB_TOKEN test no longer depends on the host
environment; README and JSDoc describe the tolerated top-level keys.
2026-10-13 15:37:16 +08:00
Turtle 7e36650116 docs(ui-user-questions): state the custom-row focus rule literally 2026-10-13 15:36:04 +08:00
dsh-portal[bot] 60328899e5 fix(web): pin a plugin enabled on the More page to the main plugins list
Switching on an experimental bundle from the More page left it only on
that page, so a plugin the profile had enabled was missing from the main
Experimental plugins group. Both lists now read one order over the
offered bundles: the main list holds the always-held ones plus the
held-back ones the profile enabled, and a pinned bundle keeps its
More-list position. A bundle's details opened outside the lists return to
the list that shows it.
2026-10-13 15:34:36 +08:00
Turtle 8e413612c8 fix(credentials-local): ignore unknown top-level keys with a warning
An unknown top-level key such as GITLAB_TOKEN no longer fails boot; it stays
on disk, resolves nothing, and is reported by name in a warning.
2026-10-13 15:20:44 +08:00
Turtle 279302b762 fix(ui-user-questions): keep a focused single-select custom row unselected while an option is chosen 2026-10-13 15:20:37 +08:00
Ruilin Geng 854e3871d0 Merge pull request #4643 from deepseek-harness/subagent-agent-presets
feat(subagent): select and restore presets for spawned agents
2026-10-13 15:17:29 +08:00
dsh-portal[bot] a6207eae63 fix(ui-subagent): order the header band preset, subagent catalog, background tasks
The root-session descendant-count dropdown registered at order -30, which put
it ahead of the preset label at the start of the header actions band. Order 10
places the catalog between the preset label (-10) and background tasks (20),
after Team navigation (-20), and off the list slot's default 0 that unordered
registrations share.

The web e2e assertion and header golden that pin these contributors' relative
order move with it.
2026-10-13 14:44:37 +08:00
winewill 6e629a5ef0 fix(desktop): preserve publish diagnostics and interrupted recovery 2026-10-13 14:20:45 +08:00
Dudu-0223 37be39704c docs: refresh subagent config catalog source location 2026-10-13 14:15:14 +08:00
Dudu-0223 b9958b901b Merge master into subagent-agent-presets and resolve integration conflicts 2026-10-13 14:10:56 +08:00
winewill 04d46c1912 fix(desktop): use native shells for cross-platform publication 2026-10-13 14:01:39 +08:00
winewill d156ee31a0 feat(desktop): publish retained packages through approved COS workflow 2026-10-13 13:59:01 +08:00
Dudu-0223 ea5ae3a7ef Merge pull request #6098 from deepseek-harness/fsy/team-runtime-context
fix(agent-team): inject identity through required runtime context
2026-10-13 13:56:54 +08:00
LogosVeritas 9f68f9be4a Merge pull request #6032 from deepseek-harness/feature/minimal-compact-image
Enable image reading and automatic context compaction in Minimal GUI and SDK profiles, with /compact in the GUI and per-tool filesystem configuration. Include image-budget recovery, cancellation handling, documentation, and updated runtime snapshots.
2026-10-13 13:56:25 +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
yudshj 30aa47281d fix(llm): 保留原始协议历史并验证跨协议无损往返 2026-10-13 12:58:08 +08:00
Dudu-0223 85259d7cfd docs(agent-team): clean up identity review feedback 2026-10-13 12:51:29 +08:00
yudshj c368939151 fix(llm): 修复响应转换产生空文本及旧会话重放失败 2026-10-13 12:26:21 +08:00
creatixchu 1571086a3b test(cua): retain the PTC transcript and clean up its helper 2026-10-13 12:13:12 +08:00
creatixchu 18ed46b1aa test(cua): seed the PTC replay and isolated helper 2026-10-13 12:10:57 +08:00
LogosVeritas 2de77d0339 test(snapshot): stabilize minimal compaction estimates 2026-10-13 12:10:16 +08:00
creatixchu ba1c62f8e7 test(cua): record direct native results through Node PTC 2026-10-13 12:10:12 +08:00
creatixchu 4377e0a998 fix(cua): retain serializable screenshot reference fields 2026-10-13 12:02:14 +08:00
creatixchu 89d018e296 fix(cua): use the public attachment admission export 2026-10-13 12:00:34 +08:00
creatixchu 4203741099 refactor(cua): use native Harness tool results and attachment admission 2026-10-13 11:59:14 +08:00
fz 565bf3c3d7 Merge pull request #6079 from deepseek-harness/feat/page-font-size-shortcuts
feat(web): add desktop font size shortcuts
2026-10-13 11:49:09 +08:00
creatixchu 46fcc3fd55 docs(cua): include the native Chinese reference in the package 2026-10-13 11:33:41 +08:00
creatixchu ac9eebafdf test(cua): raise and observe the selected native hover window 2026-10-13 11:31:09 +08:00
creatixchu 5ae8ba49db docs(cua): remove the superseded native text reference 2026-10-13 11:29:54 +08:00
kingwl 49c92c889d test(benchmarks): await sample cleanup and clarify timing reports 2026-10-13 11:29:00 +08:00
creatixchu b332bcfb7d docs(cua): publish native helper reference as Markdown 2026-10-13 11:28:54 +08:00
creatixchu 8a7f941112 fix(cua): make hover use persistent system-pointer movement 2026-10-13 11:25:19 +08:00
LogosVeritas b560423995 fix(minimal): configure tools and complete image offload recovery 2026-10-13 11:25:18 +08:00
Dudu-0223 44d41c9473 test(agent-team): refresh direct inbox identity snapshots 2026-10-13 11:20:53 +08:00
LogosVeritas 4703022ad6 Merge remote-tracking branch 'origin/master' into feature/minimal-compact-image 2026-10-13 11:19:17 +08:00
creatixchu 8304d5dee0 test(cua): activate the explicit system-hover fixture before observation 2026-10-13 11:19:07 +08:00
creatixchu 366c6409c8 fix(cua): use ordinary mouse events for system-pointer hover 2026-10-13 11:18:12 +08:00
kingwl 5d074a3f89 test(benchmarks): stabilize user-text timing samples 2026-10-13 11:17:51 +08:00
creatixchu 2a798a91e0 test(cua): cover fresh-window hover with explicit system input 2026-10-13 11:15:45 +08:00
creatixchu 4ff8889e06 fix(cua): retain the mouse-movement event number for hover 2026-10-13 11:14:03 +08:00
Dudu-0223 219be197c9 fix(agent-team): inject member identity through required runtime context 2026-10-13 11:12:49 +08:00
creatixchu 4fbf4889f9 fix(cua): correct native test completion message 2026-10-13 11:12:21 +08:00
creatixchu 02bbff61b6 test(cua): keep hover regressions independent of keyboard focus 2026-10-13 11:11:58 +08:00
fz 59b63f4ced Merge origin/master to include Azure native auth fix 2026-10-13 11:11:02 +08:00
creatixchu 9781ac0fda test(cua): isolate AppKit hover verification from keyboard scenarios 2026-10-13 11:08:46 +08:00
creatixchu 17c253ce7e feat(cua): support explicit system-pointer hover for AppKit tracking 2026-10-13 11:06:15 +08:00
Yichen Jiang 7a27ac50aa Merge pull request #6095 from deepseek-harness/worktree/hotfix-azure-native-auth
fix(llm-pi-ai): align Azure provider IDs with pi-ai 1.1.0
2026-10-13 11:03:04 +08:00
creatixchu b899807dc3 test(cua): verify native tracking areas and hover replay 2026-10-13 11:02:52 +08:00
creatixchu 2c4339bb60 test(cua): cover hover target rejection and continuous interaction 2026-10-13 11:01:39 +08:00
Turtle ead3556a04 Merge pull request #6054 from deepseek-harness/fix/subagent-no-sleep-wait
fix(prompt,goal): end the turn while waiting on subagents and read line ranges with read
2026-10-13 11:00:00 +08:00
LogosVeritas 591ed98f08 test(minimal): align CLI and Python runtime expectations 2026-10-13 10:58:09 +08:00
creatixchu 4a74e17e7b feat(cua): preserve hover across observations and popup interaction 2026-10-13 10:57:19 +08:00
Dudu-0223 c6f60e68f0 Merge pull request #6008 from deepseek-harness/fix/subagent-depth-awareness
fix(subagent): announce exhausted delegation depth
2026-10-13 10:55:07 +08:00
Yichen Jiang 23a542fcfa fix(llm-pi-ai): align preview and settings with the Azure provider ID 2026-10-13 10:51:10 +08:00
Yichen Jiang 27800fcd40 fix(llm-pi-ai): use the Azure provider ID for native auth 2026-10-13 10:36:52 +08:00
LogosVeritas 00a007d833 Merge branch 'master' into feature/minimal-compact-image 2026-10-13 10:32:01 +08:00
fz 5d84c56d84 refactor(web): share font shortcut bindings and order actions 2026-10-13 10:23:43 +08:00
fz 3ea4f4baa3 Merge remote-tracking branch 'origin/master' into feat/page-font-size-shortcuts
# Conflicts:
#	apps/web/tests/expected/plugin-manager/header-responsive.expected.md
#	apps/web/tests/plugin-manager.e2e.ts
2026-10-13 10:13:38 +08:00
Magolor 5249d9a4ce fix: prefer saved credentials and use environment keys in headless (#5483)
* ui: show locked API key environment reference

* fix(ui-settings-models): skip unused pi-ai credential reference

* fix(ui-settings-models): show active provider auth source

* test(llm): cover provider auth check failures

* docs(ui-settings-models): clarify active provider authentication

* test(web): update model key copy snapshots

* fix(ui-settings-models): clarify native authentication source hint

* fix: address PR 5483 review feedback

* docs(llm): state empty auth lookup behavior

* fix: refresh auth hints with credential snapshots

* fix: require credential refresh revision

* test: cover auth status projection

* fix: refresh catalog and browser expectations after rebase

* fix: keep auth status projection internal and explicit

* fix: preserve credential source checks with signed-in account rows

* test: pin Models authentication instead of inheriting host keys

* fix: prioritize stored credentials across providers

* fix: use one environment fallback hint

* docs: refresh credential contract

* docs: refresh config catalog

* test: align provider hint verification

* fix: use provider-native auth hints and endpoints

* fix: cover oauth-capable provider auth hints

* fix: show canonical endpoint defaults

* fix: avoid misleading endpoint fallback

* fix: label catalog endpoint defaults

* fix: shorten auth hints with hover details

* fix: clarify native auth resolution

* test: align DeepSeek endpoint hint

* fix: pair help button corner shape

* fix: address credential precedence review

Record the stored-credential precedence in a superseding Agent Note and
an upgrade guide, and align the credentials subsystem page, package
READMEs, and JSDoc with the order file > launch env > $cwd/.env >
$DSH_HOME/.env.

Report each pi-ai route's catalog endpoint and native-auth environment
names through the provider directory, check the names against the
installed pi-ai resolvers, and remove the llm/checkAuth and credential
describe operations that no Models page code calls.

Restore the route-derived key reference and the home expansion that
reads the home directory only for a leading ~.

* fix: clarify model authentication guidance

* fix: explain native provider OAuth requirements

* fix: keep authentication guidance method neutral

* fix: show credential reference source in model editor

* fix: use environment authentication in headless profile

* fix: validate headless authentication and current provider metadata

* test: retain authentication guidance coverage with account catalog updates

* test: retain writable Models credentials with the pinned environment
2026-10-13 10:10:06 +08:00
imccyu 5cf7ac9c44 Merge pull request #5897 from deepseek-harness/worktree/client-control/unified-remote-info
feat(typert): unify Remote definitions with lazy method info
2026-10-13 02:35:24 +08:00
imccyu b67cf4d381 test(web): verify plugin header wrapping by intrinsic fit 2026-10-13 01:44:18 +08:00
imccyu 29249a4a83 fix(typert): distribute indexed constraints across unions 2026-10-13 01:43:19 +08:00
imccyu 7ee6da0039 docs(typert): refresh strict input bilingual pairing 2026-10-13 00:58:01 +08:00
imccyu 2e56caee96 fix(typert): enforce strict named dictionary index constraints 2026-10-13 00:53:01 +08:00
imccyu 557e4137bf fix(typert): leave Node client bundling to operator layer 2026-10-13 00:53:01 +08:00
imccyu c36c077c5d docs: correct Gateway Config source reference after rebase 2026-10-13 00:53:00 +08:00
imccyu b769d1ce7a docs(typert): correct Remote metadata references 2026-10-13 00:53:00 +08:00
imccyu bd91a4a570 fix(typert): inject zod into remote metadata factories 2026-10-13 00:53:00 +08:00
imccyu e6278de7cc docs(typert): label the note's illustrative fences as text
The eight snippets in this note are before/after sketches: two import the
Remote source modules this change deletes, and the rest are fragments without
their surrounding context. They were never type-checked, yet each one counted
against the repository-wide opt-out ratio, which now sits above the 50% ceiling
that `doc-typecheck:contracts-ready` enforces.

Label them as text so the ceiling reflects blocks that could be checked.
2026-10-13 00:53:00 +08:00
imccyu d4d904bc4f fix(typert): resolve the file-upload types subpath in host analysis
The Typert host program analyzes packages/api/session-controller/src/commands.ts
on a clean checkout, where only tsconfig paths resolve
@deepseek-ai/dsh-client-file-upload/types; without the mapping the dependency
policy gate fails with TS2307.
2026-10-13 00:52:59 +08:00
imccyu 902df58ded refactor: scope this branch to generated Remote artifacts
The caller-connection work — forwarded-event opt-in, physical socket
ownership, and the Node/MCP artifact-only consumer fixture — moves to a
stacked PR on top of this one. This branch now changes only how Remote
definitions are generated (Typert artifacts, lazy method info,
consumer-selected input strictness, tool-schema projection) and how the
existing Registry consumes them.
2026-10-13 00:52:59 +08:00
imccyu ae8d417dcf refactor(typert): name the tool-schema projection for its owning standard 2026-10-13 00:52:59 +08:00
imccyu d5bf4b348a refactor(remote): drop unrelated churn from the metadata change 2026-10-13 00:52:58 +08:00
imccyu 570520862d refactor(typert): let consumers select input schema strictness 2026-10-13 00:52:58 +08:00
imccyu efdaa86fb8 feat(typert): unify Remote definitions with lazy method info 2026-10-13 00:52:58 +08:00
turtle1999 5a107fdef5 fix(plugin-manager): keep anchors and comment placement when pruning switches 2026-10-13 00:50:54 +08:00
fz cd5d5bc7bd test(web): await sidebar collapse before measuring plugin header 2026-10-13 00:50:08 +08:00
imccyu 27fd3c8b80 Merge pull request #6062 from deepseek-harness/feat/sidebar-session-watermark
feat(ui-workspace): fold sidebar sessions with a monotonic watermark
2026-10-13 00:45:17 +08:00
fz 0f0b30b3aa Merge remote-tracking branch 'origin/master' into feat/page-font-size-shortcuts 2026-10-13 00:44:27 +08:00
fz 0c6968b649 feat(web): adjust desktop font sizes together 2026-10-13 00:21:55 +08:00
imccyu d6e26edbec Merge pull request #6075 from deepseek-harness/worktree-messageindep
refactor(ui-chat): place Chat rows by group-tree position
2026-10-13 00:06:01 +08:00
Yifffan 6eaa595fdb Merge pull request #6073 from deepseek-harness/feat/adaptive-ui-and-skills
feat(skills): 新增自适应客户端规范(Skill),并适配插件页与自动化任务页页头
2026-10-12 23:52:49 +08:00
fz 8096e18504 feat(web): add desktop conversation font size shortcuts 2026-10-12 23:34:02 +08:00
imccyu fecf123858 docs(conversation): explain how a Chat Node chooses its group-tree position
Add a placement table to the Definition section: which `groupTree`
position a Chat Node declares for an opening human input, another Turn
input, a Turn notice outside folding, an independent folded row, and
ordinary process content, with links to the contract and the built-in
positions, and a pointer to `exclusive` claims.
2026-10-12 23:21:32 +08:00
imccyu 0b28bc80a7 fix(ui-chat): narrow input payloads before reading echo identities
`observedInputs` now reads every `input`-position Node, and a Node's
payload is not guaranteed to be an object. Check the payload and its
`source` before reading the composer `rpcId`, so an input Node without a
message payload no longer throws while pending echoes are matched.
2026-10-12 23:21:32 +08:00
imccyu 9a78e752b1 test(ui-chat): pin the input position order and a later-anchored Turn control
The position-order case includes `input`. A Turn without opening input
whose control anchors after a position-less row still renders the control
before the group that holds that row.
2026-10-12 23:21:32 +08:00
imccyu 8f5139b2c8 docs: complete the exclusive-claim contract and group-tree pointers
State in the `exclusive` JSDoc and the Conversation reference that the
claimed target's fallback does not run and that a fallback's own claim
has no effect. Reword the `input` position as the Turn's opening input
and separate the finalized-answer exemption from position-less
`assistant-step` rows. Point both package READMEs at the claim and the
group-tree positions.
2026-10-12 23:21:32 +08:00
imccyu c373b2958d test: cover exclusive matches and group-tree positions
Assembler cases cover a claim in either registration order and on live
append, other targets and target-less Definitions, the skipped fallback,
and both dispatch failures. Chat grouping cases split the step run by
position regardless of kind and regroup on a position change; the late
reply produces one Chat Node through a real assembler.
2026-10-12 23:21:32 +08:00
imccyu 2c16fb10e5 docs: record exclusive matches and migrated group-tree positions
Add the exclusive Conversation matches Agent Note; bring the group-tree
note up to the `input` position and the migrated kind lists, and the
timed-question note up to claim-based reply deduplication. Pair the
Chinese Conversation reference and Chat grouping README with the
English updates.
2026-10-12 23:21:31 +08:00
imccyu 5dbc298ed1 refactor(ui-chat): place Chat rows by group-tree position
Chat grouping, whole-Turn folding, and Turn-input detection read each
Node's declared `groupTree` position instead of the `INDEPENDENT` and
`TURN_PROCESS_INDEPENDENT_KINDS` kind lists and the user/steering/
turn-trigger checks. `ChatGroupTree` gains an `input` position between
`exchange` and `turn`; the built-in Definitions declare the positions
their kinds had, so built-in rows keep their placement.

The late question reply claims its message on the Chat target, so the
generic message Definition no longer projects a duplicate trigger and
grouping drops the message-id aggregation. Its placement is unchanged.
2026-10-12 23:21:31 +08:00
imccyu 533299e6ab feat(ui-conversation): let a Definition claim an event on its target
A match result may set `exclusive: true`. The assembler evaluates every
match of an event before accepting any, so a claim is independent of
registration order: other Definitions of the claimed target build no
Context for the event and the target's fallback does not run, while
Definitions of other targets and target-less Definitions still receive
it. Two claims on one target, or a claim without a target, throw.
2026-10-12 23:21:23 +08:00
Yif 9e62c1c229 fix(web): restore the plugin header subtitle row to its flex layout
The responsive change had moved `.pageIntro` off flex, which put the information
icon 1.06px off the subtitle's vertical centre and failed the existing geometry
assertion in `plugin-manager.e2e.ts`. Restore the flex row and the button's
`flex: none`, which keeps the 4px gap and the centring. The header still wraps
its action group onto another line, which is what this change is for.

Pin the wrap threshold in both header scenarios. Each records whether the action
group sits beside or below the heading, the plugin scenario asserts the wide and
narrow sides explicitly, and the plugin scenario launches its own scaffold so an
install task left by an earlier test cannot move the threshold between runs.
2026-10-12 23:15:20 +08:00
Yudong Han 4dff4b07f0 Merge pull request #6042 from deepseek-harness/fix/office-docx-check-scope
fix(office): 移除内置 checker 并限制 Word 无关检查
2026-10-12 22:50:11 +08:00
Yif 5a52b980e5 feat(skills): keep the mobile input keyboard closed by default
Add the eighth rule to the responsive client UI standard: on mobile, do not
raise the input keyboard automatically unless it is explicitly intended.
2026-10-12 22:33:05 +08:00
yudshj 232f86dadc docs(office): drop the checker upgrade guide
Bundled Office resource files are internal implementation, so removing
check_office.py breaks no supported surface.
2026-10-12 22:29:24 +08:00
turtle1999 5a7cb94149 fix(ui-workspace): address watermark review
- Compute each group's baseline watermark once per render.
- Skip the row-motion reset when folding drops no raised watermark.
- Document that the watermark is a row count that a reorder can outrun, and
  that any unmount of the grouped tree resets it; pin both in tests.
- Cover raise-to-current, keep-after-leaving, and fold reset in the keyless
  Web folding scenario.
2026-10-12 22:28:05 +08:00
Yif 89c4652add feat(web): reflow the automation tasks header at narrow widths
The page header keeps the title at its natural width and moves the New action
onto another line once the two no longer fit, so a narrow content column never
splits the title across lines.

Register the new browser scenario in both web programs: the host aggregate owns
files that boot the host spine, and the client-registered app project excludes
them.

The scenario asserts the title stays on one line, the action separates when the
column narrows, and focus survives the change.
2026-10-12 21:56:15 +08:00
Yif ee558c82d7 feat(web): reflow the Plugins page header at narrow widths
The page header wraps its action group onto another line when the title and the
actions no longer fit together, so the title keeps its width and the actions keep
their labels on one line. The information button follows the subtitle text
instead of a flex row, which keeps it beside the copy at every width.

Cover the header in the plugin-manager browser scenario: the action group stays
inline when it fits, moves below when it does not, keeps its labels on one line,
stays inside the header, and retains focus across the change.
2026-10-12 21:56:08 +08:00
Yif ebe725081c feat(skills): add the responsive client UI standard
Add `.agents/skills/dsh-responsive-ui`, the repository standard for adapting
client surfaces to narrow available widths: the 390 CSS px acceptance minimum,
the order in which space and information yield, text truncation, titles,
icon-only controls, mobile hover behaviour, mobile tooltips, and focus
retention across width changes.

Link it from `dsh-client-ui-ux` so the existing client UI/UX guidance routes to
the new standard.
2026-10-12 21:56:01 +08:00
Yifffan 9055e29891 Merge pull request #5172 from deepseek-harness/ui/plugin-placeholder-default-name
fix(ui): 修复会话操作间距、悬停交互与任务菜单层级
2026-10-12 21:38:13 +08:00
Yichen Jiang 3f14041b03 Merge pull request #6051 from deepseek-harness/worktree/pi-ai-1.1.0
feat(llm-pi-ai): adopt pi-ai 1.1.0 and Azure provider rename
2026-10-12 21:35:06 +08:00
Yichen Jiang f9febc7e7f test(web): wait for terminal burst delivery and rendering 2026-10-12 21:22:59 +08:00
turtle1999 c3b0492876 docs(module-graph): record the goal round driver's subagent dependency 2026-10-12 21:13:08 +08:00
turtle1999 d99d8afa43 fix(goal): start goal rounds only after progressing descendants settle
The subagent guidance tells the model to end its turn while it waits for
delegated results. With an armed goal, the round driver would then queue a
new round at every idle point, spending the round budget while the agent
only waits. The driver now joins the agent's progressing descendants
through the optional subagent service before it reserves a round, and stops
waiting when the plugin unloads.
2026-10-12 20:56:21 +08:00
Yichen Jiang 40f9bccd78 Merge remote-tracking branch 'origin/master' into worktree/pi-ai-1.1.0
# Conflicts:
#	apps/desktop/tests/prepare-runtime-patches.spec.ts
2026-10-12 20:56:10 +08:00
turtle1999 7df3c41ec9 feat(ui-workspace): fold sidebar sessions with a monotonic watermark
Each Workspace shows a contiguous prefix of its Sessions up to a watermark.
The watermark rises to cover running, waiting, unread-completion, and
current Sessions within the first 50 rows, and to a search reveal target at
any row. It never falls on its own; Show more raises it past 50, and Show
less or folding (including an ancestor) resets it.
2026-10-12 20:36:18 +08:00
winewill 3a7e896206 Merge pull request #6053 from deepseek-harness/build/macos-runner-service
build: macOS runner service and optional notarization
2026-10-12 19:42:46 +08:00
ZiyaZhang 5dec056c79 test(web): settle insertion before selecting composer text 2026-10-12 04:41:19 -07:00
turtle1999 5b4cb3dd4d fix(plugin-manager): drop profile switches the bundles already imply 2026-10-12 19:28:12 +08:00
ZiyaZhang 1d36cd5a3c test(web): wait for selection notification before blur 2026-10-12 04:26:22 -07:00
winewill 1379394224 fix: verify and number unnotarized macOS artifacts 2026-10-12 19:11:01 +08:00
Wenlu Wang 8c5e325f2d Merge pull request #5995 from deepseek-harness/perf/macos-dmg-lzma
perf(desktop): compress macOS DMGs with LZMA
2026-10-12 18:38:26 +08:00
creatixchu 6e40f80d05 test(web): await terminal output after burst delivery 2026-10-12 18:12:52 +08:00
Yichen Jiang 7b8eef3954 test(llm-pi-ai): avoid model-specific Azure assertions 2026-10-12 18:09:54 +08:00
turtle1999 7a881e944a fix(prompt): end the turn instead of sleeping on subagents and read line ranges with read
The subagent guidance now tells the model to end its turn when only waiting
for delegated results remains, since each result starts a new turn. The read
guidance names sed, head, and tail alongside cat and covers line ranges.

The compaction-output-reserve and compaction-summary-headroom context windows
grow by 40 tokens to keep their triggers between the history and its
condensed replacement.
2026-10-12 18:09:15 +08:00
kingwl 01283f1fdd perf(desktop): compress macOS DMGs with LZMA 2026-10-12 18:06:00 +08:00
ZiyaZhang 77483f61d9 fix(subagent): respect optional runtime context suppression 2026-10-12 03:04:08 -07:00
creatixchu a7f0247ada test: wait for native fragments and terminal burst delivery 2026-10-12 18:03:21 +08:00
winewill a98c6aae78 build: manage macOS runner service and optional notarization 2026-10-12 18:01:44 +08:00
lsdsjy 385f7b3140 test(web): await terminal input delivery before screen assertion 2026-10-12 18:00:53 +08:00
Yif a7aeae3e81 test(web): expect workspace tooltips to coexist with details 2026-10-12 18:00:43 +08:00
lsdsjy cdb779b70f Merge remote-tracking branch 'origin/master' into feat/web-account-login 2026-10-12 17:55:27 +08:00
lsdsjy d37fc91915 test(web): wait for account menu in collapse timing 2026-10-12 17:55:24 +08:00
yudshj f44cfa7813 fix(office): name PowerPoint and Excel scripts for their task
PowerPoint and Excel tasks in PTC mode wrote inspect.py, which shadows the
standard library and forces a delete-and-retry step. Apply the Word naming
rule to both skills, listing the libraries each one imports.
2026-10-12 17:51:44 +08:00
creatixchu 3a8df502e0 test(build): assert Windows glob omission without hiding partial builds 2026-10-12 17:50:31 +08:00
creatixchu 5831f5a3cf test(build): exercise workspace glob resolution through tsdown 2026-10-12 17:48:47 +08:00
creatixchu d629b0fd9f fix(build): normalize Windows workspace package globs 2026-10-12 17:46:09 +08:00
Yichen Jiang ab64b70d3f fix(llm-pi-ai): sync desktop patch policy and Azure snapshots 2026-10-12 17:46:06 +08:00
creatixchu 30b47e05f8 fix(cua): await fixture IPC closure and restore workspace seed 2026-10-12 17:44:14 +08:00
creatixchu 900a0b2df9 fix(cua): isolate native fixture services and repair platform CI checks 2026-10-12 17:39:24 +08:00
Yichen Jiang d79f13a4d4 feat(llm-pi-ai): adopt pi-ai 1.1.0 and Azure provider rename 2026-10-12 17:33:47 +08:00
yudshj fa740a581a fix(office): restore Word creation guidance and guard direct package writes
- Restore the office-docx new-document example from master. #6040 concerns
  edits, and no Word creation case covers its removal.
- Tell direct package writers to copy each member once into a new ZIP;
  python-docx, python-pptx, and openpyxl all reopen packages with duplicate
  members without error.
- Anchor the script-name assertion so hyphenated names such as edit-docx.py,
  which shadow no module, do not fail the Word e2e.
2026-10-12 17:31:53 +08:00
creatixchu 29893262a0 Merge master into CUA platform foundation 2026-10-12 17:26:05 +08:00
Yichen Jiang 3aa8872e67 Merge pull request #6027 from deepseek-harness/worktree/web-terminal-duplex
feat(web): carry terminal input over duplex streams
2026-10-12 17:13:57 +08:00
winewill 7ba14bcc72 Merge pull request #6037 from deepseek-harness/build/macos-self-hosted-packaging
build(macos): add self-hosted signed packaging workflow
2026-10-12 17:11:18 +08:00
lsdsjy 384d6d67a6 fix(web): 处理登录 PR 评审意见
- 未导航的空白登录页在 Host 完成后关闭
- 文档改为描述应用生效配色,并补充普通浏览器 device_info 与非回环来源限制
- 新增 Web e2e 覆盖账号菜单登录全流程
2026-10-12 16:55:30 +08:00
lsdsjy 8ee22c3668 feat(web): 在 Web UI 开放 DeepSeek 账号登录
撤销 f5e548b5af6 对 Web 的门控:账号菜单、设置中的账号页、首启登录步骤和额度提示在 Web 与 Desktop 上同样挂载,左下角菜单与 Desktop 一致(设置/意见反馈/登录)。

Web 以 login_source=web 与页面自身 origin 发起授权,点击时预开标签页并在拿到授权地址后带上主题跳转;失败、过期或取消时关闭该标签页,弹窗被拦截时仍可复制链接。Platform 页面、Desktop 首启引导和设备信息读取仍只在 Desktop 可用。
2026-10-12 16:54:49 +08:00
Yif 1f11e46e70 fix(ui): retain hover continuity through active cards 2026-10-12 16:43:40 +08:00
Yif 6b32843970 Merge master and repair SDK and job test integration 2026-10-12 16:41:44 +08:00
Dudu-0223 410244be7e Merge pull request #6035 from deepseek-harness/feat/team-interrupt-config
feat(agent-team): allow disabling the interrupt tool
2026-10-12 16:22:13 +08:00
Yichen Jiang 8e4fb1b18d Merge pull request #5120 from deepseek-harness/worktree/deepseek-account-models-ba9235
feat(llm): serve DeepSeek account models from the account listing with version gating
2026-10-12 16:20:43 +08:00
yudshj fb2a554cd3 refactor(office): remove the bundled structural checker 2026-10-12 16:07:23 +08:00
yudshj 2fe9783f55 fix(office): limit Word content validation to the edited content 2026-10-12 16:07:23 +08:00
Yichen Jiang 3d6e1b811b Merge remote-tracking branch 'origin/master' into worktree/web-terminal-duplex 2026-10-12 16:06:21 +08:00
Yichen Jiang 88788a5cb4 Revert "test(web): relax prose and JSON benchmark ratio limits"
This reverts commit 5de802071c9a2ecfcb009f4696ce8a87dc6fff79.
2026-10-12 16:06:16 +08:00
Yichen Jiang 759b9af6ff Merge remote-tracking branch 'origin/master' into worktree/deepseek-account-models-ba9235 2026-10-12 16:04:39 +08:00
Yichen Jiang 3a04f2d430 Merge pull request #6045 from deepseek-harness/fix/sdk-resume-jsonl-import
fix(sdk): import JSONL persistence as a plugin namespace in resume tests
2026-10-12 16:03:44 +08:00
turtle1999 5e9f1340d3 fix(cordis): align audit evidence and SDK test with current provider 2026-10-12 15:46:03 +08:00
Yichen Jiang 9ca2e48f9b test(web): relax prose and JSON benchmark ratio limits 2026-10-12 15:45:58 +08:00
Yichen Jiang 98229a47ea fix(sdk): import JSONL persistence as a plugin namespace in resume tests
#5938 turned @deepseek-ai/dsh-session-persistence-jsonl into a function
plugin without a default export, while the SDK resume spec from #5988
still default-imported it. Typecheck failed with TS1192 and the spec's
six cases mounted an undefined plugin, which kept the coverage, snapshot
and Windows build checks red for every pull request.
2026-10-12 15:44:38 +08:00
fz bed5d76406 Merge pull request #6036 from deepseek-harness/fix/plugin-manager-log-lifetime
fix(plugin-manager): close pnpm logs when preparation fails
2026-10-12 15:43:07 +08:00
turtle1999 288d1be7aa chore: merge master and retain Cordis cleanup guidance 2026-10-12 15:40:31 +08:00
turtle1999 59aec39e45 docs(cordis): address lifecycle audit review feedback 2026-10-12 15:33:39 +08:00
ZiyaZhang 2998eb0311 Merge master and fix depth-limit snapshot CI 2026-10-12 00:25:05 -07:00
Yichen Jiang 0bcacef96e docs: add upgrade guides for the account model catalog and pi-ai discovery
Users of v0.2.1-alpha.2 who saved an account catalog through the Models
page editor must move single-model changes into modelOverrides, because
a models field now stops llm-deepseek-account at load. pi-ai routes that
name a missing credential now list no models.
2026-10-12 14:53:34 +08:00
Yichen Jiang 9b77d375e6 docs(llm): state account override bounds, prerelease gating and catalog order
The account README now says that a prerelease orders below its release,
that listed capabilities do not bound an override, that an override for
a refused unknown model is checked once the model becomes available, and
that an account whose listed models are all refused shows the generic
empty state. resolveAdapterOptions documents that resolved models keep
the input order, which the account catalog relies on.
2026-10-12 14:53:30 +08:00
Yichen Jiang 62615b6aaf fix(llm): report a timed-out DeepSeek account listing as TIMEOUT
The listing deadline now comes from dsh-timeout and carries
MODEL_LISTING_TIMEOUT, so a listing that misses modelsTimeoutMs fails
with TIMEOUT and "timed out after <n> ms" instead of ABORTED. Rescope
and disposal aborts keep reporting ABORTED.
2026-10-12 14:53:22 +08:00
Yichen Jiang 54a7bdec23 fix(translator): read the account route's model overrides instead of its removed catalog
The account route no longer accepts a models catalog, so paid Flash
translation compares Account modelOverrides (Official keeps models) when
it checks that the owner's model configuration is unchanged before
dispatch. The paid fixture and the Web e2e serve the account GET /models
listing instead of configuring models on the account entry.
2026-10-12 14:53:15 +08:00
winewill de64badf0c Merge pull request #6039 from deepseek-harness/fix/windows-dialog-flicker
fix(desktop): prevent Windows dialog dismissal flicker
2026-10-12 14:21:25 +08:00
fz e75e13313c Merge pull request #5531 from deepseek-harness/fix/5520-job-session-timestamps
fix(jobs): persist owned lifecycle timing in sessions
2026-10-12 14:15:56 +08:00
Turtle f34e5cc59b Merge pull request #5938 from deepseek-harness/fix/persistence-disposal-order
fix(session): retain JSONL resources through provider-triggered teardown
2026-10-12 14:14:36 +08:00
Yif 1a882b5a78 fix(ui): share hover dwell across workspace and session rows 2026-10-12 14:11:45 +08:00
Yichen Jiang c319824f2b Merge origin/master into worktree/web-terminal-duplex 2026-10-12 14:07:25 +08:00
Yichen Jiang 5538b790c6 fix(web): bound terminal uplink and preserve final output 2026-10-12 13:54:11 +08:00
Yif d1e0b5e089 Merge master and simplify session hover interactions 2026-10-12 13:50:57 +08:00
winewill 4fe2381cb5 fix(desktop): release Windows modality before destroying dialogs 2026-10-12 13:46:22 +08:00
Yudong Han 278e479af8 Merge pull request #5054 from deepseek-harness/feat/office-render-tools
feat(office): split authoring and operation skills for progressive loading
2026-10-12 13:39:10 +08:00
winewill dd6a6f56b5 build(macos): persist CI signatures outside checkout 2026-10-12 13:35:45 +08:00
yudshj a9864e1aba test(office): normalize Desktop skill resource path expectation 2026-10-12 13:24:01 +08:00
Dudu-0223 8583f7f120 test(agent-team): keep interrupt regression coverage in unit tests 2026-10-12 13:16:48 +08:00
winewill ff3dfc9be6 build(macos): add self-hosted signed packaging workflow 2026-10-12 13:08:09 +08:00
yudshj efcc129820 Merge master and remove automatic nested skill injection 2026-10-12 13:07:43 +08:00
fz ae0da9482d test(jobs): record lifecycle in persistent PTC replay 2026-10-12 13:07:37 +08:00
fz 634d7e96dc fix(plugin-manager): close pnpm logs when preparation fails 2026-10-12 13:05:37 +08:00
fz 88a0be8486 Merge master after separating plugin-manager fix 2026-10-12 13:04:25 +08:00
Dudu-0223 10dfc10216 docs(agent-team): keep interrupt configuration documentation minimal 2026-10-12 12:59:17 +08:00
fz e51226401f refactor(jobs): split unrelated plugin-manager log fix 2026-10-12 12:57:13 +08:00
yudshj b8b254a6d3 test(office): refresh PTC headers from master 2026-10-12 12:55:32 +08:00
winewill a993a97c69 Merge pull request #6026 from deepseek-harness/docs/desktop-packaging-guides
docs(desktop): separate packaging signing and publishing guides
2026-10-12 12:54:55 +08:00
Dudu-0223 ecafa412ac feat(agent-team): allow disabling the interrupt tool 2026-10-12 12:53:44 +08:00
yudshj b6fc04f403 Merge latest master into Office progressive skills 2026-10-12 12:50:33 +08:00
yudshj bfcc6d50d0 refactor(office): remove command launchers and shell compatibility changes 2026-10-12 12:45:09 +08:00
Yichen Jiang c6f3639b5e docs(web): align terminal duplex documentation 2026-10-12 12:44:06 +08:00
LogosVeritas 444692d1ca feat(preset): add image reading and compaction to sdk-minimal
Reuse read_image and automatic compaction in the standalone profile without
adding SDK slash-command handling. Update profile guidance, upgrade steps,
and startup and recorded-session coverage.
2026-10-12 12:11:18 +08:00
winewill a0a3ce4f84 docs(desktop): fix migrated references and duplicate anchors 2026-10-12 12:06:58 +08:00
LogosVeritas 110ef57123 test(preset): cover manual compaction in minimal mode 2026-10-12 12:02:27 +08:00
LogosVeritas f15091a922 feat(preset): enable image reading in minimal mode 2026-10-12 12:02:27 +08:00
LogosVeritas 5b784ecbc3 feat(preset): enable automatic and manual compaction in minimal mode 2026-10-12 12:02:26 +08:00
yudshj 88f5ed353e refactor(office): keep skill API and carrier composition unchanged 2026-10-12 11:57:13 +08:00
Yichen Jiang 3a0f4976ad feat(llm): log DeepSeek account listing loads and failures to the console
Product profiles mount no console exporter for ctx.logger, so the
listing failure warning never surfaced. The account route now writes
each loaded listing, the first failure of a failing streak with the
catalog it falls back to, and hidden listing entries to the console
under the [llm-deepseek-account] tag, as the account Platform plugin
does. Hidden entries are logged once per listing on every path, not
only during model discovery.
2026-10-12 11:46:34 +08:00
winewill a73843f33c Merge remote-tracking branch 'origin/master' into docs/desktop-packaging-guides 2026-10-12 11:45:49 +08:00
Yichen Jiang 13751513c0 docs(web): remove internal terminal transport upgrade guide 2026-10-12 11:43:19 +08:00
Turtle e17e6f5c60 Merge pull request #5975 from deepseek-harness/turtle/remote-mux-heartbeat-1689ca09e33b
fix(api-gateway): count mux traffic as heartbeat liveness
2026-10-12 11:38:36 +08:00
Turtle ebae734b2b Merge pull request #5970 from deepseek-harness/turtle/image-tool-card-5f766ae62765
Show read_image path and size under the image
2026-10-12 11:33:50 +08:00
Yichen Jiang dd21f9d1b5 fix(llm): fix the account listing endpoint and name the current version
The account route always reads /models at the Messages origin; the
modelsURL option is removed so a configuration cannot point the listing
elsewhere. Model refusals now report "current version is <version>"
instead of "this build is", since users usually run a downloaded release
rather than a local build, and override diagnostics name the built-in
catalog instead of the build.
2026-10-12 11:32:37 +08:00
imccyu d0749e9f6b Merge pull request #6023 from deepseek-harness/worktree/local-gates
perf(gates): speed up local hooks, typecheck, documentation gates, and tests
2026-10-12 11:26:31 +08:00
Yichen Jiang 6cc19b22f6 Merge pull request #5460 from deepseek-harness/worktree/sleepy-grothendieck-64df7b
fix(session): reuse blank Sessions under the requested preset
2026-10-12 11:25:43 +08:00
Yichen Jiang 5379996fcb feat(web): carry terminal input over duplex streams 2026-10-12 11:18:43 +08:00
Yichen Jiang 10e87ed2d8 docs(session): drop the session.create blank reuse upgrade guide
The session.create change is not perceptible to users: its only caller is the
Web client that ships in the same release. Remove the guide and the
cross-reference added to the New Session upgrade guide.
2026-10-12 11:13:28 +08:00
Yichen Jiang 2a3d143eaa Merge remote-tracking branch 'origin/master' into worktree/sleepy-grothendieck-64df7b
# Conflicts:
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
2026-10-12 11:07:34 +08:00
winewill 28de983ef5 docs(desktop): separate packaging signing and publishing guides 2026-10-12 11:07:18 +08:00
imccyu c38a9e6111 fix(typert): classify build-output guard paths by native real path
The analyzer normalizes its root with fs.realpathSync, which keeps a
Windows 8.3 short name such as RUNNER~1 in the runner's TEMP, while
TypeScript reports module-resolution results through
fs.realpathSync.native, which expands it. The guard compared the two
spellings, found the resolved lib/ declaration outside the workspace,
and let a missing alias through on Windows. It now canonicalizes the
root and every file with realpathSync.native and reports paths spelled
under the caller's root; the guard specs also run with the workspace
root spelled through a directory link.

Refs #5852
2026-10-12 11:06:56 +08:00
imccyu ad43b1f7da docs: state that the documentation aggregate cap applies in CI
doc-sync, test:docs, hygiene, and check:all run at most 8 gates in CI as
well; the constant, the development guide, and the Agent Note now say so.
The Note also states that the source-only Typert guard rejects unresolved
workspace imports, so a missing alias fails before or after host-src.

Refs #5852
2026-10-12 11:06:56 +08:00
imccyu a99cf28864 test(docs): pin the repository-reference prefix search
uniquePrefixMatch is exported and covered for unique, shared, last,
exact, absent, and empty cases, and a fixture repository with two commits
whose names share a seven-digit prefix shows that the shared prefix is
accepted while a longer unique prefix is rejected.

Refs #5852
2026-10-12 11:06:56 +08:00
imccyu 5222ef2751 fix(typert): reject unresolved workspace imports in source-only generation
rejectBuildOutputs caught a missing paths alias only when the package's
lib/types already existed. On a tree whose packages are not yet compiled,
as when host-typert runs beside host-src on a clean checkout, the import
resolved to nothing and checkDiagnostics: false hid it. The guard now also
fails when a face program cannot resolve an import of a workspace package,
found through its node_modules link, and names the paths entry derived
from the package's exports types condition.

Refs #5852
2026-10-12 11:06:56 +08:00
imccyu 8a6c68fc34 docs: state review-requested invariants and current command names
gen-doc-graphs states that its probe memo patches the system and needs a synchronous, non-nested caller; the typecheck graph comments why host-check does not wait for host-typert; the API gateway guide names the CI build lanes as the bundling Typert path; and the build-split Note marks its host-libraries measurement as superseded by host-contracts.

Refs #5852
2026-10-12 11:06:56 +08:00
imccyu fbcb8b0306 docs(notes): record the local gate speed-ups
The new Agent Note records why type checks generate Typert declarations
without bundling, why pre-commit leaves third-party notices to the test
lane, how the local worker budget is chosen, and how the documentation
gates and type-model.spec avoid repeated work, with the measurements and
the evidence that outputs stay the same. Notes whose facts these changes
superseded now describe the source-only Typert step and the test-lane
ownership of notices freshness.

Refs #5852
2026-10-12 11:06:55 +08:00
imccyu 703c8f659c perf(docs): prefetch CLI help concurrently and memoize doc-graph probes
gen-cli-help launched its twelve source-mode dsh --help processes one
after another, each spending most of its time waiting on profile boot
I/O; they now start concurrently within the default worker budget, each
with its own temporary DSH_HOME, and the existing comparison consumes
them in the original order. gen-doc-graphs memoizes ts.sys file, directory,
realpath, and directory-listing probes for one run. Generated pages are
byte-identical; CLI help drops from about 52 s to 7 s and doc graphs from
about 47 s to 33 s.

Refs #5852
2026-10-12 11:06:54 +08:00
imccyu 67b875a5bf perf(docs): list each repository directory once per file walk
uniqueRepoFiles read every directory once per pattern and ** level and
resolved every file with realpath. One walk now lists each directory
once across patterns, resolves only paths reached through symbolic
links, and lets doc refs skip non-leading lib directories, whose files
it always excluded. markdown wrap, doc refs, and package paths return
the same file lists with about half the directory reads.

Refs #5852
2026-10-12 11:06:53 +08:00
imccyu 1e24617aad perf(docs): parse each Markdown document once in documentation gates
markdown links parsed anchor targets twice more and probed existence
without a cache; translation pairing reparsed every document after link
normalization and globbed four times; the README model-experience and
limitations checks parsed each README three times; type equivalence
parsed all 1,835 documents; Session format references reparsed each pair
four times. Each gate now parses a document once: links and anchors come
from one tree, pairing reuses the original tree when every rewritten
destination keeps the block structure and falls back to a full reparse
otherwise, the README checks share one tree, a text prefilter skips
documents that cannot hold a source-equivalence fence, and the
persistence renderer passes its trees on. Outputs are byte-identical on
the repository, and seeded faults are reported identically.

Refs #5852
2026-10-12 11:06:53 +08:00
imccyu 6fbfbb044e test(typert): run type-model.spec against an in-memory workspace
Each of the 61 cases copied the fixture onto disk and ran a full analysis
whose four TypeScript programs reloaded the DOM library, @types/node, and
zod v4; about 22,000 file-system probes per analysis dominated on a
network filesystem. The spec now mocks the node:fs calls the analyzer
makes and the ts.sys host for an in-memory workspace, stubs zod and
@types/node except in the Emitter case that runs real zod, drops the DOM
library from the fixture, and shares the pristine analysis read-only.
The file drops from about 316 s to 15 s with the same 61 cases and
assertions; the snapshot's external zod path no longer names a pnpm
store version.

Refs #5852
2026-10-12 11:06:52 +08:00
imccyu 989a52d427 build(hooks): stop regenerating third-party notices on pre-commit
Browser inputs make every package source file a generator input, so the
pre-commit job ran the 16-second generator on nearly every commit while
few commits change its output. gen-third-party-notices.spec.ts already
asserts the committed bytes in the test lane, including the required
node 24 / coverage pull-request lane, and names the regeneration command
when they are stale. The generated preamble, the notices decision, and
the development guide now describe that ownership.

Refs #5852
2026-10-12 11:06:51 +08:00
imccyu a8fd8da98a perf(docs): resolve repository-reference candidates with one object listing
verify-repository-references asked git cat-file for each of about 26,000
abbreviated hexadecimal candidates, which took 135 s on a network
filesystem. One --batch-all-objects listing (about 1.2 million objects,
0.6 s) now feeds an in-memory prefix search; a candidate still counts
only when its prefix names exactly one available object and that object
is a commit. The gate drops from 166.8 s to 15.7 s, and both
implementations classify 27,050 real and synthetic candidates
identically.

Refs #5852
2026-10-12 11:06:50 +08:00
imccyu ed7bf8cc42 perf(gates): cap local default worker counts
os.availableParallelism() reports host CPUs rather than a container's
CPU quota (256 against a 64-CPU quota here), so local defaults spawned
255 Vitest forks (55 GiB peak RSS) and oversubscribed every scheduler.
scripts/parallelism.ts keeps the CPU count in CI and caps other runs at
12, or 48 for Vitest forks and the local coverage budget; documentation
and hygiene aggregates run up to 8 gates instead of 4. Explicit
DSH_GATE_CONCURRENCY, DSH_OXLINT_THREADS, DSH_COVERAGE_MAX_WORKERS, and
--maxWorkers still win.

Refs #5852
2026-10-12 11:06:50 +08:00
imccyu 04af9a1df7 perf(build): generate Typert contracts without bundling for typecheck and lint
Type checks only need Host declarations and Typert-generated
declarations, but typecheck, lint, lint:fix, doc-typecheck, and the CI
typert-contracts gate rebuilt the whole Host tsdown pass, Desktop bundle
included, to obtain them. A source-only scripts/generate-typert.ts now
runs the same generator and artifact writer as the tsdown plugin, beside
Host package compilation: Typert reads only sources once the missing
@deepseek-ai/dsh-client-file-upload/types path alias exists, and
rejectBuildOutputs fails the pass when a workspace lib/ declaration
enters its program. typecheck runs as a build.ts scope that checks both
aggregates in build mode after their own face compiles.

Refs #5852
2026-10-12 11:06:49 +08:00
imccyu d8d942c82a Merge pull request #6020 from deepseek-harness/worktree-chatflow2
fix(ui-chat): keep notice-woken Turns in the human exchange
2026-10-12 11:00:20 +08:00
winewill b35ef84914 Merge pull request #5952 from deepseek-harness/build/windows-ci-presets-signing
build(windows): add machine presets and EV-signed Actions packaging
2026-10-12 10:48:21 +08:00
Yichen Jiang ad23ec9c3f Merge remote-tracking branch 'origin/master' into worktree/deepseek-account-models-ba9235
# Conflicts:
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	packages/client/ui-model-selection/README.i18n.yaml
#	packages/client/ui-model-selection/README.md
#	packages/client/ui-model-selection/README.zh.md
#	packages/client/ui-model-selection/src/client/ModelSelect.tsx
#	packages/client/ui-model-selection/src/client/locales.ts
#	packages/client/ui-model-selection/tests/model-select.client.spec.tsx
#	packages/client/ui-settings-models/README.i18n.yaml
#	packages/client/ui-settings-models/README.md
#	packages/client/ui-settings-models/README.zh.md
#	packages/client/ui-settings-models/src/client/ModelsSection.tsx
#	packages/client/ui-settings-models/src/client/ProviderEditor.tsx
#	packages/client/ui-settings-models/tests/components.client.spec.tsx
#	packages/llm/llm-deepseek-account/README.i18n.yaml
#	packages/llm/llm-deepseek-account/README.md
#	packages/llm/llm-deepseek-account/README.zh.md
#	packages/llm/llm-deepseek-api-key/README.i18n.yaml
#	packages/llm/llm-deepseek-api-key/README.md
#	packages/llm/llm-deepseek-api-key/README.zh.md
#	packages/llm/llm-deepseek-api-key/src/index.ts
#	packages/llm/llm-deepseek-api-key/tests/discovery.spec.ts
#	packages/llm/llm-deepseek/README.i18n.yaml
#	packages/llm/llm-deepseek/tests/account-routing.spec.ts
#	packages/llm/llm-deepseek/tests/runtime.spec.ts
#	pnpm-lock.yaml
2026-10-12 10:40:30 +08:00
imccyu 7526c914c4 test(scripts): refresh the translation prompt snapshot for the exchange term
The assembled translation prompt embeds docs/i18n/terminology.md, so the
new exchange row changes the recorded request.
2026-10-12 10:35:06 +08:00
imccyu 453a79206c fix(ui-chat): address review on notice-woken exchanges
Share locationCoordinates between the Chat Builder and GroupTreeSegments,
find the last opening Turn with a loop instead of spreading every key,
and pass the resolved Location through chatNode so message Nodes resolve
it once. The notice e2e now waits for observable barriers (each Step's
row, the notice Turn's start, the notice row) instead of fixed sleeps,
asserts the settled tail through polling plus a later-frame recheck
instead of one absolute blank threshold, and fails on console warnings.
Record exchange in the terminology table and keep Goal Round in English.
2026-10-12 10:35:05 +08:00
imccyu c62f26aaa1 fix(ui-chat): keep notice-woken Turns in the human exchange
Under "On next message" folding, a Turn woken by a notice (settled
subagent, goal round, schedule, background job) advanced the bottom Turn
and folded the finished Turn without an own-input driver, so the generic
fold room left a following reader looking at blank space.

Chat Nodes now declare a group-tree position (`groupTree`, ordered
`exchange` > `turn` > `process`). Human input claimed in Step 1 marks its
Turn as opening an exchange; notices and later steering carry no
position. `GroupTreeSegments` splits the loaded Turns at opening Turns,
and `bottomSource` covers the whole latest exchange, so notice-woken
Turns no longer fold earlier ones while the next human message folds the
exchange through the own-input path. Only `exchange` is read today; the
`turn` and `process` positions are reserved for the existing kind lists.
2026-10-12 10:35:05 +08:00
fz 77dca95039 Merge pull request #5988 from deepseek-harness/feat/sdk-resume-session
feat(sdk): add explicit persisted session resume
2026-10-12 10:34:04 +08:00
imccyu 743560522b Merge pull request #6013 from deepseek-harness/worktree-fixsystemprompt
fix(web): keep process-specific values out of the system prompt
2026-10-12 02:23:32 +08:00
imccyu 0f95e16315 Merge pull request #5824 from deepseek-harness/feat/user-text-secret-mask
feat(ui-secret-mask): mask credentials in sent user text
2026-10-12 02:18:12 +08:00
imccyu 94d4007bfd docs(ui-secret-mask): document exported format constants and methods 2026-10-12 02:05:16 +08:00
imccyu 1681948e3f refactor(ui-secret-mask): split formats, lazy tables, masks, and the scanner into modules 2026-10-12 01:58:06 +08:00
imccyu 30e31bd762 docs(ui-secret-mask): re-record README translation pairing 2026-10-12 01:43:59 +08:00
imccyu 2b229d4c0e fix(web): address review round on harness dev context
- WebServer.port reads number | undefined instead of masking the unbound
  listener with an assertion; tests that dial it narrow the bound port.
- LegacyPrompt stays module-private; the web-app type import is a
  devDependency only, since the emitted declarations never reference it.
- Copy: --public-url help names runtime context again, the public
  deployments guide, web-app JSDoc, Config.publicUrl consumers, the Agent
  Note matching rule, and three Known Limitations in the package README.
2026-10-12 01:42:45 +08:00
imccyu 035d78d4c8 fix(ui-secret-mask): confine key-block mask searches to the block 2026-10-12 01:24:38 +08:00
imccyu 993b13571b fix(ui-secret-mask): lint-clean indexed reads and add the English note 2026-10-12 01:13:11 +08:00
imccyu a93958c7d0 feat(ui-secret-mask): mask credentials in sent user text 2026-10-12 00:46:21 +08:00
imccyu ba1a9356db fix(web): advertise one Web GUI URL through the webApp service
The web-runtime row provides webApp.getAdvertisedUrl(): the row's
configured publicUrl, otherwise the listener origin. harness-dev-context
reads it instead of webStartup.publicUrl, so a profile override of
publicUrl reaches the model as it already reaches DSH_WEB_URL and the
startup URL. The new package no longer injects webServer or webStartup,
and the startup flag provider returns to its master behavior.
2026-10-12 00:34:29 +08:00
imccyu adfa92bca1 fix(web): address review on harness dev context
- WebServer exposes getOrigin() instead of an origin getter, and it fails
  loud before listen binds a port.
- The earlier Web-surface paragraph is a frozen literal, so later guidance
  edits cannot stop earlier Sessions from matching; the projection reads a
  prompt version with the loop's single-text-block rule.
- web-app drops its unused dsh-system-prompt dependency and stale
  prompt-section wording in its description, README, patch comment,
  --public-url help, and the round-trip test title.
2026-10-11 23:25:24 +08:00
imccyu b10a605e6b test(web): add harness dev context lines to recorded web sessions
Each recorded runtime-context snapshot with optional contexts gains the
tokenized checkout and Web GUI URL lines, matching the refresh output of
the code-language scenario, where only that message changed. The
minimal-preset snapshot suppresses optional context and stays unchanged.
2026-10-11 23:25:22 +08:00
imccyu f885016322 test(web): refresh code-language session for harness dev context 2026-10-11 23:25:22 +08:00
imccyu fc2ef643fb docs: regenerate package, module, and seam catalogs for harness-dev-context 2026-10-11 23:25:21 +08:00
imccyu ab560474a2 test(web): tokenize harness dev context lines in recorded sessions 2026-10-11 23:25:21 +08:00
imccyu d5b48ad0ba docs(graphs): classify the webStartup service 2026-10-11 23:25:20 +08:00
imccyu 36779dcf09 fix(web): keep process-specific values out of the system prompt
The harness checkout path and Web GUI URL now reach the model as runtime
context from the new dsh-harness-dev-context plugin, so a process started
on another port, checkout, or installation no longer rewrites a Session's
logged system prompt. A Session whose prompt already embeds the earlier
value-bearing paragraphs keeps them verbatim through a host-only session
projection.

dsh-host-webserver gains WebServer.origin and bindUrlHost(); web-app keeps
only DSH_WEB_URL, the startup URL, and the browser handoff, and its root
declares the webStartup service type. app-boot drops
addHarnessSourceSection and HARNESS_SOURCE_SECTION.
2026-10-11 23:25:19 +08:00
creatixchu f7d8ac7140 Merge current master build updates into CUA platform foundation 2026-10-11 23:17:19 +08:00
imccyu 9cac4beb05 Merge pull request #6005 from deepseek-harness/worktree-tsprofile
perf(build): parallelize build, split build:artifacts/build:check, and cut tsdown and client check time
2026-10-11 23:03:37 +08:00
imccyu f6b0c5af59 build: rename build:src and build:test to build:artifacts and build:check
build:artifacts produces every artifact, including the tsdown bundles,
the Desktop bundle, and Web, so naming it after sources undersold it;
build:check checks scripts, benchmarks, and website code besides tests.
The build.ts scopes, aggregate step ids, the Python builder flag
(--build-artifacts), workflows, callers, docs, and the build-split Agent
Note follow the new names. The note also records the naming
alternatives.

Refs #5852
2026-10-11 22:16:47 +08:00
imccyu 1a8448ad27 docs: say which build command to run when, and drop the upgrade-guide aside
- docs/development.md gains a situation-to-command table for build:src,
  build:test, build, typecheck, lint and doc-typecheck (Chinese pair
  updated).
- Note A states the typertPlugin() compatibility facts without discussing
  an upgrade guide.

Refs #5852
2026-10-11 19:43:49 +08:00
ZiyaZhang 42db4b8541 fix(subagent): expose exhausted delegation depth in runtime context 2026-10-11 04:37:57 -07:00
creatixchu d0c550823b docs(cua): align platform references and placeholder structure 2026-10-11 19:24:51 +08:00
creatixchu ef3375780b fix(build): select package manifests for workspace bundles 2026-10-11 19:21:07 +08:00
creatixchu f65cb889b8 refactor(cua): separate shared execution from macOS provider 2026-10-11 19:18:02 +08:00
imccyu f0460bab4c docs(notes): record final build measurements and address review wording
- Note B: final medians of two interleaved samples (build 317.1 -> 169.1 s,
  build:src developer view -51.6%, lint prerequisite 159.8 -> 90.9 s), the
  CI lanes that run the parallel build, and their concurrency settings.
- Both notes: medians with sample counts instead of raw sample lists.
- Note A: why the typertPlugin() hook changes need no upgrade guide; final
  serial stage times.
- Typert Remote note: the top-level build now runs the scripts/build.ts
  step graph; build:lib and build:lib:host keep their remaining callers.
- docs/development.md agreement fix; packages/client/AGENTS.md rule 8 names
  the manifest globs the gate checks.

Refs #5852
2026-10-11 19:16:57 +08:00
imccyu 1e6deb4f16 test(typert): place the worker fixture on a real package leaf
verify-package-paths reads root-relative packages/* strings in tests and
rejected the fixture's packages/generator, which names the real
packages/typert/generator leaf in the wrong group. The fixture now mirrors
that layout.

Refs #5852
2026-10-11 18:50:25 +08:00
imccyu f34c3a1ce2 docs(notes): record the lossless build speed-ups and the build command split
- 2026-10-11-lossless-build-speedups: the React type packages declared only
  at the root, the five Typert discovery/worker changes with the transform
  filter, and the client tsdown preset manifest read; each item states its
  purpose, change, measured effect, why outputs are unchanged, and its
  trade-offs.
- 2026-10-11-build-src-test-split: build:src / build:test / build, the
  dependency graph and scheduling, callers and CI lanes, which command
  detects which error, measurements, and the intentional trade-offs.
- 2026-10-05-pr-artifact-typechecks: one-line adjustments where this change
  replaces --artifacts-only with build:src and renames the Python flag.

Refs #5852
2026-10-11 18:19:23 +08:00
imccyu 129c915498 build: split build:src and build:test, and schedule build in parallel
pnpm run build ran every step in one serial chain, including the two
noEmit aggregate programs (tests, scripts, benchmarks, website) that no
artifact step consumes, so the Host aggregate check alone delayed tsdown by
about 66 s.

- build:src (scripts/build.ts --scope src) builds every artifact in the
  existing order and writes the client build record; it replaces the
  removed --artifacts-only flag and the libraries CLI mode.
- build:test (--scope test) type-checks only the Host and Client aggregate
  programs with tsc -p, concurrently, without compiling references or
  checking prerequisites.
- build runs both as one dependency graph through run-gates' new
  runAggregate: each aggregate check starts once its face's packages are
  compiled, and Client compilation stays after the Host bundle that writes
  the Typert /remote declarations. DSH_GATE_CONCURRENCY=1 serializes it;
  DSH_GATE_FAIL_FAST=1 stops the remaining steps at the first failure.
  Passing steps print one PASS line (DSH_GATE_VERBOSE=1 shows their output).
- PR artifact builds (e2e, release, preview, Node 22 compatibility) and the
  Python builder (--build-src) use build:src; dev:web and dev:desktop use
  build:src. test:*, bench, typecheck and the required consumer/Windows
  lanes keep the complete build.
- lint, lint:fix, doc-typecheck and the Typert contracts gate prepare with
  host-libraries (Host package compilation + Host bundle) instead of
  build:lib:host; their own checks never read the aggregate root or the
  Desktop bundle.

Parallel and serial build produce byte-identical lib/, Web dist and client
build record; a test-only type error fails build and build:test while
build:src passes.

Refs #5852
2026-10-11 18:19:23 +08:00
imccyu e4410b2764 perf(client): read each tsdown preset's own package manifest
The client tsdown presets found a package's own manifest by name: a glob of
every packages/*/*/package.json, parsed until the name matched. tsdown's
native config loader imports each package config without module cache, so
the module-level cache never hit and all 82 configs repeated the glob
(40-125 ms each) synchronously on tsdown's main thread, where it delayed
every other package build. It was 34.5% of the client pass's main thread.

- clientBundle, staticLinked and clientLibrary take the package directory
  (import.meta.dirname at every call site) and read <dir>/package.json
  once; the package name, loader id and externals come from that manifest.
- A missing, unreadable or nameless manifest throws an actionable error.
- The glob, the name search and the module-level caches are removed.

tsdown client pass 12.78 s -> 8.73 s (median of 3 interleaved runs each,
-31.7%); all 11,566 lib outputs byte-identical; all 82 configs resolve the
same externals and banners.

Refs #5852
2026-10-11 18:19:23 +08:00
imccyu 1c6078dca6 perf(build): declare React type packages only at the workspace root
TypeScript builds a synthetic signature for every JSX intrinsic element and
computes a module specifier for @types/react (index and jsx-runtime) per
importing file. computeModuleSpecifiers visits every symlinked location of
that file and scans all 491 tsconfig paths for each; pnpm gave 65 packages
their own node_modules/@types/react link. Client test-aggregate Check
followed 26.9 + 0.494 * N seconds for N locations.

- Remove @types/react and @types/react-dom from all 67 workspace manifests
  and declare them once in the root devDependencies; packages keep declaring
  the react and react-dom they import (ui-slots gains react).
- verify-package-dependencies rejects either package in any workspace
  manifest and requires the root declaration, so a new package cannot add
  the link back.
- packages/client/AGENTS.md states the rule.

Client aggregate Check 59.1 s -> 26.5 s; the 76 client-only package
projects 54-57 s -> ~44 s wall. File set (2,613) and diagnostics unchanged.

Refs #5852
2026-10-11 18:19:21 +08:00
imccyu c1a4ce0117 perf(typert): discover declared contributors only and generate off the bundler thread
The Host tsdown pass ran Typert synchronously inside the first package's
writeBundle, blocking every other workspace build's JavaScript hooks for
about 12 s. Discovery dominated that time: it expanded the Host aggregate's
2,064 include entries, parsed all 362 referenced tsconfigs (Client ones
included), and walked 322 packages although only 28 declare a Typert export.

- Select packages by their manifest exports before any source walk.
- Parse face aggregates without expanding their file lists.
- Parse package tsconfigs lazily, only for analyzed packages; tsc -b keeps
  validating every referenced tsconfig.
- Resolve the workspace root as the nearest ancestor holding both
  tsconfig.host.json and packages/.
- Run workspace generation in a Worker Thread started at the first
  buildStart; writeBundle awaits it once and fails the build with
  TypertWorkerError on any worker failure.
- Filter the decorator transform hook by id so only TypeScript modules
  cross into JavaScript.

tsdown host median 22.6 s -> ~10 s; all 743 hashed lib outputs are
byte-identical except the generator's own bundle.

Refs #5852
2026-10-11 18:16:20 +08:00
creatixchu e9bc3bdb37 fix(cua): align package version with master 2026-10-11 18:00:39 +08:00
creatixchu 5ea72ef0d2 fix(cua): satisfy typed lint and expose fixture failures 2026-10-11 17:58:53 +08:00
creatixchu b9de05d1c4 test(cua): include experimental package in release inventory 2026-10-11 17:55:48 +08:00
creatixchu 1b781d683b Merge master into feat/experimental-dsh-cua 2026-10-11 17:54:24 +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
imccyu 9cb2041b29 Merge pull request #5979 from deepseek-harness/messageslot
refactor(web): compose message and text-transform content
2026-10-11 15:23:08 +08:00
Yichen Jiang 8df7465cf8 Merge pull request #6002 from deepseek-harness/worktree/fix-chat-fold-growth-timing
test(web): control frames in concurrent fold scenarios
2026-10-11 15:03:23 +08:00
Yichen Jiang 38a3e05380 Merge pull request #6001 from deepseek-harness/worktree/python-release-github-large-trial
ci(python): use larger GitHub macOS runners for releases
2026-10-11 14:51:47 +08:00
Yichen Jiang 1e7ca3ebe9 test(web): control frames in concurrent fold scenarios 2026-10-11 14:50:49 +08:00
imccyu 60a4c8e077 fix(web): make whitespace trimming linear and polish content contracts 2026-10-11 14:18:10 +08:00
Yichen Jiang 1d0ede8c3a ci: trial larger GitHub macOS runners for Python releases 2026-10-11 14:00:17 +08:00
imccyu f66d19b078 refactor(web): reconcile user text with cached forward candidates 2026-10-11 12:52:06 +08:00
imccyu ed629da5da fix(web): trim unused user text features and cover rendering 2026-10-11 02:03:27 +08:00
imccyu a46d5b143f refactor(web): separate image slots by domain 2026-10-11 01:04:23 +08:00
CreatixChu 983f2a0116 Merge pull request #5035 from deepseek-harness/feat/persistent-ptc
feat(ptc): 支持遵循沙箱策略的 persistent-in-memory 执行
2026-10-10 22:52:46 +08:00
imccyu 14bee91fac test(web): refresh complete queued question reply snapshot 2026-10-10 22:47:34 +08:00
imccyu ba452cf919 docs: mark message image factory decision implemented 2026-10-10 22:43:55 +08:00
imccyu d6a5aa5cab fix(web): address user text CI checks and coverage 2026-10-10 22:43:55 +08:00
imccyu 40c6a37ad0 test(web): adapt user content fixtures to text node factories 2026-10-10 22:43:55 +08:00
imccyu bd1b8ad18e fix(test): route user text specs to the client compiler 2026-10-10 22:43:54 +08:00
imccyu 1301b26846 refactor(web): transform user text through business node slots 2026-10-10 22:43:54 +08:00
imccyu b52864330d refactor(web): extract reusable user content factory 2026-10-10 22:43:54 +08:00
imccyu 2c684c51c9 refactor(web): compose native message image factories 2026-10-10 22:43:53 +08:00
imccyu 3daec753f8 Merge pull request #5983 from deepseek-harness/worktree-chatmisc
fix(chat): hide empty reasoning and improve command and file details
2026-10-10 22:39:53 +08:00
Tianyi Cui dd15a5d066 test(ptc): cover provider unload during policy cleanup
Hold cleanup of an older retained process while a newer call is waiting, then unload the provider. Require the pending call to reject after disposal without starting another process.

Validation: 305 focused tests passed, one skipped; persistent.ts, index.ts, and policy-processes.ts have 100% statements, branches, functions, and lines. Removing the shutdown guard makes the new regression fail.
2026-10-10 22:39:34 +08:00
imccyu 68be430f62 fix(chat): handle cancelled folds and concurrent content growth 2026-10-10 22:22:45 +08:00
Tianyi Cui f62483f71f Merge pull request #5990 from deepseek-harness/turtle/worktree-lazy-fetch-d2236b7eb1d9
fix(worktree): disable lazy fetch through GIT_NO_LAZY_FETCH
2026-10-10 22:19:37 +08:00
Tianyi Cui 9d385cd0d9 Merge master into persistent PTC
Preserve the persistent runtime package description with master's alpha.2 version and locale exports. Refresh the two PTC prompt snapshots for the current plan-tool result type.

Validation: full build, 434 focused tests (one skipped), two built-runtime tests, two selected snapshots, package metadata, and all 43 doc-sync checks passed.
2026-10-10 21:55:44 +08:00
Turtleandoraluben 60808a9e99 Credit review by @oraluben
Co-authored-by: oraluben <[email protected]>
2026-10-10 17:20:38 +08:00
imccyu b3a7271720 fix(chat): coordinate visible process folding with input scrolling 2026-10-10 17:15:00 +08:00
imccyu 9f435e1592 fix(chat): show background shell launch commands 2026-10-10 16:05:27 +08:00
imccyu 0d52c0d6a2 fix(chat): align wrapped status and refine preview regression cases 2026-10-10 16:05:27 +08:00
imccyu ed160fa60c fix(chat): hide empty reasoning and improve command and file details 2026-10-10 16:05:26 +08:00
imccyu d6be036d67 Merge pull request #5982 from deepseek-harness/worktree-spacebrowser
feat(ui-workspace): expose session ids and unify toast presentation
2026-10-10 15:25:23 +08:00
fz b1d5671038 refactor(sdk): simplify resume acknowledgement and verify Python restoration 2026-10-10 15:21:40 +08:00
imccyu d0c885d7b7 fix(ui-workspace): share archive toast filter action 2026-10-10 15:13:22 +08:00
imccyu 0dcebe4419 fix(ui-workspace): declare toast registrations explicitly 2026-10-10 14:59:26 +08:00
imccyu 0c72c1cd3e docs(client): allow factories for reusable components 2026-10-10 14:59:25 +08:00
imccyu d5f19c7978 test(ui-chat): narrow restored configuration before loading 2026-10-10 14:50:17 +08:00
imccyu 70050ff276 docs(workspace): sync missing-directory membership reference 2026-10-10 14:50:16 +08:00
Turtle b37bd1f2a1 docs(worktree): scope lazy-fetch guarantee and regenerate config catalog 2026-10-10 14:31:58 +08:00
imccyu 61112eab8f fix(ui-chat): persist work details collapse timing 2026-10-10 14:22:02 +08:00
Turtle b0414b8a98 fix(worktree): disable lazy fetch through GIT_NO_LAZY_FETCH
--no-lazy-fetch exists only in Git 2.45+, so older Git exited 129 before
creating a worktree. The environment variable is also honored by the
2.39.4, 2.40.2, 2.41.1, 2.42.2, 2.43.4, and 2.44.1 maintenance releases.
2026-10-10 14:17:21 +08:00
imccyu 7d14714601 fix(workspace): retain session membership for missing directories 2026-10-10 14:16:42 +08:00
07akioni db0210088b Merge pull request #5570 from deepseek-harness/perf/trim-desktop-unused-assets
perf(desktop): trim Domino tooling and legacy KaTeX font formats
2026-10-10 13:49:38 +08:00
kingwl ea6140cee0 Merge master and resolve integration conflicts for #5570 2026-10-10 13:26:45 +08:00
07akioni 4840909ed8 Merge pull request #5528 from deepseek-harness/refactor/python-runtime-aliases
fix: preserve bundled Python interpreter aliases
2026-10-10 13:23:45 +08:00
fz 4f2fc8935a test(sdk): align replay initialization with request budget 2026-10-10 13:17:43 +08:00
imccyu b2dd2bf75b fix(ui-workspace): copy session id directly from menu 2026-10-10 13:13:47 +08:00
kingwl 2da2a27163 Merge master and resolve integration conflicts for #5528 2026-10-10 13:12:10 +08:00
fz 0bec9479ae feat(sdk): add explicit persisted session resume 2026-10-10 13:08:00 +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
07akioni 56bdf15b4f Merge pull request #5685 from deepseek-harness/fix/composer-concentric-corners
fix(web): align statistics, card, and menu radii
2026-10-10 12:57:18 +08:00
imccyu 668b7f9746 fix(ui-workspace): show full session id as selectable text 2026-10-10 11:22:21 +08:00
imccyu f409d99b0c docs(ui-workspace): refresh inline session id pairing 2026-10-10 03:39:51 +08:00
imccyu 3cae1619a5 fix(ui-workspace): place session id copy action inline 2026-10-10 03:33:29 +08:00
imccyu 76d086754a fix(ui-workspace): update toast coverage and generated expectations 2026-10-10 03:11:57 +08:00
imccyu c3ce020b12 refactor(ui-workspace): render notices through keyed toast factory 2026-10-10 02:35:58 +08:00
ZiyaandZiyaZhang c7d61ecc11 fix(i18n): 补充内置插件中文文案并精简清单展示 (#5506)
* fix(i18n): add Chinese names and descriptions for built-in plugins

* fix(i18n): keep built-in plugin localization metadata-only

---------

Co-authored-by: ZiyaZhang <[email protected]>
2026-10-09 17:18:26 +00:00
imccyu 8423a388ec fix(ui-workspace): unify shortcut fork toast notifications 2026-10-10 00:59:01 +08:00
imccyu 38c700f08e feat(ui-workspace): view and copy session id from menu 2026-10-10 00:29:01 +08:00
yudshj 839033f993 fix(office): align runtime smoke and cross-platform coverage 2026-10-10 00:01:52 +08:00
yudshj f81494ce83 Merge remote-tracking branch 'origin/master' into codex/pr5054-refactor 2026-10-09 23:56:18 +08:00
yudshj 1f01ad6410 refactor(office): simplify test setup and verify progressive workflows 2026-10-09 23:38:09 +08:00
Tianyi Cui 926a2c1610 Merge pull request #5958 from deepseek-harness/architecture-review-docs
docs: give architecture review rules a per-artifact home
2026-10-09 23:24:34 +08:00
yudshj 310596f4aa refactor(office): trim unused skill metadata and launcher input forwarding 2026-10-09 23:05:26 +08:00
imccyu 1a83d013f5 docs: raise the root AGENTS.md ceiling for the standing orders
The root file passes the 1,960-word ceiling on the branch alone but exceeds it
once master's newest standing order is merged in, which is what `node 24 /
static` reports. Raise the ceiling to 2,080 and update the documented target in
the same change, per the "a too-low ceiling is a budget bug" rule.
2026-10-09 23:04:58 +08:00
imccyu 32aba5ea37 docs: address review comments on the rules directory
- package-surface: split the two plugin forms (a function plugin named-exports
  the Loader four, a service plugin default-exports its class), keep Host
  Service Definition exports and the Client `/client` export discipline, replace
  the wrong example that quoted live production code, state that the static
  client libraries and `packages/util/*` are closed to new members, and drop
  census numbers that rot on the next package.
- client-components: use the real Slot API — the inject face with the reserved
  `hooks` compartment, the parent's derived `renderSlot`, `registerFactory`
  scope declarations with local components at `renderFactorySlot` — drop the
  string seat, pass the injected `useResource` explicitly with a protocol key,
  and describe persistence as the spec's framework-run `persist` key.
- conversation-and-session: rewrite the reasoning example on the real
  `conversation.chat.reasoning.body` seat and the published `reasoning.content`
  Factory, and drop status annotations and future-tense spec-speak.
- remote-and-api: scope the JSON requirement to the wire payload so
  `@Remote({ mode: 'stream' })`, lookup arguments, and a trailing `AbortSignal`
  stay valid; drop a leaked Chinese parenthetical from the English page.
- general: measure through `benchmarks/` (`test:bench`, `check:ci:bench`,
  `test:bench:memory`) instead of an invented command, state the baseline
  without presuming a hook, and drop a leaked exception line.
- api-gateway: link the Remote and API rules instead of restating them.
- development.zh and AGENTS: drop an orphan anchor and widen the standing
  order's trigger to the directory's scope.

The earlier "retired routing skill" claim was wrong and is dropped: that skill
was never committed, so the diff deletes nothing under `.agents/skills/`.
2026-10-09 23:03:57 +08:00
imccyu 73dbbb751c docs: give architecture review rules a per-artifact home
Move `docs/defensive-patterns.md` into `docs/defensive-patterns/`, one page per
artifact plus a README router, and state each rule as its requirement, the
condition a reviewer judges it by, its boundary and approver, and a correct and
a wrong example.

- `general.md` (the moved page): the existing bug-class rules plus timing,
  value representation, shared-state ownership, decentralization, performance
  evidence, and migrations.
- `package-surface.md`, `remote-and-api.md`, `client-components.md`,
  `conversation-and-session.md`: the review requirements for those artifacts,
  linking type definitions and semantics to `docs/subsystems/` instead of
  restating them.
- `README.md`: the habits behind the rules and the route from artifact to page.
- `dsh-code-review` reads the owning pages before judging a diff; the
  `dsh-architecture-discipline` routing skill is retired in favour of the
  directory, and every inbound reference is repaired.
- Chinese counterparts, pairing records, and the documentation tier table are
  updated together.
2026-10-09 23:03:56 +08:00
kingwl 51ca4f0787 Merge master and refresh Chat README pairing 2026-10-09 19:51:29 +08:00
winewill 5feeb15c81 docs(desktop): document accepted packaging runner risks 2026-10-09 19:34:26 +08:00
Yichen Yan 23c29d067a fix(web): persist authenticated browser preferences on the Host (#4201)
* Persist authenticated browser preferences through the Host
* test(settings): cover recovery and remote preference refresh
* test(settings): read Host preferences from a fresh browser context
* test(settings): verify remote ConfigForm refresh at plugin entry
2026-10-09 19:27:00 +08:00
Turtle e0b1ba7412 fix(api-gateway): count only queued downlink writes as liveness
A write that completes at once only reached the local send buffer, so
counting it delayed dead-peer detection until that buffer filled. Only a
write that waited in the full buffer now resets the count, and the
connection owns all three activity signals.
2026-10-09 19:24:14 +08:00
yudshj 908763515e refactor(office): run Python checker directly 2026-10-09 19:20:22 +08:00
winewill 34c034e03b fix(desktop): keep preset cache maintenance independent of signing secrets 2026-10-09 19:17:41 +08:00
yudshj 379f9cd47b feat(office): add progressive skills and direct dsoffice commands 2026-10-09 19:14:55 +08:00
Turtle 898ed6d192 fix(api-gateway): count mux traffic as heartbeat liveness
A congested remote.mux link can hold the Pong behind data frames, so the
Host terminated sockets that were still moving data and the Client then
reconnected and resent. Any received frame or completed downlink write now
resets the missed-heartbeat count.
2026-10-09 19:08:58 +08:00
winewill c7921dbf0f fix(windows): address preset preparation and review findings 2026-10-09 18:42:22 +08:00
kingwl a0d31a9f27 fix(web): restore master composer geometry 2026-10-09 18:38:13 +08:00
Yifffan fddd704c7f Merge pull request #5969 from deepseek-harness/design/official-plugin-icons-safe-area
style(web): unify official plugin artwork and add drawing skill
2026-10-09 18:10:26 +08:00
07akioni 2dd43a3ef0 Merge pull request #5565 from deepseek-harness/fix/prune-desktop-shell-build-files
desktop: prune shell source maps and build caches
2026-10-09 18:07:28 +08:00
TurtleandYifffan 7add03d235 Credit review by @Yifffan
Co-authored-by: Yifffan <[email protected]>
2026-10-09 18:01:44 +08:00
Yif 71947ec3fa fix: address artwork review and separate UX reference coverage 2026-10-09 17:58:18 +08:00
07akioni 27667dcf33 Merge master and refresh desktop README pairing for #5565 2026-10-09 17:57:26 +08:00
07akioni deb90c38e6 Merge pull request #5576 from deepseek-harness/fix/desktop-dependency-alignment
perf(deps): align desktop dependencies and share client libraries
2026-10-09 17:52:53 +08:00
07akioni 50cec2dcbf Merge pull request #5559 from deepseek-harness/fix/prune-non-windows-conpty
Trim Windows ConPTY sources from non-Windows Desktop packages
2026-10-09 17:50:31 +08:00
07akioni 614d7d7ef3 Merge pull request #5563 from deepseek-harness/perf/onboarding-lossless-webp
perf(client): encode onboarding illustrations as lossless WebP
2026-10-09 17:49:04 +08:00
Turtle 00c482e271 Show downscale facts and localize the read_image size line
Name the original size when the store downscaled the image, keep repeated
whitespace in the path, localize the Chinese size line, and gate the card
on the envelope separately from collecting extra text.
2026-10-09 17:39:28 +08:00
Turtle 4a9979cea0 Show read_image path and size under the image
The expanded read_image row no longer repeats the file name above the
image or prints the raw <path>/<type>/<content> envelope below it. It now
shows the full recorded path and each image's WxHpx and byte size in the
small code font; text appended by post-execute hooks stays visible.
2026-10-09 17:26:04 +08:00
Yif 64d56a968c fix(plugin): order badge package resources for workspace constraints 2026-10-09 17:24:39 +08:00
Yif 6c9f26ec48 style(web): unify official plugin artwork and add drawing skill 2026-10-09 17:00:38 +08:00
Tianyi Cui b9863a9e28 fix(ptc): cancel pending runs when Session policy changes
Register calls before awaiting prior process cleanup. Carry policy cancellation into both Node execution modes and retain each admission until its call settles. Track persistent calls during provider disposal as well.

Cover unchanged-mode temporary escalation and policy changes that return to the original mode while cleanup is pending. Document the admission guarantee.

Validation: 308 focused tests passed, one skipped; both changed source files have 100% coverage. Two built-runtime smokes, two selected Session snapshots, all 43 doc-sync gates, and typed lint passed.
2026-10-09 16:43:56 +08:00
kingwl 9c8d8efd22 Merge origin/master into fix/composer-concentric-corners 2026-10-09 16:30:47 +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
kingwl dd87b0f82b fix(web): preserve composer spacing and compact statistics corners 2026-10-09 16:23:06 +08:00
creatixchu aa5da68de6 docs(cua): describe the future package group without a stale path 2026-10-09 16:19:54 +08:00
kingwl 226ae8d2fd Merge master and resolve integration conflicts for #5576 2026-10-09 16:14:10 +08:00
creatixchu 97b884e841 docs(cua): state the Windows promotion condition 2026-10-09 16:12:01 +08:00
Tianyi Cui 70d443903c test(ptc): reproduce policy changes before process launch
Add a parameterized admission regression for per-call and persistent-in-memory execution. Change the Session policy synchronously after submitting the runtime call and require cancellation before spawn.

The two new cases intentionally fail on the reviewed PR #5035 head: execution succeeds with danger-full-access after the Session becomes read-only. The 47 existing host-protocol and policy-process tests pass. No production changes.
2026-10-09 16:10:40 +08:00
creatixchu 6dba7799b4 docs(cua): keep provider references in experimental group 2026-10-09 16:09:36 +08:00
fz 61fa9c0bab docs(jobs): correct job wait migration guidance 2026-10-09 16:01:29 +08:00
winewill 5801fae7dc fix(windows): recreate empty runner checkout before actions start 2026-10-09 16:01:20 +08:00
winewill 370a272bb1 fix(windows): reset runner checkout without traversing dependency junctions 2026-10-09 15:57:20 +08:00
creatixchu 2fac8a2ec4 fix(cua): follow updated PTC launch configuration 2026-10-09 15:49:16 +08:00
creatixchu c36a40656b refactor(cua): move native provider into experimental 2026-10-09 15:46:42 +08:00
creatixchu 17bfa6fd3c Merge updated persistent PTC into native CUA 2026-10-09 15:43:43 +08:00
winewill 8ac0f9b7f5 fix(windows): keep local preset auto versions independent of upload settings 2026-10-09 15:26:28 +08:00
creatixchu 6ae7312582 test(permissions): restore synchronous preset event order 2026-10-09 15:25:51 +08:00
Dudu-0223 4cf1a3a2ca Merge pull request #5923 from deepseek-harness/subagent-preset-composition
refactor(subagent): compose new children through presets
2026-10-09 15:24:36 +08:00
winewill 373aec3d30 build(windows): add machine presets and signed CI packaging 2026-10-09 15:18:56 +08:00
creatixchu b02eff33f2 chore(types): remove retired sandbox test assertion baseline 2026-10-09 15:14:56 +08:00
turtle1999 dfca3c3c6f fix(session): use namespace exports for JSONL persistence plugin 2026-10-09 15:10:49 +08:00
creatixchu 99f5750092 docs(ptc): use explicit launch configuration in persistent setup 2026-10-09 15:10:40 +08:00
creatixchu 42cf8c5ee0 Merge remote-tracking branch 'origin/master' into codex/ptc-review-policy
# Conflicts:
#	apps/desktop/tests/ptc-runtime.spec.ts
#	docs/config-catalog.md
#	docs/config-catalog.zh.md
#	packages/ptc-runtime/ptc-runtime-node/README.i18n.yaml
#	packages/ptc-runtime/ptc-runtime-node/README.md
#	packages/ptc-runtime/ptc-runtime-node/README.zh.md
#	packages/ptc-runtime/ptc-runtime-node/src/index.ts
#	packages/ptc-runtime/ptc-runtime-node/src/launch.ts
#	packages/ptc-runtime/ptc-runtime-node/tests/launch.spec.ts
2026-10-09 15:10:13 +08:00
creatixchu f6251a2dbf test(ptc): remove awaits from synchronous permission callers 2026-10-09 15:08:06 +08:00
creatixchu 9b9630e6d8 fix(ptc): preserve standalone runtime composition 2026-10-09 15:07:42 +08:00
creatixchu f660432b37 test(ptc): record synchronous policy changes before cleanup 2026-10-09 15:06:25 +08:00
creatixchu 31783b2845 test(ptc): align policy cleanup fixtures with typed APIs 2026-10-09 15:04:17 +08:00
creatixchu 4138fd1912 fix(ptc): simplify registered cleanup settlement 2026-10-09 15:02:58 +08:00
creatixchu 244ca73755 fix(ptc): keep policy cleanup waits inside PTC 2026-10-09 15:02:01 +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
turtle1999 f6eb0c39f0 docs(review): invoke Cordis audit during code review 2026-10-09 14:42:07 +08:00
Dudu-0223 540607cf2a test(web): register fork reminder snapshot in host compiler 2026-10-09 14:35:18 +08:00
Dudu-0223 194b985f13 test(subagent): reuse Web preset pins for fork reminders 2026-10-09 14:34:00 +08:00
creatixchu b17c74a0e7 test(cua): refresh capture recovery session replay 2026-10-09 14:31:41 +08:00
creatixchu 7a015d6562 test(cua): record request-local capture busy recovery 2026-10-09 14:29:00 +08:00
fz 3d89cded59 refactor(jobs): remove launcher shutdown registry 2026-10-09 14:21:29 +08:00
turtle1999 9eda3a064a docs(cordis): explain yielded disposal workaround 2026-10-09 14:15:37 +08:00
turtle1999 22d0bd19a3 fix(session): preserve JSONL persistence default plugin import 2026-10-09 14:10:52 +08:00
Dudu-0223 256b8f828d test(subagent): cover inherited schedule tools and address review 2026-10-09 14:05:07 +08:00
creatixchu 1ad84ece93 fix(cua): bound socket backpressure and allow AX cancellation to settle 2026-10-09 14:05:03 +08:00
creatixchu 258a0f65e7 fix(cua): finish shared socket cleanup without waiting for timers 2026-10-09 13:57:05 +08:00
turtle1999 4d9047be2d fix(session): retain JSONL resources through provider-triggered teardown 2026-10-09 13:46:43 +08:00
turtle1999 2075717e2f docs(cordis): audit cleanup dependency lifetimes as P0 2026-10-09 13:42:52 +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
creatixchu 6539baeda5 test(cua): cover shared capture cancellation and connection cleanup 2026-10-09 13:35:49 +08:00
creatixchu 1e67f8da60 feat(cua): share native helper across Agent connections 2026-10-09 13:29:45 +08:00
Dudu-0223 17016e16ee test(subagent): share parent schemas in snapshot header 2026-10-09 12:12:34 +08:00
Dudu-0223 648ab5167e refactor(subagent): compose new children through presets 2026-10-09 12:12:02 +08:00
creatixchu a08b54b818 test(cua): pin screenshot failure diagnostics in replay 2026-10-09 11:43:44 +08:00
creatixchu 705a96eefe feat(cua): trace screenshot phases and retain failure diagnostics 2026-10-09 11:40:45 +08:00
Yichen Jiang d6a699591b Merge remote-tracking branch 'origin/master' into worktree/sleepy-grothendieck-64df7b
# Conflicts:
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	packages/api/session-controller/README.i18n.yaml
#	packages/extensions/cordis-client-runner/tests/api-catalog.client.spec.ts
2026-10-09 11:38:51 +08:00
Yichen Jiang f7661e8a05 docs(session): record arrival order, logged restore presets, and idempotent select
Update the blank reuse Agent Note and the writer acquisition note for the
review fixes: arrival-order claims with directory checks before preset
resolution, restoration from the logged preset, latest-connection coalescing,
and the registry select that leaves an already composed blank unchanged.
2026-10-09 11:29:32 +08:00
Yichen Jiang 5087220bea fix(preset): keep a blank Session that already runs the selected preset
The Creator entry names cordis in its session.create request, and its chip may
still select cordis when the Session list updates before the new projection
arrives. select recomposed and logged unconditionally, so that race appended a
second agent-preset/selected record and announced tools/change again. select
now leaves a blank Session that records the preset and runs its current
revision unchanged; a replaced definition still recomposes and logs. The
Creator Web scenario pins a single selection record.
2026-10-09 11:29:26 +08:00
Yichen Jiang 3c1e357132 fix(web): join only the latest Workspace connection and restore logged presets
Keep one Workspace navigation options type: StartSessionOptions now carries
agentPreset, and openWorkspace and connectWorkspace take its preset field.
A connection joins the latest in-flight connection to its Workspace only when
both name the same preset, so a superseding request always reaches the Host,
and the map is keyed by the branded WorkspaceId instead of a composite
string. The fresh New Session path no longer accepts a preset it would drop.
Restoring a saved blank reloads that Session's projections first, because
listed values are cached hints that can lag the log after an unclean exit.
The New Session upgrade guide points to the session.create reuse guide.
2026-10-09 11:29:17 +08:00
Yichen Jiang 1b8a55116e fix(session): claim named Sessions in arrival order before resolving presets
session.create resolved the requested preset before queueing on the named
identity, so a later request whose preset resolved first could run first, and
an undeclared preset masked the directory conflict the README promises.
Queue first, check the named Session's directory and ownership, then resolve
the preset inside the queue, and report the preset each request left the
Session running instead of reading it after the queue moved on. The no-roster
conflict message now names its actual cause.
2026-10-09 11:29:04 +08:00
Dudu-0223 a9afe76200 feat(subagent): select and restore presets for spawned agents 2026-10-09 00:13:17 +08:00
creatixchu a61dccbd36 fix(cua): align package version with synchronized workspace 2026-10-08 22:15:20 +08:00
creatixchu 51e563d502 test(cua): refresh replay after parent synchronization 2026-10-08 21:55:43 +08:00
creatixchu e74ba9b712 fix(cua): adapt replay fixtures to session working directories 2026-10-08 21:53:13 +08:00
creatixchu 4431c95da1 Merge feat/persistent-ptc into feat/experimental-dsh-cua 2026-10-08 21:49:23 +08:00
creatixchu 6344383228 test(ptc): refresh web permission and tool instruction snapshots 2026-10-08 21:28:28 +08:00
creatixchu 433742a828 test(workflow): cover unconfined execution under full access 2026-10-08 21:15:05 +08:00
creatixchu fdb60edc33 test(workflow): type the evaluated guest completion value 2026-10-08 21:12:33 +08:00
creatixchu c2434286d1 test(workflow): provide the guest import loader in REPL coverage 2026-10-08 21:11:03 +08:00
creatixchu 834a77108d test(ptc): cover preset contention and REPL workflow expressions 2026-10-08 21:09:46 +08:00
creatixchu af930836ba test(ptc): refresh mode-switch snapshot tool descriptions 2026-10-08 21:05:40 +08:00
fz e493fd40dc fix(jobs): use short UUIDs for visible job ids 2026-10-08 21:04:58 +08:00
creatixchu b7dbd7bb03 Merge master into feat/persistent-ptc 2026-10-08 20:58:22 +08:00
fz 7af8741513 Merge origin/master into fix/5520-job-session-timestamps 2026-10-08 20:02:49 +08:00
Yichen Jiang 64c47c6306 test(client): update the inspect catalog's uiWorkspace signatures
openWorkspace and connectWorkspace accept NewSessionOptions, so the Client
Cordis inspect catalog spec pinned outdated signatures and failed both
coverage lanes. Declare openWorkspace on one line so the generated catalog
signature carries no collapsed line breaks, regenerate the catalog, and pin
the new signatures.
2026-10-08 18:52:32 +08:00
creatixchu f8f0abda80 fix(sandbox): await execution effect disposal 2026-10-08 17:57:07 +08:00
creatixchu 046d722c14 fix(ptc): preserve caller context across process control callbacks 2026-10-08 17:45:18 +08:00
Yichen Jiang d7f96a2731 docs(session): add the session.create blank reuse upgrade guide
session.create now reuses a named Session only while it is a reusable blank,
runs the requested or default preset, and creates a new Session otherwise.
Record the migration for JSON-RPC clients and Client plugins that name a
sessionId, including the removed session/writer-held result and the
existingPreset detail.
2026-10-08 17:43:02 +08:00
Yichen Jiang 5a461467db Merge remote-tracking branch 'origin/master' into worktree/sleepy-grothendieck-64df7b
# 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
2026-10-08 17:39:59 +08:00
creatixchu 87013959f8 test(ptc): pin cancellation replay and resource cleanup branches 2026-10-08 17:25:50 +08:00
creatixchu ee15e104d2 test(ptc): verify cleanup recovery and scoped execution admission 2026-10-08 17:10:55 +08:00
fz 4837be0d55 test(jobs): refresh team inbox lifecycle snapshots 2026-10-08 17:06:54 +08:00
creatixchu 209684aaae fix(ptc): retain per-call cleanup until process exit is observed 2026-10-08 17:05:07 +08:00
fz 5e6b0a5c53 Merge origin/master into fix/5520-job-session-timestamps 2026-10-08 16:56:04 +08:00
creatixchu 49f7919762 test(ptc): cover policy changes during nested tool execution 2026-10-08 16:55:32 +08:00
fz 932a19b0f9 test(web): use returned ids for background job scenarios 2026-10-08 16:44:25 +08:00
creatixchu 6885d662d2 refactor(ptc): move mode-change coordination into sandbox policy 2026-10-08 16:43:00 +08:00
fz 6ce8654ff6 fix(terminal): retain background job ids under small result caps 2026-10-08 16:30:21 +08:00
fz 54c13688a8 fix(jobs): use one durable job id and ordered shutdown 2026-10-08 16:17:52 +08:00
kingwl f02b10d90e Merge master into fix/composer-concentric-corners
Reconcile README translation records and refresh the moved schedule test reference and validation command.
2026-10-08 15:45:33 +08:00
creatixchu 614f16560f docs(ptc): refresh permission lifecycle dependency graph 2026-10-08 15:18:14 +08:00
creatixchu e9a0e41aa0 test(ptc): cover sandbox launch failures and refresh retained execution snapshot 2026-10-08 15:09:25 +08:00
creatixchu 80b42178c5 fix(ptc): keep admission closed through owner disposal 2026-10-08 15:05:04 +08:00
Yichen Jiang 4ed24b4861 test(web): follow fresh New Session and Creator creation requests
Master now creates a fresh Session for a plain New Session, and the Creator
entry names its preset in the session.create request instead of a separate
agentPresets.select call. Wait for that request in the Creator scenario and
count the startup blank in the outdated-cache scenario.
2026-10-08 15:04:47 +08:00
creatixchu 6a6105dc1b fix(ptc): preserve tool execution identity during cancellation 2026-10-08 14:59:28 +08:00
kingwl 0bfc4b97f2 Merge master and resolve integration conflicts for #5576 2026-10-08 14:57:40 +08:00
creatixchu e3a92c9de5 fix(ptc): confine persistent memory and isolate temporary approvals 2026-10-08 14:57:36 +08:00
Yichen Jiang d0eb70425e Merge remote-tracking branch 'origin/master' into worktree/sleepy-grothendieck-64df7b
# Conflicts:
#	.agents/notes/implemented/architecture/2026-09-17-process-local-blank-sessions.i18n.yaml
#	.agents/notes/implemented/architecture/2026-09-18-declarative-agent-presets.i18n.yaml
#	.agents/notes/implemented/architecture/2026-09-18-declarative-agent-presets.md
#	.agents/notes/implemented/architecture/2026-09-18-declarative-agent-presets.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
#	packages/api/session-controller/README.i18n.yaml
#	packages/client/ui-agent-preset/src/client/index.ts
#	packages/client/ui-agent-preset/tests/apply.client.spec.ts
#	packages/client/ui-workspace/README.i18n.yaml
#	packages/client/ui-workspace/README.md
#	packages/client/ui-workspace/README.zh.md
#	packages/client/ui-workspace/src/client/index.ts
#	packages/client/ui-workspace/src/client/navigation.ts
#	packages/extensions/cordis-client-runner/src/client/api-catalog.ts
2026-10-08 14:55:44 +08:00
kingwl f94df9baae Merge master and resolve integration conflicts for #5559 2026-10-08 14:55:10 +08:00
kingwl fec46cd89e Merge master and resolve integration conflicts for #5570 2026-10-08 14:54:56 +08:00
creatixchu 801861b95e Merge latest master benchmark report updates 2026-10-08 14:29:46 +08:00
creatixchu c658cba99a fix(ptc): align working-directory setup and snapshots after master merge 2026-10-08 14:26:08 +08:00
creatixchu bc2bd61f6f Merge master into feat/persistent-ptc 2026-10-08 14:17:51 +08:00
fz 2be7329142 fix(jobs): preserve lifecycle order and headless settlement 2026-10-08 12:47:48 +08:00
Turtle 2fdb9870df fix(ui-chat): explain the interrupted stop in localized copy without the code chip 2026-10-08 12:11:48 +08:00
kingwl 594d89973f Merge master and resolve integration conflicts for #5565 2026-10-08 11:47:35 +08:00
kingwl 5ebd7ac8d6 Merge master and resolve integration conflicts for #5528 2026-10-08 11:43:58 +08:00
kingwl 2e29d08896 Merge master and resolve integration conflicts for #5563 2026-10-08 11:42:51 +08:00
Turtle 830e298a04 fix(ui-chat): summarize interrupted turns as stopped and pin the notice in a web snapshot 2026-10-08 11:37:51 +08:00
fz 12dee990e1 test(web): refresh job lifecycle replay fixtures after master sync 2026-10-08 11:36:04 +08:00
fz 36e2402364 test(jobs): refresh PowerShell lifecycle snapshot after master sync 2026-10-08 11:17:31 +08:00
Turtle 3121a4b5da fix(ui-chat): show a stopped notice for turns interrupted by a DSH stop 2026-10-08 11:14:35 +08:00
fz 1dd1098cdf Merge latest master font settings after job timing validation 2026-10-08 11:03:39 +08:00
fz 672b384eea Merge master into job lifecycle timing and refresh owned fixtures 2026-10-08 11:02:16 +08:00
kingwl 401b810d5a fix(web): align task and attachment card styling 2026-10-05 00:14:25 +08:00
kingwl 5c5555eec1 docs(web): link radius rules directly and tidy curve comment 2026-10-04 23:42:50 +08:00
kingwl 3a9f0f1aab fix(web): unify conversation radii and nested menu insets 2026-10-04 23:36:21 +08:00
kingwl 169e0c3881 fix(web): reconcile composer geometry with radius standard 2026-10-04 22:52:19 +08:00
kingwl 28c57a001e fix(web): align composer corner with send button 2026-10-04 21:41:34 +08:00
kingwl 48584b0060 Merge master and fix Windows HMR watch inventory assertions 2026-10-01 13:56:00 +08:00
kingwl ca31126b20 docs(ci): reconcile coverage coordinator description with master 2026-10-01 11:16:38 +08:00
kingwl f99bafc91a fix(ci): verify complete coverage partition inventories 2026-10-01 11:14:35 +08:00
kingwl be7d58308c fix(hmr): exclude unrelated files from config watchers 2026-10-01 11:09:13 +08:00
kingwl f78b104b20 fix(deps): retain Worker-compatible Zod and align dependency checks 2026-10-01 03:19:25 +08:00
kingwl cbaefed057 fix(python): preserve SDK runtime aliases during resource sync 2026-10-01 03:02:51 +08:00
kingwl cb0cfa815f fix(web): validate KaTeX build helper and reuse browser scaffold 2026-10-01 03:02:11 +08:00
kingwl 18a8242003 docs(desktop): scope bundled ConPTY files to Windows 2026-10-01 02:58:59 +08:00
kingwl 874c399b8e chore(web): reconcile WebP documentation with master 2026-10-01 02:58:49 +08:00
kingwl bb03ef5000 fix(desktop): retain shell exclusions in update qualification builds 2026-10-01 02:53:40 +08:00
kingwl 64c5255843 perf(deps): align desktop dependencies and share client libraries 2026-10-01 01:17:17 +08:00
kingwl cc11f9145c fix(desktop): limit dependency alignment to Koffi pin 2026-09-30 23:11:09 +08:00
kingwl 8d4503f3f0 fix(desktop): preserve and verify production dependency resolutions 2026-09-30 22:15:39 +08:00
kingwl 0dac34c338 fix(desktop): pin and validate Koffi 3.1.1 2026-09-30 21:57:11 +08:00
kingwl a863b7a989 perf(desktop): trim Domino tooling and legacy KaTeX font formats 2026-09-30 21:11:26 +08:00
kingwl 019b5312b9 fix(desktop): prune shell source maps and build caches 2026-09-30 20:45:31 +08:00
kingwl d5b6cd29bb perf(client): encode onboarding illustrations as lossless WebP 2026-09-30 20:08:02 +08:00
kingwl db1d900f6d fix: omit ConPTY sources from non-Windows Desktop packages 2026-09-30 19:51:06 +08:00
fz 95fec5694e test(inspector): honor lane deadlines and settle disconnected calls 2026-09-30 17:36:33 +08:00
fz 6900e62db0 fix(plugin-manager): dispose pnpm logs on preparation failures 2026-09-30 16:58:30 +08:00
fz 4f3b86983d fix(ci): run Windows coverage with the Realm cleanup fix 2026-09-30 16:22:26 +08:00
fz 42629929ce test(web): refresh job lifecycle replay fixtures 2026-09-30 15:31:25 +08:00
fz fb578d0510 test(jobs): record PowerShell lifecycle snapshot 2026-09-30 15:25:24 +08:00
fz f899e87a1e fix(jobs): persist owned job lifecycle timing in sessions 2026-09-30 15:25:24 +08:00
lsdsjy 9e1df327ae fix(account): 延后赠金通知并隔离身份补齐通知 2026-09-30 14:54:05 +08:00
kingwl 3c95edd468 test: cover Python runtime aliases on Windows hosts 2026-09-30 14:43:28 +08:00
kingwl 099db91169 fix: preserve bundled Python interpreter aliases 2026-09-30 14:27:13 +08:00
creatixchu d94e64949d test(cua): isolate restoration from custom accessibility roots 2026-09-30 13:30:48 +08:00
creatixchu c2c5a96fde test(cua): use an empty AppKit collection option set 2026-09-30 13:28:59 +08:00
creatixchu 1038ecb92b test(cua): observe settled AppKit visibility during restoration 2026-09-30 13:28:09 +08:00
creatixchu 3865067590 test(cua): establish foreground state explicitly in restoration checks 2026-09-30 13:26:12 +08:00
creatixchu 9b1494d8d2 test(cua): await foreground fixture readiness 2026-09-30 13:24:16 +08:00
creatixchu c58e90ee1e test(cua): launch restoration fixtures through macOS application services 2026-09-30 13:23:45 +08:00
creatixchu 086e1b5c6c fix(cua): restore application visibility through accessibility 2026-09-30 13:20:02 +08:00
creatixchu 5d8a19cb0d test(cua): record window restoration and use bundled native fixtures 2026-09-30 13:17:43 +08:00
creatixchu a83cebc4cf feat(cua): restore windows without activation and reject stale coordinates 2026-09-30 13:13:57 +08:00
creatixchu 4e5234ac9c fix(cua): 保留相同文字的值语义并压缩结构容器 2026-09-30 12:04:50 +08:00
creatixchu 6ff1825139 test(cua): 固定精简和展开 AX 的会话输出 2026-09-30 11:59:48 +08:00
creatixchu 7257181b9e feat(cua): 优化 AX 信息披露与按需展开 2026-09-30 11:55:50 +08:00
creatixchu 4f6f39ea2e Merge remote-tracking branch 'origin/feat/persistent-ptc' into feat/experimental-dsh-cua 2026-09-29 20:13:38 +08:00
creatixchu c72df24922 test(session-cache): await archived fixture checkpoint durability 2026-09-29 20:12:23 +08:00
creatixchu b8c1e6094a Merge remote-tracking branch 'origin/feat/persistent-ptc' into feat/experimental-dsh-cua 2026-09-29 19:51:14 +08:00
creatixchu c7d1a0e499 test(cua): refresh native scenario for current tool guidance 2026-09-29 19:50:25 +08:00
creatixchu 137d35a600 test(ptc): refresh persistent prompt for current tool guidance 2026-09-29 19:50:24 +08:00
creatixchu 0b6c5bb7b8 merge: update native CUA to the current release base 2026-09-29 19:47:33 +08:00
creatixchu fcecd8e37b merge: update persistent PTC to the current release base 2026-09-29 19:45:45 +08:00
creatixchu 6fcf790ccb test(cua): admit compiler outputs backed by either buffer storage 2026-09-29 19:30:06 +08:00
creatixchu a6b5a222a0 fix(cua): retain immutable native executables for source runtimes 2026-09-29 19:28:38 +08:00
creatixchu e3ab7232ef fix(cua): annotate the post-approval lifetime recheck 2026-09-29 19:05:40 +08:00
creatixchu 1d40e9bf31 fix(cua): keep async lifetime check and type native test requests 2026-09-29 19:05:24 +08:00
creatixchu e87b4298fd docs(cua): document native launch and rich input behavior 2026-09-29 19:01:34 +08:00
creatixchu b36fe768bb test(cua): refresh capability snapshots and wait for window creation 2026-09-29 19:00:24 +08:00
creatixchu 29e70c8ab2 fix(cua): type protocol generator and await application launch readiness 2026-09-29 18:59:05 +08:00
creatixchu eae305af6e refactor(cua): derive native protocol tables and isolate AX formatting 2026-09-29 18:52:37 +08:00
creatixchu e12fe33aaa fix(cua): own clipboard transactions on the AppKit main actor 2026-09-29 18:35:13 +08:00
creatixchu e312d6fdf6 fix(cua): retain paste data until the clipboard consumer requests it 2026-09-29 18:27:19 +08:00
creatixchu 9a562aa6ca fix(cua): distinguish disabled paste commands and verify fixture focus 2026-09-29 18:16:58 +08:00
creatixchu 13a41cd8ed test(cua): verify native editing and physical input in AppKit 2026-09-29 18:12:24 +08:00
creatixchu 0946619d1e fix(cua): validate installed bundle signatures before launch 2026-09-29 18:05:18 +08:00
creatixchu c520c10a84 feat(cua): discover and launch applications after consent 2026-09-29 17:59:46 +08:00
creatixchu 9fbdf7e72b fix(cua): use the existing atomic-write package for paste locking 2026-09-29 17:46:17 +08:00
creatixchu 4e7a987acd docs(cua): describe clipboard paste and preservation limits 2026-09-29 17:40:28 +08:00
creatixchu d35070bf4b feat(cua): paste text and HTML through native clipboard commands 2026-09-29 17:39:34 +08:00
creatixchu d2e8a5be3a feat(cua): select literal text with verified AX ranges 2026-09-29 17:31:14 +08:00
creatixchu db59be0b6a feat(cua): support keypad and modified middle-button input 2026-09-29 17:23:15 +08:00
creatixchu b6242fe01c test(cua): refresh app-first window guidance snapshot 2026-09-29 15:02:46 +08:00
creatixchu 9c33b13e5d fix(cua): prefer direct app operations for single-window tasks 2026-09-29 15:01:27 +08:00
Yichen Jiang 32c47d6eae Merge remote-tracking branch 'origin/master' into worktree/sleepy-grothendieck-64df7b 2026-09-29 14:52:58 +08:00
Yichen Jiang f17741c756 fix(session): reuse blank Sessions under the requested preset
session.create treats a named sessionId as a blank Session to reuse while
the Host can verify it is still reusable: no turn has started, it belongs to
the requested directory, it is an unarchived member of the requested
Workspace, and no other owner writes it. The reused Session runs the
requested agentPreset or the configured default; its recorded preset is not
resolved, so a blank recorded under a preset that is no longer declared no
longer breaks New Session or startup restoration. A change is logged as
agent-preset/selected before the Agent is published. A candidate that is no
longer reusable stays unchanged and the Host creates a new Session, which
replaces the client's writer-held fallback.

The Web client restores a saved draft with its recorded preset and falls back
to the default when that preset is missing or fails to activate, passes the
creator entry's preset through creation instead of issuing a concurrent
select, and reloads the projections of a declined candidate so a stale blank
hint no longer hides a started conversation.

Related to #2566
2026-09-29 14:52:11 +08:00
creatixchu 22a5a41d0c docs(cua): remove the deferred bundle from the base module graph 2026-09-28 17:49:45 +08:00
creatixchu ecd2f24da0 test(cua): cover saved grants and denied handles on every platform 2026-09-28 17:45:14 +08:00
creatixchu 1b892c355c Merge remote-tracking branch 'origin/feat/persistent-ptc' into feat/experimental-dsh-cua 2026-09-28 17:15:40 +08:00
creatixchu 7690c3a0fe docs(cua): refresh the catalog for the manual provider layer 2026-09-28 17:07:39 +08:00
creatixchu 3793f16f3a docs(cua): document the application consent owner lifecycle 2026-09-28 17:06:10 +08:00
creatixchu 40047a4577 test(cua): cover consent independently of permission-page controls 2026-09-28 17:02:57 +08:00
creatixchu 15332e42e6 refactor(cua): keep manual provider and application consent in base layer 2026-09-28 16:53:29 +08:00
creatixchu eab69c7008 test(ptc): cover Python execution instructions 2026-09-28 16:35:59 +08:00
creatixchu 219d664ad5 test(ptc): wait for concurrent releases of the same owner 2026-09-28 16:27:46 +08:00
creatixchu bcd1e66141 fix(ptc): reserve owners through explicit environment release 2026-09-28 16:27:15 +08:00
creatixchu 6fe4bf04fc fix(ptc): retain idle process cleanup until it settles 2026-09-28 16:24:05 +08:00
creatixchu 8b5986fdb9 Merge remote-tracking branch 'origin/feat/persistent-ptc' into feat/experimental-dsh-cua 2026-09-28 16:19:52 +08:00
creatixchu 3f1c666d03 docs(ptc): keep Python model guidance in its README contract 2026-09-28 16:19:03 +08:00
creatixchu ba1ba99157 Merge remote-tracking branch 'origin/feat/persistent-ptc' into feat/experimental-dsh-cua 2026-09-28 16:17:55 +08:00
creatixchu 02c12b5a38 test(ptc): refresh runtime-owned execution instructions 2026-09-28 16:16:20 +08:00
creatixchu bf95e3a74b fix(ptc): preserve policy and evaluation lifecycle across memory runs 2026-09-28 16:08:45 +08:00
creatixchu 810fd8601f Merge PTC cleanup and refresh CUA dependency graph 2026-09-28 15:42:37 +08:00
creatixchu 3a0c2ae3bb test: drop the retired desktop keyboard cleanup patch 2026-09-28 15:42:08 +08:00
creatixchu 51000b5283 Merge updated PTC base into CUA 2026-09-28 15:35:59 +08:00
creatixchu 21e3950519 Merge master into PTC execution-mode change 2026-09-28 15:34:26 +08:00
creatixchu 83cb2c3f1a test(ptc): update desktop fixture and generated package catalog 2026-09-28 15:33:36 +08:00
creatixchu 6657824b6a Merge PTC execution-mode fixture fixes 2026-09-28 15:31:58 +08:00
creatixchu faf41c6cd7 test(ptc): validate raw mode values at the schema parser 2026-09-28 15:31:34 +08:00
creatixchu f38f025726 feat(cua): enable retained PTC through executionMode 2026-09-28 15:31:12 +08:00
creatixchu 7c05445810 test(ptc): reload execution modes through the provider fiber 2026-09-28 15:29:57 +08:00
creatixchu 30b193b38a feat(ptc): select Node state retention through executionMode 2026-09-28 15:27:39 +08:00
Yif a9d169a399 Merge master into ui/plugin-placeholder-default-name 2026-09-26 22:04:14 +08:00
Yif 6ce05157f9 Merge master to refresh session UI fixes base 2026-09-26 21:57:59 +08:00
creatixchu 6182c8b232 Merge branch 'feat/persistent-ptc' into feat/experimental-dsh-cua 2026-09-26 12:48:40 +08:00
creatixchu 2181d16495 test: wait for refreshed models before reopening editor 2026-09-26 12:47:53 +08:00
creatixchu be7ab6a17c Merge branch 'feat/persistent-ptc' into feat/experimental-dsh-cua 2026-09-26 12:34:16 +08:00
creatixchu 1c8683c47b test: preserve config reader binding in cleaner fixture 2026-09-26 12:33:53 +08:00
creatixchu 5f9d8a7467 Merge branch 'feat/persistent-ptc' into feat/experimental-dsh-cua 2026-09-26 12:12:32 +08:00
creatixchu b9e461b144 fix(build): make desktop keyboard output safe to clean 2026-09-26 12:11:56 +08:00
creatixchu 0bc7c0d5c8 test(plugins): exercise preserved non-Error startup failures 2026-09-26 12:09:59 +08:00
creatixchu 5e6524bca4 test(hooks): match the installer canonical worktree path 2026-09-26 12:09:58 +08:00
creatixchu 9210c519e2 test(plugins): cover non-Error startup diagnostics 2026-09-26 12:05:27 +08:00
creatixchu 9d2dbee7e1 test(cua): resolve provider defaults through the loader 2026-09-26 11:59:26 +08:00
creatixchu 9ea2c628c8 test(cua): exercise real loader startup and refresh configuration contracts 2026-09-26 11:57:32 +08:00
creatixchu 19b2cfa0fc fix(cua): resolve the bridge outside projects and retain setup diagnostics 2026-09-26 11:54:20 +08:00
creatixchu 74d1f1e55d fix(cua): await provider preparation through Cordis lifecycle 2026-09-26 11:38:46 +08:00
creatixchu dde252235d Merge branch 'feat/persistent-ptc' into feat/experimental-dsh-cua
# 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
2026-09-26 11:32:58 +08:00
creatixchu 683e42a3b0 Merge remote-tracking branch 'origin/master' into feat/persistent-ptc 2026-09-26 11:30:25 +08:00
creatixchu d1011ec11a refactor(cua): pass admitted parameters to the native worker 2026-09-26 11:28:23 +08:00
creatixchu f22177a1f9 refactor(cua): keep native preparation internal and remove duplicate checks 2026-09-26 11:25:04 +08:00
Yif 57eef060f0 fix(ui): isolate session hover and jobs menu fixes
Merge origin/master and move plugin manager, artwork, and automation trigger changes to their independent PR. Cover keyboard-focused inline cards without warming the next pointer visit.
2026-09-26 01:05:32 +08:00
creatixchu a9683e8a23 test(cua): use fixture-aware session recovery cases 2026-09-25 19:57:00 +08:00
creatixchu ce11b81599 fix(cua): keep existing conversations usable after activation 2026-09-25 19:55:44 +08:00
creatixchu e0d9be74df test(plugins): assert experimental metadata errors directly 2026-09-25 19:46:16 +08:00
creatixchu cc1c7dd8d1 fix(cua): retain typed configuration during native setup 2026-09-25 19:44:39 +08:00
creatixchu 15ca36182c test(cua): clarify cancellation listener callbacks 2026-09-25 19:40:57 +08:00
creatixchu 0edd6e7666 fix(cua): make awaited preparation cleanup explicit 2026-09-25 19:33:10 +08:00
creatixchu e07f6e859b test(cua): refresh the concise activation explanation 2026-09-25 19:11:11 +08:00
creatixchu 4409f16ada fix(cua): keep activation effects readable beside the switch 2026-09-25 19:07:52 +08:00
creatixchu 14588b9aa9 docs(cua): refresh the promoted package dependency graph 2026-09-25 18:58:22 +08:00
creatixchu 932bdbde60 fix(cua): normalize the published native payload manifest 2026-09-25 18:54:53 +08:00
creatixchu ae57ea3ff8 test(cua): record the optional Computer Use plugin card 2026-09-25 18:49:17 +08:00
creatixchu 73f379582f test(cua): cover failed native diagnostics and artifact locations 2026-09-25 18:42:43 +08:00
creatixchu 87dfe408a0 fix(cua): align host test compilation and package references 2026-09-25 18:35:52 +08:00
creatixchu 82b3073ba5 test(cua): align preparation fixtures and component status queries 2026-09-25 18:31:34 +08:00
creatixchu 504b6df830 feat(cua): add an experimental opt-in with on-demand native preparation 2026-09-25 18:26:24 +08:00
creatixchu 3b7e28a873 Merge branch 'feat/persistent-ptc' into feat/experimental-dsh-cua 2026-09-25 15:40:32 +08:00
creatixchu ca3a438a4e test(ptc): give nested tool fixtures an owned lifecycle context 2026-09-25 15:38:45 +08:00
creatixchu 063cf99673 Merge branch 'feat/persistent-ptc' into feat/experimental-dsh-cua 2026-09-25 13:54:37 +08:00
creatixchu 2fa5cc080c test(cua): reject calls from ended agents before helper acquisition 2026-09-25 13:54:04 +08:00
creatixchu 10d4193663 test(ptc): cover cleanup and cancellation after idle process exit 2026-09-25 13:54:03 +08:00
creatixchu 742a3a2656 test(cua): refresh state projection and window recovery snapshot 2026-09-25 13:50:53 +08:00
creatixchu 3977aa43b3 Merge branch 'feat/persistent-ptc' into feat/experimental-dsh-cua 2026-09-25 13:49:38 +08:00
creatixchu a18696bbc4 fix(cua): separate accessibility timeout and document recovery 2026-09-25 13:49:06 +08:00
creatixchu bf84ab113f docs(ptc): specify retained evaluation and release behavior 2026-09-25 13:49:05 +08:00
creatixchu 298ba12709 fix(cua): attempt all native fixture cleanup actions 2026-09-25 13:46:36 +08:00
creatixchu f8d19a57e4 test(cua): verify window ownership around accessibility reads 2026-09-25 13:46:35 +08:00
creatixchu 065963128c test(ptc): pin implicit globals and completed callback errors 2026-09-25 13:40:12 +08:00
creatixchu 34d3caed82 refactor(ptc): derive renderer formats from the runtime service 2026-09-25 13:36:42 +08:00
creatixchu e7bcb1b373 fix(cua): retain fractional drag movement delays 2026-09-25 13:34:24 +08:00
creatixchu 01aeea93f2 fix(ptc): reject dispatch to an exited retained process 2026-09-25 13:34:23 +08:00
creatixchu 7bf811902f perf(cua): assemble native response frames once 2026-09-25 13:28:30 +08:00
creatixchu fb594a665c fix(cua): project only documented accessibility state fields 2026-09-25 13:20:56 +08:00
creatixchu 5a2bb185ab fix(ptc): refresh retained binding error descriptors 2026-09-25 13:15:17 +08:00
creatixchu 1c29ff0598 fix(ptc): await every retained environment during unload 2026-09-25 13:11:00 +08:00
creatixchu d256be165c fix(ptc): return bounded parse-overflow cleanup failures 2026-09-25 13:04:16 +08:00
creatixchu cc4e98f5bc fix(cua): await screenshot projection during disposal 2026-09-25 13:02:57 +08:00
creatixchu d3ab674dcd test(ptc): pin syntax rejection before REPL startup 2026-09-25 12:47:36 +08:00
creatixchu 56a93f538c fix(cua): accept literal plus-key chords 2026-09-25 12:47:36 +08:00
creatixchu 0b26294635 test(ptc): keep process identity diagnostics free of environment data 2026-09-25 12:43:09 +08:00
creatixchu 9d6a9e28b5 fix(ptc): isolate the REPL process binding 2026-09-25 12:40:48 +08:00
creatixchu ed6a2b090d Merge typed PTC Session fixtures into dsh-cua 2026-09-25 12:22:34 +08:00
creatixchu 5e0adc7968 test(ptc): use a real Session in scoped agent fixtures 2026-09-25 12:21:37 +08:00
creatixchu 3e15397c46 Merge retired PTC cast allowances into dsh-cua 2026-09-25 12:15:19 +08:00
creatixchu 9bc8d9bed9 test(ptc): retire unknown-cast allowances for agent fixtures 2026-09-25 12:14:36 +08:00
creatixchu dd7172a44c Merge PTC CI fixture fixes into dsh-cua 2026-09-25 12:12:32 +08:00
creatixchu 3f7a8d7b7e test(ptc): give inspection fixtures lifecycle ownership 2026-09-25 12:10:56 +08:00
creatixchu 3d76810a57 test(cua): refresh tool snapshots after the master sync 2026-09-25 12:04:51 +08:00
creatixchu 502422ce1b Merge persistent PTC replay updates into dsh-cua 2026-09-25 12:01:35 +08:00
creatixchu 5e02e2a13b test(ptc): refresh the master-synced tool guidance snapshot 2026-09-25 12:00:59 +08:00
creatixchu 3337488bdf Merge master-synced persistent PTC into dsh-cua 2026-09-25 11:58:21 +08:00
creatixchu 1b6dbdde6b test(ptc): disable the split API-key provider during replay 2026-09-25 11:56:53 +08:00
creatixchu a340ecfd31 Merge master into persistent PTC 2026-09-25 11:52:21 +08:00
creatixchu d3056e3fbe test(cua): record window geometry conflict errors 2026-09-25 11:20:59 +08:00
creatixchu cd5df606db fix(cua): reject conflicting window geometry before input 2026-09-25 11:18:13 +08:00
creatixchu 505c4fcb7d perf(cua): batch AX roles with observation attributes 2026-09-24 23:17:02 +08:00
creatixchu a73eed85f1 Merge persistent PTC review fixes into dsh-cua 2026-09-24 23:08:20 +08:00
creatixchu 2c986ca3c3 test(ptc): pin raw subprocess output guidance 2026-09-24 23:03:40 +08:00
creatixchu 0309d22520 docs(ptc): clarify raw subprocess output attribution 2026-09-24 23:01:33 +08:00
creatixchu 9e17500315 fix(ptc): separate shared schema value from config type 2026-09-24 22:55:09 +08:00
creatixchu 7101555061 test(ptc): cover retained process races and refresh model evidence 2026-09-24 22:52:52 +08:00
creatixchu c5dff80c94 test(ptc): give tool bridge fixtures owned agent contexts 2026-09-24 22:48:23 +08:00
creatixchu 6a8483ce42 fix(ptc): contain retained evaluation failures and output 2026-09-24 22:44:56 +08:00
creatixchu ef09d35786 perf(cua): reduce validated Unicode event spacing 2026-09-24 21:10:10 +08:00
creatixchu 755eb3e3f9 fix(cua): deliver text line breaks with native Return events 2026-09-24 20:51:16 +08:00
creatixchu 29ecbbb3d5 test(cua): compare native text pacing with Blender readback 2026-09-24 20:44:01 +08:00
creatixchu 8f9f086180 feat(cua): configure Unicode text event pacing 2026-09-24 20:39:25 +08:00
Yif 5c0ef3a7c2 Merge master and preserve requested terminal gradient 2026-09-24 19:45:21 +08:00
creatixchu 5ed5b9cb4f test(cua): observe modal dismissal independently of window destruction 2026-09-24 19:40:59 +08:00
Yif 0701068a31 fix(ui): polish session hints, task menus and plugin examples 2026-09-24 19:40:02 +08:00
creatixchu 4a7fcb3e38 test(cua): record stale observation recovery in session replay 2026-09-24 19:37:36 +08:00
creatixchu 6718231377 fix(cua): invalidate observations when accessibility nodes disappear 2026-09-24 19:36:30 +08:00
creatixchu bdf183e7e1 test(cua): compare complete native observations on synthetic windows 2026-09-24 19:26:56 +08:00
creatixchu 6b3a196a3f fix(cua): compile batch attribute regression assertions 2026-09-24 19:24:42 +08:00
creatixchu c327aa7ddd perf(cua): batch per-node accessibility observation reads 2026-09-24 19:23:19 +08:00
creatixchu 24b2d1358e fix(cua): separate optional AX metadata from structural discovery 2026-09-24 18:57:48 +08:00
creatixchu 039e854166 fix(cua): retain panel observations when action metadata is unavailable 2026-09-24 18:54:19 +08:00
creatixchu 5435226a79 fix(cua): allow unavailable optional values in AppKit panels 2026-09-24 18:48:17 +08:00
creatixchu 110abb9ad7 fix(cua): consume requests before EOF on persistent stdin pipes 2026-09-24 18:33:13 +08:00
creatixchu bf508eaa2f fix(cua): preserve window metadata and support Apple CLI test runtimes 2026-09-24 18:23:08 +08:00
creatixchu d994a3326b feat(cua): replace the macOS Rust helper with Swift 2026-09-24 18:09:17 +08:00
Yichen Jiang 566944f970 feat(web): name an empty model catalog as unconfigured
When the loaded catalog offers no model and no provider failed to load,
the composer trigger shows a warning dot and "No model configured"
instead of the saved route id, and its menu opens on guidance to sign in
to a DeepSeek account or add an API key. A saved selection that becomes
unavailable while other models remain keeps its route id. The README no
longer claims an unavailable model disables the composer.
2026-09-24 17:05:04 +08:00
creatixchu bc6ec2b1cd test(cua): record foreground observations and align invocation assertions 2026-09-24 16:54:33 +08:00
creatixchu feba1df810 fix(cua): avoid redundant foreground requests and correlate their observations 2026-09-24 16:52:28 +08:00
creatixchu 330a6cf3e5 fix(cua): admit large screenshots through the real PTC image pipeline 2026-09-24 16:49:10 +08:00
Yichen Jiang 94593168a3 feat(llm): offer API-key route models only while their key resolves
The official DeepSeek route and pi-ai routes that name an API-key
reference again return an empty catalog while that reference resolves
to nothing, so model pickers stop offering models that can only fail
with MISSING_CREDENTIAL. A malformed key still reports
INVALID_CREDENTIAL, and routes with provider-native authentication keep
their catalogs. This restores the discovery rule that 6db08fd80f
replaced with credential-independent catalogs.
2026-09-24 16:48:19 +08:00
Yichen Jiang 6d602d792e fix(llm): satisfy type-aware lint in the DeepSeek account package
lint:contracts-ready rejected the override filter's undefined check
against its declared type and four asymmetric matchers typed any. The
override filter now takes the parsed configuration as it arrives, and
the tests narrow their matchers the way other packages do.
2026-09-24 16:48:11 +08:00
Yichen Jiang 835bf97370 feat(web): show the DeepSeek account row as signed in
The account row lost its only action when the catalog editor was removed
and rendered as a bare name. It now carries a green "Signed in" status;
the store keeps the row only while the account lists models, so the
status holds whenever the row renders. The package README no longer
describes the removed editor.
2026-09-24 16:29:19 +08:00
creatixchu a0dd723300 test(cua): observe and raise the exact Blender fixture window 2026-09-24 16:24:26 +08:00
creatixchu 149ba9acd0 test(cua): locate Blender editor independently of Retina units 2026-09-24 16:15:49 +08:00
creatixchu eb10c938ca test(cua): record AXless window pixel workflow 2026-09-24 15:33:57 +08:00
creatixchu 0a83a1a941 fix(cua): retain pixel access for owned windows without AX 2026-09-24 15:29:36 +08:00
creatixchu b3e452b942 test(mcp-client): distinguish protocol and image encoding rejection 2026-09-24 15:08:33 +08:00
creatixchu 745a25f561 test(mcp-client): isolate noncanonical image cases 2026-09-24 15:05:47 +08:00
creatixchu c2caea031e fix(mcp-client): admit large canonical image payloads without regex recursion 2026-09-24 14:59:53 +08:00
creatixchu 9a53f64dda fix(cua): send modifier transitions for native key chords 2026-09-24 14:52:18 +08:00
creatixchu de1f8b2421 test(cua): refresh foreground guidance snapshot 2026-09-24 14:15:01 +08:00
creatixchu fffc899f9c fix(cua): avoid preemptive foreground activation 2026-09-24 14:11:14 +08:00
Yichen Jiang 7755d97dba test(llm): cover the account listing and gate through the Loader composition 2026-09-24 13:38:08 +08:00
Yichen Jiang 7653ca66ab Merge remote-tracking branch 'origin/master' into worktree/deepseek-account-models-ba9235
Resolve the account provider import to carry both the account quota
codes from #4858 and APP_IDENTITY for the model gate. The upstream
account-quota spec now answers the account listing request with 404,
because the account route requests /models before its first Messages
call and a shared mocked Response body can only be read once.
2026-09-24 13:37:06 +08:00
creatixchu 33b10af1f2 fix(cua): deliver background pointer events once 2026-09-24 13:36:43 +08:00
Yichen Jiang 5dd772d537 docs(llm): document the DeepSeek account listing, overrides, and version gate 2026-09-24 13:20:44 +08:00
Yichen Jiang 783d98b78e feat(web): drop the account catalog editor from the Models page 2026-09-24 13:15:30 +08:00
Yichen Jiang e647f079b9 fix(llm): default DeepSeek account model overrides to an empty dict 2026-09-24 13:15:29 +08:00
Yichen Jiang 0a13e89b0d feat(llm): serve the DeepSeek account catalog from the account listing 2026-09-24 13:10:36 +08:00
Yichen Jiang 31ce1ad5f1 feat(llm): cache the DeepSeek account listing per credential 2026-09-24 13:08:00 +08:00
Yichen Jiang 2817b9e205 test(llm): cover listing entries that omit optional account catalog fields 2026-09-24 13:07:59 +08:00
Yichen Jiang d8c9868763 feat(llm): gate DeepSeek account models by declared client version and bundled support 2026-09-24 13:06:13 +08:00
Yichen Jiang ad0a30075f feat(llm): decode the DeepSeek account model listing entry by entry 2026-09-24 13:04:10 +08:00
Yichen Jiang 4082eb3ceb feat(llm): configure the DeepSeek account listing endpoint and model overrides 2026-09-24 13:02:47 +08:00
Yichen Jiang 20f99ffb0c feat(llm): let DeepSeek providers await their connection and refuse models before I/O 2026-09-24 12:59:31 +08:00
Yichen Jiang 7f38c1b099 fix(llm): name the Flash catalog entry DeepSeek-V4.1-Flash 2026-09-24 12:57:20 +08:00
creatixchu b60a49409d test(cua): compare file panel URLs by filesystem identity 2026-09-24 11:45:17 +08:00
creatixchu cd9fa73222 test(cua): wait on panel input readiness without AX contention 2026-09-24 11:43:29 +08:00
creatixchu 9be9a29c9b fix(cua): reject unavailable nearest element windows 2026-09-24 11:40:52 +08:00
creatixchu 4d18c4d501 test(cua): observe app-wide sheets for keyboard completion 2026-09-24 11:40:19 +08:00
creatixchu 4f64e43bb1 test(cua): focus the observed sheet after multi-window actions 2026-09-24 11:35:53 +08:00
creatixchu eefea870f4 test(cua): budget panel polling for remote AX traversal 2026-09-24 11:33:23 +08:00
creatixchu 73c80201f0 fix(cua): refuse keys before system panel input is ready 2026-09-24 11:30:18 +08:00
creatixchu c612a97e47 test(cua): keep native fixture windows outside Stage Manager sets 2026-09-24 11:26:13 +08:00
creatixchu c17f008c00 test(cua): isolate system panel regression entry 2026-09-24 11:03:07 +08:00
creatixchu dc5e41a319 test(cua): observe complete system file panel 2026-09-24 11:01:18 +08:00
creatixchu 5304401ada fix(cua): resolve AppKit sheets and system panel input 2026-09-24 10:59:22 +08:00
creatixchu 76fba44641 test(cua): refresh formatted API reference snapshot 2026-09-23 22:11:33 +08:00
creatixchu 775b6416f0 docs(cua): format object API guidance as a method list 2026-09-23 22:03:59 +08:00
creatixchu aeca82c699 fix(cua): require a logged-in console for desktop readiness 2026-09-23 21:53:01 +08:00
creatixchu df0b833500 test(cua): record desktop readiness and complete object API guidance 2026-09-23 21:38:14 +08:00
creatixchu f1c728c7af fix(cua): reject locked sessions and bound cyclic AX traversal 2026-09-23 21:13:45 +08:00
creatixchu 685bb24bf7 test(cua): record foreground recovery operation 2026-09-23 20:47:32 +08:00
creatixchu 091a085d5d feat(cua): add explicit activation for hidden application recovery 2026-09-23 20:40:46 +08:00
creatixchu 3a7e7650bb test(cua): record explicit secondary action schema and guidance 2026-09-23 20:32:34 +08:00
creatixchu 493cd1e0bc feat(cua): keep app selection in background and expose explicit AX actions 2026-09-23 20:28:31 +08:00
creatixchu f808571fff Merge remote-tracking branch 'origin/feat/persistent-ptc' into feat/experimental-dsh-cua 2026-09-23 20:22:03 +08:00
creatixchu f5c4110b5e fix(cua): activate selected apps before observing their windows 2026-09-23 20:20:33 +08:00
creatixchu 75ae02da4a fix(ptc): allow top-level await in scoped blocks 2026-09-23 20:18:49 +08:00
creatixchu 9107ae0d21 test(cua): refresh persistent PTC guidance snapshot 2026-09-23 20:18:45 +08:00
creatixchu 56f3d3135d feat(cua): expose plain-text paste and persistent PTC composition 2026-09-23 20:12:21 +08:00
creatixchu 9bd3aca35e feat(cua): support indexed pointer gestures and focused windows 2026-09-23 20:07:19 +08:00
creatixchu 2a17abf773 test(cua): declare image capability for screenshot admission 2026-09-23 17:59:41 +08:00
creatixchu ad41f5f970 test(cua): compare screenshot coordinate metadata structurally 2026-09-23 17:56:25 +08:00
creatixchu a11af71304 test(cua): cover portable screenshots and replay published package exports 2026-09-23 17:55:39 +08:00
creatixchu b44230bdbe Merge remote-tracking branch 'origin/feat/persistent-ptc' into feat/experimental-dsh-cua 2026-09-23 17:38:15 +08:00
creatixchu cd08b60f9c test(ptc): declare workflow runtime confinement capabilities 2026-09-23 17:36:07 +08:00
creatixchu dde3e78c23 Merge remote-tracking branch 'origin/master' into feat/persistent-ptc 2026-09-23 17:19:20 +08:00
creatixchu 62396f4537 docs(ptc): synchronize the generated config source reference 2026-09-23 17:16:00 +08:00
creatixchu cff260f048 chore(ptc): align published artifacts and strict lint contracts 2026-09-23 17:12:43 +08:00
creatixchu b0dc38429a test(ptc): pin packaged-host persistent bootstrap selection 2026-09-23 17:08:00 +08:00
creatixchu d59f96ece2 docs(ptc): refresh generated source locations 2026-09-23 17:06:56 +08:00
creatixchu 1393ed2e12 fix(ptc): simplify evaluation settlement and isolate published bundles 2026-09-23 17:03:04 +08:00
creatixchu 7391d06a8d fix(ptc): preserve provider identity and self-contained published entries 2026-09-23 17:00:37 +08:00
creatixchu e625e7a6a5 fix(ptc): report process loss consistently across exit event ordering 2026-09-23 16:58:27 +08:00
creatixchu f85883425d fix(ptc): release each provider once and await workflow REPL completion 2026-09-23 16:57:46 +08:00
creatixchu 4fe78cb71a fix(ptc): compose the persistent provider and share process cleanup 2026-09-23 16:55:21 +08:00
creatixchu 5f5395f7ad test(ptc): cover persistent process transport and cleanup failures 2026-09-23 16:50:38 +08:00
creatixchu 2859d2bb08 test(ptc): verify REPL callback ownership and scoped output 2026-09-23 16:48:26 +08:00
creatixchu bd2ed0ce61 test(ptc): exercise ownerless requests without explicit undefined fields 2026-09-23 16:45:21 +08:00
creatixchu 607077b985 test(ptc): cover retained state and recovery through the shipped profile 2026-09-23 16:44:48 +08:00
creatixchu 4fb14dcac6 feat(ptc): add opt-in best-effort persistent Node REPL 2026-09-23 16:41:38 +08:00
creatixchu 8a64473c5b docs(cua): document the API and external evaluator integration
Explain configuration, window and input semantics, source ownership and current limitations. Record why persistence belongs to the execution host and refresh repository catalogs.
2026-09-23 16:01:41 +08:00
creatixchu 09b60c331c test(cua): record native tool operations and image delivery
Exercise the real Loader, AgentLoop, helper transport and attachment pipeline with an explicit desktop protocol fixture. Add a headless V4 Session replay for observations and actions.
2026-09-23 16:01:40 +08:00
creatixchu ee676a3025 test(cua): verify AppKit and Electron operations on macOS
Use disposable applications and independent state records to verify clicks, Unicode input, modifiers, scrolling, dragging, capture and stale-target cleanup. Share the native test transport.
2026-09-23 16:01:39 +08:00
creatixchu 445c5f3da7 build(cua): wire the experimental package and source profile
Declare package exports, workspace dependencies and build entries. Add an opt-in macOS source composition and enforce the experimental release payload.
2026-09-23 16:01:36 +08:00
creatixchu 17a4118c20 feat(cua): integrate logged tools with Agent-owned native resources
Register native operations and model guidance, and bridge objects through the current authorized tool binding. Join Agent and provider cleanup without introducing a persistent evaluator.
2026-09-23 16:01:36 +08:00
creatixchu e3911008a8 feat(cua): bound native process communication and cleanup
Implement serial JSONL transport with request/response limits, deadlines, cancellation and process-range cleanup. Cover protocol and stream failures across host platforms.
2026-09-23 16:01:35 +08:00
creatixchu 2355950d20 feat(cua): add scoped application and window objects
Provide discovery, observations, screenshots and input through retained objects. Preserve per-window snapshot identity and prevent action options from replacing the selected target.
2026-09-23 16:01:34 +08:00
creatixchu b0507f94af feat(cua): define application types and validate the operation protocol
Define the evaluator-independent app/window API and validate method-specific requests and native responses. Test invalid targets and parameter admission.
2026-09-23 16:01:34 +08:00
creatixchu 4a6e03fecd feat(cua): implement the macOS native operation helper
Expose app and window handles, scoped AX snapshots, permissions, input and capture through a JSONL helper. Bind handles to process lifetimes and report stable native errors.
2026-09-23 16:01:31 +08:00
creatixchu 7417c38a14 feat(cua): copy licensed macOS accessibility and input primitives
Copy the selected Cua Driver macOS source without modifications. Preserve the MIT notice and a file-level source manifest.
2026-09-23 16:01:29 +08:00
3622 changed files with 142666 additions and 18132 deletions

No files matched your search

@@ -27,8 +27,8 @@
en: fbd101d4576afa92
zh: 35f5d3c2f6cad6a8
/agent-note-cooperative-tool-cancellation-at-the-registry-boundary/alternatives-considered:
en: 1f205f1a78d4fd5b
zh: 6c17e35da2909302
en: c4d86feb135d2ac4
zh: 67c56c0bc2213949
/agent-note-cooperative-tool-cancellation-at-the-registry-boundary/consequences:
en: 24a9ad2c4c7c86b5
zh: debdd29d272e07ea
@@ -62,7 +62,7 @@ No registry test can prove that arbitrary third-party same-process code observes
**Forbid around wrappers from replacing the signal.** Rejected because deadlines and nested operational scopes need lexical derivation. Capturing and fusing the caller signal preserves composition without allowing detachment.
**Race the tool promise against cancellation.** Rejected because it reports completion while side effects may remain live, violating the [quiescent-disposal rule](../../../../docs/defensive-patterns.md#dispose-must-reach-quiescence-not-just-request-it).
**Race the tool promise against cancellation.** Rejected because it reports completion while side effects may remain live, violating the [quiescent-disposal rule](../../../../docs/defensive-patterns/general.md#dispose-must-reach-quiescence-not-just-request-it).
## Consequences
@@ -62,7 +62,7 @@ Status: implemented
**禁止环绕包装层替换信号。** 不予采纳,因为截止时间和嵌套操作作用域需要词法派生信号。捕获并融合调用方信号既保留组合能力,也不允许切断调用方取消。
**让工具 promise 与取消竞速。** 不予采纳,因为这种方式会在副作用仍可能存活时报告完成,违反[dispose(资源释放)必须完全停稳的规则](../../../../docs/defensive-patterns.zh.md#dispose-must-reach-quiescence-not-just-request-it)。
**让工具 promise 与取消竞速。** 不予采纳,因为这种方式会在副作用仍可能存活时报告完成,违反[dispose(资源释放)必须完全停稳的规则](../../../../docs/defensive-patterns/general.zh.md#dispose-must-reach-quiescence-not-just-request-it)。
## 后果
@@ -27,8 +27,8 @@
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
@@ -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
@@ -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 通道并打开新通道;其完整快照补齐遗漏的图变更。启动、图更新和重建帧共用同一队列,因此代码替换不会与初始模块到达重叠。
## 包归属
@@ -18,5 +18,5 @@
en: fa503dcfaa6f438f
zh: 26052212a124e133
/agent-note-compiler-independent-typert-type-model/consequences:
en: 4a472acf377f8361
zh: cb83602b123a4aae
en: e1022cf9523fb28e
zh: 01dfb764fb98de0d
@@ -51,3 +51,5 @@ For each supported node kind and literal category, Zod emitter tests run both su
New generation targets and static checks can reuse the same TypeGraph, and business categories can extend PackageModel without parsing the AST again. Preserving pre-evaluation types and independent faces makes the model more complex than a flattened schema; emitters must explicitly declare their supported scope and fail on missing capabilities.
Explicit package opt-in keeps artifact publication and exports under package ownership. Repository orchestration may still run the whole-workspace Host contract pass for every opted-in package; that pass remains owned by the later Remote Gateway Agent Note. The static Cordis catalogs remain reproducible from the canonical model without coupling `tool-cordis` to runtime registry state. `ctx.typert` reflects only artifacts mounted in the current runtime, and unloading does not control Zod instances that consumers retain after materializing them.
Ordinary Host and Remote artifacts reuse this model to emit one static method spec per endpoint. Each parameter has normal `codec` and `strictCodec` variants derived from the same checker-resolved type model; neither analysis nor the model carries a per-method input policy. A pure factory returns a lazy info instance whose direct method index and descriptor list share objects; the existing Registry retains those descriptors, while `TYPERT_REMOTE.methods` supplies exact static types. The [unified Typert/Remote metadata note](../../proposed/architecture/2026-09-20-unified-typert-remote-metadata.md) records this delivery mechanism and the incomplete downstream adaptation. Declaration and face-isolation rules remain unchanged; separate Programs and processes do not share object identity.
@@ -51,3 +51,5 @@ Zod emitter 对支持的节点和各类 literal 逐类执行成功与失败 pars
新增生成目标或静态检查可复用同一 TypeGraph,业务类目也可在 PackageModel 上扩展,而无需再次解析 AST。保留计算前类型和独立 face 的代价是模型比打平后的 schema 更复杂,emitter 必须显式声明支持范围并对缺失能力失败。
包级显式 opt-in 使产物发布与 exports 由各包自行管理。仓库编排仍可为每个已 opt-in 的包运行全仓 Host 约定 pass;该 pass 仍由后续 Remote Gateway Agent Note 负责说明。静态 Cordis catalog 可从标准模型复现,同时不把 `tool-cordis` 与运行时注册表状态耦合。`ctx.typert` 只反映当前运行时中已挂载的产物;对于消费方物化后仍持有的 Zod 实例,卸载流程无法控制。
普通 Host 与 Remote 产物复用此模型,为每个端点生成一份静态方法 spec。每个参数都有从同一 checker 解析后类型模型派生的普通 `codec` 和 `strictCodec` 变体;分析过程与模型均不携带逐方法输入策略。纯 factory 返回惰性 info 实例,其直接方法索引与描述符列表共享对象;既有 Registry 保留这些描述符,而 `TYPERT_REMOTE.methods` 提供精确静态类型。[统一 Typert/Remote 元数据 note](../../proposed/architecture/2026-09-20-unified-typert-remote-metadata.zh.md)记录该交付机制及尚未完成的下游适配。声明与编译面隔离规则保持不变;独立 Program 和进程不共享对象身份。
@@ -12,14 +12,14 @@
en: 44d946b53496974c
zh: 403447c25a5b2d7c
/agent-note-typert-gateway-targeted-method-calls/components-and-cordis-services:
en: aa7f509189ade32e
zh: ef2841bc793f31b2
en: 02c20706e48467e6
zh: 2b0916ae0cee351b
/agent-note-typert-gateway-targeted-method-calls/business-declarations:
en: c6b70d442b137d2e
zh: 6d0dbefe89937ef1
en: b9394d5ef98cc82a
zh: 1fce07fe150722ee
/agent-note-typert-gateway-targeted-method-calls/decorators-and-the-explicit-gateway-facet:
en: 155afbe4b8af600b
zh: 14705698a9cdbd84
en: 161dccf2b4cf565d
zh: fa587fe0b46bf551
/agent-note-typert-gateway-targeted-method-calls/lookup-and-remote-scope-registration:
en: 9f7448898cf5cadc
zh: 419573c589ed6f7d
@@ -33,8 +33,8 @@
en: a244f6a88fdabf3d
zh: e4feedb7297f4865
/agent-note-typert-gateway-targeted-method-calls/three-artifact-kinds-and-two-typescript-programs:
en: 6ea4bddcf35a834d
zh: 4af474c6818b1cf0
en: c5bcd6eb76bf3d42
zh: 9df2fbb364643186
/agent-note-typert-gateway-targeted-method-calls/the-remote-package-entry:
en: 75e977fe0fb71d59
zh: 00760c79da173cb7
@@ -48,8 +48,8 @@
en: 5d14e1b7afc53efb
zh: e2f4b5814083738f
/agent-note-typert-gateway-targeted-method-calls/src-and-lib-operating-modes:
en: 1e366dcfab74831b
zh: 14199f25f20a9352
en: f1b8426d1cfaed35
zh: a370376923c98d6e
/agent-note-typert-gateway-targeted-method-calls/host-gateway-resolution:
en: 88a95d7ab4621caf
zh: 89b63fb6440fca79
@@ -66,8 +66,8 @@
en: b1c0c3dd77c56ce8
zh: 769ffd4aeefd949c
/agent-note-typert-gateway-targeted-method-calls/alternatives-considered:
en: 0d68c499b7780f4f
zh: bdd1e57232b996e2
en: eaa3ffe5ca14005c
zh: 1f0b455e30ade872
/agent-note-typert-gateway-targeted-method-calls/verification:
en: 7433bdb522bb2083
zh: 545c17613b636f00
@@ -28,7 +28,7 @@ The Remote consumer projection contains `.d.ts`, `.d.ts.map`, and `.js` files. T
| Component | Cordis service | Responsibility |
|---|---|---|
| `@deepseek-ai/dsh-typert-protocol` | Declares only the minimal `ctx.typert` protocol | `TypertRemoteService`, decorators, binding fallback, descriptors, lookup/Context, and the Remote map; no dependency on the compiler, Zod, Connection, or Browser |
| `@deepseek-ai/dsh-typert-protocol` | Declares only the minimal `ctx.typert` protocol | `TypertRemoteService`, decorators, binding fallback, descriptors, lookup/Context, the Remote map, and Zod-backed lazy info assembly; no dependency on the compiler, Connection, or Browser |
| Typert registry | `ctx.typert` | Separately stores reflection for the current environment, imported Remote contributions, lookup providers, and Context providers |
| Typert generator/loader | No new business service | Generates three kinds of `lib` artifacts from the Host/Client Programs and registers the current environment's artifacts with `ctx.typert` |
| API Gateway's Host face | `ctx.typertGateway` | Associates Host definitions with live Services, decodes parameters, resolves receivers, invokes methods, and projects unary results into carrier-neutral fields and attachments |
@@ -81,16 +81,18 @@ export class ScopedGoalService extends TypertRemoteService {
An endpoint selects exactly one invocation mode. A flow that needs an explicit `Agent` parameter uses `@Remote`. A flow that first switches to an Agent Context and then resolves a scoped receiver uses `@RemoteScope('agent')`. Typert does not infer either mode from the method body or from a missing parameter.
Business packages depend only on the lightweight `@deepseek-ai/dsh-typert-protocol`. It provides `TypertRemoteService` and declaration protocols for decorators, the binding fallback, lookup, Remote Scope, and descriptors, without depending on the TypeScript compiler, Zod, HTTP, or the Client runtime.
Business packages depend only on `@deepseek-ai/dsh-typert-protocol`. It provides `TypertRemoteService` and declaration protocols for decorators, the binding fallback, lookup, Remote Scope, and descriptors, without depending on the TypeScript compiler, HTTP, or the Client runtime. Its pure `./remote-info` entry uses Zod to assemble lazy named-input schemas without importing Cordis; the root entry also re-exports it alongside Cordis-backed declarations.
A method that cooperatively supports cancellation declares `signal: AbortSignal` as its final Host parameter. This reserved parameter is not a business value, lookup, or JSON field. The generated consumer method exposes it as a final optional parameter so ordinary calls remain unchanged while callers that own cancellation can pass a signal.
## Decorators and the explicit Gateway facet
A decorator only states that a method participates in the Remote contract. It performs no runtime type reflection and injects no hidden symbol into a Service constructor. The arguments to `@Remote('create')` and `@RemoteScope('agent', 'create')` are external method names; the decorated member may be the business method itself or an adapter such as `remoteExportCreate`. The member name becomes the external method name only when no alias is provided. Inheriting `TypertRemoteService` is the normal explicit declaration that a Service has joined the Gateway; its public readonly `typertGateway` field keeps the binding visible on the runtime instance.
A decorator states Remote participation, an optional export name, and stream delivery; `@Remote` options contain only `name` and `mode`. It performs no runtime type reflection and injects no hidden symbol into a Service constructor. The arguments to `@Remote('create')` and `@RemoteScope('agent', 'create')` are external method names; the decorated member may be the business method itself or an adapter such as `remoteExportCreate`. The member name becomes the external method name only when no alias is provided. Inheriting `TypertRemoteService` is the normal explicit declaration that a Service has joined the Gateway; its public readonly `typertRemote` field keeps the binding visible on the runtime instance.
In SRC mode, the decorator records the method name and invocation mode in a versioned descriptor on the Service prototype. The descriptor uses a stable string property name, so `remoteMethods()` can read markers produced by another installed copy of `dsh-typert-protocol`; it writes nothing to the Service instance, constructor, or method function.
Ordinary artifacts construct a lazy Remote info instance from one static method spec per endpoint. Its direct `TypertRemoteMethod` objects retain descriptor fields and add named `input(options?)` and `invoke`. Consumers select matching strict parsing and projection with `input({ strict: true })`; `input()` preserves normal handling. Host parameter codecs and the single normal `result` codec are unchanged. Existing Registry queries retain those objects, while `TYPERT_REMOTE.methods` supplies exact static types. Strict source startup prebuilds and loads ordinary Typert artifacts; fallback cannot recover erased schemas. The [unified metadata note](../../proposed/architecture/2026-09-20-unified-typert-remote-metadata.md) records this delivery mechanism and the incomplete consumer adaptation.
In LIB mode, the Typert compiler performs strict method discovery, type resolution, and descriptor generation. It accepts a literal service key in `TypertRemoteService`'s direct `super()` call or the explicit binding fallback; generation neither rewrites business source nor injects hidden registration metadata.
## Lookup and Remote Scope registration
@@ -215,7 +217,7 @@ Host lib build
→ Vite 构建 Web
```
The existing top-level `build` still runs `build:lib` before `build:web`, but `build:lib` must complete the Host and Remote artifacts before starting Client TypeScript compilation. A clean build must not depend on stale `.d.ts` files from an earlier build.
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.
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.
@@ -349,9 +351,9 @@ The Web already depends on build artifacts such as `lib/client.js`, so it requir
## SRC and LIB operating modes
SRC supports local source startup. The versioned prototype descriptors created by `@Remote` and `@RemoteScope()` provide method names and invocation modes. At runtime, the system reads ordered parameter names from the JavaScript function signature and combines them with registered lookup/Context providers to produce a permissive descriptor.
SRC supports local source startup. For methods without registered strict definitions, the versioned prototype descriptors created by `@Remote` and `@RemoteScope()` provide method names and invocation modes. At runtime, the system reads ordered parameter names from the JavaScript function signature and combines them with registered lookup/Context providers to produce a permissive descriptor.
For example, `@Remote('create') remoteExportCreate(agent, request, signal)` resolves to the external method `create`, implementation member `remoteExportCreate`, two top-level business parameters, and one cancellation injection point. Lookup registration rewrites `agent` to the wire field `agentId`, `request` is passed as a same-named JSON parameter, and the final `signal` stays outside the payload. SRC does not start a `ts.Program`, use a preload or loader hook, generate or rewrite source, or inspect the internal structure of an ordinary JSON object.
For example, `@Remote('create') remoteExportCreate(agent, request, signal)` resolves to the external method `create`, implementation member `remoteExportCreate`, two top-level business parameters, and one cancellation injection point. Lookup registration rewrites `agent` to the wire field `agentId`, `request` is passed as a same-named JSON parameter, and the final `signal` stays outside the payload. The conservative fallback does not start a `ts.Program`, use a preload or loader hook, generate or rewrite source, or inspect the internal structure of an ordinary JSON object.
A signature that SRC cannot resolve unambiguously fails on the first invocation that resolves its descriptor; Service mounting records only the decorator marker and does not inspect the JavaScript signature. SRC does not guess at object destructuring, ambiguity caused by default parameters, rest parameters, nested lookups, or complex types.
@@ -474,7 +476,7 @@ The package topology is `api/remotes → api/gateway → client/connection → h
**Perform strict reflection through decorators at runtime.** JavaScript decorators cannot recover erased TypeScript types, public symbol identity, or complete Zod codecs. Injecting a compiler-private symbol into a constructor would also hide the business class's real dependencies, so Typert generates strict information at compile time.
**Use a preload, loader hook, or complete `ts.Program` during SRC startup.** This could reuse LIB analysis but would add requirements to every source startup entry. SRC needs only a usable permissive descriptor, so it uses decorator markers, function parameter names, and explicit providers; strict checks remain in the LIB contract pass.
**Use a preload, loader hook, or complete `ts.Program` during SRC startup.** This could reuse LIB analysis but would add requirements to every source startup entry. Source startup can load ordinary prebuilt artifacts or use conservative markers without loading a TypeScript Program.
**Hand-write the Client interface.** A hand-written interface cannot guarantee that it contains only Remote-marked methods and can drift from Host signatures, lookup IDs, and Zod schemas. Client types are therefore projected automatically from the Host Program.
@@ -28,7 +28,7 @@ Remote 消费端投影同时包含 `.d.ts`、`.d.ts.map` 和 `.js`。`.d.ts` 只
| 组件 | Cordis 服务 | 职责 |
|---|---|---|
| `@deepseek-ai/dsh-typert-protocol` | 只声明 `ctx.typert` 的最小协议 | `TypertRemoteService`、decorator、binding 回退、descriptor、lookup/Context 和 Remote map;不依赖 compiler、Zod、Connection 或 Browser |
| `@deepseek-ai/dsh-typert-protocol` | 只声明 `ctx.typert` 的最小协议 | `TypertRemoteService`、decorator、binding 回退、descriptor、lookup/Context、Remote map 和基于 Zod 的惰性 info 组装;不依赖 compiler、Connection 或 Browser |
| Typert registry | `ctx.typert` | 分开保存当前环境 reflection、导入的 Remote contribution、lookup provider 和 Context provider |
| Typert generator/loader | 无新增业务服务 | 从 Host/Client Program 生成三类 `lib` 产物,并把当前环境产物注册到 `ctx.typert` |
| API Gateway 的 Host face | `ctx.typertGateway` | 关联 Host definition 与活 Service,解码参数、解析 receiver、调用方法,并将一元结果投影为载体无关的字段与附件 |
@@ -81,16 +81,18 @@ export class ScopedGoalService extends TypertRemoteService {
同一个 endpoint 只能选择一种调用模式。需要显式 `Agent` 参数的流程使用 `@Remote`;需要切换到 Agent Context 再解析 scoped receiver 的流程使用 `@RemoteScope('agent')`,两者不会由 Typert 根据方法体或参数缺失自动猜测。
业务包只依赖轻量的 `@deepseek-ai/dsh-typert-protocol`。它提供 `TypertRemoteService`,以及 decorator、binding 回退、lookup、Remote Scope 和 descriptor 的声明协议,不依赖 TypeScript compiler、Zod、HTTP 或 Client runtime。
业务包只依赖 `@deepseek-ai/dsh-typert-protocol`。它提供 `TypertRemoteService`,以及 decorator、binding 回退、lookup、Remote Scope 和 descriptor 的声明协议,不依赖 TypeScript compiler、HTTP 或 Client runtime。其纯 `./remote-info` 入口使用 Zod 组装惰性具名输入 schema,不导入 Cordis;根入口在提供依赖 Cordis 的声明之余,也重新导出该能力。
支持协作式取消的方法会把 `signal: AbortSignal` 声明为最后一个 Host 参数。这个保留参数不是业务值、lookup 或 JSON 字段。生成的消费方方法将其暴露为最后一个可选参数,因此普通调用保持不变,而拥有取消控制权的调用方可以传入 signal。
## Decorator 与显式 Gateway facet
Decorator 只表达“该方法参与 Remote 约定”,不负责运行时类型反射,也不向 Service constructor 注入隐藏 symbol。`@Remote('create')` 和 `@RemoteScope('agent', 'create')` 的参数是外部方法名;被装饰成员既可以是业务方法本身,也可以是 `remoteExportCreate` 这样的适配器。未给别名时才使用成员名作为外部方法名。继承 `TypertRemoteService` 是 Service 加入 Gateway 的常规显式声明;其 public readonly `typertGateway` 字段使运行时实例上的绑定保持可见。
Decorator 表达 Remote 参与、可选导出名称和流式投递;`@Remote` 选项仅包含 `name` 和 `mode`。它不负责运行时类型反射,也不向 Service constructor 注入隐藏 symbol。`@Remote('create')` 和 `@RemoteScope('agent', 'create')` 的参数是外部方法名;被装饰成员既可以是业务方法本身,也可以是 `remoteExportCreate` 这样的适配器。未给别名时才使用成员名作为外部方法名。继承 `TypertRemoteService` 是 Service 加入 Gateway 的常规显式声明;其 public readonly `typertRemote` 字段使运行时实例上的绑定保持可见。
SRC 模式下,decorator 把方法名和调用模式记录在 Service prototype 上的带版本描述符中。描述符使用稳定的字符串属性名,因此 `remoteMethods()` 可以读取 `dsh-typert-protocol` 另一个已安装副本生成的标记;它不会向 Service 实例、constructor 或方法函数写入任何内容。
普通产物从每个端点唯一的静态方法 spec 构造惰性 Remote info 实例。其直接 `TypertRemoteMethod` 对象保留描述符字段,并增加具名 `input(options?)` 和 `invoke`。消费方通过 `input({ strict: true })` 选择配套的严格解析与投影;`input()` 保留普通处理。Host 参数 codec 与唯一的普通 `result` codec 保持不变。既有 Registry 查询保留这些对象,`TYPERT_REMOTE.methods` 则提供精确静态类型。严格源码启动预构建并加载普通 Typert 产物;fallback 无法恢复被擦除的 schema。[统一元数据 note](../../proposed/architecture/2026-09-20-unified-typert-remote-metadata.zh.md)记录该交付机制及尚未完成的消费方适配。
LIB 的严格方法发现、类型解析和 descriptor 生成由 Typert compiler 完成。它接受 `TypertRemoteService` 直接 `super()` 调用中的字面量 service key,或显式 binding 回退;生成过程不改写业务源码,也不注入隐藏注册元数据。
## Lookup 与 Remote Scope 注册
@@ -215,7 +217,7 @@ Host lib build
→ Vite 构建 Web
```
现有顶层 `build` 仍表现为先 `build:lib`、再 `build:web`,但 `build:lib` 内部必须先完成 Host 与 Remote artifact,再启动 Client TypeScript 编译。一次干净构建不能依赖上次残留的 `.d.ts`。
顶层 `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`。
即使主要输入是源文件,需要通过编译器解析消费方 surface 的仓库门禁也有相同的前置条件。公共 `typecheck`、`lint` 和 `doc-typecheck` 命令会先执行 Host 约定 pass。门禁调度器仅可在显式的 Typert 约定依赖或完整构建依赖完成后使用对应的 `*:contracts-ready` 变体,使并行 lane 既不会读取缺失的声明,也不会针对同一输出并发运行多个生成器。
@@ -349,9 +351,9 @@ Web 本身依赖 `lib/client.js` 等构建产物,因此启动 Web 前要求完
## SRC 与 LIB 运行模式
SRC 面向本地源码启动。`@Remote` 和 `@RemoteScope()` 创建的带版本 prototype 描述符给出方法名和调用模式,运行时从 JavaScript 函数签名读取顺序参数名,并结合已注册 lookup/Context provider 生成弱 descriptor。
SRC 面向本地源码启动。对于没有已注册严格定义的方法,`@Remote` 和 `@RemoteScope()` 创建的带版本 prototype 描述符给出方法名和调用模式,运行时从 JavaScript 函数签名读取顺序参数名,并结合已注册 lookup/Context provider 生成弱 descriptor。
例如 `@Remote('create') remoteExportCreate(agent, request, signal)` 解析为外部方法 `create`、实现成员 `remoteExportCreate`、两个顶层业务参数和一个取消注入点;lookup 注册把 `agent` 改写为 wire 字段 `agentId`,`request` 按同名 JSON 参数传递,最后一个 `signal` 则留在 payload 之外。SRC 不启动 `ts.Program`,不使用 preload、loader hook、源码生成或模块改写,也不检查普通 JSON 对象的内部结构。
例如 `@Remote('create') remoteExportCreate(agent, request, signal)` 解析为外部方法 `create`、实现成员 `remoteExportCreate`、两个顶层业务参数和一个取消注入点;lookup 注册把 `agent` 改写为 wire 字段 `agentId`,`request` 按同名 JSON 参数传递,最后一个 `signal` 则留在 payload 之外。保守回退不启动 `ts.Program`,不使用 preload、loader hook、源码生成或模块改写,也不检查普通 JSON 对象的内部结构。
SRC 无法明确解析的签名会在首次调用解析其 descriptor 时失败;Service 挂载只记录 decorator 标记,不检查 JavaScript 签名。SRC 不会猜测对象解构、默认参数造成的歧义、rest 参数、嵌套 lookup 或复杂类型。
@@ -474,7 +476,7 @@ Connection 提供共享 channel interceptor 与当前 HTTP carrier 映射。WebS
**让 decorator 在运行时完成严格反射。** JavaScript decorator 无法恢复擦除后的 TypeScript 类型、公共符号身份和完整 Zod codec;向 constructor 注入 compiler 私有 symbol 又会隐藏业务类的真实依赖,因此严格信息由 Typert compiler 生成。
**SRC 启动时使用 preload、loader hook 或完整 `ts.Program`。** 这能复用 LIB 分析,但增加所有源码启动入口的要求。SRC 只需要可用的弱 descriptor,因此采用 decorator 标记、函数参数名和显式 provider;严格检查留给 LIB 约定 pass。
**SRC 启动时使用 preload、loader hook 或完整 `ts.Program`。** 这能复用 LIB 分析,但增加所有源码启动入口的要求。源码启动可加载普通预构建产物或使用保守标记,而无需加载 TypeScript Program。
**手写 Client interface。** 手写接口不能保证只包含 Remote 标记的方法,也会与 Host 签名、lookup ID 和 Zod schema 漂移,因此 Client 类型从 Host Program 自动投影。
@@ -3,8 +3,8 @@
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-04-configuration-source-ownership.md
/agent-note-one-ordering-for-configuration-sources-and-what-a-discovered-file-may-not-decide:
en: 1f6ee603f36c2e09
zh: e08197958663219c
en: 35dea8a3a8bdc842
zh: 7a1cdf184ffd889f
/agent-note-one-ordering-for-configuration-sources-and-what-a-discovered-file-may-not-decide/problem:
en: d94bb81479bfa4a1
zh: af44ceaecf84ee51
@@ -4,7 +4,7 @@ Status: implemented
English | [中文](2026-08-04-configuration-source-ownership.zh.md)
The non-secret settings tier below is superseded by [profile-owned live configuration](2026-09-19-profile-owned-live-configuration.md). Credential ordering and environment restrictions remain current.
The non-secret settings tier below is superseded by [profile-owned live configuration](2026-09-19-profile-owned-live-configuration.md), and the credential ordering by [stored credential precedence](2026-10-09-stored-credential-precedence.md). Environment restrictions remain current.
## Problem
@@ -4,7 +4,7 @@ Status: implemented
[English](2026-08-04-configuration-source-ownership.md) | 中文
下文的非秘密 settings 层已由 [profile 所属实时配置](2026-09-19-profile-owned-live-configuration.zh.md) 替代。凭据优先级与环境限制仍然适用。
下文的非秘密 settings 层已由 [profile 所属实时配置](2026-09-19-profile-owned-live-configuration.zh.md) 替代,凭据优先级已由[存储凭据优先级](2026-10-09-stored-credential-precedence.zh.md)替代。环境限制仍然适用。
## Problem
@@ -9,8 +9,8 @@
en: 504dd5f06475d707
zh: 6557fd5649f48944
/agent-note-profile-plugin-bundles-replace-the-fixed-surface-overlays/decision:
en: 4c58d379cd59d879
zh: a6b6322fe82d7b6a
en: 1077d88affd90a59
zh: 73b07bf6dcb130ba
/agent-note-profile-plugin-bundles-replace-the-fixed-surface-overlays/alternatives-considered:
en: d906d7a421c6b5c0
zh: b230cd58c1b7ac79
@@ -12,7 +12,7 @@ The `dsh` launcher hardcoded its compositions: `base.cordis.yml` + `web.cordis.y
Everything becomes a **profile**: a directory `$DSH_HOME/profiles/<name>` with a `package.json` (pnpm-managed out-of-tree plugin `dependencies` plus the profile manifest `dsh.profile` with its ordered `bundles` layer list) and a user `cordis.patch.yml`. A **bundle** is an npm package declaring `"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }`; the two manifest kinds live under distinct `dsh.profile` / `dsh.bundle` keys so a package.json states which role it plays. The tree composes over an empty root by applying each bundle's patch in `dsh.profile.bundles` order, then the user layer and `--patch` overlays — one `applyEntryPatches` call shared by boot and `--dump-config`. App invocation values later moved from launcher-derived patches to startup services in the [app-owned command-line decision](../../archived/architecture/2026-08-06-app-owned-command-line.md).
The default Profile templates use `@deepseek-ai/dsh-base` as the shared core for `web`, `headless`, `sdk`, and `acp`, with one mode bundle above it. The [standalone `sdk-minimal` profile](../../../../packages/bundle/sdk-minimal/README.md) instead lists one bundle that owns its complete explicit tree. Generic `dsh --profile <name>` hands its remaining arguments to that profile's command-line startup row: Web owns its flag family, headless owns its task positional, and the protocol profiles accept no app options. Patch overlays use launcher-owned `--patch`. A new, non-shipped target can use `--from-default-profile <template>` to copy one default template's bundle list and patch-reload policy before boot or config dump. This creates an independent profile with empty dependencies and an empty user patch: it neither reads a local profile named by the template nor records an inheritance relationship. The launcher claims the complete target directory exclusively, so existing state and concurrent creators fail without modification. `dsh plugin --profile <name> <args...>` is a thin pnpm forwarder that initializes a base-backed profile and reconciles `dsh.profile.bundles` with installed bundle declarations; a package without a bundle declaration remains a plain dependency. [Headless as a direct core entry point](../../archived/architecture/2026-08-09-headless-direct-core-entry-point.md) owns the headless composition contract.
The default Profile templates use `@deepseek-ai/dsh-base` as the shared core for `web`, `headless`, `sdk`, and `acp`, with one mode bundle above it. The [standalone `sdk-minimal` profile](../../../../packages/bundle/sdk-minimal/README.md) instead lists one bundle that owns its complete explicit tree. Generic `dsh --profile <name>` hands its remaining arguments to that profile's command-line startup row: Web owns its flag family, headless owns its task positional, and the protocol profiles accept no app options. Patch overlays use launcher-owned `--patch`. A non-shipped target can use `--from-default-profile <template>` to replace itself with a copy of one default template's bundle list and patch-reload policy before boot or config dump. This creates an independent profile with empty dependencies and an empty user patch: it neither reads a local profile named by the template nor records an inheritance relationship. The launcher assembles the copy in a staging directory beside the target and renames it into place, so repeated rescue runs reset the profile; a failed rename restores the previous directory. `dsh plugin --profile <name> <args...>` is a thin pnpm forwarder that initializes a base-backed profile and reconciles `dsh.profile.bundles` with installed bundle declarations; a package without a bundle declaration remains a plain dependency. [Headless as a direct core entry point](../../archived/architecture/2026-08-09-headless-direct-core-entry-point.md) owns the headless composition contract.
Resolution is two-anchored by construction: `dsh.profile.bundles` names resolve from the dsh installation first, then the profile directory, so in-box bundles always come from the same installation as the running `dsh` and pnpm never manages them. Bare plugin names in patch rows use the [immutable runtime resolution](2026-09-09-profile-resolution-generations.md), which applies installation-first and ordered-bundle rules in memory and expands dependencies from real package directories.
@@ -12,7 +12,7 @@ Status: implemented
一切都变成 **profile**:即目录 `$DSH_HOME/profiles/<name>`,其中包含一个 `package.json`(pnpm 管理的树外插件 `dependencies`,加上 profile manifest `dsh.profile` 及其有序的 `bundles` 层列表)和一份用户 `cordis.patch.yml`。**组合包**(bundle)是声明了 `"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }` 的 npm 包;两种 manifest 分别位于互不相同的 `dsh.profile` / `dsh.bundle` 键下,因此一份 package.json 能说明自己扮演哪种角色。配置树在空的根之上组合:按 `dsh.profile.bundles` 顺序应用每个组合包的 patch,然后是用户层与 `--patch` overlay——启动与 `--dump-config` 共享同一条 `applyEntryPatches` 路径。随后,[应用持有命令行的决策](../../archived/architecture/2026-08-06-app-owned-command-line.md)又把调用期取值从启动器派生的 patch 迁移到了启动服务。
默认 Profile 模板为 `web`、`headless`、`sdk` 与 `acp` 使用 `@deepseek-ai/dsh-base` 作为共享核心,并在其上叠加一个模式组合包。[独立 `sdk-minimal` profile](../../../../packages/bundle/sdk-minimal/README.zh.md)则只列出一个拥有完整显式配置树的组合包。通用的 `dsh --profile <name>` 把剩余参数交给该 profile 的命令行启动行:Web 持有自己的 flag 家族,headless 持有任务位置参数,协议 profile 不接受应用选项。patch overlay 使用启动器持有的 `--patch`。新的非内置目标可以使用 `--from-default-profile <template>`,在启动或配置 dump 之前复制一个默认模板的 bundle 列表与 patch 重载策略。这会创建依赖为空、用户 patch 为空的独立 profile:它既不读取与模板同名的本地 profile,也不记录继承关系。launcher 会以独占方式领取完整的目标目录,因此既有状态和并发创建者都会在不作修改的情况下失败。`dsh plugin --profile <name> <args...>` 是一层薄薄的 pnpm 转发器,负责初始化一个以 base 为基础的 profile,并依据已安装包的组合包声明调和 `dsh.profile.bundles`;没有组合包声明的包保持为普通依赖。[Headless 作为直接 core 入口](../../archived/architecture/2026-08-09-headless-direct-core-entry-point.md)负责 headless 组合约定。
默认 Profile 模板为 `web`、`headless`、`sdk` 与 `acp` 使用 `@deepseek-ai/dsh-base` 作为共享核心,并在其上叠加一个模式组合包。[独立 `sdk-minimal` profile](../../../../packages/bundle/sdk-minimal/README.zh.md)则只列出一个拥有完整显式配置树的组合包。通用的 `dsh --profile <name>` 把剩余参数交给该 profile 的命令行启动行:Web 持有自己的 flag 家族,headless 持有任务位置参数,协议 profile 不接受应用选项。patch overlay 使用启动器持有的 `--patch`。非内置目标可以使用 `--from-default-profile <template>`,在启动或配置 dump 之前,用一个默认模板的 bundle 列表与 patch 重载策略的副本替换自身。这会创建依赖为空、用户 patch 为空的独立 profile:它既不读取与模板同名的本地 profile,也不记录继承关系。launcher 在目标旁的暂存目录中组装副本并重命名到目标位置,因此重复执行 rescue 会重置该 profile;重命名失败时会恢复原有目录。`dsh plugin --profile <name> <args...>` 是一层薄薄的 pnpm 转发器,负责初始化一个以 base 为基础的 profile,并依据已安装包的组合包声明调和 `dsh.profile.bundles`;没有组合包声明的包保持为普通依赖。[Headless 作为直接 core 入口](../../archived/architecture/2026-08-09-headless-direct-core-entry-point.md)负责 headless 组合约定。
解析在构造上就是双锚点的:`dsh.profile.bundles` 中的名称先从 dsh 安装目录解析,再从 profile 目录解析,因此内置组合包始终来自与运行中 `dsh` 相同的安装,pnpm 从不管理它们。patch 行中的裸插件名称使用[不可变 runtime resolution](2026-09-09-profile-resolution-generations.zh.md),在内存中应用安装优先与有序 bundle 规则,并从包的真实目录展开依赖。
@@ -9,14 +9,14 @@
en: 1fe344c95a0de36d
zh: 656c3e0f1117f64e
/agent-note-forked-children-preserve-the-parent-request-prefix/decision:
en: b70aafc9ce30dd00
zh: 74e3ff915bd968a9
en: 3f5b3a4646c9466b
zh: 690172d6a4025b2a
/agent-note-forked-children-preserve-the-parent-request-prefix/alternatives-considered:
en: 80504ffd8331e6c5
zh: 193261bdde24b4bb
en: bc437309b64997ab
zh: 947010eed8d6fac4
/agent-note-forked-children-preserve-the-parent-request-prefix/consequences:
en: 0738d36f17c889b5
zh: b71ee47f75d6d4e0
en: 0f4d735328b3211d
zh: db4adb0f46b33bba
/agent-note-forked-children-preserve-the-parent-request-prefix/consequences/accepted-risks:
en: f634caa9973eaedb
zh: 820291096c8e7352
en: 72f4913b603e8596
zh: 50924c62200070d1
@@ -18,7 +18,7 @@ The model-facing `send_message` tool is registered globally for every Agent in a
The base bundle and the `cordis`, `standard`, and `ptc` CLI presets bind fork to the continuable lifecycle. Base-backed profiles, including headless, inherit that default. `ForkInProcessProvider.prepareContinuable()` and `ctx.subagents.startActivation()` preserve the same request-prefix behavior across these compositions.
Byte-identical prefix reuse is qualified by explicit deployment choices. A fork delegation that applies a child persona or `toolFilter` may still change the request head. In particular, filtering out `send_message` removes both the schema and the return guidance from the child; the runtime does not bypass an explicit allow-list.
Byte-identical prefix reuse depends on the assembled inputs. Structured output or generated SDK differences can change the request head. Historical child descriptors can also restore persona or tool restrictions during cold resume; filtering out `send_message` removes both its schema and return guidance. New fork starts inherit the parent preset without these override fields.
## Alternatives considered
@@ -28,11 +28,11 @@ Byte-identical prefix reuse is qualified by explicit deployment choices. A fork
**Add the return instruction to the system prompt.** This would place child-only bytes ahead of inherited messages. Appending it to the initial user task preserves the inherited prefix and keeps the parent id next to the task that needs it.
**Ignore an explicit child `toolFilter`.** Structural return tools previously bypassed the child allow-list. Rejected because a declared tool restriction must determine both schema visibility and guidance; hidden authority would make the model-facing roster inaccurate.
**Ignore a historical child `toolFilter` on resume.** A stored restriction must continue to determine both schema visibility and guidance; silently discarding it changes the restored agent’s behavior.
## Consequences
- Parent and continuable-fork child expose byte-identical ordered tool schemas when the delegation does not request a persona or tool filter.
- Parent and continuable-fork child expose byte-identical ordered tool schemas when they use the same preset and have no child-only structured output or restored historical overrides.
- The inherited Session seed precedes the child's initial task and return guidance.
- The base bundle and full CLI presets use local continuable fork for both parent and caller delivery, without a child-only request-head addition.
- The child can send messages to its direct parent through `send_message`. Parent delivery adds a separate runtime settlement notice; caller delivery returns the result through the activation receipt.
@@ -40,4 +40,4 @@ Byte-identical prefix reuse is qualified by explicit deployment choices. A fork
### Accepted risks
Provider-side prefix reuse still depends on the selected provider and model and on the absence of explicit persona or tool-filter differences. The harness proves equality of its assembled request-head inputs, not a provider's cache behavior.
Provider-side prefix reuse still depends on the selected provider and model and matching assembled inputs. The harness tests equality of its assembled request-head inputs, not a provider’s cache behavior.
@@ -18,7 +18,7 @@ fork 与 spawn 的差异在于:fork 会用 parent 已完成轮次的前缀作
base 组合包与 `cordis`、`standard` 和 `ptc` CLI preset 将 fork 绑定为可继续生命周期。基于 base 的 profile(包括 headless)继承这一默认值。`ForkInProcessProvider.prepareContinuable()` 与 `ctx.subagents.startActivation()` 在这些组合中保持相同的请求前缀行为。
逐字节相同的前缀复用受显式部署选择约束。配置 child persona 或 `toolFilter` 的 fork 委派仍可能改变请求头。尤其是过滤掉 `send_message` 时,child 会同时失去该 schema 与返回指引;运行时不会绕过显式 allow-list。
逐字节相同的前缀复用取决于组装输入。结构化输出或生成 SDK 差异可以改变请求头。历史子级 descriptor 也可在冷恢复时恢复 persona 或工具限制;过滤掉 `send_message` 会同时移除其 schema 和返回指引。新 fork 启动继承父级 preset,不含这些覆盖字段。
## 考虑过的替代方案
@@ -28,11 +28,11 @@ base 组合包与 `cordis`、`standard` 和 `ptc` CLI preset 将 fork 绑定为
**把返回指令放进系统提示词。** 这会在继承消息之前加入 child-only 字节。将其追加到初始用户任务,可以保留继承前缀,并让 parent id 紧邻需要它的任务。
**忽略显式 child `toolFilter`。** 结构性返回工具过去会绕过 child allow-list。否决该方案,因为声明的工具限制必须同时决定 schema 可见性与指引;隐藏权限会让面向模型的工具清单失真。
**恢复时忽略历史子级 `toolFilter`。** 存储的限制必须继续决定 schema 可见性和指引;静默丢弃它会改变恢复 agent 的行为。
## 后果
- 未请求 persona 或工具过滤时,parent 与可继续 fork child 暴露逐字节相同且顺序一致的工具 schema。
- 使用相同 preset 且没有子级专属结构化输出或恢复的历史覆盖时,父级与可继续的 fork 子级公开逐字节相同、顺序一致的工具 schema。
- 继承的 Session 种子位于 child 初始任务及其返回指引之前。
- base 组合包与完整 CLI preset 对 parent 和 caller 投递均使用本地可继续 fork,不增加 child-only 请求头字段。
- child 可以通过 `send_message` 向直接 parent 发消息。parent 投递会追加独立的运行时结算通知;caller 投递通过 activation 回执返回结果。
@@ -40,4 +40,4 @@ base 组合包与 `cordis`、`standard` 和 `ptc` CLI preset 将 fork 绑定为
### 已接受风险
提供方侧前缀复用仍取决于选定的提供方和模型,以及是否不存在显式 persona 或工具过滤差异。harness 证明的是自己组装出的请求头输入相等,而不是提供方的缓存行为。
提供方侧前缀复用仍取决于所选提供方和模型,以及组装输入是否相同。harness 测试的是自身组装请求头输入相等,而非提供方的缓存行为。
@@ -18,5 +18,5 @@
en: ca7c9088d0793e1e
zh: ea648bac9e726c64
/agent-note-what-stays-host-plane-once-presets-own-the-agent-plane/consequences:
en: de26cde94adda770
zh: 7f39760b77958966
en: 0a426923c2a1f774
zh: 29626804b99b35ff
@@ -38,6 +38,6 @@ Projection key presence is not a per-session capability signal ([session project
## Consequences
The context meter becomes a per-session fact instead of a function of mount history. A preset can no longer opt out of token accounting; no shipped preset did, and `minimal` now says it drops auto-compaction rather than the accounting.
The context meter is a per-session fact independent of mount history. Presets resolve the host meter for token accounting while selecting their own compaction backend; `minimal` supports both automatic compaction and the human `/compact` command.
Production entry points must compose Agents explicitly; installing the registry alone does not bind them. An unjoined Agent receives no tools, prompt sections, or skills and reports no error.
@@ -38,6 +38,6 @@ Status: implemented
## 后果
context meter 成为逐会话的事实,而不再是挂载历史的函数。代价是 preset 不能再选择不做 token 记账;随附的 preset 没有一个这么做,`minimal` 现在也写明它放弃的是自动压缩而非记账。
context meter 是独立于挂载历史的逐会话事实。Preset 使用宿主 meter 进行 token 记账,并选择自己的压缩后端;`minimal` 同时支持自动压缩和用户 `/compact` 命令。
生产入口必须显式组装 Agent;仅安装注册表不会自动绑定。未加入的 Agent 不会获得工具、提示词段落或 skill,也不会报告错误。
@@ -15,8 +15,8 @@
en: a1750de2d48551af
zh: ffea0b75c292f186
/agent-note-client-shell-layering-and-dynamic-package-boundaries/decision/shared-module-requests:
en: cedddcca8a7bae6d
zh: a568a1130229f48c
en: 96661da17622132a
zh: dab3424093130c2f
/agent-note-client-shell-layering-and-dynamic-package-boundaries/decision/parser-preloading-and-react-handoff:
en: 4b503618b271fc28
zh: e2148f44efa769d2
@@ -32,7 +32,7 @@ The `staticLinked` preset leaves every bare specifier as an external import in `
### Shared module requests
Dynamic browser bundles implicitly externalize the common baseline: `PLATFORM_MODULES` names shell-seeded React, Cordis, and static UI identities, while `PRELOADED_CLIENT_EXTERNALS` is reserved for a dynamic identity that must arrive before shell boot and is currently empty. A package uses `dsh.client.external` only for an exact non-baseline value request. Type-only imports are erased and create no request; permitted third-party implementation libraries remain private bundle contents.
Dynamic browser bundles implicitly externalize the common baseline: `PLATFORM_MODULES` names shell-seeded React, Cordis, selected third-party libraries, and static UI identities, while `PRELOADED_CLIENT_EXTERNALS` is reserved for a dynamic identity that must arrive before shell boot and is currently empty. A package uses `dsh.client.external` only for an exact non-baseline value request. Type-only imports are erased and create no request; non-baseline third-party implementation libraries remain private bundle contents. Shared third-party libraries use the shell's declared versions without per-plugin version negotiation; promotion into the baseline requires a measured reduction across the shell and shipped plugin bundles.
A request has exactly two suppliers:
@@ -32,7 +32,7 @@ Client npm 依赖区段描述安装和开发关系,但不能可靠描述 bundl
### 共享模块请求
动态浏览器 bundle 会隐式 external 统一基座:`PLATFORM_MODULES` 命名由外壳播种的 React、Cordis 和静态 UI 身份,`PRELOADED_CLIENT_EXTERNALS` 则为必须先于 shell 启动到达的动态身份预留,当前为空。包只在精确请求基座外实体时使用 `dsh.client.external`。纯类型 import 会被擦除,不产生请求;允许的第三方实现库保留为 bundle 私有内容。
动态浏览器 bundle 会隐式 external 统一基座:`PLATFORM_MODULES` 命名由外壳播种的 React、Cordis、选定的第三方库和静态 UI 身份,`PRELOADED_CLIENT_EXTERNALS` 则为必须先于 shell 启动到达的动态身份预留,当前为空。包只在精确请求基座外实体时使用 `dsh.client.external`。纯类型 import 会被擦除,不产生请求;基座外的第三方实现库保留为 bundle 私有内容。共享第三方库使用外壳声明的版本,不为单个插件协商版本;只有测量确认外壳与随包插件 bundle 的总体积减少,才将库纳入基座。
请求只有两种提供方:
@@ -9,11 +9,11 @@
en: 9f34c53e647c69ca
zh: 551776c7cc9618b5
/agent-note-client-derived-presentation-from-raw-session-tool-events/decision:
en: bdbb99c01b4c4db0
zh: 570040471ad1f7f4
en: 5fcc5a3de9f757cd
zh: a693cefdb82784ee
/agent-note-client-derived-presentation-from-raw-session-tool-events/decision/goals-and-non-goals:
en: 7e7a937dc4fe1b18
zh: 530f508b7e17b607
en: 40aceb58d71303b5
zh: 6954e41ae2fd1efd
/agent-note-client-derived-presentation-from-raw-session-tool-events/terminology:
en: 021106dd41176715
zh: 131caff7bd37251b
@@ -72,11 +72,11 @@
en: f40f0a8ee8c055c0
zh: 6957b7372956113c
/agent-note-client-derived-presentation-from-raw-session-tool-events/client-card-model-design/generic-tool-row:
en: 20ff82b092325c5c
zh: 1afd5441a9816117
en: d336c77d1b09f349
zh: 6e613f9c9108058f
/agent-note-client-derived-presentation-from-raw-session-tool-events/client-card-model-design/terminal-card:
en: eab7f366df331b6e
zh: d82499e2a982206f
en: 492669c2134169b2
zh: 0247a38df2dd8a83
/agent-note-client-derived-presentation-from-raw-session-tool-events/client-card-model-design/diff-card:
en: ea384415c94f82da
zh: 5f146c6cb534e1db
@@ -99,8 +99,8 @@
en: e5b42aa311079bd7
zh: ec4cf97037ece7e0
/agent-note-client-derived-presentation-from-raw-session-tool-events/presentation-equivalence-matrix:
en: 30915c26728eeafe
zh: eea04dea7b4604a5
en: 7974f20a6ac687ac
zh: cb4c350325a6da67
/agent-note-client-derived-presentation-from-raw-session-tool-events/client-extension-contract:
en: 96619dfd5ef299cf
zh: 253df98eb42ad75e
@@ -22,7 +22,7 @@ The required result is one raw Session journal and one Client presentation owner
## Decision
The visual-equivalence requirements below exclude the separately approved [nested terminal-card fix](../../../../packages/client/ui-tool/README.md) and [recorded tool details](../../../../packages/client/ui-tool/README.md); all other presentation and ownership constraints remain.
The visual-equivalence requirements below exclude the separately approved [nested terminal-card fix](../../../../packages/client/ui-tool/README.md), [recorded tool details](../../../../packages/client/ui-tool/README.md), and [background Shell command display](../bug-fix/2026-10-10-background-shell-command-display.md); all other presentation and ownership constraints remain.
The Session Remote journal sends only raw, validated, persistable Session events. `session.page` and `session.follow` do not parse tool arguments, query the Tools registry, restore a presenter scope, execute `presentCall` or `presentResult`, or construct or clone any tool view.
@@ -51,7 +51,7 @@ The Host `ToolDefinition.presentCall`, `ToolDefinition.presentResult`, `ToolCall
| Retained | the Session log format, Remote journal lifecycle, and Conversation identity/topology |
| Retained | the existing keyed slot, Generic fallback, and Chat, Details, and Trajectory structure |
| Forbidden | a new Client presenter service, parallel registry, or wire renderer id |
| Forbidden | new cards, visual redesign, interaction redesign, or PTC dispatch rich-card enhancements except the [nested terminal-card exception](../../../../packages/client/ui-tool/README.md) and [recorded tool details](../../../../packages/client/ui-tool/README.md) |
| Forbidden | new cards, visual redesign, interaction redesign, or PTC dispatch rich-card enhancements except the [nested terminal-card exception](../../../../packages/client/ui-tool/README.md), [recorded tool details](../../../../packages/client/ui-tool/README.md), and [background Shell command display](../bug-fix/2026-10-10-background-shell-command-display.md) |
| Forbidden | compatibility dual-writing, version negotiation, or retention of the old `view` field |
## Terminology
@@ -294,15 +294,16 @@ Unknown tools continue to use `GenericToolCard` with the name, raw arguments, re
The title, kind, rawInput, content, and locations from Generic Host `presentCall` do not currently drive an ordinary Web row. Generic `presentResult.content` also does not drive Web output, so the Client need not copy these unconsumed values.
<a id="terminal-card"></a>
### Terminal card
The Client terminal model derives existing `TerminalBlock` props from the tool name, call arguments, result content, error, and Session cwd, independently of `parentCallId`.
The Client terminal model derives existing `TerminalBlock` props from the tool name, call arguments, result content, error, and Session cwd, independently of `parentCallId`. [Background Shell command display](../bug-fix/2026-10-10-background-shell-command-display.md) governs explicit background `bash`/`pwsh` launches; the remaining ownership and fallback rules below stay in force.
| Input | Preserved result |
|---|---|
| running standard `bash`/`pwsh` foreground call | terminal prompt, description, cwd, and running state |
| successful standard foreground call | terminal output, exit code/signal, and settled state presentation |
| `run_in_background:true` | Generic row and raw result |
| explicit background standard `bash`/`pwsh` | original `background` flag with call `running` at Start; acknowledgement clears `running` and retains a static command without output or job status |
| tool execution error | Generic IN/OUT and error summary |
| running persistent `bash`/`pwsh` | terminal prompt |
| settled persistent `bash`/`pwsh` | Generic flattened result, with no new exit card |
@@ -310,7 +311,7 @@ The Client terminal model derives existing `TerminalBlock` props from the tool n
| background/error `terminal_send` | Generic result |
| PTC dispatch child | same terminal eligibility and fallback rules as a root call |
Standard shell results parse trailing `[exit code: N]` and `[killed by signal: X]` markers. A final recognized spill-policy notice selects Generic output instead: expandable in shell rows and raw in Details, because the exit marker may be displaced or omitted. A parsed marker is removed from the terminal body; timeout, sandbox denial, and markers without a pill remain in the body.
Foreground standard shell results parse trailing `[exit code: N]` and `[killed by signal: X]` markers; explicit background launch acknowledgements do not enter output or exit-status parsing. A final recognized spill-policy notice selects Generic output instead: expandable in shell rows and raw in Details, because the exit marker may be displaced or omitted. A parsed marker is removed from the terminal body; timeout, sandbox denial, and markers without a pill remain in the body.
Call `description` remains above the card and overrides the collapsed summary. Workdir continues handling absolute, relative, and missing values. Relative paths resolve against the Session cwd while preserving normalization for `.`, `..`, drive letters, and UNC roots.
@@ -318,7 +319,7 @@ For `terminal_send`, non-empty input and the session id remain verbatim tool dat
Standard and persistent providers sharing the same tool name are a special compatibility point. The Client uses currently valid argument and result features to preserve their delivered differences. Input that cannot be identified unambiguously uses a Generic settled result rather than gaining new presentation.
`TerminalBlock` ANSI handling, cursor replay, wide characters, line limits, expansion, copying, and assistive text remain unchanged.
Outside the explicit background command-only presentation, `TerminalBlock` ANSI handling, cursor replay, wide characters, line limits, expansion, copying, and assistive text remain unchanged.
### Diff card
@@ -412,7 +413,7 @@ The scenario does not import Host tool packages to compute page presentation and
| orphan result | callId title and Generic output |
| interrupted call | warning/stopped state |
| foreground bash/pwsh | current terminal prompt, body, cwd, and state |
| background/error bash/pwsh | current Generic IN/OUT |
| explicit background bash/pwsh | terminal Start and static command-only acknowledgement; launch errors retain Generic IN/OUT |
| persistent shell | current running terminal and settled Generic form |
| terminal_send | current foreground terminal and background/error Generic form |
| write/edit | current intended/applied diff and error fallback |
@@ -22,7 +22,7 @@ Host presenter 与 Client keyed renderer 分担展示会形成对同一事件的
## Decision
下述展示对等要求不包含已独立批准的[嵌套 terminal 卡片修复](../../../../packages/client/ui-tool/README.zh.md)和[记录结果的工具详情](../../../../packages/client/ui-tool/README.zh.md);其他展示与所有权约束全部保留。
下述展示对等要求不包含已独立批准的[嵌套 terminal 卡片修复](../../../../packages/client/ui-tool/README.zh.md)、[记录结果的工具详情](../../../../packages/client/ui-tool/README.zh.md)和[后台 Shell 命令展示](../bug-fix/2026-10-10-background-shell-command-display.zh.md);其他展示与所有权约束全部保留。
Session Remote journal 只下发原始、已验证、可持久化的 Session event。`session.page` 和 `session.follow` 不解析工具参数,不查询 Tools registry,不恢复 presenter scope,不执行 `presentCall`/`presentResult`,也不构造或克隆任何 tool view。
@@ -51,7 +51,7 @@ Host 的 `ToolDefinition.presentCall`、`ToolDefinition.presentResult`、`ToolCa
| 保留 | Session 日志格式、Remote journal 生命周期与 Conversation identity/topology |
| 保留 | 现有 keyed slot、Generic fallback、Chat、Details 与 Trajectory 结构 |
| 禁止 | 新 Client presenter service、平行 registry 或 wire renderer id |
| 禁止 | 新卡片、视觉改版、交互改版或 PTC dispatch rich-card 增强,[嵌套 terminal 卡片例外](../../../../packages/client/ui-tool/README.zh.md)和[记录结果的工具详情](../../../../packages/client/ui-tool/README.zh.md)除外 |
| 禁止 | 新卡片、视觉改版、交互改版或 PTC dispatch rich-card 增强,[嵌套 terminal 卡片例外](../../../../packages/client/ui-tool/README.zh.md)、[记录结果的工具详情](../../../../packages/client/ui-tool/README.zh.md)和[后台 Shell 命令展示](../bug-fix/2026-10-10-background-shell-command-display.zh.md)除外 |
| 禁止 | 为兼容保留双写、版本协商或旧 `view` 字段 |
## 术语
@@ -294,15 +294,16 @@ Chat 与 Details 通过纯 helper 复用同一 block 的解析。未来缓存必
Generic Host `presentCall` 的 title、kind、rawInput、content 与 locations 当前并不驱动普通 Web 行;Generic `presentResult.content` 也不驱动 Web 输出,因此无需把这些未消费值复制到 Client。
<a id="terminal-card"></a>
### Terminal 卡片
Client terminal model 从工具名称、调用参数、结果 content、error 与 Session cwd 派生现有 `TerminalBlock` props,不依赖 `parentCallId`。
Client terminal model 从工具名称、调用参数、结果 content、error 与 Session cwd 派生现有 `TerminalBlock` props,不依赖 `parentCallId`。[后台 Shell 命令展示](../bug-fix/2026-10-10-background-shell-command-display.zh.md)拥有主动后台 `bash`/`pwsh` 启动的展示决定;下述其余所有权和 fallback 规则继续有效。
| 输入 | 保持的结果 |
|---|---|
| 标准 `bash`/`pwsh` 前台 running | terminal prompt、description、cwd、running 状态 |
| 标准前台 success | terminal output、exit code/signal 与结算状态呈现 |
| `run_in_background:true` | Generic 行与原始结果 |
| 标准 `bash`/`pwsh` 主动后台调用 | Start 使用原 `background` 标志和调用 `running`;回执清除 `running` 后保留静态命令,不显示输出或任务状态 |
| 工具执行 error | Generic IN/OUT 与错误摘要 |
| persistent `bash`/`pwsh` running | terminal prompt |
| persistent `bash`/`pwsh` settled | Generic flattened result,不新增 exit card |
@@ -310,7 +311,7 @@ Client terminal model 从工具名称、调用参数、结果 content、error
| `terminal_send` background/error | Generic 结果 |
| PTC dispatch child | 与根调用相同的 terminal 适用规则与 fallback 规则 |
标准 shell 结果解析末尾 `[exit code: N]` 与 `[killed by signal: X]`。末尾已识别的 spill 策略提示会改用 Generic 输出:在 shell 行中可展开,在 Details 中显示原文,因为退出标记可能被移位或省略。已解析的 marker 从 terminal 正文移除;timeout、sandbox denial 与没有 pill 的 marker 留在正文。
标准前台 shell 结果解析末尾 `[exit code: N]` 与 `[killed by signal: X]`;主动后台启动回执不进入输出或退出状态解析。末尾已识别的 spill 策略提示会改用 Generic 输出:在 shell 行中可展开,在 Details 中显示原文,因为退出标记可能被移位或省略。已解析的 marker 从 terminal 正文移除;timeout、sandbox denial 与没有 pill 的 marker 留在正文。
调用 `description` 继续显示在 card 上方并覆盖折叠摘要。workdir 继续按绝对、相对和缺失三种情况处理;相对路径基于 Session cwd,且保留 `.`、`..`、盘符与 UNC root 的归一化。
@@ -318,7 +319,7 @@ Client terminal model 从工具名称、调用参数、结果 content、error
同名普通与 persistent provider 是特殊兼容点。Client 使用当前有效参数与结果特征保留已交付差异;不足以无歧义识别的输入选择 Generic settled 结果,不增加新表现。
TerminalBlock 的 ANSI、光标重放、宽字符、行数上限、展开、复制与辅助技术文本完全不变。
除主动后台的仅命令展示外,TerminalBlock 的 ANSI、光标重放、宽字符、行数上限、展开、复制与辅助技术文本保持不变。
### Diff 卡片
@@ -412,7 +413,7 @@ Deliverables Definition 按 callId 观察 root `tool/call` 与成功 `tool/resul
| orphan result | callId 标题与 Generic output |
| interrupted call | warning/stopped 状态 |
| bash/pwsh 前台 | 当前 terminal prompt、正文、cwd 与状态 |
| bash/pwsh background/error | 当前 Generic IN/OUT |
| bash/pwsh 主动后台调用 | terminal Start 与静态仅命令回执展示;启动错误保留 Generic IN/OUT |
| persistent shell | 当前 running terminal、settled Generic |
| terminal_send | 当前前台 terminal、后台/error Generic |
| write/edit | 当前 intended/applied diff 与 error fallback |
@@ -9,8 +9,8 @@
en: 01293500639c9d79
zh: 6d19e4843955c811
/agent-note-browser-launch-token-authentication/decision:
en: 54efa59339f75256
zh: 0f53ead3844151c6
en: 51885594bdeb6f0c
zh: 9c2e41ad79ccdd08
/agent-note-browser-launch-token-authentication/verification:
en: 189b36d0fc5f3a17
zh: 08993cb585204fc0
@@ -10,6 +10,8 @@ The Web Host runs tool-capable Sessions with the current operating-system user's
## Decision
Browser callers follow this decision; independent CLI/MCP admission is owned by the [API connect decision](2026-10-09-operator-scoped-connect-handshake.md).
`dsh-client-connection` authenticates the complete Host API before dispatch. Every API Proxy method, Remote unary call, generic Connection channel, and Remote WebSocket stream requires the same browser session; endpoint ownership and method names do not alter authority. The existing Host/Origin checks run first and retain their DNS-rebinding and cross-site-request role, returning 403 when they fail. A trusted Host without a valid browser session receives 401. The browser-trust rules remain owned by the [carrier-level browser trust decision](2026-07-28-api-browser-trust-boundary.md).
Each Host process generates a random launch token, retained by the root application context across Connection hot reloads. `dsh-web-app` prints and opens the normal root URL with that token in the query once per process. `frontend-static` asks Connection to authorize index responses: only `GET /?token=...` exchanges the process token for a cookie, then redirects to clean `./`; the token is not accepted on API paths or in an Authorization header. An obsolete token paired with a valid cookie redirects to clean `./`. Missing and invalid credentials receive one minimal 401 response. Static non-index assets remain public.
@@ -10,6 +10,8 @@ Web Host 以当前操作系统用户的权限运行具有工具能力的 Session
## 决策
浏览器调用方遵循本决策;独立 CLI/MCP 接纳由 [API connect 决策](2026-10-09-operator-scoped-connect-handshake.zh.md)定义。
`dsh-client-connection` 在分发前认证完整 Host API。每个 API Proxy 方法、Remote 一元调用、通用 Connection channel 和 Remote WebSocket stream 都要求同一个浏览器会话;endpoint 所有权与方法名称不改变 authority。既有 Host/Origin 校验先执行,继续负责 DNS rebinding 和跨站请求防御,失败时返回 403。Host 可信但没有有效浏览器会话时返回 401。浏览器信任规则仍由[载体级浏览器信任决策](2026-07-28-api-browser-trust-boundary.zh.md)持有。
每个 Host 进程生成随机启动令牌,并由应用根 context 跨 Connection 热重载保留。`dsh-web-app` 每个进程只打印并打开一次 query 中带该令牌的普通根 URL。`frontend-static` 请求 Connection 授权 index 响应:只有 `GET /?token=...` 会把进程令牌交换为 cookie,再重定向到干净的 `./`;API 路径和 Authorization header 都不接受该令牌。过时令牌如果同时带有有效 cookie,会重定向到干净的 `./`。缺失与无效凭据得到同一份最小 401 响应。非 index 静态资产保持公开。
@@ -21,11 +21,11 @@
en: 29def175ab33b7d6
zh: 87e55ec28c20c7c1
/agent-note-package-and-update-the-electron-desktop-application/updates-and-recovery:
en: e9f140cd22670dfa
zh: bf85cfe9c12a0345
en: 4cb7014acfe71762
zh: 2178e0337cba0324
/agent-note-package-and-update-the-electron-desktop-application/security-and-release-policy:
en: 97ee608e96fc3602
zh: 932b98e7d344cd87
en: 5faaa53b58121311
zh: e1c3fc8d21a27d1a
/agent-note-package-and-update-the-electron-desktop-application/implementation:
en: 041a6d35ed61b149
zh: 617c6b90a6f4fa4d
@@ -75,13 +75,13 @@ The [immediate-window reference](../../../../apps/desktop/README.md) owns the lo
`DSH_DESKTOP_AUTO_UPDATE_ENV` selects the test deployment by default or the production deployment for both the target-specific generic-provider URL and COS destination. Release automation supplies the test HTTPS origin through `DOWNLOAD_TEST_ORIGIN` and each deployment's bucket through `DOWNLOAD_TEST_COS_BUCKET` or `DOWNLOAD_PROD_COS_BUCKET`; keeping mutable test routing and COS storage identities out of source lets deployment infrastructure change without a code release, while the public production origin remains fixed. Packaging resolves only the public updater URL, disables electron-builder publishing, removes every COS credential field from its subprocess environment, and writes a completion record only after electron-builder and every signing or notarization hook succeeds. Target upload additionally requires the selected bucket, then requires the completion record, root dsh version, Desktop version, version-derived channel metadata, artifact names, sizes, and SHA-512 values to agree before it reads the selected credentials or sends data. It uploads immutable versioned updater payloads and any separate blockmaps before replacing the channel metadata emitted by electron-builder, and it never deletes historical objects. Stable versions use the `latest` metadata name; prereleases use the first semantic-version prerelease identifier. NSIS embeds its blockmap in the signed executable; the macOS ZIP carries a separate blockmap. Both let electron-updater download changed blocks when supported, while application replacement and the local pnpm package operation remain separate operations.
Test releases require `DOWNLOAD_TEST_RELEASE_ID` from the platform dotenv file. Their feeds and binaries share `dsh-desk/<release-id>/`; production retains its fixed directories. The ID is part of the packaged update URL, so upload rejects a different ID. Internal distribution can supply a new installer after rotation; installed clients retain their existing feed. Random directories reduce guessing but provide no authorization for link holders. The [release configuration](../../../../apps/desktop/README.md#upload-updates) defines generation and reuse.
Test releases require `DOWNLOAD_TEST_RELEASE_ID` from the platform dotenv file. Their feeds and binaries share `dsh-desk/<release-id>/`; production retains its fixed directories. The ID is part of the packaged update URL, so upload rejects a different ID. Internal distribution can supply a new installer after rotation; installed clients retain their existing feed. Random directories reduce guessing but provide no authorization for link holders. The [release configuration](../../../../apps/desktop/publishing/README.md#upload-updates) defines generation and reuse.
## Security and release policy
Core dsh and the private Desktop Host come only from the signed application resource tree. Plugin installation forwards package specs to pnpm, including local and remote sources, but never accepts raw pnpm commands. pnpm owns dependency resolution and the profile’s `allowBuilds` policy; the Host loads activated bundles.
Electron release artifacts are signed; macOS artifacts are notarized. Release package and upload commands read release settings from the Git-ignored target `.env.windows` or `.env.macos`; subprocesses receive the fields selected by orchestration. The target file exclusively owns release fields so stale shell or system credentials cannot override local selection, and loading does not mutate the parent environment. Packaging validates the mode-specific application ID, update origin, signing identity, and local files before builds, downloads, or release-record cleanup; macOS also requires one complete notarization strategy. The separate `check:package` command runs the same validation without accessing the token or Apple; actual signing and notarization still verify authentication. Configuration loading rejects missing or malformed identifiers and incomplete notarization credentials, while macOS release packaging requires signing so certificate discovery cannot silently select another installed identity or emit an unsigned release. Runtime preparation verifies the exact Authority and Team ID plus the timestamp and hardened-runtime flags on every embedded Mach-O file. An after-sign hook performs Apple's deep strict application verification and requires the same leaf Authority and Team ID before artifact creation continues. The fixed-target installer command uses [isolated App copies for parallel notarization](../process/2026-09-09-parallel-macos-notarization.md): the ZIP contains a stapled App, while the signed DMG carries the ticket covering its unstapled inner App. The DMG artifact-completion hook requires the configured identity, a valid ticket, and Gatekeeper acceptance. Both artifact lanes must succeed before the command promotes their outputs and writes the release completion record; directory-only commands still notarize and staple the App. DMG blockmaps are disabled because macOS updates consume the signed ZIP, and stapling would otherwise invalidate an already-generated DMG blockmap.
Electron release artifacts are signed; macOS artifacts are notarized. Release package and upload commands read release settings from the Git-ignored target `.env.windows` or `.env.macos`; subprocesses receive the fields selected by orchestration. Windows packaging can instead select a machine-owned public `presets/<preset>.env`, plus `secrets/windows-signing.env` for signed builds; unsigned and preparation-only modes omit the signing file. The selected files exclusively own release fields so stale shell or system credentials cannot override local selection, and loading does not mutate the parent environment. Packaging validates the mode-specific application ID, update origin, signing identity, and local files before builds, downloads, or release-record cleanup; macOS also requires one complete notarization strategy. The separate `check:package` command runs the same validation without accessing the token or Apple; actual signing and notarization still verify authentication. Configuration loading rejects missing or malformed identifiers and incomplete notarization credentials, while macOS release packaging requires signing so certificate discovery cannot silently select another installed identity or emit an unsigned release. Runtime preparation verifies the exact Authority and Team ID plus the timestamp and hardened-runtime flags on every embedded Mach-O file. An after-sign hook performs Apple's deep strict application verification and requires the same leaf Authority and Team ID before artifact creation continues. The fixed-target installer command uses [isolated App copies for parallel notarization](../process/2026-09-09-parallel-macos-notarization.md): the ZIP contains a stapled App, while the signed DMG carries the ticket covering its unstapled inner App. The DMG artifact-completion hook requires the configured identity, a valid ticket, and Gatekeeper acceptance. Both artifact lanes must succeed before the command promotes their outputs and writes the release completion record; directory-only commands still notarize and staple the App. DMG blockmaps are disabled because macOS updates consume the signed ZIP, and stapling would otherwise invalidate an already-generated DMG blockmap.
The [pinned osx-sign patch](../../../../patches/@[email protected]) uses `lstat` in both published module builds, so Framework file and directory aliases do not trigger duplicate signing. The patch remains necessary until the selected upstream release skips those aliases. PAK files are resources sealed by the enclosing bundle; individual signatures add serial timestamp requests without additional resource integrity. Desktop preserves all locale files and skips only their standalone signatures. Executable code retains Developer ID signatures, secure timestamps, and hardened runtime. The [signer traversal regression](../../../../apps/desktop/tests/macos-signing-walk.spec.ts) exercises the installed dependency with real Framework aliases; release qualification still requires strict application verification, notarization, and startup.
@@ -91,7 +91,7 @@ Windows package invocations force `ELECTRON_BUILDER_7Z_FILTER=BCJ`. The bundled
Local Windows and macOS installation testing uses an explicit `--unsigned` package invocation with the same build and runtime preparation. It strips certificate inputs, isolates artifacts in `unsigned-artifacts`, and omits updater configuration and the release completion record. macOS local builds ignore dotenv files and inherited release settings, use the standard application ID, and omit mandatory-update policy so local testing needs no deployment configuration. Their App receives a certificate-free ad-hoc signature; their DMG is unsigned and unnotarized. Windows local builds retain their platform configuration requirements. The regular package command explicitly selects signed mode even when its parent environment requests unsigned mode. This separation permits installation diagnosis without release credentials while preventing local test output from qualifying for release upload.
Windows application replacement follows the [directory-installation reference](../../../../apps/desktop/README.md): extract beside the destination with a command-line tool that returns failure status, then rename complete directories on the same volume. The installer retains the old directory through staging and restores it if promotion fails. Registration, shortcuts, and the signed uninstaller remain owned by electron-builder.
Windows application replacement follows the [directory-installation reference](../../../../apps/desktop/packaging/README.md#runtime-file-selection): extract beside the destination with a command-line tool that returns failure status, then rename complete directories on the same volume. The installer retains the old directory through staging and restores it if promotion fails. Registration, shortcuts, and the signed uninstaller remain owned by electron-builder.
Packaged applications ignore development resource and project environment overrides. Only an unpackaged Electron process can replace the pnpm entry, dsh resources, or active project.
@@ -75,13 +75,13 @@ Electron 更新只使用一个 `electron-updater` 发布流和签名 `electron-b
`DSH_DESKTOP_AUTO_UPDATE_ENV` 默认为测试部署,也可以选择生产部署,并同时决定目标专用的 generic-provider URL 与 COS 目标。发布自动化通过 `DOWNLOAD_TEST_ORIGIN` 提供测试 HTTPS origin,并通过 `DOWNLOAD_TEST_COS_BUCKET` 或 `DOWNLOAD_PROD_COS_BUCKET` 提供各部署的 bucket;可变的测试路由与 COS 存储身份不写入源码,部署基础设施变更时无需发布新代码,而公开的生产 origin 仍固定。打包只解析公开更新 URL、禁止 electron-builder 发布、从子进程环境中删除每个 COS 凭据字段,并且只有在 electron-builder 以及每个签名或公证 hook 成功后才写入完成记录。目标上传还必须提供所选 bucket,随后会先要求完成记录、根 dsh 版本、Desktop 版本、根据版本得出的频道元数据、产物名称、大小与 SHA-512 全部一致,再读取所选凭据或发送数据。它先上传不可变且带版本的更新载荷与所有独立 blockmap,最后替换 electron-builder 生成的频道元数据,并且不会删除历史对象。稳定版本使用 `latest` 元数据名称,预发布版本则使用语义化版本的第一个预发布标识符。NSIS 把 blockmap 嵌入已签名的可执行文件,macOS ZIP 则使用独立 blockmap;两者都让 electron-updater 在平台支持时只下载变化的数据块,而应用替换与本地 pnpm 包操作仍是两个独立操作。
测试发布必须从平台 dotenv 文件提供 `DOWNLOAD_TEST_RELEASE_ID`。清单和安装包共用 `dsh-desk/<release-id>/`,生产环境保留固定目录。ID 是打包更新 URL 的一部分,因此上传会拒绝不同的 ID。轮换后可通过内部分发提供新安装包,已安装客户端保留原清单地址。随机目录降低被猜中的概率,但不对持有链接者进行鉴权。[发布配置](../../../../apps/desktop/README.zh.md#upload-updates)定义生成与复用方式。
测试发布必须从平台 dotenv 文件提供 `DOWNLOAD_TEST_RELEASE_ID`。清单和安装包共用 `dsh-desk/<release-id>/`,生产环境保留固定目录。ID 是打包更新 URL 的一部分,因此上传会拒绝不同的 ID。轮换后可通过内部分发提供新安装包,已安装客户端保留原清单地址。随机目录降低被猜中的概率,但不对持有链接者进行鉴权。[发布配置](../../../../apps/desktop/publishing/README.zh.md#upload-updates)定义生成与复用方式。
## 安全与发布策略
核心 dsh 和私有 Desktop Host 只来自签名应用的资源树。插件安装把包规格交给 pnpm,包括本地和远程来源,但不接受原始 pnpm 命令。pnpm 负责依赖解析和 profile 的 `allowBuilds` 策略;Host 加载已启用的 bundle。
Electron 发布产物必须签名;macOS 产物必须公证。发布打包与上传命令从 Git 忽略的目标 `.env.windows` 或 `.env.macos` 读取发布配置,子进程通过编排器选择的环境字段接收配置。目标文件是发布字段的唯一来源,避免旧的 shell 或系统凭据覆盖本地选择;配置加载不修改父进程环境。打包在构建、下载或清理发布记录前校验该模式必需的应用 ID、更新地址、签名身份及本地文件,macOS 还要求一套完整公证凭据。单独的 `check:package` 执行同一校验而不访问 Token 或 Apple;凭据真实性仍由实际签名与公证验证。配置加载会拒绝缺失或格式错误的标识符和不完整的公证凭据,macOS 发布打包还会强制签名,避免证书发现过程静默选择其他已安装身份或生成未签名发布。运行时准备会验证每个内嵌 Mach-O 文件的精确 Authority 与 Team ID,以及时间戳和 hardened-runtime 标记。签名后钩子会执行 Apple 的深度严格应用验证,并要求同一叶证书 Authority 与 Team ID 完全匹配,验证通过后才继续生成产物。固定目标安装包命令使用[隔离的 App 副本并行公证](../process/2026-09-09-parallel-macos-notarization.zh.md):ZIP 包含已钉票的 App,签名 DMG 则携带覆盖其中未钉票 App 的票据。DMG 的 artifact-completion hook 要求其使用配置的身份、具备有效票据并通过 Gatekeeper。只有两条产物流都成功,命令才会移入其输出并写入发布完成记录;仅生成目录的命令仍会公证 App 并钉票。macOS 更新使用签名 ZIP,因此 DMG 不生成 blockmap;否则钉票会让已经生成的 DMG blockmap 失效。
Electron 发布产物必须签名;macOS 产物必须公证。发布打包与上传命令从 Git 忽略的目标 `.env.windows` 或 `.env.macos` 读取发布配置,子进程通过编排器选择的环境字段接收配置。Windows 打包也可选择机器管理的公开 `presets/<preset>.env`,签名构建另读 `secrets/windows-signing.env`;未签名和仅准备模式不读取签名文件。所选文件是发布字段的唯一来源,避免旧的 shell 或系统凭据覆盖本地选择;配置加载不修改父进程环境。打包在构建、下载或清理发布记录前校验该模式必需的应用 ID、更新地址、签名身份及本地文件,macOS 还要求一套完整公证凭据。单独的 `check:package` 执行同一校验而不访问 Token 或 Apple;凭据真实性仍由实际签名与公证验证。配置加载会拒绝缺失或格式错误的标识符和不完整的公证凭据,macOS 发布打包还会强制签名,避免证书发现过程静默选择其他已安装身份或生成未签名发布。运行时准备会验证每个内嵌 Mach-O 文件的精确 Authority 与 Team ID,以及时间戳和 hardened-runtime 标记。签名后钩子会执行 Apple 的深度严格应用验证,并要求同一叶证书 Authority 与 Team ID 完全匹配,验证通过后才继续生成产物。固定目标安装包命令使用[隔离的 App 副本并行公证](../process/2026-09-09-parallel-macos-notarization.zh.md):ZIP 包含已钉票的 App,签名 DMG 则携带覆盖其中未钉票 App 的票据。DMG 的 artifact-completion hook 要求其使用配置的身份、具备有效票据并通过 Gatekeeper。只有两条产物流都成功,命令才会移入其输出并写入发布完成记录;仅生成目录的命令仍会公证 App 并钉票。macOS 更新使用签名 ZIP,因此 DMG 不生成 blockmap;否则钉票会让已经生成的 DMG blockmap 失效。
[固定版本的 osx-sign 补丁](../../../../patches/@[email protected])在两种已发布模块构建中使用 `lstat`,因此 Framework 的文件和目录别名不会触发重复签名。选定的上游版本能够跳过这些别名前,仍需保留该补丁。PAK 文件由外层 bundle 签名记录完整性;逐个签名会增加串行时间戳请求,但不会增加资源完整性保护。Desktop 保留全部语言文件,只跳过其单独签名。可执行代码仍使用 Developer ID 签名、安全时间戳和 hardened runtime。[签名器遍历回归测试](../../../../apps/desktop/tests/macos-signing-walk.spec.ts)使用真实 Framework 别名执行已安装依赖;发布验收仍要求严格应用验证、公证和启动。
@@ -91,7 +91,7 @@ Windows 打包调用强制设置 `ELECTRON_BUILDER_7Z_FILTER=BCJ`。内置的 7-
本地 Windows 和 macOS 安装测试使用显式的 `--unsigned` 打包调用,并执行相同的构建和运行时准备。它清除证书输入,将产物隔离到 `unsigned-artifacts`,并省略更新器配置和发布完成记录。macOS 本地构建忽略 dotenv 文件和继承的发布配置,使用标准应用 ID,并省略强制更新策略,使本地测试无需部署配置。其 App 使用无需证书的 ad-hoc 签名,DMG 不签名也不公证。Windows 本地构建保留平台配置要求。即使父进程环境请求未签名模式,常规打包命令也会显式选择签名模式。这样既能在没有发布凭据时诊断安装问题,也能防止本地测试产物通过发布上传校验。
Windows 应用替换遵循[目录安装参考](../../../../apps/desktop/README.zh.md):使用能返回失败状态的命令行工具解压到目标旁边,再在同卷内改名替换完整目录。安装器在暂存期间保留旧目录,正式替换失败时恢复旧目录。注册信息、快捷方式和签名卸载器仍由 electron-builder 持有。
Windows 应用替换遵循[目录安装参考](../../../../apps/desktop/packaging/README.zh.md#runtime-file-selection):使用能返回失败状态的命令行工具解压到目标旁边,再在同卷内改名替换完整目录。安装器在暂存期间保留旧目录,正式替换失败时恢复旧目录。注册信息、快捷方式和签名卸载器仍由 electron-builder 持有。
打包应用会忽略开发资源和项目环境变量覆盖。只有未打包的 Electron 进程可以替换 pnpm 入口、dsh 资源 或活跃项目。
@@ -12,8 +12,8 @@
en: 1188c699ce6e6ca2
zh: 4d5b974cd1216f06
/agent-note-adjacent-agents-share-one-steer-send-message-operation/decision/one-model-tool-and-one-return-instruction:
en: 8a4be155a4984ca8
zh: 00c2a61da2443530
en: 4b0a25389324fa91
zh: c2cdd9f7e40ef43b
/agent-note-adjacent-agents-share-one-steer-send-message-operation/decision/complete-removal-and-reintroduction-condition:
en: 5a9ec6478cf7430c
zh: f4c2f067cc72040e
@@ -46,7 +46,7 @@ interface SendMessageInput {
}
```
Parents and children inherit the same definition in the same registry order. The standard definition carries a process-stable internal identity that a scoped same-name tool does not satisfy. A child `toolFilter` may explicitly remove the inherited tool, and a scoped replacement may provide different semantics; neither case receives the standard call instruction. When the standard tool remains visible, the continuation manager appends the JSON-encoded direct parent id and the instruction to send one self-contained result before finishing, plus earlier actionable findings, to the child's initial user task. For a fork child this task follows the inherited completed-turn prefix; no child-only system-prompt section or tool schema precedes that prefix.
Parents and children inherit the same definition in the same registry order. The standard definition carries a process-stable internal identity that a scoped same-name tool does not satisfy. An independently selected preset may omit the tool, a historical descriptor filter may remove it on resume, and a scoped replacement may provide different semantics; neither case receives the standard call instruction. When the standard tool remains visible, the continuation manager appends the JSON-encoded direct parent id and the instruction to send one self-contained result before finishing, plus earlier actionable findings, to the child's initial user task. For a fork child this task follows the inherited completed-turn prefix; no child-only system-prompt section or tool schema precedes that prefix.
The instruction is guidance, not settlement enforcement. Sending does not end the child's turn, zero or several calls remain mechanically valid, and the runtime never rejects a child for staying silent. The manager-owned `subagent-settled` notice remains unconditional and separately attributed because it records how an Activation ended and preserves terminal output when the child cannot cooperate.
@@ -46,7 +46,7 @@ interface SendMessageInput {
}
```
parent 与 child 以相同注册表顺序继承相同定义。标准定义携带进程稳定的内部身份,同名的作用域工具不满足该身份。child `toolFilter` 可以显式移除继承的工具,作用域替代工具也可以提供不同语义;两种情况都不会收到标准调用指令。当标准工具仍可见时,继续执行管理器会把经过 JSON 编码的直接 parent id、结束前发送一份自包含结果的指令,以及更早发送可操作发现的指令追加到 child 初始用户任务。对 fork child 而言,该任务位于继承的已完成轮次前缀之后;没有 child 专属系统提示词 section 或工具 schema 位于此前缀之前。
parent 与 child 以相同注册表顺序继承相同定义。标准定义携带进程稳定的内部身份,同名的作用域工具不满足该身份。独立选择的 preset 可以省略该工具,历史 descriptor 过滤器可在恢复时移除该工具,作用域替代工具也可以提供不同语义;两种情况都不会收到标准调用指令。当标准工具仍可见时,继续执行管理器会把经过 JSON 编码的直接 parent id、结束前发送一份自包含结果的指令,以及更早发送可操作发现的指令追加到 child 初始用户任务。对 fork child 而言,该任务位于继承的已完成轮次前缀之后;没有 child 专属系统提示词 section 或工具 schema 位于此前缀之前。
该指令是指导,不是结算强制。发送不会结束 child 轮次,机制仍允许零次或多次调用,runtime 绝不会因 child 保持沉默而拒绝它。由管理器负责的 `subagent-settled` 通知仍无条件发送并采用独立来源,因为它记录 Activation 如何结束,并在 child 无法配合时保留终态输出。
@@ -18,5 +18,5 @@
en: 5917a573cbdc5fa4
zh: 3c3769070539a0a9
/agent-note-handle-based-session-persistence/related:
en: 6b160ed89d2f91a7
zh: 23ee33d03847d0d0
en: 0ff54c05c2a64c07
zh: f3bd6a5d1f37563f
@@ -36,6 +36,7 @@ Resume, fork, subagent, ACP, webhook, and SDK sessions all persist through one e
## Related
- [Cleanup dependency lifetimes](../../proposed/architecture/2026-10-09-cordis-cleanup-dependency-lifetimes.md) proposes retaining final events emitted during graceful cleanup, tightening this decision's permitted root-disposal tail loss while preserving handle ownership and storage format.
- [Session persistence as an abstract service](../../../../packages/session/session-persistence/README.md) — the seam this reshapes; its interface list reflects the handle API.
- [Persistence export() and pre-release trims](../../archived/simplification/2026-08-27-persistence-export-and-pre-release-trims.md) — the preparatory removals, including the retired cold blank probe.
- [Retain ignorable external session events](2026-08-30-retain-ignorable-external-session-events.md) — the read-side refusal contract, now shared through `storage-contract` helpers.
@@ -36,6 +36,7 @@ Status: implemented
## 相关
- [清理依赖生命周期](../../proposed/architecture/2026-10-09-cordis-cleanup-dependency-lifetimes.zh.md)提议保留有序清理期间发出的最终事件,收紧本决定允许的根释放尾部丢失,同时保留句柄所有权与存储格式。
- [作为抽象服务的会话持久化](../../../../packages/session/session-persistence/README.zh.md)——本 Note 重塑的 seam;其接口列表已反映句柄 API。
- [持久化 export() 与预发布读取路径精简](../../archived/simplification/2026-08-27-persistence-export-and-pre-release-trims.md)——预备性的移除,包括已退役的冷空白探测。
- [保留可忽略的外部会话事件](2026-08-30-retain-ignorable-external-session-events.zh.md)——读取侧的拒绝约定,现经由 `storage-contract` 辅助函数共享。
@@ -15,5 +15,5 @@
en: 09be2a1cf1d52fd5
zh: 0bf0e18c73542012
/agent-note-one-output-ring-one-projection-one-event-stream-for-background-jobs/consequences:
en: 464eb843a71eb04e
zh: 783062f5b288b1b4
en: 3263801e173b6b86
zh: f6f51bb3f77a817e
@@ -35,3 +35,5 @@ The job seam had grown by accretion. Producers declared their output twice — a
The model-visible text is unchanged: status lines, tool descriptions and schemas, and notice wording are byte-identical, and the only new model-visible fact is the kill reason a `killed` status line now carries. A producer that needs to stream output picks pull sources or `append`; it never formats a consuming delta. The Web client renders every live job as expandable, so a job that never writes shows an empty running panel instead of a static row. `JobKindMap` merges are host-side, so a browser program types `JobView.kind` as the kinds it knows and treats the rest as opaque strings, which is all the roster does with it.
Review follow-ups after the master merge tightened four of these edges. A job's first event is always `registered`: the announcement precedes the pump, whose first drain runs synchronously and may announce output. A settled ring keeps every byte the model cursor has not consumed until the first terminal model read, so a job that finishes before its first `job_output` loses nothing the live cap retained. `dsh-tool-jobs` holds one claim per wait call with a `live` flag that the caller's abort clears synchronously from the abort event, so a settlement announced before the registry's rejection reaches the tool still delivers its notice, and a timed-out wait drops only its own claim while a concurrent wait keeps the settlement covered. The browser records an observation failure that precedes the anchor as an errored view, so the panel shows the notice instead of staying blank. The spill file a pull source keeps is job metadata (`JobView.output.spillPaths`, reported by every pump read and withdrawn once a read stops naming it), not per-chunk metadata, and the model's read renders the shell tools' dropped-output notice for a producer-side gap as well as for ring eviction, naming those files even after the gap chunk itself was evicted — the `before this read` variant this change had introduced is gone. An observation generation that sees its job removed mid-stream (the owner's teardown) closes with the removed job's terminal projection instead of a failed read. A rejected background spawn produces no process output, so the shell providers now serve the `spawn failed: …` note as the whole observed stderr stream (the consuming read still folds it in once), and `ShellProcess.observed` with both readers is required: the model's first `job_output` after such a failure reads exactly what master's consuming read rendered.
The [Session lifecycle decision](../bug-fix/2026-09-30-job-session-lifecycle.md) records registration and settlement for durable SDK consumers while preserving this live event stream and output ownership.
@@ -35,3 +35,5 @@ job seam 是逐步堆出来的。生产者要把输出声明两次——模型
模型可见文本保持不变:状态行、工具描述与 schema、播报措辞逐字节相同,唯一新增的模型可见事实是 `killed` 状态行现在携带的 kill 原因。需要流式输出的生产者选择拉取源或 `append`,从不格式化消耗式增量。Web 客户端把每个进行中的 job 渲染为可展开,因此从不写输出的 job 显示为空的运行中面板而非静态行。`JobKindMap` 的合并发生在宿主侧,浏览器程序把 `JobView.kind` 视为它认识的 kind 集合,其余当作不透明字符串——名册对它也只做这些。
合并 master 之后的评审跟进收紧了其中四条边。job 的第一个事件永远是 `registered`:通告先于泵,而泵的首轮 drain 同步执行且可能通告输出。已结算的环在模型的首次终态读取之前保留模型游标尚未消费的全部字节,因此在首次 `job_output` 之前结束的 job 不会丢失运行期上限保留的任何内容。`dsh-tool-jobs` 为每次等待调用保存一条带 `live` 标记的认领,调用方 abort 时在 abort 事件里同步清掉该标记,因此注册表的拒绝尚未传回工具之前宣布的结算仍会投递通知;超时的等待只丢弃自己的认领,并发的另一个等待仍覆盖该结算。浏览器把先于锚点发生的观察失败记录为带错误的视图,面板显示提示而不是留白。拉取源保留的 spill 文件是 job 元数据(`JobView.output.spillPaths`,每次泵读取都会上报,读取不再点名时即撤回),不是逐块元数据;模型读取对生产者侧缺口与环淘汰都渲染 shell 工具的丢失输出提示并列出这些文件,即使缺口块本身已被淘汰——本次变更曾引入的 `before this read` 变体已删除。观察生成过程若在流中看到其 job 被移除(拥有者 teardown),以被移除 job 的终态投影收尾,而不是一次失败的读取。被拒绝的后台 spawn 不产生任何进程输出,因此 shell provider 现在把 `spawn failed: …` 提示作为 observed stderr 的整条流提供(消耗式读取仍只折入一次),且 `ShellProcess.observed` 及其两个读取器改为必填:这类失败之后模型的首次 `job_output` 读到的正是 master 的消耗式读取所渲染的内容。
[Session 生命周期决策](../bug-fix/2026-09-30-job-session-lifecycle.zh.md)为持久化 SDK 消费方记录注册与结算,同时保留这里的运行期事件流和输出归属。
@@ -12,14 +12,14 @@
en: e01df5ba880c2972
zh: e62074a6a9ba5685
/agent-note-bundle-the-desktop-runtime-and-retain-external-plugins/package-ownership:
en: 37cae6b28fd359ef
zh: 63118e361fa8f84c
en: f683605bf4655db8
zh: de09d610ee1d4193
/agent-note-bundle-the-desktop-runtime-and-retain-external-plugins/transactions-and-upgrades:
en: 53d48f71fcc1c52e
zh: 7d61923484dbec1c
/agent-note-bundle-the-desktop-runtime-and-retain-external-plugins/alternatives-considered:
en: ead5bbd94044c3de
zh: 057cf069413d2a49
en: 67b3ba2b37e92f88
zh: 785083e5de098af8
/agent-note-bundle-the-desktop-runtime-and-retain-external-plugins/consequences:
en: 6dfa581ec6879c5b
zh: f6c735ad6a00b2b3
@@ -26,7 +26,7 @@ The resource descriptor records the exact release, Node version, platform, archi
The [Desktop file policy](../../../../apps/desktop/scripts/runtime-file-policy.ts) applies after production npm installation and before native signing or descriptor generation. npm publication lists serve library consumers and can include declarations, maps, tests, and native build inputs; they do not identify the files needed by the Desktop process. The Desktop copy omits declarations and recognized source maps because Host execution uses JavaScript and generated Typert artifacts. The Host inherits the user environment. Published npm packages and external plugin directories retain their own files. Source debugger navigation is a development-package capability.
Package-specific exclusions remove Domino tests, fs-ext compilation outputs, Koffi's Windows import library, and non-target node-pty prebuilds and debug symbols. The policy retains native executable dependencies, node-pty's ConPTY source distribution, licenses, and unrecognized assets; broad `src`, `test`, `.ts`, or `.map` exclusions could remove executable code or runtime data. Copy tests preserve sentinel assets and seal the filtered inventory; the Electron [payload smoke](../../../../apps/desktop/tests/fixtures/runtime-payload-smoke.mjs) verifies PTY output, native file seeking, FFI, image conversion, and HTML parsing. Runtime preparation still verifies every retained byte and boots the complete Host with an external plugin.
Package-specific exclusions remove Domino tests, fs-ext compilation outputs, Koffi's Windows import library, and non-target node-pty prebuilds and debug symbols. The policy retains native executable dependencies, licenses, and unrecognized assets; node-pty's ConPTY source distribution is retained only for Windows targets; broad `src`, `test`, `.ts`, or `.map` exclusions could remove executable code or runtime data. Copy tests preserve sentinel assets and seal the filtered inventory; the Electron [payload smoke](../../../../apps/desktop/tests/fixtures/runtime-payload-smoke.mjs) verifies PTY output, native file seeking, FFI, image conversion, and HTML parsing. Runtime preparation still verifies every retained byte and boots the complete Host with an external plugin.
The shared profile runner supplies missing installation and selected-bundle dependencies through the runtime resolution. pnpm-installed packages take precedence. Runtime resolution delegates to the selected package paths, so Host and plugin imports reaching the same export share its module instance. Distinct ESM and CommonJS conditional exports remain distinct entry points; runtime resolution does not merge a package’s dual implementations.
@@ -48,7 +48,7 @@ The [immediate-window reference](../../../../apps/desktop/README.md) owns direct
## Alternatives considered
Full runtime verification belongs to packaging. Startup reads the resource descriptor and checks shared package records and required Host entries. The [release-validation reference](../../../../apps/desktop/README.md) assigns release and target compatibility checks to packaging. Startup neither enumerates nor hashes installed runtime files. Reading every file before backend loading adds I/O proportional to distribution size; unusable modules instead fail when loaded. Build-time verification rejects changed, missing, extra, or linked files against the recorded inventory.
Full runtime verification belongs to packaging. Startup reads the resource descriptor and checks shared package records and required Host entries. The [release-validation reference](../../../../apps/desktop/packaging/README.md#desktop-runtime-preparation) assigns release and target compatibility checks to packaging. Startup neither enumerates nor hashes installed runtime files. Reading every file before backend loading adds I/O proportional to distribution size; unusable modules instead fail when loaded. Build-time verification rejects changed, missing, extra, or linked files against the recorded inventory.
- **Install the bundled offline seed at startup.** This preserves an ordinary pnpm installation procedure but repeats core extraction and installation on every affected machine. Materialized resources remove that work at the cost of more application files and release-builder responsibility.
- **Force host dependency versions into plugins.** This unnecessarily couples ordinary plugin dependencies to the host. The runtime resolution supplies missing packages while pnpm-owned entries retain independent versions.
@@ -26,7 +26,7 @@ Desktop 初始化时安装核心依赖图,会重复发布构建器已经完成
[桌面文件规则](../../../../apps/desktop/scripts/runtime-file-policy.ts)在生产 npm 依赖安装之后、原生签名或描述文件生成之前执行。npm 发布列表服务于库的使用者,可以包含声明、map、测试和原生构建输入,不能直接表示桌面进程需要哪些文件。桌面副本排除声明和已识别的 source map,因为 Host 执行 JavaScript 和生成的 Typert 产物。Host 继承用户环境。已发布的 npm 包和外部插件目录保留各自的文件。源码调试导航由开发包提供。
包专用排除项包括 Domino 测试、fs-ext 编译产物、Koffi 的 Windows 导入库,以及非目标平台的 node-pty 预构建文件和调试符号。规则保留原生可执行依赖、node-pty 的 ConPTY 源分发内容、许可证和未知资源;宽泛排除 `src`、`test`、`.ts` 或 `.map` 可能移除可执行代码或运行时数据。复制测试保留哨兵资源并封存过滤后的清单;Electron 的[产物 smoke](../../../../apps/desktop/tests/fixtures/runtime-payload-smoke.mjs)验证 PTY 输出、原生文件定位、FFI、图像转换和 HTML 解析。运行时准备仍会验证每个保留字节,并携带外部插件启动完整 Host。
包专用排除项包括 Domino 测试、fs-ext 编译产物、Koffi 的 Windows 导入库,以及非目标平台的 node-pty 预构建文件和调试符号。规则保留原生可执行依赖、许可证和未知资源;node-pty 的 ConPTY 源分发内容仅在 Windows 目标中保留;宽泛排除 `src`、`test`、`.ts` 或 `.map` 可能移除可执行代码或运行时数据。复制测试保留哨兵资源并封存过滤后的清单;Electron 的[产物 smoke](../../../../apps/desktop/tests/fixtures/runtime-payload-smoke.mjs)验证 PTY 输出、原生文件定位、FFI、图像转换和 HTML 解析。运行时准备仍会验证每个保留字节,并携带外部插件启动完整 Host。
共享 profile runner 通过 runtime resolution 补全安装包与选中 bundle 缺失的依赖。pnpm 安装的包优先。运行时解析委托给所选包路径,因此 Host 与插件导入同一导出时共享其模块实例。不同的 ESM 与 CommonJS 条件导出仍是不同入口;运行时解析不会合并包的双重实现。
@@ -48,7 +48,7 @@ profile manifest 分别记录 pnpm 安装的依赖及已启用 bundle 列表。
## 考虑过的替代方案
完整运行时验证属于打包流程。启动读取资源描述文件,并检查共享包记录及必要的 Host 入口。[发布验证参考](../../../../apps/desktop/README.zh.md)将发布与目标兼容性检查交给打包流程。启动既不枚举已安装运行时文件,也不计算其哈希。后端加载前读取每个文件会增加与分发体积成正比的 I/O;不可用模块改由加载时失败暴露。构建时验证按记录清单拒绝内容变化、缺失、多余或链接文件。
完整运行时验证属于打包流程。启动读取资源描述文件,并检查共享包记录及必要的 Host 入口。[发布验证参考](../../../../apps/desktop/packaging/README.zh.md#desktop-runtime-preparation)将发布与目标兼容性检查交给打包流程。启动既不枚举已安装运行时文件,也不计算其哈希。后端加载前读取每个文件会增加与分发体积成正比的 I/O;不可用模块改由加载时失败暴露。构建时验证按记录清单拒绝内容变化、缺失、多余或链接文件。
- **启动时安装内置离线 seed。** 这保留普通 pnpm 安装流程,但会在每台受影响机器上重复核心解压与安装。物化资源消除了这部分工作,代价是更多应用文件和发布构建器责任。
- **强制插件使用 Host 依赖版本。** 这会让普通插件依赖不必要地耦合于 Host。runtime resolution 提供缺失的包,pnpm 拥有的条目保留独立版本。
@@ -33,8 +33,8 @@
en: 99308bd758859e02
zh: 35f1e7719fe80d2e
/agent-note-add-immutable-profile-resolution-generations/decision/additive-package-changes:
en: 0e5e2006caec3c91
zh: fb3b61dd5692d2e3
en: 39d27cff0d419259
zh: 6cbdc88a450614d0
/agent-note-add-immutable-profile-resolution-generations/decision/filesystem-and-runtime-carriers:
en: b0607713102a093d
zh: 2de9c33bfaa0e3e2
@@ -107,7 +107,7 @@ New Workers inherit the latest published generation. Existing Workers keep the g
A caller adding a package completes its pnpm transaction before constructing a successor generation. Replacement rejects any generation that changes the directory or version of a retained package. Plugin Manager publishes the successor before mounting the new Loader row. A mount failure may leave the package installed but inactive.
A retained package keeps its normalized directory, version, and scope; installation entries also keep their declaring anchor and cannot be removed. Violating these restrictions requires process restart because Node's ESM Module Map, CommonJS cache, existing object references, and running Workers can retain the old module identity. A profile entry may change its declaring anchor: deselecting the first bundle that reaches a shared dependency can leave another bundle selecting the same physical package. Profile-scoped mappings and profile-local package names may be removed; the caller stops their plugins before deleting package files. These restrictions do not prohibit removing a linked root from the interception scope. Generation replacement does not claim to unload modules.
A retained package keeps its normalized directory, version, and scope; installation entries also keep their declaring anchor and cannot be removed. Violating these restrictions requires process restart because Node's ESM Module Map, CommonJS cache, existing object references, and running Workers can retain the old module identity. A profile entry may change its declaring anchor: deselecting the first bundle that reaches a shared dependency can leave another bundle selecting the same physical package. Profile-scoped mappings and profile-local package names may be removed; the caller stops their plugins before deleting package files. A profile mapping moves to another directory only through removal and a new installation; a linked root may point to another target ([replacement Note](2026-10-01-replace-changed-package-modules-on-recomposition.md)). These restrictions do not prohibit removing a linked root from the interception scope. Generation replacement does not claim to unload modules.
### Filesystem and runtime carriers
@@ -107,7 +107,7 @@ runtime resolution 列出它提供的包;Loader entries 组成活动插件列
添加包的调用方先完成 pnpm 事务,再构造下一代。替换操作会拒绝改变任何保留包的目录或版本。Plugin Manager 先发布后继 generation,再挂载新的 Loader 配置项。挂载失败可以留下已安装但未启用的包。
保留包的规范化目录、版本和作用域必须不变;安装条目还必须保留声明锚点,且不能移除。违反这些限制需要重启,因为 Node 的 ESM Module Map、CommonJS cache、现存对象引用和运行中的 Worker 都可能保留旧模块 identity。profile 条目可以改变声明锚点:取消选入最先找到共享依赖的组合包后,另一个组合包仍可能选中同一物理包。profile 范围的映射和 profile 本地包名可以移除;调用方在删除包文件之前先停止相关插件。这些限制不禁止从拦截范围移除 linked root。generation 换代不声称卸载模块。
保留包的规范化目录、版本和作用域必须不变;安装条目还必须保留声明锚点,且不能移除。违反这些限制需要重启,因为 Node 的 ESM Module Map、CommonJS cache、现存对象引用和运行中的 Worker 都可能保留旧模块 identity。profile 条目可以改变声明锚点:取消选入最先找到共享依赖的组合包后,另一个组合包仍可能选中同一物理包。profile 范围的映射和 profile 本地包名可以移除;调用方在删除包文件之前先停止相关插件。profile 映射只能通过卸载和重新安装移到其他目录;linked root 可以指向其他目标([替换 Note](2026-10-01-replace-changed-package-modules-on-recomposition.zh.md))。这些限制不禁止从拦截范围移除 linked root。generation 换代不声称卸载模块。
### 文件系统与运行时载体
@@ -9,8 +9,8 @@
en: 66ac3d7ce7eb8e15
zh: abb9d41b71900bbf
/agent-note-sandboxed-node-execution-for-ptc/decision:
en: cf072f53de09faca
zh: 9edd5c4a2bfaae5a
en: d6b9c22603a4f933
zh: f324b91830078d9a
/agent-note-sandboxed-node-execution-for-ptc/decision/resolved-inputs-and-policy:
en: 3aef37bb4f8a8e53
zh: a7b477324d486ab3
@@ -18,8 +18,8 @@
en: 2c7a694b04951772
zh: 133b95b0477b127f
/agent-note-sandboxed-node-execution-for-ptc/decision/resource-limits:
en: 84c73cf3ff33bc03
zh: a228080683d6252c
en: 3f1d8a30f6d48243
zh: faa7c01d7c959893
/agent-note-sandboxed-node-execution-for-ptc/alternatives-considered:
en: 8ffcc2b32a2243d7
zh: 81d48d38947abc76
@@ -12,7 +12,7 @@ The [PTC foundation](../feature/2026-06-15-ptc.md) remains responsible for regis
## Decision
`dsh-ptc-runtime-node` runs each program in one fresh Node process. The host resolves execution choices, confines the launch through the same `ctx.sandbox` provider as Bash, and gives process lifetime to `ctx.subprocess`. The child evaluates erasable TypeScript with direct Node APIs, an empty model environment and host-provided asynchronous bindings. No worker or persistent kernel remains inside this provider.
`dsh-ptc-runtime-node` in its default `per-call` mode runs each program in one fresh Node process. The host resolves execution choices, confines the launch through the same `ctx.sandbox` provider as Bash, and gives process lifetime to `ctx.subprocess`. The child evaluates erasable TypeScript with direct Node APIs, an empty model environment and host-provided asynchronous bindings. No worker or persistent kernel remains after a per-call execution.
The host preserves `ELECTRON_RUN_AS_NODE` for child startup; the bootstrap removes it from the native environment before evaluation, and model-visible `process.env` stays empty. Nested Electron launches require their own explicit Node-mode selection. Desktop uses Electron as its Node executable; removing this selector launches Electron's application path instead of the PTC bootstrap. Sandbox permission changes cannot repair that launch mismatch. The macOS Desktop regression uses real Electron to verify binding writes, direct workspace writes, and rejection of writes outside the workspace under restricted policy. It requires an installed Electron binary; ordinary runtime tests cover environment filtering without that dependency.
@@ -34,6 +34,8 @@ Program completion, timeout, cancellation and protocol failure all close executi
The default elapsed deadline is 120 seconds, capped at 600 seconds by default. Trusted service consumers may request `timeoutMs: null` to disable this timer; [workflow sandbox reuse](2026-09-13-workflow-ptc-sandbox-reuse.md) owns that caller-controlled lifetime. Omitted and numeric requests, including model-facing `run_code`, retain the numeric defaults and caps. An enabled deadline includes runtime setup and nested tool or approval waits. V8 old-generation memory, serialized outer output and control traffic have separate configured bounds. The heap limit excludes native allocations and descendant memory, and elapsed time is not a process-tree CPU budget.
The opt-in [in-memory execution mode](2026-09-23-best-effort-persistent-ptc.md) retains disposable memory under the same file policy; [retained-process permissions](2026-10-08-persistent-ptc-sandbox.md) govern policy changes and temporary escalation.
## Alternatives considered
**Keep the worker and add tool checks.** Tool checks cannot intercept direct Node imports or establish OS confinement. Keeping a worker inside a confined supervisor process preserves worker metering but adds another execution lifetime without supplying a process-tree CPU limit.
@@ -12,7 +12,7 @@ Node worker 隔离 JavaScript 状态,但不应用调用 Session 的 OS 沙箱
## 决策
`dsh-ptc-runtime-node` 在一个全新 Node 进程中运行每个程序。Host 解析执行选择,通过与 Bash 相同的 `ctx.sandbox` 提供方约束启动,并将进程生命周期交给 `ctx.subprocess`。子进程以直接 Node API、空模型环境和 Host 提供的异步绑定求值可擦除 TypeScript。本提供方不保留 worker 或持久内核。
`dsh-ptc-runtime-node` 的默认 `per-call` 模式在一个全新 Node 进程中运行每个程序。Host 解析执行选择,通过与 Bash 相同的 `ctx.sandbox` 提供方约束启动,并将进程生命周期交给 `ctx.subprocess`。子进程以直接 Node API、空模型环境和 Host 提供的异步绑定求值可擦除 TypeScript。逐次执行结束后不保留 worker 或持久内核。
Host 在子进程启动时保留 `ELECTRON_RUN_AS_NODE`;bootstrap 在求值前将其从原生环境中删除,模型可见的 `process.env` 仍为空。嵌套启动 Electron 需要自行显式选择 Node 模式。桌面端使用 Electron 作为 Node 可执行文件;删除此选择变量会启动 Electron 应用路径,而不是 PTC bootstrap。更改沙箱权限无法修复这一启动模式不匹配。macOS 桌面端回归测试使用真实 Electron 验证绑定写入、直接工作区写入以及受限策略对工作区外写入的拒绝。该测试需要已安装的 Electron 二进制文件;普通运行时测试无需此依赖即可覆盖环境过滤。
@@ -34,6 +34,8 @@ Host 在子进程启动时保留 `ELECTRON_RUN_AS_NODE`;bootstrap 在求值前
默认经过时间截止为 120 秒,默认上限为 600 秒。可信服务消费方可以请求 `timeoutMs: null` 来禁用该定时器;[工作流沙箱复用](2026-09-13-workflow-ptc-sandbox-reuse.zh.md)负责这种由调用方控制的生命周期。省略 timeout 或使用数值的请求(包括面向模型的 `run_code`)保留数值默认值与上限。启用的截止包括运行时准备以及嵌套工具或审批等待。V8 老生代内存、序列化外层输出与控制通信具有独立配置的上限。堆限制不包含原生分配和后代内存,经过时间也不是进程树 CPU 预算。
显式启用的[内存保留执行模式](2026-09-23-best-effort-persistent-ptc.zh.md)在相同文件策略下保留可丢弃内存;[常驻进程权限](2026-10-08-persistent-ptc-sandbox.zh.md)负责策略切换和临时提权。
## 考虑过的替代方案
**保留 worker 并增加工具检查。** 工具检查不能拦截直接 Node 导入,也不能建立 OS 约束。在受限监督进程中保留 worker 可以保留 worker 计量,但会增加一层执行生命周期,仍不能提供进程树 CPU 限制。
@@ -15,8 +15,8 @@
en: 3c2dd2c948ffb51b
zh: d019197df0ee3dcf
/agent-note-deepseek-account-browser-login/consequences:
en: a6eff4a0350e9018
zh: 2e3200ca089023ab
en: 3c5681b492cd34cf
zh: d7707fed9241f9bc
/agent-note-deepseek-account-browser-login/verification:
en: 881edcb454b941a6
zh: 7dc5a9b7a066f623
@@ -46,7 +46,7 @@ Profile and recharge-wallet queries use independent getProfile and getBalance op
Deployment authentication uses explicit Host-only requestHeaders on the configured Platform origin. The provider rejects redirects and reserved-header overrides so deployment cookies cannot replace account authorization or follow a browser destination. Environment-specific authentication protocols remain outside the account provider.
The bundled account UI is Desktop-only: its preload bridge enables the account launcher, settings and sign-in onboarding. Plain Web retains API-key onboarding and the standard Settings launcher without account subscription or login. The Host protocol accepts login_source (desktop or web) in auth_init; the bundled UI sends desktop. The backend accepts localhost callbacks. DSH preserves the browser-supplied localhost hostname and port without DNS resolution or conversion to an IP literal.
The bundled account UI mounts the account launcher, settings and sign-in onboarding in both Desktop and plain Web. The Host sends login_source (desktop or web) in auth_init so Platform can choose the completion experience; the UI sends desktop when the Desktop preload bridge is present and web otherwise. Web failures close the authorization tab while the original tab receives Host state; no Web UI return URL is used. The backend accepts localhost callbacks. DSH preserves the browser-supplied localhost hostname and port without DNS resolution or conversion to an IP literal.
The macOS development launcher registers an isolated, ad-hoc-signed application bundle for `dsh://open`. The bundle retains the workspace entry and development paths for Launch Services cold starts without copying credentials or changing the package-manager-owned Electron application. Protocol registration targets the latest launched development or packaged application.
@@ -46,7 +46,7 @@ DSH 授权通过 x-dsh-auth-token 请求头鉴权 Platform、推理和 Files 请
开发环境认证通过配置的平台来源上的显式 Host 请求头 requestHeaders 完成。提供者拒绝重定向和保留请求头覆盖,防止开发环境 Cookie 替换账号授权或跟随浏览器跳转地址。特定环境的认证协议不属于账号提供者。
内置账号界面仅用于 Desktop:preload 桥接启用账号入口、设置和登录引导。普通 Web 保留 API Key 引导及标准设置入口,不订阅账号状态或显示登录。Host 协议在 auth_init 中接受 login_source(desktop 或 web);内置界面发送 desktop。后端接受 localhost 回调。DSH 保留浏览器提供的 localhost 主机名和端口,不做 DNS 解析或 IP 字面量转换。
内置账号界面在 Desktop 和普通 Web 中都挂载账号入口、设置和登录引导。Host 在 auth_init 中发送 login_source(desktop 或 web),以便 Platform 选择完成页体验;存在 Desktop preload 桥接时界面发送 desktop,否则发送 web。Web 登录失败时关闭授权标签页,原标签页通过 Host 状态获知结果;不使用 Web UI 返回 URL。后端接受 localhost 回调。DSH 保留浏览器提供的 localhost 主机名和端口,不做 DNS 解析或 IP 字面量转换。
macOS 开发启动器为 `dsh://open` 注册独立、经临时签名的应用包。该应用包保留工作区入口和开发路径,使 Launch Services 能够冷启动;凭证不会被复制,也不修改包管理器安装的 Electron 应用。协议注册指向最近启动的开发版或打包版应用。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-09-17-experimental-cua-object-api.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-17-experimental-cua-object-api.md
/agent-note-native-cua-objects-independent-of-evaluator-ownership:
en: 1ea218552c24946e
zh: 83524cf60abec6c8
/agent-note-native-cua-objects-independent-of-evaluator-ownership/problem:
en: a22ddcfb339e503d
zh: a1f4fadb46fce1e7
/agent-note-native-cua-objects-independent-of-evaluator-ownership/decision:
en: 2780d1b0f5ce9ea8
zh: ff986f8c1c90300f
/agent-note-native-cua-objects-independent-of-evaluator-ownership/alternatives-considered:
en: 3d0bc77e3c432cb9
zh: 50a20eda1811f16c
/agent-note-native-cua-objects-independent-of-evaluator-ownership/consequences:
en: 528c644d5810e60b
zh: 9649dfc3b1172a6d
@@ -0,0 +1,43 @@
# Agent Note: Native CUA objects independent of evaluator ownership
Status: implemented
English | [中文](2026-09-17-experimental-cua-object-api.zh.md)
## Problem
Computer-use programs need consistent app and window operations. Coupling those operations to a CUA-specific interpreter makes input handling depend on unrelated choices about variable persistence, imports and cancellation.
## Decision
The shared `cua` library owns application/window objects, consent, and logged dispatch. `cua-macos` owns the Swift helper, its build, and its local connection lifecycle. Providers pass a prepared executable and a connection factory to the shared installer, keeping platform code out of object consumers without adding another runtime registry.
The experimental CUA package owns a native-app subset of the Codex-style object interface and selected MIT-licensed macOS behavior adapted from Cua Driver in a Swift helper. A source manifest records the adapted upstream files and local implementation. The helper is locally compiled; the package has no external Cua Driver runtime dependency. The existing [provider registration](2026-09-12-computer-use-provider-registration.md) prevents competing computer-use providers within one composition.
The facade accepts an operation dispatcher. Its tool adapter resolves the current tool binding for each operation, preserving normal authorization, logging and image admission. The execution host owns variable persistence and completion serialization. CUA defines no persistence directive, worker or run_code replacement.
Native process ownership follows the [shared helper decision](2026-10-09-cua-shared-native-helper.md), with isolated Agent connections. App handles bind process identity and kernel start time; replacing the process or helper never revives old handles. AX snapshots are scoped to an app and optional window, so independent window observations remain usable. Coordinate actions select an owned window; the Host maps [screenshot coordinates](2026-10-13-cua-hover-routes-and-coordinates.md) to native window-local points. Keyboard input remains app-scoped because macOS delivers it to the app's current focus.
The operation schema owns shared data declarations. Host-only limits and launch continuations remain separate from model parameters, and generated native field/enum tables preserve independent native admission without hand-maintained copies. Native code continues to own system calls, permissions and cancellation. AX formatting consumes immutable observations; the walker retains sole ownership of indices and AX references.
Clipboard paste is distinct from Unicode key input: Return and Tab key events can trigger actions instead of inserting clipboard content. Pasteboard access runs on the AppKit main actor and retains promised data until a reader requests it. AX command completion alone does not establish that the application has fetched the data. Restoration checks ownership before replacing contents; the caller still verifies the application result. After successful command dispatch, `pasteReadTimeoutMs` bounds waiting for a data request, with a default of 1000 ms. Expiry returns `PASTE_NOT_CONSUMED`, closes the data provider against late delivery, and restores the clipboard only while still owned. The failure reports no read within that interval; it does not prove the application will never process its queued Paste command. A delayed command may read restored clipboard contents, so callers inspect the target and never retry automatically. Foreground-dependent consumption was observed in the installed ChatGPT/Codex application (`com.openai.codex`); other applications need their own validation. Guidance presents this as an example and applies recovery instructions to any `PASTE_NOT_CONSUMED` result.
CUA registers its own Harness tool and returns operation values directly. Screenshot capture requires a resolved image-capable model and the shared attachment service. The tool admits each screenshot once and renders its durable image reference; the object API, combined observation and image sink retain the same `CuaScreenshot` containing encoded `bytes`, `mimeType`, `pixelWidth`, `pixelHeight` and `windowId` from the validated screenshot result. The bridge preserves this metadata without another upload or image emission. Callers use the reported format and dimensions when saving or declaring images.
## Alternatives considered
Depending on the complete upstream driver would simplify upgrades but does not meet the requirement for locally owned implementation. Copying browser and other platform code would add unsupported scope. The JSON operation protocol allows another native implementation without changing object consumers.
A CUA-specific persistent evaluator duplicates execution lifecycle policy and prevents independent iteration on PTC. Requiring callers to manage raw app and snapshot tokens alone loses reusable object semantics. The facade and small tool adapter provide those semantics while leaving the evaluator outside this package.
Reusing the MCP result adapter would impose MCP envelopes and wire validation on native CUA operations. Direct Harness tool output and shared attachment admission provide the needed logging and images without a second protocol.
**Fixing the screenshot format** would require otherwise unnecessary re-encoding and discard the attachment pipeline’s pass-through optimization. A format convention alone still forces callers to guess, and cannot tell them when encoding policy changes. Returning the already computed format and dimensions preserves pass-through and makes each result describe its actual bytes.
**Waiting until the full native-operation timeout** hides successful command dispatch behind a generic timeout. A separate configurable read budget reports the missing acknowledgement without treating slow applications as unsupported. Requiring every application to be foreground would claim a prerequisite that the available observations do not establish.
## Consequences
The package remains opt-in and macOS-only. Observations do not lock the desktop, and input success records dispatch rather than task completion. Callers must verify effects and must not retry uncertain input automatically. Native fixture tests verify actual window state separately from protocol and model-transcript tests. Persistent execution, binary distribution, permission onboarding and real-model task success remain separately owned work.
Source builds retain a content-addressed executable per compiled generation. The [shared helper decision](2026-10-09-cua-shared-native-helper.md) owns capture-client identity and cross-Host lifetime. Retained source generations avoid replacing a live executable, while the package payload remains separately published for distribution.
@@ -0,0 +1,43 @@
# Agent Note:独立于执行器管理的原生 CUA 对象
Status: implemented
[English](2026-09-17-experimental-cua-object-api.md) | 中文
## 问题
Computer-use 程序需要一致的应用和窗口操作。将这些操作与 CUA 专用解释器绑定,会使输入处理依赖变量持久化、导入和取消等独立设计。
## 决策
共享的 `cua` 库负责应用与窗口对象、授权和已记录的派发。`cua-macos` 负责 Swift helper、构建和本地连接生命周期。提供方将准备好的可执行文件和连接工厂交给共享安装函数,使对象调用方无需包含平台代码,也无需增加另一套运行时注册表。
实验 CUA 包提供 Codex 风格的原生应用对象接口,并在 Swift 辅助进程中改写 Cua Driver 中选取的 MIT 许可 macOS 行为。源码清单记录改写的上游文件和本地实现。辅助进程在本地编译,本包不依赖外部 Cua Driver 运行时。已有的 [provider 注册机制](2026-09-12-computer-use-provider-registration.zh.md)防止同一组合中出现竞争 provider。
对象接口接收操作分派器。工具适配模块每次操作都获取当前工具绑定,保留标准授权、日志和图片接入。执行宿主负责变量持久化和最终表达式值的序列化。CUA 不定义持久化指令、执行 worker 或 run_code 替代实现。
原生进程归属遵循[共享 helper 决策](2026-10-09-cua-shared-native-helper.zh.md),Agent 连接保持隔离。应用句柄绑定进程身份及内核启动时间,替换进程或辅助进程不会使旧句柄重新有效。AX 快照按应用和可选窗口保存,不同窗口的观察可以分别使用。坐标操作选择属于该应用的窗口;Host 按[截图坐标规则](2026-10-13-cua-hover-routes-and-coordinates.zh.md)换算为原生窗口内 point。键盘输入保持应用范围,因为 macOS 将其送往应用的当前焦点。
操作 schema 负责共享数据声明。Host 专属限制和启动续接与模型参数分开,生成的原生字段、枚举表保留独立原生校验,同时避免手工维护副本。系统调用、权限和取消仍由原生代码负责。AX 格式化只消费不可变观察数据,遍历器独自负责索引与 AX 引用。
剪贴板粘贴与 Unicode 按键输入分别实现,因为 Return 和 Tab 按键可能触发动作,而不是插入剪贴板内容。剪贴板访问使用 AppKit 主线程,保留延迟提供的数据直到读取方请求。AX 命令完成本身不能证明应用已取得数据。恢复前检查剪贴板归属;调用方仍需验证应用中的结果。 命令成功分派后,`pasteReadTimeoutMs` 限制等待数据请求的时间,默认 1000 毫秒。到期返回 `PASTE_NOT_CONSUMED`,关闭数据提供方以阻止迟到的数据交付,并仅在仍持有剪贴板时恢复原内容。该失败只说明这段时间内没有读取,不证明应用以后不会处理已排队的 Paste 命令。迟到的命令可能读取恢复后的剪贴板内容,因此调用方必须检查目标,不能自动重试。已安装的 ChatGPT/Codex 应用(`com.openai.codex`)已观察到依赖前台才能读取的行为,其他应用需要分别验证。指引将其写为例子,并对任何 `PASTE_NOT_CONSUMED` 结果提供恢复步骤。
CUA 注册自己的 Harness 工具,直接返回操作结果。截图要求解析到支持图片输入的模型,并提供共享附件服务。工具只接入一次截图,再渲染持久图片引用;对象 API、组合观察和图片输出回调保留同一个 `CuaScreenshot`,其中的编码 `bytes`、`mimeType`、`pixelWidth`、`pixelHeight` 和 `windowId` 来自已校验的截图结果。桥接保留这些元数据,不再次上传或输出图片。调用方落盘或声明图片时使用返回的格式和尺寸。
## 考虑过的替代方案
依赖完整上游驱动可以简化升级,但不符合本地维护实现的要求。复制浏览器及其他平台代码会引入尚不支持的范围。JSON 操作协议允许替换原生实现,而不改变对象调用方。
CUA 专用持久执行器会重复实现执行生命周期规则,使 PTC 无法独立演进。仅让调用方管理原始应用和快照 token,又会失去可复用的对象语义。对象接口和轻量工具适配模块提供这些语义,并将执行器留在本包之外。
复用 MCP 结果适配器会给原生 CUA 操作引入 MCP 信封和协议校验。直接使用 Harness 工具输出与共享附件接入,即可取得所需的日志和图片能力,无需再经过一种协议。
**固定截图格式**会要求原本不需要的重新编码,并失去附件处理的原样直通优化。仅约定格式仍要求调用方猜测,也无法告知编码策略的变化。返回已经算出的格式和尺寸,既保留直通优化,也让每个结果说明其实际字节。
**等到原生操作的总超时**会用一条通用超时掩盖命令已经成功分派的事实。独立、可配置的读取等待上限能报告缺失的读取确认,而不将响应慢的应用认定为不支持粘贴。要求所有应用都必须位于前台,则超出了现有观察能支持的结论。
## 后果
本包需要显式启用,且只提供 macOS 实现。观察不会锁定桌面,输入成功只记录分派完成,不能证明任务完成。调用方必须验证效果,不能自动重试结果不确定的输入。原生测试通过实际窗口状态验证效果,与协议和模型消息测试分别进行。持久执行、二进制分发、权限引导及真实模型任务成功率是分别负责的工作。
源码构建按编译产物的内容摘要保留可执行文件。[共享 helper 决策](2026-10-09-cua-shared-native-helper.zh.md)负责捕获客户端身份与跨 Host 生命期。源码各代副本避免替换运行中的可执行文件,包内产物仍单独发布。
@@ -9,14 +9,14 @@
en: 837ebb0071a5611b
zh: 23d0f0713889ead6
/agent-note-writer-acquisition-for-blank-session-reuse/decision:
en: 7f2676feb224e7ea
zh: babfda5b31dfd0fe
en: a54db0d4a97aa9b8
zh: a52d257b657fe99b
/agent-note-writer-acquisition-for-blank-session-reuse/alternatives-considered:
en: 2a67a53f7f396ba8
zh: 6df6ccc071fb0de2
/agent-note-writer-acquisition-for-blank-session-reuse/consequences:
en: 2e1f1894337689a2
zh: d0cab07cc48083ee
en: 8307612b7476e8f3
zh: 87fa39e58d33d4f1
/agent-note-writer-acquisition-for-blank-session-reuse/verification:
en: 4680e845e2ab294e
zh: ea5890c5ffb09831
en: 4367d55ae9dce5c1
zh: c610626f4e15eb77
@@ -10,7 +10,7 @@ Two Hosts sharing Session storage can select the same blank Session when opening
## Decision
The Session catalog includes persisted blanks. Startup restoration selects the saved blank; ordinary Workspace navigation selects the first eligible unarchived member in catalog order. Explicit-id `session.create` resumes that Session and acquires its writer before opening history. Only `session/writer-held` triggers creation of a new Session. Other errors propagate. Concurrent Workspace connects in one Client share the acquisition and fallback. Later navigation cancels a pending startup selection.
The Session catalog includes persisted blanks. Startup restoration selects the saved blank; ordinary Workspace navigation selects the first eligible unarchived member in catalog order. Explicit-id `session.create` names that Session; the Host resumes it and acquires its writer before opening history, and creates a new Session when another owner holds the writer ([reuse and preset rules](../bug-fix/2026-09-29-reused-blank-runs-requested-preset.md)). Other errors propagate. A Workspace connect in one Client joins the latest in-flight connect to that Workspace only when both request the same preset. Later navigation cancels a pending startup selection.
The Agent retains the acquired handle throughout its lifetime. Checking ownership and releasing a probe lock would leave a race before resume. No PID or process identity enters persisted data or the Remote schema.
@@ -28,8 +28,8 @@ This decision extends the blank-reuse policy in the [Client Session scope refere
## Consequences
A Host can reuse its own live blank or reclaim an unowned persisted blank with its slash state. Separate Hosts create distinct blanks while existing writers remain held. A held selected blank causes fresh creation even when another blank is free; navigation does not search for another candidate. No cleanup is added. Opening the same established conversation from two Hosts can still encounter writer contention. Unknown projection hints remain visible without cold body scans; automatic reuse requires known blank metadata. A non-contention acquisition failure aborts New Session and is currently reported only to the console; hidden blanks have no recovery action in this flow.
A Host can reuse its own live blank or reclaim an unowned persisted blank with its slash state. Separate Hosts create distinct blanks while existing writers remain held. A held selected blank causes fresh creation even when another blank is free; navigation does not search for another candidate. No cleanup is added. Opening the same established conversation from two Hosts can still encounter writer contention. Unknown projection hints remain visible without cold body scans; automatic reuse requires known blank metadata. A non-contention acquisition failure aborts the navigation; a hero Workspace pick or explicit draft preparation reports it as a notice and startup restoration only to the console, and a hidden blank that fails this way has no recovery action in this flow.
## Verification
Navigation tests cover saved selection, single-candidate contention fallback, non-contention failures, overlapping acquisition, and superseding navigation. A shipped Web-composition test holds a real persistence writer, verifies fresh creation despite another free blank, and reclaims the released selected blank with its plan state. The fresh-round-trip recorded Session scenario reloads its blank before submitting the recorded prompt.
Navigation tests cover saved selection, opening the Session the Host returns for a held candidate, non-contention failures, overlapping acquisition, and superseding navigation. A shipped Web-composition test holds a real persistence writer, verifies fresh creation despite another free blank, and reclaims the released selected blank with its plan state. The fresh-round-trip recorded Session scenario reloads its blank before submitting the recorded prompt.
@@ -10,7 +10,7 @@ Status: implemented
## Decision
Session 列表包含持久化空白会话。启动恢复选择已保存的空白会话;普通 Workspace 导航按列表顺序选择第一个符合条件且未归档的成员。显式 ID 的 `session.create` 会恢复该 Session,并在打开历史前取得写锁。只有 `session/writer-held` 会触发新建 Session。其他错误返回调用方。同一 Client 的并发 Workspace 连接共享获取与回退过程。后续导航会取消尚未完成的启动选择。
Session 列表包含持久化空白会话。启动恢复选择已保存的空白会话;普通 Workspace 导航按列表顺序选择第一个符合条件且未归档的成员。显式 ID 的 `session.create` 指明该 Session;Host 恢复它并在打开历史前取得写锁,写锁被其他写者持有时改为新建 Session([复用与 preset 规则](../bug-fix/2026-09-29-reused-blank-runs-requested-preset.zh.md))。其他错误返回调用方。同一 Client 中的 Workspace 连接仅在与该 Workspace 最近一次进行中的连接请求相同 preset 时与之合并。后续导航会取消尚未完成的启动选择。
Agent 在整个生命周期内持有取得的句柄。检查占用后释放探测锁,会在恢复前留下竞态。持久化数据与 Remote schema 均不引入 PID 或进程身份。
@@ -28,8 +28,8 @@ Agent 在整个生命周期内持有取得的句柄。检查占用后释放探
## Consequences
Host 可以复用自己的活动空白会话,也可以接管无人占用的持久化空白会话并保留其 slash 状态。现有写锁被持有时,不同 Host 创建不同的空白会话。所选空白会话被占用时,即使另有空闲空白会话也会新建;导航不再寻找其他候选。不增加清理逻辑。两个 Host 打开同一个已有对话时仍可能遇到写锁争用。未知投影提示仍可见且不扫描冷日志正文;自动复用要求已知的空白元数据。非锁争用的获取错误会中止新建会话,目前只报告到控制台;此流程没有针对隐藏空白会话的恢复操作。
Host 可以复用自己的活动空白会话,也可以接管无人占用的持久化空白会话并保留其 slash 状态。现有写锁被持有时,不同 Host 创建不同的空白会话。所选空白会话被占用时,即使另有空闲空白会话也会新建;导航不再寻找其他候选。不增加清理逻辑。两个 Host 打开同一个已有对话时仍可能遇到写锁争用。未知投影提示仍可见且不扫描冷日志正文;自动复用要求已知的空白元数据。非锁争用的获取错误会中止该次导航;在 hero 中选择工作区或显式准备草稿时以提示展示该错误,启动恢复只报告到控制台,以此方式失败的隐藏空白会话在此流程中没有恢复操作。
## Verification
导航测试覆盖已保存选择、单候选锁争用回退、其他错误、重叠获取以及后续导航替代。实际 Web 组合测试持有真实持久化写锁,验证另有空闲空白会话时仍新建,并接管已释放的所选空白会话及其 plan 状态。fresh-round-trip 的录制 Session 场景在提交已录制的提示词前刷新空白会话。
导航测试覆盖已保存选择、为被占用候选打开 Host 返回的 Session、其他错误、重叠获取以及后续导航替代。实际 Web 组合测试持有真实持久化写锁,验证另有空闲空白会话时仍新建,并接管已释放的所选空白会话及其 plan 状态。fresh-round-trip 的录制 Session 场景在提交已录制的提示词前刷新空白会话。
@@ -9,8 +9,8 @@
en: 725f773bb350218e
zh: dd8fbc09ef48208d
/agent-note-declarative-agent-presets-and-retained-revisions/decision:
en: 53fd72bf4bbe4eb3
zh: 406eee6e4f818e7c
en: 88edb925b556300c
zh: c6f05b641513d768
/agent-note-declarative-agent-presets-and-retained-revisions/alternatives-considered:
en: 09f40eab4659e9f6
zh: 7a70da0d0ae6845b
@@ -16,7 +16,7 @@ The registry owns each revision's scope and Loader tree. Agents link their own s
The Web editor and `agent_preset` tool accept only child plugin YAML. Saving writes the active profile's user patch, preserving display metadata and unrelated configuration. Revision checks and profile locking prevent overwriting concurrent saves; validation rejects a higher-priority override that would defeat the edit. Persistence and activation outcomes are reported separately. Tool writes require approval or full access because configuration executes in the Host.
Session data continues to record the preset identity. Restart resolves that identity against current configuration and rejects a missing definition. Old executable revisions are process-local, not serialized. New sessions without an explicit preset resolve `selectedDefault`; Client choice filtering and default correction follow [the preset UI policy](../../../../packages/client/ui-agent-preset/README.md).
Session data continues to record the preset identity. Restart resolves that identity against current configuration and rejects a missing definition; reusing a blank Session for a new conversation does not resolve it ([decision](../bug-fix/2026-09-29-reused-blank-runs-requested-preset.md)). Old executable revisions are process-local, not serialized. New sessions without an explicit preset resolve `selectedDefault`; Client choice filtering and default correction follow [the preset UI policy](../../../../packages/client/ui-agent-preset/README.md).
## Alternatives considered
@@ -16,7 +16,7 @@ Preset 目录重复承担 Cordis 配置的所有权。独立的发现、元数
Web 编辑器与 `agent_preset` 工具只接受子插件 YAML。保存写入当前 profile 的用户补丁,保留显示元数据和无关配置。版本检查和 profile 锁防止覆盖并发保存;验证拒绝会使编辑失效的更高优先级覆盖。持久化与激活结果分别报告。配置在 Host 中执行,因此工具写入需要批准或完全访问权限。
会话数据继续记录 preset 标识。重启时根据当前配置解析该标识,定义缺失则拒绝恢复。旧的可执行代际仅保留在进程内,不会序列化。未显式指定 preset 的新会话解析 `selectedDefault`;客户端选项过滤和默认值修正遵循[预设 UI 策略](../../../../packages/client/ui-agent-preset/README.zh.md)。
会话数据继续记录 preset 标识。重启时根据当前配置解析该标识,定义缺失则拒绝恢复;为新对话复用空白会话时不解析该标识([决策](../bug-fix/2026-09-29-reused-blank-runs-requested-preset.zh.md))。旧的可执行代际仅保留在进程内,不会序列化。未显式指定 preset 的新会话解析 `selectedDefault`;客户端选项过滤和默认值修正遵循[预设 UI 策略](../../../../packages/client/ui-agent-preset/README.zh.md)。
## Alternatives considered
@@ -15,5 +15,5 @@
en: 4089464ad1fb60bd
zh: 3168e9e4a205145d
/agent-note-keep-live-configuration-in-the-owning-profile/consequences:
en: 6033aaa12b2a521b
zh: 43934413c97670de
en: 41bb170694c785d6
zh: 17419fdd6d937dce
@@ -24,6 +24,6 @@ The rejected alternative is retaining settings as a second store behind a config
## Consequences
This supersedes only the non-secret settings tier in [configuration source ownership](2026-08-04-configuration-source-ownership.md); its environment restrictions and credential ordering remain applicable. [Volatile references](2026-09-18-volatile-config-references.md) own reference lifetime and ordinary update behavior.
This supersedes only the non-secret settings tier in [configuration source ownership](2026-08-04-configuration-source-ownership.md); its environment restrictions remain applicable, and [stored credential precedence](2026-10-09-stored-credential-precedence.md) supersedes its credential ordering. [Volatile references](2026-09-18-volatile-config-references.md) own reference lifetime and ordinary update behavior.
Real profile tests cover multiple instances, persisted restart, invalid edits, stale revisions, higher-layer rejection, secret redaction, and configuration expressions. Browser tests exercise form writes and reset through the assembled application. Runtime reference identity is checked across live edits.
@@ -24,6 +24,6 @@ Cordis config patch 替换完整 entry config。编辑会保留普通字段、
## 后果
本决定只替代 [配置来源所有权](2026-08-04-configuration-source-ownership.zh.md) 中的非秘密 settings 层;其中的环境限制和凭据优先级仍适用。[Volatile 引用](2026-09-18-volatile-config-references.zh.md) 负责引用生命周期和普通更新行为。
本决定只替代 [配置来源所有权](2026-08-04-configuration-source-ownership.zh.md) 中的非秘密 settings 层;其中的环境限制仍适用,其凭据优先级已由[存储凭据优先级](2026-10-09-stored-credential-precedence.zh.md)替代。[Volatile 引用](2026-09-18-volatile-config-references.zh.md) 负责引用生命周期和普通更新行为。
真实 profile 测试覆盖多实例、持久化重启、非法编辑、过期修订、高优先级层拒绝、秘密脱敏及配置表达式。浏览器测试通过完整应用执行表单写入和重置,并检查实时编辑前后的运行时引用身份。
@@ -9,14 +9,14 @@
en: 70a969dce3283824
zh: 18c61bf840a6518a
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/problem/current-state:
en: 8fb06638898de59e
zh: 79ed0cfe79e40be1
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/problem/three-unrelated-uplink-mechanisms:
en: 386be7b691ab63e7
zh: 935e0db22ae1cf5d
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/problem/concrete-costs:
en: f374976b43645d15
zh: 26b2c7aaafa41fba
en: 2de84c3fc0a3552e
zh: da9cd565588bf2dd
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/problem/uplink-consumers:
en: 403103ef08958529
zh: 2813d9d38c2c55b7
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/problem/consumer-responsibilities:
en: 28f8a3112d6ec5fe
zh: c70d68233e3f6b7f
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/problem/relation-to-background-jobs:
en: 5e67475010d52ddb
zh: ce23aad2d4aa0d67
@@ -30,8 +30,8 @@
en: 943208904785f7eb
zh: bda1969132a16101
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/decision/terms:
en: 401703adbd9cc1c2
zh: 56d960c0b6fdff57
en: a22cd84756773075
zh: 49efa0c5a37d4b57
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/decision/type-signatures:
en: 5f9cd00890b6f4c3
zh: 600ecba7e60b63f6
@@ -42,8 +42,8 @@
en: a7180d3c194cf9d9
zh: 0dd7833e0e5d0a71
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/decision/peerscope:
en: 202aa6e8e8b04bd6
zh: b1fc897c09074a7c
en: 182634406ed2b25b
zh: 0bd5900a39802224
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/decision/client-face:
en: 9f10347a73402b8b
zh: b939c7a254a8e579
@@ -75,8 +75,8 @@
en: 46a0c0b3ce1e5671
zh: 7743707de42b1d1d
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/decision/in-process-and-worker-carriers:
en: ec339e57dc2c0603
zh: 3570be8c094ea24c
en: df4ae60d8403cdbb
zh: 70c6024a2a9a1e84
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/decision/relation-to-events:
en: a011793e5a4400ed
zh: df8b20115f0348b8
@@ -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: 41128746334320c7
zh: 558a676340aae3a2
en: ecfaff2b9b2ef021
zh: 85b392d79364e0c3
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/key-sequences:
en: 130e4f9ec7296a10
zh: 2a5aa1302a8db61a
@@ -126,17 +126,17 @@
en: b42ab0932f8b31b4
zh: 7fed8cb5b6e36788
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/alternatives-considered:
en: 04e72789417d6e3a
zh: b57c5cc68dff45a5
en: b0046c9f7c12a876
zh: f3c37b466746b0fd
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/consequences:
en: 07524e0c2dc22ad5
zh: 5bf4f27160d78b43
en: dbc54ce699ef8e71
zh: 83144f07ae8f8759
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/testing:
en: 5c8188930b9d4fdb
zh: 1d94e6563cedde13
en: 06fbeed7b82ded32
zh: 66accb4bd2bf64a2
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/deferred:
en: 3b99a327464edc3e
zh: dcffdc75369925dc
en: 8368c592f30af97f
zh: 2ff89acb7b3ed477
/agent-note-remote-duplex-stream-one-kind-of-stream-an-uplink-channel-and-an-invocation-context/related:
en: 85d77fb246564024
zh: 8b400818a91a3e3a
en: 475d212ae1f9c1c9
zh: 21fe6c6cdb900a6b
@@ -8,41 +8,41 @@ English | [中文](2026-09-19-remote-duplex-stream.zh.md)
### Current state
`dsh-api-gateway` multiplexes every Typert Remote stream over one WebSocket at the fixed path `/api/remote.mux` (`packages/api/gateway/src/stream-protocol.ts`). A Host method decorated with `@Remote({ mode: 'stream' })` that returns an `Iterable` or `AsyncIterable` becomes one Host-to-Client logical stream; the optional final `signal: AbortSignal` is the only reserved parameter, never enters the wire arguments, and is appended by the Gateway after the decoded business parameters (`packages/api/gateway/src/index.ts`). The generated method the Client receives returns a bare `AsyncIterable`.
`dsh-api-gateway` multiplexes Typert Remote streams over one WebSocket at `/api/remote.mux`. A Host method declares both directions with `@Remote({ mode: 'stream' })` and `RemoteStream<Out, In>`; the generated Client method returns `RemoteStreamHandle<Out, In>`. Existing `Iterable` and `AsyncIterable` return types declare downlink-only methods. The optional final `signal: AbortSignal` is supplied by the Gateway and does not enter the wire arguments.
There are only five wire frames:
Both directions share one `streamId`:
| Direction | Frame | Definition |
| --- | --- | --- |
| Client to Host | `{ type: 'open', streamId, endpoint, payload }` | `stream-protocol.ts` |
| Client to Host | `{ type: 'item', streamId, value? }` | `stream-protocol.ts` |
| Client to Host | `{ type: 'end', streamId }` | `stream-protocol.ts` |
| Client to Host | `{ type: 'cancel', streamId }` | `stream-protocol.ts` |
| Host to Client | `{ type: 'item', streamId, value? }` | `stream-protocol.ts` |
| Host to Client | `{ type: 'error', streamId, error: { code, message, details } }` | `stream-protocol.ts` |
| Host to Client | `{ type: 'end', streamId }` | `stream-protocol.ts` |
The Client can send only open and cancel. The parser `parseRemoteStreamClientMessage` (`stream-protocol.ts`) throws on any other frame, and a Host connection that receives one closes the whole socket with 1008 (`stream-server.ts`). In short, the Gateway today supports only "a backend iterable sent to the frontend". Every feature that needs the frontend to send data to the backend continuously takes its own detour.
The Client handle's `send()` supplies uplink items, `end()` half-closes the uplink, and `dispose()` cancels the logical stream. The Host reads decoded items through `this.ctx.invocation.uplink<In>()`; the invocation also carries the admitted Peer and cancellation signal.
A Host method also has no notion of who initiated this call: `InvokeRemoteRequest = { namespace, method, args, signal }` (`packages/api/gateway/src/types.ts`) has no caller slot, `RemoteStreamOpener` has the signature `(endpoint, payload, signal)` (`stream-server.ts`), and the WebSocket checks the cookie once, at the handshake.
The transport owns validation, cancellation, and its bounded inbox. Consumer protocols own input authority, acknowledgements, and recovery after disconnection.
### Three unrelated uplink mechanisms
### Uplink consumers
| Feature | Uplink path | Location | Cost |
| --- | --- | --- | --- |
| Web terminal keystrokes | One unary RPC `terminal.write(agent, id, attachmentId, data)` per xterm `onData`; the Client serializes them through a promise chain and caps its own byte budget | `packages/api/terminal-controller/src/client/model.ts`; Host `src/index.ts` | One HTTP round trip and one authentication per keystroke; two carriers correlated by hand through `attachmentId`; `inputFull` when the budget is exhausted |
| Web terminal input and resize | `terminal.attach` carries `TerminalInput` uplink items and `TerminalFrame` downlink items; the Client pipelines commands and releases its raw-input byte budget on `ack` | `packages/api/terminal-controller/src/client/model.ts`; Host `src/terminal.ts` | The current attachment owns input; `inputFull` when the Client budget is exhausted; input is never replayed after reconnection |
| File upload | A `Blob` or `ReadableStream` goes to the native `POST /api/session/uploadFileBinary` outside the Remote layer, with `duplex: 'half'` | `packages/client/file-upload/src/client/runtime.ts`; route `src/index.ts` | Bypasses Typert descriptors; no type projection |
| Answers to approvals and questions | The Host sends waterfall frames down the `$events` stream, the Client answers through a separate unary RPC `$events/result`, correlated by `clientId` plus `eventId` | `packages/api/gateway/src/client/remote-events.ts`; Host `index.ts` | A second correlation key; `index.ts` records the race this creates |
These three mechanisms solve the same problem: the Client wants to write into a logical stream that is already open.
These consumers need Client-to-Host data while a related operation remains active. Web terminals use the duplex stream; file upload and event answers retain their own transports.
### Concrete costs
### Consumer responsibilities
- **Aligning two carriers.** The terminal uses `attachmentId` to correlate the `follow` stream with the `write` RPC and to decide who owns input. Approvals use `clientId` to correlate the `$events` stream with the `$events/result` RPC.
- **No ordering guarantee.** A unary RPC travels over HTTP and is unordered relative to the WebSocket downlink; the terminal can preserve order only by serializing writes on the Client.
- **Separate authentication and lookup per uplink.** Every `write(agent, id, attachmentId, data)` resolves `agent`, looks up the terminal, and validates the attachment.
- **No EOF.** The Client cannot express "I am done writing"; a half-close needs yet another RPC.
- **Cancellation does not cover both halves.** Cancelling the downlink stream does not cancel queued uplink writes, and vice versa.
- **Backpressure implemented three times.** The downlink pulls the iterator at the pace of the socket write callback (`stream-server.ts`); the terminal uses a bounded follower that makes a slow consumer fail explicitly (`terminal-controller/src/stream.ts`); the uplink relies on the Client capping `maxInputBytes` itself.
- **The documentation is already stale.** `docs/api-gateway.md:160` still says Remote handles one request and one result, a sentence that lags the stream mode the README records.
- **Input authority.** A terminal retains its controlling attachment as a Host-local follower. Snapshot and state frames report each attachment's `writable` state; older attachments keep receiving output after control transfers.
- **Ordering and acknowledgement.** Terminal writes and resizes share a consecutive command sequence and the terminal's operation queue. An `ack` reports provider acceptance or a control refusal; it never authorizes replay after disconnection.
- **Independent lifetimes.** Cancelling `attach` stops its input and output delivery, drops unstarted commands, and joins an in-flight input operation. The PTY lifetime is owned separately by retention and explicit close.
- **Coordinated byte limits.** The Gateway exposes its complete-frame inbox capacity and this stream's JSON envelope cost through `invocation.uplinkWindow`. The terminal publishes that window in its snapshot and uses provider acknowledgements to release in-flight capacity. Writes and resizes wait in a bounded Client queue when the window is full. `maxInputBytes` limits raw UTF-8 input; `maxBufferedBytes` separately limits the complete encoded input retained by the Client and output retained by each follower.
- **Remaining separate carriers.** Approval answers still correlate `$events` with the `$events/result` RPC through `clientId` and `eventId`. Moving this protocol onto the duplex stream remains deferred.
### Relation to background jobs
@@ -66,7 +66,7 @@ Every Remote stream accepts a Client uplink. The method's return type `RemoteStr
Client Host
───────────────────────────────── ───────────────────────────────────────────
const s = remote.job.attach(req, signal)
│ open { streamId, endpoint, payload } ───────▶ opener(endpoint, payload, uplink, peer, control)
│ open { streamId, endpoint, payload } ───────▶ opener(endpoint, payload, uplink, peer, control, window)
│ │ prepareInvocation
│ s.send(v1) → item { streamId, v1 } ───────▶ inbox.push(v1) │ invocation = { request, service, peer, signal, uplink() }
│ s.send(v2) → item { streamId, v2 } ───────▶ inbox.push(v2) │ receiverContext.extend({ invocation }).get(service)
@@ -88,7 +88,7 @@ Every Remote stream accepts a Client uplink. The method's return type `RemoteStr
| Half-close | One direction has ended while the other remains open |
| Item | One business value in one direction; every item is validated by the codec independently |
| inbox | The bounded uplink queue the Host keeps per logical stream |
| Peer | The party the connection layer admitted, one `PeerScope`; this Host has exactly one Peer, the operator |
| Peer | The party one connection speaks for, one `PeerScope` carrying its principal; every browser connection shares `connection.browserPeer`, and in-process code creates its own through `connection.createPeer()` ([Peer principals](../../implemented/architecture/2026-10-12-peer-principal-and-remote-access.md)) |
| Invocation context | `RemoteInvocation`: the request, receiving service, Peer, cancellation signal, and uplink entry of this call |
### Type signatures
@@ -135,10 +135,11 @@ export interface RemoteInvocation {
}
/** Cordis service key of the receiving service. */
readonly service: string
/** The Peer that initiated the call. In-process carriers and unadmitted calls are the operator. */
/** The Peer that initiated the call; every carrier names it explicitly. */
readonly peer: PeerScope
/** Carrier cancellation: Client cancel, socket closure, uplink failure. */
readonly signal: AbortSignal
readonly uplinkWindow?: RemoteUplinkWindow | undefined
/**
* The uplink items of this call. Can be taken once; the second call throws. With an uplink codec
* in the descriptor each item is decoded to In; without one, items are delivered as unknown with
@@ -179,24 +180,30 @@ The mechanism relies on two existing Cordis behaviors:
/** The opaque identity of one Peer. */
export type PeerId = Branded<'PeerId'>
/** One Peer's session on this Host: opened and released by the admitting side; ctx owns connection-lifetime registrations. */
/** One Peer's session on this Host: opened and released by its creator; ctx owns connection-lifetime registrations. */
export interface PeerScope {
readonly id: PeerId
readonly ctx: Context
/** Caller identity fixed at creation. */
readonly principal: Principal
/** Aborted once the Peer is disposed. */
readonly signal: AbortSignal
/** Participation in forwarded interactions; independent of RBAC. */
readonly interactions: 'answer' | 'none'
dispose(): Promise<void>
}
```
This Host has exactly one Peer: the operator. `@deepseek-ai/dsh-client-connection` creates it with `createScope(connectionCtx, peer)` when the service applies, the same mechanism an Agent uses to create its own scope: the Peer object is the ScopeKey, `peer.ctx` carries connection-lifetime effects, and `dispose()` runs with the Connection's release and quiets the fiber. It is exposed as `connection.operator`.
`@deepseek-ai/dsh-client-connection` implements the one Peer class, `Peer(owner, { principal, interactions? })`, with `createScope(owner, peer)`, the same mechanism an Agent uses to create its own scope: the Peer object is the ScopeKey, `peer.ctx` carries connection-lifetime effects, `peer.signal` aborts when the scope ends, and `dispose()` quiets the fiber. Connection creates `connection.browserPeer` with the owner principal when the service applies; [Peer principals and method-level Remote access control](../../implemented/architecture/2026-10-12-peer-principal-and-remote-access.md) owns principals, `createPeer()`, and access checks.
| Member | Semantics |
| --- | --- |
| `connection.operator: PeerScope` | The only Peer; lives and dies with the Connection |
| `connection.admit(request): PeerAdmission` | Runs the existing `requestRejection`: returns `{ rejection: 401 \| 403 }` when refused, otherwise `{ peer: operator }`. Future admitters plug in here |
| `connection.browserPeer: PeerScope` | The Peer every authenticated browser connection shares; owner principal; lives and dies with the Connection |
| `connection.admit(request): PeerAdmission` | Checks Host/Origin and signed browser or API Cookies; returns `{ rejection: 401 \| 403 }` or the credential's live Peer; connect publishes the API Peer and its lifecycle socket releases it |
The Gateway's `handleUpgrade(req, socket, head, peer)` binds an opener with a fixed Peer to that socket and registers socket closure in `peer.ctx.effect`; when the scope is already inactive, it closes the socket directly with 1001 instead. When `InvokeRemoteRequest.peer?` or the `peer` parameter of `RemoteStreamOpener` is absent, the answer is the operator, so existing in-process callers change nothing; in a composition without a Connection, the operator stand-in the Gateway builds for itself is likewise created through `createScope`, with the same `dispose()` contract. Looking up a Peer by id, associating a carrier with a second Peer, and Peer open and close events all wait until the first consumer that needs multiple Peers appears.
The Gateway's `handleUpgrade(req, socket, head, peer)` binds an opener with a fixed Peer to that socket and registers socket closure in `peer.ctx.effect`; when the scope is already inactive, it closes the socket directly with 1001 instead. `InvokeRemoteRequest.peer` and the `peer` parameter of `RemoteStreamOpener` and `wireStream.open` are required, so every in-process caller names its Peer, and the Gateway creates none. Looking up a Peer by id and Peer open and close events wait until the first consumer that needs them appears.
The Peer carries no access model: who the Peer is and what it may do are attached by business plugins on their own through `peer.ctx`.
The Peer carries its principal; what that principal may call is decided by the roles, bindings, and policies that [Peer principals and method-level Remote access control](../../implemented/architecture/2026-10-12-peer-principal-and-remote-access.md) defines.
### Client face
@@ -311,7 +318,7 @@ Credit frames wait until a consumer that needs sustained high-volume uplink appe
| Worker tunnel (`packages/experimental/webworker-runtime`) | The page adds `stream-uplink-item` / `stream-uplink-end` frames; `serveStream` assembles them into an `uplink` handed to `seams.openStream`. Same-origin trusted boundary: no inbox limit, and items after `end` are dropped |
| `remote-mock` (`packages/test-support/remote-mock`) | `StreamHandle.uplink: AsyncIterable<unknown>`; `rpc.open` passes it through |
An in-process carrier with no `peer` means the operator.
An in-process carrier passes its Peer explicitly; the in-page Host of the worker tunnel passes `connection.browserPeer`.
### Relation to `$events`
@@ -325,7 +332,7 @@ An in-process carrier with no `peer` means the operator.
// types.ts
export type RemoteStream<Out, In = never> = AsyncIterable<Out> & { readonly [STREAM_UPLINK]?: In }
export type PeerId = Branded<'PeerId'>
export interface PeerScope { readonly id: PeerId; readonly ctx: Context; dispose(): Promise<void> }
export interface PeerScope { readonly id: PeerId; readonly ctx: Context; readonly principal: Principal; readonly signal: AbortSignal; dispose(): Promise<void> }
export interface RemoteInvocation { … } // see Host face
declare module '@deepseek-ai/cordis' { interface Context { readonly invocation: RemoteInvocation | undefined } }
@@ -362,13 +369,13 @@ export interface InvokeRemoteRequest {
readonly args: Readonly<Record<string, unknown>>
/** Uplink items; absent is equivalent to an iterator that ends immediately. */
readonly uplink?: AsyncIterable<unknown>
/** The Peer that initiated the call; absent means the operator. */
readonly peer?: PeerScope
/** The Peer that initiated the call; required on every path. */
readonly peer: PeerScope
readonly signal?: AbortSignal
}
export interface TypertGatewayWireStream {
open: (endpoint: string, payload: unknown, uplink: AsyncIterable<unknown>, peer: PeerScope | undefined, signal: AbortSignal) => Promise<AsyncIterable<unknown>>
open: (endpoint: string, payload: unknown, uplink: AsyncIterable<unknown>, peer: PeerScope, signal: AbortSignal) => Promise<AsyncIterable<unknown>>
failure: (error: unknown) => RemoteStreamFailure
}
@@ -379,6 +386,7 @@ export type RemoteStreamOpener = (
uplink: AsyncIterable<unknown>,
peer: PeerScope,
control: AbortController,
window: RemoteUplinkWindow,
) => Promise<AsyncIterable<unknown>>
// index.ts Config addition
@@ -394,7 +402,7 @@ const signal = request.signal ?? NEVER_ABORTED_SIGNAL
const invocation = new GatewayInvocation(
{ namespace: request.namespace, method: request.method, args: request.args },
descriptor.service,
request.peer ?? this.operatorPeer(),
request.peer,
signal,
new UplinkDecoder(request.uplink ?? EMPTY_ASYNC_ITERABLE, descriptor.uplink?.codec, endpoint, control),
)
@@ -444,8 +452,8 @@ cancel : missing → ignore; otherwise control.abort(new Error('Remote stream ca
| `dsh-typert-protocol` (`types.ts`, `index.ts`) | `RemoteStream`, `RemoteStreamHandle`, `PeerId`, `PeerScope`, `RemoteInvocation`, the `ctx.invocation` declaration merge, `InvocationDescriptor.uplink`; the decorator recognizes only `mode: 'stream'`; `TypertRemoteService` registers the `invocation` accessor when constructed |
| `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` (`operator-peer.ts`, `rpc.ts`, `rpc-host.ts`, `index.ts`) | The operator `PeerScope`; `connection.operator` 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)`; `operatorPeer()` |
| `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` 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 |
@@ -560,7 +568,7 @@ for await (const reply of stream) replies.push(reply) // ['> a', '> b']
**Putting the Client's `send` on the `AsyncIterable` the generated method returns.** Hanging methods on an `AsyncIterable` is unconventional; the handle type `RemoteStreamHandle` extends it explicitly.
**Copying the Access mechanism.** Access control is business semantics, served by `vouch` and the `remote/invoke` waterfall; this Note only needs "whose stream is this", and `PeerScope` suffices.
**Copying the Access mechanism.** Access control is business semantics, served by `vouch` and the `remote/invoke` waterfall; this Note only needs "whose stream is this", and `PeerScope` suffices. Method-level access control later moved to [Peer principals and method-level Remote access control](../../implemented/architecture/2026-10-12-peer-principal-and-remote-access.md), which supersedes this part.
**Keeping unary RPC for the uplink.** One HTTP round trip, one authentication, and one lookup per uplink; two carriers with no ordering relation that only Client-side serialization can order; no EOF; cancellation that does not cover both halves; a hand-made correlation key per feature.
@@ -574,10 +582,11 @@ for await (const reply of stream) replies.push(reply) // ['> a', '> b']
- **Bought**: one stream with two directions, so interactive scenarios such as terminal keystrokes, approval answers, and background-job stdin no longer each build their own "one stream plus one unary"; a Host method knows its caller; the uplink and downlink types are declared in one place, the return type; uplink items come from the browser and are validated strictly per item at the Host, while downlink items are Host-produced typed values that pass straight through; the generated output and wire frames of existing `AsyncIterable<Out>` methods and unary methods are completely unchanged.
- **`this.ctx.invocation` is `undefined` outside a Remote call**, so a method that reads it must handle the optional; a service method invoked only directly in process correctly reads `undefined`.
- **Web terminals use `attach`** for screen output and input commands, with provider acknowledgements and per-attachment writable state; their Client retains the current generation's handle and never replays input.
- **The inbox limit applies to every stream**: a method that does not read its uplink fails as a whole when it receives many uplink frames. This is a deliberate explicit failure, recorded in the README.
- **The operator is the only Peer**, and `admit` is the only admission point; the first consumer that needs a second Peer must add opening, association, and events in `dsh-client-connection`.
- **Every browser connection shares `browserPeer`**, and `admit` is the only network admission point; in-process code creates further Peers with `connection.createPeer()`.
- **No cross-direction ordering guarantee between uplink and downlink**; a protocol that needs request-response pairing carries its own sequence numbers.
- **Upper-layer protocols do not yet expose `send`**: consumers of `$stream`, snapshot, and journal use only the downlink today; the object they receive is already the handle.
- **Upper-layer supervisors expose only the downlink**: `$stream`, snapshot and journal do not expose `send`; a domain that writes uplink retains its generated physical handle for that generation.
- **The worker tunnel behaves differently from the WebSocket carrier**: no inbox limit, and items after `end` are dropped, because page to worker is a same-origin trusted boundary.
## Testing
@@ -588,17 +597,15 @@ Retention cases in `gateway-stream.host.spec.ts` and `gateway.client.spec.ts` co
- Gateway Host: exact-key validation in frame parsing; `UplinkInbox` buffering during opening, `item` after `end`, overflow, ignored unknown `streamId`, and cleanup on socket closure; `UplinkDecoder` delivery with and without a codec and abort on decode failure; `uplink()` taken only once; the `extend({ invocation })` view and the origin of `peer`; `cancellableStream` closing the uplink before `return()`.
- Gateway Client: the handle's `send` (JSON validation, queued before open, throws after termination), idempotent `end`, `dispose` driving the `cancel` frame and ending later reads; the pump's three exits; the pump stopping as soon as a terminal frame arrives.
- typert: the analyzer's handling of `RemoteStream<Out, In>`, `RemoteStream<Out>`, `never`, and the existing `AsyncIterable<Out>`; decorator options; registry load-time validation.
- connection: the three outcomes of `admit`, 401, 403, and the operator; the operator reaching handlers through the bridge and the shared handler; release with the Connection.
- connection: the three outcomes of `admit`, 401, 403, and `browserPeer`; `browserPeer` reaching handlers through the bridge and the shared handler; release with the Connection.
- remote-mock and the worker tunnel: uplink pass-through; the direct proxy returning the real handle.
- Existing stream and unary method tests pass without changed expectations.
## Deferred
- `$stream` / snapshot / journal expose the handle's `send` to upper-layer protocols.
- Web terminal: one `attach` stream replaces `follow` plus `write` plus `resize`, and `attachmentId` retires.
- Duplex `$events`: waterfall answers become uplink items on the same `streamId`, and `$events/result` and `clientId` retire.
- Credit frames: wait for a consumer that needs sustained high-volume uplink; the inbox limit is the initial credit.
- The `streamInboxBytes` default of 262144 is far more than keyboard and stdin need; if the first consumer is terminal paste, it may be aligned with the `maxInputBytes` of `terminal-controller`.
- A Host-initiated half-close frame (the Host stops reading the uplink but keeps writing the downlink): no consumer; not added.
- `uplink()` on unary methods: it can read items that arrive while the method runs, semantically valid but of doubtful use; allowed, noted in the README.
@@ -606,4 +613,4 @@ Retention cases in `gateway-stream.host.spec.ts` and `gateway.client.spec.ts` co
- [Remote event delivery](../../../../packages/api/remotes/README.md): the unary `$events/result` answer path; the duplex `$events` item under Deferred partially supersedes it once it lands.
- [Session history and event transport](2026-08-18-session-history-and-event-transport.md): the `$stream()` supervisor and the journal / snapshot protocols above it, which this note leaves unchanged.
- [Web sidebar terminal](../feature/2026-09-09-web-sidebar-terminal.md): the unary terminal `write` and `attachmentId`; the `attach` stream under Deferred replaces it once it lands.
- [Web sidebar terminal](../feature/2026-09-09-web-sidebar-terminal.md): the `attach` stream carries input commands, provider acknowledgements and per-attachment writable state; reconnect never replays input.
@@ -8,41 +8,41 @@ Status: implemented
### 现状
`dsh-api-gateway` 在一条 WebSocket 上复用全部 Typert Remote 流,路径固定为 `/api/remote.mux`(`packages/api/gateway/src/stream-protocol.ts`)。一个 Host 方法用 `@Remote({ mode: 'stream' })` 修饰并返回 `Iterable` 或 `AsyncIterable`,就成为一条 Host 到 Client 的逻辑流;可选的末位 `signal: AbortSignal` 是唯一的保留参数,不进线路参数,由网关在解码后的业务参数之后追加(`packages/api/gateway/src/index.ts`)。客户端拿到的生成方法返回一个裸 `AsyncIterable`。
`dsh-api-gateway` 在 `/api/remote.mux` 的一条 WebSocket 上复用 Typert Remote 流。Host 方法通过 `@Remote({ mode: 'stream' })` 和 `RemoteStream<Out, In>` 声明两个方向;生成的 Client 方法返回 `RemoteStreamHandle<Out, In>`。既有的 `Iterable` 和 `AsyncIterable` 返回类型声明仅下行的方法。可选的末位 `signal: AbortSignal` 由 Gateway 提供,不进入线路参数。
线路帧只有五种:
两个方向共用一个 `streamId`:
| 方向 | 帧 | 定义 |
| --- | --- | --- |
| Client 到 Host | `{ type: 'open', streamId, endpoint, payload }` | `stream-protocol.ts` |
| Client 到 Host | `{ type: 'item', streamId, value? }` | `stream-protocol.ts` |
| Client 到 Host | `{ type: 'end', streamId }` | `stream-protocol.ts` |
| Client 到 Host | `{ type: 'cancel', streamId }` | `stream-protocol.ts` |
| Host 到 Client | `{ type: 'item', streamId, value? }` | `stream-protocol.ts` |
| Host 到 Client | `{ type: 'error', streamId, error: { code, message, details } }` | `stream-protocol.ts` |
| Host 到 Client | `{ type: 'end', streamId }` | `stream-protocol.ts` |
客户端能发的只有打开与取消。解析器 `parseRemoteStreamClientMessage`(`stream-protocol.ts`)对其他任何帧抛错,Host 连接收到后以 1008 关闭整个 socket(`stream-server.ts`)。结论:网关今天只支持"后端 iterable 发到前端"。需要前端向后端持续发数据的功能,各自绕路。
Client 句柄的 `send()` 提供上行项,`end()` 半关闭上行,`dispose()` 取消逻辑流。Host 通过 `this.ctx.invocation.uplink<In>()` 读取解码后的项;调用上下文同时携带获准接入的 Peer 和取消信号。
Host 方法也没有"这次调用是谁发起的"这个概念:`InvokeRemoteRequest = { namespace, method, args, signal }`(`packages/api/gateway/src/types.ts`)没有调用者槽位,`RemoteStreamOpener` 的签名是 `(endpoint, payload, signal)`(`stream-server.ts`),WebSocket 只在握手时校一次 cookie。
传输层负责校验、取消和有界 inbox。消费者协议负责输入权限、确认以及断线后的恢复。
### 三套各自为政的上行机制
### 上行消费者
| 功能 | 上行方式 | 位置 | 代价 |
| --- | --- | --- | --- |
| Web 终端按键 | 每次 xterm `onData` 一个 unary RPC `terminal.write(agent, id, attachmentId, data)`,客户端用 promise 链串行化并自限字节预算 | `packages/api/terminal-controller/src/client/model.ts`;Host `src/index.ts` | 一次按键一次 HTTP 往返与鉴权;两条载体靠 `attachmentId` 手工关联;满了报 `inputFull` |
| Web 终端输入与尺寸调整 | `terminal.attach` 承载上行 `TerminalInput` 和下行 `TerminalFrame`;Client 连续发送命令,并在收到 `ack` 后释放原始输入字节预算 | `packages/api/terminal-controller/src/client/model.ts`;Host `src/terminal.ts` | 当前附件拥有输入权;Client 预算耗尽时报 `inputFull`;重连后绝不重放输入 |
| 文件上传 | `Blob` 或 `ReadableStream` 走 Remote 层之外的原生 `POST /api/session/uploadFileBinary`,`duplex: 'half'` | `packages/client/file-upload/src/client/runtime.ts`;路由 `src/index.ts` | 不经 Typert 描述符,没有类型投影 |
| 审批与提问的回答 | Host 在 `$events` 流上下发 waterfall 帧,Client 用一个独立的 unary RPC `$events/result` 回答,靠 `clientId` 加 `eventId` 关联 | `packages/api/gateway/src/client/remote-events.ts`;Host `index.ts` | 第二套关联键;`index.ts` 记录了由此产生的竞态 |
这三套机制解决的是同一个问题:一条已经打开的逻辑流,客户端想往里写。
这些消费者都需要在相关操作仍进行时从 Client 向 Host 传送数据。Web 终端使用双工流;文件上传和事件回答保留各自的传输方式。
### 具体代价
### 消费者职责
- **两条载体对齐**。终端要用 `attachmentId` 把 `follow` 流与 `write` RPC 关联起来,并用它决定谁拥有输入权。审批要用 `clientId` 把 `$events` 流与 `$events/result` RPC 关联起来。
- **没有顺序保证**。unary RPC 走 HTTP,与 WebSocket 下行互不排序;终端只能在客户端串行化写入来自保顺序。
- **每次上行单独鉴权与查找**。`write(agent, id, attachmentId, data)` 每次解析 `agent`、查终端、校验 attachment。
- **没有 EOF**。客户端无法表达"我这边写完了",半关闭要再加一个 RPC。
- **取消不覆盖两半**。取消下行流不会取消排队中的上行写;反之亦然。
- **背压各写一套**。下行按 socket 写回调节拍拉迭代器(`stream-server.ts`);终端用有界 follower 让慢消费者显式失败(`terminal-controller/src/stream.ts`);上行靠客户端自限 `maxInputBytes`。
- **文档已经过时**。`docs/api-gateway.md:160` 仍写着 Remote 只处理一请求一结果,这一句已经落后于 README 记录的 stream 模式。
- **输入权限**。终端通过 Host 本地的 follower 持有控制附件。快照和状态帧报告各附件的 `writable` 状态;控制权转移后,旧附件继续接收输出。
- **顺序与确认**。终端输入和尺寸调整共用连续命令序列及终端操作队列。`ack` 报告提供者已接受操作或输入权限拒绝,绝不授权断线后重放。
- **独立生命周期**。取消 `attach` 会停止其输入和输出传递、丢弃尚未开始的命令,并等待在途输入操作收尾。PTY 的生命周期由保留机制和显式关闭独立管理。
- **协调字节限额**。Gateway 通过 `invocation.uplinkWindow` 提供完整帧 inbox 容量及本流的 JSON 封装开销。终端在快照中公布窗口,并通过提供者确认释放在途额度。窗口满时,输入和尺寸调整命令在有界 Client 队列中等待。`maxInputBytes` 限制原始 UTF-8 输入;`maxBufferedBytes` 分别限制 Client 保留的完整编码输入及每个 follower 保留的输出。
- **仍独立的载体**。审批回答仍通过 `clientId` 和 `eventId` 将 `$events` 与 `$events/result` RPC 关联。将该协议迁入双工流仍属后续工作。
### 与后台 job 的关系
@@ -66,7 +66,7 @@ Host 方法也没有"这次调用是谁发起的"这个概念:`InvokeRemoteReq
Client Host
───────────────────────────────── ───────────────────────────────────────────
const s = remote.job.attach(req, signal)
│ open { streamId, endpoint, payload } ───────▶ opener(endpoint, payload, uplink, peer, control)
│ open { streamId, endpoint, payload } ───────▶ opener(endpoint, payload, uplink, peer, control, window)
│ │ prepareInvocation
│ s.send(v1) → item { streamId, v1 } ───────▶ inbox.push(v1) │ invocation = { request, service, peer, signal, uplink() }
│ s.send(v2) → item { streamId, v2 } ───────▶ inbox.push(v2) │ receiverContext.extend({ invocation }).get(service)
@@ -88,7 +88,7 @@ Host 方法也没有"这次调用是谁发起的"这个概念:`InvokeRemoteReq
| 半关闭 | 一个方向已结束而另一个方向仍开放 |
| 项 | 一个方向上的一个业务值;每项独立经 codec 校验 |
| inbox | Host 侧每逻辑流一个的有界上行队列 |
| Peer | 连接层被接纳的一方,一个 `PeerScope`;本 Host 只有操作者一个 Peer |
| Peer | 一条连接代表的一方,一个携带 principal 的 `PeerScope`;所有浏览器连接共享 `connection.browserPeer`,进程内代码经 `connection.createPeer()` 自建([Peer 身份属性](../../implemented/architecture/2026-10-12-peer-principal-and-remote-access.zh.md)) |
| 调用上下文 | `RemoteInvocation`:本次调用的请求、接收服务、Peer、取消信号与上行入口 |
### 类型签名
@@ -135,10 +135,11 @@ export interface RemoteInvocation {
}
/** Cordis service key of the receiving service. */
readonly service: string
/** The Peer that initiated the call. In-process carriers and unadmitted calls are the operator. */
/** The Peer that initiated the call; every carrier names it explicitly. */
readonly peer: PeerScope
/** Carrier cancellation: Client cancel, socket closure, uplink failure. */
readonly signal: AbortSignal
readonly uplinkWindow?: RemoteUplinkWindow | undefined
/**
* The uplink items of this call. Can be taken once; the second call throws. With an uplink codec
* in the descriptor each item is decoded to In; without one, items are delivered as unknown with
@@ -179,24 +180,30 @@ declare module '@deepseek-ai/cordis' {
/** The opaque identity of one Peer. */
export type PeerId = Branded<'PeerId'>
/** One Peer's session on this Host: opened and released by the admitting side; ctx owns connection-lifetime registrations. */
/** One Peer's session on this Host: opened and released by its creator; ctx owns connection-lifetime registrations. */
export interface PeerScope {
readonly id: PeerId
readonly ctx: Context
/** Caller identity fixed at creation. */
readonly principal: Principal
/** Aborted once the Peer is disposed. */
readonly signal: AbortSignal
/** Participation in forwarded interactions; independent of RBAC. */
readonly interactions: 'answer' | 'none'
dispose(): Promise<void>
}
```
本 Host 只有一个 Peer:操作者。`@deepseek-ai/dsh-client-connection` 在服务 apply 时用 `createScope(connectionCtx, peer)` 建它,与 Agent 建自己的 scope 是同一机制:Peer 对象就是 ScopeKey,`peer.ctx` 承接连接期效果,`dispose()` 随 connection 释放让 fiber 静默。它以 `connection.operator` 暴露。
`@deepseek-ai/dsh-client-connection` 实现唯一的 Peer 类 `Peer(owner, { principal, interactions? })`,用 `createScope(owner, peer)` 建立,与 Agent 建自己的 scope 是同一机制:Peer 对象就是 ScopeKey,`peer.ctx` 承接连接期效果,scope 结束时 `peer.signal` 中止,`dispose()` 让 fiber 静默。Connection 在服务 apply 时以 owner principal 创建 `connection.browserPeer`;principal、`createPeer()` 与访问检查归 [Peer 身份属性与 Remote 方法级访问控制](../../implemented/architecture/2026-10-12-peer-principal-and-remote-access.zh.md)。
| 成员 | 语义 |
| --- | --- |
| `connection.operator: PeerScope` | 唯一的 Peer,随 connection 生灭 |
| `connection.admit(request): PeerAdmission` | 过既有的 `requestRejection`:被拒返回 `{ rejection: 401 \| 403 }`,否则返回 `{ peer: operator }`。将来的接纳方在这里接入 |
| `connection.browserPeer: PeerScope` | 所有通过认证的浏览器连接共享的 Peer;principal 为 owner;随 connection 生灭 |
| `connection.admit(request): PeerAdmission` | 检查 Host/Origin 与浏览器或 API 签名 Cookie;返回 `{ rejection: 401 \| 403 }` 或凭据对应的存活 Peer;API Peer 在 connect 时发布,并由其生命周期 socket 负责释放 |
网关 `handleUpgrade(req, socket, head, peer)` 为该 socket 绑一个固定 Peer 的 opener,并在 `peer.ctx.effect` 里登记 socket 关闭;scope 已失效时改为直接以 1001 关闭 socket。`InvokeRemoteRequest.peer?` 与 `RemoteStreamOpener` 的 `peer` 形参缺席时按操作者作答,现有进程内调用方零改动;没有 connection 的组合里网关自建的操作者替身同样经 `createScope` 建立,`dispose()` 契约一致。按 id 查 Peer、把载体关联到第二个 Peer、Peer 的开关事件,都等第一个需要多 Peer 的消费者出现时再加。
网关 `handleUpgrade(req, socket, head, peer)` 为该 socket 绑一个固定 Peer 的 opener,并在 `peer.ctx.effect` 里登记 socket 关闭;scope 已失效时改为直接以 1001 关闭 socket。`InvokeRemoteRequest.peer`、`RemoteStreamOpener` 与 `wireStream.open` 的 `peer` 形参均为 required,每个进程内调用方都显式给出 Peer,网关不自建 Peer。按 id 查 Peer、Peer 的开关事件,都等第一个需要它们的消费者出现时再加。
Peer 不携带访问模型:Peer 是谁、能做什么,由业务插件通过 `peer.ctx` 自行附加。
Peer 携带自己的 principal;该 principal 能调用什么,由 [Peer 身份属性与 Remote 方法级访问控制](../../implemented/architecture/2026-10-12-peer-principal-and-remote-access.zh.md) 定义的 role、binding 与 policy 判定。
### Client 面
@@ -311,7 +318,7 @@ credit 帧留待需要持续大流量上行的消费者出现时再加;inbox
| worker 隧道(`packages/experimental/webworker-runtime`) | 页面新增 `stream-uplink-item` / `stream-uplink-end` 帧;`serveStream` 把它们组装成 `uplink` 交给 `seams.openStream`。同源可信边界:没有 inbox 上限,`end` 之后的项丢弃 |
| `remote-mock`(`packages/test-support/remote-mock`) | `StreamHandle.uplink: AsyncIterable<unknown>`;`rpc.open` 透传 |
进程内载体的 `peer` 缺席即操作者。
进程内载体显式传入 Peer;worker 隧道的页内 Host 传 `connection.browserPeer`。
### 与 `$events` 的关系
@@ -325,7 +332,7 @@ credit 帧留待需要持续大流量上行的消费者出现时再加;inbox
// types.ts
export type RemoteStream<Out, In = never> = AsyncIterable<Out> & { readonly [STREAM_UPLINK]?: In }
export type PeerId = Branded<'PeerId'>
export interface PeerScope { readonly id: PeerId; readonly ctx: Context; dispose(): Promise<void> }
export interface PeerScope { readonly id: PeerId; readonly ctx: Context; readonly principal: Principal; readonly signal: AbortSignal; dispose(): Promise<void> }
export interface RemoteInvocation { … } // see Host face
declare module '@deepseek-ai/cordis' { interface Context { readonly invocation: RemoteInvocation | undefined } }
@@ -362,13 +369,13 @@ export interface InvokeRemoteRequest {
readonly args: Readonly<Record<string, unknown>>
/** Uplink items; absent is equivalent to an iterator that ends immediately. */
readonly uplink?: AsyncIterable<unknown>
/** The Peer that initiated the call; absent means the operator. */
readonly peer?: PeerScope
/** The Peer that initiated the call; required on every path. */
readonly peer: PeerScope
readonly signal?: AbortSignal
}
export interface TypertGatewayWireStream {
open: (endpoint: string, payload: unknown, uplink: AsyncIterable<unknown>, peer: PeerScope | undefined, signal: AbortSignal) => Promise<AsyncIterable<unknown>>
open: (endpoint: string, payload: unknown, uplink: AsyncIterable<unknown>, peer: PeerScope, signal: AbortSignal) => Promise<AsyncIterable<unknown>>
failure: (error: unknown) => RemoteStreamFailure
}
@@ -379,6 +386,7 @@ export type RemoteStreamOpener = (
uplink: AsyncIterable<unknown>,
peer: PeerScope,
control: AbortController,
window: RemoteUplinkWindow,
) => Promise<AsyncIterable<unknown>>
// index.ts Config addition
@@ -394,7 +402,7 @@ const signal = request.signal ?? NEVER_ABORTED_SIGNAL
const invocation = new GatewayInvocation(
{ namespace: request.namespace, method: request.method, args: request.args },
descriptor.service,
request.peer ?? this.operatorPeer(),
request.peer,
signal,
new UplinkDecoder(request.uplink ?? EMPTY_ASYNC_ITERABLE, descriptor.uplink?.codec, endpoint, control),
)
@@ -444,8 +452,8 @@ cancel : missing → ignore; otherwise control.abort(new Error('Remote stream ca
| `dsh-typert-protocol`(`types.ts`、`index.ts`) | `RemoteStream`、`RemoteStreamHandle`、`PeerId`、`PeerScope`、`RemoteInvocation`、`ctx.invocation` 声明合并、`InvocationDescriptor.uplink`;修饰器只认 `mode: 'stream'`;`TypertRemoteService` 构造时注册 `invocation` accessor |
| `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`(`operator-peer.ts`、`rpc.ts`、`rpc-host.ts`、`index.ts`) | 操作者 `PeerScope`;`connection.operator` 与 `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)`;`operatorPeer()` |
| `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` 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 目录 |
@@ -560,7 +568,7 @@ for await (const reply of stream) replies.push(reply) // ['> a', '> b']
**客户端 `send` 放在生成方法返回的 `AsyncIterable` 上。** `AsyncIterable` 上挂方法不常规;句柄类型 `RemoteStreamHandle` 显式扩展它。
**照搬 Access 机制。** 访问控制是业务语义,`vouch` 与 `remote/invoke` waterfall 为它服务;本 Note 只需要"这条流是谁的",`PeerScope` 就够。
**照搬 Access 机制。** 访问控制是业务语义,`vouch` 与 `remote/invoke` waterfall 为它服务;本 Note 只需要"这条流是谁的",`PeerScope` 就够。方法级访问控制后来归入 [Peer 身份属性与 Remote 方法级访问控制](../../implemented/architecture/2026-10-12-peer-principal-and-remote-access.zh.md),该部分由其 supersede。
**继续用 unary RPC 做上行。** 每次上行一次 HTTP 往返、一次鉴权、一次查找;两条载体没有顺序关系,只能靠客户端串行化;没有 EOF;取消不覆盖两半;每个功能各造一套关联键。
@@ -574,10 +582,11 @@ for await (const reply of stream) replies.push(reply) // ['> a', '> b']
- **买到的**:一条流两个方向,终端按键、审批回答、后台任务 stdin 这类交互式场景不再各造一套"一条流加一个 unary";Host 方法知道调用者;上行类型与下行类型在返回类型里一处声明,上行项来自浏览器,在 Host 逐项严格校验,下行项是 Host 自产的类型值,直接透传;既有 `AsyncIterable<Out>` 方法与 unary 方法的生成物与线路帧完全不变。
- **`this.ctx.invocation` 在非 Remote 调用下是 `undefined`**,方法读它要处理可选;一个只被进程内直接调用的服务方法读到 `undefined` 是正确行为。
- **Web 终端使用 `attach`** 传输屏幕输出和输入命令,并提供提供者确认和附件专属可写状态;Client 保留当前代次的句柄,绝不重放输入。
- **inbox 上限对所有流生效**:一个不读上行的方法收到大量上行帧会整流失败。这是刻意的显式失败,README 记明。
- **Peer 只有操作者一个**,`admit` 是唯一的接纳点;第一个需要第二个 Peer 的消费者要在 `dsh-client-connection` 加上打开、关联与事件。
- **所有浏览器连接共享 `browserPeer`**,`admit` 是唯一的网络接纳点;进程内代码经 `connection.createPeer()` 创建其他 Peer。
- **上行与下行之间没有跨方向顺序保证**;需要请求应答配对的协议自带序号。
- **上层协议尚未透出 `send`**:`$stream`、snapshot、journal 的消费者今天只用下行;它们拿到的对象已经是句柄。
- **上层监督器仅暴露下行**:`$stream`、snapshot 和 journal 不暴露 `send`;需要写入上行的领域保留该代次的生成物理句柄。
- **worker 隧道与 WebSocket 载体行为不同**:没有 inbox 上限,`end` 之后的项丢弃,因为页面到 worker 是同源可信边界。
## 测试
@@ -588,17 +597,15 @@ for await (const reply of stream) replies.push(reply) // ['> a', '> b']
- 网关 Host:帧解析的精确键校验;`UplinkInbox` 的 opening 缓冲、`end` 后 `item`、超限、未知 `streamId` 忽略、socket 关闭时的收尾;`UplinkDecoder` 有无 codec 两种交付与解码失败中止;`uplink()` 只能取一次;`extend({ invocation })` 视图与 `peer` 的来源;`cancellableStream` 先关 uplink 再 `return()`。
- 网关 Client:句柄的 `send`(JSON 校验、open 前入队、终止后抛错)、`end` 幂等、`dispose` 驱动 `cancel` 帧并结束后续读取;泵的三种退出;终止帧到达即停泵。
- typert:analyzer 对 `RemoteStream<Out, In>`、`RemoteStream<Out>`、`never` 与既有 `AsyncIterable<Out>` 的处理;修饰器选项;registry 加载校验。
- connection:`admit` 的 401、403 与操作者三种结果;操作者经桥与共享处理器到达;随 connection 释放。
- connection:`admit` 的 401、403 与 `browserPeer` 三种结果;`browserPeer` 经桥与共享处理器到达;随 connection 释放。
- remote-mock、worker 隧道:上行透传;直接代理返回真句柄。
- 既有 stream 与 unary 方法的测试不改期望值即通过。
## 后续
- `$stream` / snapshot / journal 把句柄的 `send` 透出给上层协议。
- Web 终端:一条 `attach` 流取代 `follow` 加 `write` 加 `resize`,`attachmentId` 退场。
- 双工 `$events`:waterfall 的回答成为同一 `streamId` 上的上行项,`$events/result` 与 `clientId` 退场。
- credit 帧:等需要持续大流量上行的消费者出现;inbox 上限就是初始授信。
- `streamInboxBytes` 默认值 262144 对键盘与 stdin 远超需要;若第一个消费者是终端粘贴,可能与 `terminal-controller` 的 `maxInputBytes` 对齐。
- Host 发起的半关闭帧(Host 不再读上行但继续写下行):没有消费者,不加。
- unary 方法的 `uplink()`:能读到方法运行期间到达的项,语义成立但用处存疑;允许,README 注明。
@@ -606,4 +613,4 @@ for await (const reply of stream) replies.push(reply) // ['> a', '> b']
- [Remote 事件投递](../../../../packages/api/remotes/README.zh.md):`$events/result` 的 unary 回答路径;「后续」里的双工 `$events` 落地后部分取代它。
- [会话历史与事件传输](2026-08-18-session-history-and-event-transport.zh.md):`$stream()` 监督器与其上的 journal / snapshot 协议,本 Note 不改它们。
- [Web 侧边栏终端](../feature/2026-09-09-web-sidebar-terminal.zh.md):终端按键的 unary `write` 与 `attachmentId`;「后续」里的 `attach` 流落地后取代它。
- [Web 侧边栏终端](../feature/2026-09-09-web-sidebar-terminal.zh.md):`attach` 流承载输入命令、提供者确认和附件专属可写状态;重连绝不重放输入。
@@ -15,8 +15,8 @@
en: adccc5dc758ad8f6
zh: 50304d68db42e8b1
/agent-note-timed-user-questions-as-two-settlements/decision/ownership:
en: 0543678ad9d26fc5
zh: aa6ba207e661c567
en: a28e37fda6e9eaff
zh: 9ca622e09ab24b4d
/agent-note-timed-user-questions-as-two-settlements/decision/foreground-wait-and-claim-lifetime:
en: 447713bbdf1ab4d8
zh: 2757678ae47822aa
@@ -30,14 +30,14 @@
en: 3a33f2583e30e32a
zh: 0ad7e8d1e47dc510
/agent-note-timed-user-questions-as-two-settlements/decision/reply-node-and-grouping:
en: 10323b0e0340a40b
zh: a17fc3a4d4d820a7
en: d87d7432c37405a7
zh: 49f1bbf53856851a
/agent-note-timed-user-questions-as-two-settlements/alternatives-considered:
en: bf61d0ec80c63bb4
zh: d4ce4d4438ffb013
en: 1a070542bfa40e68
zh: 82e32964da912dd8
/agent-note-timed-user-questions-as-two-settlements/verification:
en: d912a73a5ff409eb
zh: 7b626794773e2301
en: 1d19d196c363a273
zh: 7cb0475f0cb554d1
/agent-note-timed-user-questions-as-two-settlements/consequences:
en: 2bc99c99be8ca9da
zh: 8f262ec8b9b0945e
en: 0580e85cc12439c5
zh: a4b151d2c9e5b747
@@ -29,7 +29,7 @@ Foreground answers use the existing Remote Event waterfall. A timeout ends that
| `UserQuestionService` | Foreground waits and Client claims, unattended deadlines, late-answer validation and steering, and the durable question projection. |
| Gateway and Remote runtime | Existing request delivery, settlement, cancellation, and Remote stream transport; no question-specific timer or event-name branch. |
| `ui-user-questions` | One card per Session and call, answer-channel selection, local countdown and drafts, and the reply Definition and renderer. |
| `ui-chat` | Message-id-based presentation aggregation and ordinary process/Turn folding. |
| `ui-chat` | Ordinary process/Turn folding. |
| Question-specific tool row | Pending/answered presentation and panel actions, composed in `QuestionToolRow` without adding `rowAction` to generic `ToolRow`. |
The [question service](../../../../packages/interaction/user-questions/README.md), [tool](../../../../packages/interaction/tool-ask-user/README.md), and [questions UI](../../../../packages/client/ui-user-questions/README.md) own their package contracts.
@@ -72,11 +72,11 @@ Autofocus applies only when the answer channel is ready, no countdown is active,
The questions UI identifies a late reply from `source.kind`, then parses its recorded JSON for question-and-answer presentation. Pasting identical JSON in an ordinary user message does not identify it as a reply. Unreadable recorded payloads retain their raw-text presentation.
The ordinary message Definition and the question-reply Definition both match the appended message and retain separate Node Store projections with the same message id. Chat business grouping omits the `turn-trigger` presentation when a `question-reply` with that id is present. The duplicate trigger neither renders nor splits a group; the reply renders once. The same aggregation applies to unscoped historical input.
The question-reply Definition claims the appended reply message on the Chat target through an [exclusive match](2026-10-12-exclusive-conversation-matches.md), so the ordinary message Definition builds no `turn-trigger` or context projection for it and the reply renders once, including for unscoped historical input.
The reply participates in ordinary process grouping and whole-Turn folding. It is not independently visible: reading it can require expanding both its Turn and process group. Turn disclosure stays before the group it controls, including a reply that opens the Turn.
This decision adds no process-role field, independent-layout flag, user-message structure change, or target-local fallback dispatch. The independent-kind rules stay in Chat's existing grouping logic. A node-data-owned declaration of grouping and overlap semantics is deferred infrastructure, not part of the question feature.
This decision adds no independent-layout flag or user-message structure change. The reply declares no group-tree position, so it follows the default placement of process content.
## Alternatives considered
@@ -90,7 +90,7 @@ This decision adds no process-role field, independent-layout flag, user-message
**Reclassify JSON inside generic `MessageItem`, borrow the relay form, or use the default context row.** Parsing text to identify the business message confuses its source with its content; the relay form describes another agent's message, and a generic context row exposes JSON. A dedicated source, Definition, and renderer own the question presentation.
**Independent reply layout through generic node metadata.** It broadens a question feature into a conversation-infrastructure change. Ordinary folding and Chat-owned aggregation are sufficient for this decision; independently declared node-data grouping semantics remain a separate design task.
**Independent reply layout through generic node metadata.** It broadens a question feature into a conversation-infrastructure change. Ordinary folding is sufficient for this decision; placing the reply as user input through its group-tree position is separate work.
**A log-only close event or a manufactured dismissal message on panel close.** A log-only close hides the user's decision from the model and adds another durable mechanism. Manufacturing a reply treats hiding the panel as an answer. Panel close therefore leaves a timed question answerable.
@@ -101,7 +101,7 @@ This decision adds no process-role field, independent-layout flag, user-message
- [Service tests](../../../../packages/interaction/user-questions/tests/user-questions.spec.ts) and [wait tests](../../../../packages/interaction/user-questions/tests/timed-wait.spec.ts) cover claimed and unclaimed waits, last-claim disconnect, cancellation, timeout error mapping, continued-only answers, and exact answer-id validation.
- [Projection tests](../../../../packages/interaction/user-questions/tests/projection.spec.ts) distinguish legacy and timed schemas and cover native and PTC pending calls, other failures, resume repair, queued reply cancellation, and admitted settlement.
- [Client listener tests](../../../../packages/client/ui-user-questions/tests/browser-plugin.client.spec.ts) and [composer tests](../../../../packages/client/ui-user-questions/tests/user-questions-composer.client.spec.tsx) cover claim retention through Host acceptance, focus before handshake, indefinite waits, draft retention, RPC resubmission, and panel actions.
- [Grouping tests](../../../../packages/client/ui-chat/tests/process-groups.client.spec.ts) and [browser replay](../../../../apps/web/tests/question-composer.e2e.ts) cover one rendered reply with both projections retained, ordinary collapsed placement, disclosure ordering, and the original tool row's read-only late-answer panel.
- [Grouping tests](../../../../packages/client/ui-chat/tests/process-groups.client.spec.ts) and [browser replay](../../../../apps/web/tests/question-composer.e2e.ts) cover ordinary collapsed placement, disclosure ordering, and the original tool row's read-only late-answer panel.
- Legacy round-trip, cancellation, plan-review intent, and Session-remount regressions remain covered by their owning tests. Live multi-Client delivery races and real Host restart remain integration coverage gaps beyond isolated lifecycle and projection tests.
## Consequences
@@ -111,7 +111,6 @@ The model can continue independent work while the question remains answerable. B
- Take time affects only one Client. Another Client can answer or expire the shared waterfall; local focus does not establish a global hold.
- A terminal claim-stream failure can reject the foreground question rather than produce pending. No unattended-wait fallback is provided for that failure.
- Host and Client clocks need not align, but Client system-clock changes can move expiry and transport latency can delay the local countdown's start.
- The duplicate projections remain stored. Aggregation adds linear scans during structural grouping rebuilds; ordinary text-only streaming updates retain their incremental path. This is an algorithmic bound, not a measured latency claim.
- Unanswered continued questions can remain after reopening a Session. Panel close records nothing, and visibility follows normal folding rather than an always-visible reply guarantee.
- A queued late reply can be canceled before the agent admits it. The question stays answerable until an admitted `user/message` records the answer.
- A PTC sub-call interrupted before its `tool/ptc-dispatch` result is recorded cannot be reconstructed from the parent `run_code` failure, so it cannot take a late answer after recovery.
@@ -29,7 +29,7 @@ Status: implemented
| `UserQuestionService` | 前台等待与 Client 接手、无人接手时的 deadline、迟到回答校验与 steering(中途引导),以及持久问题投影。 |
| Gateway 与 Remote 运行时 | 现有请求投递、结算、取消及 Remote stream 传输;不包含问题专属计时器或按事件名分支。 |
| `ui-user-questions` | 每个 Session 和调用的一张卡片、回答通道选择、本地倒计时与草稿,以及回复 Definition 和渲染器。 |
| `ui-chat` | 基于消息 id 的呈现聚合,以及普通过程/轮次折叠。 |
| `ui-chat` | 普通过程/轮次折叠。 |
| 问题专用工具行 | pending/已回答呈现与面板操作,在 `QuestionToolRow` 中组合,不向通用 `ToolRow` 添加 `rowAction`。 |
[问题服务](../../../../packages/interaction/user-questions/README.zh.md)、[工具](../../../../packages/interaction/tool-ask-user/README.zh.md)和[问题 UI](../../../../packages/client/ui-user-questions/README.zh.md)分别拥有各包的约定。
@@ -72,11 +72,11 @@ Client 计算 `Date.now() + remainingMs`。手动聚焦未编辑的回答区会
问题 UI 通过 `source.kind` 识别迟到回复,再解析已记录的 JSON 来呈现问答。普通用户消息粘贴相同 JSON 不会被识别为回复。无法读取的已记录载荷保留原始文本呈现。
普通消息 Definition 和问答回复 Definition 都匹配追加的消息,在 Node Store 中保留消息 id 相同的独立投影。存在同 id 的 `question-reply` 时,Chat 业务分组省略 `turn-trigger` 呈现。重复触发节点既不渲染,也不切开分组,回复只显示一次。相同聚合也适用于无轮次归属的历史输入。
问答回复 Definition 通过[独占匹配](2026-10-12-exclusive-conversation-matches.zh.md)在 Chat target 上认领追加的回复消息,普通消息 Definition 因此不再为它建立 `turn-trigger` 或 context 投影,回复只显示一次;无轮次归属的历史输入同样如此。
回复参与普通过程分组和整轮折叠,不保证独立可见:阅读时可能需要展开所属轮次与过程分组。轮次折叠控件始终在所控制的分组之前,包括由回复开启轮次的情况。
本决策不添加 process-role 字段、独立布局标记、用户消息结构变更或目标内 fallback 派发。独立 kind 规则保留在 Chat 现有分组逻辑中。由节点 data 声明分组与重叠语义的机制是留待拆分的基础设施,不属于问题功能。
本决策不添加独立布局标记或用户消息结构变更。回复不声明分组树位置,因此按过程内容的默认位置放置。
## Alternatives considered
@@ -90,7 +90,7 @@ Client 计算 `Date.now() + remainingMs`。手动聚焦未编辑的回答区会
**在通用 `MessageItem` 内重新分类 JSON、借用 relay form,或使用默认 context 行。** 解析文本来识别业务消息混淆了来源与内容;relay form 表示其他 agent 的消息,通用 context 行则会暴露 JSON。专用来源、Definition 和渲染器拥有问题呈现。
**通过通用节点元数据让回复独立布局。** 这会把问题功能扩大为对话基础设施变更。普通折叠与 Chat 内聚合足以满足本决策;由节点 data 独立声明分组语义仍是单独的设计任务。
**通过通用节点元数据让回复独立布局。** 这会把问题功能扩大为对话基础设施变更。普通折叠足以满足本决策;通过分组树位置把回复放置为用户输入是单独的工作。
**面板关闭时写仅入日志的关闭事件,或伪造放弃消息。** 仅入日志会让模型看不到用户决定,还增加另一套持久机制。伪造回复则把隐藏面板当成回答。因此关闭面板仍让计时问题保持可回答。
@@ -101,7 +101,7 @@ Client 计算 `Date.now() + remainingMs`。手动聚焦未编辑的回答区会
- [服务测试](../../../../packages/interaction/user-questions/tests/user-questions.spec.ts)与[等待测试](../../../../packages/interaction/user-questions/tests/timed-wait.spec.ts)覆盖有人与无人接手、最后接手方断开、取消、超时错误映射、仅接受已继续问题的回答,以及答案 id 的精确校验。
- [投影测试](../../../../packages/interaction/user-questions/tests/projection.spec.ts)区分 legacy 与 timed schema,并覆盖原生及 PTC pending 调用、其他失败、恢复修复、排队回复取消和准入后结算。
- [Client 监听器测试](../../../../packages/client/ui-user-questions/tests/browser-plugin.client.spec.ts)与[composer 测试](../../../../packages/client/ui-user-questions/tests/user-questions-composer.client.spec.tsx)覆盖接手保留到 Host 接受结果、握手前聚焦、无限期等待、草稿保留、RPC 重提及面板操作。
- [分组测试](../../../../packages/client/ui-chat/tests/process-groups.client.spec.ts)与[浏览器回放](../../../../apps/web/tests/question-composer.e2e.ts)覆盖保留两个投影但只渲染一次回复、普通折叠位置、折叠控件顺序,以及原工具行的迟到答案只读面板。
- [分组测试](../../../../packages/client/ui-chat/tests/process-groups.client.spec.ts)与[浏览器回放](../../../../apps/web/tests/question-composer.e2e.ts)覆盖普通折叠位置、折叠控件顺序,以及原工具行的迟到答案只读面板。
- legacy 问答往返、取消、计划评审意图及 Session 重新挂载的回归仍由各自测试覆盖。真实多 Client 投递竞争与真实 Host 重启仍是独立生命周期和投影测试之外的集成覆盖缺口。
## Consequences
@@ -111,7 +111,6 @@ Client 计算 `Date.now() + remainingMs`。手动聚焦未编辑的回答区会
- 「慢慢回答」只影响一个 Client。其他 Client 可以回答或让共享 waterfall 到期,本地聚焦不构成全局暂停。
- 接手 stream 的终止性失败可能让前台问题失败,而不是产生 pending。不为该失败提供无人接手等待的 fallback。
- Host 与 Client 时钟不必对齐,但 Client 系统时钟变化可能移动到期时间,传输延迟可能推迟本地倒计时的开始。
- 重复投影仍保留在存储中。聚合在结构性分组重建时增加线性扫描;普通纯文本 stream 更新保留增量路径。这是算法复杂度界限,不是实测延迟结论。
- 无人回答的已继续问题可以在重新打开 Session 后继续存在。关闭面板不写记录,可见性遵循普通折叠,而不是保证回复始终可见。
- 排队的迟到回复可能在 agent 准入前被取消。只有已准入的 `user/message` 记录答案,问题在此之前保持可回答。
- PTC 子调用若在记录 `tool/ptc-dispatch` 结果前中断,无法从父 `run_code` 的失败中重建,因此恢复后不能接受迟到回答。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-09-23-best-effort-persistent-ptc.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-23-best-effort-persistent-ptc.md
/agent-note-node-ptc-execution-modes-and-in-memory-state:
en: 078a89753b06cd6a
zh: 4004e5e4bb42f2cc
/agent-note-node-ptc-execution-modes-and-in-memory-state/problem:
en: e42db40b879fdbf4
zh: 6017da9c4da17835
/agent-note-node-ptc-execution-modes-and-in-memory-state/decision:
en: d2d08c54c6520f02
zh: 520a9b3421ff5c65
/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
@@ -0,0 +1,37 @@
# Agent Note: Node PTC execution modes and in-memory state
Status: implemented
English | [中文](2026-09-23-best-effort-persistent-ptc.zh.md)
## Problem
Interactive automation retains application objects, imported modules and helper functions between model calls. Recreating every object for every program prevents consumers from using ordinary JavaScript references across observations and actions.
## Decision
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.
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.
The Session records submitted programs, nested tool calls and returned output. It does not serialize the REPL heap or rerun earlier programs after restart, resume or fork: replaying automation could repeat external side effects. A missing variable is a normal program error, and the model-visible instructions require recreating variables and reacquiring resources. No environment identifier or new durable Session event is required because the provider exposes no unlogged model input.
Cancellation, deadline expiry, protocol or output-limit failure, owner disposal and process exit discard the environment. Ordinary exceptions retain earlier mutations. A retained function uses the active evaluation's host bindings; asynchronous work from an earlier evaluation cannot acquire a later evaluation's tool authority or output sink.
## Alternatives considered
Separate npm providers plus a shared library allow independent distribution, at the cost of another package, dependency declarations and cross-package exports for shared implementation details. The two Node modes have no current need for separate release schedules or independent dependencies. A second plugin subpath permits separate loading but asks deployments to replace plugin entries to select an execution policy. A configuration field expresses that selection while preserving the existing entry. Independent distribution or substantially different dependencies can justify revisiting this choice.
A new Cordis service for common helpers would require another registration and injected dependency without adding an independently replaceable runtime capability. Internal functions and classes provide the required reuse. A common execution superclass would also couple fresh-process cleanup with owner-retained lifetimes; the provider shares mechanisms without merging those lifecycles.
Serializing arbitrary JavaScript objects cannot preserve native resources and closures. Replaying prior programs can repeat writes and input actions. Both would imply recovery guarantees that in-memory execution does not offer.
A realm inside the Harness process shares process lifetime with model code and cannot terminate a synchronous loop independently. A managed child process permits cancellation and resource cleanup while retaining imports and variables during healthy use. Process ownership alone does not provide a file sandbox or multi-tenant isolation.
## Consequences
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.
@@ -0,0 +1,37 @@
# Agent Note: Node PTC 执行模式与内存状态
Status: implemented
[English](2026-09-23-best-effort-persistent-ptc.md) | 中文
## 问题
交互式自动化需要在模型调用之间保留应用对象、导入模块和辅助函数。如果每次执行都重新创建对象,消费者就无法在观察与操作之间继续使用普通 JavaScript 引用。
## 决策
现有 `@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)负责文件约束、权限切换与临时提权。
一个 npm 包和一个插件入口保持现有安装与加载路径稳定。通信、绑定代理、JSON 编码和输出限制继续作为包内模块复用。Node 提供方将保留进程的所有权交给内部执行对象,该对象不注册额外的 Cordis 服务。逐次执行和内存保留各自管理不同的生命周期。`programFormat` 描述代码求值形式,与状态保留独立:REPL 不必跨调用保留状态。运行时通过 `executionInstructions` 负责语法、完成值和状态保留指引;工具层负责调用参数、SDK 声明和图片附加。工作流根据 `programFormat` 包装程序;所选运行时无法执行受限文件策略时,工作流拒绝执行。
Session 记录提交的程序、嵌套工具调用和返回输出。它不序列化 REPL 堆,也不在重启、恢复或派生后重新执行旧程序,因为重放自动化可能重复外部操作。缺失变量产生普通程序错误,模型指引要求重新创建变量并获取资源。提供方没有引入日志之外的模型输入,因此不需要环境标识或新的持久 Session 事件。
取消、截止时间到期、协议失败、输出超限、所有者销毁和进程退出都会丢弃环境。普通异常保留此前发生的修改。保留的函数使用当前执行的宿主绑定;较早执行中的异步任务不能获取后续执行的工具权限或向其输出。
## 考虑过的替代方案
将提供方拆成独立 npm 包并增加共享库,可以分别分发,但需要维护额外的包、依赖声明和共享实现的跨包导出。两种 Node 模式目前没有不同发布节奏或独立依赖的需求。第二个插件子路径允许独立加载,但要求部署配置通过替换插件入口来选择执行策略。配置字段直接表达这个选择,并保留已有入口。需要独立分发或出现明显不同的依赖时,可以重新评估拆包。
为公共辅助代码新增 Cordis 服务,会增加注册和注入依赖,却没有提供独立替换的运行能力。内部函数和类已经能够完成复用。共同的执行基类还会把新进程清理与按所有者保留进程的生命周期耦合起来,因此提供方共用处理机制,各自管理生命周期。
序列化任意 JavaScript 对象无法保留原生资源和闭包。重放旧程序可能重复写入和输入操作。这两种做法都会暗示内存保留模式没有提供的恢复保证。
Harness 进程内的独立执行上下文与模型代码共享进程生命周期,无法单独终止同步死循环。托管子进程可以在正常使用时保留导入和变量,同时支持取消与资源清理。仅有进程所有权不能提供文件沙箱或多租户隔离。
## 影响
选择 `persistent-in-memory` 会保留执行状态;[权限决策](2026-10-08-persistent-ptc-sandbox.zh.md)定义其文件限制。嵌套工具仍遵循原有授权和日志规则。即使 Harness 没有重启,内存和原生句柄也可能丢失。模型需要从引用或资源错误中恢复,运行时不会自动重试程序。
空闲所有者保留一个没有过期计时器的进程。调用方显式释放所有者,没有所有者的服务调用使用临时进程。程序使用 REPL 语句和末尾表达式,不支持顶层 return;同一保留所有者的调用不能重叠。工具 schema 和程序指引跟随所选模式。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-09-24-deepseek-account-remote-model-catalog.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-deepseek-account-remote-model-catalog.md
/agent-note-deepseek-account-models-come-from-the-account-listing-and-gate-by-client-version:
en: e6d82714ac89a7ae
zh: 54c14278e749e7e1
/agent-note-deepseek-account-models-come-from-the-account-listing-and-gate-by-client-version/problem:
en: 2eeb916eb65c66e7
zh: 7bf5c0abb8bb61e4
/agent-note-deepseek-account-models-come-from-the-account-listing-and-gate-by-client-version/decision:
en: 42e800c4cd3b6ebe
zh: 06fabe5a9681fa27
/agent-note-deepseek-account-models-come-from-the-account-listing-and-gate-by-client-version/alternatives-considered:
en: 7c7880fdb0cc95f6
zh: 2b8a539192626789
/agent-note-deepseek-account-models-come-from-the-account-listing-and-gate-by-client-version/consequences:
en: 352ece6c2decaaf7
zh: 1d0c3620fd26a35c
@@ -0,0 +1,36 @@
# Agent Note: DeepSeek account models come from the account listing and gate by client version
Status: implemented
English | [中文](2026-09-24-deepseek-account-remote-model-catalog.zh.md)
## Problem
The account route's catalog was deployment configuration: a build advertised the models it shipped with, a new model reached users only through a release, and nothing kept an older build from requesting a model it cannot drive. The account service publishes per-account model metadata at `GET /models`, and that listing can name models an older build does not understand.
## Decision
- [`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.
## Alternatives considered
**Server-side filtering by client version only.** Codex sends `client_version` and lets its backend drop models. An older build then cannot show that a newer version supports a model, and a listing that omits a declaration would expose an unknown model.
**Default parameters for unknown models.** Reasonix and Pi drive unknown ids with generic defaults, and MiniMax treats that as a defect. A model that needs client changes would run with the wrong behavior.
**Whole-listing validation.** Codex, ZCode, and Kimi reject the whole listing on one bad field. One malformed row would then send every account back to the bundled catalog.
**Persisting the last good listing.** A durable cache adds a persistence format and its migration duty; the bundled catalog already covers offline starts.
**Keeping `models` with merge-by-id semantics.** The API-key route's `models` replaces its catalog, so the same field name with merge semantics would invert its meaning between sibling routes.
**Generalizing the shared transport.** Per-model effort levels, an output-capability field, and catalog-change announcements would widen the Messages package for one consumer. The listed effort levels match the bundled defaults, and a model that needs different ones needs a release, which the gate enforces.
## Consequences
- An older build never offers a model it does not know unless the service declares a minimum version that build satisfies, and the service must declare `min_version` for a future build to show that an upgrade enables a model.
- A modified client can still request any id; the service enforces version floors only if it rejects such requests itself.
- The first account access after sign-in may wait up to `modelsTimeoutMs`; an offline start uses bundled capabilities until a listing loads; a listing change during a running process reaches the Web picker at its next catalog refresh.
@@ -0,0 +1,36 @@
# Agent Note: DeepSeek 账号模型来自账号列表,并按客户端版本门控
Status: implemented
[English](2026-09-24-deepseek-account-remote-model-catalog.md) | 中文
## 问题
账号路由的目录原本是部署配置:每个构建只提供它发布时自带的模型,新模型只能随发版到达用户,也没有办法阻止旧构建请求它驱动不了的模型。账号服务在 `GET /models` 上按账号发布模型元数据,这份列表可能包含旧构建不认识的模型。
## 决策
- [`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` 两个提供方钩子;列表、门控与合并都留在账号包中。
## 考虑过的替代方案
**只在服务端按客户端版本过滤。** Codex 发送 `client_version`,由后端去掉模型。这样旧构建无法提示新版本支持某个模型,而且列表漏掉声明时会暴露未知模型。
**未知模型套用默认参数。** Reasonix 和 Pi 用通用默认值驱动未知 id,MiniMax 把这种做法当作缺陷。需要客户端改动的模型会以错误的方式运行。
**整份列表严格校验。** Codex、ZCode 和 Kimi 会因为一个坏字段拒绝整份列表。一条格式错误的记录就会让所有账号退回内置目录。
**持久化上一次成功的列表。** 持久缓存要新增持久化格式并承担迁移责任;内置目录已经覆盖离线启动。
**保留 `models` 并改为按 id 合并。** API key 路由的 `models` 会替换整个目录,同名字段在兄弟路由上改成合并语义会让含义相反。
**扩展共享传输层。** 按模型的推理档位、输出能力字段和目录变更通知,会为一个使用方扩大 Messages 包的范围。列表中的推理档位与内置默认值一致;需要不同档位的模型需要发版适配,门控正是用来保证这一点。
## 影响
- 旧构建永远不会提供它不认识的模型,除非服务端声明了该构建满足的最低版本;服务端必须声明 `min_version`,将来的构建才能提示升级后可用。
- 改过的客户端仍然可以请求任意 id;只有服务端自己拒绝这类请求时,版本下限才会被强制执行。
- 登录后的第一次账号访问最多等待 `modelsTimeoutMs`;离线启动在加载到列表之前使用内置能力;进程运行中途的列表变化要等 Web 选择器下一次刷新目录时才会显示。
@@ -15,8 +15,8 @@
en: 5cfe619038339caa
zh: c2d2651d304d867c
/agent-note-preset-scoped-schedule-tools/consequences:
en: 546d83d28487d1c1
zh: 438922cd4f3ff9ec
en: d7c7e6b8e6268559
zh: 801e5be70e00cc90
/agent-note-preset-scoped-schedule-tools/testing:
en: 1c5f34cf30a24fd7
zh: 6f7a372de52d0ecd
en: d42cb866907a96b5
zh: 6e66f8babe744bcb
@@ -26,9 +26,9 @@ English | [中文](2026-09-24-preset-scoped-schedule-tools.zh.md)
- Each preset that mounts the row pays the four schemas' fixed token cost, and tool availability is read from the composition instead of inferred from the Host service's presence.
- A deployment can mount `dsh-schedule` for storage and delivery without granting its agents model-driven reminder management.
- `dsh-schedule` injects no `ctx.tools` and registers no model-facing tool.
- A delegated child cannot use the four tools through either layer: the `tool-subagent` and `tool-subagent-fork` rows in `standard`, `cordis`, and `ptc` deny all four names through `toolFilter`, and the provider applies that filter in the child scope through `ctx.tools.restrict()`, so the tools leave the child's prompt; `subagentCallerRefusal` in `dsh-tool-schedule` independently returns `{ code: 'subagent_session', message: 'A delegated subagent cannot use reminders.' }` from every one of the four tools when the caller's delegation depth is above zero, so a call that reaches a tool anyway is refused.
- A delegated child inherits the preset's four reminder schemas. `subagentCallerRefusal` in `dsh-tool-schedule` returns `{ code: 'subagent_session', message: 'A delegated subagent cannot use reminders.' }` from each tool when the caller's delegation depth is above zero. The [preset composition decision](../simplification/2026-10-09-preset-owned-subagent-composition.md) removes the redundant child filter; visibility does not authorize execution.
- A Session a delegated child owns can never receive a delivered reminder, so `ScheduleService.create` and `ScheduleService.update` refuse it with `subagent_session`. The refusal reads the delegation depth — the accounting the delegation cap itself enforces, carried by the persisted session header across a cold resume. The rule sits in the service, so other consumers reach it too — the Automation surface included.
## Testing
`packages/schedule/tool-schedule/tests/tool-schedule.spec.ts` pins the four definitions and their error mapping, including `refuses every tool for a delegated child caller`. `apps/cli/tests/web-agent-presets.e2e.ts` asserts the `minimal` preset's tool list, and `snapshots/web/minimal-preset/tool-schemas.expected.json` records its bash-only tool table. `packages/schedule/schedule/tests/subagent-ownership.spec.ts` pins the delegation-depth refusal, and `scripts/optional-bundles.spec.ts` asserts that both delegation rows in the three presets deny all four tool names.
`packages/schedule/tool-schedule/tests/tool-schedule.spec.ts` pins the four definitions and their error mapping, including `refuses every tool for a delegated child caller`. `apps/cli/tests/web-agent-presets.e2e.ts` asserts the `minimal` preset's tool list, and `snapshots/web/minimal-preset/tool-schemas.expected.json` records its tool table without reminder schemas. `packages/schedule/schedule/tests/subagent-ownership.spec.ts` pins the delegation-depth refusal, and `scripts/optional-bundles.spec.ts` asserts that both delegation rows in the three presets omit `toolFilter`. The Web preset e2e also checks equal parent/child tool schemas and refusal from all four reminder executors for fresh and seeded children. `snapshots/web/subagent-fork-schedule` records all four refusals in a seeded fork through the shipped Web composition and shares the standard preset's prompt and schema pins.
@@ -26,9 +26,9 @@ Status: implemented
- 挂载该行的每个 preset 承担四个 schema 的固定 token 成本,工具可用性取自组合,而不是由宿主服务是否存在推断。
- 部署可以只为存储与投递挂载 `dsh-schedule`,而不授予其 Agent 由模型驱动的提醒管理能力。
- `dsh-schedule` 不注入 `ctx.tools`,也不注册任何面向模型的工具。
- 被委派的子 agent 无法通过两道中的任何一道使用这四个工具:`standard`、`cordis` 与 `ptc` 中的 `tool-subagent` 与 `tool-subagent-fork` 两行通过 `toolFilter` 拒绝全部四个名称,provider 会在子作用域内通过 `ctx.tools.restrict()` 应用该过滤,因此这些工具会从子 agent 的 prompt 中消失;`dsh-tool-schedule` 中的 `subagentCallerRefusal` 会在调用方委派深度大于零时,从四个工具中的每一个独立返回 `{ code: 'subagent_session', message: 'A delegated subagent cannot use reminders.' }`,因此即使某个调用仍能到达工具,也会被拒绝。
- 被委派的子 agent 继承 preset 的四个提醒 schema。调用方委派深度大于零时,`dsh-tool-schedule` 中的 `subagentCallerRefusal` 从每个工具返回 `{ code: 'subagent_session', message: 'A delegated subagent cannot use reminders.' }`。[preset 组装决策](../simplification/2026-10-09-preset-owned-subagent-composition.zh.md)移除了重复的子级过滤;可见性不授予执行权限。
- 被委派子 agent 拥有的 Session 永远收不到投递的提醒,因此 `ScheduleService.create` 与 `ScheduleService.update` 会以 `subagent_session` 拒绝它。该拒绝读取委派深度——委派封顶本身执行的记账,由持久化 session header 承载并跨冷恢复保留。这条规则在服务层,因此其它消费方(包括自动化任务页面)也会命中。
## Testing
`packages/schedule/tool-schedule/tests/tool-schedule.spec.ts` 固定这四个定义及其错误映射,包括 `refuses every tool for a delegated child caller`。`apps/cli/tests/web-agent-presets.e2e.ts` 断言 `minimal` preset 的工具列表,`snapshots/web/minimal-preset/tool-schemas.expected.json` 记录其只含 `bash` 的工具表。`packages/schedule/schedule/tests/subagent-ownership.spec.ts` 固定委派深度拒绝,`scripts/optional-bundles.spec.ts` 断言三个 preset 的两行委派行都拒绝全部四个工具名。
`packages/schedule/tool-schedule/tests/tool-schedule.spec.ts` 固定这四个定义及其错误映射,包括 `refuses every tool for a delegated child caller`。`apps/cli/tests/web-agent-presets.e2e.ts` 断言 `minimal` preset 的工具列表,`snapshots/web/minimal-preset/tool-schemas.expected.json` 记录其不含提醒工具 schema 的工具表。`packages/schedule/schedule/tests/subagent-ownership.spec.ts` 固定委派深度拒绝,`scripts/optional-bundles.spec.ts` 断言三个 preset 的两行委派行都不含 `toolFilter`。Web preset e2e 还检查父子工具 schema 相等,且四个提醒执行器对新建和带种子的子级均拒绝执行。`snapshots/web/subagent-fork-schedule` 通过出货 Web 组合记录带种子 fork 中的四次拒绝,并共享 standard preset 的提示和 schema 预期。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-09-26-cua-application-consent.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-26-cua-application-consent.md
/agent-note-application-consent-for-native-computer-use:
en: d570ebf4790311f9
zh: a6ec87cb3ae3d0f1
/agent-note-application-consent-for-native-computer-use/problem:
en: b32a7ce6483b9b48
zh: ec0938ad032b99d8
/agent-note-application-consent-for-native-computer-use/decision:
en: 92247ff599c1103f
zh: 26845420a9711c83
/agent-note-application-consent-for-native-computer-use/alternatives-considered:
en: 4693dcd217c4fedb
zh: 1dc9bf9067314006
/agent-note-application-consent-for-native-computer-use/consequences:
en: 0146a69ab2086edc
zh: 4257bc1ab4432c3f
@@ -0,0 +1,38 @@
# Agent Note: Application consent for native Computer Use
Status: implemented
English | [中文](2026-09-26-cua-application-consent.zh.md)
## Problem
macOS privileges authorize an execution host, not a user's choice of target application. Retained app and window objects can outlive that choice. Concurrent permission decisions must not restore access after the user revokes it.
## 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.
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.
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.
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.
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.
## 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.
**Add an approval database or Session control stream.** Profile settings already own user configuration, and User Questions already owns interactive answers.
**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.
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.
@@ -0,0 +1,38 @@
# Agent Note: 原生 Computer Use 的应用授权
Status: implemented
[English](2026-09-26-cua-application-consent.md) | 中文
## 问题
macOS 权限授权执行宿主,不能表达用户允许操作哪些目标应用。保留的应用和窗口对象可能比用户的许可存活更久。并发授权决定不能在用户撤销后恢复访问。
## 决策
CUA 执行器在每次对象 API 和原生工具调用时检查应用授权。人工决定复用 User Questions 服务,长期许可保存在 provider 的现有 profile 配置中。通用工具审批和操作系统权限仍是独立要求。任意 persistent PTC 代码和另行授权的 shell 工具保留各自的执行权限。
长期身份由规范化可执行文件路径和经过验证的签名指定要求组成,以 SHA-256 摘要表示。路径阻止许可跟随应用副本,签名要求区分替换程序,并允许符合相同签名要求的更新。签名不可验证或使用 ad-hoc 签名的应用只能获得任务授权,许可绑定进程实例和 Agent 连接生命周期。显示名称不用于确定身份。
选择尚未运行的应用时,先准备可执行文件身份和句柄,不立即启动。同一授权决定保护内部启动续接,helper 在启动前再次核对可执行文件,并在启动后绑定实际进程。已签名应用沿用签名指定要求身份;签名不可验证的程序使用本次待启动选择的内容身份,不能生成长期许可。先准备元数据再询问,可以避免启动用户拒绝的应用。
同一任务对同一应用的选择串行执行,避免未完成的问题产生相互冲突的决定。长期许可的新增在同一写入队列内读取当前配置。拒绝会阻止任务内的再次询问;用户显式恢复的长期许可在执行检查时优先于旧拒绝记录。
删除长期许可会使保留目标应用句柄的 Agent 连接失效,并取消活动操作;Agent 或提供方销毁时等待对应的原生连接清理。重新授权使用新连接,因此旧应用对象和窗口快照不能重新生效。[原生对象决策](2026-09-17-experimental-cua-object-api.zh.md)继续负责进程身份、坐标语义和与执行器分离的设计。
## 考虑过的替代方案
**依赖模型指引或隐藏控件。** 保留的对象和直接工具调用仍能到达原生操作。执行器必须在派发时检查用户决定。
**保存应用名称或未签名路径。** 替换程序可以复用二者。长期许可需要可验证的签名身份;任务授权支持未签名的开发应用,但不承诺跨进程复用。
**新增审批数据库或 Session 控制流。** Profile settings 已负责用户配置,User Questions 已负责交互回答。
**撤销后保留原生状态。** 原生应用查找可能返回旧句柄并保留窗口快照。关闭 Agent 连接可以通过现有传输清理释放其原生状态;保留其他应用的句柄则需要新增按应用销毁状态的原生协议。
## 影响
撤销一个应用也会使同一 Agent 连接中其他应用的句柄失效。这些应用需要重新选择;长期许可仍可复用,任务许可随之失效。其他任务使用独立连接,除非也持有被撤销应用的许可,否则不受影响。
授权决定和失败结果由策略回归测试及 CUA 录制会话验证。桌面安装包的二进制分发、签名和打包宿主权限不属于源码启动流程。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-09-27-progressive-office-skills.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-progressive-office-skills.md
/agent-note-progressive-office-skills-with-ordinary-shell-execution:
en: 42e2f062c37e9ef1
zh: 5cd3d421adec1cac
/agent-note-progressive-office-skills-with-ordinary-shell-execution/problem:
en: a9984a594cdac98d
zh: 38768e7699bc8d39
/agent-note-progressive-office-skills-with-ordinary-shell-execution/decision:
en: d057c558d6b9803c
zh: acf3fdbc4cd7cee3
/agent-note-progressive-office-skills-with-ordinary-shell-execution/alternatives-considered:
en: 8bca955255e6d982
zh: bc313a4bcab3f6f9
/agent-note-progressive-office-skills-with-ordinary-shell-execution/consequences:
en: cd4ff2423b2f5f9e
zh: 63135aeae9c46415
@@ -0,0 +1,32 @@
# Agent Note: Progressive Office skills with ordinary shell execution
Status: implemented
English | [中文](2026-09-27-progressive-office-skills.zh.md)
## Problem
Combined authoring and rendering instructions spend context on operations unrelated to ordinary editing. Rendering tool activation adds a second discovery path and durable state even though shell tools already own execution permissions and process management. A missing renderer can prevent loading otherwise usable editing guidance.
## Decision
The [Office provider](../../../../packages/skill/skill-office/README.md) lists a compact router and six format-specific authoring or operation skills. The model loads full instructions by name and selects rendering for appearance-focused changes unless the user explicitly skips it. Rendering uses a private, randomly named temporary directory inside the task workspace and removes it after inspection. Skills invoke the shared Python checker and configured LibreOffice Kit CLI through ordinary Bash or PowerShell. SDK and Desktop own their Office composition separately; ordinary Web and the shared base do not mount these plugins.
Carriers own interpreter and resource locations. Operation skills append the configured Node and CLI paths only when their bodies are loaded; authoring skills use the selected Python and shared checker resource. Loading instructions does not prepare engines or authoring payloads. Ordinary shell execution retains its existing permissions and process management.
Office dependency ranges allow patch updates; the root lock records the resolved adapter and engine versions.
## Alternatives considered
**Keep a dedicated renderer tool.** A small typed schema constrains rendering arguments, but requires its own execution configuration, scoped registration, projection, and activation lifecycle. The CLI operation skills accept shell quoting and model-selected arguments in exchange for reusing the existing approval, sandbox, cancellation, and output mechanisms.
**Hide subskills from the catalog.** This requires a generic visibility policy independent of invocation permission. Listing seven short summaries preserves the existing skill API while full instruction bodies remain demand-loaded.
**Automatically inject guides for file extensions.** Extensions do not distinguish text edits, recalculation, conversion, and requested visual checks. Model-selected routing keeps unrelated operations out of context and permits explicit direct invocation.
## Consequences
Instruction loading appends ordinary logged skill results; completion does not erase them. Historical tool records remain readable and existing declaration reconciliation handles resumed sessions without a Session format change or agent-loop modification. A dedicated renderer would need a new independently justified user or security requirement before reintroduction.
Verification covers catalog entries, denied and explicit invocation, authoring without executable paths, native/PTC requests, resume and fork, ordinary and minimal profile composition, resource packaging, context budgets, and real-model operation selection. Rendering failures preserve usable sources and stop visual checks; successful commands alone cannot establish usable images.
@@ -0,0 +1,32 @@
# Agent Note: Office 技能渐进加载与普通 shell 执行
Status: implemented
[English](2026-09-27-progressive-office-skills.md) | 中文
## 问题
合并的创作和渲染说明会为普通编辑无关的操作消耗上下文。渲染工具激活增加第二条发现路径和持久状态,而 shell 工具已经负责执行权限及进程管理。缺少渲染器也可能阻止加载原本可用的编辑指南。
## 决策
[Office 提供者](../../../../packages/skill/skill-office/README.zh.md) 列出精简路由和六个各格式创作或操作技能。模型按名称加载完整说明;以外观为目标的修改会选择渲染,除非用户明确要求跳过。渲染使用任务工作区内随机命名的私有临时目录,并在检查后删除。技能通过普通 Bash 或 PowerShell 调用共享 Python 检查器和配置的 LibreOffice Kit CLI。SDK 和 Desktop 分别拥有 Office 组合;普通 Web 和共享 base 不挂载这些插件。
载体负责解释器和资源位置。操作技能仅在加载正文时附加配置的 Node 与 CLI 路径;创作技能使用选定的 Python 和共享检查器资源。加载说明不准备引擎或创作 payload。普通 shell 执行保留现有权限和进程管理。
Office 依赖范围允许补丁版本更新;根锁文件记录实际解析的适配器与引擎版本。
## 考虑过的替代方案
**保留专属渲染工具。** 小型类型化 schema 能约束渲染参数,但需要专属执行配置、作用域注册、投影和激活生命周期。CLI 操作技能接受 shell 引用和模型选择参数的成本,以复用现有审批、沙箱、取消及输出机制。
**从目录隐藏子技能。** 这需要独立于调用权限的通用可见性策略。列出七条简短摘要可保留现有技能 API,完整说明仍按需加载。
**按文件扩展名自动注入指南。** 扩展名无法区分文本编辑、重计算、转换和用户要求的视觉检查。模型选路使无关操作不进入上下文,并允许显式直接调用。
## 影响
说明加载追加普通的已记录技能结果;操作完成不会擦除这些结果。历史工具记录仍可读取,恢复会话使用现有声明更新机制,无需修改 Session 格式或 agent loop。重新引入专属渲染器需要新的、独立成立的用户或安全要求。
验证覆盖目录条目、拒绝及显式调用、无可执行文件路径时的创作、native/PTC 请求、恢复和 fork、常规及 minimal 组合、资源打包、上下文预算和真实模型操作选择。渲染失败时保留可用源文件并停止视觉检查;命令成功本身不能证明图片可用。
@@ -12,23 +12,23 @@
en: a15c7c13f167e5ac
zh: a15c7c13f167e5ac
/agent-note-refresh-runtime-resolution-after-package-operations-and-expire-hmr-package-configuration/decision/responsibilities:
en: 3ffd0c78fa204b04
zh: f468bab00a7ff357
en: d53a850a2deabc08
zh: 01c2f735f2a9562a
/agent-note-refresh-runtime-resolution-after-package-operations-and-expire-hmr-package-configuration/decision/runtime-resolution:
en: ca45b973e1968533
zh: 002f23bb70e4fed7
en: 42a3ed903fbb5483
zh: 7e1f6df1137794c3
/agent-note-refresh-runtime-resolution-after-package-operations-and-expire-hmr-package-configuration/decision/plugin-manager:
en: 9b04926c175f22e8
zh: fbf05dd4065b0738
en: 7ec1ad2a691bbd12
zh: 01057bbe0240056a
/agent-note-refresh-runtime-resolution-after-package-operations-and-expire-hmr-package-configuration/decision/hmr-package-configuration-expiry:
en: c7de8dafe47a5587
zh: 6010a70d8451edd1
/agent-note-refresh-runtime-resolution-after-package-operations-and-expire-hmr-package-configuration/decision/future-work:
en: 31818f8f7f80470b
zh: ecb7ac85bb49cf6d
en: 8b5faf72418a43dd
zh: 3d676c9b54697a2a
/agent-note-refresh-runtime-resolution-after-package-operations-and-expire-hmr-package-configuration/alternatives-considered:
en: 7c1c6d21570cda0d
zh: e6f8334c85f1b404
en: f04a27b3525180b0
zh: 9f3a01d0400c5a88
/agent-note-refresh-runtime-resolution-after-package-operations-and-expire-hmr-package-configuration/verification:
en: 939cedceed200cb2
zh: c7bb02d0da400c8d
@@ -17,9 +17,9 @@ Two pieces of Host state did not follow changes while the process ran:
| Owner | Does | Does not |
|---|---|---|
| resolver (`app-boot/src/profile-resolution/resolver.ts`) | `replace()` allows removing `scope: profile` entries and profile-local names | Touch any Node cache |
| plugin-manager | Calls `pluginPackages.refresh()` at fixed points of package operations | Construct resolutions or manage module and package-configuration caches |
| HMR (`packages/boot/hmr`) | Expires package configuration and preserves ordinary reloads for manifests loaded as JSON modules | Reload plugins for configuration-only manifests; handle `node_modules` package replacement |
| resolver (`app-boot/src/profile-resolution/resolver.ts`) | `replace()` allows removing `scope: profile` entries and profile-local names, and changing a retained profile entry's version in place | Touch any Node cache |
| plugin-manager | Without HMR, calls `pluginPackages.refresh()` at fixed points of package operations; with HMR, leaves publication to its recomposition | Construct resolutions or manage module and package-configuration caches |
| HMR (`packages/boot/hmr`) | Expires package configuration and preserves ordinary reloads for manifests loaded as JSON modules; replaces plugins whose loaded package files changed during recomposition ([replacement Note](2026-10-01-replace-changed-package-modules-on-recomposition.md)) | Reload plugins for configuration-only manifests |
### Runtime resolution
@@ -29,20 +29,18 @@ Validation of a new generation in `replace()`:
- `scope: profile` entries may be removed.
- Profile-local package names may be removed.
- Retained profile entries keep their normalized directory, version, and scope, but may change declaring anchor when another selected bundle supplies the same package. Installation mappings and declaring anchors remain unchanged. The profile scope cannot change; a new local name cannot override an existing entry; a published link name cannot select another real directory.
- Retained profile entries keep their normalized directory and scope, but may change declaring anchor when another selected bundle supplies the same package, and may change version when the package is upgraded in place ([replacement Note](2026-10-01-replace-changed-package-modules-on-recomposition.md)). Installation mappings and declaring anchors remain unchanged. The profile scope cannot change; a new local name cannot override an existing entry. A published link name may select another real directory; Node's cached real paths of the previous one are relocated.
### Plugin Manager
| Operation | Publication point |
|---|---|
| Install a new package | After pnpm succeeds and the bundle selection is saved, before reconciliation, whether or not the bundle is enabled |
| Overwrite an installed package | No publication; the result is `restart-required` |
| Enable a bundle | After the selection is saved, before reconciliation |
| Disable a bundle | With HMR, after reconciliation stops its plugins; without HMR, no publication, and running plugins keep the current table |
| Remove | After pnpm remove succeeds, inside the HMR transaction |
| Failed or cancelled install, failed removal | No publication |
| Install, enable, disable or remove with HMR | Inside HMR's recomposition, after the rows the generation drops stopped and before the generation applies ([replacement Note](2026-10-01-replace-changed-package-modules-on-recomposition.md)) |
| Install a new package or enable a bundle without HMR | After the profile files are saved, before reconciliation |
| Remove without HMR | After pnpm remove succeeds |
| Disable without HMR, an install over an installed package, a failed or cancelled install, a failed removal | No publication; an install over a package the spec names is refused before pnpm runs |
`createRuntimeResolution()` returns a `ProfileRuntimeResolution`, which privately keeps the installation anchor, Harness home, and profile directory it was computed from. Its `computeLatestResolution()` rereads that profile with the same inputs and returns a new object for the latest generation, leaving the original unchanged; Workers receive only the table fields. `PluginPackages.refresh()` calls the current resolution's `computeLatestResolution()` and then `replace()`; a resolution constructed as plain data cannot be refreshed and makes the call throw.
`createRuntimeResolution()` returns a `ProfileRuntimeResolution`, which privately keeps the installation anchor, Harness home, and profile directory it was computed from. Its `computeLatestResolution()` rereads that profile with the same inputs and returns a new object for the latest generation, leaving the original unchanged; Workers receive only the table fields. `PluginPackages.refresh()` calls the current resolution's `computeLatestResolution()` and then `replace()`; a resolution constructed as plain data cannot be refreshed and makes the call throw. app-boot's `recomposeProfile()` first stops the rows the generation no longer lists, so their plugins dispose while their packages are still mapped, then publishes the latest generation once; a table that is already published is not published again.
Without HMR, deselecting a startup bundle does not stop its plugins. While such a bundle remains deselected, later package operations keep the existing runtime table instead of publishing its removal. Other no-HMR installations and removals of non-running bundles still publish normally.
@@ -79,7 +77,7 @@ The binding's `getNearestParentPackageJSON` is called only by `package_json_read
### Future Work
- Online replacement inside `node_modules`, same-path reinstall, changed link targets, and cross-package reload propagation remain unsupported. Package updates need process restart; this does not guarantee that every management result already reports that requirement correctly.
- A retained profile mapping that moves to another directory waits for a restart; Plugin Manager moves a package by removing and installing it. In-place replacement inside `node_modules` is handled by the [replacement Note](2026-10-01-replace-changed-package-modules-on-recomposition.md).
- TSX versions using an asynchronous loader thread keep that thread's package configuration outside these hooks. Synchronizing it is deferred; this change does not provide general Worker cache synchronization.
- CommonJS private-request-cache refresh with registered synchronous resolve hooks is deferred. Those requests retain their original loader behavior.
- Disposing HMR restores the native readers, whose previous cached configuration can become visible again. Preserving invalidation state across HMR replacement is deferred.
@@ -90,7 +88,7 @@ The binding's `getNearestParentPackageJSON` is called only by `package_json_read
**Reread every observed manifest when runtime resolution publishes (#5496).** This places package-configuration expiry in table publication, decoupled from module caches: overwriting an installed package reports restart, yet the next unrelated publication makes new imports load the new version while the old plugin still runs. Package configuration is content and belongs with module caches, in HMR.
**Let HMR reload packages inside `node_modules`.** It requires finding affected plugins over a module graph that includes `node_modules`, preventing duplicate evaluation of shared libraries and Cordis, and handling missing dynamic-import edges, module side effects, and Workers. It is not done here; those scenarios keep requiring restart.
**Let HMR reload packages inside `node_modules`.** It requires finding affected plugins over a module graph that includes `node_modules`, preventing duplicate evaluation of shared libraries and Cordis, and handling missing dynamic-import edges, module side effects, and Workers. It is not done here; the [replacement Note](2026-10-01-replace-changed-package-modules-on-recomposition.md) adds it at profile recomposition.
**Reload every plugin for a package.json change.** Configuration-only manifests do not require module evaluation. Manifests actually imported as JSON modules retain ordinary dependency-driven reloads.
@@ -17,9 +17,9 @@ Host 运行期间有两处状态没有随变化更新:
| 负责方 | 做什么 | 不做什么 |
|---|---|---|
| resolver(`app-boot/src/profile-resolution/resolver.ts`) | `replace()` 允许移除 `scope: profile` 条目和 profile 本地包名 | 不碰 Node 的任何缓存 |
| plugin-manager | 在包操作的确定时点调用 `pluginPackages.refresh()` | 不构造 resolution;不管模块缓存与包配置缓存 |
| HMR(`packages/boot/hmr`) | 使包配置失效,并保留作为 JSON 模块加载的 manifest 的普通重载 | 不因仅作为配置的 manifest 重载插件;不处理 `node_modules` 包替换 |
| resolver(`app-boot/src/profile-resolution/resolver.ts`) | `replace()` 允许移除 `scope: profile` 条目和 profile 本地包名,并允许原地更改保留的 profile 条目的版本 | 不碰 Node 的任何缓存 |
| plugin-manager | 没有 HMR 时,在包操作的确定时点调用 `pluginPackages.refresh()`;有 HMR 时,把发布交给其重新组合 | 不构造 resolution;不管模块缓存与包配置缓存 |
| HMR(`packages/boot/hmr`) | 使包配置失效,并保留作为 JSON 模块加载的 manifest 的普通重载;重新组合时替换已加载包文件发生变化的插件([替换说明](2026-10-01-replace-changed-package-modules-on-recomposition.zh.md)) | 不因仅作为配置的 manifest 重载插件 |
### runtime resolution
@@ -29,20 +29,18 @@ Host 运行期间有两处状态没有随变化更新:
- `scope: profile` 的条目可以移除。
- profile 本地包名可以移除。
- 保留的 profile 条目保持规范化目录、版本与作用域不变;另一个已选 bundle 提供同一包时,可以更换声明锚点。安装范围的映射与声明锚点保持不变。profile 范围不能变;新本地包名不能覆盖已有条目;已发布的 link 名称不能换真实目录。
- 保留的 profile 条目保持规范化目录与作用域不变;另一个已选 bundle 提供同一包时,可以更换声明锚点;包被原地升级时,可以更改版本([替换说明](2026-10-01-replace-changed-package-modules-on-recomposition.zh.md))。安装范围的映射与声明锚点保持不变。profile 范围不能变;新本地包名不能覆盖已有条目。已发布的 link 名称可以选中其他真实目录;Node 缓存的原目录真实路径会被重新定位。
### plugin-manager
| 操作 | 发布时点 |
|---|---|
| 安装新包 | pnpm 成功、保存 bundle 选择之后,reconciliation 之前;无论是否立即启用都发布 |
| 覆盖已安装的包 | 不发布,结果为 `restart-required` |
| 启用 bundle | 保存选择后发布,再做 reconciliation |
| 停用 bundle | 有 HMR 时,reconciliation 让插件退出后再发布;没有 HMR 时不发布,运行中的插件继续使用原表 |
| 卸载 | pnpm remove 成功后,在 HMR 事务内发布 |
| 安装失败、取消、卸载失败 | 不发布 |
| 有 HMR 时的安装、启用、停用或卸载 | 在 HMR 的重新组合内,新一代丢弃的行停止之后、新一代应用之前([替换说明](2026-10-01-replace-changed-package-modules-on-recomposition.zh.md)) |
| 没有 HMR 时安装新包或启用 bundle | 保存 profile 文件之后,reconciliation 之前 |
| 没有 HMR 时卸载 | pnpm remove 成功后 |
| 没有 HMR 时停用、覆盖已安装的包、安装失败或取消、卸载失败 | 不发布;spec 指明已安装的包时,覆盖安装在 pnpm 运行之前被拒绝 |
`createRuntimeResolution()` 返回 `ProfileRuntimeResolution`,它私有保存构造时的 installAnchor、home 和 profile 目录;`computeLatestResolution()` 用同一组输入重新读取 profile,返回表示最新代的新对象,原实例不变;Worker 只收到表数据。`PluginPackages.refresh()` 调用当前 resolution 的 `computeLatestResolution()` 再 `replace()`;以纯数据构造的 resolution 无法刷新,调用时抛错。
`createRuntimeResolution()` 返回 `ProfileRuntimeResolution`,它私有保存构造时的 installAnchor、home 和 profile 目录;`computeLatestResolution()` 用同一组输入重新读取 profile,返回表示最新代的新对象,原实例不变;Worker 只收到表数据。`PluginPackages.refresh()` 调用当前 resolution 的 `computeLatestResolution()`,再调用 `replace()`;以纯数据构造的 resolution 无法刷新,调用时抛错。app-boot 的 `recomposeProfile()` 先停止新一代不再列出的行,使其插件在包仍有映射时完成释放,再发布一次最新一代;已发布的表不会再次发布。
没有 HMR 时,取消启动 bundle 的选择不会停止它的插件。只要该 bundle 仍未选中,后续包操作就保留当前 runtime 表,不发布其移除。其他无 HMR 的安装和未运行 bundle 卸载仍正常发布。
@@ -79,7 +77,7 @@ binding 的 `getNearestParentPackageJSON` 只被 `package_json_reader` 自己调
### 后续工作
- 不支持 `node_modules` 内在线替换、同路径重装、link 换目标及跨包重载传播。包更新需要重启进程;这不保证每个管理操作的结果都已正确报告这一要求。
- 移动到其他目录的保留 profile 映射需要等待重启;Plugin Manager 通过卸载再安装来移动包。`node_modules` 内的原地替换由[替换说明](2026-10-01-replace-changed-package-modules-on-recomposition.zh.md)处理。
- 使用异步 loader 线程的 TSX 版本,其线程内包配置不受这些 hook 管理。线程同步留待后续;本次不提供通用 Worker 缓存同步。
- 注册了同步 resolve hooks 时,CommonJS 私有请求缓存刷新留待后续;这些请求保留原有 loader 行为。
- 销毁 HMR 会恢复原生 reader,其此前缓存的配置可能重新可见。跨 HMR 替换保留失效状态留待后续。
@@ -90,7 +88,7 @@ binding 的 `getNearestParentPackageJSON` 只被 `package_json_reader` 自己调
**在 runtime resolution 发布时全量重读已见过的 manifest(#5496)。** 它把包配置失效放进了绑定表发布,和模块缓存脱了钩:覆盖已安装的包时报需要重启,但下一次无关的发布会让新导入拿到新版本,旧插件仍在运行。包配置缓存属于内容,应该和模块缓存由同一方负责,也就是 HMR。
**让 HMR 支持 `node_modules` 内的包重载。** 这需要沿包含 `node_modules` 的模块图确定受影响插件,防止共享库和 Cordis 被重复求值,还要处理动态 import 漏边、模块副作用和 Worker。本次不做,这些场景继续要求重启。
**让 HMR 支持 `node_modules` 内的包重载。** 这需要沿包含 `node_modules` 的模块图确定受影响插件,防止共享库和 Cordis 被重复求值,还要处理动态 import 漏边、模块副作用和 Worker。本次不做;[替换说明](2026-10-01-replace-changed-package-modules-on-recomposition.zh.md)在 profile 重新组合时加入了这一能力。
**package.json 变化时重载所有插件。** 仅作为配置的 manifest 不要求模块求值。实际作为 JSON 模块导入的 manifest 保留普通的依赖驱动重载。
@@ -0,0 +1,34 @@
# Bilingual-pair consistency record for 2026-10-01-replace-changed-package-modules-on-recomposition.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-01-replace-changed-package-modules-on-recomposition.md
/agent-note-replace-plugins-whose-loaded-package-files-changed-during-profile-recomposition:
en: 72fe06d4e304418d
zh: bc3c96e957889bc1
/agent-note-replace-plugins-whose-loaded-package-files-changed-during-profile-recomposition/problem:
en: c43fb0d2df54b28f
zh: 6636548449663fc5
/agent-note-replace-plugins-whose-loaded-package-files-changed-during-profile-recomposition/decision:
en: a15c7c13f167e5ac
zh: a15c7c13f167e5ac
/agent-note-replace-plugins-whose-loaded-package-files-changed-during-profile-recomposition/decision/one-recomposition-path:
en: f2874c3fc946cf7a
zh: 4c32b9f94e1f9709
/agent-note-replace-plugins-whose-loaded-package-files-changed-during-profile-recomposition/decision/detecting-changed-modules:
en: 1fedf2dde87f41bf
zh: c2249267d1c2bc78
/agent-note-replace-plugins-whose-loaded-package-files-changed-during-profile-recomposition/decision/replacing-them:
en: 6fe2cbe5aa3a9786
zh: fe30fbf672ea8c21
/agent-note-replace-plugins-whose-loaded-package-files-changed-during-profile-recomposition/decision/plugin-manager-and-runtime-resolution:
en: 794ed33204d91014
zh: b08daa2a43cbe3b3
/agent-note-replace-plugins-whose-loaded-package-files-changed-during-profile-recomposition/alternatives-considered:
en: a67d0c6bc4957060
zh: 32e02ab45b8e71cc
/agent-note-replace-plugins-whose-loaded-package-files-changed-during-profile-recomposition/consequences:
en: 2cb3f39c6a910685
zh: e14938c868390f39
/agent-note-replace-plugins-whose-loaded-package-files-changed-during-profile-recomposition/verification:
en: d4750abb810faac2
zh: 78c9c4d86d719b9b
@@ -0,0 +1,86 @@
# Agent Note: Replace plugins whose loaded package files changed during profile recomposition
Status: implemented
English | [中文](2026-10-01-replace-changed-package-modules-on-recomposition.zh.md)
## Problem
Node caches an evaluated module by file URL. A profile installs packages with pnpm's hoisted layout, so a package lives at `node_modules/<name>` whatever its version. Overwriting, upgrading or reinstalling it keeps every file URL, and a later import or Loader entry restart returns the cached module of the previous version. The [package-refresh Note](2026-09-30-profile-package-refresh-and-manifest-invalidation.md) refreshes runtime resolution and package configuration but leaves this module cache; Plugin Manager therefore reported overwrites as `restart-required`, and a reinstall at the same path after removal reported `applied` while the previous version kept running. A `link:` development package hits the same cache whenever its build output changes, unless its directory is listed in HMR's source watch roots.
## Decision
### One recomposition path
`Hmr.reconcileProfile()` is the profile recomposition used by HMR's profile watches and by Plugin Manager; the Config Editor still recomposes through app-boot directly. It runs inside `runExclusive()`. Plugin Manager only edits profile files and runs pnpm, and refuses to install over an installed package it can name before pnpm runs; another version is a removal and an installation, each with its own recomposition. The steps of a recomposition are:
1. Read the profile patches and compare loaded modules below the profile directory and its linked package targets with disk.
2. Plan the replacement as below; a patch read or comparison failure throws or is reported before any module changes.
3. Evict the replaceable changed modules and their importers. A changed module whose importers reach the process entry point's static graph or a plugin that no Loader entry owns stays loaded with its importers; the other changes proceed.
4. Stop the Loader entries running the replaced plugins by deleting their runtimes, so the Loader does not record them as disabled.
5. Call app-boot's `recomposeProfile()` once with the entries that ran before the stops. It records the activation state, stops the rows the generation no longer lists by removing them from their groups, publishes the runtime resolution, runs the compatibility preflight against the published packages, and applies the generation. The Loader imports the current files when it starts an entry: rows the generation keeps start once with the new code and their new configuration; rows it disables, refuses or removes stay stopped and never run the new code; an entry it inserts links against the current files. Stopped entries the update did not try to start, below an unchanged Include or outside the profile composition, start afterwards; an entry whose import failed during the update is imported once more. Activation is judged: a stopped entry that failed afterwards fails the operation, while an entry waiting for a service is a warning, since it starts once a provider appears. If no generation applied, the published resolution and the stopped rows are restored, and the stopped entries start again.
6. Evict the loaded modules below the directories of the mappings the recomposition dropped, so a removed or deselected package, including its dynamically imported dependencies, is imported afresh when it returns. A bundle switched off while its package stays mapped keeps its unchanged modules.
### Detecting changed modules
[`ModuleFiles`](../../../../packages/boot/hmr/src/module-files.ts) records inode, size and modification time for the loaded ES and CommonJS modules below the profile directory and the real directories of its linked packages. Modules of the dsh installation are outside this scope. Change time is excluded: pnpm imports packages by hard link from its store, and adding a link elsewhere changes a loaded file's change time without changing its content. Records are taken at application readiness, after each recomposition, and for modules a source reload evaluated again. A recomposition records the identities it read before replacing anything, so a write during the recomposition stays detectable; modules first loaded during it are recorded after settlement to avoid an unnecessary restart during the next installation. An edit between first evaluation and that record can be missed; the timestamp fallback still covers modules first loaded outside recomposition. If the readiness observation fails, the fallback starts at HMR construction time, not the Unix epoch. A module is stale when its file disappeared or its identity differs. A module first loaded after the last recording has no record; it is stale when its file was modified at or after that recording, less a 2-second filesystem timestamp resolution, so a reinstall between loading and the next comparison is not missed. Version numbers are not consulted: lockfiles record no content identity for `file:` and `link:` dependencies, and file identity covers registry, tarball, directory and link installs alike.
### Replacing them
A stale module whose importer closure reaches the process entry point's static graph, including installed packages, stays loaded and is reported. The remaining stale modules and their static importers, across ES module links and CommonJS `require` children, form the affected set, and every manifest from a stale file up to its profile root or linked target expires through `PackageManifests.invalidate()`. The whole affected set is evicted without a backup before recomposition, so every entry that imports during recomposition reaches the current files. A plugin registered outside a Loader entry has no owner that can start its replacement, so the changes its modules import stay pending; plugins are read from the ES module namespace and from cached CommonJS exports, so a CommonJS plugin registered through `require` keeps its cache too. HMR imports no replacement code itself, so admission is the Loader's ordinary activation: a disabled, removed or refused row never evaluates the new module, and dynamic `disabled` expressions and nested Includes are evaluated as at startup. Every recomposition imports again each enabled entry whose last import failed, after expiring the package configuration below the profile roots and evicting the modules whose links or evaluation failed, which Node keeps cached, so a corrected file or a later-installed dependency takes effect without a restart. A plugin counts as owned only when an affected plugin's own Loader entry fiber starts it; a changed plugin another plugin registers through a dynamic import stays pending. Unchanged dependencies of a replaced plugin are not re-evaluated, so shared libraries keep one instance. Source reloads through `partialReload()` keep their own in-place replacement with rollback.
`reconcileProfile()` returns `pending`, each stale module that loads only after a restart with its reason (`host-module`, `unmanaged-plugin`, or `module-comparison` when planning failed). Pending modules stay stale, including modules first loaded after the last recording, so the next recomposition retries them. A module first loaded during a recomposition whose file was modified after the comparison also stays stale: activation may have evaluated it before that write. A failure while listing or comparing modules keeps the previous records and sets the profile-wide `restartReason: module-comparison`, even without known module URLs. Neither prevents recomposition. An activation failure, including a replacement that fails to import, is thrown with the Loader's import error; an unchanged failure is returned as a warning by later recompositions. Plugin Manager reports a thrown failure as `failed` with that diagnostic, and turns `pending` into `restart-required` with its `restartReason`, unless the operation reports `overridden`. The Web page localizes the reason, and the agent tool derives a `nextStep` sentence from it, so no surface parses a diagnostic string.
The [packed worker loader](../../../../packages/experimental/webworker-runtime/src/module-system/module-loader.ts) stores evaluated modules in a private map and its `require` exposes no cache. HMR cannot replace them through Node APIs and reports a restart requirement. Native Node still exposes its caches when using asynchronous module hooks.
### Plugin Manager and runtime resolution
Plugin Manager refuses to install over an installed package it can name before pnpm runs, so a new version or location is a removal and an installation: the removal stops the plugins, evicts their modules and drops the mappings, and the installation maps the package afresh. Runtime resolution accepts a retained profile mapping whose version changes while its normalized directory and scope stay, as a shared dependency upgraded by another installation does, and rejects one that moved to another directory. A linked root may point to another directory; a file Node's cached link real path selects below the previous target, for an importer outside it, is relocated below the current one. Rejected publications, including changes to installation mappings or local overrides, leave the resolution and the composition unchanged and report `restart-required` with reason `package-moved`.
## Alternatives considered
**A separate package-replacement call from Plugin Manager.** Plugin Manager knows which package it installed, but not which Loader entries import it, and row additions, removals and configuration changes in the new bundle patch must apply in the same step. A second operation would split one profile change into two transactions with an undefined order. Recomposition already reads the new profile; comparing loaded files there covers Plugin Manager, CLI installs followed by a recomposition, and link builds.
**Compare package versions from the profile manifest or lockfile.** The profile manifest stores specs, not resolved versions, and `pnpm update` can change a version without changing it. Lockfiles carry no content identity for `file:` and `link:` dependencies, and a same-version reinstall of a tarball changes content without changing the version.
**Let app-boot call HMR from inside its recomposition.** Hooks for the steps before applying and before judging would split the order of one operation between two owners. HMR instead stops everything it replaces first and passes the stopped entries to one app-boot call.
**Expose the recomposition's stages to HMR.** Separate calls to record activation, prepare after publication, apply, restore and drop mappings let HMR insert its stops between them, but they leave callers ordering obligations that the types cannot express. Stopping before the call costs a restart of the stopped plugins when preparation fails.
**Publish twice, keeping the dropped mappings while the generation applies.** A temporary table with both the new and the dropped mappings lets one Loader update stop and start entries, but it is a third generation that must stay recomputable when the update fails. Stopping the dropped rows first lets one publication follow them.
**Overwrite an installed bundle in one operation.** Stopping the bundle before pnpm and starting it afterwards, or detecting moved packages from the loaded module files, keeps one call for an upgrade, but needs a restart path for every pnpm failure, a selection restored in its order, and a release rule for moved directories. Refusing the overwrite leaves a removal and an installation, which the existing operations already run in this order.
**Preload the replacement and keep the previous plugin when it fails to load.** HMR would import new code before stopping the previous runtime and restore the previous caches on failure. Preloading must not evaluate a row the generation disables, removes or refuses, so HMR would decide admission before the Loader does; dynamic `disabled` expressions and nested Includes cannot be decided without activation, so those changes would need a restart. Rollback must also remove new importers of restored modules, and the previous code would still receive the new configuration. A restart would load the same broken files, so keeping the previous plugin only delays a failure that needs a fix anyway.
**Restart replaced plugins in place before recomposition.** New code would start with the previous configuration, which an upgraded version may reject, and a row the new composition disables or refuses, for example an incompatible version left on disk by a failed install, would briefly run.
**Replace running plugins after recomposition.** Recomposition would restart the previous code with the new configuration, which the previous version may reject, so a successful upgrade would be reported as a failure; an entry inserted during recomposition would also link against the previous copy of a changed dependency still cached for a running plugin.
**Stop affected plugins before pnpm runs.** This prevents previous code from lazily loading files pnpm has already replaced, but adds downtime for the whole install and holds the HMR queue during pnpm.
**Re-evaluate every dependency of a replaced plugin.** Source reloads reload a plugin's non-`node_modules` dependency tree. Doing the same below `node_modules` would create second instances of unchanged shared libraries and Cordis packages used by other plugins.
**Record identities in a module load hook.** `module.registerHooks()` could record each file at load time and close the window for modules loaded between recordings, but it adds a hook to every module load and interacts with TSX and the vendored Loader's internals. The timestamp rule only causes an extra reload in that window.
## Consequences
- Upgrades through a removal and an installation, and packages whose files changed in place, take effect without a restart when the replacement loads and starts; a replacement that fails to load or start stops the previous plugin and reports `failed` with the error, and it starts once its files or dependencies are corrected.
- Each recomposition stats every loaded file of the profile; the module graph is walked when a file changed, and the host graph is collected on first use.
- Dynamic `import()` edges are not followed: an importer keeps running while its next dynamic import evaluates the new file. Existing Workers and the TSX loader thread keep their caches.
- Replaced Loader entries stop before recomposition and start once afterwards; the previous version does not return.
- Loader entries outside the profile composition restart with replaced code without a new compatibility check ([limitation](../../../../packages/boot/hmr/README.md#known-limitations-and-deferred-work)).
- `ModuleFiles` belongs to one HMR instance. A recomposition that restarts the `hmr` entry itself starts a new instance whose first observation records the current files, so changes pending at that point are no longer reported or retried.
- `pending` describes the whole profile, so a host module brought into scope by a linked workspace package makes later unrelated operations report `restart-required` until the process restarts.
- A file replaced by a hard link to an older store file keeps an older modification time; when its module was first loaded after the last recording, the change is not detected.
- A change confined to `package.json` is not detected. HMR's profile watches do not recompose for dependency-only manifest changes, so installs performed outside Plugin Manager wait for the next recomposition or restart.
## Verification
| Coverage | Location |
|---|---|
| In-place upgrades with bundle patch changes, configurations the previous version rejects, inserted entries and disabled rows, plugins without a Loader entry, shared-dependency importers, linked packages, hard links, CommonJS `require` children and their failures, failed replacement and retry, nested groups and Includes, dynamic disabling, same-path reinstall, late-loaded modules, host-graph modules, comparison and recomposition failures, eviction of packages a recomposition stopped using or whose mappings it dropped, and the restart of stopped entries after a rejected publication or failed preparation | `packages/boot/hmr/tests/package-reload.spec.ts` |
| Refused overwrites, removal then installation with HMR, failed installation, relinked and link-to-registry reinstalls, unmanaged plugins, comparison failures | `packages/boot/plugin-manager/tests/package-reload.spec.ts`, `packages/boot/plugin-manager/tests/manager.spec.ts` |
| In-place profile mapping version change, retained directory and scope rejection including a relinked path inside the profiles tree, relinked roots and native lookups relocated below the current target | `packages/boot/app-boot/tests/profile-resolution.spec.ts` |
Tests need no API key and make no model calls.
@@ -0,0 +1,86 @@
# Agent Note: profile 重新组合时替换已加载包文件发生变化的插件
Status: implemented
[English](2026-10-01-replace-changed-package-modules-on-recomposition.md) | 中文
## Problem
Node 按文件 URL 缓存已求值的模块。profile 使用 pnpm 的 hoisted 布局安装包,因此无论版本如何,包都位于 `node_modules/<name>`。覆盖、升级或重装包时所有文件 URL 保持不变,之后的导入或 Loader entry 重启返回的是旧版本的缓存模块。[包刷新说明](2026-09-30-profile-package-refresh-and-manifest-invalidation.zh.md)刷新 runtime resolution 和包配置,但不处理这一模块缓存;因此 Plugin Manager 把覆盖报告为 `restart-required`,而删除后在同一路径重装时报告 `applied`,旧版本却仍在运行。`link:` 开发包的构建输出变化时也会命中同一缓存,除非其目录列在 HMR 的源码监听根目录中。
## Decision
### 唯一的重新组合路径
`Hmr.reconcileProfile()` 是 HMR 的 profile 监听和 Plugin Manager 共用的 profile 重新组合入口;Config Editor 仍直接通过 app-boot 重新组合。它在 `runExclusive()` 内运行。Plugin Manager 只编辑 profile 文件并运行 pnpm;对于在运行 pnpm 之前能确定包名的已安装包,它拒绝覆盖安装;安装另一个版本需要先卸载再安装,每一步各有一次重新组合。一次重新组合的步骤如下:
1. 读取 profile patch,将 profile 目录及其已链接包目标之下的已加载模块与磁盘比较。
2. 按下文规划替换;patch 读取或比较失败时,在任何模块变化之前抛出错误或报告失败。
3. 逐出可替换的已变化模块及其导入方。若已变化模块的导入方可到达进程入口的静态依赖图,或可到达没有 Loader entry 拥有的插件,该模块连同其导入方保持加载;其余变化照常处理。
4. 通过删除运行时停止运行被替换插件的 Loader entry,使 Loader 不把它们记录为已停用。
5. 以停止之前正在运行的条目调用一次 app-boot 的 `recomposeProfile()`。它记录激活状态,把新一代组合不再列出的行从其所在组中移除,以停止这些行,发布 runtime resolution,基于已发布的包运行兼容性预检,然后应用新一代组合。Loader 在启动条目时导入当前文件:新一代组合保留的行以新代码和新配置启动一次;停用、拒绝或删除的行保持停止,不会运行新代码;新插入的条目链接当前文件。更新未尝试启动的已停止条目(位于未变化 Include 之下或 profile 组合之外)随后启动;更新期间导入失败的条目会再导入一次。随后判定激活结果:之后失败的已停止条目使操作失败;等待服务的条目只是警告,因为出现提供方后它即启动。若没有新一代组合被应用,已发布的 resolution 和已停止的行被恢复,已停止的条目重新启动。
6. 逐出重新组合删除的映射所在目录之下的已加载模块,使已删除或取消选入的包连同其动态导入的依赖在重新加入时重新导入。关闭组合包而其包仍有映射时,它未变化的模块保持加载。
### 检测已变化的模块
[`ModuleFiles`](../../../../packages/boot/hmr/src/module-files.ts) 为 profile 目录及其已链接包的真实目录之下已加载的 ES 和 CommonJS 模块记录 inode、大小和修改时间。dsh 安装目录中的模块不在此范围内。不记录状态变更时间:pnpm 通过硬链接从其 store 导入包,在其他位置添加链接会改变已加载文件的状态变更时间,而内容不变。记录发生在应用就绪时和每次重新组合之后;源码重载重新求值模块时,也为这些模块记录。重新组合记录的是它在替换任何模块之前读取的文件身份,因此重新组合期间发生的写入仍可被检测到;在重新组合期间首次加载的模块在结算后记录,避免下一次安装时发生不必要的重启。首次求值与记录之间的修改可能漏检;时间戳兜底仍覆盖重新组合之外首次加载的模块。就绪时的观察失败时,兜底从 HMR 构造时间开始,而非 Unix 纪元。文件已消失或身份不同的模块视为陈旧。在上次记录之后才首次加载的模块没有记录;若其文件的修改时间不早于该次记录时间减去 2 秒文件系统时间戳精度,则视为陈旧,因此不会漏掉在加载与下一次比较之间发生的重装。比较不参考版本号:lockfile 不为 `file:` 和 `link:` 依赖记录内容标识,而文件身份同样覆盖 registry、tarball、目录和 link 安装。
### 替换
导入方闭包可到达进程入口静态依赖图(包括已安装包)的陈旧模块保持加载,并被报告。其余陈旧模块及其静态导入方(沿 ES 模块链接和 CommonJS `require` 子模块)构成受影响集合,从每个陈旧文件到其 profile 根目录或链接目标的各级 manifest 都通过 `PackageManifests.invalidate()` 失效。整个受影响集合在重新组合之前被逐出且不保留备份,因此重新组合期间执行导入的每个条目都读取当前文件。在 Loader entry 之外注册的插件没有能启动其替换版本的所有者,因此其模块导入的变化保持待处理;插件从 ES 模块命名空间和已缓存的 CommonJS exports 中读取,因此通过 `require` 注册的 CommonJS 插件同样保留其缓存。HMR 本身不导入替换代码,因此是否允许加载由 Loader 的普通激活决定:停用、删除或被拒绝的行永远不会对新模块求值,动态 `disabled` 表达式和嵌套 Include 按启动时的方式求值。每次重新组合都会先让 profile 根目录下的包配置失效,并逐出链接或求值失败的模块(Node 会在缓存中保留这些模块),再次导入上次导入失败的每个已启用条目,因此修正后的文件或之后安装的依赖无需重启即生效。只有当受影响插件自身的 Loader entry fiber 启动某个插件时,该插件才算有所有者;由另一个插件通过动态导入注册的已变化插件保持待处理。被替换插件中未变化的依赖不会重新求值,因此共享库保持单一实例。通过 `partialReload()` 进行的源码重载保留其自身带回滚的原地替换。
`reconcileProfile()` 返回 `pending`,即每个只有重启后才能加载的陈旧模块及其原因(`host-module`、`unmanaged-plugin`,规划失败时为 `module-comparison`)。待处理模块保持陈旧,包括在上次记录之后才首次加载的模块,因此下一次重新组合会重试它们。在重新组合期间首次加载、且其文件在比较之后被修改的模块同样保持陈旧:激活可能在该写入之前已对其求值。列出或比较模块时发生的失败会保留原有记录,并设置 profile 级 `restartReason: module-comparison`,即使无法确定模块 URL。两种情况都不会阻止重新组合。激活失败(包括替换版本导入失败)会连同 Loader 的导入错误一起抛出;未变化的失败在后续重新组合中作为警告返回。Plugin Manager 把抛出的失败报告为带该诊断的 `failed`,把 `pending` 转为带其 `restartReason` 的 `restart-required`,除非操作报告 `overridden`。Web 页面本地化该原因,agent 工具据此生成一句 `nextStep`,因此没有任何界面解析诊断字符串。
[打包 worker loader](../../../../packages/experimental/webworker-runtime/src/module-system/module-loader.ts) 在私有映射中保存已求值模块,其 `require` 不暴露缓存。HMR 无法通过 Node API 替换它们,因此报告需要重启。原生 Node 使用异步模块 hook 时仍暴露其缓存。
### Plugin Manager 与 runtime resolution
对于在运行 pnpm 之前能确定包名的已安装包,Plugin Manager 拒绝覆盖安装,因此新版本或新位置需要先卸载再安装:卸载停止插件、逐出其模块并删除映射,安装再重新映射该包。runtime resolution 接受保留的 profile 映射在规范化目录和作用域不变时更改版本,即共享依赖被另一次安装升级的情形;移动到其他目录的映射被拒绝。linked root 可以指向其他目录;对于原目标之外的导入方,Node 缓存的链接真实路径若选中原目标之下的文件,该文件会被重新定位到当前目标之下。被拒绝的发布,包括对安装范围映射或本地覆盖的更改,不改变 resolution 和组合,并报告原因是 `package-moved` 的 `restart-required`。
## Alternatives considered
**由 Plugin Manager 单独调用包替换。** Plugin Manager 知道自己安装了哪个包,但不知道哪些 Loader entry 导入了它;新组合包 patch 中的行增加、删除和配置变更也必须在同一步应用。第二个操作会把一次 profile 变更拆成两个顺序未定义的事务。重新组合本来就会读取新的 profile;在此处比较已加载文件,同时覆盖 Plugin Manager、之后执行重新组合的 CLI 安装以及 link 构建。
**比较 profile manifest 或 lockfile 中的包版本。** profile manifest 存储的是 spec,不是解析后的版本,`pnpm update` 可以在不修改 spec 的情况下改变版本。lockfile 不为 `file:` 和 `link:` 依赖记录内容标识,而同版本重装 tarball 会改变内容却不改变版本。
**让 app-boot 在其重新组合内部调用 HMR。** 为应用之前和判定之前的步骤提供 hook,会把一次操作的顺序拆分给两个所有者。HMR 改为先停止它替换的所有内容,再把已停止的条目传给一次 app-boot 调用。
**向 HMR 暴露重新组合的各个阶段。** 分别调用记录激活状态、发布后准备、应用、恢复和丢弃映射,可让 HMR 在其间插入停止操作,但会给调用方留下类型无法表达的顺序义务。在调用之前停止插件,代价是准备失败时需要重新启动已停止的插件。
**发布两次,在应用新一代组合期间保留被丢弃的映射。** 同时包含新映射和被丢弃映射的临时表,可以让一次 Loader 更新既停止又启动条目,但它是第三个 generation,更新失败时必须仍能重新计算。先停止被丢弃的行,之后只需发布一次。
**在一次操作中覆盖已安装的组合包。** 在 pnpm 运行之前停止该组合包、之后再启动,或根据已加载模块文件检测已移动的包,可以让升级只需一次调用;但这需要为每种 pnpm 失败提供重启路径、按原顺序恢复选择列表,并为已移动的目录制定释放规则。拒绝覆盖安装后只剩卸载和安装,现有操作已按此顺序执行。
**预加载替换版本,并在其加载失败时保留原插件。** HMR 将在停止旧运行时之前导入新代码,并在失败时恢复旧缓存。预加载不得对新一代组合停用、删除或拒绝的行求值,因此 HMR 要先于 Loader 决定是否允许加载;动态 `disabled` 表达式和嵌套 Include 不经激活就无法判定,因此这些变化需要重启。回滚还必须删除已恢复模块的新导入方,而旧代码仍会收到新配置。重启会加载相同的损坏文件,因此保留原插件只是推迟一个无论如何都需要修复的失败。
**在重新组合之前原地重启被替换的插件。** 新代码会先以旧配置启动,而升级后的版本可能拒绝旧配置;新组合停用或拒绝的行(例如安装失败后留在磁盘上的不兼容版本)也会短暂运行。
**在重新组合之后替换运行中的插件。** 重新组合会以新配置重启旧代码,而旧版本可能拒绝新配置,导致一次成功的升级被报告为失败;重新组合期间插入的条目也会链接到仍为运行中插件缓存的、已变化依赖的旧副本。
**在 pnpm 运行前停止受影响插件。** 这能防止旧代码惰性加载 pnpm 已经替换的文件,但会让整个安装期间插件不可用,并在 pnpm 运行期间占用 HMR 队列。
**重新求值被替换插件的全部依赖。** 源码重载会重载插件在 `node_modules` 之外的依赖树。若对 `node_modules` 之下做同样处理,会为其他插件使用的、未变化的共享库和 Cordis 包创建第二个实例。
**在模块加载 hook 中记录文件身份。** `module.registerHooks()` 可以在加载时记录每个文件,消除两次记录之间加载的模块的检测空窗,但这会给每次模块加载增加一个 hook,并与 TSX 和 vendored Loader 的内部机制相互影响。时间戳规则在该空窗内只会导致一次多余的重载。
## Consequences
- 通过卸载再安装进行的升级,以及文件被原地更改的包,在替换版本成功加载并启动时无需重启即生效;替换版本加载或启动失败时,原插件停止并报告 `failed` 及该错误,其文件或依赖被修正后它即启动。
- 每次重新组合对 profile 的所有已加载文件执行一次 stat;有文件变化时才遍历模块图,宿主依赖图在首次使用时收集。
- 不跟踪动态 `import()` 边:导入方继续运行,而其下一次动态导入会求值新文件。已存在的 Worker 和 TSX loader 线程保留各自的缓存。
- 被替换的 Loader entry 在重新组合之前停止,之后启动一次;旧版本不会恢复。
- profile 组合之外的 Loader entry 以替换后的代码重启,不重新进行兼容性检查([限制](../../../../packages/boot/hmr/README.zh.md#known-limitations-and-deferred-work))。
- `ModuleFiles` 属于单个 HMR 实例。重启 `hmr` 条目本身的重新组合会启动一个新实例,其首次观察记录当前文件,因此此时待处理的变化不再被报告或重试。
- `pending` 描述整个 profile,因此由已链接工作区包纳入范围的宿主模块,会使后续无关操作也报告 `restart-required`,直到进程重启。
- 若文件被替换为指向较旧 store 文件的硬链接,它保留较旧的修改时间;当其模块在上次记录之后才首次加载时,该变化不会被检测到。
- 仅限于 `package.json` 的变化不会被检测到。HMR 的 profile 监听不会因仅涉及依赖的 manifest 变化而重新组合,因此在 Plugin Manager 之外执行的安装要等到下一次重新组合或重启。
## Verification
| 覆盖面 | 位置 |
|---|---|
| 伴随组合包 patch 变更的原地升级、旧版本拒绝的配置、新插入的条目和停用行、没有 Loader entry 的插件、共享依赖的导入方、已链接包、硬链接、CommonJS `require` 子模块及其失败、替换失败与重试、嵌套 group 和 Include、动态停用、同路径重装、延后加载的模块、宿主依赖图中的模块、比较失败与重新组合失败、逐出重新组合不再使用或已删除其映射的包,以及发布被拒绝或准备失败后已停止条目的重启 | `packages/boot/hmr/tests/package-reload.spec.ts` |
| 被拒绝的覆盖安装、有 HMR 时先卸载再安装、安装失败、重新链接和从链接改为 registry 的重装、非托管插件、比较失败 | `packages/boot/plugin-manager/tests/package-reload.spec.ts`、`packages/boot/plugin-manager/tests/manager.spec.ts` |
| profile 映射原地更改版本、保留目录和作用域的拒绝(包括 profiles 树内重新链接的路径)、重新链接的 root 以及被重新定位到当前目标之下的原生查询 | `packages/boot/app-boot/tests/profile-resolution.spec.ts` |
测试不需要 API key,不调用模型。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-10-08-persistent-ptc-sandbox.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-08-persistent-ptc-sandbox.md
/agent-note-retained-ptc-processes-follow-file-policy:
en: ef07a82c85e490e1
zh: b1662d1970d2b83b
/agent-note-retained-ptc-processes-follow-file-policy/problem:
en: b8cb574032d4e6e4
zh: 6a1a25dec5f96086
/agent-note-retained-ptc-processes-follow-file-policy/decision:
en: 5109a4bb5be39867
zh: 2dcc602aba9eae61
/agent-note-retained-ptc-processes-follow-file-policy/alternatives-considered:
en: 10bebc7f982cf7f4
zh: 9247a2ba57921a11
/agent-note-retained-ptc-processes-follow-file-policy/consequences:
en: b3ddf273158d3754
zh: 916356f4074b3622
@@ -0,0 +1,31 @@
# Agent Note: Retained PTC processes follow file policy
Status: implemented
English | [中文](2026-10-08-persistent-ptc-sandbox.zh.md)
## Problem
Retaining JavaScript variables does not require broader file access. A process that outlives one execution can also outlive a one-time approval or a Session policy change. File-policy changes must invalidate retained environments and begin stopping their file handles and background work.
## Decision
`persistent-in-memory` starts its Node process through the same sandbox provider as `per-call`. A retained owner uses one resolved mode, workspace root and Session identity until release. The [memory decision](2026-09-23-best-effort-persistent-ptc.md) owns retained values, failure recovery and package composition; this note owns file authority.
The [PTC-owned cleanup decision](2026-10-09-ptc-owned-policy-cleanup.md) supersedes this note’s permission-setter completion and cleanup-ownership decision. Permission changes commit synchronously; PTC owns immediate cancellation and waits internally before subsequent execution. The tools package keeps Agent ownership and omits the owner for temporary escalations.
An approved one-time escalation omits the state owner and receives a fresh temporary environment. Its process is released before the call returns. It cannot access standing variables, and its declarations do not survive. The standing environment keeps its original policy and variables. Nested tools continue to apply their own permissions and approvals.
## Alternatives considered
**Unconfined retained execution.** Omitting confinement simplifies a temporary prototype but couples variable retention to full host file access. The existing process sandbox supports long-lived processes, so this grant is unnecessary.
**Changing the running process policy.** Existing backends apply restrictions at launch. Arbitrary retained objects, open handles and background work cannot be transferred into another policy with the existing interface. Releasing the environment prevents later evaluations from reusing those resources.
**Coordinating through permission presets.** File-policy changes also have direct callers. PTC observes committed Session mode events from both paths, independently of user-facing preset selection.
**Keeping an elevated environment.** A one-time approval cannot authorize future programs or idle background work. Omitting the state owner makes the approval lifetime explicit while preserving the standing environment.
## Consequences
Normal calls retain variables without changing file permissions. Changing sandbox mode discards variables. Escalated programs must be self-contained. Runtime consumers must release a retained owner before supplying a different policy without a Session mode change; the provider rejects inconsistent reuse. The sandbox inherits the existing backends’ file-effect coverage and platform limitations.
@@ -0,0 +1,31 @@
# Agent Note: 保留状态的 PTC 进程遵循文件策略
Status: implemented
[English](2026-10-08-persistent-ptc-sandbox.md) | 中文
## 问题
保留 JavaScript 变量不需要更大的文件访问权限。跨调用存活的进程也可能在单次审批结束或 Session 策略变化后继续运行。文件策略变化必须使保留环境失效,并开始停止其文件句柄和后台工作。
## 决策
`persistent-in-memory` 与 `per-call` 通过同一个沙箱提供方启动 Node 进程。保留状态的所有者在释放前使用固定的已解析权限模式、工作区根目录和 Session 身份。[内存决策](2026-09-23-best-effort-persistent-ptc.zh.md)负责保留值、失败恢复和包组成;本文负责文件权限。
[PTC 自行等待清理的决策](2026-10-09-ptc-owned-policy-cleanup.zh.md)替代本文关于权限设置完成时点和清理归属的决策。权限变化同步提交;PTC 负责立即取消,并在后续执行前内部等待清理。工具包保留 Agent 所有权,在临时提权时省略所有者。
单次获准的提权省略状态所有者,使用全新的临时环境。该次调用返回前释放进程,无法访问原环境的变量,也不会保留新声明。原环境保持原权限和变量。嵌套工具继续应用各自的权限和审批规则。
## 考虑过的替代方案
**不限制保留状态的执行环境。** 省略沙箱能简化临时原型,却把变量保留与完整宿主文件访问权限绑定。现有进程沙箱支持长期运行的进程,因此这项授权没有必要。
**修改运行中进程的策略。** 现有后端在启动时应用限制。现有接口无法把任意保留对象、打开的句柄和后台工作转移到另一策略。释放环境可以防止后续执行复用这些资源。
**通过权限预设协调清理。** 文件策略也有直接调用方。PTC 观察两类调用方提交的 Session 模式事件,无需依赖面向用户的预设选择。
**保留提权环境。** 单次审批不能授权未来程序或空闲期间的后台工作。省略状态所有者明确了审批生效的范围,同时保留原环境。
## 影响
普通调用保留变量,同时维持原文件权限。切换沙箱模式会丢弃变量。提权程序必须包含执行所需的完整代码。未发生 Session 模式变化时,运行时消费者在传入不同策略前必须释放保留状态的所有者;提供方拒绝策略不一致的复用。沙箱继承现有后端的文件约束范围与平台限制。
@@ -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
Loaded 100 of 3622 files, more files were not shown because too many files have changed in this diff. Show more