dsh-ptc-plus 0.2.3 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.en.md +171 -0
  2. package/README.md +94 -67
  3. package/assets/dsh-ptc-plus-banner-en.webp +0 -0
  4. package/assets/ptc-plus-repair-en.png +0 -0
  5. package/assets/ptc-plus-settings-en.png +0 -0
  6. package/assets/ptc-plus-settings-zh.png +0 -0
  7. package/client.js +1211 -128
  8. package/docs/adr/0019-plugin-settings-and-kill-switch.md +20 -4
  9. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
  10. package/docs/adr/0022-stage-npm-releases-with-oidc.md +31 -0
  11. package/docs/architecture.md +10 -9
  12. package/docs/client-ui.md +30 -12
  13. package/docs/installation.md +13 -5
  14. package/docs/publishing.md +25 -1
  15. package/docs/runtime-reference.md +18 -12
  16. package/docs/value-wire.md +1 -1
  17. package/index.js +43 -12
  18. package/internal/cell-analysis.js +20 -3
  19. package/internal/cell-rewriter.js +36 -2
  20. package/internal/config-spec.js +192 -82
  21. package/internal/direct-surface-owner.js +77 -23
  22. package/internal/edit-transport-owner.js +10 -1
  23. package/internal/failure-reporting.js +15 -0
  24. package/internal/rejected-cell-editor.js +21 -12
  25. package/internal/repl-memory-projection.js +277 -0
  26. package/internal/run-code-description.js +34 -0
  27. package/internal/runtime-bridge-owner.js +63 -15
  28. package/internal/runtime-config.js +2 -0
  29. package/internal/session-cell-executor.js +14 -4
  30. package/internal/session-events.js +7 -0
  31. package/internal/session-journal.js +3 -2
  32. package/internal/session-log-view.js +2 -1
  33. package/internal/session-runtime.js +19 -2
  34. package/internal/session-state.js +83 -4
  35. package/internal/settings-compat.js +31 -0
  36. package/package.json +20 -13
  37. package/src/client-activity.js +472 -0
  38. package/src/client.js +536 -38
  39. package/README.zh.md +0 -144
  40. package/assets/ptc-plus-settings.png +0 -0
  41. /package/assets/{dsh-ptc-plus-banner.webp → dsh-ptc-plus-banner-zh.webp} +0 -0
  42. /package/assets/{ptc-plus-repair.png → ptc-plus-repair-zh.png} +0 -0
@@ -8,25 +8,41 @@ Accepted
8
8
 
9
9
  ## Context
10
10
 
11
- PTC Plus 0.1.0 deliberately had no Client UI. The runtime surface was the DSH PTC mode `run_code`/`edit_run_code` transport, and adding a settings bundle was judged not to improve the core REPL path. The plugin also lacked a user-facing way to disable its runtime without removing the Cordis entry, and enabling/disabling state was invisible outside the session log. The operator now wants a plugin settings UI containing every Host Config field, a kill switch, and an explicit enabled marker. `dsh-element-inspector` demonstrates the supported DSH pattern: Host registers a settings namespace and the browser half contributes a card into `settings.plugin.item`. `deepseek-harness-rc2` defines the settings service, client `settingsScope`, and the slot contract.
11
+ PTC Plus 0.1.0 deliberately had no Client UI. The runtime surface was the DSH PTC mode `run_code`/`edit_run_code` transport, and adding a settings bundle was judged not to improve the core REPL path. The plugin also lacked a user-facing way to disable its runtime without removing the Cordis entry, and enabling/disabling state was invisible outside the session log. PTC Plus requires a plugin settings UI containing every Host Config field, a kill switch, and an explicit enabled marker. DSH's public settings service registers a namespace, the browser half contributes a card through `settings.plugin.item`, and the client receives the scoped settings service through `settingsScope`.
12
12
 
13
13
  ## Decision
14
14
 
15
15
  PTC Plus registers one DSH settings namespace `ptc-plus` with the same field list, defaults and validation as the Host Config schema. The field definitions live once in `internal/config-spec.js`; `index.js` builds the Cordis Config schema from it, and the esbuild client bundle imports the same module so the UI does not copy defaults or validation.
16
16
 
17
+ The settings card separates four groups: common and compatibility controls, optional capabilities, advanced behavior, and resource limits. High-attention decisions appear before policies that most users can leave at their defaults. `autoDescribeRunCode` defaults to true and controls only a request-bound local execution policy. When enabled, a call that omits the outer `description` uses derived arguments for local DSH validation, and the fallback summary enters presentation metadata only; when disabled, DSH validates the original arguments. Both states produce byte-stable model-visible tool schemas, order, and system sections with the required `description` declaration. Original call arguments, explicit summaries, cell source, and nested native-tool arguments remain unchanged.
18
+
17
19
  The configurable set is the Host runtime configuration plus `enabled: boolean` (default true). `enabled` is the kill switch: when false the Host registers only the settings namespace and keeps the client card available; it does not install the runtime bridge, edit transport, direct-surface owner, system prompt section, tool registrations or session hooks. When true the same runtime as pre-settings PTC Plus is installed. Every field is live when its owner can reconcile it: the Host observes the settings scope and installs, uninstalls, or reconfigures owners without replacing existing session-bound bindings. Node fixes a worker's V8 `maxOldGenerationSizeMb` limit at construction, so changing that field while a session worker is active is rejected and the settings provider is restored to the previous value; other reconfiguration failures use the same compensation path. The TypeScript language check occurs at activation, allowing disabled settings to load on a host using another code runtime while rejecting and rolling back an enable attempt. If the settings service is absent, the Host falls back to composition config and enables the runtime.
18
20
 
19
- Enabled state is visible in the settings card as “已启用/已停用”. The collapsed card header also identifies the session-level TypeScript REPL and exposes an accessible expand/collapse label. When enabled for a session using the `code` preset, the Client separately shows a `PTC Plus` header indicator. Stable REPL guidance remains protocol text without UI branding. The tool names remain `run_code`/`edit_run_code`; we do not rename DSH-owned transport names.
21
+ Every settings-card and PTC Plus tool-row string is registered into the DSH client locale namespace `settings.ptcPlus` and follows the active DSH client locale. Field copy exists in both locales from the single `internal/config-spec.js` source (`label`/`labelEn`, `description`/`descriptionEn`); display chrome copy is owned by the Client half. The stable REPL guidance remains plugin-owned protocol text without UI branding. The transport names remain `run_code`/`edit_run_code`; DSH owns their identity.
22
+
23
+ The existing `conversation.session.header.actions` entry with id `ptc-plus-active` remains the lightweight enabled indicator. Hovering it, or focusing it from the keyboard, opens a compact reusable-binding card. The card lists the top-level bindings the current runtime owner has proved reusable by the agent, classified as variable, function, class, or import. Each entry includes its original declaration line and column, a one-line preview, and an accessible inline action that expands the declaration source. Preparation owns declaration spans before execution and `BindingCatalog` retains that provenance as bindings are replaced or replayed; the presentation path never reads, serializes, previews, or invokes the bound runtime values or getters. The runtime emits a complete, value-independent `meta.dshPtcPlusBindings` presentation envelope after each settled cell. The enclosed inventory is bounded to 128 entries, 1024 UTF-16 code units per declaration source, and 16384 code units of declaration source in aggregate, and reports its exact valid total and omitted count. Volatile is a cold-recovery classification, not a live-availability classification, so a live volatile worker continues to publish all bindings it retains. A restore-bearing or discarded settlement resets the worker and reports the binding surface as unconfirmed until a later cell materializes and proves the effective state.
24
+
25
+ The inventory preserves `BindingCatalog`'s last-declared-first stack order instead of sorting by name. Declarations later in one cell appear above earlier declarations, and redeclaring an existing binding moves it back to the top. The Client aligns every entry through a fixed-width type column followed by its name and definition preview.
26
+
27
+ The Host optionally registers `ptcPlusRepl` through DSH's public `sessionProjections` seam. Each enabled runtime activation owns an opaque presentation generation shared by its metadata producer and projection definition. The projection's pure fold pairs public `run_code` and `edit_run_code` events through the formal `tool/call.data.callId` and `tool/result.data.message.source.callId` identity; optional `sourceEventSeqs` provenance is not required. It accepts only complete snapshots from its own generation, clears live proof at `session/end-seed`, and serves only the inventory to the Client. A paired result with absent, malformed, or foreign-generation metadata cannot overwrite an already proved inventory; unrelated tool results and surface replacements are no-ops. The bounded call-correlation state is checkpointed separately from the wire value, carries the generation, and has its own invalidation version. A cached state from another generation normalizes to an unconfirmed inventory without replaying stale live claims. DSH deployments without the service, hosts exposing an incompatible registration contract, and unsupported versioned envelopes retain the indicator without binding data while the core runtime stays enabled. Existing logs require no migration. The DSH header-action kit exposes no public navigation command for opening a tool call or source location, so the card uses inline inspection and does not reach through private stores or query Host DOM. Source navigation requires a public Host capability. The projection generation and private metadata are presentation-only: neither enters model messages nor changes the closed journal schema, canonical result value, runtime context, tool schema, replay reader, authority, or compatibility rules.
28
+
29
+ While the setting is ready and enabled, the Client registers `run_code` and `edit_run_code` in the public keyed `tool.call.toolview` slot; disabling disposes both entries and restores DSH's owner fallback. Each replacement row preserves expandable source, result, visible lifecycle state, and Inspect access, then derives optional feature notices from existing PTC Plus presentation metadata. A non-PTC session uses a neutral Code title unless a settled result carries a valid PTC journal. The view never folds a conversation window or displays cumulative counts. Unknown or malformed metadata fails closed without hiding the base row; v1 journals with non-empty confirms also fail closed because the Client deliberately does not load the session identity needed to resolve them.
30
+
31
+ Feature notices name actual per-result events rather than configuration defaults: validated import/export/mixed-redeclaration provenance, a complete safe-edit target/derived-run/non-noop/optional-recovery-boundary relation consistent with any target precondition in the outer call, successful `code.run` calls, a completed durable recovery proven by the stable `PTC-R002` warning/recover/rolled-back tuple, and `repl.state` operations. Volatile-retained and discarded state remain recovery classifications, not user-facing feature notices. Recovery boundaries are validated when present but deliberately not displayed. Features without an existing Client-only fact, including ordinary loose redeclarations, canonicalizer provenance, official ownership of a `cordis_*` member, and whether a model recovery tip was selected, are not guessed from code shapes, name prefixes, or settings. The body feature row remains read-only and adds no Host owner. The REPL-memory card adds only the public session projection and parallel presentation snapshot described above; it adds no tool, prompt section, runtime context, session event type, journal field, value inspection, or migration behavior.
20
32
 
