pi2dsh 0.12.4 → 0.13.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 +61 -8
- package/README.zh.md +51 -8
- package/dist/client.js +187 -0
- package/dist/host.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{runtime-DH0zEUpu.mjs → runtime-B-SlR8mJ.mjs} +34 -6
- package/dist/runtime-B-SlR8mJ.mjs.map +1 -0
- package/dist/runtime.d.mts +6 -0
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +1 -1
- package/package.json +2 -1
- package/dist/runtime-DH0zEUpu.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -170,6 +170,7 @@ it work. **This is the list to trust.**
|
|
|
170
170
|
| Plugin | What was exercised | Where | Example |
|
|
171
171
|
|---|---|---|---|
|
|
172
172
|
| [`@kassing/pi-vision`](https://www.npmjs.com/package/@kassing/pi-vision) | Image analysis delegated to a vision model; image-admission companion route; analysis injected into a text-only model's turn | CLI + web | [`vision-bridge`](examples/vision-bridge/) |
|
|
173
|
+
| [`@crazygit/pi-codex-image-gen`](https://www.npmjs.com/package/@crazygit/pi-codex-image-gen) | ChatGPT/Codex OAuth → `gpt-image-2` generation; local reference-image upload through DSH approval; image edit; native attachment storage and inline Web rendering | CLI + web | [`codex-image-gen`](examples/codex-image-gen/) |
|
|
173
174
|
| [`pi-btw`](https://www.npmjs.com/package/pi-btw) | `/btw <question>` as a real child session in DSH's subagent UI; `/btw-inject`; `/btw --save`; main thread stays clean | CLI + web | [`side-conversation`](examples/side-conversation/) |
|
|
174
175
|
| [`pi-powerline-footer`](https://www.npmjs.com/package/pi-powerline-footer) | A terminal status line — model, thinking level, project, context usage — drawn into DSH's widget dock, colour included | web | [`presentation-surfaces`](examples/presentation-surfaces/) |
|
|
175
176
|
| [`pi-vision-tool`](https://www.npmjs.com/package/pi-vision-tool) | Tool registration through a JSON-Schema shape DSH had to convert (`anyOf` → `oneOf`) | CLI + web | — |
|
|
@@ -306,23 +307,71 @@ The rules that keep it honest:
|
|
|
306
307
|
- **Verified, not asserted.** Every capability has a public-API contract test,
|
|
307
308
|
and ships only after running end to end on a real DSH loop — CLI *and* web.
|
|
308
309
|
|
|
310
|
+
## What this is teaching us about DSH
|
|
311
|
+
|
|
312
|
+
pi2dsh is also an executable stress test of DSH's plugin architecture. Pi gives
|
|
313
|
+
that test a useful workload: a large, already-used public plugin ABI rather than
|
|
314
|
+
a set of examples invented to fit the host.
|
|
315
|
+
|
|
316
|
+
The result so far is specific, not a thumbs-up/thumbs-down verdict:
|
|
317
|
+
|
|
318
|
+
- DSH's public seams successfully carry whole capabilities: tools, commands,
|
|
319
|
+
model adapters, user questions, native child sessions and browser slots.
|
|
320
|
+
- The pressure points appear when an out-of-repo plugin needs to extend an
|
|
321
|
+
existing capability *from the inside*: add a durable session-event type,
|
|
322
|
+
intercept the real provider request/response, control compaction before it
|
|
323
|
+
happens, or participate in trust before project resources load.
|
|
324
|
+
- A working pi2dsh sidecar or alternate adapter is useful product behaviour,
|
|
325
|
+
but it is **not** counted as proof that the native DSH seam is complete.
|
|
326
|
+
|
|
327
|
+
For example, a `pi-btw` answer is a real DSH child session — visible, resumable
|
|
328
|
+
and continuable by the host. Pi custom entries are different: they still need a
|
|
329
|
+
pi2dsh sidecar because an out-of-repo plugin cannot safely add a new event type
|
|
330
|
+
to DSH's durable log. Likewise, a transport-owning Pi provider can register a
|
|
331
|
+
native DSH route with its full compatibility profile, while the hand-configured
|
|
332
|
+
`llm-pi-ai` schema still drops some wire-compatibility fields.
|
|
333
|
+
|
|
334
|
+
The project follows one **[Pi → DSH architecture mapping standard](docs/architecture-mapping-standard.md)**.
|
|
335
|
+
All 111 Pi surfaces belong to capability contracts, all 45 DSH subsystems belong
|
|
336
|
+
to carrying mechanisms, and each capability contract maps to concrete public
|
|
337
|
+
DSH seams. Real plugin runs reference those mappings and record the five-layer
|
|
338
|
+
trace and five-grade result. The structured source of truth is
|
|
339
|
+
[`architecture-ledger.json`](docs/architecture-ledger.json); it produces the
|
|
340
|
+
**[theoretical architecture matrix](docs/architecture-mapping-matrix.md)**,
|
|
341
|
+
**[real-plugin validation matrix](docs/plugin-validation-matrix.md)** and
|
|
342
|
+
**[current architecture conclusions](docs/dsh-architecture-conformance.md)**.
|
|
343
|
+
That makes both top-down status and per-plugin drill-down answerable from the
|
|
344
|
+
same ledger. The current five DSH gap IDs are confirmed findings, not a claim
|
|
345
|
+
of complete coverage. Current
|
|
346
|
+
upstream reports include
|
|
347
|
+
[#2708 — durable events for out-of-repo plugins](https://github.com/deepseek-ai/deepseek-harness/discussions/2708)
|
|
348
|
+
and
|
|
349
|
+
[#3076 — provider compat fields dropped by `llm-pi-ai`](https://github.com/deepseek-ai/deepseek-harness/discussions/3076).
|
|
350
|
+
|
|
309
351
|
## Pi capabilities on DSH
|
|
310
352
|
|
|
311
353
|
Every surface a Pi package can touch, and what it maps onto. These tables are
|
|
312
354
|
generated from the rules the bridge consults at runtime, so they cannot drift
|
|
313
355
|
from the code.
|
|
314
356
|
|
|
357
|
+
For pinned Pi 0.84.1, the generated catalog has **111 upstream-shaped rule
|
|
358
|
+
rows**. The bridge also keeps one
|
|
359
|
+
documented compatibility extension, `unregisterTool`; it appears on the tools
|
|
360
|
+
detail page but is deliberately outside that total. Nested objects such as the
|
|
361
|
+
session manager can expose several methods behind one row; the architecture
|
|
362
|
+
audit states this boundary explicitly.
|
|
363
|
+
|
|
315
364
|
<!-- capability-table:start -->
|
|
316
365
|
| Area | Pi surfaces | Status |
|
|
317
366
|
|---|---|---|
|
|
318
|
-
| [Tools](docs/capabilities/tools.md) |
|
|
319
|
-
| [Commands, flags, editor input](docs/capabilities/commands.md) |
|
|
367
|
+
| [Tools](docs/capabilities/tools.md) | 11 | 2 same semantics · 9 mapped, difference stated |
|
|
368
|
+
| [Commands, flags, editor input](docs/capabilities/commands.md) | 6 | 6 mapped, difference stated |
|
|
320
369
|
| [Messages, context, agent loop](docs/capabilities/conversation.md) | 20 | 9 same semantics · 11 mapped, difference stated |
|
|
321
|
-
| [Sessions & side conversations](docs/capabilities/sessions.md) |
|
|
370
|
+
| [Sessions & side conversations](docs/capabilities/sessions.md) | 23 | 5 same semantics · 18 mapped, difference stated |
|
|
322
371
|
| [Models, providers, credentials](docs/capabilities/models.md) | 15 | 1 same semantics · 11 mapped, difference stated · 3 not available |
|
|
323
|
-
| [Asking the user, rendering](docs/capabilities/interaction.md) |
|
|
372
|
+
| [Asking the user, rendering](docs/capabilities/interaction.md) | 32 | 5 same semantics · 27 mapped, difference stated |
|
|
324
373
|
| [Project environment & resources](docs/capabilities/environment.md) | 4 | 1 same semantics · 1 mapped, difference stated · 2 not available |
|
|
325
|
-
| **Total** | **
|
|
374
|
+
| **Total** | **111** | **23 same semantics · 83 mapped, difference stated · 5 not available** |
|
|
326
375
|
<!-- capability-table:end -->
|
|
327
376
|
|
|
328
377
|
Plus **202 imported symbols** from Pi's three runtime packages
|
|
@@ -330,9 +379,10 @@ Plus **202 imported symbols** from Pi's three runtime packages
|
|
|
330
379
|
shims — so a plugin's own Pi version pins never load. They are listed in
|
|
331
380
|
[Imported Pi runtime symbols](docs/capabilities/imports.md).
|
|
332
381
|
|
|
333
|
-
Each area page states
|
|
334
|
-
|
|
335
|
-
|
|
382
|
+
Each area page states the Pi capability contract, the theoretical DSH mechanism
|
|
383
|
+
and public seam, and the current implementation for every surface. Adding a
|
|
384
|
+
surface without a capability assignment or theoretical mapping fails the docs
|
|
385
|
+
check.
|
|
336
386
|
|
|
337
387
|
Start at the [capability index](docs/capabilities/README.md). Machine-readable:
|
|
338
388
|
`pi2dsh matrix --json`.
|
|
@@ -369,8 +419,10 @@ in one has actually been executed against a real DSH loop before landing.
|
|
|
369
419
|
| Example | What you get |
|
|
370
420
|
|---|---|
|
|
371
421
|
| [`vision-bridge`](examples/vision-bridge/) | A text-only model answers questions about images — CLI and web, probe images included |
|
|
422
|
+
| [`codex-image-gen`](examples/codex-image-gen/) | Generate and edit images with a ChatGPT/Codex subscription; DSH approval and inline result included |
|
|
372
423
|
| [`side-conversation`](examples/side-conversation/) | `/btw <question>` runs a side thread in DSH's native subagent UI; your main conversation stays clean |
|
|
373
424
|
| [`presentation-surfaces`](examples/presentation-surfaces/) | A real plugin's terminal chrome (`pi-powerline-footer`) in DSH's web seats, plus which of the top-50 Pi plugins draw at all |
|
|
425
|
+
| [`subscription-login`](examples/subscription-login/) | Use a ChatGPT / Claude / Copilot / Kimi subscription as a DSH model: `/login`, then the route and credential appear on their own |
|
|
374
426
|
| [`gateway-compat`](examples/gateway-compat/) | Private / domestic / proxy gateways that reject the `developer` role: why reasoning 400s, and how a Pi provider plugin gets past it (recording proxy included, so you can read the exact request body we send) |
|
|
375
427
|
| [`custom-gateways`](examples/custom-gateways/) | Add any OpenAI-compatible gateway the official DSH way, and every Pi plugin sees it |
|
|
376
428
|
|
|
@@ -391,6 +443,7 @@ pnpm verify # typecheck + contract tests + packaging
|
|
|
391
443
|
pnpm audit:community # static screening over the top-50 corpus
|
|
392
444
|
pnpm test:community # deep runtime + official plugin-manager + e2e
|
|
393
445
|
DEEPSEEK_API_KEY=… pnpm test:live # real-model acceptance (key from env only)
|
|
446
|
+
CODEX_AUTH_FILE=… pnpm test:codex-image # real OAuth generation + reference edit + Web pixels
|
|
394
447
|
```
|
|
395
448
|
|
|
396
449
|
Acceptance evidence per capability: [docs/acceptance.md](docs/acceptance.md).
|
package/README.zh.md
CHANGED
|
@@ -152,6 +152,7 @@ Web 里**直接粘图**——哪怕你的主模型是纯文本的。DSH 正常
|
|
|
152
152
|
| 插件 | 验证了什么 | 在哪验的 | 示例 |
|
|
153
153
|
|---|---|---|---|
|
|
154
154
|
| [`@kassing/pi-vision`](https://www.npmjs.com/package/@kassing/pi-vision) | 图片委托给视觉模型;贴图伴生路由;分析结果注入纯文本模型的这一轮 | CLI + Web | [`vision-bridge`](examples/vision-bridge/) |
|
|
155
|
+
| [`@crazygit/pi-codex-image-gen`](https://www.npmjs.com/package/@crazygit/pi-codex-image-gen) | ChatGPT/Codex OAuth 调 `gpt-image-2` 生图;本地参考图走 DSH 审批后上传;编辑图片;原生附件存储并在 Web 内直接显示 | CLI + Web | [`codex-image-gen`](examples/codex-image-gen/) |
|
|
155
156
|
| [`pi-btw`](https://www.npmjs.com/package/pi-btw) | `/btw <问题>` 跑成 DSH 子代理界面里的真子会话;`/btw-inject`;`/btw --save`;主会话保持干净 | CLI + Web | [`side-conversation`](examples/side-conversation/) |
|
|
156
157
|
| [`pi-powerline-footer`](https://www.npmjs.com/package/pi-powerline-footer) | 终端状态条(模型、思考档位、项目、上下文用量)画进 DSH 的 widget dock,带颜色 | Web | [`presentation-surfaces`](examples/presentation-surfaces/) |
|
|
157
158
|
| [`pi-vision-tool`](https://www.npmjs.com/package/pi-vision-tool) | 工具注册,且带一个 DSH 需要转换的 JSON Schema 形状(`anyOf` → `oneOf`) | CLI + Web | — |
|
|
@@ -265,31 +266,70 @@ footer、title、working/thinking 类),都画在宿主自己的 slot 座位
|
|
|
265
266
|
- **验证过才算数。** 每项能力都有公开 API 契约测试,并且必须在真实 DSH loop 上
|
|
266
267
|
端到端跑通——CLI **和** Web 双端——才会发布。
|
|
267
268
|
|
|
269
|
+
## 这件事正在检验 DSH 什么
|
|
270
|
+
|
|
271
|
+
pi2dsh 同时也是一套会执行的 DSH 插件架构压力测试。Pi 提供的不是为了迎合 DSH
|
|
272
|
+
现编的几个 demo,而是一套已经被大量真实插件用过的公开 ABI,所以它很适合回答:
|
|
273
|
+
DSH 所说的“能力由插件自由组合”,到底走到了哪一步。
|
|
274
|
+
|
|
275
|
+
目前的结论不是简单的“好”或“不好”,而是边界已经很清楚:
|
|
276
|
+
|
|
277
|
+
- DSH 对**替换一整项能力**的公开 seam 是成立的:工具、命令、模型 adapter、用户
|
|
278
|
+
提问、原生子会话和浏览器 slot 都已经承接住真实 Pi 能力。
|
|
279
|
+
- 压力集中在**从内部扩展已有能力**:仓外插件新增一种持久会话事件、拦截真正发出的
|
|
280
|
+
provider 请求/响应、在压缩发生前改变决定、在项目资源加载前参与 trust。
|
|
281
|
+
- pi2dsh 用 sidecar 或另一条 adapter 把功能绕通,对用户有价值,但**不能算 DSH
|
|
282
|
+
原生架构已经承接成功**。我们会把“功能可用”和“宿主 seam 完整”分开记。
|
|
283
|
+
|
|
284
|
+
一个容易说错的例子:`pi-btw` 的回答已经是真正的 DSH child session,宿主能打开、
|
|
285
|
+
续聊和恢复;sidecar 存的是 Pi 自定义 entry 等 DSH 仓外插件目前无法写进原生日志的
|
|
286
|
+
事实,不是把整个子会话伪造了一遍。模型侧也一样:自带 transport 的 Pi provider
|
|
287
|
+
可以注册成原生 DSH route,并保留完整 compat;但手写 `llm-pi-ai` 配置时,部分
|
|
288
|
+
wire 兼容字段仍会被 schema 丢掉。
|
|
289
|
+
|
|
290
|
+
项目采用一套统一的 **[Pi → DSH 架构映射标准](docs/architecture-mapping-standard.md)**:
|
|
291
|
+
111 条 Pi 接口先归入能力契约,DSH 的 45 个模块归入承载机制,再建立到具体公开 seam
|
|
292
|
+
的理论映射;真实插件只引用这些映射,记录五层流转和五级结果。结构化事实保存在
|
|
293
|
+
[`architecture-ledger.json`](docs/architecture-ledger.json),由同一份总账生成
|
|
294
|
+
**[理论架构矩阵](docs/architecture-mapping-matrix.md)**、
|
|
295
|
+
**[真实插件验证矩阵](docs/plugin-validation-matrix.md)** 和
|
|
296
|
+
**[当前架构结论](docs/dsh-architecture-conformance.md)**。这样既能从总体看到已经适配的
|
|
297
|
+
能力,也能从任一插件下钻到它用了哪些 Pi 能力、落到哪些 DSH 机制、实际达到哪一级。
|
|
298
|
+
当前 5 个 DSH 缺口是已经坐实的发现,**不是已经完成全覆盖**。
|
|
299
|
+
当前已向上游提交的实证包括
|
|
300
|
+
[#2708:让仓外插件安全写持久事件](https://github.com/deepseek-ai/deepseek-harness/discussions/2708)
|
|
301
|
+
和
|
|
302
|
+
[#3076:`llm-pi-ai` 丢 provider compat 字段](https://github.com/deepseek-ai/deepseek-harness/discussions/3076)。
|
|
303
|
+
|
|
268
304
|
## Pi 的开放能力在 DSH 上怎么落
|
|
269
305
|
|
|
270
306
|
Pi 包能碰到的每一个面,以及它落到 DSH 的什么位置。下面这些表是从桥在运行时真正
|
|
271
307
|
查的那份规则生成的,所以不会和代码脱节。
|
|
272
308
|
|
|
309
|
+
针对固定的 Pi 0.84.1,当前生成目录有 **111** 条上游形状的规则行。桥另外保留了一个
|
|
310
|
+
兼容扩展 `unregisterTool`;它会出现在工具细表中,但明确不计入这个总数。
|
|
311
|
+
`sessionManager` 等嵌套对象仍可能用一行代表多个方法,架构审计会明确写出这层边界。
|
|
312
|
+
|
|
273
313
|
<!-- capability-table:start -->
|
|
274
314
|
| 能力域 | Pi 面数 | 状态 |
|
|
275
315
|
|---|---|---|
|
|
276
|
-
| [工具](docs/capabilities/tools.md) |
|
|
277
|
-
| [命令、flag、编辑器输入](docs/capabilities/commands.md) |
|
|
316
|
+
| [工具](docs/capabilities/tools.md) | 11 | 2 语义一致 · 9 已映射并写明差异 |
|
|
317
|
+
| [命令、flag、编辑器输入](docs/capabilities/commands.md) | 6 | 6 已映射并写明差异 |
|
|
278
318
|
| [消息、上下文、agent 循环](docs/capabilities/conversation.md) | 20 | 9 语义一致 · 11 已映射并写明差异 |
|
|
279
|
-
| [会话与侧边对话](docs/capabilities/sessions.md) |
|
|
319
|
+
| [会话与侧边对话](docs/capabilities/sessions.md) | 23 | 5 语义一致 · 18 已映射并写明差异 |
|
|
280
320
|
| [模型、provider、凭证](docs/capabilities/models.md) | 15 | 1 语义一致 · 11 已映射并写明差异 · 3 不提供 |
|
|
281
|
-
| [向用户提问与渲染](docs/capabilities/interaction.md) |
|
|
321
|
+
| [向用户提问与渲染](docs/capabilities/interaction.md) | 32 | 5 语义一致 · 27 已映射并写明差异 |
|
|
282
322
|
| [项目环境与资源](docs/capabilities/environment.md) | 4 | 1 语义一致 · 1 已映射并写明差异 · 2 不提供 |
|
|
283
|
-
| **合计** | **
|
|
323
|
+
| **合计** | **111** | **23 语义一致 · 83 已映射并写明差异 · 5 不提供** |
|
|
284
324
|
<!-- capability-table:end -->
|
|
285
325
|
|
|
286
326
|
另外还有 Pi 三个运行时包(`pi-coding-agent`、`pi-tui`、`pi-ai`)的 **202 个
|
|
287
327
|
导入符号**,由 vendored 或 headless shim 提供——所以插件自己钉的 Pi 版本永远不会
|
|
288
328
|
被加载,清单见[导入的 Pi 运行时符号](docs/capabilities/imports.md)。
|
|
289
329
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
330
|
+
每个能力域页面对每一个面都写清它属于哪个 Pi 能力契约、理论上应落到哪个 DSH 承载
|
|
331
|
+
机制与公开 seam、当前代码实际上如何实现。新增接口如果没有能力归属或理论映射,文档
|
|
332
|
+
检查会直接失败。
|
|
293
333
|
|
|
294
334
|
从[能力索引](docs/capabilities/README.md)开始看。机器可读版:`pi2dsh matrix --json`。
|
|
295
335
|
|
|
@@ -318,8 +358,10 @@ loop 上实际跑过才会进来。
|
|
|
318
358
|
| 示例 | 你能得到什么 |
|
|
319
359
|
|---|---|
|
|
320
360
|
| [`vision-bridge`](examples/vision-bridge/) | 纯文本模型回答图片问题——CLI 与 Web 双端,附探针图 |
|
|
361
|
+
| [`codex-image-gen`](examples/codex-image-gen/) | 用 ChatGPT/Codex 订阅生图和改图,包含 DSH 上传审批与 Web 内直接显示结果 |
|
|
321
362
|
| [`side-conversation`](examples/side-conversation/) | `/btw <问题>` 在 DSH 原生子代理界面里开一条侧边线程,主会话保持干净 |
|
|
322
363
|
| [`presentation-surfaces`](examples/presentation-surfaces/) | 真插件(`pi-powerline-footer`)的终端界面画进 DSH Web 座位,附 top50 里哪些 Pi 插件会画界面 |
|
|
364
|
+
| [`subscription-login`](examples/subscription-login/) | 用 ChatGPT / Claude / Copilot / Kimi 订阅账号当 DSH 的模型:`/login`、登录后自动建路由与凭证 |
|
|
323
365
|
| [`gateway-compat`](examples/gateway-compat/) | 私有 / 国内 / 代理网关拒收 `developer` 角色:为什么一开推理就 400,以及用 Pi provider 插件怎么绕过去(附透传录制代理,能看到我们真正发出去的请求体) |
|
|
324
366
|
| [`custom-gateways`](examples/custom-gateways/) | 按 DSH 官方方式接任何 OpenAI 兼容网关,每个 Pi 插件都能看到它 |
|
|
325
367
|
|
|
@@ -340,6 +382,7 @@ pnpm verify # 类型检查 + 契约测试 + 打包检查
|
|
|
340
382
|
pnpm audit:community # 前 50 静态筛查
|
|
341
383
|
pnpm test:community # 深度运行时 + 官方插件管理器 + e2e
|
|
342
384
|
DEEPSEEK_API_KEY=… pnpm test:live # 真实模型验收(key 只从环境读)
|
|
385
|
+
CODEX_AUTH_FILE=… pnpm test:codex-image # 真 OAuth 生图 + 参考图编辑 + Web 像素显示
|
|
343
386
|
```
|
|
344
387
|
|
|
345
388
|
逐项能力的验收证据:[docs/acceptance.md](docs/acceptance.md)。
|
package/dist/client.js
CHANGED
|
@@ -316,8 +316,194 @@ window.__ModuleLoader__.load({
|
|
|
316
316
|
flexDirection: "column",
|
|
317
317
|
gap: "4px",
|
|
318
318
|
padding: "4px 2px"
|
|
319
|
+
},
|
|
320
|
+
imageTool: {
|
|
321
|
+
margin: "4px 0",
|
|
322
|
+
overflow: "hidden",
|
|
323
|
+
borderRadius: "8px",
|
|
324
|
+
border: "1px solid rgba(120,120,130,0.22)",
|
|
325
|
+
background: "var(--dsh-color-bg-subtle, rgba(120,120,130,0.06))",
|
|
326
|
+
color: "var(--dsh-color-text, inherit)",
|
|
327
|
+
font: "400 12px/1.5 system-ui, sans-serif"
|
|
328
|
+
},
|
|
329
|
+
imageToolToggle: {
|
|
330
|
+
width: "100%",
|
|
331
|
+
display: "flex",
|
|
332
|
+
alignItems: "center",
|
|
333
|
+
gap: "8px",
|
|
334
|
+
padding: "8px 10px",
|
|
335
|
+
cursor: "pointer",
|
|
336
|
+
border: "none",
|
|
337
|
+
background: "transparent",
|
|
338
|
+
color: "inherit",
|
|
339
|
+
textAlign: "left",
|
|
340
|
+
font: "500 12px/1.5 system-ui, sans-serif"
|
|
341
|
+
},
|
|
342
|
+
imageToolStatus: {
|
|
343
|
+
color: "#2FBC44",
|
|
344
|
+
fontSize: "10px"
|
|
345
|
+
},
|
|
346
|
+
imageToolSummary: {
|
|
347
|
+
opacity: .62,
|
|
348
|
+
overflow: "hidden",
|
|
349
|
+
textOverflow: "ellipsis",
|
|
350
|
+
whiteSpace: "nowrap"
|
|
351
|
+
},
|
|
352
|
+
imageToolBody: {
|
|
353
|
+
display: "flex",
|
|
354
|
+
flexDirection: "column",
|
|
355
|
+
gap: "8px",
|
|
356
|
+
padding: "0 10px 10px",
|
|
357
|
+
borderTop: "1px solid rgba(120,120,130,0.14)"
|
|
358
|
+
},
|
|
359
|
+
imageToolText: {
|
|
360
|
+
margin: "8px 0 0",
|
|
361
|
+
whiteSpace: "pre-wrap",
|
|
362
|
+
wordBreak: "break-word",
|
|
363
|
+
opacity: .82
|
|
364
|
+
},
|
|
365
|
+
imageGrid: {
|
|
366
|
+
display: "flex",
|
|
367
|
+
flexWrap: "wrap",
|
|
368
|
+
gap: "8px"
|
|
369
|
+
},
|
|
370
|
+
imageFrame: {
|
|
371
|
+
display: "block",
|
|
372
|
+
maxWidth: "min(320px, 100%)",
|
|
373
|
+
maxHeight: "320px",
|
|
374
|
+
borderRadius: "8px",
|
|
375
|
+
objectFit: "contain",
|
|
376
|
+
background: "rgba(0,0,0,0.08)"
|
|
377
|
+
},
|
|
378
|
+
imageError: {
|
|
379
|
+
padding: "12px",
|
|
380
|
+
color: "#F63218"
|
|
319
381
|
}
|
|
320
382
|
};
|
|
383
|
+
/** Pull one image through DSH's own session-authorized attachment RPC. */
|
|
384
|
+
function AuthorizedToolImage({ sessionId, attachment }) {
|
|
385
|
+
const [url, setUrl] = (0, react.useState)(void 0);
|
|
386
|
+
const [failed, setFailed] = (0, react.useState)(false);
|
|
387
|
+
(0, react.useEffect)(() => {
|
|
388
|
+
const controller = new AbortController();
|
|
389
|
+
let objectUrl;
|
|
390
|
+
(async () => {
|
|
391
|
+
try {
|
|
392
|
+
const response = await fetch("/api/session.attachment", {
|
|
393
|
+
method: "POST",
|
|
394
|
+
headers: { "content-type": "application/json" },
|
|
395
|
+
signal: controller.signal,
|
|
396
|
+
body: JSON.stringify({
|
|
397
|
+
type: "client-request",
|
|
398
|
+
rpcId: `pi2dsh-image-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
399
|
+
method: "session.attachment",
|
|
400
|
+
payload: {
|
|
401
|
+
sessionId,
|
|
402
|
+
attachmentId: attachment.attachmentId
|
|
403
|
+
}
|
|
404
|
+
})
|
|
405
|
+
});
|
|
406
|
+
const envelope = await response.json();
|
|
407
|
+
const data = envelope.result?.ok === true ? envelope.result.value?.data : void 0;
|
|
408
|
+
if (!response.ok || typeof data !== "string") throw new Error("attachment unavailable");
|
|
409
|
+
const binary = atob(data);
|
|
410
|
+
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
411
|
+
objectUrl = URL.createObjectURL(new Blob([bytes], { type: attachment.mediaType }));
|
|
412
|
+
if (!controller.signal.aborted) setUrl(objectUrl);
|
|
413
|
+
} catch {
|
|
414
|
+
if (!controller.signal.aborted) setFailed(true);
|
|
415
|
+
}
|
|
416
|
+
})();
|
|
417
|
+
return () => {
|
|
418
|
+
controller.abort();
|
|
419
|
+
if (objectUrl !== void 0) URL.revokeObjectURL(objectUrl);
|
|
420
|
+
};
|
|
421
|
+
}, [
|
|
422
|
+
sessionId,
|
|
423
|
+
attachment.attachmentId,
|
|
424
|
+
attachment.mediaType
|
|
425
|
+
]);
|
|
426
|
+
if (failed) return (0, react.createElement)("div", { style: styles.imageError }, "Image attachment could not be loaded.");
|
|
427
|
+
if (url === void 0) return (0, react.createElement)("div", { style: styles.imageToolText }, "Loading image…");
|
|
428
|
+
return (0, react.createElement)("img", {
|
|
429
|
+
src: url,
|
|
430
|
+
alt: attachment.name ?? "Image returned by Pi tool",
|
|
431
|
+
style: styles.imageFrame,
|
|
432
|
+
"data-pi2dsh": "tool-image"
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
function firstArgumentSummary(raw) {
|
|
436
|
+
try {
|
|
437
|
+
const parsed = JSON.parse(raw);
|
|
438
|
+
const preferred = parsed.prompt ?? parsed.description;
|
|
439
|
+
if (typeof preferred === "string") return preferred;
|
|
440
|
+
const first = Object.values(parsed).find((value) => typeof value === "string");
|
|
441
|
+
return typeof first === "string" ? first : "";
|
|
442
|
+
} catch {
|
|
443
|
+
return raw;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
/** Browser row shared by the explicitly supported Pi image tools. */
|
|
447
|
+
function PiImageToolView({ toolName, block, sessionId }) {
|
|
448
|
+
const [expanded, setExpanded] = (0, react.useState)(true);
|
|
449
|
+
const settled = block.kind === "tool-result";
|
|
450
|
+
const argsRaw = settled ? block.call?.argsRaw ?? "" : block.argsRaw ?? "";
|
|
451
|
+
const content = settled && Array.isArray(block.content) ? block.content : [];
|
|
452
|
+
const images = content.flatMap((item) => item.type === "image" && item.attachment !== void 0 ? [item.attachment] : []);
|
|
453
|
+
const text = content.filter((item) => item.type === "text" && typeof item.text === "string").map((item) => item.text).join("\n");
|
|
454
|
+
const summary = firstArgumentSummary(argsRaw);
|
|
455
|
+
return (0, react.createElement)("div", {
|
|
456
|
+
style: styles.imageTool,
|
|
457
|
+
"data-pi2dsh": "image-tool-result",
|
|
458
|
+
"data-tool": toolName
|
|
459
|
+
}, (0, react.createElement)("button", {
|
|
460
|
+
type: "button",
|
|
461
|
+
style: styles.imageToolToggle,
|
|
462
|
+
"aria-expanded": expanded,
|
|
463
|
+
onClick: () => setExpanded((value) => !value)
|
|
464
|
+
}, (0, react.createElement)("span", { style: styles.imageToolStatus }, block.isError === true ? "●" : settled ? "●" : "◌"), (0, react.createElement)("span", null, toolName), summary === "" ? null : (0, react.createElement)("span", { style: styles.imageToolSummary }, `· ${summary}`)), !expanded ? null : (0, react.createElement)("div", { style: styles.imageToolBody }, text === "" ? null : (0, react.createElement)("div", { style: styles.imageToolText }, text), sessionId === void 0 || images.length === 0 ? null : (0, react.createElement)("div", { style: styles.imageGrid }, ...images.map((attachment) => (0, react.createElement)(AuthorizedToolImage, {
|
|
465
|
+
key: attachment.attachmentId,
|
|
466
|
+
sessionId,
|
|
467
|
+
attachment
|
|
468
|
+
})))));
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Register the image row under exact tool names published at package mount.
|
|
472
|
+
* Only known image tools appear in that list, so text-only and native tools
|
|
473
|
+
* retain DSH's existing cards.
|
|
474
|
+
*/
|
|
475
|
+
function installImageToolViews(scope) {
|
|
476
|
+
const installed = /* @__PURE__ */ new Set();
|
|
477
|
+
const start = () => {
|
|
478
|
+
let live = true;
|
|
479
|
+
const sync = async () => {
|
|
480
|
+
try {
|
|
481
|
+
const response = await fetch("/pi2dsh/image-tool-names");
|
|
482
|
+
if (!response.ok || !live) return;
|
|
483
|
+
const payload = await response.json();
|
|
484
|
+
if (!Array.isArray(payload.names)) return;
|
|
485
|
+
for (const value of payload.names) {
|
|
486
|
+
if (typeof value !== "string" || value === "" || installed.has(value)) continue;
|
|
487
|
+
installed.add(value);
|
|
488
|
+
scope.slots.inject("tool.call.toolview", () => scope.slots.register({
|
|
489
|
+
name: "tool.call.toolview",
|
|
490
|
+
key: value
|
|
491
|
+
}, PiImageToolView));
|
|
492
|
+
}
|
|
493
|
+
} catch {}
|
|
494
|
+
};
|
|
495
|
+
sync();
|
|
496
|
+
const timer = window.setInterval(() => {
|
|
497
|
+
sync();
|
|
498
|
+
}, POLL_MS);
|
|
499
|
+
return () => {
|
|
500
|
+
live = false;
|
|
501
|
+
window.clearInterval(timer);
|
|
502
|
+
};
|
|
503
|
+
};
|
|
504
|
+
if (scope.effect !== void 0) scope.effect(start, "pi2dsh: image tool views");
|
|
505
|
+
else start();
|
|
506
|
+
}
|
|
321
507
|
/**
|
|
322
508
|
* The frame-wide seat: the side-conversation panel, plus whatever packages
|
|
323
509
|
* pinned frame-wide — transient title and Pi's status entries, as pills.
|
|
@@ -479,6 +665,7 @@ window.__ModuleLoader__.load({
|
|
|
479
665
|
});
|
|
480
666
|
});
|
|
481
667
|
ctx.inject(["slots"], (scope) => {
|
|
668
|
+
installImageToolViews(scope);
|
|
482
669
|
scope.slots.inject("shell.overlay", () => scope.slots.register({
|
|
483
670
|
name: "shell.overlay",
|
|
484
671
|
id: "pi2dsh-overlay",
|
package/dist/host.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { r as registerVisionCompanions, t as applyPiPackage } from "./runtime-
|
|
2
|
+
import { r as registerVisionCompanions, t as applyPiPackage } from "./runtime-B-SlR8mJ.mjs";
|
|
3
3
|
import { t as resolvePiPackage } from "./source-0sA5z08z.mjs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { readFile } from "node:fs/promises";
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { r as registerVisionCompanions } from "./runtime-
|
|
2
|
+
import { r as registerVisionCompanions } from "./runtime-B-SlR8mJ.mjs";
|
|
3
3
|
import { t as resolvePiPackage } from "./source-0sA5z08z.mjs";
|
|
4
4
|
import { applyPiHost, manifestForInstalled } from "./host.mjs";
|
|
5
5
|
import { a as CONTEXT_RULES, c as PI_AI_PACKAGES, d as UI_CONTEXT_RULES, f as ruleForApi, g as ruleForUiContextProperty, h as ruleForHostImport, i as API_RULES, l as PI_CODING_AGENT_PACKAGES, m as ruleForEvent, n as convertPiMcpConfig, o as EVENT_RULES, p as ruleForContextProperty, r as renderMcpPatch, s as HOST_IMPORT_RULES, t as collectPiMcpServers, u as PI_TUI_PACKAGES } from "./mcp-config-CZepacin.mjs";
|
|
@@ -786,6 +786,15 @@ var BrowserSurfaces = class {
|
|
|
786
786
|
#draftRequests = /* @__PURE__ */ new Map();
|
|
787
787
|
#liveDrafts = /* @__PURE__ */ new Map();
|
|
788
788
|
#completionSources = /* @__PURE__ */ new Map();
|
|
789
|
+
#imageToolNames = /* @__PURE__ */ new Set();
|
|
790
|
+
/** Register one known image tool's exact DSH wire name. */
|
|
791
|
+
registerImageTool(name) {
|
|
792
|
+
if (name.length > 0) this.#imageToolNames.add(name);
|
|
793
|
+
}
|
|
794
|
+
/** Exact wire names needing the generic Pi image-result browser view. */
|
|
795
|
+
imageToolNames() {
|
|
796
|
+
return [...this.#imageToolNames];
|
|
797
|
+
}
|
|
789
798
|
/**
|
|
790
799
|
* Track one child session under its parent.
|
|
791
800
|
* @param parentSessionId - the session the panel floats over.
|
|
@@ -1158,6 +1167,14 @@ function registerBrowserSurfaceRoute(ctx, registry) {
|
|
|
1158
1167
|
response.end(JSON.stringify({ items }));
|
|
1159
1168
|
return;
|
|
1160
1169
|
}
|
|
1170
|
+
if (url.pathname === "/pi2dsh/image-tool-names") {
|
|
1171
|
+
response.writeHead(200, {
|
|
1172
|
+
"content-type": "application/json; charset=utf-8",
|
|
1173
|
+
"cache-control": "no-store"
|
|
1174
|
+
});
|
|
1175
|
+
response.end(method === "HEAD" ? void 0 : JSON.stringify({ names: registry.imageToolNames() }));
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1161
1178
|
if (url.pathname !== "/pi2dsh/browser-state") {
|
|
1162
1179
|
response.writeHead(404);
|
|
1163
1180
|
response.end();
|
|
@@ -2549,6 +2566,10 @@ function registerPiProviderRoute(options) {
|
|
|
2549
2566
|
//#region src/runtime.ts
|
|
2550
2567
|
/** Fallback thread ids when a child session reports none. */
|
|
2551
2568
|
let sidePanelSerial = 0;
|
|
2569
|
+
const KNOWN_IMAGE_TOOLS_BY_PACKAGE = /* @__PURE__ */ new Map([["@crazygit/pi-codex-image-gen", /* @__PURE__ */ new Set(["codex_generate_image"])]]);
|
|
2570
|
+
function isKnownImageTool(packageName, toolName) {
|
|
2571
|
+
return KNOWN_IMAGE_TOOLS_BY_PACKAGE.get(packageName)?.has(toolName) === true;
|
|
2572
|
+
}
|
|
2552
2573
|
function logger(ctx) {
|
|
2553
2574
|
const candidate = ctx.logger;
|
|
2554
2575
|
return {
|
|
@@ -4204,16 +4225,17 @@ async function supersedeActiveLogin(state) {
|
|
|
4204
4225
|
async function ensureLoggedInProviderRoute(ctx, state, name, config) {
|
|
4205
4226
|
if (!providerSupportsOAuth(config)) return false;
|
|
4206
4227
|
if (providerCarriesTransport(config) || state.providerRouteDisposers.has(name)) return false;
|
|
4207
|
-
|
|
4228
|
+
const alreadyRouted = llmOf(ctx)?.listProviders().some((provider) => provider.id === name) === true;
|
|
4208
4229
|
if (await storedOAuthCredential(oauthStoreOf(state), name).catch(() => void 0) === void 0) return false;
|
|
4230
|
+
const published = await publishOAuthCredential(ctx, state, name, config);
|
|
4231
|
+
if (!published.ok) return false;
|
|
4232
|
+
keepOAuthCredentialFresh(ctx, state);
|
|
4233
|
+
if (alreadyRouted) return false;
|
|
4209
4234
|
const settings = optionalService(ctx, "settings");
|
|
4210
4235
|
if (settings === void 0) {
|
|
4211
4236
|
logger(ctx).warn(`[pi2dsh] logged in to ${JSON.stringify(name)}, but this composition has no settings service to declare its model route in`);
|
|
4212
4237
|
return false;
|
|
4213
4238
|
}
|
|
4214
|
-
const published = await publishOAuthCredential(ctx, state, name, config);
|
|
4215
|
-
if (!published.ok) return false;
|
|
4216
|
-
keepOAuthCredentialFresh(ctx, state);
|
|
4217
4239
|
await settings.update("llm-pi-ai", { providers: { [name]: {
|
|
4218
4240
|
...typeof config.name === "string" ? { displayName: config.name } : {},
|
|
4219
4241
|
...published.baseUrl === void 0 ? {} : { baseURL: published.baseUrl },
|
|
@@ -4460,6 +4482,7 @@ function registerTool(ctx, state, tool) {
|
|
|
4460
4482
|
};
|
|
4461
4483
|
const dispose = ctx.tools.register(definition);
|
|
4462
4484
|
state.toolDisposers.set(tool.name, dispose);
|
|
4485
|
+
if (isKnownImageTool(state.packageName, tool.name)) state.shared.browserSurfaces?.registerImageTool(tool.name);
|
|
4463
4486
|
}
|
|
4464
4487
|
function unregisterTool(state, name) {
|
|
4465
4488
|
const dispose = state.toolDisposers.get(name);
|
|
@@ -5176,7 +5199,11 @@ async function applyPiPackage(ctx, options) {
|
|
|
5176
5199
|
oauth: provider.auth.oauth
|
|
5177
5200
|
});
|
|
5178
5201
|
ensureLoginCommand(ctx, state);
|
|
5179
|
-
for (const [name, config] of [...state.providers])
|
|
5202
|
+
for (const [name, config] of [...state.providers]) try {
|
|
5203
|
+
await ensureLoggedInProviderRoute(ctx, state, name, config);
|
|
5204
|
+
} catch (error) {
|
|
5205
|
+
logger(ctx).warn(`[pi2dsh] could not restore the route for logged-in provider ${JSON.stringify(name)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
5206
|
+
}
|
|
5180
5207
|
if (options.manifest.skillDirs.length > 0) {
|
|
5181
5208
|
if (ctx.get("skills") === void 0) logger(ctx).warn("[pi2dsh] migrated skills were not mounted because this DSH composition has no ctx.skills");
|
|
5182
5209
|
else {
|
|
@@ -5335,6 +5362,7 @@ const runtimeInternals = {
|
|
|
5335
5362
|
resolveOfferedChoice,
|
|
5336
5363
|
dshToPiContent,
|
|
5337
5364
|
expandPrompt,
|
|
5365
|
+
isKnownImageTool,
|
|
5338
5366
|
isSubagentOrigin,
|
|
5339
5367
|
normalizeToolResult,
|
|
5340
5368
|
splitArguments,
|
|
@@ -5344,4 +5372,4 @@ const runtimeInternals = {
|
|
|
5344
5372
|
//#endregion
|
|
5345
5373
|
export { runtimeInternals as i, normalizeToolSchema as n, registerVisionCompanions as r, applyPiPackage as t };
|
|
5346
5374
|
|
|
5347
|
-
//# sourceMappingURL=runtime-
|
|
5375
|
+
//# sourceMappingURL=runtime-B-SlR8mJ.mjs.map
|