better-dsh 0.2.2-a → 0.2.2-c
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/docs/50_test-reports/2026-09-06-write/345/267/245/345/205/267sandbox/345/215/207/347/272/247/351/200/217/344/274/240bug/345/244/215/345/217/221/345/217/212/346/214/202/350/265/267-/344/272/213/344/273/266/346/212/245/345/221/212.md +43 -0
- package/docs/50_test-reports/v0.2.4-ios-focus-zoom-suppression/345/256/236/346/265/213/346/212/245/345/221/212.md +158 -0
- package/docs/60_exploration-and-research/01-cordis-runtime/bun-compile-cordis-runtime-bootstrap-research.md +536 -0
- package/docs/60_exploration-and-research/01-cordis-runtime/cordis-customization-and-override-mechanics.md +418 -0
- package/docs/60_exploration-and-research/01-cordis-runtime/cordis-research.md +350 -0
- package/docs/60_exploration-and-research/01-cordis-runtime/dsh-cordis-hotplug-mcp-patch-research.md +265 -0
- package/docs/60_exploration-and-research/02-dsh-webui/dsh-web-profile-package-map.md +186 -0
- package/docs/60_exploration-and-research/02-dsh-webui/dsh-web-ui-slot-system-research.md +310 -0
- package/docs/60_exploration-and-research/02-dsh-webui/dsh-webui-backend-data-inventory.md +633 -0
- package/docs/60_exploration-and-research/02-dsh-webui/dsh-webui-strip-boundary-research.md +300 -0
- package/docs/60_exploration-and-research/02-dsh-webui/dsh-webui-wire-appendix.md +3729 -0
- package/docs/60_exploration-and-research/02-dsh-webui/web-frontend-composability-research.md +191 -0
- package/docs/60_exploration-and-research/02-dsh-webui/webui-wire-data/capture-live-turn.json +1 -0
- package/docs/60_exploration-and-research/02-dsh-webui/webui-wire-data/observed-endpoints.json +224 -0
- package/docs/60_exploration-and-research/02-dsh-webui/webui-wire-data/remote-inventory.json +110954 -0
- package/docs/60_exploration-and-research/02-dsh-webui/webui-wire-data/served-index-sample.html +47 -0
- package/docs/60_exploration-and-research/02-dsh-webui/webui-wire-data/session-events.json +9411 -0
- package/docs/60_exploration-and-research/02-dsh-webui/webui-wire-data/ws-frame-examples.json +20 -0
- package/docs/60_exploration-and-research/03-mobile-ios/dsh-mobile-spa-ios-input-experience-research.md +160 -0
- package/docs/60_exploration-and-research/03-mobile-ios/ios-chat-app-bridge-research.md +324 -0
- package/docs/60_exploration-and-research/04-session-storage/alpha5-compaction-jsonl-mapping.md +1532 -0
- package/docs/60_exploration-and-research/04-session-storage/alpha5-compaction-sample/episode-failed.json +46 -0
- package/docs/60_exploration-and-research/04-session-storage/alpha5-compaction-sample/episode1.json +1124 -0
- package/docs/60_exploration-and-research/04-session-storage/alpha5-compaction-sample/episode2.json +1240 -0
- package/docs/60_exploration-and-research/05-dashr-dev/plugin-development.md +148 -0
- package/docs/60_exploration-and-research/05-dashr-dev/upstream-alignment.md +102 -0
- package/docs/60_exploration-and-research/README.md +87 -0
- package/docs/60_exploration-and-research/bun-compile-cordis-runtime-bootstrap-research.md +348 -0
- package/docs/60_exploration-and-research/dsh-mobile-spa-ios-input-experience-research.md +160 -0
- package/lib/index.d.ts +9 -1
- package/lib/index.js +258 -6
- package/package.json +1 -1
- package/docs/REPL-/345/267/245/345/205/267/350/260/203/347/224/250-/346/210/252/346/226/255/350/257/212/346/226/255.md +0 -110
- package/docs/adr/0001-bridge-tool-layer-not-service-layer.md +0 -14
- package/docs/adr/0002-masking-is-presentation-only.md +0 -15
- package/docs/plans/A2A-messaging-channel-test-archive.md +0 -256
- package/docs/plans/code-mode-vs-rlm-ipython-comparison.md +0 -137
- package/docs/plans/dashr-blueprint-review.md +0 -201
- package/docs/plans/dashr-blueprint.md +0 -561
- package/docs/plans/dashr-compaction-window-and-archive.md +0 -307
- package/docs/plans/dashr-profile-layer-feasibility.md +0 -367
- package/docs/plans/dashr-sandbox-escalation-semantics-gap.md +0 -171
- package/docs/plans/dashr-security-sandbox-analysis.md +0 -187
- package/docs/plans/dashr-surface-invariant-and-omp-imports.md +0 -97
- package/docs/plans/ipython-kernel-interactive-interface-test-report.md +0 -152
- package/docs/plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft.md +0 -146
- package/docs/plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft_v3.md +0 -50
- package/docs/plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft_v4.md +0 -79
- package/docs/plans/kernel-refactoring/Dash-vs-PrimeAgent-systemprompt-toolcatalog-comparison.md +0 -138
- package/docs/plans/kernel-refactoring/RLM-system-prompt-injection-gap-report.md +0 -161
- package/docs/plans/kernel-refactoring/V0.1.5-development-plan.md +0 -109
- package/docs/plans/kernel-refactoring/actinoable-surface-to-llm-in-agent-runtime_dsh.md +0 -50
- package/docs/plans/kernel-refactoring/actinoable-surface-to-llm-in-agent-runtime_prime.md +0 -113
- package/docs/plans/recallable-compaction.md +0 -147
- package/docs/plans/spike-tag-repro.mjs +0 -102
- package/docs/plans/upstream-analysis.md +0 -128
- package/docs/v0.1.8-improved-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -142
- package/docs/v0.1.8-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -193
- package/docs/v0.1.8b-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -96
- package/docs/v0.1.8c-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -127
- package/docs/v0.1.8d-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -150
- package/docs/v0.1.8d_artifacts/README.md +0 -138
- package/docs/v0.1.8d_artifacts/code-mode-repl-only.observation.md +0 -74
- package/docs/v0.1.8d_artifacts/dsh-session-session-4a293388-9ae1-474b-87a0-9e17bb556d94.jsonl +0 -3890
- package/docs/v0.1.8d_artifacts/dsh-session-session-4a293388-9ae1-474b-87a0-9e17bb556d94.w-sample-0435.jsonl +0 -544
- package/docs/v0.1.8d_artifacts/functions.json +0 -592
- package/docs/v0.1.8d_artifacts/skills-catalog.snapshot.md +0 -30
- package/docs/v0.1.8d_artifacts/tools-sdk.output-schemas.json +0 -1236
- package/docs/v0.1.8d_artifacts/tools-sdk.python.txt +0 -592
- package/docs/v0.1.8d_artifacts/tools-sdk.typescript.txt +0 -516
- package/docs/v0.1.8d_artifacts/wire-vs-transcription.diff.md +0 -54
- package/docs/v0.1.8e-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -224
- package/docs/v0.1.9a-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -168
- package/docs/v0.2.0b-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -123
- package/docs/v0.2.0b_artifacts/f2probe/Cargo.lock +0 -7
- package/docs/v0.2.0b_artifacts/f2probe/Cargo.toml +0 -6
- package/docs/v0.2.0b_artifacts/f2probe/src/bin/messy.rs +0 -8
- package/docs/v0.2.0b_artifacts/f2probe/src/main.rs +0 -4
- package/docs/v0.2.0b_artifacts/hashline-probe.md +0 -5
- package/docs/v0.2.0b_artifacts/slowprobe/Cargo.lock +0 -7
- package/docs/v0.2.0b_artifacts/slowprobe/Cargo.toml +0 -7
- package/docs/v0.2.0b_artifacts/slowprobe/build.rs +0 -4
- package/docs/v0.2.0b_artifacts/slowprobe/src/main.rs +0 -13
- package/docs/v0.2.1-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -110
- package/docs/v0.2.1b-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -86
- package/docs/v0.2.1c-/345/256/236/346/265/213/346/212/245/345/221/212.md +0 -66
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ready": "{\"type\":\"item\",\"streamId\":\"3d525a05-e4d2-445f-865f-c25baed76f38\",\"value\":{\"type\":\"ready\",\"clientId\":\"74ba497b-8edd-4039-bb1a-99d371daad30\",\"host\":{\"home\":\"/home/u1\"}}}",
|
|
3
|
+
"baseline": "{\"type\":\"item\",\"streamId\":\"a63bfda4-2347-4ed8-8051-a8e540534ecf\",\"value\":{\"type\":\"baseline\",\"value\":{\"queues\":{\"session-8e966430-59af-4bba-9ddf-571d311bc1e1\":[],\"session-61cfb1f3-204c-49ee-be76-0b7075cc91fe\":[],\"session-69b311b1-36d2-411b-a9b3-64381089f9c5\":[],\"session-2547aab1-d7a3-45ce-9120-4440412c8ea2\":[]},\"jobs\":{\"session-8e966430-59af-4bba-9ddf-571d311bc1e1\":[],\"session-61cfb1f3-204c-49ee-be76-0b7075cc91fe\":[],\"session-69b311b1-36d2-411b-a9b3-64381089f9c5\":[],\"session-2547aab1-d7a3-45ce-9120-4440412c8ea2\":[]},\"projections\":{\"session-8e966430-59af-4bba-9ddf-571d311bc1e1\":{\"asOfSeq\":90565,\"values\":{\"goal\":null,\"title\":\"IPython REPL tool availability\",\"sessionStats\":{\"turns\":13,\"steps\":58,\"llmMs\":931101,\"toolMs\":15797,\"ttftMs\":88766,\"ttftSteps\":58,\"decodeMs\":842335,\"decodeTokens\":92410},\"turnOutline\":[{\"turn\":1,\"seq\":5,\"prompt\":\"do you have ipython repl tool\",\"response\":\"Short answer:",
|
|
4
|
+
"snapshot": "{\"type\":\"item\",\"streamId\":\"958525c2-bc47-46a7-8f96-798a0d21cb90\",\"value\":{\"type\":\"snapshot\",\"header\":{\"version\":0,\"id\":\"session-61cfb1f3-204c-49ee-be76-0b7075cc91fe\",\"createdAt\":1788439501181,\"cwd\":\"/home/u1/workspaces/temp\",\"delegationDepth\":0,\"agentPreset\":\"standard\"},\"cursor\":3,\"records\":[{\"type\":\"event\",\"event\":{\"type\":\"permission/preset\",\"seq\":0,\"time\":1788439501191,\"data\":{\"preset\":\"workspace-write\"}}},{\"type\":\"event\",\"event\":{\"type\":\"sandbox/mode\",\"seq\":1,\"time\":1788439501196,\"data\":{\"mode\":\"workspace-write\"}}},{\"type\":\"event\",\"event\":{\"type\":\"approval/policy\",\"seq\":2,\"time\":1788439501196,\"data\":{\"policy\":\"ask\"}}},{\"type\":\"event\",\"event\":{\"type\":\"session/end-seed\",\"seq\":3,\"time\":1788445135978,\"data\":{}}}],\"hasMore\":false,\"projections\":{\"asOfSeq\":3,\"values\":{\"goal\":null,\"title\":null,\"sessionStats\":{\"turns\":0,\"steps\":0,\"llmMs\":0,\"toolMs\":0,\"ttftMs\":0,\"ttftSteps\":0,\"decodeMs\":0,\"deco",
|
|
5
|
+
"event/agent/inbox/spliced": "{\"type\":\"item\",\"streamId\":\"958525c2-bc47-46a7-8f96-798a0d21cb90\",\"value\":{\"type\":\"event\",\"event\":{\"type\":\"agent/inbox/spliced\",\"seq\":4,\"time\":1788635028103,\"data\":{\"target\":\"next-turn\",\"start\":0,\"inserted\":[{\"content\":[{\"type\":\"text\",\"text\":\"用一句话回答:1+1=? 不要调用任何工具\"}],\"source\":{\"kind\":\"user\",\"rpcId\":\"16f8164e-7939-4495-b7ca-4142463b7aa1\",\"clientTimeZone\":\"Asia/Hong_Kong\"},\"role\":\"user\",\"id\":\"03533ce9-c9b1-47a2-859f-8971885ffe78\"}]}}}}",
|
|
6
|
+
"queue": "{\"type\":\"item\",\"streamId\":\"a63bfda4-2347-4ed8-8051-a8e540534ecf\",\"value\":{\"type\":\"queue\",\"sessionId\":\"session-61cfb1f3-204c-49ee-be76-0b7075cc91fe\",\"items\":[{\"id\":\"03533ce9-c9b1-47a2-859f-8971885ffe78\",\"placement\":\"queued\",\"rpcId\":\"16f8164e-7939-4495-b7ca-4142463b7aa1\",\"message\":{\"id\":\"03533ce9-c9b1-47a2-859f-8971885ffe78\",\"content\":[{\"type\":\"text\",\"text\":\"用一句话回答:1+1=? 不要调用任何工具\"}]}}]}}",
|
|
7
|
+
"emit": "{\"type\":\"item\",\"streamId\":\"3d525a05-e4d2-445f-865f-c25baed76f38\",\"value\":{\"type\":\"emit\",\"event\":\"api-session/status\",\"args\":[\"session-61cfb1f3-204c-49ee-be76-0b7075cc91fe\",true]}}",
|
|
8
|
+
"event/turn/start": "{\"type\":\"item\",\"streamId\":\"958525c2-bc47-46a7-8f96-798a0d21cb90\",\"value\":{\"type\":\"event\",\"event\":{\"type\":\"turn/start\",\"seq\":5,\"time\":1788635028111,\"data\":{\"turn\":1}}}}",
|
|
9
|
+
"projection": "{\"type\":\"item\",\"streamId\":\"a63bfda4-2347-4ed8-8051-a8e540534ecf\",\"value\":{\"type\":\"projection\",\"sessionId\":\"session-61cfb1f3-204c-49ee-be76-0b7075cc91fe\",\"key\":\"turnOutline\",\"value\":[{\"turn\":1,\"seq\":5,\"prompt\":\"\",\"response\":\"\"}],\"seq\":5}}",
|
|
10
|
+
"event/step/start": "{\"type\":\"item\",\"streamId\":\"958525c2-bc47-46a7-8f96-798a0d21cb90\",\"value\":{\"type\":\"event\",\"event\":{\"type\":\"step/start\",\"seq\":7,\"time\":1788635028148,\"data\":{\"turn\":1,\"step\":1}}}}",
|
|
11
|
+
"event/user/message": "{\"type\":\"item\",\"streamId\":\"958525c2-bc47-46a7-8f96-798a0d21cb90\",\"value\":{\"type\":\"event\",\"event\":{\"type\":\"user/message\",\"seq\":8,\"time\":1788635028149,\"data\":{\"content\":[{\"type\":\"text\",\"text\":\"用一句话回答:1+1=? 不要调用任何工具\"}],\"source\":{\"kind\":\"user\",\"rpcId\":\"16f8164e-7939-4495-b7ca-4142463b7aa1\",\"clientTimeZone\":\"Asia/Hong_Kong\"},\"role\":\"user\",\"id\":\"03533ce9-c9b1-47a2-859f-8971885ffe78\"},\"surfaceOp\":\"append\"}}}",
|
|
12
|
+
"event/session/title": "{\"type\":\"item\",\"streamId\":\"958525c2-bc47-46a7-8f96-798a0d21cb90\",\"value\":{\"type\":\"event\",\"event\":{\"type\":\"session/title\",\"seq\":11,\"time\":1788635028154,\"data\":{\"title\":\"用一句话回答:1+1=? 不要调用任\",\"messageSeqs\":[8],\"source\":{\"kind\":\"fallback\"}}}}}",
|
|
13
|
+
"event/request/header": "{\"type\":\"item\",\"streamId\":\"958525c2-bc47-46a7-8f96-798a0d21cb90\",\"value\":{\"type\":\"event\",\"event\":{\"type\":\"request/header\",\"seq\":12,\"time\":1788635028157,\"data\":{\"header\":{\"config\":{\"provider\":\"deepseek-official\",\"model\":\"deepseek-v4-flash\",\"reasoningEffort\":\"high\",\"maxTokens\":256000},\"adapterDefaults\":{\"maxTokens\":true},\"system\":\"You are an AI agent powered by DeepSeek Harness.\\n\\nThe DeepSeek Harness implementation checkout is at /home/u1/workspaces/dashr/upstream/deepseek-harness/. The checkout location and current working directory are separate values and may differ; never infer the working directory from this path. Use pwd to determine the current working directory. Use this checkout only to inspect or extend DSH itself.\\n\\nYou are interacting with the user through the DeepSeek Harness Web GUI at http://127.0.0.1:4999. When the user refers to \\\"this page\\\", \\\"this GUI\\\", or \\\"this app\\\" without naming another target, they mean this GUI. The browser provides no implicit DOM, route, or screenshot context. The client-plugin HMR receiver is active, but client-plugin changes reload without a refresh only while `pnpm run dev:web` is also running from this same checkout to rebuild their bundles; verify that watcher before promising automatic updates. Every other change — the apps/web shell and plain packages — requires rebuilding the affected Web artifacts and verifying this existing URL after a page refresh. Starting another server does not update this GUI. The apps/web Vite ent",
|
|
14
|
+
"event/request/context": "{\"type\":\"item\",\"streamId\":\"958525c2-bc47-46a7-8f96-798a0d21cb90\",\"value\":{\"type\":\"event\",\"event\":{\"type\":\"request/context\",\"seq\":13,\"time\":1788635028159,\"data\":{\"provider\":\"deepseek-official\",\"model\":\"deepseek-v4-flash\",\"contextWindow\":1000000}}}}",
|
|
15
|
+
"event/session/title-llm-request": "{\"type\":\"item\",\"streamId\":\"958525c2-bc47-46a7-8f96-798a0d21cb90\",\"value\":{\"type\":\"event\",\"event\":{\"type\":\"session/title-llm-request\",\"seq\":14,\"time\":1788635028162,\"data\":{\"titleProvider\":\"session-title-first-prompt-llm\",\"messageSeqs\":[8],\"route\":{\"provider\":\"deepseek-official\",\"model\":\"deepseek-v4-flash\"},\"system\":\"Create a concise title for an AI coding-assistant session from the supplied human messages.\\nReturn only the title on one line, **in plain text of natural language**, with no quotes, prefix, explanation, Markdown, XML, or terminal control codes. No code is allowed.\\nUse the language of the messages.\\nAim for about 5 words in non-CJK languages or 10 CJK characters.\",\"messages\":[{\"content\":[{\"type\":\"text\",\"text\":\"Generate the session title from this JSON array of human messages:\\n[{\\\"seq\\\":8,\\\"text\\\":\\\"用一句话回答:1+1=? 不要调用任何工具\\\"}]\"}],\"source\":{\"kind\":\"plugin\",\"plugin\":\"dsh-session-title-llm\"},\"role\":\"user\",\"id\":\"439607f3-0df3-41f5-a972-05c32e3b30e2\"}],\"maxTokens\":64}}}}",
|
|
16
|
+
"event/assistant/chunk": "{\"type\":\"item\",\"streamId\":\"958525c2-bc47-46a7-8f96-798a0d21cb90\",\"value\":{\"type\":\"event\",\"event\":{\"type\":\"assistant/chunk\",\"seq\":16,\"time\":1788635028891,\"data\":{\"turn\":1,\"step\":1,\"chunk\":{\"type\":\"block-start\",\"index\":0,\"blockType\":\"text\"}}}}}",
|
|
17
|
+
"event/assistant/message": "{\"type\":\"item\",\"streamId\":\"958525c2-bc47-46a7-8f96-798a0d21cb90\",\"value\":{\"type\":\"event\",\"event\":{\"type\":\"assistant/message\",\"seq\":21,\"time\":1788635028915,\"data\":{\"turn\":1,\"step\":1,\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"2\"}],\"source\":{\"kind\":\"model\",\"provider\":\"deepseek-official\",\"model\":\"deepseek-v4-flash\"},\"id\":\"74572ee8-a676-4165-8f64-89bb6a742b5e\"},\"usage\":{\"inputTokens\":886,\"outputTokens\":2,\"totalTokens\":10488,\"cacheReadTokens\":9600,\"reasoningTokens\":0}},\"sourceEventSeqs\":[16,17,18,19,20],\"surfaceOp\":\"append\"}}}",
|
|
18
|
+
"event/step/end": "{\"type\":\"item\",\"streamId\":\"958525c2-bc47-46a7-8f96-798a0d21cb90\",\"value\":{\"type\":\"event\",\"event\":{\"type\":\"step/end\",\"seq\":22,\"time\":1788635028916,\"data\":{\"turn\":1,\"step\":1}}}}",
|
|
19
|
+
"event/turn/end": "{\"type\":\"item\",\"streamId\":\"958525c2-bc47-46a7-8f96-798a0d21cb90\",\"value\":{\"type\":\"event\",\"event\":{\"type\":\"turn/end\",\"seq\":23,\"time\":1788635028916,\"data\":{\"turn\":1,\"reason\":{\"kind\":\"completed\"}}}}}"
|
|
20
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# DSH Web UI 移动端(iOS Safari)输入体验研究 — focus 放大 / 键盘遮蔽(v2)
|
|
2
|
+
|
|
3
|
+
- 日期:2026-09-03(v1 初稿;v2 同日晚修订,含 TypingMind 逆向实录与裁决更新)
|
|
4
|
+
- 范围:DSH Web UI(`upstream/deepseek-harness` checkout,tag `dsh-v0.1.2-alpha.5`)在 iOS Safari 上的输入体验。**遵 user 2026-09-03 裁决:只解决 ①focus 放大、②虚拟键盘遮挡两个问题;左栏挤压会话区(原 D4)no-go** —— 那是框架级问题,改它是无底洞;侧栏弹出时内容完整即可,隐藏侧栏后自然回到会话区,挤压是暂时性的,由它去。
|
|
5
|
+
- 参照物:typingmind.com(逆向实录见 §2);上游源码逐行取证;WebKit Bugzilla 现状核查(2026-09-03)。
|
|
6
|
+
- 关联:`docs/50_test-reports/v0.2.1f-plugin-shipped-ui-patches实测报告.md`(手势/mobile CSS 已发布态)、`ios-chat-app-bridge-research.md`(native 壳路线)。逆向工作产物:`work/typingmind-re/`(case: `work/typingmind-web-re`,reverse-skill offline-sample)。
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 0. 结论(TL;DR)
|
|
11
|
+
|
|
12
|
+
两个目标问题全部可在 better-dsh 插件内闭环,**零上游改动**。v2 关键更新:
|
|
13
|
+
|
|
14
|
+
1. **16px 论据已从"推断"升级为"实测"**:TypingMind 聊天输入框 **手机 16px / 桌面 14px**(CDP 活体测量,§2.3)—— 它不是"字体小也没事",而是刻意在手机端维持 16px、桌面才降到 14px(Tailwind `text-base sm:text-sm`)。你看它"字也不大"是桌面印象。
|
|
15
|
+
2. **行业存在两条正路**(§3.2):A) 移动端字号地板 16px(TypingMind 现行);B) JS 在 iOS 窄屏把 `user-scalable` 翻成 `no`(**iOS 10+ 并不禁双指缩放,只杀 focus 自动放大**;Discourse 曾用 A 后整体迁移到 B,原因是 A 的视觉膨胀)。DSH 选 A/B/A+B 是待讨论的决策点。
|
|
16
|
+
3. **键盘问题存在两层事实**(§3.3):浏览器内 Safari = 键盘 overlay 无 opt-out(WebKit 259770 仍 NEW),必须 visualViewport shim;**PWA standalone 态 = 引擎原生 resize(innerHeight/dvh 随键盘收缩)**,无需 shim —— 这就是 TypingMind 零键盘代码的原因(它的推荐移动形态是 PWA)。DSH manifest 已是 fullscreen,"推荐 PWA + 保留浏览器内 shim"可作组合策略。
|
|
17
|
+
4. **动态岛假设有真实对应物但不是本症状的机制**(§4):WebKit 300523(iOS 26.0 仅动态岛机型,键盘关闭/滚动后 viewport 上移数像素侵入安全区,26.1 beta 已修,应用侧无法绕过)—— 证明"动态岛参与 viewport 计算出错"这类 bug 存在,但其症状是**几像素上移**,不是 120% 宽度放大;放大是 font-size 机制(16/14≈1.14 起步,与观察值吻合)。
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. 症状与根因(v1 取证维持有效,摘要)
|
|
22
|
+
|
|
23
|
+
### D1 — focus/JS 定位输入框 → 页面放大到 115–120%
|
|
24
|
+
|
|
25
|
+
- viewport:`apps/web/index.html:5` = `width=device-width, initial-scale=1`,全仓无 `maximum-scale`/`user-scalable` 处理。
|
|
26
|
+
- 字号:composer `.card { font-size: var(--dsh-content-font-size, 14px) }`(`InputBar.module.css:55`,`.input` 继承;contenteditable 锚点 `[data-composer-input]`);`--dsh-content-font-size` 由 `ui-theme/src/boot-theme.ts:21` 写 body,**默认 14px**;permission/model 原生 `<select>` 13px。全仓可聚焦控件 13–14px,全部低于 iOS 16px 阈值 → focus 必放大。16/14 ≈ 1.14,与观察到的 115–120% 吻合。
|
|
27
|
+
|
|
28
|
+
### D2 — 键盘弹出时页面不上推,input 被 overlay 遮住
|
|
29
|
+
|
|
30
|
+
- WebKit 未实现 `interactive-widget`([bug 259770](https://bugs.webkit.org/show_bug.cgi?id=259770),2026-09-03 核查仍 NEW/P2/Nobody)→ iOS 浏览器内键盘 overlay layout viewport,无 opt-out。
|
|
31
|
+
- DSH 布局:`html/body/#root {height:100%}`(`client/web/src/base.css:6`)+ `.frame` grid overflow hidden,composer 在文档流底部 → Safari 只做不可控 page pan,经常 pan 不到位。
|
|
32
|
+
- 唯一引擎 API:`window.visualViewport`(`resize`/`scroll` + `height`/`offsetTop`/`scale`)。`100dvh` 无济于事(响应工具栏不响应键盘)。
|
|
33
|
+
|
|
34
|
+
### D3 — 切会话自动 focus(D1+D2 的连锁触发器,保留在方案内待裁决)
|
|
35
|
+
|
|
36
|
+
`InputBar.tsx` unlock effect:`useEffect(..., [locked, sessionId, editor])` → `editor.getRootElement()?.focus()` —— 注释原文 "Unlock (mount / session switch) returns focus to the box"。切会话/首载 hero 必触发程序化聚焦 = 无人请求的键盘 + 放大。桌面这是特性(键盘用户续打),移动端是 bug。**user 裁决聚焦两症状,D3 正是两症状在"切会话"场景的共同触发层**,修它属于两症状的修复范围,但是否要"移动端切会话后不聚焦"仍留作决策点(§6)。
|
|
37
|
+
|
|
38
|
+
### ~~D4 — 左栏挤压~~(no-go,user 2026-09-03 裁决)
|
|
39
|
+
|
|
40
|
+
不再处理。机制留档备查:`narrowExpanded` 仅跨 1024 断点清除;`computeColumns` sidebar 永不让步(`SIDEBAR_MIN=264`),center 吸收全部赤字。v1 里的 M4(点击 treeitem 自动收栏)随之撤销。
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 2. TypingMind 逆向实录(2026-09-03,work/typingmind-re)
|
|
45
|
+
|
|
46
|
+
### 2.1 分发形态定性:无 DMG,现行 = PWA only
|
|
47
|
+
|
|
48
|
+
官方 install 文档(docs.typingmind.com/install-typingmind-app)明示安装方式 = **PWA**:桌面 Chrome/Edge 地址栏安装图标、iOS Safari Add to Home Screen,"No app store, no download required"。历史上的 macOS app(changelog "MacOS app v1.15.0",Setapp 渠道)已非现行分发。GitHub `typingmind/typingmind` 是 issue/docs 门面,应用本体闭源。**结论:web bundle(typingmind.com 的 Next.js chunks + PWA 全家桶)就是完整 app package** —— 逆向它 = 逆向完整应用。
|
|
49
|
+
|
|
50
|
+
### 2.2 静态扫描(149 个 JS chunk ≈11MB + 4 个 CSS,样本 tarball 已存 case)
|
|
51
|
+
|
|
52
|
+
| 检索 | 结果 |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `visualViewport` | 仅 1 处,Floating UI 定位库内部偏移计算 —— **无键盘 shim** |
|
|
55
|
+
| `maximum-scale` / `user-scalable` / viewport 改写 | **无**(JS 与 CSS 均零命中) |
|
|
56
|
+
| `fontSize:"16px"` JS | 2 处 = Prism 代码高亮主题(噪音) |
|
|
57
|
+
| `safe-area-inset` | **真实使用**:CSS `env(safe-area-inset-bottom/left/right)`;JS 侧 workspace bar 高度 `calc(58px + env(safe-area-inset-bottom))`(chunk 3a4r…)—— 标准全面屏适配通道 |
|
|
58
|
+
| "dynamic island"/"notch" | 零真实命中(唯一 "notch" 是用户评价文案) |
|
|
59
|
+
| 表单基线 | Tailwind Forms 全局:`[type=text],…,textarea,select { font-size:1rem }` = 16px(无 html 根字号覆写) |
|
|
60
|
+
| PWA | manifest `display:standalone`;全套 iPhone/iPad splash;`apple-mobile-web-app-capable` |
|
|
61
|
+
|
|
62
|
+
### 2.3 活体测量(CDP 双宽度,google-chrome --remote-debugging-port + Node 22 原生 WebSocket,`Emulation.setDeviceMetricsOverride`)
|
|
63
|
+
|
|
64
|
+
主输入框 `<textarea id="chat-input-textbox">`,类名含 `text-base ... sm:text-sm`:
|
|
65
|
+
|
|
66
|
+
| viewport | computed font-size(#chat-input-textbox) | 机制 |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| 390×844(手机) | **16px** | `text-base`(1rem)生效 |
|
|
69
|
+
| 1280×900(桌面) | **14px** | `sm:text-sm`(≥640px 才降档) |
|
|
70
|
+
|
|
71
|
+
同页实测:viewport meta 活体值 `initial-scale=1, viewport-fit=cover`;搜索框 16px;根字号 16px(未覆写)。
|
|
72
|
+
|
|
73
|
+
**结论:TypingMind 对 focus 放大的对策 = 手机端输入面 16px(Tailwind 响应式降档手法)+ 不动 viewport meta + 无任何 JS 键盘/缩放处理。** 它的移动端键盘体验依赖 PWA standalone 的引擎原生行为(§3.3)。
|
|
74
|
+
|
|
75
|
+
### 2.4 行业演化旁证:Discourse PR #30877
|
|
76
|
+
|
|
77
|
+
Discourse 曾实现方案 A:`--font-size-ios-input: max(1em, 16px)`(其注释原话 "inputs/textareas in iOS need to be at least 16px to avoid triggering zoom on focus"),后整体替换为方案 B:iOS 上 JS 把 `user-scalable=yes` 翻成 `no`,注释原话:"**In iOS Safari, setting user-scalable=no doesn't actually prevent the user from zooming in. But, it does prevent the annoying 'auto zoom' when focussing input fields with small font-sizes.**" —— 迁移动机是 A 造成输入框视觉膨胀。两条路都被大型产品实证有效。
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 3. 机制结论与充要性(v2 修正)
|
|
82
|
+
|
|
83
|
+
### 3.1 放大机制的准确表述
|
|
84
|
+
|
|
85
|
+
iOS Safari(默认配置)在 `initial-scale=1` 且未禁缩放时,对 computed font-size **< 16px** 的可聚焦控件(input/select/textarea/contenteditable)在 focus 时自动放大 visual viewport 至文本 ≥16px 可读级。这是充分条件级的行业共识(TypingMind 刻意工程 + Discourse 注释 + 大量社区文献),且数值与 DSH 症状吻合(14px→×1.14)。**充要性的诚实边界**:
|
|
86
|
+
- 16px 在**默认 Safari 配置**下充分;非绝对必要(maximum-scale/user-scalable=no 亦阻断)。
|
|
87
|
+
- 例外残存:用户系统级辅助功能(更大文本、Safari 每站 Page Zoom 设置)可抬高实际阈值或残留缩放;`<select>` 聚焦在个别 iOS 版本有独立报告(如 SO #64076385 "not prevented with 16px",403 未能取全文,标题即反例存在性证明)。**这恰是 A+B 双保险的理由**(§6 决策点 1)。
|
|
88
|
+
|
|
89
|
+
### 3.2 方案空间(放大问题)
|
|
90
|
+
|
|
91
|
+
| 方案 | 手段 | 代价 | 先例 |
|
|
92
|
+
|---|---|---|---|
|
|
93
|
+
| **A 字号地板** | 窄屏 CSS:`[data-composer-input],[data-composer-placeholder],input,select,textarea { font-size: max(16px, var(--dsh-content-font-size,14px)) }` | 输入面视觉变大(13/14→16px);对字号偏好用户保序 | TypingMind 现行;Discourse v1 |
|
|
94
|
+
| **B 禁缩放标记** | 窄屏 JS 改写 viewport meta 追加 `maximum-scale=1, user-scalable=no` | iOS 10+ **不禁双指缩放**(Discourse 注释实证),只杀 focus 自动放大;桌面/Android 不动 | Discourse v2(现行) |
|
|
95
|
+
| A+B | 地板兜字义,标记兜例外 | 叠加 | —— |
|
|
96
|
+
|
|
97
|
+
B 的实现要点:只在 narrow + touch 检测下改写(避免桌面与 Android 误伤),boot script 早期执行(先于任何 focus)。
|
|
98
|
+
|
|
99
|
+
### 3.3 键盘机制的两层事实(v2 关键更新)
|
|
100
|
+
|
|
101
|
+
- **浏览器内 Safari**:键盘 overlay,无 opt-out(259770),必须 `visualViewport` shim(v1 M2 方案维持):`intrusion = innerHeight − visualViewport.height`,>阈值且 `scale≈1` 时以 `--dashr-vvh` 收缩 `#root` + `scrollTo(0,0)` 抗 pan。
|
|
102
|
+
- **PWA standalone(Add to Home Screen)**:dev.to 2026-07 实测文(iOS 17/18):键盘弹出时 **`window.innerHeight`、`visualViewport.height`、`100dvh` 全部收缩**(引擎原生 resize,等价 `resizes-content`),`interactive-widget` 在 standalone 被忽略。已知 bug:**键盘关闭后 viewport 卡在小尺寸不恢复**(直到杀进程);社区解法 = blur 后 140ms 对全高元素做 `display:none→reflow→restore` 翻转强制重测(配 backdrop-filter 遮罩隐藏闪跳)。**TypingMind 零键盘代码成立的原因 = 其推荐移动形态是 PWA standalone**。DSH 的 manifest 已是 `display:fullscreen`,具备同路线条件。
|
|
103
|
+
- 策略组合(待讨论):浏览器内 Safari 用户 → M2 shim;PWA 用户 → 引擎原生 + 可选 viewport-stuck 自愈;是否把"装成 PWA"作为官方推荐移动用法(对齐 typingmind)是产品决策点(§6 决策点 3)。
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 4. 动态岛假设验证(user 2026-09-03 提出方向)
|
|
108
|
+
|
|
109
|
+
**方向部分成立 —— 动态岛确实参与了一类真实 viewport bug,但不是本症状的机制:**
|
|
110
|
+
|
|
111
|
+
- [WebKit bug 300523](https://bugs.webkit.org/show_bug.cgi?id=300523)(REGRESSION, iOS 26.0,iPhone 15 Pro 实测 100% 复现,非动态岛机型 iPhone 13 Pro Max iOS 18 不复现):键盘关闭或滚动/重渲染后,Safari **错误计算 visual viewport 高度,内容上移数像素侵入动态岛安全区**(fixed/sticky 头部漂移)。`viewport-fit=cover/contain`、`env(safe-area-inset-top)` padding、visualViewport JS 重算**均无法绕过**;Simon Fraser 确认 iOS 26.1 beta 已修。
|
|
112
|
+
- 该 bug 的症状是**纵向几像素漂移**,不是横向 115–120% 放大,且只在 iOS 26.0 存在(26.1 已修)。DSH 若在 iOS 26.0 真机观察到顶栏上漂数像素,即此 bug,升级即愈,应用侧无动作空间。
|
|
113
|
+
- **机制结论**:布局视口宽度由 viewport meta 决定(390pt 机型 = 390 CSS px),动态岛裁剪通过 `env(safe-area-inset-*)` 暴露、不改变布局宽度与缩放比;"focus 后重新拿 2000px 物理高度再按旧比例放大"无证据支持(按此假设放大应与焦点控件字号无关,而 TypingMind 16px 输入框在同一机型上不放大 —— 反证)。**120% 放大维持 font-size 机制定性**;动态岛类 bug 作为独立 bug class 记录在案。
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 5. 实现方案(v2,全部 better-dsh 插件增量,零上游改动;待批准后动工)
|
|
118
|
+
|
|
119
|
+
配置通道沿用 `__DASHR_MOBILE__`(`web-trust.ts` boot script)扩键:`zoomGuard: 'font' | 'meta' | 'both' | 'off'`(默认待裁决)、`keyboardShim: true`、`focusGate: true`(若裁决保留 D3 修复)。
|
|
120
|
+
|
|
121
|
+
- **M1 放大防护**(对应 §3.2 A/B,二选一或叠加,boot script 装 B、claimStyles 装 A)。
|
|
122
|
+
- **M2 浏览器内键盘 shim**(§3.3;visualViewport → `--dashr-vvh` 收缩 `#root`;阈值 + scale guard + rAF 合帧)。
|
|
123
|
+
- **M2b standalone viewport 自愈**(可选):blur 后 display-flip 重测,防 PWA 态卡小 viewport。
|
|
124
|
+
- **M3 focus gate**(D3 触发层,裁决点 2):boot script 包 `HTMLElement.prototype.focus`,只拦窄屏 `[data-composer-input]` 的非用户发起聚焦(pointerdown 在 `[data-composer-card]`/弹层内放行)。若裁决"移动端切会话保留自动聚焦",则 M3 撤销,症状由 M1+M2 兜底(键盘弹出但可见、不放大)。
|
|
125
|
+
- ~~M4 自动收左栏~~ —— **撤销**(D4 no-go)。
|
|
126
|
+
|
|
127
|
+
桌面零影响(全部 narrow-gated);验证计划维持 v1 §5(4999 预演 + client spec + 真机 iOS 清单),真机清单新增:iOS 26.0 顶栏上漂观察项(对照 300523)、PWA standalone 态键盘 + 卡死自愈验证。
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 6. 待讨论决策点(user 明确先讨论后开发)
|
|
132
|
+
|
|
133
|
+
1. **放大防护选型**:A(字号地板,视觉变大)/ B(user-scalable=no 标记,iOS 10+ 不禁双指)/ A+B 双保险。倾向建议:**B 为主 + A 只保 composer**(B 零视觉扰动且 Discourse 实证;composer 16px 同时改善手机可读性)——待你裁决。
|
|
134
|
+
2. **切会话自动聚焦(D3)**:移动端是否取消?(取消 = 切会话后纯净阅读态;保留 = 现状行为,靠 M1+M2 兜底症状。)
|
|
135
|
+
3. **移动端官方形态**:是否把"Add to Home Screen(PWA standalone)"作为推荐用法(键盘问题在引擎层消失,对齐 typingmind 路线)?浏览器内 Safari 用户仍由 M2 覆盖。
|
|
136
|
+
4. M2b(standalone 卡死自愈)是否纳入首版。
|
|
137
|
+
|
|
138
|
+
## 7. 证据索引(v2 增补)
|
|
139
|
+
|
|
140
|
+
| 事实 | 位置/来源 |
|
|
141
|
+
|---|---|
|
|
142
|
+
| TypingMind 分发 = PWA only(无 DMG) | docs.typingmind.com/install-typingmind-app(2026-09-03) |
|
|
143
|
+
| 输入框 16px@390 / 14px@1280(实测) | CDP 活体测量,脚本 `work/typingmind-re/measure.mjs`,样本 `work/typingmind-re/typingmind-web-bundle.tar.gz` |
|
|
144
|
+
| Tailwind Forms 基线 1rem=16px | 其 CSS chunk(case 存档) |
|
|
145
|
+
| 无键盘 shim / 无 viewport 改写 | bundle 全量 grep(case 存档) |
|
|
146
|
+
| safe-area = env() 标准通道 | 其 CSS + chunk 3a4r…(case 存档) |
|
|
147
|
+
| Discourse A→B 迁移及 B 不禁双指缩放 | Discourse PR #30877 diff(注释原话) |
|
|
148
|
+
| standalone PWA 键盘 resize + 卡死 bug + display-flip 自愈 | dev.to/cederhook 2026-07(iOS 17/18 实测) |
|
|
149
|
+
| 动态岛 viewport bug(上移数像素,26.1 修) | WebKit bug 300523 |
|
|
150
|
+
| 浏览器内无 interactive-widget | WebKit bug 259770(仍 NEW/Nobody) |
|
|
151
|
+
| DSH 侧全部源码锚点 | 见 v1 §6(`apps/web/index.html:5`、`InputBar.module.css:55`、`boot-theme.ts:21`、`InputBar.tsx` unlock effect、`base.css:6`、`columns.ts`、`stores.ts`) |
|
|
152
|
+
|
|
153
|
+
## 8. 参考文献
|
|
154
|
+
|
|
155
|
+
- [WebKit Bug 259770 – interactive-widget](https://bugs.webkit.org/show_bug.cgi?id=259770) · [WebKit Bug 300523 – Dynamic Island viewport shift](https://bugs.webkit.org/show_bug.cgi?id=300523)
|
|
156
|
+
- [Discourse PR #30877 – Replace font-size-ios-input workaround](https://github.com/discourse/discourse/pull/30877)
|
|
157
|
+
- [Fixing the iOS standalone-PWA keyboard bug (dev.to, 2026-07)](https://dev.to/cederhook/fixing-the-ios-standalone-pwa-keyboard-bug-that-shrinks-your-viewport-for-good-63d)
|
|
158
|
+
- [Chromium: viewport resize behavior](https://developer.chrome.com/blog/viewport-resize-behavior/) · [CSS Viewport §interactive-widget](https://drafts.csswg.org/css-viewport-1/#interactive-widget-section)
|
|
159
|
+
- [TIL: Avoid text-sm on inputs (guidefari)](https://guidefari.com/safari-ios-input-zoom/) · [SO #64076385 – 16px 反例存在性](https://stackoverflow.com/questions/64076385/input-zoom-on-iphone-safari-not-prevented-with-16px)
|
|
160
|
+
- typingmind.com(bundle/manifest/viewport 实测);docs.typingmind.com(install 文档)
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# iOS 原生 Chat App 桥接 DSH 调研(Chatbox / Cherry Studio)
|
|
2
|
+
|
|
3
|
+
- 日期:2026-09-02
|
|
4
|
+
- 结论状态:调研完成,未实施
|
|
5
|
+
- 目标:让没有 iOS App 的 Agent(DSH)借用已有原生 iOS App 的壳,获得原生移动端体验。核心思路:把 DSH 桥接成 OpenAI-compatible 的数据流(`/v1/chat/completions` SSE),供 Chatbox / Cherry Studio 消费。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## TL;DR
|
|
10
|
+
|
|
11
|
+
| 问题 | 答案 |
|
|
12
|
+
|---|---|
|
|
13
|
+
| Chatbox 有 iOS App 吗 | **有,已上架 App Store**([id6471368056](https://apps.apple.com/us/app/chatbox-powerful-ai-client/id6471368056),4.6★),Google Play/APK 也有 |
|
|
14
|
+
| Chatbox iOS 能接自定义 OpenAI 兼容端点吗 | **能**。自定义 provider 只支持 OpenAI 规范(`apiHost` + `apiPath`,默认 `/v1/chat/completions`),且支持 JSON/deep-link 一键导入(v1.15.1+) |
|
|
15
|
+
| Cherry Studio iOS 过了测试期吗 | **没有**。截至 2026-09-02 仍是 TestFlight 内测 + IPA 侧载,最新版 v0.1.7(2026-02-27),未上 App Store |
|
|
16
|
+
| DSH 能被桥接吗 | **能,且不需改 harness 内核**。官方已有 4 个程序化表面:`headless` 一次性 CLI、Python/TS SDK(JSON-RPC over stdio,带事件流)、ACP server(标准 Agent Client Protocol)、webhook。推荐:OpenAI shim + Python SDK |
|
|
17
|
+
| 推荐路线(2026-09-02 下午更新,见 §9) | **主路线:ACP 直连** —— iOS ACP 客户端已存在且在 App Store(Agmente 等),DSH 自带 `--profile acp`,套一层 stdio→wss 即零开发直连;**兜底:Chatbox + OpenAI shim**(§4)。Cherry Studio 观望其 GA |
|
|
18
|
+
| iOS 上有"Cherry Studio × Chatbox 整合体"吗 | **有,Agmente**:原生 iOS、App Store 在架、直接说 ACP(thinking/tool call/权限审批全语义)、支持远端 `wss://`(见 §9) |
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 1. Chatbox 现状
|
|
23
|
+
|
|
24
|
+
### 1.1 基本信息
|
|
25
|
+
|
|
26
|
+
- 仓库:[chatboxai/chatbox](https://github.com/chatboxai/chatbox)(Community Edition,**GPLv3**,Electron 桌面端开源;官方注明"regularly sync code from the pro repo",iOS/Android 由 pro 仓库构建发布)。
|
|
27
|
+
- iOS:**App Store 正式在架**([Chatbox - Powerful AI Client](https://apps.apple.com/us/app/chatbox-powerful-ai-client/id6471368056),"Designed for iPad",4.6★/595 ratings)。Android:Google Play(`xyz.chatboxapp.chatbox`)+ 官网 APK。
|
|
28
|
+
- 特性(App Store 页自述):多模型接入、"**Flexibly configure your own model services**"(自定义模型服务)、文档理解、图片生成、Markdown/LaTeX/HTML 渲染、本地优先存储、流式回复。
|
|
29
|
+
|
|
30
|
+
### 1.2 Provider 协议(桥接的关键契约)
|
|
31
|
+
|
|
32
|
+
官方[导入第三方提供方配置文档](https://raw.githubusercontent.com/chatboxai/chatbox-docs/main/guides/providers/import-config.md)(v1.15.1+)定义的 `ProviderConfig`:
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
{
|
|
36
|
+
id: string, name: string,
|
|
37
|
+
type: 'openai', // 目前仅支持 openai 规范的 API
|
|
38
|
+
urls: { website, getApiKey?, docs?, models? },
|
|
39
|
+
settings: {
|
|
40
|
+
apiHost: string, // 如 https://bridge.pc.randomhash.app
|
|
41
|
+
apiPath?: string, // 默认 /v1/chat/completions
|
|
42
|
+
apiKey?: string, // 桥的 Bearer token
|
|
43
|
+
models: ModelInfo[] // modelId/nickname/_type/capabilities/contextWindow/maxOutput
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- **对我们最重要的一条**:自定义 provider 走且仅走 **OpenAI Chat Completions 规范**——这正好是桥接层要实现的东西,无需任何私有协议。
|
|
49
|
+
- 一键导入:`chatbox://provider/import?config=$BASE64_JSON`(deep link,iOS Safari 点击即入 App)。可以做一个"扫码/点链接即配好"的 onboarding 页面。
|
|
50
|
+
- `ModelInfo.capabilities`(`vision | reasoning | tool_use`)与 `contextWindow/maxOutput` 决定 Chatbox 的调用方式与限流,桥应在 `/v1/models` + 导入配置里如实声明。
|
|
51
|
+
|
|
52
|
+
### 1.3 对桥接的含义
|
|
53
|
+
|
|
54
|
+
- Chatbox 把**完整对话历史**随每个请求发来(OpenAI API 无状态语义)→ 桥可以无状态化,也可以做"粘性会话"(见 §4.3)。
|
|
55
|
+
- Chatbox 支持图片输入(vision)→ OpenAI `image_url` 消息可映射到 DSH SDK 的 `SdkEncodedImageBlock`。
|
|
56
|
+
- Chatbox 是纯 chat UI,不渲染 function-call 协议 → agent 的工具调用必须在**服务端**由 DSH 自己闭环,进度只能以文本增量呈现。
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 2. Cherry Studio 移动端现状(用户 asked:测试过了没?)
|
|
61
|
+
|
|
62
|
+
**答案:还没。截至 2026-09-02 仍是内测(TestFlight),未上 App Store。**
|
|
63
|
+
|
|
64
|
+
### 2.1 版本时间线([releases](https://github.com/CherryHQ/cherry-studio-app/releases))
|
|
65
|
+
|
|
66
|
+
| 版本 | 日期 | 要点 |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| 0.1.0 | 2025-10-31 | Day one 内测:[iOS TestFlight](https://testflight.apple.com/join/Mdd3bqvT) + IPA/APK 侧载([LINUX.do 公告](https://linux.do/t/topic/1109448)) |
|
|
69
|
+
| 0.1.1 | 2025-11-04 | CherryAI 免费模型、token 用量展示、局域网同步、provider 增删改修复 |
|
|
70
|
+
| 0.1.2 | 2025-11-20 | Gemini 3 适配、响应式布局、ai-core 升级 |
|
|
71
|
+
| 0.1.5 | 2025-12-25 | 左滑 Topic、粘贴图片、局域网传输改 TCP |
|
|
72
|
+
| 0.1.6 | 2026-01-08 | PDF 上传、**StreamableHTTP MCP**、迁移 pnpm |
|
|
73
|
+
| **0.1.7(最新)** | **2026-02-27** | HeroUI 重构、流式自动滚动开关等;此后 ~6 个月无新 release |
|
|
74
|
+
|
|
75
|
+
### 2.2 仓库与 Roadmap
|
|
76
|
+
|
|
77
|
+
- 仓库:[CherryHQ/cherry-studio-app](https://github.com/CherryHQ/cherry-studio-app)(Expo React Native + Tamagui + Redux,开源)。
|
|
78
|
+
- [Roadmap #234](https://github.com/CherryHQ/cherry-studio-app/issues/234)(最后更新 2026-04-25,仍 open):待办 = 同步桌面端 V2 数据结构、WebDAV、桌面↔移动数据同步、升级 RN 0.83/Expo 55。
|
|
79
|
+
- README:多 LLM provider"逐步集成 OpenAI, Gemini, Anthropic 等";provider/渠道管理已可用(0.1.1 起修复增删改)。
|
|
80
|
+
|
|
81
|
+
### 2.3 判断
|
|
82
|
+
|
|
83
|
+
- 用户两个月前(~2026-07)"在测试"的信息**今天仍成立**:最新 release 停在 2026-02 的 0.1.7,roadmap 未完成,未见 App Store listing。
|
|
84
|
+
- 移动端已有 OpenAI 规范 provider + 自定义渠道 + MCP(StreamableHTTP)——**一旦桥做好,Cherry Studio 移动端同样能直接消费**(它的 provider 模型与桌面版同源:自定义 apiHost + OpenAI 兼容格式)。
|
|
85
|
+
- 风险:项目节奏慢(半年没发版)、iOS 侧载 IPA 需自签(个人 Apple ID 7 天过期)、TestFlight 席位可能满。**不宜作为第一落地目标**。
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 3. DSH 侧可桥接面盘点(本机 upstream `dsh-v0.1.2-alpha.5` 源码实证)
|
|
90
|
+
|
|
91
|
+
DSH 官方已有 **4 个程序化驱动表面**,桥接完全不需要 hack web GUI:
|
|
92
|
+
|
|
93
|
+
| 表面 | 入口 | 特性 | 适配桥接度 |
|
|
94
|
+
|---|---|---|---|
|
|
95
|
+
| **headless profile** | `dsh --profile headless "task"` | 跑一个任务、打印最终答案、exit code 表达成败;不开端口、不留进程 | ★★★(最简 MVP,但**无增量流式**、一次一任务) |
|
|
96
|
+
| **Python SDK**(`pip install deepseek-harness-sdk`,[PyPI 已发布](https://pypi.org/project/deepseek-harness-sdk/):v0.1.2a3 2026-09-01 上架,owner `DeepSeek-Harness`,MIT,随包捆绑同版本 `deepseek-harness-runtime-bin` dsh runtime wheel,无需系统 Node) | `DeepSeekHarness(dsh_home, cwd, provider, model, reasoning_effort, max_tokens)` → `harness.run(prompt, session_id=...)` | 启动 `dsh --profile sdk` 子进程,newline-JSON-RPC over stdio;`RunResult(final_response, finish_reason, events, notifications)`;**`session.event` 通知流 = 增量文本块 → 可转 SSE**;session_id 复用即续会话(durable);支持图片块 | ★★★★★(推荐主通道) |
|
|
97
|
+
| **TS SDK / JSON-RPC server** | `dsh-sdk-client` ↔ `dsh-sdk-jsonrpc-server` 插件 | 同一协议的 TS 实现(`initialize` / `session/prompt` / `session.event` / `session.status` / `subagent.*`) | ★★★★(若桥用 Node 写) |
|
|
98
|
+
| **ACP server** | `dsh --profile acp` | 标准 Agent Client Protocol(JSON-RPC stdio):建/列/续/关会话、发文本+图片、收语义更新、**答权限询问**、取消 | ★★★★(标准化更好,但生态里 chat app 不讲 ACP,最终仍要 shim) |
|
|
99
|
+
| webhook 子系统 | `webhook-github` 等 | 已验证的外部事件 → 创建 Session(fire-and-forget) | ★★(触发型,非对话型) |
|
|
100
|
+
| web GUI 私有协议 | 3080 端口 typertGateway Remote 层 | token 认证 + 私有 typed 协议,**非公开稳定 API**,streaming 刻意在其外 | ✗(不建议第三方 App 直连) |
|
|
101
|
+
|
|
102
|
+
两个额外发现:
|
|
103
|
+
|
|
104
|
+
1. **`packages/test-support/llm-mock-server`(`dsh-llm-mock-server`)——是测试替身,不是桥,对 Python 桥参考价值≈0**:只存在于 upstream 源码 `test-support/` 区(**不随 prod npm 包分发**;入口是 monorepo 根的 `pnpm run mock:llm`)。协议层它确实是 OpenAI-compatible `POST /v1/chat/completions` + Bearer + SSE。但用途是给 **DSH 自己的 LLM 客户端**当假供应商:按预写剧本(FIFO)回放流重置/429/500/畸形 chunk 等抽风行为,在真实 HTTP 边界测 DSH 的重试/退避/超时——这活儿真供应商和 openai 官方 SDK(客户端库)都演不出来,所以 DSH 才自己造了个假服务端。**它不连模型、不跑 agent,把 Chatbox 指上去只会收到剧本假响应**。注意桥的协议契约的权威是 **OpenAI 规范 + Chatbox 实际发送/期待的行为**,不是 DSH 的测试代码——mock server 被 DSH 测试验证过,对 Chatbox 兼容性没有任何背书。Python 桥的正确参考物:openai 官方 Python SDK 的 pydantic 类型(可直接 import 解析请求/构造响应)+ FastAPI/sse-starlette(SSE 分帧),测试时把 openai 官方客户端指向桥跑通即可。mock server 仅在选 TS/Node 桥(用 `dsh-sdk-client`)时才有同语言搬运价值。
|
|
105
|
+
2. **安全默认**:SDK/ACP 是 automation-only(无人值守),`DeepSeekHarness` 必须显式 `dsh_home`(绝不读 `~/.dsh`)——桥应使用独立 DSH_HOME(如 `.dsh-bridge/`),与 prod `~/.dsh` 隔离,天然规避误操作。
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 4. 桥接架构提案(`dsh-openai-bridge`)
|
|
110
|
+
|
|
111
|
+
### 4.1 组件图
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
┌─────────────┐ OpenAI 规范 ┌──────────────────────────┐ JSON-RPC/stdio ┌────────────────┐
|
|
115
|
+
│ Chatbox iOS │ ──────────────▶ │ dsh-openai-bridge │ ───────────────▶ │ dsh --profile │
|
|
116
|
+
│ (App Store) │ GET /v1/models │ (FastAPI, 常驻) │ session/prompt │ sdk (子进程池) │
|
|
117
|
+
│ Cherry 移动端│ POST /v1/chat/ │ Bearer 校验 │ session.event │ → 工具/模型/ │
|
|
118
|
+
│ (TestFlight)│ completions │ model→profile 映射 │ ◀── 通知流 │ 会话持久化 │
|
|
119
|
+
└─────────────┘ (SSE stream) └──────────────────────────┘ └────────────────┘
|
|
120
|
+
▲ │
|
|
121
|
+
│ chatbox://provider/import │ 独立 DSH_HOME(隔离 prod)
|
|
122
|
+
└──── onboarding 页(一键导入配置) │ 部署: LAN 直连 / Caddy 反代(需批准) / Tailscale
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 4.2 协议映射
|
|
126
|
+
|
|
127
|
+
| OpenAI 侧 | DSH 侧 |
|
|
128
|
+
|---|---|
|
|
129
|
+
| `POST /v1/chat/completions` | `harness.run(prompt, session_id=…)` |
|
|
130
|
+
| `messages[]`(含图片 `image_url`) | 文本拼装为任务 prompt;图片 → `SdkEncodedImageBlock` |
|
|
131
|
+
| `model: "dsh-agent"` / `"dsh-web"` / … | 伪模型名 → (profile / provider / reasoning_effort) 映射表 |
|
|
132
|
+
| SSE `delta.content` | `session.event` 通知流中的文本块增量(`on_notification`) |
|
|
133
|
+
| `finish_reason: stop/length` | `RunResult.finish_reason`: `completed`→stop、`max-tokens`→length、`error`→stop+错误文本 |
|
|
134
|
+
| `GET /v1/models` | 映射表导出(同时生成 Chatbox `ProviderConfig` JSON) |
|
|
135
|
+
| `Authorization: Bearer <key>` | 桥自有 API key(用户填进 Chatbox 的 apiKey 字段) |
|
|
136
|
+
|
|
137
|
+
### 4.3 会话连续性(关键设计点)
|
|
138
|
+
|
|
139
|
+
OpenAI 请求无状态(全量 history 每次都发),两条路线:
|
|
140
|
+
|
|
141
|
+
- **A. 无状态(MVP)**:每次请求把整段 history 压成一个 prompt,跑一次性任务(headless 或新 session)。实现最简,语义忠实;代价是 DSH 侧上下文/工具状态不复用、每请求冷启动。
|
|
142
|
+
- **B. 粘性会话(推荐 V1)**:以 (chat 标识 + history 前缀哈希) 映射到稳定 `session_id`,只发**最新一轮 user 消息**,DSH 侧 durable session 保留上下文与工作区状态;检测到 history 被编辑/回退则重开 session。SDK 明确支持:*"Reusing both a harness and session id continues the durable conversation"*。
|
|
143
|
+
- 进程模型:常驻 `DeepSeekHarness` 子进程池(按 session 粘住),避免每请求 spawn dsh(冷启动秒级)。
|
|
144
|
+
|
|
145
|
+
### 4.4 流式与长任务
|
|
146
|
+
|
|
147
|
+
- Agent 一轮可能跑数分钟(工具循环)。SSE 需周期性 keep-alive(注释帧/心跳 delta),防止 iOS URLSession/Chatbox 超时断流。
|
|
148
|
+
- 工具活动不可用 function-call 协议呈现 → 约定**文本化进度**:如 `⚙️ running bash …` 细节折叠(Chatbox 渲染 Markdown,可用 `> ` 引用块或 `<details>`),最终只保留 assistant 正文。
|
|
149
|
+
- 非流式兜底:`stream:false` 直接回 `RunResult.final_response`。
|
|
150
|
+
|
|
151
|
+
### 4.5 部署拓扑(贴合本机现状)
|
|
152
|
+
|
|
153
|
+
- 桥进程:systemd user unit,监听 `127.0.0.1:<port>`;独立 `DSH_HOME=~/.dsh-bridge`。
|
|
154
|
+
- 暴露三选一:① LAN 直连(iPhone 同 WiFi,`http://192.168.31.130:port/v1`,Chatbox 允许 http 自定义 host);② **Caddy 反代**加子域(如 `dshapi.pc.randomhash.app` → 桥端口,HTTPS 自动证书,外网可达)——改 `/etc/caddy/Caddyfile` 按 AGENTS.md 需明确批准;③ Tailscale(最省事且不暴露公网)。
|
|
155
|
+
- 凭据:桥的 Bearer key 即 Chatbox 里填的 API key;`.env` 沿用 `~/.dsh/.env` 的真实 key 供 DSH 模型侧使用。
|
|
156
|
+
|
|
157
|
+
### 4.6 安全
|
|
158
|
+
|
|
159
|
+
- 桥 = 公网可打到的 agent 执行面:必须有 Bearer 鉴权、限流、超时;建议白名单 workspace(`cwd` 固定到专用目录)+ sandbox 策略(workspace-write、危险操作 guard)。
|
|
160
|
+
- automation 表面无人工确认(ACP 才有 permission answer 循环,SDK 直接跑)→ 远端触发 `bash` 等工具的风险要靠 DSH 侧 sandbox/permission profile 约束,而不是靠 App。
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 5. 先例(prior art)
|
|
165
|
+
|
|
166
|
+
- [i-am-logger/claude-code-proxy](https://github.com/i-am-logger/claude-code-proxy)、[AntonioAEMartins/claude-code-proxy](https://github.com/AntonioAEMartins/claude-code-proxy):把 Claude Code CLI 包成 OpenAI Chat Completions API(含 SSE)——**同一模式的成熟先例**,证明"CLI agent → OpenAI 兼容端点 → 任意 chat App"路线可行且社区有需求。
|
|
167
|
+
- keenturbo/[2API](https://github.com/keenturbo/2API):各家模型互转 OpenAI 兼容 API 的教程集。
|
|
168
|
+
- 桥的 OpenAI 协议面:openai 官方 Python SDK(客户端库;其 pydantic 类型可 import 来做服务端解析/构造,也是测试桥的首选客户端)+ FastAPI/sse-starlette;DSH repo 内 `dsh-llm-mock-server`(TS,测试替身)仅在 TS 桥路线下可搬运其服务端代码(见 §3)。
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 6. 风险与缺口
|
|
173
|
+
|
|
174
|
+
| 风险 | 影响 | 缓解 |
|
|
175
|
+
|---|---|---|
|
|
176
|
+
| Cherry Studio 移动端长期 0.1.x、半年无 release | 第二目标不确定 | 先落地 Chatbox;Cherry 观望 v0.2/GA |
|
|
177
|
+
| 长任务 SSE 被移动端掐断 | 体验中断 | 心跳帧 + 非流式兜底 + 断线后按 session_id 拉回结果 |
|
|
178
|
+
| 每请求冷启动慢(spawn dsh) | 首字延迟 | 常驻子进程池 + 粘性会话 |
|
|
179
|
+
| 无 function-call 透传,工具过程只能文本化 | 可视化降级 | 约定 Markdown 进度样式;未来可发富卡片(仅 dsh web 有) |
|
|
180
|
+
| 公网暴露 agent 执行面 | 安全 | Bearer + 限流 + sandbox profile + Tailscale 优先 |
|
|
181
|
+
| Chatbox 移动端与桌面端 provider 能力可能有差 | 配置不通 | iOS 实测验证(App Store 版当前支持自定义 provider,见 §1.1);deep link 导入兜底 |
|
|
182
|
+
| GPLv3(Chatbox CE) | 仅 API 互通无碍;若 fork 其代码需遵守 GPL | 我们只做服务端,不碰其代码 |
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 7. 建议路线
|
|
187
|
+
|
|
188
|
+
1. **MVP(半天级)**:FastAPI 单文件桥:`POST /v1/chat/completions`(`stream:false`)→ 每请求 `dsh --profile headless` 拼全量 history 跑一次;`/v1/models` 假列表 + Chatbox 导入 JSON。局域网 iPhone 实测 Chatbox 连通。
|
|
189
|
+
2. **V1(1–2 天)**:换 Python SDK 常驻进程池 + 粘性 session_id + `session.event`→SSE 增量流 + 心跳;onboarding 页生成 `chatbox://provider/import` deep link;Caddy/Tailscale 暴露。
|
|
190
|
+
3. **V2(按需)**:图片输入(vision)、`model` 后缀映射 reasoning_effort、多 profile 伪模型(dsh-fast/dsh-max)、Cherry Studio 移动端接入验证、ACP 后端可替换实现。
|
|
191
|
+
|
|
192
|
+
> 附带红利:桥一旦存在,**任何** OpenAI 兼容客户端(不止这两个 App:LobeChat、OpenWebUI、Raycast、快捷指令……)都能直接消费 DSH agent。
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 8. 参考
|
|
197
|
+
|
|
198
|
+
- Chatbox:[GitHub](https://github.com/chatboxai/chatbox) · [App Store](https://apps.apple.com/us/app/chatbox-powerful-ai-client/id6471368056) · [官网](https://chatboxai.app) · [provider 导入配置文档](https://raw.githubusercontent.com/chatboxai/chatbox-docs/main/guides/providers/import-config.md)
|
|
199
|
+
- Cherry Studio 移动端:[GitHub](https://github.com/CherryHQ/cherry-studio-app) · [Releases](https://github.com/CherryHQ/cherry-studio-app/releases) · [Roadmap #234](https://github.com/CherryHQ/cherry-studio-app/issues/234) · [TestFlight](https://testflight.apple.com/join/Mdd3bqvT) · [发布公告 (LINUX.do)](https://linux.do/t/topic/1109448)
|
|
200
|
+
- DSH(本机源码 `upstream/deepseek-harness` @ `dsh-v0.1.2-alpha.5`):`python/README.md`、`python/sdk/README.md`、`packages/sdk/protocol/README.md`、`packages/bundle/headless/README.md`、`packages/acp/README.md`、`packages/api/README.md`、`packages/test-support/llm-mock-server/README.md`
|
|
201
|
+
- 先例:[claude-code-proxy (i-am-logger)](https://github.com/i-am-logger/claude-code-proxy) · [claude-code-proxy (AntonioAEMartins)](https://github.com/AntonioAEMartins/claude-code-proxy) · [2API](https://github.com/keenturbo/2API)
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## 9. 追加调研(2026-09-02 下午):ACP 路线成立,OpenClaw 官方 iOS 已上架
|
|
206
|
+
|
|
207
|
+
> 背景:用户把目标升级为"iOS 上现成的、能接 **Agent**(非纯 LLM)的 App"——即 Cherry Studio(Agent 接口完善)× Chatbox(App Store 入场券)的整合体。结论:**这个整合体已经存在,而且不止一个。**
|
|
208
|
+
|
|
209
|
+
### 9.1 iOS 上的 ACP 原生客户端([ACP 官方 clients 页](https://agentclientprotocol.com/get-started/clients) Mobile clients 专区)
|
|
210
|
+
|
|
211
|
+
| App | App Store | 协议/形态 | 远端连接 | 备注 |
|
|
212
|
+
|---|---|---|---|---|
|
|
213
|
+
| **[Agmente](https://github.com/rebornix/Agmente)** ⭐首推 | ✅ [id6756249477](https://apps.apple.com/us/app/agmente/id6756249477) | **原生 ACP** + Codex app-server;Swift 原生、MIT;工具调用/结果/会话历史全渲染 | ✅ 官方路径:远端 host 起 agent → `stdio→wss`(`npx -y @rebornix/stdio-to-ws --persist --grace-period 604800 "<agent> --acp" --port 8765`)→ TLS → App 填 `wss://`;支持 Bearer + Cloudflare Access | 作者 rebornix(GitHub 资深工程师);README 明示支持 Copilot CLI/Gemini CLI/Claude Code adapters/Qwen/Mistral Vibe 等"任何 ACP agent" |
|
|
214
|
+
| [Shellular](https://github.com/shellular-org/app) | ✅ [id6761985327](https://apps.apple.com/us/app/shellular/id6761985327) | Claude Code/Codex/Pi 手机遥控(ACP 类) | ✅ | 2026-07 HN 热帖"用手机跑编程 agent" |
|
|
215
|
+
| [MobileVibe/Mobvibe](https://github.com/Eric-Song-Nop/mobvibe) | ✅ [id6758524635](https://apps.apple.com/tw/app/mobilevibe/id6758524635) | Claude Code Remote Control | ✅ | LINUX.do 作者帖(封号后自做) |
|
|
216
|
+
| [Happy](https://github.com/slopus/happy) | ✅ [id6748571505](https://apps.apple.com/us/app/happy-claude-code-client/id6748571505) | Claude Code/Codex 专用(`happy` CLI 包装 + 自家 E2E relay) | ✅(自家 server 中转) | 产品成熟但**绑定 claude/codex 两个 CLI**,对任意 ACP agent 不通用 |
|
|
217
|
+
| ACP UI(formulahendry/acp-ui) | 未逐个核实 | 声称 iOS/Android/Web | — | 备选 |
|
|
218
|
+
|
|
219
|
+
**对 DSH 的意义——零开发直连路径(理论,待 iPhone 实测)**:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
# PC 侧(DSH 自带 ACP server profile):
|
|
223
|
+
DSH_HOME=~/.dsh-acp npx -y @rebornix/stdio-to-ws --persist --grace-period 604800 \
|
|
224
|
+
"dsh --profile acp" --port 8765
|
|
225
|
+
# Caddy 给 wss:// 套 TLS(改 Caddyfile 需批准,AGENTS.md 约定)
|
|
226
|
+
# iPhone Agmente 填 wss://<host> + Bearer
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
ACP 的表达力正是 OpenAI shim 给不了的:**thinking 过程、tool call 事件、权限审批(permission request→iOS 上点批准)、cancel**,DSH 的 ACP server(`packages/acp`:建/续/关会话、MCP attach、模型选择、文本+图片 prompt、语义更新、答权限、取消)全部原生覆盖。原 §4 的 OpenAI shim 方案**降级为兜底**(覆盖 Chatbox 等纯 LLM 壳 App 仍有价值)。
|
|
230
|
+
|
|
231
|
+
### 9.2 OpenClaw:官方 iOS App **已于 2026-06-30 上架** App Store + Google Play
|
|
232
|
+
|
|
233
|
+
- 多源报道(2026-06-30):[新浪](https://finance.sina.com.cn/roll/2026-06-30/doc-inifczzw4921295.shtml)、[ZOL](https://ai.zol.com.cn/1207/12079539.html)、[太平洋](https://www.pconline.com.cn/ai/article/1612736.html)、[MacMagazine](https://macmagazine.com.br/post/2026/06/30/openclaw-ganha-aplicativo-para-ios-com-controle-remoto-de-agentes/)、[36氪("OpenClaw和Cursor杀入手机")](https://www.36kr.com/p/3875041298961416);repo 有 `apps/ios/`;设计方向见 [issue #85731](https://github.com/openclaw/openclaw/issues/85731)(含 approvals/approval queue 界面 = 权限审批一等公民)。
|
|
234
|
+
- 对 DSH:**无直接消费价值**——OpenClaw App 只连 OpenClaw 自家 gateway(它本身是 agent 运行时,不是通用 agent 客户端)。除非把 DSH 包装成 OpenClaw 的 skill/agent(不建议)。但生态信号明确:OpenClaw 也在向 ACP 收敛(acpx CLI 在 ACP clients 榜单、`@openclaw/acp-standard` plugin PR #28662)。
|
|
235
|
+
- 用户问"OpenClaw 有没有 iOS 计划"——答案:不只是计划,**已上架两个月**。
|
|
236
|
+
|
|
237
|
+
### 9.3 Cherry Studio iOS 状态复查(2026-09-02 当日二次核实)
|
|
238
|
+
|
|
239
|
+
- repo **活跃**:`pushed_at = 2026-09-02T13:12Z`(当天数小时前还在推代码);stars 3776。
|
|
240
|
+
- 但 **最新 release 仍是 v0.1.7(2026-02-27),TestFlight 仍在,App Store 仍无 listing**——用户在 App Store 搜不到是准确的。判断:v0.2/桌面端 V2 数据同步重构进行中(roadmap #234),上架遥遥无期,继续观望。
|
|
241
|
+
- **协议考证(纠正)**:用户猜测 Cherry Studio 接 OpenClaw 走 ACP。经官方文档 ask 接口核实:Cherry 官方文档中 **Cherry Agent 的后端协议要求是 Anthropic 兼容(`/v1/messages`)**([providers 文档](https://docs.cherryai.com.cn/pre-basic/providers.md):"Cherry Agent 需要此类型(Anthropic 兼容)"),文档中未见 ACP。网上"Cherry Studio 接 OpenClaw"教程大概率是把 OpenClaw 的 **Anthropic 兼容端点**当 provider 用。若 Cherry iOS 将来上架,DSH 对接它的正确姿势可能是 **Anthropic `/v1/messages` shim**(而非 ACP bridge),比 OpenAI shim 多一层 thinking/tool_use 块语义,工作量相近。
|
|
242
|
+
- DeepWiki 显示 cherry-studio 桌面版有 "Code Tools and CLI Integration"(Claude Code 等 CLI 集成)章节,其协议(ACP 或内部 spawn)未证实——若为 ACP,则 Cherry 桌面/未来 iOS 亦可直连 DSH ACP server。
|
|
243
|
+
|
|
244
|
+
### 9.4 更新后的路线排序
|
|
245
|
+
|
|
246
|
+
1. **主路线(ACP 直连,≈零开发)**:Agmente + `@rebornix/stdio-to-ws` + `dsh --profile acp` + Caddy TLS。先 iPhone 实测握手兼容性(DSH ACP 是标准 Zed 式 ACP,理论兼容,未实测)。
|
|
247
|
+
2. **兜底(OpenAI shim,1–2 天)**:§4 方案不变,覆盖 Chatbox 等一切 OpenAI 兼容客户端。
|
|
248
|
+
3. **观望**:Cherry Studio iOS(若上架,按 Anthropic-compat shim 对接);OpenClaw App(与 DSH 无消费关系)。
|
|
249
|
+
|
|
250
|
+
### 9.5 本机 runtime 网络接口扫描(2026-09-02 实测;用户已装 Agmente,确认其服务端形态 = WS/WSS + Bearer)
|
|
251
|
+
|
|
252
|
+
**问题**:本机已装的 agent 运行时,有没有原生就挂 WS/WSS(说 ACP)的?——**答案:没有。**
|
|
253
|
+
|
|
254
|
+
| Runtime(本机实测) | 网络原生接口 | ACP 传输层 | Agmente 直连? |
|
|
255
|
+
|---|---|---|---|
|
|
256
|
+
| **Hermes** v0.20.6 | HTTP API ×4 个 profile 常驻(`0.0.0.0:8642-8645`,JSON+SSE:`/api/sessions/{id}/chat`、`/v1/runs`、OpenAI 兼容子集);**WebSocket 仅 `/v1/browser-control/ws`**(浏览器控制通道,一次性 ticket + 子协议,非 agent 对话入口)——源码 `~/.hermes/hermes-agent/gateway/platforms/api_server.py` 实证 | `hermes acp` = **stdio only**(`--help` 无任何 port/ws 参数,面向 Zed/VS Code/JetBrains) | ❌ HTTP API 是 Hermes 私有 REST,非 ACP |
|
|
257
|
+
| **OpenCode** 1.17.13 | `opencode serve`(自家 HTTP API + basic auth) | `opencode acp --port N` 的 **port 是它内部 HTTP server**(供 ACP 层经 SDK 调 opencode 本体),ACP wire 本身接在 stdin/stdout(二进制字符串实证:handler 把 `process.stdin` ReadableStream 喂给 ACP connection,stdout 做 sink) | ❌ 矩阵文档"stdio + HTTP"的 HTTP 部分不承载 ACP |
|
|
258
|
+
| **DSH** 0.1.2-alpha.3/5 | web GUI 私有协议(3080/3081/4999) | `dsh --profile acp` = stdio(upstream `packages/acp` 明确 JSON-RPC over stdio) | ❌ |
|
|
259
|
+
| Pi / Claude Code / agy | 无 HTTP/WS daemon | 无原生 ACP(Claude 有 `@agentclientprotocol/claude-agent-acp` adapter) | ❌(Claude 走 adapter+wrapper) |
|
|
260
|
+
| OpenClaw | WS gateway :18789 是**自家协议**非 ACP;且不在本机(在 dev4) | `openclaw acp`(bridge to gateway) | ❌ |
|
|
261
|
+
|
|
262
|
+
**结论与对策**:ACP 生态目前的远端标准形态就是"远端 host 起 stdio ACP agent → `stdio-to-ws` 包成 wss"(Agmente 官方 quick start 即如此)。`@rebornix/stdio-to-ws` v0.2.0(npm 实查:Apache-2.0,31KB,仅依赖 ws+minimist,`--persist --grace-period 604800` 断连后子进程保活 7 天)就是为这个缺口而生的通用件。零代码拓扑:
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
iPhone Agmente ─wss+Bearer→ Caddy(TLS) → stdio-to-ws → stdio → ┬─ dsh --profile acp (DSH_HOME=~/.dsh-acp)
|
|
266
|
+
├─ hermes acp
|
|
267
|
+
└─ opencode acp
|
|
268
|
+
```
|
|
269
|
+
每个 agent 一个 wrapper 实例/端口。若要**单端点多路复用多 agent**(一个 wss,session 级路由到不同 agent),才需要自研小 ACP-over-WS 网关(Node+ws 几百行,newSession 时路由)——建议先跑通单 agent 实测 Agmente 兼容性再决定。
|
|
270
|
+
|
|
271
|
+
⚠️ 顺手发现:Hermes 4 个 API server 绑 `0.0.0.0`(全网卡监听);已有 `API_SERVER_KEY` 认证(`~/.hermes/.env`),但建议确认 LAN 暴露是否合意。
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## 10. DSH-ACP 实测记录(2026-09-02 晚,全部通过)
|
|
276
|
+
|
|
277
|
+
测试环境:`DSH_HOME=<ws>/.dsh-test`(隔离测试 home,真实模型 key),agent 工作目录 `.scratch/acp-playground`,模型 `deepseek-official/deepseek-v4-flash`(3 次真实小回合)。脚本:`.scratch/acp_smoke.py`(stdio 直连)、`.scratch/acp_ws_test.py`(WS 两阶段,含断线重连)。
|
|
278
|
+
|
|
279
|
+
### 10.1 Phase 1 — stdio 直连 `dsh --profile acp`:✅ PASS
|
|
280
|
+
|
|
281
|
+
| 步骤 | 结果 |
|
|
282
|
+
|---|---|
|
|
283
|
+
| `initialize` | 1.7s 返回;agentInfo `deepseek-harness-acp v0.0.1`;caps:mcp.http=true、sessionCapabilities **close/list/resume**、promptCapabilities image=false |
|
|
284
|
+
| `session/new` | sessionId + configOptions(model select:deepseek-official/deepseek-v4-flash) |
|
|
285
|
+
| `session/prompt`("Reply with exactly: ACP-OK") | 流式 `agent_message_chunk` + `usage_update`;stopReason=end_turn;回复正是 `ACP-OK` |
|
|
286
|
+
| `session/close` + stdin EOF | 干净退出 |
|
|
287
|
+
|
|
288
|
+
### 10.2 Phase 2 — WebSocket(Agmente 确切路径):✅ PASS
|
|
289
|
+
|
|
290
|
+
`npx -y @rebornix/stdio-to-ws --persist --grace-period 604800 "dsh --profile acp" --port 8800`
|
|
291
|
+
|
|
292
|
+
- **Phase A**:connect → 信封帧 `{"type":"connected","clientId":…}` → initialize → session/new → prompt → **`WS-ACP-OK`**;期间观察到 `agent_thought_chunk`(思维流)、`agent_message_chunk`、`usage_update`。
|
|
293
|
+
- **Phase B(断线重连)**:断开 → 带 `X-Client-Id` 头重连 → 信封 `{"type":"reconnect",…}`(**同一 dsh 子进程被重新挂上**)→ `session/list` 列出 2 个会话(含 Phase 1 stdio 测试的历史会话 = 跨进程持久化)→ 对原 sessionId 直接 prompt "What was the secret word?" → 回答 **`KIWI-7741`**(断线前埋的秘密词)→ **memory-across-reconnect: PASS**。
|
|
294
|
+
|
|
295
|
+
### 10.3 踩坑记录(对给 Agmente 配 DSH 有直接参考价值)
|
|
296
|
+
|
|
297
|
+
1. **换行分隔必须由客户端补**:wrapper 是纯字节管道,ACP 是 newline-delimited JSON-RPC。发 JSON 不带 `\n` → dsh 行读取器永远等待 → 表现为"子进程沉默卡死"。补 `\n` 后立刻全通。
|
|
298
|
+
2. **wrapper 信封协议**:每连接首帧 `{"type":"connected","clientId"}`;重连须带 `X-Client-Id` 请求头 → 收 `{"type":"reconnect"}` 并回放断线期间缓冲的消息。ACP 消息解析前要跳过信封帧。
|
|
299
|
+
3. **黏包**:一个 stdout chunk 的多条 JSON 可能黏在一个 WS 帧里,客户端须按行拆分。
|
|
300
|
+
4. **`session/load` 不存在**(-32601);DSH 的会话恢复按 caps 是 **`session/resume`**。且 `--persist` 同子进程存活期间,直接对原 sessionId `session/prompt` 也能续(B 阶段即如此)。
|
|
301
|
+
5. `promptCapabilities.image=false`:当前默认路由不接受图片 prompt。
|
|
302
|
+
|
|
303
|
+
### 10.4 结论与服务端建议命令
|
|
304
|
+
|
|
305
|
+
DSH → iOS Agmente 的服务端链路**已在协议层全通**(stdio 与 WS 两种传输、断线重连、跨进程会话持久化、思维流/工具事件通道均验证)。剩余步骤只有网络暴露与 iPhone 真机连接:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
# systemd user service 建议形态
|
|
309
|
+
Environment=DSH_HOME=/home/u1/workspaces/dashr/.dsh-acp # 建议专用 home,与 .dsh-test/prod 分离
|
|
310
|
+
Environment=npm_config_cache=/home/u1/workspaces/dashr/.scratch/npm-cache
|
|
311
|
+
ExecStart=/usr/bin/env npx -y @rebornix/stdio-to-ws --persist --grace-period 604800 \
|
|
312
|
+
"dsh --profile acp" --port 8800
|
|
313
|
+
# Caddy: dshacp.pc.randomhash.app { reverse_proxy 127.0.0.1:8800 } → iPhone Agmente 填 wss://dshacp.pc.randomhash.app
|
|
314
|
+
# (改 Caddyfile 需批准;wrapper 本身不带 Bearer 校验,认证依赖 TLS + Cloudflare Access 或前置网关,见 §9.1 Agmente 的 Access 支持)
|
|
315
|
+
|
|
316
|
+
### 10.5 iPhone Agmente 真机首连(2026-09-02 深夜)— 首连成功,notice 已定性
|
|
317
|
+
|
|
318
|
+
用户 iPhone Agmente 连 `ws://192.168.31.130:8800`:服务器被识别(deepseek-harness-acp v0.0.1),随后 App 弹 `Session…NotSupported` 提示。wrapper 日志定性:
|
|
319
|
+
|
|
320
|
+
- Agmente 连接序列:initialize(clientInfo "Agmente iOS")→ `session/list` → **`session/load {sessionId:"capability-probe"}` 能力探测** → -32601 → 弹 notice。它还实测了 X-Client-Id 重连(wrapper 信封 reconnect 正常工作)。
|
|
321
|
+
- **规范定性(ACP schema 原文)**:`session/resume` = "Resumes an existing session without returning previous messages (unlike session/load)"。DSH 实现的是 resume(与它宣告的 `sessionCapabilities.resume{}` 一致),未实现 `session/load`(完整 transcript 回放)。**该 notice 是能力降级提示,非故障**——Agmente 自己的 App Store 描述就是这个模式的说明(历史本地存、agent 记得即可续)。
|
|
322
|
+
- **聊天主路径已验证**:模拟 Agmente 全序列(initialize→list→load 探测→session/new cwd=/tmp→中文 prompt)→ stopReason=end_turn、回复正常。**用户操作:关掉 notice,新建对话直接聊。**
|
|
323
|
+
- 次要坑:Agmente 发过 `cwd:"~/.dsh-acp"` 被 DSH 拒(-32602 要求绝对路径)——App 里 workspace/目录字段须填绝对路径(如 `/tmp` 或 `/home/u1/workspaces/dashr/.scratch/acp-playground`),不能带 `~`。
|
|
324
|
+
- 消除 notice/获得完整回放需给 DSH 实现 `session/load`(upstream 功能缺口,非配置项);当前 resume-only 已满足移动端使用。
|