21
33
  Runtime activation is transactional. If a live enable cannot complete, every owner acquired before the failing setup step is disposed, the failure is logged, and the settings provider is asked to persist `enabled: false`; a failed compensating write remains an explicit activation diagnostic rather than an unreported state mismatch.
22
34
 
23
35
  ## Alternatives Considered
24
36
 
25
- 1. Keep no Client UI. Rejected: the operator explicitly needs a settings surface and a kill switch. Away from settings, users cannot tell whether PTC Plus is actively installed because the only marker is application behavior.
37
+ 1. Keep no Client UI. Rejected: PTC Plus requires a settings surface and a kill switch. Away from settings, users cannot tell whether PTC Plus is actively installed because the only marker is application behavior.
26
38
  2. Rely on DSH generic schema rendering without a `settings.plugin.item` card. Rejected: the generic tab renders only namespace cards supplied through the slot; shipping no card would leave the namespace invisible even though the schema is registered.
27
39
  3. Rename the `run_code` tool or use a schema/name-level UI alias. Rejected: PTC Plus must preserve the DSH host tool names and their public pipeline semantics; presentation labels belong in Client-owned UI, not in transport identity or model-visible stable text.
28
40
  4. Keep non-`enabled` fields restart-applied. Rejected: a settings UI that persists values without changing the active runtime is misleading. Runtime owners now accept live configuration updates; session kernels keep their bindings while applying new limits and policies to subsequent cells.
41
+ 5. Keep the card copy Chinese-only, or let the card branch on the browser locale itself. Rejected: the plugin's own copy must follow the same Host-owned locale preference every other settings surface follows, so it registers both dictionaries under `settings.ptcPlus` and uses the `t` seat composed by the DSH slot renderer; duplicating per-locale text outside `internal/config-spec.js` would break the single-source rule for field copy.
42
+ 6. Restyle rendered tool calls by querying Host DOM. Rejected: DOM structure is not a DSH extension contract and would silently break across Client versions. The public keyed toolview slot provides the presentation-only boundary.
43
+ 7. Query the live worker or serialize binding values when the card opens. Rejected: hover must not execute code, invoke getters, cross the Value Graph boundary, or make UI availability affect runtime state. Bounded declaration provenance from the already submitted source communicates useful continuity without those side effects.
44
+ 8. Implement “jump to definition” by locating Host conversation DOM. Rejected: the header-action kit has no public navigation capability in the supported DSH generations, and DOM shape is not a compatibility contract. Inline definition inspection is available; actual navigation waits for a public Host surface.
29
45
 
30
46
  ## Consequences
31
47
 
32
- The package gains a browser bundle (`src/client.js` → `client.js`), `dsh.client` metadata, a `./client` export and the `@deepseek-ai/dsh-settings` dependency. Host tests must cover the settings-disabled path and the live toggle path. Bundle loading follows DSH's public injected module and slot contracts; it does not advertise a literal DSH compatibility floor. Missing settings service degrades to the previous behavior instead of failing the plugin.
48
+ The package gains a browser bundle (`src/client.js` → `client.js`), `dsh.client` metadata, a `./client` export and the `@deepseek-ai/dsh-settings` dependency. Host tests must cover the settings-disabled path, live toggle path, value-independent binding snapshot, definition-source budgets, and optional projection registration; Client tests cover both running and settled tool blocks, the `arguments` and `argsRaw` fields, source/result fallback, exact feature derivation, malformed metadata, and the hover/focus card. Bundle loading follows DSH's public injected module and slot contracts; it does not advertise a literal DSH compatibility floor. Missing settings or session-projection services degrade the affected UI while the core runtime stays enabled instead of failing the plugin. Client presentation cannot affect model requests, canonical values, session persistence, or recovery.
@@ -22,11 +22,11 @@ The official plugin also registers Host inspect providers in the process-global
22
22
 
23
23
  Cordis tools remain native program bindings under `tools.*`. The PTC code-only direct-tool projection remains exactly `[run_code, edit_run_code]`; native agents and unrelated scopes do not inherit this option.
24
24
 
25
- Cordis definitions and Runs are process-local, while PTC journals replay every settled program binding as a recorded value without redispatch. On the first prompt for an agent or a new Cordis enable generation, PTC Plus derives whether normalized journals contain a Cordis transcript. If they do, it emits the fixed `tools:ptc-plus-cordis-recovery` runtime context: recorded values remain historical data, but the model must perform a live read-only Cordis Inspect before relying on prior IDs, state, approvals, or capability observations. The context remains until a newly settled successful `cordis_inspect*` call appears in the session log. It neither asserts that live state was lost nor reruns a mutating call.
25
+ Cordis definitions and Runs are process-local, while PTC journals replay every settled program binding as a recorded value without redispatch. On the first prompt for an agent or a new Cordis enable generation, PTC Plus derives whether normalized journals contain a Cordis transcript. If they do, it emits the fixed `tools:ptc-plus-cordis-recovery` runtime context: recorded values remain journal data, but the model must perform a live read-only Cordis Inspect before relying on prior IDs, state, approvals, or capability observations. The context remains until a newly settled successful `cordis_inspect*` call appears in the session log. It neither asserts that live state was lost nor reruns a mutating call.
26
26
 
27
27
  ## Alternatives Considered
28
28
 
29
- 1. Add the Cordis row to the PTC preset. Rejected: PTC Plus is installed as a host plugin and does not own or fork DSH's shipped preset files; the operator asked for a plugin setting.
29
+ 1. Add the Cordis row to the PTC preset. Rejected: PTC Plus is installed as a host plugin and does not own or fork DSH's shipped preset files; a plugin setting keeps the optional mount within the plugin's ownership boundary.
30
30
  2. Register Cordis tools during `system-prompt/assemble` and continue with the existing assembly. Rejected: provider and schema collection has already happened, so waiting alone would still omit the tools. The selected barrier instead discards that stale snapshot and asks DSH's public assembler to recollect after the owner-controlled mount is ready.
31
31
  3. Copy the tool definitions or their current names. Rejected: DSH owns their schemas, guidance, lifecycle, authority, and compatibility. Even a names-only completeness list would drift when DSH evolves and create a second contract.
32
32
  4. Enable the tools by default. Rejected: the tools can execute model-written plugins against the live runtime and carry a fixed schema cost. That trust and context expansion must be explicit.
