dsh-ptc-plus 0.4.3 → 0.4.10
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.en.md +15 -3
- package/README.md +15 -3
- package/client.js +1157 -533
- package/cordis.patch.yml +12 -2
- package/docs/README.md +2 -0
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +60 -4
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +5 -1
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +14 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0023-global-user-bindings.md +1 -1
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +82 -18
- package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +3 -3
- package/docs/adr/0032-own-session-log-facts-in-a-host-only-projection.md +27 -0
- package/docs/adr/0033-gate-client-visual-usability-in-a-browser.md +23 -0
- package/docs/architecture.md +6 -4
- package/docs/binding-review-placement-design.md +29 -21
- package/docs/client-ui.md +20 -8
- package/docs/client-visual-contract.md +48 -0
- package/docs/durability-design.md +12 -0
- package/docs/installation.md +48 -9
- package/docs/publishing.md +4 -4
- package/docs/release-notes.md +109 -0
- package/docs/runtime-reference.md +8 -2
- package/docs/user-bindings.en.md +2 -2
- package/docs/user-bindings.md +3 -3
- package/docs/verification-optimization-work.md +26 -0
- package/docs/verification.md +7 -1
- package/index.js +28 -7
- package/internal/compiler-platform.js +5 -3
- package/internal/compiler-service.js +2 -9
- package/internal/cordis-tools-owner.js +75 -16
- package/internal/direct-surface-owner.js +333 -7
- package/internal/edit-transport-owner.js +8 -5
- package/internal/execution-provider-owner.js +157 -0
- package/internal/execution-seam-compat.js +69 -113
- package/internal/isolated-worker.js +12 -8
- package/internal/recovery-tips.js +50 -21
- package/internal/rejected-cell-editor.js +11 -21
- package/internal/runtime-bridge-owner.js +88 -57
- package/internal/runtime-contexts.js +13 -13
- package/internal/session-events.js +40 -4
- package/internal/session-journal-recovery.js +678 -181
- package/internal/session-log-view.js +189 -43
- package/internal/session-runtime.js +58 -10
- package/internal/settings-compat.js +12 -2
- package/internal/user-bindings-owner.js +6 -5
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +18 -13
- package/src/client-authoring-view.js +222 -141
- package/src/client-binding-review.js +2 -0
- package/src/client-code-editor.js +6 -0
- package/src/client-copy.js +10 -0
- package/src/client-feature-gates.js +12 -5
- package/src/client-host-compat.js +23 -2
- package/src/client-primitives.js +249 -0
- package/src/client-repl-view.js +3 -3
- package/src/client-settings-view.js +2 -29
- package/src/client-styles.js +92 -33
- package/src/client-tool-view.js +8 -3
- package/src/client-workbench.js +9 -7
- package/src/client.js +72 -46
- package/assets/ptc-plus-bindings-en.png +0 -0
- package/assets/ptc-plus-bindings-zh.png +0 -0
- package/assets/ptc-plus-repair-en.png +0 -0
- package/assets/ptc-plus-repair-zh.png +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# GitHub Release Notes
|
|
2
|
+
|
|
3
|
+
本文件拥有每个 GitHub Release 的**内容取舍与写作风格**。发布流程(tag、CI 身份校验、npm staging、2FA approve)由 [publishing.md](publishing.md) 拥有;包内容由 manifest 与发布白名单拥有;面向用户的长期说明由 [README.md](../README.md) 与 [README.en.md](../README.en.md) 拥有。写 notes 时按本文件执行,不需要回看历史 Release。
|
|
4
|
+
|
|
5
|
+
## 读者与边界
|
|
6
|
+
|
|
7
|
+
读者是使用插件的用户。notes 只写他们需要知道的产品事实:能不能装、能不能跑、行为变了什么、坏掉的东西修好了什么、升级要做什么。
|
|
8
|
+
|
|
9
|
+
不写产物的生成过程:
|
|
10
|
+
|
|
11
|
+
- 不写写作时间或发行状态(“写作时”“目前”“本文完成于”);需要指明目标发行时直接写版本号
|
|
12
|
+
- 不写验证与取证过程(谁在哪个客户端上核对过、跑过哪些命令、门禁结果)
|
|
13
|
+
- 不写内部实现与流程术语(内部模块名、实现步骤、评审与 CI 与发布自动化)
|
|
14
|
+
- 不写协商过程与修正记录(“按反馈”“不再使用某说法”)
|
|
15
|
+
|
|
16
|
+
验证证据留在交付记录里(CI run、发布日志、维护文档),不进入 notes。
|
|
17
|
+
|
|
18
|
+
## 先按用户视角计算重要性
|
|
19
|
+
|
|
20
|
+
从 `git log --format='%h %s%n%b' <上一个 tag>..<本次 tag>` 收集全部变更,按用户的遭遇排序后再提炼;提交顺序、提交类型(feat/fix/docs/chore)和改动行数都不参与排序。
|
|
21
|
+
|
|
22
|
+
1. **还能不能用**:对当前官方 DSH 发布与上一代宿主契约的适配、安装与升级路径、激活与启动。用户最先问的是“装上还能不能跑”,排第一。
|
|
23
|
+
2. **有什么变化**:新增能力、默认值变化、既有行为改变,以及需要用户动手的迁移。
|
|
24
|
+
3. **哪些毛病没了**:用户可观察到的错误、闪烁、失效入口、丢失的数据关系。
|
|
25
|
+
4. **升级要求**:Node 版本、是否需要重启、是否有配置迁移。
|
|
26
|
+
5. **内部交付**默认不出现;只有当它改变用户可见结果时,才用用户能理解的方式提及。
|
|
27
|
+
|
|
28
|
+
兼容性声明落在公共扩展面上——设置座位、slot、公共 `Menu` 区域、会话投影、能力探测。版本号只用于标明本次适配的目标发行,不构成版本白名单;能力与座位仍以宿主声明的公共扩展面为准([ADR 0017](adr/0017-track-the-latest-dsh-public-surface.md))。
|
|
29
|
+
|
|
30
|
+
## 风格
|
|
31
|
+
|
|
32
|
+
- 双语且等价:中文在前,`---` 之后是 English;两段不互相省略,小节顺序一致。小节标题用 `<h3>`,中文 anchor 用 `cn-v0XY`,英文用 `en-v0XY`。
|
|
33
|
+
- 一条 bullet 只写一个用户可观察的结果;写清触发条件与边界(何时生效、何时不做、是否自动执行)。
|
|
34
|
+
- 用用户能观察到的对象描述变化:绑定、星光菜单、设置卡片、状态提示、cell 执行。宿主声明的座位键名(如 `plugins.row.config`)保留,内部模块名和实现步骤不写。
|
|
35
|
+
- 不写营销形容词,也不承诺恢复能力、永久记忆等无法保证的结果。
|
|
36
|
+
- 结构稳定,便于读者按标题 diff 两个版本:宿主适配 → 新增 → 修复 → 升级(含安装命令)→ `Full Changelog` compare 链接。
|
|
37
|
+
|
|
38
|
+
## 模板
|
|
39
|
+
|
|
40
|
+
````markdown
|
|
41
|
+
[中文](#cn-v0XY) | [English](#en-v0XY)
|
|
42
|
+
|
|
43
|
+
<h3 id="cn-v0XY">宿主适配</h3>
|
|
44
|
+
|
|
45
|
+
* 本次适配的目标 DSH 发行与它使用的公共扩展面
|
|
46
|
+
* 不满足该契约的宿主如何继续工作
|
|
47
|
+
|
|
48
|
+
<h3>新增</h3>
|
|
49
|
+
|
|
50
|
+
* ……
|
|
51
|
+
|
|
52
|
+
<h3>修复</h3>
|
|
53
|
+
|
|
54
|
+
* ……
|
|
55
|
+
|
|
56
|
+
<h3>升级</h3>
|
|
57
|
+
|
|
58
|
+
* 需要 Node.js `^22.19.0 || >=24.0.0`;升级插件后请重启 DSH
|
|
59
|
+
* 是否有配置迁移,以及旧配置的表现
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
dsh plugin --profile <profile> add dsh-ptc-plus@<version>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
<h3 id="en-v0XY">Host Compatibility</h3>
|
|
68
|
+
|
|
69
|
+
* ……
|
|
70
|
+
|
|
71
|
+
<h3>New Features</h3>
|
|
72
|
+
|
|
73
|
+
* ……
|
|
74
|
+
|
|
75
|
+
<h3>Fixes</h3>
|
|
76
|
+
|
|
77
|
+
* ……
|
|
78
|
+
|
|
79
|
+
<h3>Upgrade</h3>
|
|
80
|
+
|
|
81
|
+
* Requires Node.js `^22.19.0 || >=24.0.0`; restart DSH after upgrading the plugin
|
|
82
|
+
|
|
83
|
+
```sh
|
|
84
|
+
dsh plugin --profile <profile> add dsh-ptc-plus@<version>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
Full Changelog: https://github.com/muyuanjin/dsh-ptc-plus/compare/<上一个 tag>...<本次 tag>
|
|
90
|
+
````
|
|
91
|
+
|
|
92
|
+
## 示例(v0.4.3 的宿主适配小节)
|
|
93
|
+
|
|
94
|
+
````markdown
|
|
95
|
+
<h3 id="cn-v043">宿主适配</h3>
|
|
96
|
+
|
|
97
|
+
* 适配 DSH 0.1.7 的公共扩展面:设置卡片注册到宿主的插件配置座位(侧栏 **插件 → 已安装** 的 `plugins.row.config`),星光入口的四个动作进入公共 `Menu` 的 `children` 区域
|
|
98
|
+
* 不渲染 `children` 的宿主同样可用:同一套插件自有动作区由 pinned 区域承载,上一代设置座位 `settings.plugin.item` 继续生效
|
|
99
|
+
* 兼容性由公共能力探测决定:探测只记录一次已挂载观测,支持 `children` 的宿主不会重复渲染
|
|
100
|
+
````
|
|
101
|
+
|
|
102
|
+
同一版的 `feat(review)`、`chore(release)` 等内部提交不进入 notes;测试夹具修复属于交付记录,不作为用户条目。
|
|
103
|
+
|
|
104
|
+
## 发布时的用法
|
|
105
|
+
|
|
106
|
+
1. 取全量变更:`git log --format='%h %s%n%b' <上一个 tag>..<本次 tag>`。
|
|
107
|
+
2. 按用户视角排序,决定每条要不要写、写在哪个小节。
|
|
108
|
+
3. 逐条确认陈述在产品里成立;无法确认的条目删除。
|
|
109
|
+
4. 按模板写完;npm 版本可安装后创建 GitHub Release(流程见 [publishing.md](publishing.md))。
|
|
@@ -20,6 +20,8 @@ For PTC mode requests using the code-only direct-tool projection, PTC Plus prese
|
|
|
20
20
|
|
|
21
21
|
`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.
|
|
22
22
|
|
|
23
|
+
One provider failure has a bounded transport recovery. When the provider reports the exact invalid-tool-JSON `MALFORMED_RESPONSE` as a terminal chunk or stream exception, PTC Plus changes that terminal reason to `tool-calls`; this error cannot terminate the whole turn. DSH already preserves an unparseable argument string and routes it through ordinary validation, tool execution/result recording, and the next model step. A response is trusted for call preservation only when its buffered body consists entirely of tool-call-specific framing plus at most one `usage` event. If such a response contains exactly one internally consistent declared `run_code` or `edit_run_code` block, PTC Plus also tests deletion of one final `}`. It applies that correction only when the resulting value is an object accepted by the live direct-tool schema. The call keeps its index, identity, and name and proceeds through ordinary DSH validation, recording, scheduling, authority, and execution. Without a unique correction, trusted internally consistent complete blocks retain their original identity and argument bytes for the ordinary rejected-tool-result path; PTC Plus re-emits canonical start/delta/end framing so every tolerated provider variant passes DSH's stream invariant. If a tool block is incomplete or inconsistent, none was emitted, multiple usage events appear, or any other event occurs in the buffered body, PTC Plus closes any already visible text or reasoning block from its accumulated content, removes buffered unsafe block material, and emits one deterministic `run_code` call whose argument string cannot parse as a JSON object. DSH rejects it during ordinary schema validation, so no partial model call executes and the model still receives another step. Valid streams and other provider failures retain their original result. This is not a general JSON repair mechanism.
|
|
24
|
+
|
|
23
25
|
When a live parse error occurs exactly at the cell EOF, PTC Plus checks all 12 suffixes of one or two closing tokens drawn from `}`, `)`, 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.
|
|
24
26
|
|
|
25
27
|
Use `return` for an explicit result and the native Node `console` surface for logs. Its formatting methods, including `dir` options, tables, counters, groups, timers, assertions, and traces, retain Node semantics. `process.stdout/stderr.write`, direct fd 1/2 writes and inherited child stdio are also captured. Console entries retain call order and each descriptor pipe retains byte order; the merged log does not claim a total chronology across stdout, stderr and the private control channel. All forms share the cell's aggregate output budget. Native expression completion can also supply a result value:
|
|
@@ -61,6 +63,8 @@ The configuration uses `ptc-plus` as the DSH plugin ID and `dsh-ptc-plus` as the
|
|
|
61
63
|
|
|
62
64
|
`autoDescribeRunCode` defaults to `true` and appears as “Allow run_code execution without a summary.” When enabled, a call that omits the outer `run_code.description` uses derived arguments for local DSH validation, and the fallback summary enters presentation metadata only. When disabled, DSH validates the original arguments. Both states expose byte-identical model requests with the required `description` declaration; original call arguments, existing summaries, cell source, and nested native-tool JSON remain unchanged.
|
|
63
65
|
|
|
66
|
+
Missing-description diagnostics append a complete DSH user message to `additionalContexts`, with an identity, text content and a `plugin:ptc-plus` notice source. The diagnostic distinguishes outer transport arguments from nested native-tool arguments and preserves existing additional messages. These notices are ordinary inbox messages, separate from recovery-tip escalation and state snapshots. This prevents new malformed diagnostic records; it does not repair session files already written by older plugin versions.
|
|
67
|
+
|
|
64
68
|
```yaml
|
|
65
69
|
- id: ptc-plus
|
|
66
70
|
name: dsh-ptc-plus
|
|
@@ -89,9 +93,11 @@ The configuration uses `ptc-plus` as the DSH plugin ID and `dsh-ptc-plus` as the
|
|
|
89
93
|
|
|
90
94
|
The five legacy binding and module switches remain migration inputs. Complete enabled/protected choices map to the corresponding new policy; mixed old choices retain their effective controls until the user explicitly selects the new switch. The Host schema preserves an omitted `bindingUpdates` value until migration, and the settings card resolves the same policy. Selecting the switch also clears the compatibility marker.
|
|
91
95
|
|
|
92
|
-
`enabled: false` is the settings-based kill switch: the Host keeps the settings namespace, client card, and passive cleanup that can withdraw old PTC declarations at the next permitted accepted step. It removes execution hooks, prompt sections, and tool surfaces without reading binding storage. 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 renders in whichever settings seat the installed DSH declares — the sidebar **Plugins → Installed** page per-bundle configuration (`plugins.row.config`), or the preceding Host's **Settings → Plugin configuration** card (`settings.plugin.item`) ([ADR 0019](adr/0019-plugin-settings-and-kill-switch.md)) — shows “已启用” or “已停用”, and disables every control except `enabled` while the plugin is off. The card does not register in General settings; the sparkle menu opens the same complete form in an application dialog. 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.
|
|
96
|
+
`enabled: false` is the settings-based kill switch: the Host keeps the settings namespace, client card, and passive cleanup that can withdraw old PTC declarations at the next permitted accepted step. It removes execution hooks, prompt sections, and tool surfaces without reading binding storage. 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 or a submitted cell is preparing to execute 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 renders in whichever settings seat the installed DSH declares — the sidebar **Plugins → Installed** page per-bundle configuration (`plugins.row.config`), or the preceding Host's **Settings → Plugin configuration** card (`settings.plugin.item`) ([ADR 0019](adr/0019-plugin-settings-and-kill-switch.md)) — shows “已启用” or “已停用”, and disables every control except `enabled` while the plugin is off. The card does not register in General settings; the sparkle menu opens the same complete form in an application dialog. 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.
|
|
97
|
+
|
|
98
|
+
`cordisToolsEnabled: true` atomically mounts the official `@deepseek-ai/dsh-tool-cordis` plugin and exactly the shipped `cordis-plugin-development` companion Skill in agent scopes that expose the `run_code` and `skill` tools plus the public preset and Skill services. PTC Plus resolves the official `cordis` preset through DSH's public preset service, lets the maintained filesystem provider read its Skill root as an immutable bundled Host resource, 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. An agent scope that omits one of those surfaces is left for later: the owner publishes nothing there, reports nothing, and retries when the tool surface changes or the next prompt is assembled. A scope whose exposed preset, Skill/tool service, declared Cordis service, inspect manifest, or activation contradicts the host contract fails the setting change at installation and removes every provisional contribution; reached through the host's own `agent/created` or assemble call it withdraws from that scope with one warning instead, so this plugin's unavailability never fails an agent another plugin created ([ADR 0029](adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md)). 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]`. Cordis can evaluate model-written plugins against the live runtime, so enabling it grants shell-equivalent trust.
|
|
93
99
|
|
|
94
|
-
|
|
100
|
+
The Skill root uses the preset's absolute composition path when that generation publishes one. A pathless generation is resolved through the active Host's public `pluginPackages.packageOf` service, which returns the official singular or plural preset package from that installation rather than an ambient package reachable from the plugin profile. An older generation with no such service may resolve only its historical plural package relative to the Host's `ctx.baseUrl`; the fallback is disabled whenever `pluginPackages` exists. The exact `cordis-plugin-development/SKILL.md` must exist before the mount begins. An invalid published path remains authoritative and fails instead of falling through to another generation's package layout.
|
|
95
101
|
|
|
96
102
|
Keep large Cordis host or client source in a top-level binding before calling a Cordis tool. If that call rejects while the worker stays live, an initialized binding can be reused in a short continuation. Decide whether to retry from the operation owner's retry/idempotence contract and available execution facts; a thrown call or a smaller cell does not prove that no effect occurred.
|
|
97
103
|
|
package/docs/user-bindings.en.md
CHANGED
|
@@ -18,7 +18,7 @@ To revise a saved binding, use `/binding edit <id> <requirement>`. Its storage `
|
|
|
18
18
|
|
|
19
19
|
## Draft panel
|
|
20
20
|
|
|
21
|
-
A new draft
|
|
21
|
+
A new draft floats above the composer. Click the title bar or use Enter/Space to collapse and expand it without moving the conversation or composer. Long content scrolls inside the panel. When space above the composer is limited, the preview shows only its header and enlarge action. If even the header cannot fit, the composer menu and historical request keep the draft entry; manual draft entries always open the large dialog. Choose **Enlarge binding draft** for a large review dialog with independently scrolling content and save/discard actions kept at the bottom. The header keeps resize and close controls spaced apart. Escape, the backdrop, or **Shrink binding draft** returns to preview without saving or discarding; if preview cannot fit, focus returns to the composer entry. The separate close button hides the draft panel, which can be reopened from the sparkle menu.
|
|
22
22
|
|
|
23
23
|
- **Save as disabled**: keep the entry in the global list without loading it.
|
|
24
24
|
- **Save and enable**: save it and allow subsequent calls to load it.
|
|
@@ -27,7 +27,7 @@ A new draft opens above the composer. Click the title bar or use Enter/Space to
|
|
|
27
27
|
|
|
28
28
|
The sparkle button shows a delayed PTC Plus tooltip identifying it as the global bindings and settings entry; a pending draft changes it to the draft state. The tooltip stays hidden while the menu, management dialog, or settings dialog is open. The button shows a badge for pending drafts. Open its menu by hovering, clicking, or using the keyboard, then select the draft to show it again. Each session retains one current candidate. A confirmed save or discard closes the panel; failures leave the draft available for retry.
|
|
29
29
|
|
|
30
|
-
The sparkle menu is available before the first message and opens once the pointer dwells on the entry; incidental pointer movement across the composer leaves it closed. A hovered menu closes as soon as the pointer leaves it; clicking keeps it open until you click again, click elsewhere, or press Escape. Global bindings
|
|
30
|
+
The sparkle menu is available before the first message and opens once the pointer dwells on the entry; incidental pointer movement across the composer leaves it closed. A hovered menu closes as soon as the pointer leaves it; clicking keeps it open until you click again, click elsewhere, or press Escape. Global bindings are grouped by enabled state with counts. Enabled entries appear first; each menu opening starts with the disabled group collapsed. Click the group or press Enter/Space to expand it. Entries retain their names, purposes, and status text. Only the catalog scrolls; authoring, revision, management, and settings actions stay at the bottom, as does Back in the revision step. Click an entry to toggle it for all sessions. The catalog refreshes in the background, and the entries stay usable while it does; writes are serialized, so an entry stays usable during an in-flight write and a duplicate toggle is dropped rather than queued, and you reload after a conflict or failure before trying again. After a toggle, focus follows the regrouped entry, or returns to the disabled group button if that group hides the entry; focus outside the menu is left alone. Recovery scrolls only the binding list to reveal the target, leaving the footer and conversation in place. **Manage global bindings** opens the full workbench. **Write a new binding** and **Revise a binding** appear when the authoring command is available; the revision step lists existing entries and prefills `/binding edit <id> `. The reusable REPL binding list shows how many later cells reused each binding, with the total in the section title; the count comes from static references in each later cell's compiled source, and a redeclaration counts as neither a reuse nor a reset of the accumulated count.
|
|
31
31
|
|
|
32
32
|
The original binding request retains its requirement, status, and accepted source in history. Historical inspection does not restore permission to save that draft. When another edit changes the catalog, reload its current state before deciding whether to save.
|
|
33
33
|
|
package/docs/user-bindings.md
CHANGED
|
@@ -18,16 +18,16 @@
|
|
|
18
18
|
|
|
19
19
|
## 草稿面板
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
新草稿自动悬浮在输入框上方。点击标题栏或使用 Enter/Space 折叠、展开,不挤动对话和输入框;长内容可在面板内滚动;输入框上方空间不足时只显示标题栏,仍可放大查看;连标题也放不下时保留星光菜单和历史请求的草稿入口,手动草稿入口始终直接打开大窗口。点击 **放大查看绑定草稿** 打开大尺寸审阅窗口,正文独立滚动,底部始终保留保存与丢弃按钮。标题栏的尺寸切换与关闭按钮之间留有间距。按 Escape、点击遮罩或 **缩小显示绑定草稿** 返回预览;预览没有可用空间时焦点回到星光入口,不保存或丢弃草稿。独立关闭按钮隐藏草稿面板,可从星光菜单重新打开。
|
|
22
22
|
|
|
23
23
|
- **保存为停用**:保存到全局列表,暂不加载。
|
|
24
24
|
- **保存并启用**:保存并允许后续调用加载。
|
|
25
25
|
- **丢弃草稿**:放弃这份未保存候选。
|
|
26
26
|
- **关闭面板**:只隐藏面板,不保存或丢弃。
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
输入框星光按钮显示待处理草稿的角标。悬停、点击或键盘打开菜单后,选择草稿即可在大窗口中重新审阅。每个会话保留一份当前候选。保存或丢弃确认成功后面板关闭;失败时保留草稿供重试。
|
|
29
29
|
|
|
30
|
-
星光按钮的 PTC Plus 提示延迟显示,说明它是全局绑定与设置入口;有待处理草稿时改为提示草稿状态。菜单、管理窗口或设置窗口打开时隐藏提示,避免遮挡。星光菜单在首条消息发出前即可使用,鼠标停留一下才展开,扫过输入区不会弹出。悬停打开后鼠标移开菜单即自动关闭;点击可让菜单保持常开,再点一次、点击别处或按 Escape
|
|
30
|
+
星光按钮的 PTC Plus 提示延迟显示,说明它是全局绑定与设置入口;有待处理草稿时改为提示草稿状态。菜单、管理窗口或设置窗口打开时隐藏提示,避免遮挡。星光菜单在首条消息发出前即可使用,鼠标停留一下才展开,扫过输入区不会弹出。悬停打开后鼠标移开菜单即自动关闭;点击可让菜单保持常开,再点一次、点击别处或按 Escape 关闭。全局绑定按启用、停用分组并显示数量,已启用条目优先展示,停用组每次打开菜单时默认折叠,可点击或用 Enter/Space 展开;条目保留名称、用途与状态文字。只有绑定列表滚动,底部编写、修改、管理与设置操作固定可见,“修改绑定”第二步的“返回”也固定在底部;点击条目切换启停,配置对所有会话生效。目录在后台刷新,刷新期间沿用上一次的列表并可以继续操作;切换写入逐条串行化,写入进行中条目保持可用,重复请求被直接丢弃;发生冲突或失败时可重新加载后再操作。启停后条目进入对应分组;焦点仍在菜单中时跟随该条目,若条目进入折叠的停用组则回到组按钮,不抢走菜单外的焦点。焦点恢复仅滚动绑定列表来显示目标,不移动底栏或对话。“管理全局绑定”打开完整工作台;编写命令可用时显示“编写新绑定”和“修改绑定”,“修改绑定”第二步从已有条目中选择后预填 `/binding edit <id> `。REPL 可复用绑定列表会显示每项被后续 cell 复用的次数,并在区块标题显示总复用次数;次数按后续 cell 编译后源码中的静态引用统计,重定义或重声明本身不算一次复用,也不会把已累计的次数清零。
|
|
31
31
|
|
|
32
32
|
历史中的“绑定编写”请求保留需求、状态和接受时的源码。它供查看当时内容,不重新获得保存资格。目录发生并发修改时,重新加载最新状态后再决定是否保存。
|
|
33
33
|
|
|
@@ -24,6 +24,32 @@ owns command syntax, thresholds, generated evidence, and the final gate.
|
|
|
24
24
|
- Node test options may follow a standalone `--` after the focused source and
|
|
25
25
|
test selections.
|
|
26
26
|
|
|
27
|
+
Compiler instrumentation belongs to the test runner, not the compiler service.
|
|
28
|
+
The runner prepares bytecode without coverage, then preloads
|
|
29
|
+
`scripts/instrument-compiler-coverage.mjs` before the compiler captures Node's
|
|
30
|
+
`Script` constructor. Only the exact compiler bundle bypasses cached bytecode
|
|
31
|
+
consumption during that run; cache export uses the prepared, uninstrumented
|
|
32
|
+
bytes. The real-worker testing entry installs the same seam before importing
|
|
33
|
+
the production worker, removes its private entry locator and bytecode-path
|
|
34
|
+
environment variable, restores the ordinary worker's `process.argv[1]` through
|
|
35
|
+
the worker realm restoration boundary, and then runs the unchanged worker. Production compilation
|
|
36
|
+
does not read `NODE_V8_COVERAGE` or `DSH_PTC_COMPILER_BYTECODE` and never opens a
|
|
37
|
+
file selected by those variables.
|
|
38
|
+
|
|
39
|
+
The testing `Script` seam preserves the compiler's option prototype and property
|
|
40
|
+
descriptors while disabling only `cachedData`. Ordinary scripts keep their
|
|
41
|
+
original options. Descriptor conversion uses the shared compiler descriptor
|
|
42
|
+
owner; the seam captures its required primordials before user execution so
|
|
43
|
+
caller prototype and static-method mutations cannot reach private compilation.
|
|
44
|
+
Prepared cache export copies typed-array internal data through a captured native
|
|
45
|
+
constructor, without invoking public length getters on its private snapshot.
|
|
46
|
+
The shared compiler platform also uses captured native byte operations for
|
|
47
|
+
encoding and decoding, so first-use TypeScript lowering cannot invoke a caller's
|
|
48
|
+
replacement typed-array length getter in either ordinary or covered execution.
|
|
49
|
+
Covered and ordinary workers use the same absolute entry
|
|
50
|
+
normalization for URL objects, file-URL strings, and filenames; the testing
|
|
51
|
+
preload does not independently reinterpret a caller's entry.
|
|
52
|
+
|
|
27
53
|
## Workflow
|
|
28
54
|
|
|
29
55
|
Use focused coverage after the nearest behavioral test passes or after a full
|
package/docs/verification.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
## Incremental independent review
|
|
6
6
|
|
|
7
|
+
The standard local and CI gates run `test:client:visual`: one Client suite exports fresh renderer fixtures, followed by deterministic browser layout and visual assertions. Install its locked browser first with `npx playwright install --with-deps chromium`; missing browsers fail verification. The [Client visual contract](client-visual-contract.md) owns the bounded control/state inventory, reference palettes, fault counterexamples and official-Host evidence limits. Focused diagnostics may run `npm run test:client:visual` independently; they do not replace the final gate.
|
|
8
|
+
|
|
7
9
|
Independent review is partitioned before reviewers start. Create the ignored working plan, replace every placeholder, then record it against the commit that defines the change scope:
|
|
8
10
|
|
|
9
11
|
```bash
|
|
@@ -41,6 +43,10 @@ The final verdict uses `dsh-review-verdict/v2`. Pre-commit verifies its plan has
|
|
|
41
43
|
|
|
42
44
|
`npm run verify` and `npm run check` validate the ledger, compare generated bundles, check syntax, run Client tests, and run all backend tests with coverage. Backend test files run in separate Node processes. The default process count is half the available CPUs, rounded down and bounded between one and four; each process can also own runtime Workers. Tests within each file retain their existing ordering. Coverage still requires 100% lines and functions and at least 95% branches, including compiler source remapping. The graded merge additionally asserts that the plugin-source closure it claims — `index.js` and every `internal/*.js` owner — appears in the merged report, so a module no instrumented process loads fails the gate instead of silently leaving it. Generated artifacts stay outside that closure: the root `compiler-*.cjs` bundles and the `internal/*.cjs` shims are remapped onto their original `internal/` sources or verified by the bundle checks, and `scripts/migrate-session-log.mjs` is verified by its own migration suite.
|
|
43
45
|
|
|
46
|
+
Coverage, the exhaustive backend suite, and the review-bound proof belong to the local pre-commit `npm run check`. CI starts from a clean lockfile install and runs `verify:ci` on every supported Node and operating-system entry: frozen-baseline validation, semantic ownership, generated builds, Client tests, and focused runtime contracts covering compiler startup, helper-process lifecycle, plugin takeover, native-call transport, and durable argument diagnostics. Ubuntu Node 24 additionally exercises the packed plugin against both frozen official `latest` and `next` DSH graphs. CI therefore proves portable installation and runtime integration without repeating coverage or the exhaustive semantic suite on hosted runners.
|
|
47
|
+
|
|
48
|
+
`host:baseline:verify`, which is part of deterministic verification, is offline. It requires the installed root DSH `next` cohort to match `package-lock.json` and validates the tracked `compat/latest` declaration and lock without installing it. CI separately runs `test:host-contract`: the root graph exercises `next`, while a temporary consumer created from `compat/latest/package-lock.json` exercises `latest` against the packed plugin. Registry tag lookup and lock regeneration belong only to `host:baseline:update` and the scheduled baseline-refresh workflow. That workflow publishes a changed frozen candidate as a pull request and explicitly dispatches this CI workflow once because its `GITHUB_TOKEN`-authored events cannot start another run; ordinary gates never install a floating channel.
|
|
49
|
+
|
|
44
50
|
Use focused coverage while developing or after a full gate identifies a narrow coverage failure. Select every source whose threshold matters and the smallest test set that can distinguish the correction:
|
|
45
51
|
|
|
46
52
|
```bash
|
|
@@ -73,7 +79,7 @@ Coverage runs additionally set `DSH_PTC_TEST_WORKER_COVERAGE=1` and load `script
|
|
|
73
79
|
|
|
74
80
|
`npm run coverage` and `npm run coverage:focus` print their current stage and total elapsed time. Each invocation collects worker coverage in its own temporary directory under `coverage/`, removed after c8 finishes reporting, so simultaneous runs cannot erase or combine each other's evidence. The default report is printed to the terminal. A separate `coverage/run-*.tap` log retains Node's test diagnostics, including process exit codes and signals that the concise terminal reporter can omit. An explicit Node test reporter option replaces these default reporters. Source changes during verification still invalidate a `check` proof; separate coverage directories do not make concurrent source edits safe.
|
|
75
81
|
|
|
76
|
-
Windows Node 24 and 26 reproductions and stacks identify a cppgc finalizer (`node::contextify::ContextifyScript`, or `ContextifyContext` when coverage was enabled) running after the environment that owned its private realm was gone, which wrote through a dangling `node::Realm` during isolate teardown. The reproduction ended the whole DSH process when an in-host session worker was terminated; macOS runners also observed whole-file aborts under coverage, but the available evidence does not establish that those aborts came from the same Windows stack or that the same cause applies across platforms. The session kernel now runs in a plugin-owned helper process (`internal/kernel-child.js`); `IsolatedWorker` sends a cooperative shutdown and, if the helper does not leave within its bound, kills only that helper. DSH is no longer the worker-teardown target on any supported host, so a wedged kernel is contained as a helper failure rather than a host-native crash.
|
|
82
|
+
Windows Node 24 and 26 reproductions and stacks identify a cppgc finalizer (`node::contextify::ContextifyScript`, or `ContextifyContext` when coverage was enabled) running after the environment that owned its private realm was gone, which wrote through a dangling `node::Realm` during isolate teardown. The reproduction ended the whole DSH process when an in-host session worker was terminated; macOS runners also observed whole-file aborts under coverage, but the available evidence does not establish that those aborts came from the same Windows stack or that the same cause applies across platforms. The session kernel now runs in a plugin-owned helper process (`internal/kernel-child.js`); `IsolatedWorker` sends a cooperative shutdown and, if the helper does not leave within its bound, kills only that helper. DSH is no longer the worker-teardown target on any supported host, so a wedged kernel is contained as a helper failure rather than a host-native crash. The former file-level retry wrapper is gone, and any failing CI smoke or local exhaustive test fails its run. This boundary removes the plugin's in-host kill exposure; it neither repairs the upstream Node path nor proves the macOS abort shared the Windows cause.
|
|
77
83
|
|
|
78
84
|
Native oracle workers and external Windows command fixtures explicitly disable inherited `NODE_V8_COVERAGE`; they execute no code included by the plugin coverage gate. Subprocesses testing plugin implementations still collect coverage. PowerShell semantic-version observations share one invocation, and Windows registry fixtures read the three effective registry values with one real npm configuration query. Each retains its original inputs and assertions.
|
|
79
85
|
|
package/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import Schema from '@deepseek-ai/schemastery'
|
|
|
10
10
|
import { randomUUID } from 'node:crypto'
|
|
11
11
|
import { createDirectSurfaceOwner } from './internal/direct-surface-owner.js'
|
|
12
12
|
import { createRuntimeMessageOwner } from './internal/runtime-contexts.js'
|
|
13
|
+
import { createSessionLogOwner } from './internal/session-log-view.js'
|
|
13
14
|
import { PTC_DELIVERY_CONTEXT } from './internal/runtime-messages.js'
|
|
14
15
|
import { createCordisToolsOwner } from './internal/cordis-tools-owner.js'
|
|
15
16
|
import { createEditTransportOwner, EDIT_RUN_CODE } from './internal/edit-transport-owner.js'
|
|
@@ -50,7 +51,7 @@ export const Config = Schema.object(Object.fromEntries(
|
|
|
50
51
|
* it under the service name of one generation, so `installExecutionSeam`
|
|
51
52
|
* selects the live one.
|
|
52
53
|
*/
|
|
53
|
-
export const inject = ['tools', 'systemPrompt', 'agents'
|
|
54
|
+
export const inject = ['tools', 'systemPrompt', 'agents']
|
|
54
55
|
|
|
55
56
|
function replGuidance({ bindingPolicy, rewritesEnabled, languageSemantics, durableReplay, cordisToolsEnabled }) {
|
|
56
57
|
const looseTopLevelRedeclarations = bindingPolicy.variableRedeclarations
|
|
@@ -100,7 +101,7 @@ Native tool availability, executable names, shells, and path syntax depend on th
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
/** Register the session-bound REPL runtime. */
|
|
103
|
-
function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessionId) {
|
|
104
|
+
function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessionId, sessionLog) {
|
|
104
105
|
const presentationGeneration = randomUUID()
|
|
105
106
|
const replMemoryProjection = createReplMemoryProjection(presentationGeneration)
|
|
106
107
|
const userBindingDraftProjection = createUserBindingDraftProjection(presentationGeneration)
|
|
@@ -331,6 +332,7 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
331
332
|
ready = awaitCordisToolsOwner(initialCordisTools, new Promise(() => {}))
|
|
332
333
|
}
|
|
333
334
|
userBindings = createUserBindingsOwner(ctx, {
|
|
335
|
+
readSessionLog: sessionLog.read,
|
|
334
336
|
enabled: activeConfig.userBindingsEnabled,
|
|
335
337
|
draftProjectionAvailable,
|
|
336
338
|
maxWallMs: activeConfig.maxWallMs,
|
|
@@ -342,6 +344,7 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
342
344
|
ctx,
|
|
343
345
|
seam,
|
|
344
346
|
observeSession: id => observationInterest.has(id),
|
|
347
|
+
readSessionLog: sessionLog.read,
|
|
345
348
|
sessionConfig: activeConfig,
|
|
346
349
|
userBindingsCwd: userBindings.cwd,
|
|
347
350
|
maxNestedRunCodeDepth: activeConfig.maxNestedRunCodeDepth,
|
|
@@ -352,6 +355,8 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
352
355
|
bindingSubmissionForAgent: (agent, ensureLease, onAccepted) => userBindings.submissionForAgent(agent, ensureLease, onAccepted),
|
|
353
356
|
})
|
|
354
357
|
editTransport = createEditTransportOwner(ctx, {
|
|
358
|
+
readSessionLog: runtimeBridge.readSubmissionLog,
|
|
359
|
+
sessionLogView: sessionLog.project,
|
|
355
360
|
durableReplay: activeConfig.durableReplay,
|
|
356
361
|
executeTentative: runtimeBridge.executeTentative,
|
|
357
362
|
presentationGeneration,
|
|
@@ -359,6 +364,7 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
359
364
|
toolSchemasForAgent,
|
|
360
365
|
})
|
|
361
366
|
directSurface = createDirectSurfaceOwner({
|
|
367
|
+
sessionLogView: sessionLog.project,
|
|
362
368
|
editTransport,
|
|
363
369
|
runtimeConfig: activeConfig,
|
|
364
370
|
canonicalizeToolCalls: activeConfig.canonicalizeToolCalls,
|
|
@@ -395,6 +401,7 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
395
401
|
))
|
|
396
402
|
.then(result => directSurface.argumentDiagnostic(exec, result))
|
|
397
403
|
}
|
|
404
|
+
if (exec.name === EDIT_RUN_CODE) return runtimeBridge.withSubmission(exec, next)
|
|
398
405
|
return next()
|
|
399
406
|
}))
|
|
400
407
|
disposers.push(ctx.on('tools/result', (exec, result) => {
|
|
@@ -527,6 +534,7 @@ export const apply = (ctx, config = {}) => {
|
|
|
527
534
|
|
|
528
535
|
/** Install the plugin into the scope of the seam the host registered. */
|
|
529
536
|
function applyWithExecutionSeam(ctx, resolvedConfig, seam, volatileHost, volatileConfig) {
|
|
537
|
+
const sessionLog = createSessionLogOwner(ctx)
|
|
530
538
|
const toolSchemasForAgent = agent => typeof ctx.tools.schemas === 'function'
|
|
531
539
|
? ctx.tools.schemas(agent)
|
|
532
540
|
: []
|
|
@@ -613,7 +621,7 @@ function applyWithExecutionSeam(ctx, resolvedConfig, seam, volatileHost, volatil
|
|
|
613
621
|
}
|
|
614
622
|
let candidate
|
|
615
623
|
try {
|
|
616
|
-
candidate = installPtCRuntime(ctx, record.config, seam, toolSchemasForAgent, sessionId)
|
|
624
|
+
candidate = installPtCRuntime(ctx, record.config, seam, toolSchemasForAgent, sessionId, sessionLog)
|
|
617
625
|
record.runtime = candidate
|
|
618
626
|
} catch (error) {
|
|
619
627
|
const failedRuntime = error?.[INSTALL_CLEANUP]
|
|
@@ -757,7 +765,7 @@ function applyWithExecutionSeam(ctx, resolvedConfig, seam, volatileHost, volatil
|
|
|
757
765
|
|
|
758
766
|
const messageOwner = createRuntimeMessageOwner(context => (
|
|
759
767
|
committed?.retiring ? [] : committed?.runtime.contextsForRequest(context) ?? []
|
|
760
|
-
))
|
|
768
|
+
), sessionLog.project)
|
|
761
769
|
ctx.effect(() => ctx.systemPrompt.context({
|
|
762
770
|
name: PTC_DELIVERY_CONTEXT, order: 98, text: '',
|
|
763
771
|
}), 'ptc-plus dynamic message delivery witness')
|
|
@@ -788,7 +796,7 @@ function applyWithExecutionSeam(ctx, resolvedConfig, seam, volatileHost, volatil
|
|
|
788
796
|
? { enabled: false }
|
|
789
797
|
: Object.fromEntries(CONFIG_FIELDS.map(field => [field.key, previousConfig[field.key]]))
|
|
790
798
|
try {
|
|
791
|
-
await settingsWriter.update(
|
|
799
|
+
await settingsWriter.update(patch)
|
|
792
800
|
} catch (rollbackError) {
|
|
793
801
|
reportActivationFailure(new Error(
|
|
794
802
|
`ptc-plus: failed to roll back runtime configuration: ${rollbackError.message}`,
|
|
@@ -840,11 +848,24 @@ function applyWithExecutionSeam(ctx, resolvedConfig, seam, volatileHost, volatil
|
|
|
840
848
|
},
|
|
841
849
|
onChange: reconcile,
|
|
842
850
|
},
|
|
843
|
-
onProvider(
|
|
844
|
-
settingsWriter =
|
|
851
|
+
onProvider(_provider, writer) {
|
|
852
|
+
settingsWriter = writer
|
|
853
|
+
return () => { if (settingsWriter === writer) settingsWriter = undefined }
|
|
845
854
|
},
|
|
846
855
|
})
|
|
847
856
|
}
|
|
857
|
+
ctx.effect(() => seam.onSourceChange(() => {
|
|
858
|
+
const current = resolveConfig(configSource())
|
|
859
|
+
if (!current.enabled) return undefined
|
|
860
|
+
if (seam.language !== 'typescript') {
|
|
861
|
+
const language = seam.language
|
|
862
|
+
const cleanup = controller.uninstall()
|
|
863
|
+
reportActivationFailure(new Error('ptc-plus: unsupported code runtime language '
|
|
864
|
+
+ JSON.stringify(language) + '; only "typescript" is supported'))
|
|
865
|
+
return cleanup
|
|
866
|
+
}
|
|
867
|
+
return reconcile(true)
|
|
868
|
+
}), 'ptc-plus original language lifecycle')
|
|
848
869
|
watchVolatileConfig(volatileHost, ctx, volatileConfig, () => reconcile())
|
|
849
870
|
if (configurationGeneration === 0) return reconcile(settingsWriter === undefined)
|
|
850
871
|
}
|
|
@@ -20,6 +20,7 @@ const nativePathToFileURL = pathToFileURL
|
|
|
20
20
|
const nativeStripTypeScriptTypes = stripTypeScriptTypes
|
|
21
21
|
const runInContext = Script.prototype.runInContext
|
|
22
22
|
const NativeUint8Array = Uint8Array
|
|
23
|
+
const typedArrayLength = descriptor(Object.getPrototypeOf(NativeUint8Array.prototype), 'length').get
|
|
23
24
|
const NativeWeakRef = WeakRef, deref = WeakRef.prototype.deref
|
|
24
25
|
const NativeError = Error
|
|
25
26
|
const parseJson = JSON.parse
|
|
@@ -65,11 +66,12 @@ export const compilerPlatformBridge = {
|
|
|
65
66
|
fileURLToPath: input => nativeFileURLToPath(nativeUrl(input)),
|
|
66
67
|
pathToFileURL: input => ownUrl(nativePathToFileURL(input)),
|
|
67
68
|
encode(source, encoding) {
|
|
68
|
-
const
|
|
69
|
-
const
|
|
69
|
+
const size = byteLength(source, encoding)
|
|
70
|
+
const buffer = new NativeUint8Array(size)
|
|
71
|
+
const length = apply(codecs[encoding].write, buffer, [source, 0, size])
|
|
70
72
|
return { buffer, length }
|
|
71
73
|
},
|
|
72
|
-
decode: (buffer, encoding) => apply(codecs[encoding].slice, buffer, [0, buffer
|
|
74
|
+
decode: (buffer, encoding) => apply(codecs[encoding].slice, buffer, [0, apply(typedArrayLength, buffer, [])]),
|
|
73
75
|
textEncoder() {
|
|
74
76
|
const encoder = new NativeTextEncoder()
|
|
75
77
|
return { encode: source => apply(encoderEncode, encoder, [source]),
|
|
@@ -2,7 +2,6 @@ import { readFileSync } from 'node:fs'
|
|
|
2
2
|
import { constants, createContext, Script } from 'node:vm'
|
|
3
3
|
import { fileURLToPath } from 'node:url'
|
|
4
4
|
import { workerData } from 'node:worker_threads'
|
|
5
|
-
import { deserialize } from 'node:v8'
|
|
6
5
|
import { copyCompilerData } from './compiler-data.js'
|
|
7
6
|
import { PreflightError } from './cell-analysis-contract.js'
|
|
8
7
|
import { ModuleRewriteError } from './cell-error.js'
|
|
@@ -29,10 +28,7 @@ const define = compilerDescriptors.defineProperty
|
|
|
29
28
|
const keys = Object.keys
|
|
30
29
|
let compiler
|
|
31
30
|
let compilerScript
|
|
32
|
-
|
|
33
|
-
const bytecodeFile = process.env.DSH_PTC_COMPILER_BYTECODE
|
|
34
|
-
let workerCache = bytecodeFile ? deserialize(readFileSync(bytecodeFile)) : undefined
|
|
35
|
-
if (workerCache?.source !== compilerSource) workerCache = undefined
|
|
31
|
+
let workerCache
|
|
36
32
|
// Bytecode contains no compiler realm or program state. Match the complete
|
|
37
33
|
// source: V8's cache validation alone does not prove source text equality.
|
|
38
34
|
let cachedData = workerData?.compilerCache?.source === compilerSource
|
|
@@ -43,9 +39,6 @@ if (workerData !== null && typeof workerData === 'object') delete workerData.com
|
|
|
43
39
|
/** Reuse compiled compiler code across fresh workers, never their realm state. */
|
|
44
40
|
export function compilerWorkerCache() {
|
|
45
41
|
if (compilerScript === undefined) return undefined
|
|
46
|
-
// Instrumented compiler instances neither produce nor consume bytecode.
|
|
47
|
-
// Coverage's runner supplies a snapshot from its uninstrumented preparation.
|
|
48
|
-
if (workerCache === undefined && coverageEnabled) return undefined
|
|
49
42
|
workerCache ??= { source: compilerSource, data: apply(createCachedData, compilerScript, []) }
|
|
50
43
|
return { source: workerCache.source, data: new NativeUint8Array(workerCache.data) }
|
|
51
44
|
}
|
|
@@ -58,7 +51,7 @@ function compilerEntry() {
|
|
|
58
51
|
// Workers can execute host-prepared cells without compiling dynamic source.
|
|
59
52
|
// Capture source and platform before user code, but parse only on first use.
|
|
60
53
|
compilerScript = new NativeScript(compilerSource, { __proto__: null, filename, displayErrors: false,
|
|
61
|
-
cachedData
|
|
54
|
+
cachedData })
|
|
62
55
|
cachedData = undefined
|
|
63
56
|
const context = nativeCreateContext(dontContextify)
|
|
64
57
|
define(context, '__compilerBaseUrl', { value: import.meta.url })
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as CordisTools from '@deepseek-ai/dsh-tool-cordis'
|
|
2
2
|
import * as CordisSkillFilesystem from '@deepseek-ai/dsh-skill-filesystem'
|
|
3
|
+
import { existsSync } from 'node:fs'
|
|
4
|
+
import { createRequire } from 'node:module'
|
|
3
5
|
import { dirname, isAbsolute, join } from 'node:path'
|
|
4
6
|
import { isDeepStrictEqual } from 'node:util'
|
|
5
7
|
import { RUN_CODE } from './runtime-bridge-owner.js'
|
|
@@ -8,6 +10,16 @@ const CORDIS_PRESET_ID = 'cordis'
|
|
|
8
10
|
const CORDIS_SKILL_NAME = 'cordis-plugin-development'
|
|
9
11
|
const CORDIS_SKILL_PROVIDER = 'ptc-plus-cordis'
|
|
10
12
|
const COMPANION_SKILL_TOOL = 'skill'
|
|
13
|
+
const COMPANION_PACKAGE_LAYOUTS = Object.freeze([
|
|
14
|
+
Object.freeze({
|
|
15
|
+
name: '@deepseek-ai/dsh-agent-preset',
|
|
16
|
+
skillRoot: directory => join(directory, 'skills'),
|
|
17
|
+
}),
|
|
18
|
+
Object.freeze({
|
|
19
|
+
name: '@deepseek-ai/dsh-agent-presets',
|
|
20
|
+
skillRoot: directory => join(directory, 'presets', CORDIS_PRESET_ID, 'skills'),
|
|
21
|
+
}),
|
|
22
|
+
])
|
|
11
23
|
|
|
12
24
|
function exactCompanionCandidates(observation) {
|
|
13
25
|
if (Array.isArray(observation)) {
|
|
@@ -131,25 +143,52 @@ function requireCompanionServices(agent) {
|
|
|
131
143
|
return { agentPresets, skills }
|
|
132
144
|
}
|
|
133
145
|
|
|
134
|
-
|
|
146
|
+
function containsCompanionSkill(directory, fileExists) {
|
|
147
|
+
return fileExists(join(directory, CORDIS_SKILL_NAME, 'SKILL.md'))
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Resolve the official Cordis companion Skill across DSH preset generations. */
|
|
151
|
+
export async function resolveCompanionSkillDirectory(agentPresets, options = {}) {
|
|
135
152
|
const preset = await agentPresets.resolve(CORDIS_PRESET_ID)
|
|
136
|
-
|
|
153
|
+
const resolution = typeof options === 'function' ? options() : options
|
|
154
|
+
const fileExists = resolution.fileExists ?? existsSync
|
|
155
|
+
if (typeof preset?.broken === 'string' && preset.broken.length > 0) {
|
|
137
156
|
throw new Error(`ptc-plus: DSH cordis preset is unavailable: ${preset.broken}`)
|
|
138
157
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
158
|
+
if (preset?.path !== undefined) {
|
|
159
|
+
if (typeof preset.path !== 'string' || !isAbsolute(preset.path)) {
|
|
160
|
+
throw new Error('ptc-plus: DSH cordis preset did not expose an absolute composition path')
|
|
161
|
+
}
|
|
162
|
+
const directory = join(dirname(preset.path), 'skills')
|
|
163
|
+
if (!containsCompanionSkill(directory, fileExists)) {
|
|
164
|
+
throw new Error(`ptc-plus: DSH cordis preset does not contain ${CORDIS_SKILL_NAME}/SKILL.md beside its composition`)
|
|
165
|
+
}
|
|
166
|
+
return directory
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
for (const layout of COMPANION_PACKAGE_LAYOUTS) {
|
|
170
|
+
const pkg = resolution.packageOf?.(layout.name, resolution.packageBaseUrl)
|
|
171
|
+
if (pkg === undefined) continue
|
|
172
|
+
if (pkg?.name !== layout.name || typeof pkg.dir !== 'string' || !isAbsolute(pkg.dir)) {
|
|
173
|
+
throw new Error(`ptc-plus: DSH pluginPackages returned an invalid ${layout.name} package resource`)
|
|
174
|
+
}
|
|
175
|
+
const directory = layout.skillRoot(pkg.dir)
|
|
176
|
+
if (containsCompanionSkill(directory, fileExists)) return directory
|
|
148
177
|
}
|
|
149
|
-
|
|
150
|
-
|
|
178
|
+
|
|
179
|
+
if (resolution.packageOf === undefined && resolution.resolveLegacyPackageManifest !== undefined) {
|
|
180
|
+
let manifest
|
|
181
|
+
try {
|
|
182
|
+
manifest = resolution.resolveLegacyPackageManifest('@deepseek-ai/dsh-agent-presets/package.json')
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (error?.code !== 'MODULE_NOT_FOUND' && error?.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') throw error
|
|
185
|
+
}
|
|
186
|
+
if (manifest !== undefined) {
|
|
187
|
+
const directory = join(dirname(manifest), 'presets', CORDIS_PRESET_ID, 'skills')
|
|
188
|
+
if (containsCompanionSkill(directory, fileExists)) return directory
|
|
189
|
+
}
|
|
151
190
|
}
|
|
152
|
-
|
|
191
|
+
throw new Error(`ptc-plus: the active DSH installation does not publish a loadable ${CORDIS_SKILL_NAME} companion Skill through its package-resolution surface`)
|
|
153
192
|
}
|
|
154
193
|
|
|
155
194
|
async function requireCompanionSkill(agent, skills) {
|
|
@@ -270,6 +309,7 @@ export function createCordisToolsOwner(
|
|
|
270
309
|
ctx,
|
|
271
310
|
cordisPlugin = CordisTools,
|
|
272
311
|
skillFilesystemPlugin = CordisSkillFilesystem,
|
|
312
|
+
companionResolution = undefined,
|
|
273
313
|
) {
|
|
274
314
|
if (typeof ctx?.agents?.list !== 'function') {
|
|
275
315
|
throw new Error('ptc-plus: cordisToolsEnabled requires the DSH agents.list API')
|
|
@@ -281,6 +321,24 @@ export function createCordisToolsOwner(
|
|
|
281
321
|
const cordisInspectLeases = createCordisInspectLeases(ctx)
|
|
282
322
|
const scopedCordisPlugin = cordisInspectLeases.plugin(cordisPlugin)
|
|
283
323
|
const scopedSkillPlugin = exactCompanionSkillPlugin(skillFilesystemPlugin)
|
|
324
|
+
const packageBaseUrl = ctx.baseUrl
|
|
325
|
+
const skillResolution = companionResolution ?? (() => {
|
|
326
|
+
const pluginPackages = ctx.get?.('pluginPackages')
|
|
327
|
+
return {
|
|
328
|
+
packageOf: pluginPackages === undefined
|
|
329
|
+
? undefined
|
|
330
|
+
: (name, parentURL) => {
|
|
331
|
+
if (typeof pluginPackages?.packageOf !== 'function') {
|
|
332
|
+
throw new Error('ptc-plus: cordisToolsEnabled requires the DSH pluginPackages.packageOf API')
|
|
333
|
+
}
|
|
334
|
+
return pluginPackages.packageOf(name, parentURL)
|
|
335
|
+
},
|
|
336
|
+
packageBaseUrl,
|
|
337
|
+
resolveLegacyPackageManifest: pluginPackages === undefined && typeof packageBaseUrl === 'string'
|
|
338
|
+
? specifier => createRequire(packageBaseUrl).resolve(specifier)
|
|
339
|
+
: undefined,
|
|
340
|
+
}
|
|
341
|
+
})
|
|
284
342
|
let disposed = false
|
|
285
343
|
|
|
286
344
|
const leafErrors = error => error instanceof AggregateError
|
|
@@ -364,13 +422,14 @@ export function createCordisToolsOwner(
|
|
|
364
422
|
mounts.set(agent, mount)
|
|
365
423
|
mount.activation = Promise.resolve().then(async () => {
|
|
366
424
|
const { agentPresets, skills } = requireCompanionServices(agent)
|
|
367
|
-
const skillDirectory = await
|
|
425
|
+
const skillDirectory = await resolveCompanionSkillDirectory(agentPresets, skillResolution)
|
|
368
426
|
if (mount.disposed) return
|
|
369
427
|
|
|
370
428
|
const skillFiber = agent.ctx.plugin(scopedSkillPlugin, {
|
|
371
429
|
providerName: CORDIS_SKILL_PROVIDER,
|
|
372
430
|
includeDefaultRoots: false,
|
|
373
|
-
|
|
431
|
+
bundledSkillDir: skillDirectory,
|
|
432
|
+
watch: false,
|
|
374
433
|
})
|
|
375
434
|
if (typeof skillFiber?.dispose !== 'function') {
|
|
376
435
|
throw new Error('ptc-plus: DSH Context.plugin did not return a disposable Cordis Skill fiber')
|