relay-dsh-plugin-claude 0.1.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 ADDED
@@ -0,0 +1,202 @@
1
+ # Claude Code Conversations for DeepSeek Harness
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ `relay-dsh-plugin-claude` adds **Claude Code as a conversation backend** to the
6
+ official [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
7
+ (DSH) Web UI. After installation, **Claude Code** appears in DSH's New Session
8
+ mode menu. One DSH Session is bound to one Claude Agent SDK session.
9
+
10
+ ![Codex and Claude Code in the DSH New Session mode menu](docs/images/dsh-new-session-backends.jpg)
11
+
12
+ The screenshot was captured from official DSH `0.1.1-rc.2` with the Codex and
13
+ Claude plugins installed. If you install only this plugin, only **Claude Code**
14
+ is added.
15
+
16
+ ## Do I Need This Plugin?
17
+
18
+ Install it when you want to:
19
+
20
+ - use Claude Code inside DSH instead of switching to a separate Claude Code
21
+ interface;
22
+ - keep DSH's native conversation history, composer, approvals, questions, and
23
+ tool presentation;
24
+ - let one DSH Session continue the same Claude Agent SDK session across turns;
25
+ - use Claude models, reasoning, interruption, and DSH-contributed tools in the
26
+ same conversation.
27
+
28
+ You do not need it to use DSH's standard agents. It also does not add Relay
29
+ Events, file browsing, or a terminal panel. Those are separate optional plugins.
30
+
31
+ ## Quick Start With Official DSH
32
+
33
+ The steps below were validated with:
34
+
35
+ - DeepSeek Harness `0.1.1-rc.2`, commit
36
+ [`b150a551`](https://github.com/deepseek-ai/deepseek-harness/commit/b150a551b8d465e31e418e1b2eaf5e79bbb7d28e)
37
+ - Node.js 22.13 or newer
38
+ - `pnpm` available on `PATH`
39
+
40
+ DSH is currently a developer preview and may introduce compatibility-breaking
41
+ changes. This repository tracks official releases and records its tested version
42
+ here.
43
+
44
+ ### 1. Prepare Claude Code authentication
45
+
46
+ The Claude Agent SDK is installed as a normal dependency of this plugin. Before
47
+ starting your first DSH Claude session, complete normal local Claude Code setup
48
+ and authentication for the user that will run DSH.
49
+
50
+ Follow the official [Claude Code setup documentation](https://code.claude.com/docs/en/setup),
51
+ then start Claude Code once to verify that authentication succeeds:
52
+
53
+ ```bash
54
+ claude
55
+ ```
56
+
57
+ Credentials stay under Claude Code's normal local authentication mechanism; this
58
+ plugin does not collect them.
59
+
60
+ ### 2. Install from npm
61
+
62
+ Stop a running DSH Web process before changing Profile bundles. Then run:
63
+
64
+ ```bash
65
+ npx @deepseek-ai/dsh@0.1.1-rc.2 plugin --profile web add relay-dsh-plugin-claude
66
+ ```
67
+
68
+ The official DSH CLI initializes the `web` Profile if it does not exist, asks
69
+ `pnpm` to install the npm package and Claude Agent SDK dependencies, and adds the
70
+ plugin's bundle layer. No Relay checkout is required. The current npm release is
71
+ `0.1.0`. The first installation can
72
+ take longer while platform-specific Claude Agent SDK packages are downloaded;
73
+ wait for pnpm's final `Done` message or an explicit error.
74
+
75
+ If you already installed the `dsh` command, the shorter equivalent is:
76
+
77
+ ```bash
78
+ dsh plugin --profile web add relay-dsh-plugin-claude
79
+ ```
80
+
81
+ To test an unreleased commit instead, replace the package name with
82
+ `github:yangbobo2021/relay-dsh-plugin-claude`.
83
+
84
+ ### 3. Start or restart DSH Web
85
+
86
+ ```bash
87
+ npx @deepseek-ai/dsh@0.1.1-rc.2 web
88
+ ```
89
+
90
+ If you use an installed command, run `dsh web` instead. Bundle membership is read
91
+ at startup, so restarting after installation, update, or removal is required.
92
+
93
+ ### 4. Start a Claude Code conversation
94
+
95
+ 1. Open the DSH URL printed in the terminal. The default is
96
+ `http://127.0.0.1:3080`.
97
+ 2. On first launch, read the DSH testing notice and select **Continue**.
98
+ 3. Select **Add workspace** in the left sidebar and choose the project directory
99
+ Claude may work in.
100
+ 4. Select **New Session**.
101
+ 5. Open the mode menu labeled **Standard mode** and choose **Claude Code**.
102
+ 6. Enter a message and send it. Choose the backend before the first message;
103
+ existing sessions keep the backend with which they were created.
104
+
105
+ There is no separate activation command. A successful install plus a DSH restart
106
+ activates the bundle and registers the managed **Claude Code** mode automatically.
107
+
108
+ ## What Works
109
+
110
+ - One persistent Claude Agent SDK session per DSH Session
111
+ - Model and reasoning selection
112
+ - Streaming answers and tool activity in the native DSH conversation
113
+ - DSH approval and user-question flows
114
+ - Interruption and session continuation
115
+ - Generic DSH tools exposed through an in-process Claude SDK MCP server
116
+
117
+ Tools execute through the owning Agent's DSH tool runtime and remain subject to
118
+ DSH permissions and Claude approval behavior. The tool bridge requires the
119
+ default SDK backend. If a developer explicitly selects the CLI fallback, the
120
+ plugin refuses contributed DSH tools instead of silently dropping them.
121
+
122
+ ## Plugin Boundary and Relay
123
+
124
+ This repository was designed and compatibility-tested in
125
+ [Relay](https://github.com/yangbobo2021/Relay), an open-source project for
126
+ long-running agent work, external-event delivery, reusable DSH workbench views,
127
+ and multiple conversation backends.
128
+
129
+ The plugin is independently installable. It has no runtime dependency on the
130
+ Relay application, Relay Events, or another Relay plugin. It does not replace the
131
+ official DSH layout or install Files and Terminal views. This separation lets a
132
+ user install only Claude while the broader Relay project can compose Codex,
133
+ Claude, events, waits, monitors, and workbench extensions when those capabilities
134
+ are needed.
135
+
136
+ Explore or star Relay to follow that broader work:
137
+ <https://github.com/yangbobo2021/Relay>.
138
+
139
+ ## Update, Inspect, or Remove
140
+
141
+ Stop DSH Web before changing the bundle, then restart it afterward.
142
+
143
+ ```bash
144
+ # Show why the plugin is installed
145
+ dsh plugin --profile web why relay-dsh-plugin-claude
146
+
147
+ # Update the npm dependency
148
+ dsh plugin --profile web update relay-dsh-plugin-claude
149
+
150
+ # Remove it
151
+ dsh plugin --profile web remove relay-dsh-plugin-claude
152
+ ```
153
+
154
+ Use the `npx @deepseek-ai/dsh@0.1.1-rc.2` prefix instead of `dsh` when you do not
155
+ have a persistent DSH command.
156
+
157
+ ## Troubleshooting
158
+
159
+ ### Claude Code is missing from the mode menu
160
+
161
+ Restart DSH Web. Then run `dsh plugin --profile web why
162
+ relay-dsh-plugin-claude`. If pnpm cannot find the package, repeat the npm
163
+ installation command and read its final error.
164
+
165
+ ### The first message reports an authentication error
166
+
167
+ Start `claude` in the same user environment that starts DSH and complete normal
168
+ Claude Code authentication. Restart DSH afterward.
169
+
170
+ ### The composer is disabled
171
+
172
+ DSH requires a workspace before starting a coding conversation. Select **Add
173
+ workspace**, choose a directory, and return to **New Session**.
174
+
175
+ ### Installation says pnpm is missing
176
+
177
+ Install pnpm using its [official installation guide](https://pnpm.io/installation)
178
+ and confirm `pnpm --version` works in the same terminal.
179
+
180
+ ### DSH changed and the plugin no longer starts
181
+
182
+ DSH is a developer preview. Include the output of `dsh --version`, the plugin
183
+ source revision, and the startup error in a
184
+ [GitHub issue](https://github.com/yangbobo2021/relay-dsh-plugin-claude/issues).
185
+
186
+ ## Development
187
+
188
+ ```bash
189
+ git clone https://github.com/yangbobo2021/relay-dsh-plugin-claude.git
190
+ cd relay-dsh-plugin-claude
191
+ npm install
192
+ DSH_ROOT=/path/to/deepseek-harness npm run verify
193
+ npm pack
194
+ ```
195
+
196
+ `npm run verify` runs type checking, tests, and the production build. Boundary
197
+ tests reject accidental runtime dependencies on Relay or another feature plugin.
198
+
199
+ ## Feedback
200
+
201
+ Report bugs and feature requests in this repository's
202
+ [issue tracker](https://github.com/yangbobo2021/relay-dsh-plugin-claude/issues).
package/README.zh.md ADDED
@@ -0,0 +1,191 @@
1
+ # 在 DeepSeek Harness 中使用 Claude Code 对话
2
+
3
+ [English](README.md) | 中文
4
+
5
+ `relay-dsh-plugin-claude` 为官方
6
+ [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)Web
7
+ 界面增加 **Claude Code 对话后端**。安装后,DSH 的新建会话模式菜单中会出现
8
+ **Claude Code**。每个 DSH Session 会绑定一个 Claude Agent SDK Session。
9
+
10
+ ![DSH 新建会话菜单中的 Codex 和 Claude Code](docs/images/dsh-new-session-backends.jpg)
11
+
12
+ 上图来自安装了 Codex 和 Claude 插件的官方 DSH `0.1.1-rc.2`。如果只安装
13
+ 本插件,菜单中只会新增 **Claude Code**。
14
+
15
+ ## 什么情况下需要这个插件?
16
+
17
+ 以下情况适合安装:
18
+
19
+ - 希望直接在 DSH 中使用 Claude Code,而不必切换到单独的 Claude Code
20
+ 界面;
21
+ - 希望保留 DSH 原生的对话历史、输入框、审批、提问和工具展示;
22
+ - 希望一个 DSH Session 在多轮对话中持续使用同一个 Claude Agent SDK
23
+ Session;
24
+ - 希望在同一对话中使用 Claude 模型、reasoning、中断以及 DSH 插件贡献的
25
+ 工具。
26
+
27
+ 使用 DSH 标准 Agent 不需要安装本插件。本插件也不提供 Relay Events、文件
28
+ 浏览和终端面板,这些能力由其他可选插件提供。
29
+
30
+ ## 基于官方 DSH 的快速安装
31
+
32
+ 以下步骤已经在这些版本上实际验证:
33
+
34
+ - DeepSeek Harness `0.1.1-rc.2`,commit
35
+ [`b150a551`](https://github.com/deepseek-ai/deepseek-harness/commit/b150a551b8d465e31e418e1b2eaf5e79bbb7d28e)
36
+ - Node.js 22.13 或更高版本
37
+ - `pnpm` 已加入 `PATH`
38
+
39
+ DSH 当前仍是开发者预览版本,可能发生不兼容修改。本仓库会跟进官方版本,
40
+ 并在这里记录已经验证的版本。
41
+
42
+ ### 1. 准备 Claude Code 认证
43
+
44
+ Claude Agent SDK 会作为本插件的普通依赖一起安装。首次创建 DSH Claude
45
+ 会话前,请为运行 DSH 的用户完成 Claude Code 的本地配置和认证。
46
+
47
+ 按照官方 [Claude Code 安装说明](https://code.claude.com/docs/en/setup)完成
48
+ 设置,然后启动一次 Claude Code,确认认证成功:
49
+
50
+ ```bash
51
+ claude
52
+ ```
53
+
54
+ 认证信息仍由 Claude Code 原有的本地机制管理,本插件不会收集认证信息。
55
+
56
+ ### 2. 从 npm 安装
57
+
58
+ 修改 Profile 插件前,请先停止正在运行的 DSH Web,然后执行:
59
+
60
+ ```bash
61
+ npx @deepseek-ai/dsh@0.1.1-rc.2 plugin --profile web add relay-dsh-plugin-claude
62
+ ```
63
+
64
+ 官方 DSH CLI 会在需要时初始化 `web` Profile,通过 `pnpm` 安装 npm 包和
65
+ Claude Agent SDK 依赖,并将插件加入 Bundle 配置。用户不需要下载 Relay
66
+ 仓库。当前 npm 版本为 `0.1.0`。首次安装还会下载 Claude Agent SDK 的平台相关软件包,可能需要
67
+ 更长时间;请等待 pnpm 最终显示 `Done` 或明确的错误信息。
68
+
69
+ 如果已经安装了持久可用的 `dsh` 命令,也可以使用:
70
+
71
+ ```bash
72
+ dsh plugin --profile web add relay-dsh-plugin-claude
73
+ ```
74
+
75
+ 如需测试尚未发布的提交,可将包名替换为
76
+ `github:yangbobo2021/relay-dsh-plugin-claude`。
77
+
78
+ ### 3. 启动或重启 DSH Web
79
+
80
+ ```bash
81
+ npx @deepseek-ai/dsh@0.1.1-rc.2 web
82
+ ```
83
+
84
+ 如果使用已经安装的命令,则执行 `dsh web`。DSH 只在启动时读取 Bundle
85
+ 成员,因此安装、更新或删除插件后必须重启。
86
+
87
+ ### 4. 新建 Claude Code 对话
88
+
89
+ 1. 打开终端中显示的 DSH 地址,默认是 `http://127.0.0.1:3080`。
90
+ 2. 首次启动时阅读 DSH 测试提示,然后点击 **Continue**。
91
+ 3. 点击左侧栏的 **Add workspace**,选择允许 Claude 操作的项目目录。
92
+ 4. 点击 **New Session**。
93
+ 5. 打开当前显示为 **Standard mode** 的模式菜单,选择 **Claude Code**。
94
+ 6. 输入消息并发送。请在发送第一条消息前选择后端;已有会话会继续使用创建
95
+ 时选择的后端。
96
+
97
+ 插件不需要单独的激活命令。安装成功并重启 DSH 后,Bundle 会自动激活,
98
+ 并注册由插件管理的 **Claude Code** 模式。
99
+
100
+ ## 支持的能力
101
+
102
+ - 每个 DSH Session 持续绑定一个 Claude Agent SDK Session
103
+ - 模型和 reasoning 选择
104
+ - 在 DSH 原生对话中流式显示回答和工具活动
105
+ - DSH 原生审批和用户提问流程
106
+ - 中断和会话延续
107
+ - 通过进程内 Claude SDK MCP Server 提供通用 DSH 工具
108
+
109
+ 工具通过当前 Agent 的 DSH 工具运行时执行,并继续受到 DSH 权限和 Claude
110
+ 审批机制约束。工具桥接依赖默认 SDK 后端。如果开发者明确选择 CLI fallback,
111
+ 插件会拒绝 DSH 贡献工具,而不是静默丢弃工具。
112
+
113
+ ## 插件边界及与 Relay 的关系
114
+
115
+ 本仓库在 [Relay](https://github.com/yangbobo2021/Relay) 项目中完成设计与
116
+ 兼容性验证。Relay 是面向长时间运行 Agent、外部事件投递、可复用 DSH
117
+ 工作台视图和多种对话后端的开源项目。
118
+
119
+ 本插件可以独立安装。运行时不依赖 Relay 应用、Relay Events 或其他 Relay
120
+ 插件,也不会替换 DSH 官方布局或安装 Files、Terminal 视图。用户可以只安装
121
+ Claude;需要时,Relay 项目则可以进一步组合 Codex、Claude、事件、Wait、
122
+ Monitor 和工作台扩展。
123
+
124
+ 可以访问或 Star Relay 仓库,关注这些更完整的工作:
125
+ <https://github.com/yangbobo2021/Relay>。
126
+
127
+ ## 更新、检查或删除
128
+
129
+ 修改 Bundle 前先停止 DSH Web,完成后重新启动。
130
+
131
+ ```bash
132
+ # 检查插件为何被安装
133
+ dsh plugin --profile web why relay-dsh-plugin-claude
134
+
135
+ # 更新 npm 依赖
136
+ dsh plugin --profile web update relay-dsh-plugin-claude
137
+
138
+ # 删除插件
139
+ dsh plugin --profile web remove relay-dsh-plugin-claude
140
+ ```
141
+
142
+ 如果没有持久安装 `dsh` 命令,请将命令开头的 `dsh` 替换为
143
+ `npx @deepseek-ai/dsh@0.1.1-rc.2`。
144
+
145
+ ## 常见问题
146
+
147
+ ### 模式菜单中没有 Claude Code
148
+
149
+ 先重启 DSH Web,再执行 `dsh plugin --profile web why
150
+ relay-dsh-plugin-claude`。如果 pnpm 找不到插件,请重新执行 npm 安装命令,
151
+ 并查看最后显示的错误。
152
+
153
+ ### 第一条消息提示认证失败
154
+
155
+ 在启动 DSH 的同一用户环境中运行 `claude`,完成正常的 Claude Code 认证,
156
+ 然后重启 DSH。
157
+
158
+ ### 输入框不可用
159
+
160
+ DSH 在开始编码对话前必须选择工作区。点击 **Add workspace**,选择一个目录,
161
+ 然后返回 **New Session**。
162
+
163
+ ### 安装时提示找不到 pnpm
164
+
165
+ 按照 pnpm 的[官方安装说明](https://pnpm.io/installation)安装,并在同一个
166
+ 终端中确认 `pnpm --version` 可以执行。
167
+
168
+ ### DSH 更新后插件无法启动
169
+
170
+ DSH 仍是开发者预览版本。请在
171
+ [GitHub Issue](https://github.com/yangbobo2021/relay-dsh-plugin-claude/issues)
172
+ 中附上 `dsh --version` 输出、插件源码版本和启动错误。
173
+
174
+ ## 开发验证
175
+
176
+ ```bash
177
+ git clone https://github.com/yangbobo2021/relay-dsh-plugin-claude.git
178
+ cd relay-dsh-plugin-claude
179
+ npm install
180
+ DSH_ROOT=/path/to/deepseek-harness npm run verify
181
+ npm pack
182
+ ```
183
+
184
+ `npm run verify` 会执行类型检查、测试和生产构建。边界测试会阻止插件意外
185
+ 增加对 Relay 或其他功能插件的运行时依赖。
186
+
187
+ ## 反馈
188
+
189
+ 请通过本仓库的
190
+ [Issue Tracker](https://github.com/yangbobo2021/relay-dsh-plugin-claude/issues)
191
+ 报告错误或提出功能建议。
@@ -0,0 +1,3 @@
1
+ - insert:
2
+ - id: relay-claude-host
3
+ name: 'relay-dsh-plugin-claude'
package/lib/client.js ADDED
@@ -0,0 +1,177 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "relay-dsh-plugin-claude",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
+ let react_jsx_runtime = require("react/jsx-runtime");
10
+ //#region \0relay-css-module:./src/client/ClaudeActivityView.module.css.mjs
11
+ const css = ".ts8jCW_activity{width:min(100%,960px);color:var(--dsw-alias-label-secondary)}.ts8jCW_summary{min-width:0;color:var(--dsw-alias-label-tertiary);flex:1;align-items:center;gap:8px;font-size:13px;display:flex}.ts8jCW_summary :first-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.ts8jCW_detail{border-left:1px solid var(--dsw-alias-border-l2);margin:4px 0 8px 28px;padding-left:12px;overflow:hidden}.ts8jCW_detail pre{max-height:320px;color:var(--dsw-alias-label-secondary);font:var(--dsw-font-markdown-code-block-small);white-space:pre-wrap;word-break:break-word;margin:0 0 8px;overflow:auto}.ts8jCW_provenance{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;margin-bottom:8px;font-size:12px;line-height:18px;overflow:hidden}";
12
+ const tagId = "relay-dsh-plugin-claude/ClaudeActivityView.module.css";
13
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
14
+ const tag = document.createElement("style");
15
+ tag.dataset.plugin = "relay-dsh-plugin-claude";
16
+ tag.dataset.pluginCss = tagId;
17
+ tag.textContent = css;
18
+ document.head.appendChild(tag);
19
+ }
20
+ var ClaudeActivityView_module_css_default = {
21
+ "activity": "ts8jCW_activity",
22
+ "detail": "ts8jCW_detail",
23
+ "provenance": "ts8jCW_provenance",
24
+ "summary": "ts8jCW_summary"
25
+ };
26
+ //#endregion
27
+ //#region src/client/ClaudeActivityView.tsx
28
+ function dotState(status) {
29
+ if (status === "running") return "ongoing";
30
+ if (status === "error") return "error";
31
+ return "done";
32
+ }
33
+ const ClaudeActivityView = (0, react.memo)(function ClaudeActivityView({ node }) {
34
+ const activity = node.data;
35
+ const [open, setOpen] = (0, react.useState)(false);
36
+ const expandable = activity.input !== void 0 || activity.output !== void 0 || activity.provenance !== void 0;
37
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
38
+ className: ClaudeActivityView_module_css_default.activity,
39
+ "data-claude-activity": activity.type,
40
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
41
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCodeOutline16, {}),
42
+ title: activity.title,
43
+ open,
44
+ expandable,
45
+ onToggle: () => {
46
+ setOpen((value) => !value);
47
+ },
48
+ expandOnRowClick: true,
49
+ collapsedContent: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
50
+ className: ClaudeActivityView_module_css_default.summary,
51
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: activity.summary }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, {
52
+ state: dotState(activity.status),
53
+ size: 8
54
+ })]
55
+ }),
56
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
57
+ className: ClaudeActivityView_module_css_default.detail,
58
+ children: [
59
+ activity.provenance !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
60
+ className: ClaudeActivityView_module_css_default.provenance,
61
+ title: `Claude Code · Session ${activity.provenance.claudeSessionId} · Turn ${activity.provenance.turnId}`,
62
+ children: [
63
+ "Claude Code · Session ",
64
+ shortId(activity.provenance.claudeSessionId),
65
+ " · Turn ",
66
+ shortId(activity.provenance.turnId)
67
+ ]
68
+ }) : null,
69
+ activity.input !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { children: activity.input }) : null,
70
+ activity.output !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { children: activity.output }) : null
71
+ ]
72
+ })
73
+ })
74
+ });
75
+ });
76
+ function shortId(value) {
77
+ return value.length > 15 ? `${value.slice(0, 8)}...${value.slice(-4)}` : value;
78
+ }
79
+ //#endregion
80
+ //#region src/client/claude-activity.ts
81
+ const claudeActivityDefinition = {
82
+ kind: "relay-claude-activity",
83
+ target: "chat",
84
+ match: (event) => event.type === "relay-claude/activity" ? {
85
+ id: event.data.itemId,
86
+ role: event.data.phase === "started" ? "start" : "update"
87
+ } : null,
88
+ start: (_context, match) => {
89
+ if (match.event.type !== "relay-claude/activity") throw new Error("Claude activity start requires relay-claude/activity");
90
+ return {
91
+ ...match.event.data.activity,
92
+ provenance: {
93
+ claudeSessionId: match.event.data.claudeSessionId,
94
+ turnId: match.event.data.turnId
95
+ }
96
+ };
97
+ },
98
+ update: (context, match) => match.event.type === "relay-claude/activity" ? {
99
+ ...match.event.data.activity,
100
+ provenance: {
101
+ claudeSessionId: match.event.data.claudeSessionId,
102
+ turnId: match.event.data.turnId
103
+ }
104
+ } : context.state,
105
+ buildViewNode: (context) => {
106
+ if (context.start === void 0 || context.state === void 0) return null;
107
+ return {
108
+ key: context.key,
109
+ kind: "relay-claude-activity",
110
+ id: context.id,
111
+ target: "chat",
112
+ anchorSeq: context.start.event.seq,
113
+ location: context.start.location,
114
+ visibility: "visible",
115
+ data: context.state
116
+ };
117
+ }
118
+ };
119
+ //#endregion
120
+ //#region src/client/index.ts
121
+ const inject = [
122
+ "slots",
123
+ "theme",
124
+ "locale",
125
+ "remote",
126
+ "sessions",
127
+ "connection",
128
+ "conversationEvents"
129
+ ];
130
+ async function apply(ctx) {
131
+ ctx.conversationEvents.register(claudeActivityDefinition);
132
+ ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
133
+ name: "conversation.chat.node",
134
+ key: "relay-claude-activity"
135
+ }, ClaudeActivityView));
136
+ const unsubscribe = installModelSelection(ctx);
137
+ return async () => {
138
+ unsubscribe();
139
+ };
140
+ }
141
+ function installModelSelection(ctx) {
142
+ const connection = ctx.get("connection");
143
+ const selecting = /* @__PURE__ */ new Set();
144
+ const sync = () => {
145
+ const list = ctx.sessions.list.getSnapshot();
146
+ const id = list.current;
147
+ if (id === void 0 || list.byId[id]?.blank !== true || selecting.has(id)) return;
148
+ const preset = list.byId[id]?.agentPreset;
149
+ if (preset === "relay-codex") return;
150
+ selecting.add(id);
151
+ connection.api.sessions.models({ sessionId: id }).then(async (response) => {
152
+ const { result } = response;
153
+ if (!result.ok) return;
154
+ const target = preset === "relay-claude" ? result.value.groups.find((group) => group.id === "relay-claude") : result.value.current.provider === "relay-claude" ? result.value.groups.find((group) => group.id !== "relay-claude" && group.id !== "relay-codex") : void 0;
155
+ const model = target?.models[0];
156
+ if (target && model) await connection.api.sessions.selectModel({
157
+ sessionId: id,
158
+ provider: target.id,
159
+ model: model.id,
160
+ ...model.reasoning?.defaultEffort ? { reasoningEffort: model.reasoning.defaultEffort } : {}
161
+ });
162
+ }).catch(() => {}).finally(() => {
163
+ selecting.delete(id);
164
+ });
165
+ };
166
+ const off = ctx.sessions.list.subscribe(sync);
167
+ sync();
168
+ return off;
169
+ }
170
+ //#endregion
171
+ exports.apply = apply;
172
+ exports.inject = inject;
173
+ return module.exports;
174
+ }
175
+ });
176
+
177
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":["css","DisclosureRow","IconCodeOutline16","StateDot"],"sources":["../src/client/ClaudeActivityView.tsx","../src/client/claude-activity.ts","../src/client/index.ts"],"sourcesContent":["import { memo, useState } from 'react'\nimport {\n DisclosureRow,\n IconCodeOutline16,\n StateDot,\n type StateDotState,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { ClaudeActivityData } from './claude-activity.ts'\nimport css from './ClaudeActivityView.module.css'\n\ntype ClaudeActivityViewProps = PropsRuntime<'conversation.chat.node', 'relay-claude-activity'>\n\nfunction dotState(status: ClaudeActivityData['status']): StateDotState {\n if (status === 'running') return 'ongoing'\n if (status === 'error') return 'error'\n return 'done'\n}\n\nexport const ClaudeActivityView = memo(function ClaudeActivityView({ node }: ClaudeActivityViewProps) {\n const activity = node.data as ClaudeActivityData\n const [open, setOpen] = useState(false)\n const expandable = activity.input !== undefined || activity.output !== undefined || activity.provenance !== undefined\n return (\n <div className={css.activity} data-claude-activity={activity.type}>\n <DisclosureRow\n icon={<IconCodeOutline16 />}\n title={activity.title}\n open={open}\n expandable={expandable}\n onToggle={() => { setOpen(value => !value) }}\n expandOnRowClick\n collapsedContent={(\n <span className={css.summary}>\n <span>{activity.summary}</span>\n <StateDot state={dotState(activity.status)} size={8} />\n </span>\n )}\n >\n <div className={css.detail}>\n {activity.provenance !== undefined ? (\n <div\n className={css.provenance}\n title={`Claude Code · Session ${activity.provenance.claudeSessionId} · Turn ${activity.provenance.turnId}`}\n >\n Claude Code · Session {shortId(activity.provenance.claudeSessionId)} · Turn {shortId(activity.provenance.turnId)}\n </div>\n ) : null}\n {activity.input !== undefined ? <pre>{activity.input}</pre> : null}\n {activity.output !== undefined ? <pre>{activity.output}</pre> : null}\n </div>\n </DisclosureRow>\n </div>\n )\n})\n\nfunction shortId(value: string): string {\n return value.length > 15 ? `${value.slice(0, 8)}...${value.slice(-4)}` : value\n}\n","import type { ChatConversationViewNode, ConversationNodeDefinition } from '@deepseek-ai/dsh-client-runtime/client'\n\nexport type ClaudeActivityStatus = 'running' | 'completed' | 'error'\n\nexport interface ClaudeActivityData {\n readonly type: string\n readonly status: ClaudeActivityStatus\n readonly title: string\n readonly summary?: string\n readonly input?: string\n readonly output?: string\n readonly provenance?: {\n readonly claudeSessionId: string\n readonly turnId: string\n }\n}\n\nexport interface ClaudeActivityEventData {\n readonly version: 1\n readonly claudeSessionId: string\n readonly turnId: string\n readonly itemId: string\n readonly phase: 'started' | 'completed'\n readonly activity: ClaudeActivityData\n}\n\ndeclare module '@deepseek-ai/dsh-session/types' {\n interface SessionEventMap {\n 'relay-claude/activity': ClaudeActivityEventData\n }\n}\n\ndeclare module '@deepseek-ai/dsh-client-ui-conversation/client' {\n interface ChatNodeDataMap {\n 'relay-claude-activity': ClaudeActivityData\n }\n}\n\nexport const claudeActivityDefinition: ConversationNodeDefinition<ClaudeActivityData> = {\n kind: 'relay-claude-activity',\n target: 'chat',\n match: event => event.type === 'relay-claude/activity'\n ? { id: event.data.itemId, role: event.data.phase === 'started' ? 'start' : 'update' }\n : null,\n start: (_context, match) => {\n if (match.event.type !== 'relay-claude/activity') {\n throw new Error('Claude activity start requires relay-claude/activity')\n }\n return {\n ...match.event.data.activity,\n provenance: {\n claudeSessionId: match.event.data.claudeSessionId,\n turnId: match.event.data.turnId,\n },\n }\n },\n update: (context, match) => match.event.type === 'relay-claude/activity'\n ? {\n ...match.event.data.activity,\n provenance: {\n claudeSessionId: match.event.data.claudeSessionId,\n turnId: match.event.data.turnId,\n },\n }\n : context.state,\n buildViewNode: (context): ChatConversationViewNode | null => {\n if (context.start === undefined || context.state === undefined) return null\n return {\n key: context.key,\n kind: 'relay-claude-activity',\n id: context.id,\n target: 'chat',\n anchorSeq: context.start.event.seq,\n location: context.start.location,\n visibility: 'visible',\n data: context.state,\n }\n },\n}\n","import type { IApiClient } from '@deepseek-ai/dsh-api-remotes/client'\nimport type { ClientContext, SessionId } from '@deepseek-ai/dsh-client-runtime/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport { ClaudeActivityView } from './ClaudeActivityView.tsx'\nimport { claudeActivityDefinition } from './claude-activity.ts'\n\ninterface ConnectionFace { api: { sessions: Pick<IApiClient['sessions'], 'models' | 'selectModel'> } }\ninterface ModelGroup { readonly id: string; readonly models: readonly { readonly id: string; readonly reasoning?: { readonly defaultEffort?: string } }[] }\n\nexport const inject = ['slots', 'theme', 'locale', 'remote', 'sessions', 'connection', 'conversationEvents']\n\nexport async function apply(ctx: ClientContext): Promise<() => Promise<void>> {\n ctx.conversationEvents.register(claudeActivityDefinition)\n ctx.slots.inject('conversation.chat.node', () => ctx.slots.register({\n name: 'conversation.chat.node', key: 'relay-claude-activity',\n }, ClaudeActivityView))\n const unsubscribe = installModelSelection(ctx)\n return async () => { unsubscribe() }\n}\n\nfunction installModelSelection(ctx: ClientContext): () => void {\n const connection = ctx.get('connection' as never) as unknown as ConnectionFace\n const selecting = new Set<string>()\n const sync = (): void => {\n const list = ctx.sessions.list.getSnapshot()\n const id = list.current\n if (id === undefined || list.byId[id]?.blank !== true || selecting.has(id)) return\n const preset = list.byId[id]?.agentPreset\n if (preset === 'relay-codex') return\n selecting.add(id)\n void connection.api.sessions.models({ sessionId: id }).then(async (response: Awaited<ReturnType<ConnectionFace['api']['sessions']['models']>>) => {\n const { result } = response\n if (!result.ok) return\n const target = preset === 'relay-claude'\n ? (result.value.groups as readonly ModelGroup[]).find(group => group.id === 'relay-claude')\n : result.value.current.provider === 'relay-claude'\n ? (result.value.groups as readonly ModelGroup[]).find(group => group.id !== 'relay-claude' && group.id !== 'relay-codex')\n : undefined\n const model = target?.models[0]\n if (target && model) await connection.api.sessions.selectModel({ sessionId: id as SessionId, provider: target.id, model: model.id, ...(model.reasoning?.defaultEffort ? { reasoningEffort: model.reasoning.defaultEffort } : {}) })\n }).catch(() => {}).finally(() => { selecting.delete(id) })\n }\n const off = ctx.sessions.list.subscribe(sync); sync(); return off\n}\n"],"mappings":"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"}