@@ -0,0 +1,31 @@
1
+ # Stage npm Releases With OIDC
2
+
3
+ ## Problem
4
+
5
+ The npm registry is retiring long-lived bypass-2FA credentials as a direct publication mechanism and now supports workflow-bound OIDC identities plus staged publication. PTC Plus needs a release path that proves the package came from an already verified Git commit, does not place reusable npm write authority in GitHub secrets, preserves maintainer proof-of-presence, and publishes the exact artifact that was inspected. Running the complete platform matrix again for a tag wastes CI capacity without strengthening identity when the same commit already has a successful required CI run.
6
+
7
+ ## Decision
8
+
9
+ The `Release` GitHub Actions workflow is the only automated npm publication path. A maintainer explicitly dispatches it from an annotated `v<package.version>` tag ref; there is no independent tag input. Before obtaining npm authority, the workflow proves that its `GITHUB_REF`, provenance-bearing `GITHUB_SHA`, annotated tag target, checked-out commit, manifest version, and the successful `main` push run of `.github/workflows/ci.yml` all identify the same commit. Normal CI does not run for tag pushes.
10
+
11
+ The npm Trusted Publisher is bound to the exact repository, `release.yml` workflow filename, and `npm-release` GitHub environment. Its allowed action is only `npm stage publish`; direct `npm publish` and reusable npm write tokens are absent from the workflow. The package uses npm's most restrictive recommended publishing-access policy even when npm documentation and the live UI use different labels for it. After the OIDC staging path is proven, maintainers revoke every remaining reusable token capable of publishing or staging this package rather than treating the selected UI label as evidence that no such credentials remain. The release job receives only `contents: read` and `id-token: write`, uses a GitHub-hosted runner without a package-manager cache, revalidates the dispatch ref and SHA after the environment boundary, builds and smoke-tests one tarball, and sends that same file to npm staging with provenance.
12
+
13
+ Promotion is separate from staging. A maintainer reviews the staged metadata and downloadable tarball, then approves or rejects it through npm with interactive 2FA. A successful approval is not treated as immediate availability because npm publish-time scanning may delay or block registry visibility. GitHub Release publication follows observed availability of the exact npm version.
14
+
15
+ npm 12 install-time restrictions remain enabled for the release job. The manifest explicitly denies the unnecessary `esbuild` install script, and release installation replaces registry hosts with the explicitly selected npm registry without enabling Git dependencies, arbitrary remote tarball dependencies, or all install scripts.
16
+
17
+ ## Alternatives considered
18
+
19
+ **Store a granular bypass-2FA npm token in GitHub secrets.** This leaves reusable write authority available to workflow code and depends on a credential class whose direct-publish capability is being retired. It also provides weaker provenance and audit boundaries than a workflow-specific OIDC exchange.
20
+
21
+ **Publish directly with OIDC after a GitHub Environment approval.** This removes long-lived credentials but substitutes a GitHub approval for npm proof-of-presence. Stage-only authority keeps CI compromise from making a version public and remains compatible with npm's dual-use publication requirements if they apply.
22
+
23
+ **Run the full platform matrix again inside the release workflow.** Revalidation of an immutable SHA repeats expensive deterministic work without changing the artifact identity. The release workflow instead verifies the successful CI run for that exact SHA, then performs npm 12 installation, build, package lint, audit, pack, checksum, and clean-consumer smoke on the release artifact.
24
+
25
+ **Publish a locally generated tarball after CI.** Interactive local publishing provides 2FA but separates the published bytes from the workflow identity and makes provenance, repeatability, and artifact review depend on workstation state.
26
+
27
+ **Automatically stage every unpublished manifest version pushed to `main`.** This removes one maintainer action but turns an ordinary merge into a release request and can reserve a semver version in npm staging before its annotated release identity exists.
28
+
29
+ ## Consequences
30
+
31
+ An npm release requires a successful immutable CI identity, an explicit workflow dispatch, a narrowly scoped OIDC exchange, and a separate npm 2FA promotion. With package-capable reusable tokens revoked, no npm write secret needs rotation, and a compromised release job can at most create a staged candidate under the configured package and workflow relationship. The trade-off is an intentional manual promotion step and a possible registry scanning delay. Maintainers must configure the npm Trusted Publisher, package publishing access, GitHub environment, tag protection, and post-verification token revocation outside the repository; those deployment facts cannot be inferred or silently created by source code.
@@ -15,8 +15,8 @@ PTC Plus 把 DSH PTC 模式的顶层 `run_code` 变成与 session 绑定的连
15
15
  主入口接管模型直接发起的顶层 `run_code`,并通过 DSH tool registry 真实注册 `edit_run_code`。
16
16
  使用 code-only direct-tool projection 的 PTC request 始终按固定顺序暴露 `[run_code, edit_run_code]`;插件用 agent scope 的
17
17
  `tools.register()` 真实注册 edit transport,并用同一 scope 的 `tools.presentAs('both')` 放行这两个 provider tool call,再把其他 native tool 保留在 program SDK。首次可判定的 prompt assembly 在安装这项 presentation effect 前建立 agent composition owner;注册与 mode 记录都在所属 session、agent 或插件释放时撤销,native-mode 与无关 agent scope 不继承 `edit_run_code`。
18
- DSH 为一个 agent composition 固定选择 `native`、`code` 或 `both`。首次 assembly 的非空 `tools:code-only` section text 证明 `code`,同名空 section 证明 `both`;只有缺少该 owner signal 时才能从 tool shape 推断。后续 assembly 不重新解释插件自己的 `tools.presentAs('both')` 效果,而是从 agent composition owner 派生 presentation。`code` 独占 direct-tool collapse、顶层 native-call normalization 和 native dispatch rejection;`both` 保留 native direct tools。`code` 与 `both` 都适配 PTC `run_code` schema、program SDK 和 session runtime contexts,只有 `code` 安装并直接执行 `edit_run_code`。model stream 将完整 call 的 session 与 call ID 绑定到由 composition mode 派生的 request policy,dispatch wrapper 替换 cancellation signal 不改变执行判定;result settlement 和 session、agent 或插件释放分别撤销单次 call 与整个 lifecycle。
19
- 若模型误发 live schema 可证明的顶层 native call,stream canonicalizer 会把这个 out-of-surface 调用规范为与模型本应生成的同一 member `run_code`;未知、畸形或不一致输入原样透传。派生 cell 不携带 provenance、纠错提示或其他模型可见标记;普通参数保留原始 JSON,含 own `__proto__` 的参数使用安全字面量,超过 TypeScript parser 安全深度的参数才使用浅层 `JSON.parse` 表达式。该历史修改只修复无效 direct call。已声明的 `run_code` 与 `edit_run_code` 原样通过,因此 session history、UI 与下一轮 model context 保留合法调用的实际 tool name 和参数。
18
+ DSH 为一个 agent composition 固定选择 `native`、`ptc` 或 `both`。首次 assembly 的非空 `tools:ptc-only` section text 证明 `ptc`,同名空 section 证明 `both`;`tools:code-only` 仅在 `tools:ptc-only` 缺失时作为兼容别名,两者同时存在时以 `tools:ptc-only` 为准。只有缺少 owner signal 时才能从 tool shape 推断。后续 assembly 不重新解释插件自己的 `tools.presentAs('both')` 效果,而是从 agent composition owner 派生 presentation。`ptc` 独占 direct-tool collapse、顶层 native-call normalization 和 native dispatch rejection;`both` 保留 native direct tools。`ptc` 与 `both` 都适配 PTC `run_code` schema、program SDK 和 session runtime contexts,只有 `ptc` 安装并直接执行 `edit_run_code`。model stream 将完整 call 的 session 与 call ID 绑定到由 composition mode 派生的 request policy,dispatch wrapper 替换 cancellation signal 不改变执行判定;result settlement 和 session、agent 或插件释放分别撤销单次 call 与整个 lifecycle。
19
+ 若模型误发 live schema 可证明的顶层 native call,stream canonicalizer 会把这个 out-of-surface 调用规范为与模型本应生成的同一 member `run_code`;未知、畸形或不一致输入原样透传。派生 cell 不携带 provenance、纠错提示或其他模型可见标记;普通参数保留原始 JSON,含 own `__proto__` 的参数使用安全字面量,超过 TypeScript parser 安全深度的参数才使用浅层 `JSON.parse` 表达式。该规范化只修复无效 direct call。已声明的 `run_code` 与 `edit_run_code` 原样通过,因此 session history、UI 与下一轮 model context 保留合法调用的实际 tool name 和参数。
20
20
 
21
21
  `edit_run_code` 对其持久化 `tool/call` 事件出现时当前 open turn 最近的可编辑 cell 生效,无论该 cell 被拒绝、运行失败,还是成功执行但结果需要微调。执行通过 call event sequence 读取该快照,后续 settlement 或 handler 调度不改变目标;派生 cell 则按外层 `tool/result` event sequence 进入恢复历史,使 cold replay 保持 live kernel 的实际结算顺序。
22
22
  调查 tool 不擦除目标;edit 发起后的其他 settlement 不追溯改变其目标;一次成功 edit 生成的新 cell 成为下一次 edit 的目标。调用方必须且只能提交 `edits` 或
@@ -40,20 +40,21 @@ return 或打印需要展示的值。失败恢复先按状态分类:解析或
40
40
  `run_code` 并复用已有 binding,完整重写只保留给结构性改动或超出编辑预算的修正。能力和命令执行依赖当前 request
41
41
  与 execution world,模型必须先探查 live binding、实际 executable 和路径语义,不假设某个平台、shell 或 package runner。
42
42
 
43
- live `PTC-C001` 若精确落在源码 EOF,独立的 bounded analyzer 只把追加单个 `}`、`)`、`]` 的三个源码分别送入该 cell 提交时的同一 preparation context。仅当唯一候选通过、无 binding collision,现有 exact editor 能从不超过固定预算的唯一尾部物化同一源码,并且 runtime 已取得该 rejected `run_code` 的持久 call sequence 时,诊断才输出一行可直接调用的 `edit_run_code({ edits: [...], expected_target_call_seq })`。edit transport 把该 sequence 与其 call event 捕获的目标比较,目标已变化时不编辑也不派生执行。这项 validated repair 优先于长度阈值,但只证明语法/preflight 接受;插件不自动 dispatch,也不改写原始 `run_code`。非 EOF、多候选、多 token、超预算、缺少目标身份或后续 preparation 拒绝均回到原有 length-adaptive help。
43
+ live `PTC-C001` 若精确落在源码 EOF,独立的 bounded analyzer 只把追加单个 `}`、`)`、`]` 的三个源码分别送入该 cell 提交时的同一 preparation context。仅当唯一候选通过、无 binding collision,现有 exact editor 能从不超过固定预算的唯一尾部物化同一源码,并且 runtime 已取得该 rejected `run_code` 的持久 call sequence 时,诊断才输出一行可直接调用的 `edit_run_code({ edits: [...], expected_target_call_seq })`。edit transport 把该 sequence 与其 call event 捕获的目标比较,目标已变化时不编辑也不派生执行。这项 validated repair 优先于长度阈值,但只证明语法/preflight 接受;插件不自动 dispatch,也不改写原始 `run_code`。非 EOF、多候选、多 token、超预算、缺少目标身份或后续 preparation 拒绝均使用 length-adaptive help。
44
44
 
45
45
  稳定指引保留这些跨任务不变量和失败恢复的优先动作。长 cell 失败的短提示直接作为当前 `PTC-X001` 的结构化 `help` 输出,避免一次性建议触发完整 runtime-context 快照;重复绑定失败和当前 execution world 中由诊断确认的 executable、shell 或 path 错误仍由当前 session log 派生为 `tools:ptc-plus-tip/<trigger>/<ordinal>` runtime context,分别提示能力探查或重新确认环境。投影只接受 DSH system-prompt owner 的规范快照,并按命名 section 的有效状态变化重建提示;聚合快照重复同一 section 不增加次数,正文不参与身份判断。
