dsh-ptc-plus 0.2.2 → 0.2.4

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.
@@ -6,7 +6,8 @@ not achieve the intended result. PTC Plus therefore registers `edit_run_code` as
6
6
 
7
7
  ```ts
8
8
  edit_run_code({
9
- edits: Array<{ old_string: string; new_string: string }>
9
+ edits: Array<{ old_string: string; new_string: string }>,
10
+ expected_target_call_seq?: number
10
11
  })
11
12
 
12
13
  edit_run_code({
@@ -15,7 +16,8 @@ edit_run_code({
15
16
  flags: string
16
17
  replacement: string
17
18
  expected_matches: number
18
- }>
19
+ }>,
20
+ expected_target_call_seq?: number
19
21
  })
20
22
  ```
21
23
 
@@ -65,7 +67,11 @@ session-log projection captures the eligible target at the edit call event and a
65
67
  source only when its target sequence matches that snapshot and its journal is valid and non-noop,
66
68
  so live and recovered eligibility derive from the same persisted relation.
67
69
 
68
- The caller sends exactly one of `edits` or `regex_edits`. Exact edits contain at most 16 items; each
70
+ The caller sends exactly one of `edits` or `regex_edits`, plus an optional non-negative
71
+ `expected_target_call_seq` precondition. The precondition must equal the call sequence of the target
72
+ captured for the persisted edit event; a mismatch returns an unedited result before derived
73
+ dispatch. Ordinary model-authored edits omit it and retain latest-target selection. Exact edits
74
+ contain at most 16 items; each
69
75
  non-empty `old_string` must differ from `new_string` and occur exactly once in the target. Regular
70
76
  expression edits also contain at most 16 items, use unique `gimsu` flags, reject zero-length matches,
71
77
  and require an exact `expected_matches` count. All ranges resolve against the original target and
@@ -98,6 +104,22 @@ operation and outcome; changing an aggregate runtime-context snapshot merely to
98
104
  would repeat unrelated policy context. Rewrite feedback and unrelated recovery tips retain their
99
105
  own independently justified lifecycles.
100
106
 
107
+ A live `PTC-C001` at the exact source EOF may carry a validated one-call repair. The analyzer tries
108
+ only the three single-token suffixes `}`, `)`, and `]` against the same binding catalog, reserved
109
+ bindings, redeclaration mode, rewrite policy, and import state as the rejected cell. Exactly one
110
+ candidate must complete preparation without a binding collision, and the existing exact editor
111
+ must be able to express it from a bounded unique source suffix. The rejected call must also have a
112
+ persistent event sequence. The diagnostic then renders those already-validated
113
+ `edit_run_code({ edits: [...], expected_target_call_seq })` arguments on one help line. At dispatch,
114
+ the precondition binds the proof to that rejected cell even if another matching source has become
115
+ editable. This evidence takes precedence over the generic source-length recommendation, but remains
116
+ conditional on
117
+ `edit_run_code` being declared for that request. Zero or multiple candidates, non-EOF errors,
118
+ editor budget failures, missing target identity, and candidates rejected later in preparation
119
+ retain the generic guidance.
120
+ The plugin never applies this repair itself: syntax/preflight acceptance does not prove model
121
+ intent, and explicit edit dispatch preserves DSH authority and truthful history.
122
+
101
123
  ## Alternatives considered
102
124
 
103
125
  **Rewrite `edit_run_code` to `run_code` before persistence.** This reuses DSH's default `run_code`-only
@@ -16,7 +16,7 @@ PTC Plus registers one DSH settings namespace `ptc-plus` with the same field lis
16
16
 
17
17
  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
