dsh-ptc-plus 0.2.3 → 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.
- package/README.md +28 -11
- package/README.zh.md +28 -11
- package/assets/dsh-ptc-plus-banner-en.webp +0 -0
- package/assets/ptc-plus-settings-en.png +0 -0
- package/assets/ptc-plus-settings-zh.png +0 -0
- package/client.js +139 -46
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +2 -1
- package/docs/adr/0022-stage-npm-releases-with-oidc.md +31 -0
- package/docs/architecture.md +3 -3
- package/docs/client-ui.md +2 -2
- package/docs/installation.md +5 -5
- package/docs/publishing.md +25 -1
- package/docs/runtime-reference.md +1 -1
- package/index.js +13 -11
- package/internal/config-spec.js +46 -1
- package/internal/direct-surface-owner.js +21 -18
- package/internal/settings-compat.js +31 -0
- package/package.json +17 -11
- package/src/client.js +88 -26
- package/assets/ptc-plus-settings.png +0 -0
- /package/assets/{dsh-ptc-plus-banner.webp → dsh-ptc-plus-banner-zh.webp} +0 -0
|
@@ -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.
|
package/docs/architecture.md
CHANGED
|
@@ -15,7 +15,7 @@ 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`、`
|
|
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 的实际结算顺序。
|
|
@@ -50,9 +50,9 @@ system sections、tool schema 或 tool order,也不假设 Windows、WSL、POSI
|
|
|
50
50
|
|
|
51
51
|
## 设置与启用开关
|
|
52
52
|
|
|
53
|
-
Host half 通过 DSH 公共 settings 服务注册 `ptc-plus`
|
|
53
|
+
Host half 通过 DSH 公共 settings 服务注册 `ptc-plus` 命名空间,优先使用 provider-owned `installSection()`,并在旧 Host 上适配根模块的 `installSettingsSection()`;两条路径共享同一 fallback、watch、rollback 与 disposal 语义。字段清单、默认值与校验来自 `internal/config-spec.js`。
|
|
54
54
|
Client half 通过 `settings.plugin.item` 卡片呈现全部配置。`enabled` 是 kill switch:关闭时只保留 settings 注册和设置卡片,
|
|
55
|
-
不注册 runtime、hook、tool surface 或 system prompt section
|
|
55
|
+
不注册 runtime、hook、tool surface 或 system prompt section;设置卡片显示“已启用/已停用”,当前 `ptc` 与旧 Host `code` preset 会话在头部单独显示 `PTC Plus` 指示器。稳定指引不包含 UI 品牌名。
|
|
56
56
|
所有字段都由 settings watch 即时交给各自 owner,且不替换已有 session-bound binding。每个已提交 cell 固定其提交时的配置代际,timer、worker 消息、program binding bridge、结果校验和诊断共同消费该快照;重配置更新随后提交 cell 的默认值。`maxOldGenerationSizeMb` 在活动 worker 存在时因 Node 的创建期限制而拒绝并回滚。settings 服务缺失时 Host 回退到 composition config,原有行为不变。
|
|
57
57
|
|
|
58
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
|
-
|
|
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
|
|
package/docs/installation.md
CHANGED
|
@@ -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
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
package/docs/publishing.md
CHANGED
|
@@ -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`
|
|
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
|
|
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 {
|
|
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(
|
|
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
|
-
|
|
516
|
-
settingsWriter = settings.settings
|
|
517
|
-
})
|
|
518
|
-
installSettingsSection(
|
|
516
|
+
installSettingsSectionCompat({
|
|
519
517
|
ctx,
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
}
|
package/internal/config-spec.js
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
|
|
@@ -11,6 +11,7 @@ const RUN_CODE_TOOL_DESCRIPTION = 'Evaluate the next TypeScript cell in this ses
|
|
|
11
11
|
const RUN_CODE_CODE_DESCRIPTION = 'Code for the next REPL cell, parsed as the body of an async TypeScript function.'
|
|
12
12
|
const RUN_CODE_DESCRIPTION_DESCRIPTION = 'Short active-voice summary of what this cell does, 5-10 words (shown in the UI).'
|
|
13
13
|
const CODE_TRANSPORT_INSTRUCTION = '`run_code` and `edit_run_code` are the only tools callable directly. Call every native tool declared by the SDK from inside a program.'
|
|
14
|
+
const PTC_COLLAPSE_SECTION_NAMES = Object.freeze(['tools:ptc-only', 'tools:code-only'])
|
|
14
15
|
|
|
15
16
|
function adaptRunCodeSchema(tool) {
|
|
16
17
|
const parameters = tool.parameters
|
|
@@ -94,19 +95,21 @@ export function createCordisRecoveryPolicy(initiallyEnabled) {
|
|
|
94
95
|
function presentationState(assembly) {
|
|
95
96
|
const tools = Array.isArray(assembly?.tools) ? assembly.tools : []
|
|
96
97
|
if (!tools.some(tool => tool?.name === RUN_CODE)) {
|
|
97
|
-
return { presentation: 'native', ownerProven: false }
|
|
98
|
+
return { presentation: 'native', ownerProven: false, collapseSectionName: undefined }
|
|
98
99
|
}
|
|
99
|
-
const collapse =
|
|
100
|
+
const collapse = PTC_COLLAPSE_SECTION_NAMES
|
|
101
|
+
.map(name => assembly?.sections?.find(section => section?.name === name))
|
|
102
|
+
.find(section => section !== undefined)
|
|
100
103
|
if (collapse !== undefined) {
|
|
101
104
|
const presentation = typeof collapse.text === 'string' && collapse.text.trim().length > 0
|
|
102
|
-
? '
|
|
105
|
+
? 'ptc'
|
|
103
106
|
: 'both'
|
|
104
|
-
return { presentation, ownerProven: true }
|
|
107
|
+
return { presentation, ownerProven: true, collapseSectionName: collapse.name }
|
|
105
108
|
}
|
|
106
109
|
const presentation = tools.every(tool => tool?.name === RUN_CODE || tool?.name === EDIT_RUN_CODE)
|
|
107
|
-
? '
|
|
110
|
+
? 'ptc'
|
|
108
111
|
: 'both'
|
|
109
|
-
return { presentation, ownerProven: false }
|
|
112
|
+
return { presentation, ownerProven: false, collapseSectionName: undefined }
|
|
110
113
|
}
|
|
111
114
|
|
|
112
115
|
async function* bindCallPolicies(source, policy, calls) {
|
|
@@ -219,10 +222,10 @@ export function createDirectSurfaceOwner({
|
|
|
219
222
|
const initialState = presentationState(initialAssembly)
|
|
220
223
|
let composition = compositions.get(agent)
|
|
221
224
|
if (composition === undefined && id !== undefined
|
|
222
|
-
&& (initialState.ownerProven || initialState.presentation === '
|
|
225
|
+
&& (initialState.ownerProven || initialState.presentation === 'ptc')) {
|
|
223
226
|
composition = captureComposition(agent, id, initialState.presentation)
|
|
224
227
|
}
|
|
225
|
-
if (composition?.presentation === '
|
|
228
|
+
if (composition?.presentation === 'ptc' && !editTransport.isInstalled(agent)) {
|
|
226
229
|
editTransport.ensureInstalled(agent)
|
|
227
230
|
}
|
|
228
231
|
|
|
@@ -235,7 +238,7 @@ export function createDirectSurfaceOwner({
|
|
|
235
238
|
if (composition === undefined && id !== undefined) {
|
|
236
239
|
const state = initialState.ownerProven ? initialState : completedState
|
|
237
240
|
composition = captureComposition(agent, id, state.presentation)
|
|
238
|
-
if (composition.presentation === '
|
|
241
|
+
if (composition.presentation === 'ptc' && !editTransport.isInstalled(agent)) {
|
|
239
242
|
editTransport.ensureInstalled(agent)
|
|
240
243
|
}
|
|
241
244
|
}
|
|
@@ -257,10 +260,10 @@ export function createDirectSurfaceOwner({
|
|
|
257
260
|
throw new Error('ptc-plus: native agent composition assembled with run_code')
|
|
258
261
|
}
|
|
259
262
|
const sessionPtc = presentation !== 'native' && id !== undefined
|
|
260
|
-
const
|
|
263
|
+
const sessionPtcProjection = presentation === 'ptc' && id !== undefined
|
|
261
264
|
const runCode = tools.find(tool => tool?.name === RUN_CODE)
|
|
262
265
|
let directTools = tools
|
|
263
|
-
if (
|
|
266
|
+
if (sessionPtcProjection) {
|
|
264
267
|
editTransport.ensureInstalled(agent)
|
|
265
268
|
directTools = [adaptRunCodeSchema(runCode), editRunCodeSchema()]
|
|
266
269
|
}
|
|
@@ -277,13 +280,13 @@ export function createDirectSurfaceOwner({
|
|
|
277
280
|
: assembly.contexts
|
|
278
281
|
const projectsSections = presentation !== 'native' && Array.isArray(assembly.sections)
|
|
279
282
|
&& assembly.sections.some(section => section?.name === 'tools:sdk'
|
|
280
|
-
|| (
|
|
283
|
+
|| (sessionPtcProjection && section?.name === completedState.collapseSectionName))
|
|
281
284
|
const sections = projectsSections
|
|
282
285
|
? assembly.sections.map(section => {
|
|
283
286
|
if (section?.name === 'tools:sdk') {
|
|
284
287
|
return { ...section, text: capabilitySdk(section.text) }
|
|
285
288
|
}
|
|
286
|
-
if (
|
|
289
|
+
if (sessionPtcProjection && section?.name === completedState.collapseSectionName) {
|
|
287
290
|
return { ...section, text: CODE_TRANSPORT_INSTRUCTION }
|
|
288
291
|
}
|
|
289
292
|
return section
|
|
@@ -291,7 +294,7 @@ export function createDirectSurfaceOwner({
|
|
|
291
294
|
: assembly.sections
|
|
292
295
|
|
|
293
296
|
if (id !== undefined) {
|
|
294
|
-
const nativeSchemas = presentation === '
|
|
297
|
+
const nativeSchemas = presentation === 'ptc'
|
|
295
298
|
? new Map(toolSchemasForAgent(agent)
|
|
296
299
|
.filter(schema => typeof schema?.name === 'string'
|
|
297
300
|
&& schema.name !== RUN_CODE && schema.name !== EDIT_RUN_CODE)
|
|
@@ -302,7 +305,7 @@ export function createDirectSurfaceOwner({
|
|
|
302
305
|
|
|
303
306
|
return {
|
|
304
307
|
...assembly,
|
|
305
|
-
tools:
|
|
308
|
+
tools: sessionPtcProjection
|
|
306
309
|
? directTools
|
|
307
310
|
: directTools.map(tool => tool?.name === RUN_CODE ? adaptRunCodeSchema(tool) : tool),
|
|
308
311
|
sections,
|
|
@@ -313,7 +316,7 @@ export function createDirectSurfaceOwner({
|
|
|
313
316
|
const optionSessionId = options.sessionId === undefined ? undefined : String(options.sessionId)
|
|
314
317
|
const policy = requestPolicy(options?.signal, optionSessionId)
|
|
315
318
|
if (policy === undefined || optionSessionId === undefined) return next()
|
|
316
|
-
const source = policy.presentation === '
|
|
319
|
+
const source = policy.presentation === 'ptc'
|
|
317
320
|
? canonicalizeToolCallStream(next(), {
|
|
318
321
|
tools: options.tools,
|
|
319
322
|
nativeSchemas: currentCanonicalizeToolCalls ? policy.nativeSchemas : new Map(),
|
|
@@ -330,14 +333,14 @@ export function createDirectSurfaceOwner({
|
|
|
330
333
|
const policy = executionPolicy(id, exec.callId)
|
|
331
334
|
?? requestPolicy(exec?.signal, id)
|
|
332
335
|
if (exec.name === EDIT_RUN_CODE) {
|
|
333
|
-
return policy?.presentation === '
|
|
336
|
+
return policy?.presentation === 'ptc'
|
|
334
337
|
? undefined
|
|
335
338
|
: rejection(`tool ${EDIT_RUN_CODE} is not declared for this request`)
|
|
336
339
|
}
|
|
337
340
|
if (exec.name === RUN_CODE || exec.parent !== undefined) {
|
|
338
341
|
return undefined
|
|
339
342
|
}
|
|
340
|
-
if (policy?.presentation !== '
|
|
343
|
+
if (policy?.presentation !== 'ptc') return undefined
|
|
341
344
|
return rejection(`tool ${exec.name} is not a direct PTC tool; use run_code or edit_run_code directly, and call native tools from inside run_code`)
|
|
342
345
|
},
|
|
343
346
|
handleResult(exec) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Install an optional settings section across current and legacy DSH settings APIs. */
|
|
2
|
+
export function installSettingsSectionCompat({
|
|
3
|
+
ctx,
|
|
4
|
+
settingsModule,
|
|
5
|
+
namespace,
|
|
6
|
+
schema,
|
|
7
|
+
entry,
|
|
8
|
+
hooks,
|
|
9
|
+
onProvider,
|
|
10
|
+
}) {
|
|
11
|
+
ctx.inject(['settings'], settingsContext => {
|
|
12
|
+
const provider = settingsContext.settings
|
|
13
|
+
onProvider(provider)
|
|
14
|
+
if (typeof provider?.installSection === 'function') {
|
|
15
|
+
provider.installSection(ctx, namespace, schema, entry, hooks)
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
if (typeof settingsModule?.installSettingsSection !== 'function') {
|
|
19
|
+
throw new Error('ptc-plus: incompatible settings service; expected installSection or installSettingsSection')
|
|
20
|
+
}
|
|
21
|
+
const mountedOwner = new Proxy(ctx, {
|
|
22
|
+
get(target, property) {
|
|
23
|
+
if (property === 'inject') {
|
|
24
|
+
return (_services, attach) => attach(settingsContext)
|
|
25
|
+
}
|
|
26
|
+
return Reflect.get(target, property, target)
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
settingsModule.installSettingsSection(mountedOwner, namespace, schema, entry, hooks)
|
|
30
|
+
})
|
|
31
|
+
}
|