46
46
  相同提示受 `tipCooldownMessages` 间隔约束,连续未解决时才升级为详细版本,成功 cell 会重置未解决计数;提示不会改变
47
47
  system sections、tool schema 或 tool order,也不假设 Windows、WSL、POSIX、shell 或 package runner。
48
48
 
49
- 当前策略位于独立的 `internal/recovery-tips.js` local provider,核心只消费其有界的 named context。另一个固定名称 `tools:ptc-plus-cordis-recovery` 只在新 agent 或 Cordis 重新启用代际观察到历史 Cordis transcript 时出现,将 replay value 限定为历史数据,并保持到 session log 出现新的成功 `cordis_inspect*` settlement;它不受疲劳阈值控制、不重放调用,也不推断 live process 是否实际丢失。未来外部决策插件的接入必须等待稳定的 facts/decision contract;在此之前不猜测跨插件 API,local provider 继续作为 fallback。
49
+ 当前策略位于独立的 `internal/recovery-tips.js` local provider,核心只消费其有界的 named context。另一个固定名称 `tools:ptc-plus-cordis-recovery` 只在新 agent 或 Cordis 重新启用代际观察到历史 Cordis transcript 时出现,将 replay value 限定为历史数据,并保持到 session log 出现新的成功 `cordis_inspect*` settlement;它不受疲劳阈值控制、不重放调用,也不推断 live process 是否实际丢失。外部决策插件必须提供稳定的 facts/decision contract 才能接入;缺少该契约时,核心不猜测跨插件 API,并继续使用 local provider。
50
50
 
51
51
  ## 设置与启用开关
52
52
 
53
- Host half 通过 DSH 公共 settings 服务注册 `ptc-plus` 命名空间,字段清单、默认值与校验来自 `internal/config-spec.js`。
54
- Client half 通过 `settings.plugin.item` 卡片呈现全部配置。`enabled` 是 kill switch:关闭时只保留 settings 注册和设置卡片,
55
- 不注册 runtime、hook、tool surface 或 system prompt section;设置卡片显示“已启用/已停用”,`code` preset 会话在头部单独显示 `PTC Plus` 指示器。稳定指引不包含 UI 品牌名。
56
- 所有字段都由 settings watch 即时交给各自 owner,且不替换已有 session-bound binding。每个已提交 cell 固定其提交时的配置代际,timer、worker 消息、program binding bridge、结果校验和诊断共同消费该快照;重配置更新随后提交 cell 的默认值。`maxOldGenerationSizeMb` 在活动 worker 存在时因 Node 的创建期限制而拒绝并回滚。settings 服务缺失时 Host 回退到 composition config,原有行为不变。
53
+ Host half 通过 DSH 公共 settings 服务注册 `ptc-plus` 命名空间,优先使用 provider-owned `installSection()`,并兼容根模块的 `installSettingsSection()`;两条路径共享同一 fallback、watch、rollback 与 disposal 语义。字段清单、默认值与校验来自 `internal/config-spec.js`。
54
+ Client half 通过 `settings.plugin.item` 卡片呈现全部配置。`enabled` 是 kill switch:关闭时只保留 settings 注册和设置卡片;`enhancedToolView` 是默认开启的正文展示开关,关闭时只交还 DSH 原生 `run_code`/`edit_run_code` generic row,
55
+ 不注册 runtime、hook、tool surface 或 system prompt section;`ptc` 与兼容 `code` preset 会话在头部单独显示 `PTC Plus` 指示器。设置卡片显示“已启用/已停用”,稳定指引不包含 UI 品牌名。
56
+ `autoDescribeRunCode` 默认开启且只控制请求绑定的本地执行策略。开启时,缺少外层 `run_code.description` 的调用使用派生参数通过本地 DSH 校验,备用摘要仅进入 presentation metadata;关闭时由 DSH 校验原始参数。两种状态的模型可见 tool schema、tool order 和 system sections 保持字节稳定并包含 required `description`;原始调用参数、cell 与嵌套 native 参数保持不变。
57
+ 所有字段都由 settings watch 即时交给各自 owner,且不替换已有 session-bound binding。每个已提交 cell 固定其提交时的配置代际,timer、worker 消息、program binding bridge、结果校验和诊断共同消费该快照;重配置更新随后提交 cell 的默认值。`maxOldGenerationSizeMb` 在活动 worker 存在时因 Node 的创建期限制而拒绝并回滚。settings 服务缺失时 Host 回退到 composition config,并保持相同的运行时语义。
57
58
 
58
59
  `cordisToolsEnabled` 默认关闭且即时生效。Host 只在可见 `run_code` 的 agent scope 中挂载官方 `@deepseek-ai/dsh-tool-cordis`,并通过公共 `agentPresets` service 定位 shipped `cordis` preset,再用维护中的 Skill filesystem plugin 把其 companion Skill 目录发布到同一 scope。两个 child fiber、tool guidance 与 `cordis-plugin-development` Skill 是一个 mount;首轮 request 等待完整发布和 scoped Skill load 验证,关闭、agent/runtime 释放或任一激活失败时逆序卸载。工具名、数量、schema 和 guidance 直接来自 official tool fiber,Skill 内容直接来自 shipped preset,Host 均不复制。官方插件同时向 process-global `cordisInspect` 注册 Host provider;owner 将 manifest 相同的 per-agent 注册合并为引用计数 lease,查询委托给当前仍存活的官方 registration,最后一份 lease 释放后才注销 provider。manifest 不一致时启用失败。该开关不切换 preset,也不改变 code-only direct-tool projection。
59
60
 
@@ -117,7 +118,7 @@ CodeRuntime request 已携带的 owner-provided program namespace 会被原样
117
118
 
118
119
  journal 通过 `run_code.output.presentationMeta` 附着到最终 result,再由 `tools/result` 做两阶段确认。缺失、损坏或被替换的 journal 形成 unknown/volatile 边界;未进入 runtime 的 call 由后续 `confirms` 以对应 `tool/call.seq` 证明为 no-op。volatile 源码保留在原 session log,但不参与 cold replay。
119
120
 
120
- durable replay 无法验证某个 node 时,当前已结算 `tool/result` 的私有 `meta.dshPtcPlusRecoveryBoundaries` 记录失败 call 与其 parent frontier,不修改冻结的历史 result。boundary 在进入排序前必须完成 schema 与 event-sequence validation;损坏 metadata 使恢复 fail closed,不能从 fold 中静默消失。折叠器剪除该 node 及依赖后代、重算可重建 checkpoints;kernel 重置 worker 并逐级验证更早 frontier,随后执行当前 live cell。新 durable node 的 parent 始终是 worker 实际拥有的 frontier。旧版自定义 boundary event 不由运行时生成,迁移工具必须在 DSH restore 前显式转换它。
121
+ durable replay 无法验证某个 node 时,当前已结算 `tool/result` 的私有 `meta.dshPtcPlusRecoveryBoundaries` 记录失败 call 与其 parent frontier,不修改冻结的历史 result。boundary 在进入排序前必须完成 schema 与 event-sequence validation;损坏 metadata 使恢复 fail closed,不能从 fold 中静默消失。折叠器剪除该 node 及依赖后代、重算可重建 checkpoints;kernel 重置 worker 并逐级验证更早 frontier,随后执行当前 live cell。新 durable node 的 parent 始终是 worker 实际拥有的 frontier。自定义 boundary event 不由运行时生成,迁移工具必须在 DSH restore 前显式转换它。
121
122
 
122
123
  诊断由封闭结构确定性渲染,包括语法、preflight、绑定冲突、输出、运行异常和恢复边界。普通成功与首次进入 volatile 不投影 warning/note;恢复分类保留在 journal 和 `repl.state(list)` 中。
123
124
 
package/docs/client-ui.md CHANGED
@@ -1,33 +1,51 @@
1
1
  # Client UI
2
2
 
3
- PTC Plus 现在提供 DSH Web/Desktop 的**插件设置卡片**,位置在 Settings → Plugin configuration。
3
+ PTC Plus 在 DSH Web/Desktop 的 Settings → Plugin configuration 中提供**插件设置卡片**。
4
4
 
5
5
  ## 设置命名空间
6
6
 
7
7
  Host half 通过 DSH 公共 `settings` 服务注册命名空间 `ptc-plus`。字段清单、默认值和校验来自 `internal/config-spec.js`,
8
- 由 `index.js` 的 `Config` schema 与设置注册共用;client half 构建时把同一份字段清单打进 bundle,不在 UI 中复制默认值。
8
+ 由 `index.js` 的 `Config` schema 与设置注册共用;client half 构建时把同一份字段清单打进 bundle,不在 UI 中复制默认值。卡片按“常用与兼容性”“可选能力”“高级行为”“资源限制”分区显示,用户需要主动决策的选项位于前面。
9
9
 
10
10
  ## 可用设置
11
11
 
12
12
  | 分组 | 字段 | 说明 |
13
13
  | --- | --- | --- |
14
14
  | 开关 | `enabled` | 关闭后不注册 `run_code`/`edit_run_code`、不修改 system prompt、不创建 session runtime;只保留设置 UI。 |