18
 
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.
19
+ Every settings-card string — field labels and hints, header description, enabled state (“已启用/已停用”, or “Enabled/Disabled” in English), expand/collapse name, sync and result messages, and the session indicator tooltip — 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`); card chrome copy is owned by the Client half. The stable REPL guidance remains plugin-owned protocol text without UI branding. The tool names remain `run_code`/`edit_run_code`; we do not rename DSH-owned transport names.
20
20
 
21
21
  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
22
 
@@ -26,6 +26,7 @@ Runtime activation is transactional. If a live enable cannot complete, every own
26
26
  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
27
  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
28
  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.
29
+ 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.
29
30
 
30
31
  ## Consequences
31
32
 
@@ -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 those credentials no longer exist. 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 now requires a successful immutable CI identity, an explicit workflow dispatch, a narrowly scoped OIDC exchange, and a separate npm 2FA promotion. Once the proven migration removes legacy package-capable tokens, 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,12 +15,12 @@ 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。
18
+ DSH 为一个 agent composition 固定选择 `native`、`ptc` 或 `both`。首次 assembly 的非空 `tools:ptc-only` section text 证明 `ptc`,同名空 section 证明 `both`;旧 Host 的 `tools:code-only` 仅在当前 section 缺失时作为边界别名,两者同时存在时当前 section 优先。只有缺少 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
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` 或
23
- `regex_edits` 之一。`internal/rejected-cell-editor.js` 统一拥有 schema、匹配、replacement 语义、预算和原子源码组装;
23
+ `regex_edits` 之一,并可携带 `expected_target_call_seq` 作为目标前置条件;它与 call event 已捕获目标不一致时,在任何派生 dispatch 前返回未编辑结果。`internal/rejected-cell-editor.js` 统一拥有 schema、匹配、replacement 语义、预算和原子源码组装;
24
24
  所有位置都针对原始目标源码解析,全部范围不得重叠,预算通过后才以单次线性扫描物化结果。
25
25
 
26
26
  注册工具通过 DSH 公共 `tools.execute()` 将完整修改后源码派生执行为 `run_code`。外层历史仍是模型真实发出的
@@ -40,6 +40,8 @@ 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。
44
+
43
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 不增加次数,正文不参与身份判断。
44
46
  相同提示受 `tipCooldownMessages` 间隔约束,连续未解决时才升级为详细版本,成功 cell 会重置未解决计数;提示不会改变
45
47
  system sections、tool schema 或 tool order,也不假设 Windows、WSL、POSIX、shell 或 package runner。
@@ -48,9 +50,9 @@ system sections、tool schema 或 tool order,也不假设 Windows、WSL、POSI
48
50
 
49
51
  ## 设置与启用开关
50
52
 
51
- Host half 通过 DSH 公共 settings 服务注册 `ptc-plus` 命名空间,字段清单、默认值与校验来自 `internal/config-spec.js`。
53
+ Host half 通过 DSH 公共 settings 服务注册 `ptc-plus` 命名空间,优先使用 provider-owned `installSection()`,并在旧 Host 上适配根模块的 `installSettingsSection()`;两条路径共享同一 fallback、watch、rollback 与 disposal 语义。字段清单、默认值与校验来自 `internal/config-spec.js`。
52
54
  Client half 通过 `settings.plugin.item` 卡片呈现全部配置。`enabled` 是 kill switch:关闭时只保留 settings 注册和设置卡片,
53
- 不注册 runtime、hook、tool surface 或 system prompt section;设置卡片显示“已启用/已停用”,`code` preset 会话在头部单独显示 `PTC Plus` 指示器。稳定指引不包含 UI 品牌名。
55
+ 不注册 runtime、hook、tool surface 或 system prompt section;设置卡片显示“已启用/已停用”,当前 `ptc` 与旧 Host `code` preset 会话在头部单独显示 `PTC Plus` 指示器。稳定指引不包含 UI 品牌名。
54
56
  所有字段都由 settings watch 即时交给各自 owner,且不替换已有 session-bound binding。每个已提交 cell 固定其提交时的配置代际,timer、worker 消息、program binding bridge、结果校验和诊断共同消费该快照;重配置更新随后提交 cell 的默认值。`maxOldGenerationSizeMb` 在活动 worker 存在时因 Node 的创建期限制而拒绝并回滚。settings 服务缺失时 Host 回退到 composition config,原有行为不变。
55
57
 
56
58
  `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。
package/docs/client-ui.md CHANGED
@@ -27,7 +27,7 @@ Host half 通过 DSH 公共 `settings` 服务注册命名空间 `ptc-plus`。字
27
27
 
28
28
  `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
29
 
