dsh-as-aistudio 0.1.0 → 0.2.0

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,6 @@
1
1
  # dsh-as-aistudio
2
2
 
3
- **把 DeepSeek Harness 用成 Google AI Studio —— 一轮对话就是一个可以反复打磨的 prompt playground。** 改一句提示词并重跑、原位重跑某条回复、把不该留下的那一轮从模型上下文里删掉、让发出的提示词按 Markdown 渲染。这四个能力各自是一个独立插件,本包把它们组合成一次安装,并在「设置 → 插件 → AI Studio」里如实报告哪几个真的活着。
3
+ **把 DeepSeek Harness 用成 Google AI Studio —— 一轮对话就是一个可以反复打磨的 prompt playground,而且插件列表里只占一行。** 改一句提示词并重跑、原位重跑某条回复、把不该留下的那一轮从模型上下文里删掉、让发出的提示词按 Markdown 渲染。
4
4
 
5
5
  [中文](#中文) · [English](#english)
6
6
 
@@ -10,77 +10,57 @@
10
10
 
11
11
  ### 为什么需要它
12
12
 
13
- AI Studio 的好用之处不在模型,在于**一轮对话中的每一步都能回头改**:提示词写错了就地改完再跑一次、这次答得不好原地重跑、这段不该进上下文就删掉。DSH 的会话日志是 append-only 的,官方只提供整段压缩(`/compact`),没有轮次级的编辑、重跑与删除。
13
+ AI Studio 的好用之处不在模型,在于一轮对话中的每一步都能回头改。DSH 官方只提供整段压缩(/compact),没有轮次级的编辑、重跑与删除。
14
14
 
15
- 这四个组件补上了这一层,而本包负责让它们**装得下、合得来、看得见**:
15
+ 四个组件补上了这一层,而这个包负责让它们装得下、合得来、看得见:
16
16
 
17
- - **装得下** —— 一次安装启用整条动作条;
18
- - **合得来** —— 四个组件各自独立、可任意子集共存,互操作契约由本包固化并测试;
19
- - **看得见** —— 谁装了、谁启用了、谁缺席,面板上一目了然。
17
+ - **装得下** —— 自包含安装,一次装完;
18
+ - **合得来** —— 四个组件各自独立、任意子集共存,互操作契约由本包固化并测试;
19
+ - **看得见** —— 设置 → 插件 → AI Studio 面板如实报告哪几个真的活着。
20
20
 
21
21
  ### 特性
22
22
 
23
- - **一次安装,整条动作条** —— 安装本包即可启用编辑、原位重跑、删除与 Markdown 气泡四个组件;
24
- - **任意子集都能跑** —— 只装其中一个组件时它自己就是完整功能;装两个、三个、四个,或者叠加本包,都不会重复挂载、不会抢同一行;
25
- - **组件仍是独立包** —— 每个组件有自己的仓库、自己的 `cordis.patch.yml`、自己单独的安装方式;本包**不内联、不魔改**它们的代码;
26
- - **互操作契约可验证** —— slot order 分配、行隐藏归属、兄弟探测协议写在 `docs/INTEROP.md`,并由 `test/manifest.test.js` 断言;
27
- - **如实报告** —— `GET /api/dsh-as-aistudio/status` 与设置面板区分「已启用 / 已安装未启用 / 未安装」,缺失组件只会退化,不会把宿主带崩;
28
- - **web 与桌面端同构** —— 浏览器半侧只注册一个设置页标签、只发一次相对路径请求;没有 DOM 注入、没有 MutationObserver、没有键盘监听。
29
-
30
- ### 组件
31
-
32
- | 组件 | 能力 | 仓库 |
33
- | --- | --- | --- |
34
- | `dsh-edit-turn` | 就地改提示词并重跑(回滚到那一条之前) | <https://github.com/DDDMUC/dsh-edit-turn> |
35
- | `dsh-rerun-turn` | 原位重跑:同一句提示词重新生成,后续轮次原样存活 | <https://github.com/DDDMUC/dsh-rerun-turn> |
36
- | `dsh-delete-turn` | 把一条消息 / 一个步骤 / 整条回复从模型上下文里拿掉 | <https://github.com/DDDMUC/dsh-delete-turn> |
37
- | `dsh-markdown-bubble` | 已发送的提示词按 Markdown 渲染 | <https://github.com/DDDMUC/dsh-markdown-bubble> |
38
-
39
- 四个组件都是**可选依赖**(`optionalDependencies`):装不上、被移除、离线都只会让对应那一行缺席,本包照常启动。
23
+ - **一行搞定** —— 插件列表里只有 AI Studio 一行。四个组件是本包的 dependencies,由本包挂载,不再各自占一行(和 @linxin666/dsh-web-all 同一个模式)。
24
+ - **组件仍是独立包** —— 每个都有自己的仓库与 cordis.patch.yml,单独安装照样工作;本包不复制任何组件逻辑:宿主半侧(路由、回滚、重放)就是组件自己,浏览器 factory 由 tools/vendor.mjs 从组件源码逐字剥离生成。
25
+ - **副本不会漂移** —— npm run vendor 重新生成内联包,npm run build 重新生成大 bundle,verify:vendor / verify:build 是只读校验;测试里 vendor-sync / build-sync 会在副本过期时失败。
26
+ - **一个挂了不影响其余** —— 四个 vendored factory 各自带 guard 挂载,一个抛错不会带走另外三个。
27
+ - **互操作契约可验证** —— slot order 分配、行隐藏归属、兄弟探测协议写在 docs/INTEROP.md,并由组合矩阵测试断言。
28
+ - **web 与桌面端同构** —— 同一份 dsh.client.platform: web bundle;没有 location.origin / window.open / navigator.* / dialog / popover。
40
29
 
41
30
  ### 安装
42
31
 
43
32
  ```sh
44
- # AI Studio 全家桶(studio + 四个组件一起启用)
45
33
  dsh plugin --profile web add dsh-as-aistudio
46
-
47
- # 只想要其中一个能力,就单独装那一个
48
- dsh plugin --profile web add dsh-delete-turn
49
34
  ```
50
35
 
51
- 桌面端把 `--profile web` 换成 `--profile desktop` 即可,两端共用同一份浏览器半侧。
36
+ 桌面端把 --profile web 换成 --profile desktop。四个组件会作为 dependencies 一起装上,不需要逐个装。
52
37
 
53
38
  ### 使用
54
39
 
55
- 装好后,动作条就在消息行上:
40
+ 装好后动作条就在消息行上:
56
41
 
57
- 1. 悬停**你发出的那条消息** → 铅笔(编辑并重跑);
58
- 2. 悬停**模型的回复** → 重跑 / 删除;
59
- 3. 「设置 → 插件」多出一个 **AI Studio** 标签页,列出每个组件的实时状态、版本、它占用的槽位,以及互操作契约的分配表。
42
+ 1. 悬停你发出的那条消息 → 铅笔(编辑并重跑);
43
+ 2. 悬停模型的回复 → 重跑 / 删除;
44
+ 3. 设置 → 插件 多出一个 AI Studio 标签页,列出每个组件的实时状态、版本、占用的槽位,以及互操作契约分配表。
60
45
 
61
46
  ### 工作原理
62
47
 
63
- - 本包的 `cordis.patch.yml` 为**自己和四个组件**各插入一行,行的 `name` 就是组件的裸包名 —— 这是客户端模块扫描器解析 `dsh.client` 声明的方式;
64
- - 宿主侧只做一件事:读 loader 的条目树,回答「这个组件现在有没有启用的条目、装的是哪个版本」,走 loopback-only 的 `/api/dsh-as-aistudio/status`;
65
- - 浏览器侧只做一件事:往 `settings.plugins.tab` 注册一个标签页;
66
- - 条目树按 id 归并,所以「本包插入的行」与「组件自己的 bundle 插入的行」会合并成**一个**条目,而不是挂载两次;
67
- - 某个组件缺席时,它的那一行会在 import 阶段失败并被 loader 隔离,其余行照常启动。
68
-
69
- ### 已修复 / 版本
70
-
71
- - 0.1.0 —— 首个版本:组合四个组件、固化互操作契约、报告组件状态。
48
+ - **宿主半侧**(src/index.js):await import 导入每个组件的宿主半侧并 ctx.plugin 挂进本 fiber —— 它们的 loopback 路由与回滚/重放服务因此就位。缺哪个报告哪个,绝不致命。
49
+ - **浏览器半侧**(src/client.js,由 tools/build.mjs 生成):单一大 bundle —— studio 自己的浏览器半侧 + 四个组件的 factory(各自 var module / var exports / const react,靠 var 提升共处一个 scope)。bundle 是按 Loader 行下发的,所以这是它们到达页面的唯一方式。
50
+ - **为什么宿主半侧不内联**:宿主代码对插件列表不可见,复制它只会凭空多出一份要维护的东西。
51
+ - **状态面板**:读 GET /api/dsh-as-aistudio/status,对每个组件报告 installed / hostMounted / version / ownRows(ownRows 大于 0 表示它还被独立安装着)。
72
52
 
73
53
  ### 已知限制
74
54
 
75
- - 动作条本身由四个组件各自渲染,本包不接管;某个组件缺席时它那一个按钮就不会出现,这符合预期;
76
- - 状态面板不提供开关:启用/禁用条目请在「设置 → 插件」的官方列表里做,那是平台自己的所有权;
77
- - 组件缺席时,宿主日志会有一条该条目的 import 失败记录。这是 loader 的诚实报错,不是本包的异常。
55
+ - 四个组件的 browser factory 是生成副本。改了组件必须 npm run vendor && npm run build 再发本包;verify:vendor / verify:build 会拦住过期副本。
56
+ - 组件缺席(有人手动删了依赖)时,它的 browser half 不会挂载,面板显示「已安装,宿主未挂载」或「未安装」。
57
+ - 宿主版本常量只在进程启动时读一次:改了本包要重启宿主 + 硬刷新。
78
58
 
79
59
  ### 兼容性
80
60
 
81
- - DSH `>=0.1.6-alpha.2`(`0.2.0-rc.1` 实测通过);
82
- - profile:`web`、`desktop`(`headless` 无 Web 界面,仅宿主半侧可用);
83
- - 四个组件都可单独安装,也可以任意子集叠加本包。
61
+ - DSH >=0.1.6-alpha.2(0.2.0-rc.1 实测通过);
62
+ - profile:web、desktop;
63
+ - 四个组件都可单独安装,也可以任意子集和本包共存。
84
64
 
85
65
  ### License
86
66
 
@@ -92,78 +72,51 @@ MIT
92
72
 
93
73
  ### Why you need it
94
74
 
95
- What makes AI Studio pleasant is not the model: it is that **every step of a turn can be revisited**. Rewrite a prompt in place and run it again, regenerate a reply without disturbing what came later, remove a turn that should never have entered the context. DSH's session log is append-only and the platform only offers whole-transcript compaction (`/compact`) — there is no per-turn edit, re-run or delete.
96
-
97
- Four components supply that layer. This package makes them **installable, compatible and visible**:
98
-
99
- - **installable** — one install turns on the whole action strip;
100
- - **compatible** — each component is independent and any subset coexists; the interop contract is pinned here and tested;
101
- - **visible** — which component is installed, enabled or absent, on one panel.
75
+ What makes AI Studio pleasant is not the model: it is that every step of a turn can be revisited. DSH offers whole-transcript compaction only. Four components add turn-level edit, re-run and delete; this package makes them installable, compatible and visible.
102
76
 
103
77
  ### Features
104
78
 
105
- - **One install, the whole strip** — installing this package enables edit, re-run, delete and the Markdown bubble;
106
- - **Any subset works** — one component alone is a complete feature; two, three, four, or components plus this package never double-mount and never fight over a row;
107
- - **Components stay independent packages** — each keeps its own repo, its own `cordis.patch.yml` and its own install path; this package does not inline or rewrite their code;
108
- - **A verifiable interop contract** — slot orders, row-hide ownership and the sibling probe protocol live in `docs/INTEROP.md` and are asserted by `test/manifest.test.js`;
109
- - **Honest reporting** — `GET /api/dsh-as-aistudio/status` and the settings panel distinguish enabled / installed-not-enabled / absent; a missing component degrades, never breaks the host;
110
- - **Web and desktop are one implementation** — the browser half registers one settings tab and makes one relative-path request; no DOM injection, no MutationObserver, no keyboard listener.
111
-
112
- ### Components
113
-
114
- | Component | What it does | Repo |
115
- | --- | --- | --- |
116
- | `dsh-edit-turn` | Rewrite a prompt in place and re-run it (rolls back to just before that turn) | <https://github.com/DDDMUC/dsh-edit-turn> |
117
- | `dsh-rerun-turn` | Infix re-run: regenerate one reply from the same prompt, later turns survive | <https://github.com/DDDMUC/dsh-rerun-turn> |
118
- | `dsh-delete-turn` | Remove a message, a step or a whole reply from the derived model context | <https://github.com/DDDMUC/dsh-delete-turn> |
119
- | `dsh-markdown-bubble` | Render sent prompts as Markdown | <https://github.com/DDDMUC/dsh-markdown-bubble> |
120
-
121
- All four are **optional dependencies**: a component that cannot be installed, was removed, or is unavailable offline simply misses its row; the studio still boots.
79
+ - **One row** — the plugin list shows only AI Studio. The four components are dependencies mounted by this package and no longer occupy rows of their own (the @linxin666/dsh-web-all pattern).
80
+ - **Components stay independent packages** — each keeps its own repo and its own cordis.patch.yml and works alone. This package copies no component logic: the host halves ARE the components, and the browser factories are lifted verbatim from their sources by tools/vendor.mjs.
81
+ - **The vendored copy cannot drift** — npm run vendor regenerates it, npm run build regenerates the bundle, and verify:vendor / verify:build are read-only gates; the test suite fails on a stale copy.
82
+ - **One failure does not take the rest** — each vendored factory mounts behind its own guard.
83
+ - **A verifiable interop contract** — slot orders, row-hide ownership and the sibling probe protocol live in docs/INTEROP.md and are asserted by the combination matrix.
84
+ - **Web and desktop are one implementation** — the same dsh.client.platform: web bundle; no location.origin, window.open, navigator.*, dialog or popover.
122
85
 
123
86
  ### Install
124
87
 
125
88
  ```sh
126
- # The whole AI Studio set (studio plus its four components)
127
89
  dsh plugin --profile web add dsh-as-aistudio
128
-
129
- # Or just one capability
130
- dsh plugin --profile web add dsh-delete-turn
131
90
  ```
132
91
 
133
- On the desktop app use `--profile desktop`; both share the same browser half.
92
+ Use --profile desktop on the desktop app. The four components arrive as dependencies; there is nothing else to install.
134
93
 
135
94
  ### Usage
136
95
 
137
- The action strip lives on the message rows:
138
-
139
- 1. Hover **a message you sent** → the pencil (edit and re-run);
140
- 2. Hover **a model reply** → re-run / delete;
141
- 3. Settings → Plugins gains an **AI Studio** tab listing each component's live state, version, the slot it claims, and the interop allocation.
96
+ 1. Hover a message you sent → the pencil (edit and re-run);
97
+ 2. Hover a model reply → re-run / delete;
98
+ 3. Settings → Plugins gains an AI Studio tab listing each component's live state, version, the slot it claims, and the interop allocation.
142
99
 
143
100
  ### How it works
144
101
 
145
- - This package's `cordis.patch.yml` inserts one row for itself and one per component, each named by the component's bare package name — that is how the client-module scanner resolves a `dsh.client` declaration;
146
- - The host half does one thing: read the loader's entry tree and answer "does this component have an enabled entry, and which version is installed", over a loopback-only `/api/dsh-as-aistudio/status`;
147
- - The browser half does one thing: register a tab into `settings.plugins.tab`;
148
- - The entry tree is keyed by id, so a row inserted by this patch and the same row inserted by a component's own bundle merge into **one** entry instead of mounting the plugin twice;
149
- - A missing component fails at import and is isolated by the loader; every other row still starts.
150
-
151
- ### Fixes / versions
152
-
153
- - 0.1.0 — first release: composes the four components, pins the interop contract, reports component status.
102
+ - **Host half** (src/index.js): imports each component's host half and applies it into this fiber with ctx.plugin, which is what puts its loopback routes and its rollback / replay services in place. A missing one is reported, never fatal.
103
+ - **Browser half** (src/client.js, generated by tools/build.mjs): one bundle — the studio's own browser half plus the four component factories, each with its own var module / var exports / const react in one scope via hoisting. A bundle is served per Loader row, so this is the only way they can reach the page.
104
+ - **Why the host halves are not vendored**: host code is invisible to the plugin list, so copying it would only create a second thing to fix.
105
+ - **The panel** reads GET /api/dsh-as-aistudio/status and reports installed / hostMounted / version / ownRows per component (ownRows greater than 0 means it is also installed standalone).
154
106
 
155
107
  ### Known limitations
156
108
 
157
- - The action strip is rendered by the components themselves; this package does not take it over. A missing component means its button is absent, which is the point;
158
- - The panel reports but does not toggle: enable or disable a row in the platform's own Settings → Plugins list, which owns that state;
159
- - A missing component leaves one import failure in the host log. That is the loader being honest, not an exception from this package.
109
+ - The four browser factories are a generated copy. After changing a component, run npm run vendor && npm run build before releasing this package; the verify gates block a stale copy.
110
+ - A component that is somehow absent is not mounted and the panel says so.
111
+ - The host version constant is read once at process start: after changing this package, restart the host and hard-refresh.
160
112
 
161
113
  ### Compatibility
162
114
 
163
- - DSH `>=0.1.6-alpha.2` (verified on `0.2.0-rc.1`);
164
- - Profiles: `web`, `desktop` (`headless` has no web surface; the host half still loads);
165
- - Every component installs alone, and any subset composes with this package.
115
+ - DSH >=0.1.6-alpha.2 (verified on 0.2.0-rc.1);
116
+ - Profiles: web, desktop;
117
+ - Every component installs alone, and any subset coexists with this package.
166
118
 
167
119
  ### License
168
120
 
169
121
  MIT
122
+
package/cordis.patch.yml CHANGED
@@ -1,30 +1,23 @@
1
- # dsh-as-aistudio bundle patch: the AI Studio composition.
1
+ # dsh-as-aistudio bundle patch: the AI Studio composition, ONE row.
2
2
  #
3
- # Rows are inserted for the studio itself and for each component it composes, so
4
- # one install turns the whole strip on. Every component keeps its own repo, its
5
- # own package and its own cordis.patch.yml: installing one alone still mounts it
6
- # through that row, and the platform's entry tree is keyed by id, so a row
7
- # inserted twice (by this patch and by the component's own bundle) merges into a
8
- # single entry rather than mounting the plugin twice.
3
+ # Self-contained: the plugin list shows exactly this row. The four components are
4
+ # ordinary npm dependencies of this package (see package.json), not profile
5
+ # bundles, so they have no rows of their own and never appear in that list.
9
6
  #
10
- # A missing package fails in isolation: @cordisjs/plugin-loader's Entry#_init
11
- # catches the import error, logs it and returns, so the other rows still start.
12
- # That is what makes "any subset of the components, on top of the studio" safe,
13
- # and the studio's own panel reports the gap instead of hiding it.
7
+ # The node half mounts each component HOST half (its loopback routes and its
8
+ # rollback / replay services) by importing the installed package, so host logic
9
+ # stays in the components and there is only one copy of it. The browser bundles
10
+ # travel inside this package (src/client.js) because a browser bundle is served
11
+ # per Loader row and the components have none.
14
12
  #
15
- # The entry names are the BARE package names on purpose: the client-module
16
- # scanner treats the name as a package specifier and resolves
17
- # `<name>/package.json` for the `dsh.client` declaration, so a subpath entry
18
- # would be cached as "not a client package" and the browser half would silently
19
- # never load.
13
+ # A component that is not installed is reported by /api/dsh-as-aistudio/status and
14
+ # is never mounted - the studio boots either way. Each component still has its own
15
+ # cordis.patch.yml, so installing one standalone mounts it through its own row.
16
+ #
17
+ # The entry name is the BARE package name on purpose: the client-module scanner
18
+ # treats it as a package specifier and resolves <name>/package.json for the
19
+ # dsh.client declaration, so a subpath entry would be cached as "not a client
20
+ # package" and the browser half would silently never load.
20
21
  - insert:
21
22
  - id: dsh-as-aistudio
22
23
  name: 'dsh-as-aistudio'
23
- - id: dsh-edit-turn
24
- name: 'dsh-edit-turn'
25
- - id: dsh-rerun-turn
26
- name: 'dsh-rerun-turn'
27
- - id: dsh-delete-turn
28
- name: 'dsh-delete-turn'
29
- - id: dsh-markdown-bubble
30
- name: 'dsh-markdown-bubble'
package/docs/INTEROP.md CHANGED
@@ -132,3 +132,84 @@ function foreignHideOn(row, own) {
132
132
  1. 每个包:`npm test`(+ `verify:*`)全绿;
133
133
  2. 组合矩阵:把 4 个 client bundle 以 2^4 种组合装载进同一个 DOM stub,断言:无异常、slot 注册 id 唯一、无重复按钮、隐藏归因正确;
134
134
  3. 真机:web profile 装载 `dsh-as-aistudio` + 任意子集,页面加载无 `error` 级别日志。
135
+ ## 9. 热重载纪律(HMR)—— 2026-10-01「点不动」事故后新增
136
+
137
+ **改任何组件的 `client.js` 之后,必须重启宿主进程 + 让页面硬刷新,不要把 HMR 混合态交付给用户。**
138
+
139
+ 事故形态(dsh-rerun-turn,2026-10-01 13:5x):用户点回答行 / 用户行的 ↻ 没有任何反应,宿主 `/debug` 也收不到 `apply` 请求;直取 `/plugins/<id>/client.js` 返回空。
140
+ 根因不是代码缺陷,而是**混合态**:client bundle 被 HMR 单独重载(0.1.21 / 0.1.22 的改动触发了多次),而宿主进程没有重启 —— 页面里是「新 bundle 实例 + 旧实例已 dispose 的控制器 / 旧按钮上已死的监听」。
141
+ 恢复动作:**重启宿主 + 硬刷新页面**(重启后 host 0.1.22、bundle 200 非空、两处 ↻ 点击均正常触发 `apply`)。
142
+
143
+ 配套约定:
144
+
145
+ - 交付前检查三件事:宿主版本 == bundle 版本;`/plugins/<entry>/client.js` 返回 200 且非空;行上的动作按钮真的点得动。
146
+ - 组件在 `dispose` 之后会主动早退(不唤醒已退休的控制器),所以混合态表现为**静默无响应**而不是报错 —— 这是刻意的设计,不是 Bug;遇到它先重启宿主再判断。
147
+ - 跨端(host / client)不兼容改动必须 bump 三处版本号(`package.json` + 宿主半侧 + 浏览器半侧)并跑该仓库的全部门禁。
148
+ - 本契约的 §2(slot order 分配)与 §4(隐藏归因)属于三个注入型组件的**共享面**:改其中一侧,必须同时检查另外两侧不受影响;组合矩阵(`dsh-as-aistudio/test/combination.test.js`)是这条规则的回归网。
149
+ ## 10. 跨插件运行时契约:dsh-edit-turn → dsh-rerun-turn
150
+
151
+ 这是**唯一一条两个组件之间真实存在的运行时依赖**,也是唯一一条不走 DOM、只走回环 HTTP 的依赖。写在契约里,
152
+ 是因为它跨越两个仓库、两个维护者,任何一侧单独改动都不会让另一侧的测试变红。
153
+
154
+ 流程(`dsh-edit-turn` 0.2.12+ 的提示词编辑器「重跑」按钮):
155
+
156
+ 1. 按钮**只在探测到 rerun-turn 挂载时**出现:不带 `sessionId` 请求 `GET /dsh-rerun-turn/state`,
157
+ 按 **400 / 405 = 装着、404 = 没装** 判定;
158
+ 2. 点击后先 `POST /dsh-edit-turn/apply` 就地保存改写(不调模型),
159
+ 3. 紧接着链式 `POST /dsh-rerun-turn/apply { sessionId, seq }`,其中 `seq` 取自 rerun-turn `/state` 的
160
+ `replies[]`(匹配被编辑那一轮的 `turn`、取 `seq` 最大者);
161
+ 4. rerun-turn 侧的 `planRerun` 沿 `source.kind = plugin:dsh-edit-turn` 的就地替换链读**活节点**,
162
+ 于是重跑用改后的措辞生成。
163
+
164
+ 双向边界:
165
+
166
+ - edit-turn **只**走 rerun-turn 的公开回环路由,不碰对方的 client 半侧或 DOM;
167
+ - 反过来,**纯 dsh-edit-turn(没装 rerun-turn)没有任何重跑入口** —— 编辑器只有「取消 / 保存」。
168
+
169
+ 不许改的形状(回归测试:`dsh-rerun-turn/test/contract.test.js`,16 例,已做变异验证):
170
+
171
+ | 形状 | 约束 |
172
+ | --- | --- |
173
+ | 挂载探测 | 不带 `sessionId` 的 `GET /state` 必须仍答 400;改成 404 按钮会静默消失 |
174
+ | 目标列表 | `/state` 持续返回完整的 `replies[{seq, turn}]` |
175
+ | 调用形状 | `POST /apply` 只要求 `sessionId` + (`seq` \| `messageId`),**不得新增必填参数** |
176
+ | 载荷 | edit-turn 不传文本;未知字段必须被忽略而不是 400 |
177
+ | 错误码 | `not-rerunnable` / `already-retired` / `busy` / `rerunning` / `stale` / `session-not-found` / `session-not-active` / `invalid` 机读且稳定(edit-turn 原样提示给用户) |
178
+ | 链式解析 | `planRerun` 继续跟随就地替换链取活文本 |
179
+
180
+ **给改这两个包的 agent**:动路由、`planRerun` 或错误码之前,先在 `dsh-rerun-turn` 跑
181
+ `node --test test/contract.test.js`;把探测状态的 400 改成 404 会让 2 例转红,给 `/apply` 加一个必填
182
+ `confirmToken` 会让 5 例转红 —— 破坏契约会在本地就暴露,而不是等用户发现按钮点不动。
183
+
184
+ ## 11. 自包含架构(2026-10-02)
185
+
186
+ 用户要求「插件列表里只有 AI Studio 一行」。平台的清单服务返回扁平数组,过滤条件是
187
+ !entry.options.group && !entry.disabled && entry.fiber.state === 2
188
+ (@deepseek-ai/dsh-plugin-package-inventory-deepseek/lib/index.js:100)—— 没有 hidden 开关,
189
+ group 也只能把 group 自己藏起来,子条目照样平铺。所以「在跑等价于在列表里」是硬约束。
190
+
191
+ 唯一的出路是让四个组件不再是 Loader 行。本包因此改成自包含(@linxin666/dsh-web-all 同款):
192
+
193
+ | 部件 | 位置 | 说明 |
194
+ | --- | --- | --- |
195
+ | patch | cordis.patch.yml | 只插一行(本包自己) |
196
+ | 组件身份 | package.json dependencies | 普通依赖,不是 profile bundle |
197
+ | 宿主半侧 | src/index.js | await import 加 ctx.plugin 挂载组件的宿主半侧 |
198
+ | 浏览器半侧 | src/client.js | 单一大 bundle(生成物) |
199
+ | 内联来源 | src/vendor/*.js | tools/vendor.mjs 从组件源码逐字剥离 factory |
200
+ | 缝合器 | tools/build.mjs | studio 加四个 factory 合成一个 bundle |
201
+
202
+ 两条设计红线:
203
+
204
+ 1. 宿主半侧不内联。宿主代码对列表不可见,复制它只会多出一份要维护的东西;import 加
205
+ ctx.plugin 就够,且组件的路由、回滚、重放逻辑仍是唯一正本。
206
+ 2. 浏览器半侧必须内联。bundle 是按 Loader 行下发的(/plugins/<行id>/client.js),
207
+ 组件没有行就没有 bundle。
208
+
209
+ 防漂移:副本由工具生成、带 sha256 戳,npm run verify:vendor / verify:build 只读校验,
210
+ 测试在副本与组件源不一致时失败。手工编辑 src/vendor/* 或 src/client.js 是禁止的。
211
+
212
+ 实测(隔离实例,只装本包 tarball):composed 184 行里组件行 0;/api/dsh-as-aistudio/status
213
+ hostMounted 4/4;GET /dsh-rerun-turn/state 返回 400(edit-turn 的挂载探测契约未破);
214
+ boot graph 66 条里组件条目 0;served bundle 200 且含四个 factory。
215
+
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-as-aistudio",
3
- "version": "0.1.0",
4
- "description": "Google-AI-Studio-style turn controls for DeepSeek Harness, in one install: edit a prompt and re-run it, re-run a reply in place, delete a turn from the model context, and render sent prompts as Markdown. Each of the four components is an independent plugin that also works alone, and any subset of them coexists; this package composes them, enables the rows that mount them, and reports which ones are live in Settings -> Plugins -> AI Studio.",
3
+ "version": "0.2.0",
4
+ "description": "Google-AI-Studio-style turn controls for DeepSeek Harness, in one install: edit a prompt and re-run it, re-run a reply in place, delete a turn from the model context, and render sent prompts as Markdown. Self-contained: the plugin list shows this one row; the four components are dependencies mounted by it. Each still installs alone and works alone.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
7
7
  "exports": {
@@ -11,6 +11,7 @@
11
11
  },
12
12
  "files": [
13
13
  "src",
14
+ "tools",
14
15
  "cordis.patch.yml",
15
16
  "docs/INTEROP.md",
16
17
  "README.md",
@@ -37,13 +38,18 @@
37
38
  "node": ">=22"
38
39
  },
39
40
  "scripts": {
40
- "test": "node --test \"test/*.test.js\""
41
+ "vendor": "node tools/vendor.mjs",
42
+ "build": "node tools/vendor.mjs && node tools/build.mjs",
43
+ "test": "node --test test/*.test.js",
44
+ "check": "npm run build && npm test",
45
+ "verify:vendor": "node tools/vendor.mjs --check",
46
+ "verify:build": "node tools/build.mjs --check"
41
47
  },
42
- "optionalDependencies": {
48
+ "dependencies": {
43
49
  "dsh-delete-turn": "^0.1.7",
44
- "dsh-edit-turn": "^0.2.14",
45
- "dsh-markdown-bubble": "^0.1.1",
46
- "dsh-rerun-turn": "^0.1.22"
50
+ "dsh-edit-turn": "^0.2.15",
51
+ "dsh-markdown-bubble": "^0.1.4",
52
+ "dsh-rerun-turn": "^0.1.24"
47
53
  },
48
54
  "dsh": {
49
55
  "bundle": {
@@ -52,7 +58,11 @@
52
58
  "client": {
53
59
  "platform": "web",
54
60
  "inject": [
55
- "@deepseek-ai/dsh-client-locale"
61
+ "@deepseek-ai/dsh-client-locale",
62
+ "@deepseek-ai/dsh-client-ui-chat",
63
+ "@deepseek-ai/dsh-client-ui-conversation",
64
+ "@deepseek-ai/dsh-client-ui-primitives",
65
+ "@deepseek-ai/dsh-client-ui-settings-plugins"
56
66
  ]
57
67
  },
58
68
  "engines": {
@@ -65,4 +75,4 @@
65
75
  }
66
76
  }
67
77
  }
68
- }
78
+ }