15
- | Cordis | `cordisToolsEnabled` | 默认关闭;开启后为 PTC agent 加入官方 Cordis 工具、指引与精确的 `cordis-plugin-development` companion Skill,不发布同目录 sibling。 |
16
- | 计算 | `computeMs` / `maxWallMs` | 单 cell CPU 与墙钟预算。 |
17
- | 输出 | `maxOutputBytes` / `maxValueNodes` / `maxValueEdges` / `maxValueArrayLength` / `maxValueBigIntDigits` | Value Graph 与输出字节预算。 |
15
+ | 展示 | `enhancedToolView` | 默认开启;关闭后注销 PTC Plus 的两个 keyed tool view,恢复 DSH 原生 generic row。 |
16
+ | 常用与兼容性 | `autoDescribeRunCode` / `canonicalizeToolCalls` | 默认开启;允许缺少外层摘要的 `run_code` 执行,并修复 schema 可唯一确认的顶层 native 工具误调用。 |
17
+ | 可选能力 | `cordisToolsEnabled` | 默认关闭;开启后为 PTC agent 加入官方 Cordis 工具、指引与精确的 `cordis-plugin-development` companion Skill,不发布同目录 sibling。 |
18
+ | 高级行为 | `looseTopLevelRedeclarations` / `autoRewriteImports` / `autoStripExports` / `autoSplitRedeclarations` / `durableReplay` / `tipsEnabled` | 默认开启;控制跨 cell 重声明、模块语法适配、worker 重启后的状态恢复与失败提示。 |
19
+ | 计算 | `computeMs` / `maxWallMs` | 单 cell CPU 与总耗时预算。 |
18
20
  | Worker | `maxOldGenerationSizeMb` / `maxNestedRunCodeDepth` | kernel worker 内存与嵌套执行深度。 |
19
- | 行为 | `canonicalizeToolCalls` / `looseTopLevelRedeclarations` / `durableReplay` | REPL 与 transport 策略。 |
20
- | 模块 | `autoRewriteImports` / `autoStripExports` / `autoSplitRedeclarations` | AST 重写开关。 |
21
- | 提示 | `tipsEnabled` / `tipCooldownMessages` / `tipEscalationFailures` | 恢复提示策略。 |
21
+ | 输出 | `maxOutputBytes` | 单 cell 日志与返回结果的合计字节上限。 |
22
+ | 返回值 | `maxValueNodes` / `maxValueEdges` / `maxValueArrayLength` / `maxValueBigIntDigits` | Value Graph 与大数组、BigInt 的返回上限。 |
23
+ | 提示阈值 | `tipCooldownMessages` / `tipEscalationFailures` | 同类提示间隔与详细提示阈值。 |
22
24
 
23
- ## enabled 开关与启用标识
25
+ ## enabled 开关与正文功能标记
24
26
 
25
27
  所有字段都是实时设置:Host 在 settings watch 中安装、卸载或重配置 runtime。已提交的 cell 固定使用提交时的配置快照,运行中的更新从随后提交的 cell 开始使用;这不会替换 session-bound REPL 或已有 binding。`maxOldGenerationSizeMb` 在活动 worker 存在时因 Node 的创建期限制而拒绝并回滚,其他可重配置字段照常交给 owner。关闭后唯一的宿主副作用是注册 settings 命名空间,
26
28
  保证设置卡片仍然可读;此时只有 `enabled` 可写,其他控件被禁用。运行时重配置失败时回滚到上一次已应用值。
27
29
 
28
30
  `cordisToolsEnabled` 默认关闭并即时生效。它不切换 preset,而是把官方 Cordis 工具、owner guidance 与 `cordis-plugin-development` Skill 作为一个 agent-scoped mount 加入或移出 PTC agent;顶层仍为 `run_code` / `edit_run_code`,普通 agent 不继承。Host 缺少 official preset、Skill/Cordis service 或任一 contribution 加载失败时,启用会完整回滚。完整运行要求见 [运行时参考](runtime-reference.md)。
29
31
 
30
- 设置卡片折叠头部显示 `PTC Plus`、`PTC 模式的会话级 TypeScript REPL。`、启用状态和可访问的展开/收起名称;稳定 REPL 指引不承载 UI 品牌名。启用且会话选择 `code` preset 时,`conversation.session.header.actions` 额外显示 `PTC Plus` 指示器。关闭时不注入任何 PTC 指引或工具 surface。
32
+ `enhancedToolView` 默认开启并即时生效。开启时 PTC Plus 通过公共 keyed tool-view surface 为 `run_code` 与 `edit_run_code` 提供增强行,并在可用时使用 DSH 公共 `DisclosureRow`/`CodeBlock` primitive,缺少某项 capability 时使用插件自有的等价降级;关闭时立即注销这两个 keyed view,由 DSH 原生 generic row 负责布局、状态、代码高亮和输入/输出卡片。该开关只影响 Client 展示,不改变工具、prompt、runtime 或 session 语义。
33
+
34
+ `autoDescribeRunCode` 的设置名称是“允许执行缺少摘要的 run_code”,默认开启并即时生效。缺少外层 `run_code.description` 时,调用使用派生参数通过本地 DSH 校验,备用摘要仅进入 presentation metadata;关闭时由 DSH 校验原始参数。两种状态的模型请求保持字节稳定并包含 required `description`,原始调用参数、已有摘要、cell 源码和嵌套 native 工具参数保持不变。
35
+
36
+ 设置卡片、正文 tool view 与 REPL 可复用 binding 卡片的全部文案都注册到 DSH client locale 的 `settings.ptcPlus` 命名空间,随当前界面语言在中文与 English 之间切换;字段名称与说明的两种语言文本同样来自 `internal/config-spec.js`(`label`/`labelEn`、`description`/`descriptionEn`,某一字段的说明要么两种语言都有,要么都没有),展示 chrome 文案由 client half 拥有。稳定 REPL 指引不承载 UI 品牌名。启用且会话选择 `ptc` 或兼容的 `code` preset 时,`conversation.session.header.actions` 以稳定 id `ptc-plus-active` 显示简洁的 `PTC Plus` 标识;当前 session projection 与兼容顶层字段冲突时以前者为准。关闭时不注入任何 PTC 指引或工具 surface。
37
+
38
+ 绿色 `PTC Plus` 活动标识支持鼠标悬浮、键盘聚焦和点击。卡片通过浏览器 Popover top layer 脱离普通 stacking context,按触发器和当前视口的可用空间在上方或下方定位,因此不受会话侧栏覆盖;长列表只在卡片内部滚动。变量、函数、类和导入使用可区分的类型色彩。卡片读取公共 `sessionProjections` 携带的 `projectionValues.ptcPlusRepl`,展示当前 runtime owner 已证明可供该 agent 后续 cell 复用的 binding 名称、类别、单行定义预览以及原始行列;点击整行即可在卡片内展开有界 TypeScript 声明源码,并以抽屉式过渡动画显示。声明来源由 AST preparation 从已经提交的 cell 文本确定,BindingCatalog 在替换和 replay 时更新,不读取值、不执行代码、不触发 getter。Host 在每个结算结果的并行私有 `meta.dshPtcPlusBindings` envelope 中写入 runtime generation 和完整的 value-independent inventory;最多携带 128 项、名称最长 128 字符、每段声明源码至多 1024 个 UTF-16 code unit、声明源码合计至多 16384 个 code unit,同时保留精确有效总数和省略数。volatile cell 的 live binding 继续显示;restore 或 discarded settlement 重置 worker,在后续 cell 重新物化有效 binding surface 前显示不可确认。
39
+
40
+ 清单按最近声明或重声明优先的栈顺序排列,同一 cell 内靠后的声明位于靠前声明之上。BindingCatalog 保留这个顺序,presentation snapshot 和 projection 原样传递,不根据名称重新排序。
41
+
42
+ 投影通过 DSH 正式的 `tool/call.data.callId` / `tool/result.data.message.source.callId` 身份配对 `run_code` 与 `edit_run_code`,不依赖可选 `sourceEventSeqs`。它只接受当前 runtime generation 的 envelope,并在 `session/end-seed` 清除旧 lifecycle 的 live 证明;缺少、损坏、其他 generation 的 metadata、无关 tool result 与 surface replacement 都不能覆盖仍有效的最后已证明清单。缺少有效 metadata、projection service 或兼容 registration contract 时只显示不可确认,不主动读取历史、不迁移日志,也不影响继续会话。来源跳转要求 Host 提供打开 tool call 或 source location 的公共 capability;缺少该 capability 时,卡片只提供内联检查,不访问私有 store 或宿主 DOM。
43
+
44
+ `enhancedToolView` 开启且插件启用时,Client 通过公共 keyed `tool.call.toolview` 注册 `run_code` 与 `edit_run_code`;任一开关关闭时实时 dispose 两个注册,让 owner 恢复 DSH 原生 fallback。这个正文 slot 只依赖 Host 提供的 key、`toolName`、`sessionId`、`useSessions`、运行中/已结算 `block` 与 `inspect` 公共契约。增强行保留 Code / Code edit 标题、可展开的原始源码(`arguments` 与 `argsRaw` 任一公共形态均可读)、结果、可见且可访问的运行/失败/中断状态和 Inspect 入口;在提供时优先使用公共 `DisclosureRow` 与 `CodeBlock`,缺少 `DisclosureRow` 时回退到插件自有行,缺少 `CodeBlock` 时回退为纯文本源码。`DisclosureRow` 路径把运行/失败/中断状态、摘要和已证明的功能标记放进同一行始终可见的单行预览;完成态由标题与摘要表达,运行/失败/中断状态由文字 chip 承担并保持可访问。预览间距由插件自有类对称提供,并按单行内容布局,不依赖 Host primitive 的行内几何;源码、结果和 Inspect 只出现在展开主体。结果内容与原生 ioCard 采用相同的灰色圆角背景(`--dsw-alias-markdown-code-block` 加 12px 圆角与边框),失败态使用错误色文字;代码块的语言/复制栏保持原生 CodeBlock 的常亮可见;`检查调用` 入口采用原生 inspectButton 同款胶囊样式,默认 opacity 0,仅在卡片 `:hover` 或 `:focus-visible` 时淡入(opacity .1s),不触发任何布局位移。窄屏下回退路径的摘要允许换行。
45
+
46
+ 功能标记不是计数,也不把“插件已启用”冒充成一次功能收益。Client 只在完整 v1-v3 journal 和对应附属 metadata 能证明时显示:自动改写 import(附模块名)、自动剥离 export、自动拆分混合重声明(附已有 binding)、带完整 target/derived-run/non-noop/可选恢复边界关系且与调用目标前置条件一致的安全编辑执行、成功的 `code.run`、由 `PTC-R002` 的 `warning/recover/rolled-back` 语义元组证明本次确实发生的持久重放,以及 `repl.state` 操作。进程保留或 discarded 状态不作为正文功能标记。普通宽松重声明、顶层 native 调用是否来自 canonicalizer、`cordis_*` 成员是否由官方可选 Cordis mount 提供,以及是否实际选择了 prompt 恢复提示没有独立的 Client-only 事实,因此不根据可复制的代码形状、工具名前缀或设置默认值推测;恢复边界本身不展示。
47
+
48
+ 这条路径不检查或改写宿主 DOM,不调用 Host、不主动加载历史。binding 卡片只消费随结果持久化的 value-independent presentation metadata,其中源码只是已提交 cell 的有界声明片段,不是运行时值;它不改变 canonical result、tool schema、system prompt、runtime context、journal schema 或迁移器。未知 journal 版本、未知 metadata 字段和损坏 metadata 只会让对应功能标记或 binding 列表缺席,不会隐藏源码或结果;无法脱离 session call identity 解析非空 v1 `confirms` 时同样只省略标记,不触发历史读取或迁移。因此 UI 是否安装、能否渲染以及用户是否悬浮、聚焦或展开正文行,都不会改变模型请求或会话继续语义。
31
49
 