30
- 设置卡片折叠头部显示 `PTC Plus`、`PTC 模式的会话级 TypeScript REPL。`、启用状态和可访问的展开/收起名称;稳定 REPL 指引不承载 UI 品牌名。启用且会话选择 `code` preset 时,`conversation.session.header.actions` 额外显示 `PTC Plus` 指示器。关闭时不注入任何 PTC 指引或工具 surface。
30
+ 设置卡片的全部文案(字段名称与说明、折叠头部副标题、启用状态、展开/收起名称、同步与错误提示)都注册到 DSH client locale 的 `settings.ptcPlus` 命名空间,随当前界面语言在中文与 English 之间切换;字段名称与说明的两种语言文本同样来自 `internal/config-spec.js`(`label`/`labelEn`、`description`/`descriptionEn`,某一字段的说明要么两种语言都有,要么都没有),卡片 chrome 文案由 client half 拥有。稳定 REPL 指引不承载 UI 品牌名。启用且会话选择当前 `ptc` 或旧 Host `code` preset 时,`conversation.session.header.actions` 额外显示 `PTC Plus` 指示器,其 tooltip 也跟随界面语言;当前 session projection 与旧顶层字段冲突时以前者为准。关闭时不注入任何 PTC 指引或工具 surface。
31
31
 
32
32
  live 配置若因宿主能力缺失或 runtime 安装/重配置失败,会先回滚所有已创建或更新的 owner,再把持久设置回写为上一次已应用值;回滚写入失败时 Host 记录 activation diagnostic,避免静默把配置显示成不存在的 runtime。
33
33
 
@@ -39,7 +39,7 @@ live 配置若因宿主能力缺失或 runtime 安装/重配置失败,会先
39
39
  npm run build
40
40
  ```
41
41
 
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 模块。
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、locale、session、slot 与 UI primitive 模块。
43
43
 
44
44
  ## 回退
45
45
 
@@ -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.1
26
+ dsh plugin --profile <profile> add dsh-ptc-plus@0.2.4
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.1.tgz
63
+ dsh plugin --profile <profile> add /absolute/path/to/dsh-ptc-plus-0.2.4.tgz
64
64
  dsh --profile <profile> --dump-config
65
65
  ```
66
66
 
@@ -79,4 +79,4 @@ dsh plugin add github:muyuanjin/dsh-ptc-plus#main
79
79
  dsh --dump-config
80
80
  ```
81
81
 
82
- After an npm release, the package spec may instead be `dsh-ptc-plus@0.2.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.
82
+ After an npm release, the package spec may instead be `dsh-ptc-plus@0.2.4`. 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,10 +8,12 @@ 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; invalid or unavailable 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
 
15
+ When a live parse error occurs exactly at the cell EOF, PTC Plus checks the three single-closing-token corrections `}`, `)`, and `]` with the same preparation context. If exactly one correction succeeds, the existing editor can express it from a bounded unique suffix, and the rejected call has a persistent event identity, `PTC-C001` includes a directly callable `edit_run_code({ edits: [...], expected_target_call_seq })` invocation. The guard must match the target captured when the edit call is persisted, so a later editable cell suppresses execution even when the same literal replacement would match it. The plugin does not apply or execute the correction automatically; ambiguous, broader, and unbound repairs keep the ordinary length-adaptive guidance.
16
+
15
17
  Only values explicitly returned or printed appear in a cell result:
16
18
 
