dsh-ptc-plus 0.2.1 → 0.2.2

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 CHANGED
@@ -1,6 +1,9 @@
1
1
  # dsh-ptc-plus
2
2
 
3
+ English | [简体中文](README.zh.md)
4
+
3
5
  [![Node.js ^22.19.0 || >=24.0.0](https://img.shields.io/badge/Node.js-%5E22.19.0%20%7C%7C%20%3E%3D24.0.0-5fa04e?logo=nodedotjs&logoColor=white)](package.json)
6
+ [![npm version](https://img.shields.io/npm/v/dsh-ptc-plus?logo=npm)](https://www.npmjs.com/package/dsh-ptc-plus)
4
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
8
  [![DeepSeek Harness](https://img.shields.io/badge/DeepSeek%20Harness-PTC%20mode-4b6bfb)](https://github.com/deepseek-ai/deepseek-harness)
6
9
  [![Status: community plugin](https://img.shields.io/badge/Status-community%20plugin-lightgrey)](docs/publishing.md)
@@ -107,10 +110,12 @@ Open **Settings → Plugin configuration** to use the card shown above. The `ena
107
110
 
108
111
  Every setting applies live and keeps existing bindings. A submitted cell uses one configuration for its complete execution; changes made while it runs apply to cells submitted afterward. A failed change rolls back. Node fixes a worker's V8 old-generation limit when the worker starts, so that one setting is rejected while a session worker is active and can be changed after the session is disposed. A failed enable is rolled back and persisted as disabled.
109
112
 
110
- `cordisToolsEnabled` is off by default. Turning it on atomically adds DSH's official Cordis tools, owner guidance, and `cordis-plugin-development` companion Skill to PTC agents; turning it off removes all three. It neither switches presets nor changes the direct `run_code`/`edit_run_code` surface. Cordis runs model-written plugins against the live DSH runtime, so enabling it requires shell-level trust.
113
+ `cordisToolsEnabled` is off by default. Turning it on atomically adds DSH's official Cordis tools, owner guidance, and exactly the `cordis-plugin-development` companion Skill to PTC agents; sibling Skills in the shipped preset are not exposed. Turning it off removes all three. It neither switches presets nor changes the direct `run_code`/`edit_run_code` surface. Cordis runs model-written plugins against the live DSH runtime, so enabling it requires shell-level trust.
111
114
 
112
115
  When a Cordis call rejects after a cell has assigned a large host or client source string, that top-level binding remains live. Retry only the Cordis call from a short later cell and reuse the binding instead of resending the source.
113
116
 
117
+ After cold recovery or re-enabling Cordis, recorded Cordis values remain historical data but do not prove that process-local Plugins, Runs, approvals, or earlier Inspect observations are still live. PTC Plus adds a bounded recovery context until a new successful Cordis Inspect call validates the current process.
118
+
114
119
  See [Client UI](docs/client-ui.md), [ADR 0019](docs/adr/0019-plugin-settings-and-kill-switch.md), and [ADR 0020](docs/adr/0020-optional-cordis-tools-in-ptc-mode.md).
115
120
 
116
121
  ## Scope
@@ -119,14 +124,14 @@ PTC Plus provides the session-bound persistent `run_code` layer. DSH and the ope
119
124
 
120
125
  ## Install
121
126
 
122
- Requires Node.js `^22.19.0 || >=24.0.0` and DSH with TypeScript PTC mode:
127
+ Requires Node.js `^22.19.0 || >=24.0.0` and DSH with TypeScript PTC mode. Install the published package from npm into the profile you use:
123
128
 
124
129
  ```sh
125
- dsh plugin --profile <profile> add github:muyuanjin/dsh-ptc-plus#main
130
+ dsh plugin --profile <profile> add dsh-ptc-plus
126
131
  dsh --profile <profile> --dump-config
127
132
  ```
128
133
 
129
- Other install methods and compatibility details are in the [installation guide](docs/installation.md).
134
+ Restart that DSH profile after installation. Version-pinned npm, GitHub, local-checkout, and tarball installs are covered in the [installation guide](docs/installation.md).
130
135
 
131
136
  `danger-full-access` is the primary supported experience. The worker isolates lifecycle, not malicious code.
132
137
 
package/README.zh.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # dsh-ptc-plus
2
2
 
3
+ [English](README.md) | 简体中文
4
+
3
5
  [![Node.js ^22.19.0 || >=24.0.0](https://img.shields.io/badge/Node.js-%5E22.19.0%20%7C%7C%20%3E%3D24.0.0-5fa04e?logo=nodedotjs&logoColor=white)](package.json)
6
+ [![npm version](https://img.shields.io/npm/v/dsh-ptc-plus?logo=npm)](https://www.npmjs.com/package/dsh-ptc-plus)
4
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
8
  [![DeepSeek Harness](https://img.shields.io/badge/DeepSeek%20Harness-PTC%20mode-4b6bfb)](https://github.com/deepseek-ai/deepseek-harness)
6
9
  [![Status: community plugin](https://img.shields.io/badge/Status-community%20plugin-lightgrey)](docs/publishing.md)
@@ -107,10 +110,12 @@ import { readFile } from 'node:fs/promises'
107
110
 
108
111
  所有设置都会实时应用,并保留已有 binding。已提交的 cell 在完整执行期间使用同一份配置;执行期间发生的更新用于随后提交的 cell。更新失败会回滚。Node 在 worker 创建时固定 V8 old-generation 上限,因此活动 session worker 存在时修改这一项会被拒绝,释放 session 后才能修改。启用失败时,运行时会回滚并把设置保持为停用。
109
112
 
110
- `cordisToolsEnabled` 默认关闭。打开后,DSH 官方 Cordis 工具、owner guidance 与 `cordis-plugin-development` companion Skill 会作为一个整体加入 PTC agent,关闭时也会一起移除;它不切换 preset,也不改变 `run_code`/`edit_run_code` 的直接调用面。Cordis 能在实时 DSH runtime 中运行模型编写的插件,开启它需要接受 shell 级信任。
113
+ `cordisToolsEnabled` 默认关闭。打开后,DSH 官方 Cordis 工具、owner guidance 与精确的 `cordis-plugin-development` companion Skill 会作为一个整体加入 PTC agent;同一 shipped preset 目录中的其他 Skill 不会随之暴露。关闭时这三项也会一起移除;它不切换 preset,也不改变 `run_code`/`edit_run_code` 的直接调用面。Cordis 能在实时 DSH runtime 中运行模型编写的插件,开启它需要接受 shell 级信任。
111
114
 
112
115
  如果 Cordis 调用在 cell 已经赋值大段 host 或 client 源码之后才失败,这些顶层 binding 仍会保留。后续只需在短 cell 中重试 Cordis 调用并复用该 binding,无需再次传输源码。
113
116
 
117
+ cold recovery 或重新启用 Cordis 后,已记录的 Cordis value 仍是历史数据,但不能证明进程内 Plugin、Run、approval 或先前 Inspect observation 仍然存活。PTC Plus 会提供有界恢复 context,直到一次新的成功 Cordis Inspect 调用验证当前进程。
118
+
114
119
  详见 [客户端 UI](docs/client-ui.md)、[ADR 0019](docs/adr/0019-plugin-settings-and-kill-switch.md) 与 [ADR 0020](docs/adr/0020-optional-cordis-tools-in-ptc-mode.md)。
115
120
 
116
121
  ## 范围
@@ -119,14 +124,14 @@ PTC Plus 提供会话绑定的持久 `run_code` 层。原生工具的权限、
119
124
 
120
125
  ## 安装
121
126
 
122
- 要求 Node.js `^22.19.0 || >=24.0.0`,并已安装带 TypeScript PTC 模式的 DSH:
127
+ 要求 Node.js `^22.19.0 || >=24.0.0`,并已安装带 TypeScript PTC 模式的 DSH。把 npm 正式包安装到你实际使用的 profile:
123
128
 
124
129
  ```sh
125
- dsh plugin --profile <profile> add github:muyuanjin/dsh-ptc-plus#main
130
+ dsh plugin --profile <profile> add dsh-ptc-plus
126
131
  dsh --profile <profile> --dump-config
127
132
  ```
128
133
 
129
- 其他安装方法和兼容性说明见[安装指南](docs/installation.md)。
134
+ 安装后重启对应的 DSH profile。固定 npm 版本、GitHub、本地 checkout 和 tarball 安装方式见[安装指南](docs/installation.md)。
130
135
 
131
136
  `danger-full-access` 是首要支持方式。worker 只隔离生命周期,不隔离恶意代码。
132
137
 
@@ -6,12 +6,14 @@ The persistent REPL contract must stay short enough for every request, while som
6
6
 
7
7
  ## Decision
8
8
 
9
- PTC Plus keeps the stable REPL guidance limited to invariants, discovery entry points, and environment-neutral boundaries. `internal/session-log-view.js` scans the event sequence once, pairs settled calls within each open turn, validates execution journals and rewrite records once, and returns immutable facts for edit targeting, successful-run reset, context distance, and canonical DSH system-prompt snapshots. Each snapshot preserves its named sections. `internal/runtime-contexts.js` renders rewrite feedback only when the rewritten cell failed or lacks a valid journal, plus at most one context from the reserved `tools:ptc-plus-tip/<trigger>/<ordinal>` name family. Successful transparent rewrites remain result metadata and do not create aggregate snapshot churn. It never emits edit provenance or target-lifecycle context: those facts belong to the registered tool call/result and its private derived-execution metadata. Tips never change the tool list, tool schemas, or system sections.
9
+ PTC Plus keeps the stable REPL guidance limited to invariants, discovery entry points, and environment-neutral boundaries. `internal/session-log-view.js` scans the event sequence once, pairs settled calls within each open turn, validates execution journals and rewrite records once, and returns immutable facts for edit targeting, successful-run reset, context distance, normalized Cordis call/inspection counts, and canonical DSH system-prompt snapshots. Each snapshot preserves its named sections. `internal/runtime-contexts.js` renders rewrite feedback only when the rewritten cell failed or lacks a valid journal, the fixed `tools:ptc-plus-cordis-recovery` context when a new agent or Cordis enable generation contains historical Cordis transcript without a newer successful live Inspect, plus at most one context from the reserved `tools:ptc-plus-tip/<trigger>/<ordinal>` name family. Successful transparent rewrites remain result metadata and do not create aggregate snapshot churn. It never emits edit provenance or target-lifecycle context: those facts belong to the registered tool call/result and its private derived-execution metadata. These contexts never change the tool list, tool schemas, or system sections.
10
10
 
11
11
  The default triggers are a repeated binding failure and a failure whose diagnostic or structured cause identifies an executable, shell, or path problem. The first tip is concise. A matching trigger may produce another tip only after `tipCooldownMessages` model-context steps; unresolved matching tips reach the detailed form after `tipEscalationFailures` occurrences. A successful cell resets the unresolved escalation count. Every emitted tip name carries its stable trigger identity and next per-trigger ordinal. Reconstruction accepts only canonical snapshots from DSH's system-prompt owner, treats a named section as one effective-state transition even when another owner's aggregate update repeats it, and ignores legacy or malformed plugin prose. Visible wording does not determine cooldown or escalation identity.
12
12
 
13
13
  Platform wording names execution-world differences without assuming Windows, WSL, POSIX, a shell, or package-runner availability. `edit_run_code` remains independently available as a fixed real tool and does not depend on a recovery-tip trigger.
14
14
 
15
+ The Cordis recovery context is not a failure tip and is not controlled by fatigue settings. It narrows the meaning of recorded values after a lifecycle boundary: they remain historical program data but cannot prove current process-local state. It clears only after a newly settled successful `cordis_inspect*` transcript, does not infer whether the original process survived, and never redispatches a historical effect.
16
+
15
17
  ## Alternatives considered
16
18
 
17
19
  - **Keep all recovery instructions in the stable system prompt.** This makes every request pay for rare failures and makes platform-specific text look universally applicable.
@@ -20,6 +22,7 @@ Platform wording names execution-world differences without assuming Windows, WSL
20
22
  - **Infer tip identity from rendered prose or one fixed section name.** Wording changes would reset history, unrelated plugin text could create false matches, and an unchanged aggregate context would not persist repeated occurrences.
21
23
  - **Change the available tools when a tip is needed.** This would break the fixed code-only direct-tool prefix and make failure state observable through schema churn.
22
24
  - **Let each runtime-context renderer scan the event log.** Independent scans duplicate parsing and can disagree about call/result pairing, turn boundaries, repair consumption, or successful-run resets. A shared projection keeps event interpretation separate from presentation policy.
25
+ - **Mark all Cordis calls volatile.** This would discard reconstructable REPL bindings and special values merely because their external referents need revalidation. Recorded-value replay and live-state proof are separate contracts.
23
26
 
24
27
  ## Consequences
25
28
 
@@ -14,7 +14,7 @@ Tool registration must finish before the prompt assembly returned to the agent;
14
14
 
15
15
  ## Decision
16
16
 
17
- Add `cordisToolsEnabled: boolean`, default `false`, as a live PTC Plus setting. When enabled, PTC Plus mounts the official `@deepseek-ai/dsh-tool-cordis` plugin and a maintained `@deepseek-ai/dsh-skill-filesystem` provider in each agent scope that exposes `run_code`; disabling it disposes both fibers immediately. The provider resolves the shipped `cordis` preset through the public `agentPresets` service and publishes only its sibling `skills` directory under a PTC Plus-specific provider name. It neither switches the current preset nor includes default roots. If an agent is created before `run_code` becomes visible, the owner retains it as pending and retries on the DSH tools-change signal.
17
+ Add `cordisToolsEnabled: boolean`, default `false`, as a live PTC Plus setting. When enabled, PTC Plus mounts the official `@deepseek-ai/dsh-tool-cordis` plugin and a maintained `@deepseek-ai/dsh-skill-filesystem` provider in each agent scope that exposes `run_code`; disabling it disposes both fibers immediately. The provider resolves the shipped `cordis` preset through the public `agentPresets` service and reads its sibling `skills` root without including default roots. At the public `skills.registerProvider` seam, PTC Plus transparently filters discovery and loading to exactly `cordis-plugin-development`; the maintained filesystem provider still owns parsing, resource bases, watch, invalidation, incomplete observations, and body loading. This prevents unrelated or obsolete sibling Skills from silently enlarging the PTC surface without copying the companion content or switching the current preset. If an agent is created before `run_code` becomes visible, the owner retains it as pending and retries on the DSH tools-change signal.
18
18
 
19
19
  The Skill provider and official Cordis child fiber form one reversible mount. Both must settle, and the scoped `skills` service must prove that `cordis-plugin-development` resolves from the added provider as model-invocable and loadable, before the first prompt assembly is returned. A prepended public `system-prompt/assemble` waterfall listener is the final race barrier: if collection began before the mount settled, it waits for readiness, discards that stale assembly, and invokes public assembly again so DSH recollects the official providers. It does not synthesize or mutate Cordis schemas or guidance. The Cordis fiber's resolved `inject` declaration is the authoritative required-service list, and its live registrations are the authoritative tool names, schemas, and guidance. A missing or broken preset, Skill service/tool, declared service, publication, or child fiber is a configuration error and rolls back every contribution.
20
20
 
@@ -22,6 +22,8 @@ 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.
26
+
25
27
  ## Alternatives Considered
26
28
 
27
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.
@@ -30,7 +32,8 @@ Cordis tools remain native program bindings under `tools.*`. The PTC code-only d
30
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.
31
33
  5. Mount one global Cordis tool fiber and hide it from native agents. Rejected: its prompt hook and `@pluginId` context listener would remain global even if tool schemas were restricted, so unrelated agents would inherit behavior they did not enable.
32
34
  6. Copy `SKILL.md` into PTC Plus or add the Cordis preset's directory to global Skill roots. Rejected: copied instructions drift, while a global root leaks the optional workflow into agents that did not enable it.
35
+ 7. Publish the complete preset-local Skill directory. Rejected: directory membership is not the PTC feature contract; unrelated sibling additions would silently change the model surface and may target a different authoring workflow or tool generation.
33
36
 
34
37
  ## Consequences
35
38
 
36
- PTC Plus declares the maintained DSH Cordis tool, Skill filesystem, settings, and tool-runtime packages as required peers through the current release channel and mirrors them in development dependencies. They resolve from DSH's installation-maintained profile fallback; installing PTC Plus must not add a profile-local DSH core package instance. When enabled, the official Cordis schemas, guidance, and companion Skill become available together in PTC agent scopes; disabled configurations remain unchanged. Changing this option updates live agent scopes without a DSH restart, including repeated disable/re-enable cycles. A DSH release may rename or reshape its Cordis surface without requiring a mirrored-name change in PTC Plus, provided the public plugin, preset, Skill, and inspect-provider contracts remain compatible.
39
+ PTC Plus declares the maintained DSH Cordis tool, Skill filesystem, settings, and tool-runtime packages as required peers through the current release channel and mirrors them in development dependencies. They resolve from DSH's installation-maintained profile fallback; installing PTC Plus must not add a profile-local DSH core package instance. When enabled, the official Cordis schemas, guidance, and exact companion Skill become available together in PTC agent scopes; disabled configurations remain unchanged. Changing this option updates live agent scopes without a DSH restart, including repeated disable/re-enable cycles. Resume and re-enable may append one bounded recovery snapshot but do not change stable tool schemas or system sections. A DSH release may rename or reshape its Cordis tool surface without requiring a mirrored-name change in PTC Plus, while the named companion Skill remains an explicit compatibility contract and fails activation if unavailable or inconsistent.
@@ -29,7 +29,7 @@ DSH 为一个 agent composition 固定选择 `native`、`code` 或 `both`。首
29
29
  `run_code`。无目标或参数不合法时不执行,返回 `{ edited: false, reason }` 并保留原目标。
30
30
  进程内 claim 区分 executing 与 settled:抛错、取消或缺少有效 PTC journal 的派生结果立即释放;已由 journal 证明进入 runtime 的派生执行继续占用旧 target,直到外层 result 的私有 metadata 通过最终 policy 并由 session log 投影为新 target。最终 result 丢失 metadata 时由 `tools/result` 释放。session log 投影在 edit call event 处记录 eligible target,只接受 target call sequence 匹配该快照且 journal 有效、非 noop 的派生源码,因此 live 与 cold recovery 从同一持久关系决定可编辑目标。
31
31
 
32
- `internal/session-log-view.js` 单次前向扫描 session events,分别投影最新执行、可编辑目标、rewrite metadata 和恢复 tip
32
+ `internal/session-log-view.js` 单次前向扫描 session events,分别投影最新执行、可编辑目标、rewrite metadata、规范 journal 中的 Cordis transcript 计数和恢复 tip
33
33
  所需事实。`edit_run_code` 不产生专用 runtime context:真实 call/result 已完整表达操作身份和结果,额外 contribution
34
34
  只会触发 DSH 聚合 runtime-context 全量快照并重复无关 policy 文本。只有失败或完成状态不可信的 rewrite feedback 才保留独立生命周期;成功的透明改写不产生 runtime context。
35
35
 
@@ -44,7 +44,7 @@ return 或打印需要展示的值。失败恢复先按状态分类:解析或
44
44
  相同提示受 `tipCooldownMessages` 间隔约束,连续未解决时才升级为详细版本,成功 cell 会重置未解决计数;提示不会改变
45
45
  system sections、tool schema 或 tool order,也不假设 Windows、WSL、POSIX、shell 或 package runner。
46
46
 
47
- 当前策略位于独立的 `internal/recovery-tips.js` local provider,核心只消费其有界的 named context。未来外部决策插件的接入必须等待稳定的 facts/decision contract;在此之前不猜测跨插件 API,local provider 继续作为 fallback。
47
+ 当前策略位于独立的 `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。
48
48
 
49
49
  ## 设置与启用开关
50
50
 
@@ -66,7 +66,7 @@ Client half 通过 `settings.plugin.item` 卡片呈现全部配置。`enabled`
66
66
  | Replacement | 只有 DSH 拥有的 compaction 或其他显式 surface replacement 可以替换已保留历史;PTC Plus 不用 replacement 表达瞬时状态。 |
67
67
  | Independent request | 新的辅助模型调用必须单独说明 route、prefix 和 token 影响,不能用它的缓存表现证明主会话前缀稳定。当前插件不发起辅助模型请求。 |
68
68
 
69
- 一次性或会变化的 session 状态必须作为 `PromptAssembly.contexts` 的命名贡献交给 DSH。DSH 将完整快照记录为带来源的 `user/message` 并追加到历史尾部;值未变时不重复,值变化或全部消失时追加更新或 clearance。完整快照携带当时所有 owner 的命名 context,因此任一贡献变化都会使未变化贡献在新的尾部消息中再次出现;这会增加 append-only history token,但不会改写已有前缀。PTC Plus 不通过绕过 prompt assembly 的私有消息通道规避这项宿主聚合成本。此类状态不得进入 `PromptAssembly.sections`,也不得通过增删 tool、改变 schema 字段或调整 tool order 传递。
69
+ 一次性或会变化的 session 状态必须作为 `PromptAssembly.contexts` 的命名贡献交给 DSH。DSH 将完整快照记录为带来源的 `user/message` 并追加到历史尾部;值未变时不重复,值变化或全部消失时追加更新或 clearance。完整快照携带当时所有 owner 的命名 context,因此任一贡献变化都会使未变化贡献在新的尾部消息中再次出现;这会增加 append-only history token,但不会改写已有前缀。PTC Plus 不通过绕过 prompt assembly 的私有消息通道规避这项宿主聚合成本。Cordis 恢复 context 使用规范 journal 的历史 transcript 与当前 agent/enable generation 作为事实源,不能从进程内 Plugin registry 直接生成未记录输入。此类状态不得进入 `PromptAssembly.sections`,也不得通过增删 tool、改变 schema 字段或调整 tool order 传递。
70
70
 
71
71
  所有模型可见输入都必须能从 session log 重建。静态 system/schema 由 `request/header` 保存,动态 context 由带来源的 `user/message` 保存;进程内临时状态不能直接成为未记录的模型输入。在同一插件版本与配置下,普通执行结果、可编辑目标、诊断和其他插件拥有的运行期变化都不得改变 header;`edit_run_code` 的身份与结果由真实 call/result 表达,不生成 edit feedback context。插件升级、显式配置变化、provider/model route 变化、真实 native capability schema 变化,以及 DSH 拥有的 history replacement 可以使缓存从首个变化 token 起失效。
72
72
 
@@ -111,7 +111,7 @@ CodeRuntime request 已携带的 owner-provided program namespace 会被原样
111
111
  }
112
112
  ```
113
113
 
114
- `calls` 只保存 global、member、PTC Value Graph 编码的 args/result 或 error,以及 settlement 序号。cold replay 校验调用名称、参数、数量和提交顺序,并按 recorded settlement order 释放 recorded result;不会重新 dispatch program binding 或重做外部 effect。该规则同样适用于 native tools、owner-provided namespace 和 `code.run`,不按名称分支。若基础设施终止时仍有未结算 binding,heap 回滚到 durable frontier,discarded journal 以最先观察到的 `global.member` 保留 possible-effect boundary。effect、completeness 和 source metadata 属于 capability explorer,不伪装成 journal 字段。
114
+ `calls` 只保存 global、member、PTC Value Graph 编码的 args/result 或 error,以及 settlement 序号。cold replay 校验调用名称、参数、数量和提交顺序,并按 recorded settlement order 释放 recorded result;不会重新 dispatch program binding 或重做外部 effect。该规则同样适用于 native tools、owner-provided namespace 和 `code.run`,不按名称分支。Cordis 的进程内对象不会因此被宣称已恢复;presentation 可以从已验证 transcript 派生重新检查要求,但不能改变 replay。若基础设施终止时仍有未结算 binding,heap 回滚到 durable frontier,discarded journal 以最先观察到的 `global.member` 保留 possible-effect boundary。effect、completeness 和 source metadata 属于 capability explorer,不伪装成 journal 字段。
115
115
 
116
116
  journal 通过 `run_code.output.presentationMeta` 附着到最终 result,再由 `tools/result` 做两阶段确认。缺失、损坏或被替换的 journal 形成 unknown/volatile 边界;未进入 runtime 的 call 由后续 `confirms` 以对应 `tool/call.seq` 证明为 no-op。volatile 源码保留在原 session log,但不参与 cold replay。
117
117
 
package/docs/client-ui.md CHANGED
@@ -12,7 +12,7 @@ Host half 通过 DSH 公共 `settings` 服务注册命名空间 `ptc-plus`。字
12
12
  | 分组 | 字段 | 说明 |
13
13
  | --- | --- | --- |
14
14
  | 开关 | `enabled` | 关闭后不注册 `run_code`/`edit_run_code`、不修改 system prompt、不创建 session runtime;只保留设置 UI。 |
15
- | Cordis | `cordisToolsEnabled` | 默认关闭;开启后为 PTC agent 加入创造模式使用的官方 Cordis 工具、指引与 companion Skill。 |
15
+ | Cordis | `cordisToolsEnabled` | 默认关闭;开启后为 PTC agent 加入官方 Cordis 工具、指引与精确的 `cordis-plugin-development` companion Skill,不发布同目录 sibling。 |
16
16
  | 计算 | `computeMs` / `maxWallMs` | 单 cell CPU 与墙钟预算。 |
17
17
  | 输出 | `maxOutputBytes` / `maxValueNodes` / `maxValueEdges` / `maxValueArrayLength` / `maxValueBigIntDigits` | Value Graph 与输出字节预算。 |
18
18
  | Worker | `maxOldGenerationSizeMb` / `maxNestedRunCodeDepth` | kernel worker 内存与嵌套执行深度。 |
@@ -87,7 +87,7 @@ journal、diagnostic、source、cause、call、operation、completion 和 comple
87
87
  }
88
88
  ```
89
89
 
90
- `settle` 必须是从 0 开始的连续序列。重放按源码产生调用,但不重新 dispatch;它校验 global、member、args 和提交数量,再按 recorded settlement order 释放 recorded value/error。这个 `recorded-value` 承诺重建的是 REPL 计算状态,不表示外部 effect 被重做、撤销或验证。completeness、effect、authority 和 source evidence 属于 capability metadata,不伪装成 journal 字段。
90
+ `settle` 必须是从 0 开始的连续序列。重放按源码产生调用,但不重新 dispatch;它校验 global、member、args 和提交数量,再按 recorded settlement order 释放 recorded value/error。这个 `recorded-value` 承诺重建的是 REPL 计算状态,不表示外部 effect 被重做、撤销或验证。对进程内 Cordis 状态,恢复值明确只是历史数据:Cordis 可见时,固定 runtime context 要求在把旧 ID、Run、approval 或 capability observation 当作当前事实前完成一次新的成功 live Inspect。completeness、effect、authority 和 source evidence 属于 capability metadata,不伪装成 journal 字段。
91
91
 
92
92
  journal 的 capability-call `args`/`value` 与 completion value 保存 `ptc-value-graph/v1` canonical envelope,不保存 decoded rich JS value,也不依赖递归 `JSON.stringify` 或 `structuredClone`。因此深层数组/对象、own `__proto__`、`undefined`、special number、BigInt、hole、shared identity 和 cycle 不会被外层 session JSON 改写。完整支持域和预算见 [PTC Value Graph V1](value-wire.md)。
93
93
 
@@ -18,7 +18,7 @@ DSH authority/policy -> native dispatch -> canonical result
18
18
  `-> args/result + settlement transcript
19
19
  ```
20
20
 
21
- cold replay 校验相同的调用序列并返回 recorded value,不重新执行工具。这保证 REPL 计算状态可重建,但不声称外部 effect 可逆或仍与历史相同。
21
+ cold replay 校验相同的调用序列并返回 recorded value,不重新执行工具。这保证 REPL 计算状态可重建,但不声称外部 effect 可逆或仍与历史相同。Cordis Plugin、Run 和 Inspect observation 属于进程内实时状态;恢复出的 Cordis value 只作为历史数据保留,后续状态性决策必须先按恢复 context 执行新的 live Inspect。
22
22
 
23
23
  同一 settlement 规则适用于当前 request 中的全部 program binding,包括 owner-provided namespace 与 `code.run`。已结算的 value/error 可进入 durable transcript;若取消、超时或 worker failure 发生时调用仍未结算,cell heap 回滚,discarded journal 以实际 `global.member` 保留 possible-effect boundary。分类来自可观察生命周期,不来自 capability 名称表。
24
24
 
@@ -71,10 +71,12 @@ The configuration uses `ptc-plus` as the DSH plugin ID and `dsh-ptc-plus` as the
71
71
 
72
72
  `enabled: false` is the settings-based kill switch: the Host keeps only the settings namespace and client card, and removes every runtime hook, prompt section, and tool surface. Every setting is applied live when its owner can reconcile it. A submitted cell retains one configuration snapshot through preflight, execution, binding calls, completion validation, and diagnostics; an update accepted while it runs applies to cells submitted afterward. Node fixes a worker's V8 old-generation limit at creation, so changing `maxOldGenerationSizeMb` while a session worker is active is rejected and rolled back rather than reported as applied. Other non-`enabled` updates reconfigure existing owners without replacing session-bound bindings. The settings card is available under Settings → Plugin configuration, shows “已启用” or “已停用”, and disables every control except `enabled` while the plugin is off. If a live enable or reconfiguration cannot install a complete runtime, PTC Plus unwinds or restores every owner and persists the last applied settings; a failed compensating settings write is surfaced as an activation diagnostic. The TypeScript language check is deferred until runtime activation, so a non-TypeScript host can load the plugin in disabled mode but cannot enable it.
73
73
 
74
- `cordisToolsEnabled: true` atomically mounts the official `@deepseek-ai/dsh-tool-cordis` plugin and the shipped `cordis-plugin-development` companion Skill in PTC agent scopes. PTC Plus resolves the official `cordis` preset through DSH's public preset service and publishes only that preset's Skill directory through the maintained filesystem provider; it does not copy the Skill or switch the agent's preset. The first request waits until the Cordis tool fiber, owner guidance, Skill provider, and scoped Skill load are all ready. A missing or broken preset, Skill/tool service, declared Cordis service, inconsistent inspect manifest, or rejected activation fails the setting change and removes every provisional contribution. Disable and agent/runtime disposal remove both fibers. Native agents inherit neither the resulting `tools.*` members nor the Skill, and the code-only direct-tool projection remains `[run_code, edit_run_code]`. Agents whose `run_code` surface appears after creation are retried on the DSH tools-change signal. Cordis can evaluate model-written plugins against the live runtime, so enabling it grants shell-equivalent trust.
74
+ `cordisToolsEnabled: true` atomically mounts the official `@deepseek-ai/dsh-tool-cordis` plugin and exactly the shipped `cordis-plugin-development` companion Skill in PTC agent scopes. PTC Plus resolves the official `cordis` preset through DSH's public preset service, lets the maintained filesystem provider own its Skill root, and filters that provider at the public registration boundary so sibling Skills are never published to the PTC scope. It does not copy the Skill or switch the agent's preset. The first request waits until the Cordis tool fiber, owner guidance, exact Skill provider, and scoped Skill load are all ready. A missing or broken preset, Skill/tool service, declared Cordis service, inconsistent inspect manifest, or rejected activation fails the setting change and removes every provisional contribution. Disable and agent/runtime disposal remove both fibers. Native agents inherit neither the resulting `tools.*` members nor the Skill, and the code-only direct-tool projection remains `[run_code, edit_run_code]`. Agents whose `run_code` surface appears after creation are retried on the DSH tools-change signal. Cordis can evaluate model-written plugins against the live runtime, so enabling it grants shell-equivalent trust.
75
75
 
76
76
  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.
77
77
 
78
+ 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.
79
+
78
80
  `durableReplay: false` starts new kernels without historical REPL state while preserving live bindings in the current process. It does not delete session logs.
79
81
 
80
82
  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.
@@ -8,6 +8,63 @@ const CORDIS_PRESET_ID = 'cordis'
8
8
  const CORDIS_SKILL_NAME = 'cordis-plugin-development'
9
9
  const CORDIS_SKILL_PROVIDER = 'ptc-plus-cordis'
10
10
 
11
+ function exactCompanionCandidates(observation) {
12
+ if (Array.isArray(observation)) {
13
+ return observation.filter(candidate => candidate?.name === CORDIS_SKILL_NAME)
14
+ }
15
+ if (observation === null || typeof observation !== 'object'
16
+ || !Array.isArray(observation.candidates)) return observation
17
+ return {
18
+ ...observation,
19
+ candidates: observation.candidates.filter(candidate => candidate?.name === CORDIS_SKILL_NAME),
20
+ }
21
+ }
22
+
23
+ function exactCompanionProvider(provider) {
24
+ if (provider === null || typeof provider !== 'object'
25
+ || typeof provider.name !== 'string'
26
+ || typeof provider.list !== 'function'
27
+ || typeof provider.get !== 'function') {
28
+ throw new Error('ptc-plus: DSH Cordis Skill provider is incompatible')
29
+ }
30
+ return {
31
+ ...provider,
32
+ async list(options) {
33
+ return exactCompanionCandidates(await Reflect.apply(provider.list, provider, [options]))
34
+ },
35
+ async get(candidate, options) {
36
+ if (candidate?.name !== CORDIS_SKILL_NAME) return undefined
37
+ const skill = await Reflect.apply(provider.get, provider, [candidate, options])
38
+ return skill?.name === CORDIS_SKILL_NAME ? skill : undefined
39
+ },
40
+ }
41
+ }
42
+
43
+ function exactCompanionSkillPlugin(skillFilesystemPlugin) {
44
+ return {
45
+ ...skillFilesystemPlugin,
46
+ apply(pluginCtx, config) {
47
+ if (typeof pluginCtx?.extend !== 'function') {
48
+ throw new Error('ptc-plus: cordisToolsEnabled requires the DSH Context.extend API')
49
+ }
50
+ const skills = pluginCtx.get?.('skills')
51
+ if (typeof skills?.registerProvider !== 'function') {
52
+ throw new Error('ptc-plus: cordisToolsEnabled requires the DSH skills.registerProvider API')
53
+ }
54
+ const facade = new Proxy(skills, {
55
+ get(target, property) {
56
+ if (property === 'registerProvider') {
57
+ return create => target.registerProvider(control => exactCompanionProvider(create(control)))
58
+ }
59
+ const value = Reflect.get(target, property, target)
60
+ return typeof value === 'function' ? value.bind(target) : value
61
+ },
62
+ })
63
+ return skillFilesystemPlugin.apply(pluginCtx.extend({ skills: facade }), config)
64
+ },
65
+ }
66
+ }
67
+
11
68
  function requireFiberServices(agent, fiber, owner) {
12
69
  if (typeof agent?.ctx?.get !== 'function') {
13
70
  throw new Error('ptc-plus: cordisToolsEnabled requires the DSH agent Context.get API')
@@ -32,8 +89,9 @@ function requireCompanionServices(agent) {
32
89
  throw new Error('ptc-plus: cordisToolsEnabled requires the DSH agentPresets.resolve API')
33
90
  }
34
91
  const skills = agent.ctx.get('skills')
35
- if (typeof skills?.list !== 'function' || typeof skills?.get !== 'function') {
36
- throw new Error('ptc-plus: cordisToolsEnabled requires the DSH skills list/get APIs')
92
+ if (typeof skills?.registerProvider !== 'function'
93
+ || typeof skills.list !== 'function' || typeof skills.get !== 'function') {
94
+ throw new Error('ptc-plus: cordisToolsEnabled requires the DSH skills registerProvider/list/get APIs')
37
95
  }
38
96
  if (agent.ctx.tools?.get?.('skill', agent) === undefined) {
39
97
  throw new Error('ptc-plus: cordisToolsEnabled requires the DSH skill tool in the PTC agent scope')
@@ -57,10 +115,11 @@ async function requireCompanionSkill(agent, skills) {
57
115
  cwd: agent.session?.header?.cwd,
58
116
  scope: agent,
59
117
  }
60
- const summary = (await skills.list(lookup)).find(skill => skill?.name === CORDIS_SKILL_NAME)
61
- if (summary?.provider !== CORDIS_SKILL_PROVIDER
118
+ const owned = (await skills.list(lookup)).filter(skill => skill?.provider === CORDIS_SKILL_PROVIDER)
119
+ const summary = owned[0]
120
+ if (owned.length !== 1 || summary?.name !== CORDIS_SKILL_NAME
62
121
  || summary.invocation?.modelInvocable !== true) {
63
- throw new Error(`ptc-plus: official Cordis companion Skill ${JSON.stringify(CORDIS_SKILL_NAME)} is not model-invocable`)
122
+ throw new Error(`ptc-plus: official Cordis Skill provider must publish exactly ${JSON.stringify(CORDIS_SKILL_NAME)} as model-invocable`)
64
123
  }
65
124
  const skill = await skills.get(CORDIS_SKILL_NAME, lookup)
66
125
  if (skill?.provider !== CORDIS_SKILL_PROVIDER
@@ -177,6 +236,7 @@ export function createCordisToolsOwner(
177
236
  const pending = new Set()
178
237
  const cordisInspectLeases = createCordisInspectLeases(ctx)
179
238
  const scopedCordisPlugin = cordisInspectLeases.plugin(cordisPlugin)
239
+ const scopedSkillPlugin = exactCompanionSkillPlugin(skillFilesystemPlugin)
180
240
  let disposed = false
181
241
 
182
242
  const leafErrors = error => error instanceof AggregateError
@@ -242,7 +302,7 @@ export function createCordisToolsOwner(
242
302
  const skillDirectory = await companionSkillDirectory(agentPresets)
243
303
  if (mount.disposed) return
244
304
 
245
- const skillFiber = agent.ctx.plugin(skillFilesystemPlugin, {
305
+ const skillFiber = agent.ctx.plugin(scopedSkillPlugin, {
246
306
  providerName: CORDIS_SKILL_PROVIDER,
247
307
  includeDefaultRoots: false,
248
308
  customSkillDirs: [skillDirectory],
@@ -59,6 +59,38 @@ function rejection(message) {
59
59
  }
60
60
  }
61
61
 
62
+ export function createCordisRecoveryPolicy(initiallyEnabled) {
63
+ const states = new WeakMap()
64
+ let enabled = initiallyEnabled
65
+ let generation = enabled ? 1 : 0
66
+ return Object.freeze({
67
+ reconfigure(nextEnabled) {
68
+ if (!enabled && nextEnabled) generation += 1
69
+ enabled = nextEnabled
70
+ },
71
+ required(agent, view) {
72
+ if (!enabled) return false
73
+ let state = states.get(agent)
74
+ if (state?.generation !== generation) {
75
+ state = {
76
+ generation,
77
+ baselineInspections: view.cordisTranscript.inspections,
78
+ required: view.cordisTranscript.calls > 0,
79
+ }
80
+ states.set(agent, state)
81
+ }
82
+ if (state.required
83
+ && view.cordisTranscript.inspections > state.baselineInspections) {
84
+ state.required = false
85
+ }
86
+ return state.required
87
+ },
88
+ disposeAgent(agent) {
89
+ states.delete(agent)
90
+ },
91
+ })
92
+ }
93
+
62
94
  function presentationState(assembly) {
63
95
  const tools = Array.isArray(assembly?.tools) ? assembly.tools : []
64
96
  if (!tools.some(tool => tool?.name === RUN_CODE)) {
@@ -110,6 +142,7 @@ export function createDirectSurfaceOwner({
110
142
  const compositions = new Map()
111
143
  const canonicalRequests = new WeakMap()
112
144
  const sessions = new Map()
145
+ const cordisRecovery = createCordisRecoveryPolicy(runtimeConfig.cordisToolsEnabled)
113
146
  let currentCanonicalizeToolCalls = canonicalizeToolCalls
114
147
  const tipConfig = {
115
148
  enabled: runtimeConfig.tipsEnabled,
@@ -175,6 +208,7 @@ export function createDirectSurfaceOwner({
175
208
  return Object.freeze({
176
209
  reconfigure(nextConfig) {
177
210
  currentCanonicalizeToolCalls = nextConfig.canonicalizeToolCalls
211
+ cordisRecovery.reconfigure(nextConfig.cordisToolsEnabled)
178
212
  tipConfig.enabled = nextConfig.tipsEnabled
179
213
  tipConfig.cooldownMessages = nextConfig.tipCooldownMessages
180
214
  tipConfig.escalationFailures = nextConfig.tipEscalationFailures
@@ -231,7 +265,11 @@ export function createDirectSurfaceOwner({
231
265
  directTools = [adaptRunCodeSchema(runCode), editRunCodeSchema()]
232
266
  }
233
267
  const runtimeContexts = sessionPtc
234
- ? sessionRuntimeContexts(agent, tipConfig)
268
+ ? sessionRuntimeContexts(agent, tipConfig, {
269
+ cordisRecoveryRequired(view) {
270
+ return cordisRecovery.required(agent, view)
271
+ },
272
+ })
235
273
  : { contexts: [] }
236
274
  const contexts = sessionPtc && Array.isArray(assembly.contexts)
237
275
  && runtimeContexts.contexts.length > 0
@@ -310,6 +348,7 @@ export function createDirectSurfaceOwner({
310
348
  disposeAgent(agent) {
311
349
  const id = sessionId(agent)
312
350
  compositions.delete(agent)
351
+ cordisRecovery.disposeAgent(agent)
313
352
  if (id !== undefined) clearSession(id)
314
353
  },
315
354
  disposeSession(session) {
@@ -2,6 +2,8 @@ import { latestRecoveryTip } from './recovery-tips.js'
2
2
  import { projectSessionLog } from './session-log-view.js'
3
3
 
4
4
  const REWRITE_FEEDBACK = 'tools:ptc-plus-rewrite-info'
5
+ const CORDIS_RECOVERY = 'tools:ptc-plus-cordis-recovery'
6
+ const CORDIS_RECOVERY_TEXT = 'Recorded Cordis values in the recovered REPL are historical data, not proof that process-local Plugins, Runs, approvals, or Inspect observations still exist in the current DSH process. Before relying on prior Cordis IDs, state, or capability data, follow the current Cordis owner guidance and call the live read-only Cordis Inspect bindings through `tools.*`; do not rerun mutating Cordis calls merely to reconstruct history.'
5
7
 
6
8
  function continuationFeedback(view) {
7
9
  const run = view.latestRun
@@ -18,13 +20,17 @@ function continuationFeedback(view) {
18
20
  }
19
21
 
20
22
  /** Build all dynamic PTC contexts from one session-log projection. */
21
- export function sessionRuntimeContexts(agent, tipConfig) {
23
+ export function sessionRuntimeContexts(agent, tipConfig, options = {}) {
22
24
  const view = projectSessionLog(agent)
23
25
  const rewrite = continuationFeedback(view)
24
26
  const tip = latestRecoveryTip(view, tipConfig)
27
+ const cordisRecovery = options.cordisRecoveryRequired?.(view) === true
28
+ ? { name: CORDIS_RECOVERY, text: CORDIS_RECOVERY_TEXT }
29
+ : undefined
25
30
  return Object.freeze({
26
31
  contexts: Object.freeze([
27
32
  ...(rewrite === undefined ? [] : [{ name: REWRITE_FEEDBACK, text: rewrite }]),
33
+ ...(cordisRecovery === undefined ? [] : [cordisRecovery]),
28
34
  ...(tip === undefined ? [] : [tip]),
29
35
  ]),
30
36
  })
@@ -27,6 +27,20 @@ function isContextStep(event) {
27
27
  || (event?.type === 'user/message' && event.data?.source?.kind === 'user')
28
28
  }
29
29
 
30
+ function cordisTranscriptFacts(timeline) {
31
+ let calls = 0
32
+ let inspections = 0
33
+ for (const result of timeline.results.values()) {
34
+ for (const call of result?.journal?.calls ?? []) {
35
+ if (call.global !== 'tools' || typeof call.member !== 'string'
36
+ || !call.member.startsWith('cordis_')) continue
37
+ calls += 1
38
+ if (call.ok === true && call.member.startsWith('cordis_inspect')) inspections += 1
39
+ }
40
+ }
41
+ return Object.freeze({ calls, inspections })
42
+ }
43
+
30
44
  /** Project session events into immutable facts consumed by prompt presentation. */
31
45
  export function projectSessionLog(agent, requestedEdit) {
32
46
  const events = agent?.session?.events
@@ -39,6 +53,7 @@ export function projectSessionLog(agent, requestedEdit) {
39
53
  latestRun: undefined,
40
54
  editableRun: undefined,
41
55
  repairSource: undefined,
56
+ cordisTranscript: Object.freeze({ calls: 0, inspections: 0 }),
42
57
  })
43
58
  }
44
59
  let contextStep = 0
@@ -60,6 +75,7 @@ export function projectSessionLog(agent, requestedEdit) {
60
75
  : undefined
61
76
  const latestRun = timeline.openTurn ? timeline.latestRun : undefined
62
77
  const editableRun = timeline.openTurn ? timeline.editableRun : undefined
78
+ const cordisTranscript = cordisTranscriptFacts(timeline)
63
79
  return Object.freeze({
64
80
  openTurn: timeline.openTurn,
65
81
  contextStep,
@@ -68,6 +84,7 @@ export function projectSessionLog(agent, requestedEdit) {
68
84
  latestRun,
69
85
  editableRun,
70
86
  repairSource: editableRun?.source,
87
+ cordisTranscript,
71
88
  ...(requestedEdit === undefined ? {} : { requestedEditTarget }),
72
89
  })
73
90
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ptc-plus",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "A session-bound agent-native REPL for DeepSeek Harness PTC mode.",
5
5
  "type": "module",
6
6
  "main": "./index.js",