32
50
  live 配置若因宿主能力缺失或 runtime 安装/重配置失败,会先回滚所有已创建或更新的 owner,再把持久设置回写为上一次已应用值;回滚写入失败时 Host 记录 activation diagnostic,避免静默把配置显示成不存在的 runtime。
33
51
 
@@ -39,9 +57,9 @@ live 配置若因宿主能力缺失或 runtime 安装/重配置失败,会先
39
57
  npm run build
40
58
  ```
41
59
 
42
- `package.json` 声明 `dsh.client` 与 `./client` export。`npm run build` 从 `src/client.js` 生成 `client.js`,`npm run build:check` 比较确定性产物;`prepack` 会阻止陈旧 bundle 发布。Client half 只依赖 DSH 注入面提供的 settings、session、slot 与 UI primitive 模块。
60
+ `package.json` 声明 `dsh.client` 与 `./client` export。`npm run build` 从 `src/client.js` 生成 `client.js`,`npm run build:check` 比较确定性产物;`prepack` 会阻止陈旧 bundle 发布。Client half 只依赖 DSH 注入面提供的 settings、locale、session、slot 与 UI primitive 模块。
43
61
 
44
62
  ## 回退
45
63
 
46
64
  如果 DSH settings service 不可用,Host half 不会因缺少设置服务而加载失败:设置卡片不可用时直接回退为 composition `config`,
47
- `enabled` 默认开启,原有运行时行为保持不变。
65
+ `enabled` 默认开启,运行时按 composition config 工作。
@@ -1,6 +1,6 @@
1
1
  # Installation
2
2
 
3
- PTC Plus requires Node.js `^22.19.0 || >=24.0.0` and the latest available DSH release with TypeScript PTC mode. Compatibility follows DSH's public extension surfaces rather than a version allowlist.
3
+ PTC Plus requires Node.js `^22.19.0 || >=24.0.0` and targets the latest available DSH release with TypeScript PTC mode. Compatibility with the preceding Host contract is retained through public capability detection; when both generations expose competing presentation evidence, the current DSH contract wins. Compatibility follows those live extension surfaces rather than a version allowlist.
4
4
 
5
5
  Install the plugin into the profile that actually runs the target DSH surface. Do not assume a profile named `default` is active.
6
6
 
@@ -16,14 +16,14 @@ Install the plugin into the profile that actually runs the target DSH surface. D
16
16
 
17
17
  `danger-full-access` is the primary supported experience. The worker isolates the REPL lifecycle; it is not a malicious-code sandbox. DSH continues to own native-tool scope, policy, approval, cancellation, sandboxing, and scheduling. Narrower profiles expose only their available capabilities; PTC Plus does not simulate missing authority or add another permission system.
18
18
 
19
- The optional `cordisToolsEnabled` integration requires the current DSH installation to provide its shipped `cordis` preset plus the public preset, Skill, Cordis, settings, and tool-runtime packages. PTC Plus declares those host-owned DSH packages as required peers instead of installing private runtime copies; DSH's profile module fallback must resolve them from the active installation. Do not copy `SKILL.md` or add the Cordis preset's Skill directory to global roots. If the host surface is incomplete, plugin activation or enabling the setting fails instead of loading a second DSH core.
19
+ The optional `cordisToolsEnabled` integration requires the current DSH installation to provide its shipped `cordis` preset plus the public preset, Skill, Cordis, settings, and tool-runtime packages. PTC Plus declares those host-owned DSH packages as unrestricted required peers instead of installing private runtime copies; runtime capability validation owns compatibility, and CI imports the packed plugin against both the current and preceding release channels. DSH's profile module fallback must resolve the peers from the active installation. Do not copy `SKILL.md` or add the Cordis preset's Skill directory to global roots. If the host surface is incomplete, plugin activation or enabling the setting fails instead of loading a second DSH core.
20
20
 
21
21
  ## npm Release
22
22
 
23
23
  Use this form after the selected version is available from the npm registry:
24
24
 
25
25
  ```sh
26
- dsh plugin --profile <profile> add dsh-ptc-plus@0.2.3
26
+ dsh plugin --profile <profile> add dsh-ptc-plus@0.3.1
27
27
  dsh --profile <profile> --dump-config
28
28
  ```
29
29
 
@@ -60,7 +60,7 @@ pnpm dsh --profile <profile> --dump-config
60
60
 
61
61
  ```sh
62
62
  npm pack
63
- dsh plugin --profile <profile> add /absolute/path/to/dsh-ptc-plus-0.2.3.tgz
63
+ dsh plugin --profile <profile> add /absolute/path/to/dsh-ptc-plus-0.3.1.tgz
64
64
  dsh --profile <profile> --dump-config
65
65
  ```
66
66
 
@@ -70,6 +70,14 @@ Windows development checkouts can create and install an immutable content-addres
70
70
  scripts\install-dev.cmd <profile>
71
71
  ```
72
72
 
73
+ ## Isolated latest-DSH development launcher
74
+
75
+ To test this checkout in a separate DSH installation, double-click `scripts\run-dev-dsh.cmd`. It resolves the current `@deepseek-ai/dsh@alpha` dist-tag, installs that exact version only when the cached version changes, creates the shipped `web` profile inside an isolated `DSH_HOME`, installs this checkout, and starts DSH. If no port is supplied, it selects a free loopback port automatically, so another DSH on port 3080 does not block the test instance. The launcher does not modify the repository or your normal DSH home.
76
+
77
+ The default cache is `%LOCALAPPDATA%\dsh-ptc-plus-dev`. It contains the isolated `DSH_HOME`, DSH installations, immutable plugin snapshots, and a pnpm store. Only the newest three DSH versions and plugin snapshots are retained, and the dedicated pnpm store is pruned after each install. Set `DSH_DEV_CACHE` to move the cache, `DSH_DEV_PROFILE` to change the profile name, `DSH_DEV_VERSION` to select another npm dist-tag or version, `DSH_DEV_PORT` to choose a fixed Web port, or `DSH_DEV_MAX_VERSIONS` to change the retention count (1-10).
78
+
79
+ The Windows launchers normalize the process, machine, and user `PATH` values in memory, remove duplicate entries, and add the Node/cache directories they need. They do not create drive mappings, junction trees, or persistent environment changes. If a genuinely unique PATH is still too long for `cmd.exe`, the launcher stops before npm or DSH runs and asks you to shorten the relevant PATH value.
80
+
73
81
  ## DSH Desktop
74
82
 
75
83
  On Windows or macOS, choose **Open DSH Terminal** from the Desktop tray. Bare commands in that terminal target the active profile:
@@ -79,4 +87,4 @@ dsh plugin add github:muyuanjin/dsh-ptc-plus#main
79
87
  dsh --dump-config