17
19
  ```ts
@@ -95,7 +97,7 @@ Rewrites are recorded as `meta.dshPtcPlusRewrites` on the tool result (parallel
95
97
 
96
98
  | Code | Meaning | State effect |
97
99
  | --- | --- | --- |
98
- | `PTC-C001` | The cell cannot be parsed | Not executed; REPL unchanged |
100
+ | `PTC-C001` | The cell cannot be parsed | Not executed; REPL unchanged; a uniquely validated and target-bound single-token EOF closure includes a directly callable declared `edit_run_code` repair; otherwise retry a short corrected cell with `run_code`, use edit for a localized long-cell correction, and resend with `run_code` for broad, ambiguous, or unbound repairs |
99
101
  | `PTC-C002` | Preflight rejected a kernel-control import | Not executed; REPL unchanged |
100
102
  | `PTC-N001` | Top-level binding conflict | Not executed; REPL unchanged |
101
103
  | `PTC-O001` | Unsupported or over-budget output | Cell executed; earlier mutations may exist |
package/index.js CHANGED
@@ -16,7 +16,8 @@ import {
16
16
  CONFIG_FIELDS,
17
17
  SETTINGS_NAMESPACE,
18
18
  } from './internal/config-spec.js'
19
- import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings'
19
+ import { installSettingsSectionCompat } from './internal/settings-compat.js'
20
+ import * as dshSettings from '@deepseek-ai/dsh-settings'
20
21
 
21
22
  const INSTALL_CLEANUP = Symbol('ptc-plus install cleanup')
22
23
 
@@ -473,7 +474,7 @@ export function apply(ctx, config = {}) {
473
474
  ? { enabled: false }
474
475
  : Object.fromEntries(CONFIG_FIELDS.map(field => [field.key, previousConfig[field.key]]))
475
476
  try {
476
- await settingsWriter.update(settingsNamespace(SETTINGS_NAMESPACE), patch)
477
+ await settingsWriter.update(SETTINGS_NAMESPACE, patch)
477
478
  } catch (rollbackError) {
478
479
  reportActivationFailure(new Error(
479
480
  `ptc-plus: failed to roll back runtime configuration: ${rollbackError.message}`,
@@ -512,21 +513,22 @@ export function apply(ctx, config = {}) {
512
513
  }
513
514
  }
514
515
  if (typeof ctx.inject === 'function') {
515
- ctx.inject(['settings'], settings => {
516
- settingsWriter = settings.settings
517
- })
518
- installSettingsSection(
516
+ installSettingsSectionCompat({
519
517
  ctx,
520
- settingsNamespace(SETTINGS_NAMESPACE),
521
- Config,
522
- resolvedConfig,
523
- {
518
+ settingsModule: dshSettings,
519
+ namespace: SETTINGS_NAMESPACE,
520
+ schema: Config,
521
+ entry: resolvedConfig,
522
+ hooks: {
524
523
  setSource(source) {
525
524
  configSource = source
526
525
  },
527
526
  onChange: reconcile,
528
527
  },
529
- )
528
+ onProvider(provider) {
529
+ settingsWriter = provider
530
+ },
531
+ })
530
532
  }
531
533
  if (configurationGeneration === 0) return reconcile(settingsWriter === undefined)
532
534
  }
@@ -4,21 +4,30 @@ export const MAX_TIMER_DELAY_MS = 2_147_483_647
4
4
  /** Stable settings namespace used by both Host and browser halves. */
5
5
  export const SETTINGS_NAMESPACE = 'ptc-plus'
6
6
 
7
- /** Ordered field definitions for the plugin configuration card. */
7
+ /**
8
+ * Ordered field definitions for the plugin configuration card. `label` and
9
+ * `description` are the canonical Host schema copy; `labelEn` and
10
+ * `descriptionEn` carry the English settings-card copy. A field's description
11
+ * exists in both locales or in neither.
12
+ */
8
13
  export const CONFIG_FIELDS = Object.freeze([
9
14
  {
10
15
  key: 'enabled',
11
16
  type: 'boolean',
12
17
  default: true,
13
18
  label: '启用 PTC Plus',
19
+ labelEn: 'Enable PTC Plus',
14
20
  description: '',
21
+ descriptionEn: '',
15
22
  },
16
23
  {
17
24
  key: 'cordisToolsEnabled',
18
25
  type: 'boolean',
19
26
  default: false,
20
27
  label: '在 PTC 模式中启用 Cordis 工具',
28
+ labelEn: 'Enable Cordis tools in PTC mode',
21
29
  description: '即时为 PTC agent 加入或移除官方 Cordis 工具。',
30
+ descriptionEn: 'Adds or removes the official Cordis tools for the PTC agent immediately.',
22
31
  },
23
32
  {
24
33
  key: 'computeMs',
@@ -27,7 +36,9 @@ export const CONFIG_FIELDS = Object.freeze([
27
36
  min: 1,
28
37
  max: Number.MAX_SAFE_INTEGER,
29
38
  label: '单 cell 最大 CPU 时间 (ms)',
39
+ labelEn: 'Max CPU time per cell (ms)',
30
40
  description: '同步计算超过该预算的 cell 会被中断。',
41
+ descriptionEn: 'Cells whose synchronous computation exceeds this budget are interrupted.',
31
42
  },
32
43
  {
33
44
  key: 'maxWallMs',
@@ -36,7 +47,9 @@ export const CONFIG_FIELDS = Object.freeze([
36
47
  min: 1,
37
48
  max: MAX_TIMER_DELAY_MS,
38
49
  label: '单 cell 最大墙钟时间 (ms)',
50
+ labelEn: 'Max wall-clock time per cell (ms)',
39
51
  description: '完整 cell 执行(含异步等待)的最长耗时。',
52
+ descriptionEn: 'The longest a full cell execution may take, including async waits.',
40
53
  },
41
54
  {
42
55
  key: 'maxOutputBytes',
@@ -45,7 +58,9 @@ export const CONFIG_FIELDS = Object.freeze([
45
58
  min: 1,
46
59
  max: Number.MAX_SAFE_INTEGER,
47
60
  label: '最大输出字节',
61
+ labelEn: 'Max output bytes',
48
62
  description: '限制单个 cell 的输出和结果数据总大小。',
63
+ descriptionEn: 'Caps the combined size of one cell output and result data.',
49
64
  },
50
65
  {
51
66
  key: 'maxOldGenerationSizeMb',
@@ -54,7 +69,9 @@ export const CONFIG_FIELDS = Object.freeze([
54
69
  min: 1,
55
70
  max: Number.MAX_SAFE_INTEGER,
56
71
  label: 'worker 旧生代内存上限 (MiB)',
72
+ labelEn: 'Worker old-generation memory cap (MiB)',
57
73
  description: '每个 worker 的 V8 旧生代上限;活动 worker 存在时修改会被拒绝。',
74
+ descriptionEn: 'Per-worker V8 old-generation limit; changes are rejected while an active worker exists.',
58
75
  },
59
76
  {
60
77
  key: 'maxValueNodes',
@@ -63,7 +80,9 @@ export const CONFIG_FIELDS = Object.freeze([
63
80
  min: 1,
64
81
  max: Number.MAX_SAFE_INTEGER,
65
82
  label: 'Value Graph 最大节点数',
83
+ labelEn: 'Max Value Graph nodes',
66
84
  description: '限制单次返回值的节点数。',
85
+ descriptionEn: 'Caps the node count of a single returned value.',
67
86
  },
68
87
  {
69
88
  key: 'maxValueEdges',
@@ -72,7 +91,9 @@ export const CONFIG_FIELDS = Object.freeze([
72
91
  min: 1,
73
92
  max: Number.MAX_SAFE_INTEGER,
74
93
  label: 'Value Graph 最大边数',
94
+ labelEn: 'Max Value Graph edges',
75
95
  description: '限制单次返回值的引用关系数。',
96
+ descriptionEn: 'Caps the reference-edge count of a single returned value.',
76
97
  },
77
98
  {
78
99
  key: 'maxValueArrayLength',
@@ -81,7 +102,9 @@ export const CONFIG_FIELDS = Object.freeze([
81
102
  min: 1,
82
103
  max: Number.MAX_SAFE_INTEGER,
83
104
  label: '数组最大声明长度',
105
+ labelEn: 'Max declared array length',
84
106
  description: '限制返回数组的最大长度。',
107
+ descriptionEn: 'Caps the maximum length of a returned array.',
85
108
  },
86
109
  {
87
110
  key: 'maxValueBigIntDigits',
@@ -90,7 +113,9 @@ export const CONFIG_FIELDS = Object.freeze([
90
113
  min: 1,
91
114
  max: Number.MAX_SAFE_INTEGER,
92
115
  label: 'BigInt 最大十进制位数',
116
+ labelEn: 'Max BigInt decimal digits',
93
117
  description: '限制返回值中 BigInt 的十进制位数。',
118
+ descriptionEn: 'Caps the decimal digits of BigInt values in a returned value.',
94
119
  },
95
120
  {
96
121
  key: 'maxNestedRunCodeDepth',
@@ -99,56 +124,72 @@ export const CONFIG_FIELDS = Object.freeze([
99
124
  min: 1,
100
125
  max: Number.MAX_SAFE_INTEGER,
101
126
  label: 'code.run 最大递归深度',
127
+ labelEn: 'Max code.run recursion depth',
102
128
  description: '限制 code.run 的嵌套层数。',
129
+ descriptionEn: 'Caps how deeply code.run may nest.',
103
130
  },
104
131
  {
105
132
  key: 'canonicalizeToolCalls',
106
133
  type: 'boolean',
107
134
  default: true,
108
135
  label: '规范顶层 native 误调',
136
+ labelEn: 'Canonicalize top-level native mis-calls',
109
137
  description: '修正可以明确识别的顶层 native 误调。',
138
+ descriptionEn: 'Repairs top-level native mis-calls that can be identified unambiguously.',
110
139
  },
111
140
  {
112
141
  key: 'looseTopLevelRedeclarations',
113
142
  type: 'boolean',
114
143
  default: true,
115
144
  label: '宽松顶层重声明',
145
+ labelEn: 'Loose top-level redeclarations',
116
146
  description: '允许顶层 const/let 重声明已有变量。',
147
+ descriptionEn: 'Allows top-level const/let to redeclare existing variables.',
117
148
  },
118
149
  {
119
150
  key: 'durableReplay',
120
151
  type: 'boolean',
121
152
  default: true,
122
153
  label: '持久重放',
154
+ labelEn: 'Durable replay',
123
155
  description: 'worker 重启后恢复可以重建的 REPL 状态。',
156
+ descriptionEn: 'Rebuilds recoverable REPL state after a worker restart.',
124
157
  },
125
158
  {
126
159
  key: 'autoRewriteImports',
127
160
  type: 'boolean',
128
161
  default: true,
129
162
  label: '自动改写 import',
163
+ labelEn: 'Auto-rewrite import',
130
164
  description: '允许在 run_code 中使用静态 import。',
165
+ descriptionEn: 'Allows static import declarations inside run_code.',
131
166
  },
132
167
  {
133
168
  key: 'autoStripExports',
134
169
  type: 'boolean',
135
170
  default: true,
136
171
  label: '自动剥离 export',
172
+ labelEn: 'Auto-strip export',
137
173
  description: '允许在 run_code 中使用顶层 export。',
174
+ descriptionEn: 'Allows top-level export modifiers inside run_code.',
138
175
  },
139
176
  {
140
177
  key: 'autoSplitRedeclarations',
141
178
  type: 'boolean',
142
179
  default: true,
143
180
  label: '自动拆分混合重声明',
181
+ labelEn: 'Auto-split mixed redeclarations',
144
182
  description: '允许顶层解构声明同时包含新旧变量。',
183
+ descriptionEn: 'Allows top-level destructuring declarations that mix new and existing variables.',
145
184
  },
146
185
  {
147
186
  key: 'tipsEnabled',
148
187
  type: 'boolean',
149
188
  default: true,
150
189
  label: '启用恢复提示',
190
+ labelEn: 'Enable recovery tips',
151
191
  description: '在符合条件的失败后显示恢复提示。',
192
+ descriptionEn: 'Shows recovery tips after qualifying failures.',
152
193
  },
153
194
  {
154
195
  key: 'tipCooldownMessages',
@@ -157,7 +198,9 @@ export const CONFIG_FIELDS = Object.freeze([
157
198
  min: 1,
158
199
  max: Number.MAX_SAFE_INTEGER,
159
200
  label: '恢复提示冷却步数',
201
+ labelEn: 'Recovery tip cooldown (messages)',
160
202
  description: '同类恢复提示之间的最小间隔。',
203
+ descriptionEn: 'The minimum gap between recovery tips of the same kind.',
161
204
  },
162
205
  {
163
206
  key: 'tipEscalationFailures',
@@ -166,7 +209,9 @@ export const CONFIG_FIELDS = Object.freeze([
166
209
  min: 1,
167
210
  max: Number.MAX_SAFE_INTEGER,
168
211
  label: '恢复提示升级失败次数',
212
+ labelEn: 'Recovery tip escalation failures',
169
213
  description: '连续失败达到此次数后显示更详细的恢复提示。',
214
+ descriptionEn: 'After this many consecutive failures, a more detailed recovery tip is shown.',
170
215
  },
171
216
  ])
172
217