80
88
  ```
81
89
 
82
- After an npm release, the package spec may instead be `dsh-ptc-plus@0.2.3`. For a local package, use its absolute tarball path. Restart DSH Desktop after installation. Linux Desktop is not a current DSH Desktop release target; use DSH CLI/Web on Linux.
90
+ After an npm release, the package spec may instead be `dsh-ptc-plus@0.3.1`. For a local package, use its absolute tarball path. Restart DSH Desktop after installation. Linux Desktop is not a current DSH Desktop release target; use DSH CLI/Web on Linux.
@@ -14,6 +14,30 @@
14
14
 
15
15
  在 `cordis.patch.yml` 和解析后的 DSH 配置中,`id: ptc-plus` 标识插件配置项,`name: dsh-ptc-plus` 标识待加载的包。内部错误前缀、runtime context 名称和 worker 临时路径会按所属子系统使用 `ptc-plus` 或 `dsh-ptc-plus`;它们不是另外的公开产品名。
16
16
 
17
+ ## 发布权限与流水线
18
+
19
+ npm 发布遵循 [ADR 0022](adr/0022-stage-npm-releases-with-oidc.md),使用 `.github/workflows/release.yml` 声明的 Trusted Publisher,不向 GitHub 保存 npm write token。npm package settings 必须把 publisher 精确绑定到 `muyuanjin/dsh-ptc-plus`、workflow filename `release.yml` 和 environment `npm-release`,且只允许 `npm stage publish`。Publishing access 选择页面中最严格且标记为 recommended 的 2FA 选项;npm 文档称其为 `Require two-factor authentication and disallow tokens`,当前 npm 页面也可能显示 `Require two-factor authentication and disallow bypass 2fa tokens (recommended)`。不要从滚动变化的标签推断账户中已不存在传统 token。OIDC 只把经过验证的 tarball 放入 npm staging,维护者仍须在 npm CLI 或 npmjs.com 检查 staged package 并以 2FA approve,包才会进入公开 registry。
20
+
21
+ 普通 `CI` 只响应 `main` push 与 pull request。发布前创建 annotated `v<package.version>` tag,原子推送 release commit 与 tag,等待该 commit 的 `CI` 成功,然后以该 tag 作为 ref 显式运行 `Release` workflow。workflow 不接受另一个 tag input,并会重新证明 dispatch 的 `GITHUB_REF`、`GITHUB_SHA`、annotated tag target、checkout HEAD、manifest 版本和成功 CI 的 SHA 完全一致;release job 使用 GitHub-hosted Node `24.15.0`、npm `12.0.2`、无 package-manager cache 和最小 `id-token: write` 权限,构建一个 tarball、执行 clean-consumer smoke,再对同一文件执行 `npm stage publish`。`package.json#allowScripts` 显式拒绝当前不需要的 `esbuild` install script;`replace-registry-host=always` 让 release install 将 lockfile 中的 registry tarball 路由到显式 npm 官方 registry,而不放宽 npm 12 的 Git/remote dependency 默认拒绝。
22
+
23
+ ```powershell
24
+ git tag -a "v$((Get-Content package.json | ConvertFrom-Json).version)" -m "v$((Get-Content package.json | ConvertFrom-Json).version)"
25
+ git push --atomic origin main "v$((Get-Content package.json | ConvertFrom-Json).version)"
26
+ gh workflow run release.yml --ref "v$((Get-Content package.json | ConvertFrom-Json).version)"
27
+ ```
28
+
29
+ workflow 成功只表示 package 已 staged,并非已经公开发布。维护者从 workflow 日志或 npmjs.com 的 Staged Packages 页面取得 stage id,检查 metadata 和下载的 tarball 后执行:
30
+
31
+ ```powershell
32
+ npm.cmd stage view '<stage-id>' --json --registry=https://registry.npmjs.org/
33
+ npm.cmd stage download '<stage-id>' --registry=https://registry.npmjs.org/
34
+ npm.cmd stage approve '<stage-id>' --registry=https://registry.npmjs.org/
35
+ ```
36
+
37
+ `npm stage approve` 必须完成 npm 2FA。approve 后 npm 会进行 publish-time malware scan;公开可安装通常延迟约 5 分钟,高峰或需进一步检查时可能超过 15 分钟。发布自动化必须轮询明确版本并允许该延迟,不能把 approve 返回等同于 registry 已可安装。扫描确认版本可用后再发布对应 GitHub Release。staged 内容错误时使用 `npm stage reject <stage-id>` 并完成 2FA,不能移动既有 tag 或用同一版本覆盖。
38
+
39
+ 首次 OIDC staging 成功后,检查 npm 账户的 Access Tokens,并撤销所有仍可对本包执行 publish 或 stage 的 granular/automation token,尤其是 bypass-2FA token;在 Trusted Publisher 尚未实测成功前保留恢复路径,不能把未验证的凭据切换当成完成。后续 workflow 不保存或读取 npm token,定期清点用于发现账户侧重新引入的发布凭据。
40
+
17
41
  ## Release checklist
18
42
 
19
43
  1. 在 Node `22.19.0` 与 Node 24.x 上运行 `npm ci`、`npm run build:check` 和 `npm run check`;Node 24.x 还应覆盖 Windows、Linux 与 macOS。仓库 CI 定义同一矩阵。
@@ -22,7 +46,7 @@
22
46
  4. 在 Windows 与 Linux CLI/Web 实机启动临时 profile。macOS 由原生 runner 验证 CLI/Web;Windows 与 macOS Desktop 从托盘打开当前 profile 的 DSH Terminal 安装,重启 Desktop 后做一次 PTC 模式 smoke。Desktop 当前发布平台不包括 Linux。
23
47
  5. 只有在明确授权模型消耗后才运行 `npm run test:expensive` 与 `npm run test:ab`。发布结论以结构化测试结果为准,不把一次模型样本推广为普遍保证。
24
48
  6. 确认 npm 包名可用、待发布版本高于 registry 版本、README 截图来自真实 DSH 会话,并核对 repository、homepage、bugs、license 与 `dsh-plugin` topic。
25
- 7. 最后检查 `npm pack --silent --json` 的名称、版本、大小与文件列表,再创建并推送 release commit/tag,随后发布同一 commit 产生的 tarball。
49
+ 7. 最后检查 `npm pack --silent --json` 的名称、版本、大小与文件列表,创建 annotated release tag,并确认 tag commit 已通过 `CI`。运行 `Release` workflow 将同一源码产生的 tarball送入 npm staging;人工检查并以 2FA approve 后,等待 registry 扫描完成,再创建 GitHub Release。
26
50
 
27
51
  远程仓库、registry、签名凭据、目标平台运行状态和模型额度都属于发布时事实,不能由本地 manifest 推断。
28
52
 
@@ -8,7 +8,7 @@ The default loose binding mode lets a complete top-level `const` or `let` declar
8
8
 
9
9
  Every capability namespace is leased to one cell. Captured `tools`, `capabilities`, `repl`, `code`, or member functions expire when that cell ends.
10
10
 
11
- For PTC mode requests using the code-only direct-tool projection, PTC Plus presents two tools in stable order: `run_code` and `edit_run_code`. The latter edits the most recent eligible cell visible when the edit call is dispatched, including a cell that completed successfully but needs a small adjustment. That target remains fixed while the call is in flight; later tool settlements cannot retarget it. The edit accepts exactly one atomic `edits` or `regex_edits` array and an optional `expected_target_call_seq` precondition. Invalid, unavailable, or mismatched-target edits return `{ edited: false, reason }` without executing source or consuming the target.
11
+ For PTC mode requests using the code-only direct-tool projection, PTC Plus presents two tools in stable order: `run_code` and `edit_run_code`. It recognizes the current Host's `tools:ptc-only` owner section and the preceding Host's `tools:code-only` alias, with the current section authoritative if both appear. The latter tool edits the most recent eligible cell visible when the edit call is dispatched, including a cell that completed successfully but needs a small adjustment. That target remains fixed while the call is in flight; later tool settlements cannot retarget it. The edit accepts exactly one atomic `edits` or `regex_edits` array and an optional `expected_target_call_seq` precondition. Invalid, unavailable, or mismatched-target edits return `{ edited: false, reason }` without executing source or consuming the target.
12
12
 
13
13
  `edit_run_code` is a real registered tool call. Session history and later model requests retain its original name and delta arguments. The plugin executes the materialized source as a host-derived `run_code`, returns only the edit status, value, and logs to the model, and keeps the complete source plus journal in private replay metadata. It does not rewrite assistant history or emit edit-specific runtime context.
14
14
 
@@ -45,28 +45,34 @@ For commands, prefer project-declared scripts and use an available typed tool. W
45
45
 
46
46
  The configuration uses `ptc-plus` as the DSH plugin ID and `dsh-ptc-plus` as the package name. The distinction is intentional: the former is the runtime/settings identity, while the latter is the repository and npm package identity.
47
47
 
48
+ `enhancedToolView` defaults to `true` and controls only the browser presentation of `run_code` and `edit_run_code`. Turning it off unregisters PTC Plus's keyed tool views so DSH's native generic row renders the calls; turning it on restores the compatibility renderer without changing execution, prompt, or session behavior.
49
+
50
+ `autoDescribeRunCode` defaults to `true` and appears as “Allow run_code execution without a summary.” When enabled, a call that omits the outer `run_code.description` uses derived arguments for local DSH validation, and the fallback summary enters presentation metadata only. When disabled, DSH validates the original arguments. Both states expose byte-identical model requests with the required `description` declaration; original call arguments, existing summaries, cell source, and nested native-tool JSON remain unchanged.
51
+
48
52
  ```yaml
49
53
  - id: ptc-plus
50
54
  name: dsh-ptc-plus
51
55
  config:
52
56
  enabled: true
57
+ enhancedToolView: true
58
+ canonicalizeToolCalls: true
59
+ autoDescribeRunCode: true
60
+ looseTopLevelRedeclarations: true
61
+ autoRewriteImports: true
62
+ autoStripExports: true
63
+ autoSplitRedeclarations: true
64
+ durableReplay: true
65
+ tipsEnabled: true
53
66
  cordisToolsEnabled: false
54
67
  computeMs: 60000
55
68
  maxWallMs: 600000
56
- maxOutputBytes: 67108864
57
69
  maxOldGenerationSizeMb: 512
70
+ maxNestedRunCodeDepth: 8
71
+ maxOutputBytes: 67108864
58
72
  maxValueNodes: 100000
59
73
  maxValueEdges: 1000000
60
74
  maxValueArrayLength: 1000000
61
75
  maxValueBigIntDigits: 100000
62
- maxNestedRunCodeDepth: 8
63
- canonicalizeToolCalls: true
64
- looseTopLevelRedeclarations: true
65
- durableReplay: true
66
- autoRewriteImports: true
67
- autoStripExports: true
68
- autoSplitRedeclarations: true
69
- tipsEnabled: true
70
76
  tipCooldownMessages: 3
71
77
  tipEscalationFailures: 2
72
78
  ```
@@ -77,9 +83,9 @@ The configuration uses `ptc-plus` as the DSH plugin ID and `dsh-ptc-plus` as the
77
83
 
78
84
  Keep large Cordis host or client source in a top-level binding before calling a Cordis tool. A parse or validation rejection is a runtime failure, so bindings assigned before it remain live for a short retry cell; reuse the binding instead of resending the source.
79
85
 
80
- Cordis Plugins and Runs are process-local. On a resumed agent or after Cordis is re-enabled, normalized journal calls can restore their recorded return values but do not prove that prior IDs, approvals, Runs, or Inspect observations are live. The fixed `tools:ptc-plus-cordis-recovery` context remains present until a new successful `cordis_inspect*` call is settled. Use that live read-only inspection before a stateful Cordis decision; do not rerun a mutating historical call merely to rebuild state.
86
+ Cordis Plugins and Runs are process-local. On a resumed agent or after Cordis is re-enabled, normalized journal calls can restore their recorded return values but do not prove that prior IDs, approvals, Runs, or Inspect observations are live. The fixed `tools:ptc-plus-cordis-recovery` context remains present until a new successful `cordis_inspect*` call is settled. Use that live read-only inspection before a stateful Cordis decision; do not rerun a recorded mutating call merely to rebuild state.
81
87
 
82
- `durableReplay: false` starts new kernels without historical REPL state while preserving live bindings in the current process. It does not delete session logs.
88
+ `durableReplay: false` starts new kernels without recorded REPL state while preserving live bindings in the current process. It does not delete session logs.
83
89
 
84
90
  Recovery tips are disabled with `tipsEnabled: false`. When enabled, a bounded runtime context in the reserved `tools:ptc-plus-tip/<trigger>/<ordinal>` name family may appear after a repeated binding failure or a diagnostic that identifies an executable, shell, or path problem in the current execution world. The trigger and per-trigger ordinal are reconstructed from canonical named system-prompt snapshots; repeated aggregate copies and visible wording do not advance the history. A tip is subject to `tipCooldownMessages` and becomes detailed only after `tipEscalationFailures` unresolved matches; it never changes the code-only direct-tool list or schema. `edit_run_code` does not emit a runtime context because its real call and result already carry the relevant fact.
85
91
 
@@ -52,7 +52,7 @@ DSH 的公共 `CodeRuntime`/tool output 只接受 JSON-safe structured value。
52
52
  - renderer 只是模型展示,不参与 hydrate;需要继续计算时应保留 live REPL binding;
53
53
  - journal 保存 canonical graph envelope,不保存 renderer 反解析结果,也不把 decoded rich value 交给外层 `JSON.stringify`。
54
54
 
55
- unsupported value 或预算超限继续使用 `PTC-O001`,但帮助文本只要求返回受支持的 PTC value 或缩小结果;`undefined` 不再是错误,也不要求模型用 `null` 替换。
55
+ unsupported value 或预算超限使用 `PTC-O001`,帮助文本只要求返回受支持的 PTC value 或缩小结果;`undefined` 属于受支持值,不要求模型用 `null` 替换。
56
56
 
57
57
  cell program binding 的参数和结果经过同一 graph IPC,因此 owner-provided binding 可以接收和返回
58
58
  完整 `PTCValueV1` 支持域,包括 `undefined`、special number、BigInt、hole、shared identity 与 cycle。
package/index.js CHANGED
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import Schema from '@deepseek-ai/schemastery'
10
+ import { randomUUID } from 'node:crypto'
10
11
  import { createDirectSurfaceOwner } from './internal/direct-surface-owner.js'
11
12
  import { createCordisToolsOwner } from './internal/cordis-tools-owner.js'
12
13
  import { createEditTransportOwner, EDIT_RUN_CODE } from './internal/edit-transport-owner.js'
@@ -16,7 +17,9 @@ import {
16
17
  CONFIG_FIELDS,
17
18
  SETTINGS_NAMESPACE,
18
19
  } from './internal/config-spec.js'
19
- import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings'
20
+ import { installSettingsSectionCompat } from './internal/settings-compat.js'
21
+ import { createReplMemoryProjection } from './internal/repl-memory-projection.js'
22
+ import * as dshSettings from '@deepseek-ai/dsh-settings'
20
23
 
21
24
  const INSTALL_CLEANUP = Symbol('ptc-plus install cleanup')
22
25
 
@@ -80,6 +83,8 @@ Native tool availability, executable names, shells, and path syntax depend on th
80
83
 
81
84
  /** Register the session-bound REPL runtime. */
82
85
  function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId) {
86
+ const presentationGeneration = randomUUID()
87
+ const replMemoryProjection = createReplMemoryProjection(presentationGeneration)
83
88
  let activeConfig = resolvedConfig
84
89
  let cordisTools
85
90
  let runtimeBridge
@@ -150,6 +155,25 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
150
155
  }
151
156
  const cancelCordisActivation = () => pendingCordisActivation?.cancel()
152
157
  try {
158
+ if (typeof ctx.inject === 'function') {
159
+ const projectionInjection = ctx.inject(['sessionProjections'], (scope) => {
160
+ if (disposed) return
161
+ try {
162
+ const unregister = scope.sessionProjections?.register?.(replMemoryProjection)
163
+ if (typeof unregister !== 'function') {
164
+ throw new Error('ptc-plus: sessionProjections.register did not return a disposer')
165
+ }
166
+ disposers.push(unregister)
167
+ } catch (error) {
168
+ ctx.logger?.warn?.('ptc-plus: REPL memory projection unavailable', error)
169
+ }
170
+ })
171
+ if (typeof projectionInjection === 'function') {
172
+ disposers.push(projectionInjection)
173
+ } else if (typeof projectionInjection?.dispose === 'function') {
174
+ disposers.push(() => projectionInjection.dispose())
175
+ }
176
+ }
153
177
  cordisTools = activeConfig.cordisToolsEnabled
154
178
  ? createCordisToolsOwner(ctx)
155
179
  : undefined
@@ -164,12 +188,14 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
164
188
  ctx,
165
189
  sessionConfig: activeConfig,
166
190
  maxNestedRunCodeDepth: activeConfig.maxNestedRunCodeDepth,
191
+ presentationGeneration,
167
192
  sessionId,
168
193
  toolSchemasForAgent,
169
194
  })
170
195
  editTransport = createEditTransportOwner(ctx, {
171
196
  durableReplay: activeConfig.durableReplay,
172
197
  executeTentative: runtimeBridge.executeTentative,
198
+ presentationGeneration,
173
199
  sessionId,
174
200
  toolSchemasForAgent,
175
201
  })
@@ -202,7 +228,11 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
202
228
  disposers.push(ctx.on('tools/execute', (exec, next) => {
203
229
  const rejected = directSurface.executionRejection(exec)
204
230
  if (rejected !== undefined) return rejected
205
- if (exec.name === RUN_CODE) return runtimeBridge.handleExecute(exec, next)
231
+ if (exec.name === RUN_CODE) {
232
+ const executionArguments = directSurface.executionArguments(exec)
233
+ return Promise.resolve(runtimeBridge.handleExecute(exec, next, executionArguments))
234
+ .then(result => directSurface.argumentDiagnostic(exec, result))
235
+ }
206
236
  return next()
207
237
  }))
208
238
  disposers.push(ctx.on('tools/result', (exec, result) => {
@@ -473,7 +503,7 @@ export function apply(ctx, config = {}) {
473
503
  ? { enabled: false }
474
504
  : Object.fromEntries(CONFIG_FIELDS.map(field => [field.key, previousConfig[field.key]]))
475
505
  try {
476
- await settingsWriter.update(settingsNamespace(SETTINGS_NAMESPACE), patch)
506
+ await settingsWriter.update(SETTINGS_NAMESPACE, patch)
477
507
  } catch (rollbackError) {
478
508
  reportActivationFailure(new Error(
479
509
  `ptc-plus: failed to roll back runtime configuration: ${rollbackError.message}`,
@@ -512,21 +542,22 @@ export function apply(ctx, config = {}) {
512
542
  }
513
543
  }
514
544
  if (typeof ctx.inject === 'function') {
515
- ctx.inject(['settings'], settings => {
516
- settingsWriter = settings.settings
517
- })
518
- installSettingsSection(
545
+ installSettingsSectionCompat({
519
546
  ctx,
520
- settingsNamespace(SETTINGS_NAMESPACE),
521
- Config,
522
- resolvedConfig,
523
- {
547
+ settingsModule: dshSettings,
548
+ namespace: SETTINGS_NAMESPACE,
549
+ schema: Config,
550
+ entry: resolvedConfig,
551
+ hooks: {
524
552
  setSource(source) {
525
553
  configSource = source
526
554
  },
527
555
  onChange: reconcile,
528
556
  },
529
- )
557
+ onProvider(provider) {
558
+ settingsWriter = provider
559
+ },
560
+ })
530
561
  }
531
562
  if (configurationGeneration === 0) return reconcile(settingsWriter === undefined)
532
563
  }