dsh-data-cleaning-agent 0.5.1 → 0.5.2

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/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.5.2] - 2026-09-03
8
+
9
+ > DSH 原生 UI 对齐版本;不改变 QCC 工具范围、OAuth、计费确认或数据契约。
10
+
11
+ ### Added
12
+ - 在 DSH 原生输入框工具行增加上传清洗、质量体检、匹配核验、字段补全和任务历史五个能力入口。
13
+ - 在原生会话头增加「清洗工作台」恢复入口,任务历史页复用 Host `/mvp/jobs` 状态。
14
+
15
+ ### Changed
16
+ - 左栏入口采用与 MCP连接器一致的兼容方式:公开 `sidebar.footer.action` 仅托管生命周期和降级,
17
+ 实际按钮 Portal 到 `sidebar.workspaces` 前,显示在「新会话」与「工作区」之间。
18
+ - 点击左栏入口通过 DSH `workspaces` / `sessions` / `conversation` 服务打开中央原生会话并预填清洗提示词。
19
+ - 工作台由 980px 模态遮罩改为默认 510px 的非模态右侧面板;中央原生会话保持可见,移动端仍安全降级为全宽面板。
20
+
21
+ ### Fixed
22
+ - 修复应用入口错误显示在侧栏底部、点击后只出现大尺寸右侧遮罩而没有中央原生对话的问题。
23
+ - 修复 DSH 禁止同一 store handle 跨 root/session scope 复用导致的运行时挂载错误;会话按钮通过同页事件桥接根工作台状态。
24
+ - 右栏打开时按实际重叠量左移输入框能力栏,避免窄桌面视口中「匹配核验」「字段补全」「任务历史」被遮挡。
25
+ - 为 alpha.2 的 `uiWorkspace.connectWorkspace` 与 rc.2 的 `workspaces.connectWorkspace` 增加能力探测 Bridge,避免预发布版本入口只打开工作台却无法预填原生会话。
26
+
7
27
  ## [0.5.1] - 2026-09-03
8
28
 
9
29
  > 文档与发布流程修正版本;无运行时、QCC 工具契约或 API 变化。
package/README.en.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > A data cleaning & completion agent plugin for DeepSeek Harness: local CSV/XLSX/JSON engine plus optional Qichacha (QCC) MCP enterprise-data enrichment. Initiated and maintained by the Qichacha (QCC) team.
4
4
  >
5
- > Current source version / 当前源码版本: **0.5.1** (stable release)
5
+ > Current source version / 当前源码版本: **0.5.2** (stable release)
6
6
 
7
7
  [![CI](https://github.com/duhu2000/dsh-data-cleaning-agent/actions/workflows/ci.yml/badge.svg)](https://github.com/duhu2000/dsh-data-cleaning-agent/actions/workflows/ci.yml)
8
8
  [![npm](https://img.shields.io/npm/v/dsh-data-cleaning-agent)](https://www.npmjs.com/package/dsh-data-cleaning-agent)
@@ -40,10 +40,10 @@ Fully restart DeepSeek Harness afterwards (stop and re-run `dsh web`). Then say
40
40
  "help me clean this batch of company list data" and the plugin loads its built-in Skill and
41
41
  drives the clean / complete / profile tools.
42
42
 
43
- After restart, a "Data Cleaning" entry appears at the bottom of the sidebar: click it to open the
44
- workbench (upload & map profile match & review enrich & export, four steps). When the model calls the clean /
45
- complete / profile tools, matching tool cards render in the conversation, and a jobs pill in the
46
- workbench header shows queued / running tasks.
43
+ After restart, a "Data Cleaning" entry appears between "New Session" and "Workspaces" near the top
44
+ of the sidebar. It opens a native DSH conversation with five composer actions (upload, profile,
45
+ match, enrich, and history) and a right-side workbench for the four-step flow. Clean / complete /
46
+ profile tool calls continue to render matching cards in the native conversation.
47
47
 
48
48
  Without the `dsh` CLI, use the install script:
49
49
 
@@ -65,7 +65,7 @@ Or let an agent install it for you:
65
65
  | Parse | web `/data-cleaning/api/mvp/parse` | CSV / XLSX / JSON |
66
66
  | Async jobs | web `/data-cleaning/api/mvp/jobs` | job state machine + persistent storage |
67
67
  | UI | web `/data-cleaning/` | upload → clean/complete → export |
68
- | In-app entry | sidebar "Data Cleaning" button | opens the Mockup-aligned workbench: upload & map profile match & review enrich & export |
68
+ | In-app entry | top sidebar entry + five native composer actions | keeps the DSH conversation in the center and opens the Mockup-aligned workbench on the right |
69
69
  | Tool cards | `tool.call.toolview` (`data_clean_rows`/`data_complete_rows`/`data_profile`) | render clean/complete/profile result cards in-conversation with running/done/failed state |
70
70
  | Task progress | workbench header jobs pill | polls `/data-cleaning/api/mvp/jobs`; shows queued / running tasks |
71
71
  | Skill | `data-cleaning` | guides the model through the workflow |
@@ -106,6 +106,7 @@ See [the 0.4.0 release record](docs/RELEASE-0.4.0.md) for scope, validation gate
106
106
  See [the Phase-3 acceptance record](docs/PHASE3-ACCEPTANCE.md) and
107
107
  [the 0.5.0 release record](docs/RELEASE-0.5.0.md) for verification, upgrade, and rollback.
108
108
  See [the 0.5.1 release record](docs/RELEASE-0.5.1.md) for the README fix and release-text gate.
109
+ See [the 0.5.2 release record](docs/RELEASE-0.5.2.md) for native DSH UI alignment, verification, and rollback.
109
110
 
110
111
  See [docs/PLAN-OSS.md](docs/PLAN-OSS.md) for details.
111
112
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > 在 DeepSeek Harness 中清洗、补全、画像企业名单数据的智能体插件:本地 CSV/XLSX/JSON 引擎 + 可选企查查(Qichacha/QCC)MCP 企业数据补全,由企查查(Qichacha/QCC)团队发起并维护。
4
4
  >
5
- > 当前源码版本 / Current source version: **0.5.1**(正式版本)
5
+ > 当前源码版本 / Current source version: **0.5.2**(正式版本)
6
6
 
7
7
  [![CI](https://github.com/duhu2000/dsh-data-cleaning-agent/actions/workflows/ci.yml/badge.svg)](https://github.com/duhu2000/dsh-data-cleaning-agent/actions/workflows/ci.yml)
8
8
  [![npm](https://img.shields.io/npm/v/dsh-data-cleaning-agent)](https://www.npmjs.com/package/dsh-data-cleaning-agent)
@@ -35,8 +35,9 @@ dsh plugin --profile web add dsh-data-cleaning-agent
35
35
  安装后**完全重启** DeepSeek Harness(停止后重新运行 `dsh web`)。
36
36
  之后在对话中说「帮我清洗这批企业名单数据」,插件会自动加载内嵌 Skill 并调度清洗/补全/画像工具。
37
37
 
38
- 重启后侧边栏底部会出现「数据清洗」入口:点击打开工作台(上传与映射 数据体检 → 匹配核验 → 补全与导出四步),
39
- 模型调用清洗/补全/画像工具时对话内会渲染对应工具卡片,工作台头部用任务 pill 展示排队/进行中的任务。
38
+ 重启后,「数据清洗」入口会显示在侧边栏顶部的「新会话」与「工作区」之间。点击后打开 DSH 原生会话,
39
+ 输入框工具行提供上传清洗、质量体检、匹配核验、字段补全和任务历史五个入口;右侧工作台承载
40
+ 上传与映射 → 数据体检 → 匹配核验 → 补全与导出四步。模型调用工具时,对话内会渲染对应工具卡片。
40
41
 
41
42
  没有 `dsh` CLI 时,也可以用安装脚本:
42
43
 
@@ -58,7 +59,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/duhu2000/dsh-data-cleaning-a
58
59
  | 解析 | web `/data-cleaning/api/mvp/parse` | CSV / XLSX / JSON |
59
60
  | 异步任务 | web `/data-cleaning/api/mvp/jobs` | 任务状态机 + 持久化存储 |
60
61
  | UI | web `/data-cleaning/` | 上传 → 清洗/补全 → 导出 |
61
- | 应用内入口 | 侧边栏「数据清洗」按钮 | 打开 Mockup 对齐工作台:上传与映射 数据体检 → 匹配核验 → 补全与导出 |
62
+ | 应用内入口 | 侧栏顶部「数据清洗」+ 原生输入框五能力按钮 | 中央保留 DSH 原生对话,右侧打开 Mockup 对齐工作台 |
62
63
  | 工具卡片 | `tool.call.toolview`(`data_clean_rows`/`data_complete_rows`/`data_profile`) | 对话内渲染清洗/补全/画像结果卡,含运行/已完成/失败状态 |
63
64
  | 任务进度 | 工作台头部任务 pill | 轮询 `/data-cleaning/api/mvp/jobs`,展示排队/运行中任务 |
64
65
  | Skill | `data-cleaning` | 引导模型按工作流调度上述工具 |
@@ -102,6 +103,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/duhu2000/dsh-data-cleaning-a
102
103
  0.5.0 三域验收状态见 [docs/PHASE3-ACCEPTANCE.md](docs/PHASE3-ACCEPTANCE.md),
103
104
  发布记录、升级与回滚见 [docs/RELEASE-0.5.0.md](docs/RELEASE-0.5.0.md)。
104
105
  0.5.1 文档补丁与防回归发布门见 [docs/RELEASE-0.5.1.md](docs/RELEASE-0.5.1.md)。
106
+ 0.5.2 DSH 原生 UI 对齐范围、验收与回滚见 [docs/RELEASE-0.5.2.md](docs/RELEASE-0.5.2.md)。
105
107
 
106
108
  ## 本地开发
107
109
 
@@ -18,9 +18,14 @@
18
18
  MVP 路由、Phase-3 capabilities、estimate 与未确认 enrich 阻断均通过。rc.2 另完成实际工作台渲染、
19
19
  中文企业名称映射和本地清洗闭环;alpha.2 仍只定位为兼容探针。
20
20
 
21
- 0.5.1 README 状态与发布 Gate 文档补丁候选,不修改 Host/Client、QCC 契约或运行时依赖,
21
+ 0.5.1 为已发布的 README 状态与发布 Gate 文档补丁,不修改 Host/Client、QCC 契约或运行时依赖,
22
22
  因此继承 0.5.0 的 DSH、Node 与 OAuth 兼容矩阵。
23
23
 
24
+ 0.5.2 源码候选只调整 Client UI:入口 Portal 到 `sidebar.workspaces` 前,中央区域复用 DSH 原生会话,
25
+ 五能力入口使用公开 `conversation.input.left`,会话头恢复入口使用公开
26
+ `conversation.session.header.actions`,右侧面板继续使用 additive `shell.overlay`。不替换单占位
27
+ `details` / `conversation.session.header`,也不修改 Host、QCC 工具契约或计费安全门。
28
+
24
29
  ## 2. Node 运行时
25
30
 
26
31
  - 本包 `engines.node` 声明 `>=20`。
@@ -73,7 +78,7 @@ MVP 路由、Phase-3 capabilities、estimate 与未确认 enrich 阻断均通过
73
78
 
74
79
  ### 4.2 应用内入口(M1–M3)双基线实测
75
80
 
76
- 侧边栏「数据清洗」入口、全屏工作台、三张工具卡片(`data_clean_rows` /
81
+ 0.5.0/0.5.1 的侧边栏底部入口、全屏工作台、三张工具卡片(`data_clean_rows` /
77
82
  `data_complete_rows` / `data_profile`)与任务 pill 在双基线均已通过隔离 `DSH_HOME` 冒烟验证:
78
83
 
79
84
  - **rc.2**:根 HTML 直接引用 `/plugins/dsh-data-cleaning-agent/client.js?rev=…`,client bundle
@@ -84,6 +89,12 @@ MVP 路由、Phase-3 capabilities、estimate 与未确认 enrich 阻断均通过
84
89
 
85
90
  两条基线均返回 `[dc-agent] host apply() ran`,且未发起任何真实 QCC 调用。
86
91
 
92
+ 0.5.2 将入口和工作台改为 Mockup 对齐结构;已在 rc.2 与 alpha.2 的全新隔离安装中复验顶部
93
+ Portal、原生会话、五能力按钮、跨 scope 状态桥、窄桌面布局避让与非模态右栏。alpha.2 将
94
+ 工作区导航方法放在 `uiWorkspace.connectWorkspace`,rc.2 使用 `workspaces.connectWorkspace`;
95
+ 插件只做运行时能力探测并保留 `sessions.create` 安全降级,不承诺 alpha 实验面稳定。Portal 失败时
96
+ 仍保留 footer 降级按钮。
97
+
87
98
  ### 4.3 0.5.0 三域兼容面
88
99
 
89
100
  | 能力 | rc.2 | alpha.2 | 备注 |
@@ -2,13 +2,15 @@
2
2
 
3
3
  > 版本:**0.5.1**(文档与发布流程修正,无运行时代码变化)
4
4
  >
5
- > 公共发布状态以 npm Registry 与 GitHub Releases 为准;本文件随 0.5.1 发布物固化。
5
+ > 状态:**✅ 已发布;npm `latest`、Git tag 与 GitHub Latest 均为 `0.5.1`。**
6
+ >
7
+ > 发布时间:2026-09-03T03:06:54Z
6
8
 
7
9
  ## 1. 目的与范围
8
10
 
9
11
  0.5.1 只修正 0.5.0 npm tarball 中不可变 README 的历史状态文案,并加固发布流程:
10
12
 
11
- - 中英文 README 随 `package.json` 更新为 0.5.1 文档补丁候选;
13
+ - 中英文 README 随 `package.json` 更新,并在标签提交中切换为不含动态 `latest` 的 0.5.1 正式态;
12
14
  - 标签发布时,`docs:check` 强制要求两份 README 都声明当前版本已正式发布;
13
15
  - Release workflow 显式设置 `DSH_RELEASE_MODE=1`,严格文案门在 `npm publish` 前执行;
14
16
  - 增加普通分支允许候选、标签拒绝候选、标签接受正式文案三项单测;
@@ -33,9 +35,11 @@
33
35
  - [x] 发布前将 README.md / README.en.md 切换为不含动态 `latest` 的 0.5.1 正式发布态。
34
36
  - [x] `DSH_RELEASE_MODE=1 npm run check` 在正式文案下全绿:128/128、36 文件。
35
37
  - [x] 用户单独批准 `v0.5.1` tag、OIDC npm publish 与 GitHub Release。
36
- - [ ] 发布后核验 SLSA provenance、GitHub Latest 与公共 Registry 全新安装。
38
+ - [x] `v0.5.1` annotated tag 指向 `19205ec`;Release workflow `33710151625` 全绿。
39
+ - [x] npm OIDC publish 与 SLSA v1 provenance、GitHub Latest 均已验证。
40
+ - [x] 公共 Registry 全新安装通过,ESM 导出与中英文 README 正式态均正确。
37
41
 
38
- 未经最后两项确认,不得创建标签或发布 npm。
42
+ 后续任何版本仍须重新取得发布授权,不得覆盖已发布版本。
39
43
 
40
44
  ## 4. 回滚
41
45
 
@@ -0,0 +1,43 @@
1
+ # 0.5.2 DSH 原生 UI 对齐发布记录
2
+
3
+ > 版本:**0.5.2**
4
+ >
5
+ > 状态:**正式版本;由 `v0.5.2` 标签触发 npm OIDC Trusted Publishing 与 GitHub Release。**
6
+ >
7
+ > 发布日期:2026-09-03
8
+
9
+ ## 1. 目的与范围
10
+
11
+ 0.5.2 将数据清洗补全智能体的入口和工作流对齐 DSH 原生交互:
12
+
13
+ - 左栏入口位于「新会话」与「工作区」之间,不再显示在侧栏底部;
14
+ - 点击入口后复用中央原生会话,并预填数据清洗提示词;
15
+ - 原生输入框工具行提供上传清洗、质量体检、匹配核验、字段补全和任务历史五个入口;
16
+ - 工作台改为非模态右侧面板,中央会话保持可见;
17
+ - 任务历史复用 Host `/mvp/jobs` 状态,可恢复已有任务;
18
+ - 对 DSH `0.1.1-rc.2` 与 `0.1.2-alpha.2` 的 workspace 连接服务做隔离能力探测。
19
+
20
+ 本版本不扩展历史域、人员域或招投标域,不改变 QCC OAuth、计费确认、工具允许列表或数据契约。
21
+
22
+ ## 2. 验证
23
+
24
+ - `DSH_RELEASE_MODE=1 npm run check`:lint、双语正式版本文案、marketing、pack 白名单和 132 项测试全部通过;
25
+ - DSH `0.1.1-rc.2` / `0.1.2-alpha.2` 全新隔离安装与 UI 冒烟通过;
26
+ - 已验证顶部入口、中央原生会话、五能力按钮、非模态右栏、布局避让、上传解析和质量体检;
27
+ - 本轮 UI 验收没有调用 QCC,也没有触碰生产 DSH Host。
28
+
29
+ ## 3. 升级与回滚
30
+
31
+ 升级:
32
+
33
+ ```bash
34
+ dsh plugin --profile web add dsh-data-cleaning-agent@0.5.2
35
+ ```
36
+
37
+ 如需回滚,安装上一稳定版本:
38
+
39
+ ```bash
40
+ dsh plugin --profile web add dsh-data-cleaning-agent@0.5.1
41
+ ```
42
+
43
+ npm 已发布版本不可覆盖;如发布后发现问题,使用新的补丁版本修复。
package/lib/client.js CHANGED
@@ -3,11 +3,13 @@
3
3
  *
4
4
  * 通过 `window.__ModuleLoader__.load({id, factory})` 注册为惰性 CJS 工厂,
5
5
  * 与 dsh-mcp-connector@0.2.32(生产 profile 中已实测的「MCP连接器」)同构:
6
- * - `react` / `@deepseek-ai/dsh-client-ui-primitives` 由 web shell 静态模块表提供,
6
+ * - `react` / `react-dom` / `@deepseek-ai/dsh-client-ui-primitives` 由 web shell 静态模块表提供,
7
7
  * 第三方 bundle 无需打包 React、无需构建步骤。
8
8
  * - `defineStore` 首选 `@deepseek-ai/dsh-client-store`,回退 `@deepseek-ai/dsh-client-runtime/client`。
9
- * - 入口注册到 `sidebar.footer.action`(order 10,排在 MCP连接器的 order 0 下方),
10
- * 工作台注册到 `shell.overlay`(order 200)。
9
+ * - `sidebar.footer.action` 仅托管入口生命周期和降级渲染;实际入口 Portal
10
+ * `sidebar.workspaces` 前,位于「新会话」与「工作区」之间。
11
+ * - DSH 原生会话保持在中间,能力入口使用 `conversation.input.left`,右侧非模态
12
+ * 工作台使用 `shell.overlay`,会话头使用 `conversation.session.header.actions`。
11
13
  *
12
14
  * 0.5.0 工作台复用本地 `/mvp/*` 与三域 `/phase3/*` 后端,完整覆盖上传映射、
13
15
  * 数据体检、匹配核验、补全导出;计费调用仍由 Host Bridge 的确认/幂等/上限门约束。
@@ -20,7 +22,19 @@ window.__ModuleLoader__.load({
20
22
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
21
23
 
22
24
  const react = require('react');
23
- const { Button } = require('@deepseek-ai/dsh-client-ui-primitives');
25
+ const reactDom = require('react-dom');
26
+ const primitives = require('@deepseek-ai/dsh-client-ui-primitives');
27
+ const {
28
+ Button,
29
+ IconArchiveOutline20,
30
+ IconChecklistOutline14,
31
+ IconCloseOutline16,
32
+ IconDataOutline16,
33
+ IconDownloadOutline16,
34
+ IconFullscreenOutline16,
35
+ IconPaperclipOutline16,
36
+ IconSearchOutline16,
37
+ } = primitives;
24
38
 
25
39
  let defineStore;
26
40
  try {
@@ -41,20 +55,45 @@ window.__ModuleLoader__.load({
41
55
  /** 客户端所需服务:与 mcp-connector 对齐(槽位 + 会话/工作区/输入机)。 */
42
56
  const inject = ['slots', 'sessions', 'workspaces', 'conversation'];
43
57
 
44
- const FOOTER_STYLE_ID = 'dsh-data-cleaning-agent-sidebar';
58
+ const UI_STYLE_ID = 'dsh-data-cleaning-agent-ui';
59
+ const SIDEBAR_WORKSPACES_SELECTOR = '[data-slot="sidebar.workspaces"]';
60
+ const TOP_MOUNT_SELECTOR = '[data-data-cleaning-top-mount="true"]';
45
61
  const stylesCss = `
46
- [data-slot="sidebar.footer.action"] {
47
- display: flex !important;
48
- flex-direction: column;
62
+ .dcAgentTopMount {
63
+ flex: none;
49
64
  min-width: 0;
50
65
  width: 100%;
51
66
  }
52
67
 
68
+ .dcAgentTopEntry {
69
+ box-sizing: border-box;
70
+ width: 100%;
71
+ padding-right: var(--dsh-sidebar-inline-padding, 12px);
72
+ }
73
+
53
74
  .dcAgentLauncher {
54
75
  flex: none;
55
76
  box-sizing: border-box;
56
77
  width: 100%;
78
+ height: 42px;
79
+ margin: 0 0 8px;
80
+ padding: 0 10px 0 8px;
81
+ justify-content: flex-start;
57
82
  min-width: 0;
83
+ overflow: hidden;
84
+ border-radius: 12px;
85
+ white-space: nowrap;
86
+ }
87
+ .dcAgentLauncher[data-wide="false"] {
88
+ width: 36px;
89
+ height: 36px;
90
+ padding: 0;
91
+ justify-content: center;
92
+ border-radius: 50%;
93
+ }
94
+ .dcAgentTopEntry[data-wide="false"] {
95
+ width: 36px;
96
+ padding-right: 0;
58
97
  }
59
98
 
60
99
  .dcAgentOverlay {
@@ -64,22 +103,79 @@ window.__ModuleLoader__.load({
64
103
  display: flex;
65
104
  align-items: stretch;
66
105
  justify-content: flex-end;
67
- background: rgba(8, 10, 14, 0.22);
106
+ pointer-events: none;
107
+ background: transparent;
68
108
  }
69
109
 
70
110
  .dcAgentWorkbench {
71
111
  display: flex;
72
112
  flex-direction: column;
73
- width: min(980px, calc(100vw - 72px));
113
+ width: min(510px, calc(100vw - 72px));
74
114
  height: 100vh;
75
115
  border: 1px solid light-dark(#d5dae4, #2a3038);
76
- border-radius: 14px 0 0 14px;
116
+ border-radius: 12px 0 0 12px;
77
117
  background: light-dark(#ffffff, #161b23);
78
118
  color: light-dark(#172033, #edf2fa);
79
- box-shadow: light-dark(0 18px 50px rgba(33, 55, 88, .14), 0 20px 60px rgba(0, 0, 0, .42));
119
+ box-shadow: light-dark(-10px 0 32px rgba(33, 55, 88, .12), -12px 0 40px rgba(0, 0, 0, .32));
80
120
  overflow: hidden;
121
+ pointer-events: auto;
122
+ }
123
+ .dcAgentWorkbench.is-expanded { width: min(980px, calc(100vw - 72px)); }
124
+
125
+ .dcAgentCapabilities {
126
+ display: flex;
127
+ align-items: center;
128
+ gap: 4px;
129
+ max-width: min(56vw, 560px);
130
+ overflow-x: auto;
131
+ scrollbar-width: none;
132
+ transform: translateX(var(--dc-agent-workbench-shift, 0px));
133
+ transition: transform 160ms ease;
134
+ }
135
+ .dcAgentCapabilities::-webkit-scrollbar { display: none; }
136
+ .dcAgentCapability {
137
+ display: inline-flex;
138
+ align-items: center;
139
+ gap: 5px;
140
+ flex: 0 0 auto;
141
+ min-height: 30px;
142
+ padding: 4px 8px;
143
+ border: 1px solid transparent;
144
+ border-radius: 8px;
145
+ background: transparent;
146
+ color: light-dark(#5d687a, #aab5c7);
147
+ font: inherit;
148
+ font-size: 12px;
149
+ cursor: pointer;
150
+ }
151
+ .dcAgentCapability:hover,
152
+ .dcAgentCapability:focus-visible,
153
+ .dcAgentCapability.is-active {
154
+ border-color: light-dark(#c9d9f8, #365681);
155
+ background: light-dark(#edf4ff, #172841);
156
+ color: light-dark(#1556cf, #8cb3ff);
157
+ }
158
+ .dcAgentCapability svg { flex: 0 0 auto; }
159
+
160
+ .dcAgentHeaderAction {
161
+ display: inline-flex;
162
+ align-items: center;
163
+ gap: 6px;
164
+ min-height: 30px;
165
+ padding: 4px 9px;
166
+ border: 1px solid light-dark(#d9dee8, #394352);
167
+ border-radius: 8px;
168
+ background: light-dark(#ffffff, #191f28);
169
+ color: inherit;
170
+ font: inherit;
171
+ font-size: 12px;
172
+ cursor: pointer;
173
+ }
174
+ .dcAgentHeaderAction:hover,
175
+ .dcAgentHeaderAction:focus-visible {
176
+ border-color: light-dark(#2869e6, #6d9eff);
177
+ color: light-dark(#1556cf, #8cb3ff);
81
178
  }
82
- .dcAgentWorkbench.is-expanded { width: calc(100vw - 72px); }
83
179
 
84
180
  .dcAgentWbHeader {
85
181
  display: flex;
@@ -365,17 +461,19 @@ window.__ModuleLoader__.load({
365
461
  }
366
462
  @media (max-width: 760px) {
367
463
  .dcAgentWorkbench, .dcAgentWorkbench.is-expanded { width: 100vw; border-radius: 0; }
464
+ .dcAgentOverlay { background: rgba(8, 10, 14, 0.22); pointer-events: auto; }
368
465
  .dcAgentStepper { grid-template-columns: repeat(2, minmax(0, 1fr)); }
369
466
  .dcAgentQccBadge { display: none; }
370
467
  .dcAgentCandidate { grid-template-columns: 1fr; }
468
+ .dcAgentCapabilityLabel { display: none; }
371
469
  }
372
470
  `;
373
471
 
374
- /** footer 多个入口纵向堆叠(与 mcp-connector 注入的列式 CSS 幂等并存)。 */
375
- function installSidebarStyles() {
376
- if (document.querySelector(`style[data-plugin="${FOOTER_STYLE_ID}"]`) !== null) return () => {};
472
+ /** 安装入口、会话能力按钮与右侧工作台样式。 */
473
+ function installUiStyles() {
474
+ if (document.querySelector(`style[data-plugin="${UI_STYLE_ID}"]`) !== null) return () => {};
377
475
  const style = document.createElement('style');
378
- style.dataset.plugin = FOOTER_STYLE_ID;
476
+ style.dataset.plugin = UI_STYLE_ID;
379
477
  style.textContent = stylesCss;
380
478
  document.head.append(style);
381
479
  return () => { style.remove(); };
@@ -392,6 +490,16 @@ window.__ModuleLoader__.load({
392
490
  { key: 'enrich', label: '补全与导出', icon: '⬇️' },
393
491
  ];
394
492
 
493
+ const CAPABILITIES = [
494
+ { key: 'upload', label: '上传清洗', icon: IconPaperclipOutline16, fallback: '+' },
495
+ { key: 'profile', label: '质量体检', icon: IconChecklistOutline14, fallback: '✓' },
496
+ { key: 'review', label: '匹配核验', icon: IconSearchOutline16, fallback: '⌕' },
497
+ { key: 'enrich', label: '字段补全', icon: IconDataOutline16, fallback: '▦' },
498
+ { key: 'history', label: '任务历史', icon: IconArchiveOutline20, fallback: '◷' },
499
+ ];
500
+
501
+ const DEFAULT_SESSION_PROMPT = '请帮我清洗并补全企业名单。我会在右侧数据清洗工作台上传文件或粘贴数据。';
502
+
395
503
  /** 后端往返:POST JSON,返回解析后的对象。 */
396
504
  async function api(path, body) {
397
505
  const res = await fetch(path, {
@@ -422,13 +530,20 @@ window.__ModuleLoader__.load({
422
530
  qccEstimate: null,
423
531
  qccRun: null,
424
532
  paidConfirmed: false,
533
+ activeSessionId: null,
425
534
  jobs: [], // 后台任务列表(/mvp/jobs 轮询),仅用于状态 pill,不接计费遥测
426
535
  }),
427
536
  actions: {
428
537
  open: (draft) => { draft.open = true; },
538
+ openAt: (draft, step, sessionId) => {
539
+ draft.open = true;
540
+ if (step) draft.step = step;
541
+ if (sessionId) draft.activeSessionId = sessionId;
542
+ },
429
543
  close: (draft) => { draft.open = false; },
430
544
  toggleExpanded: (draft) => { draft.expanded = !draft.expanded; },
431
545
  setStep: (draft, step) => { draft.step = step; },
546
+ setActiveSession: (draft, sessionId) => { draft.activeSessionId = sessionId ?? null; },
432
547
  setBusy: (draft, busy) => { draft.busy = busy; },
433
548
  setError: (draft, error) => { draft.error = error; },
434
549
  setInput: (draft, input) => { draft.input = input; },
@@ -453,27 +568,178 @@ window.__ModuleLoader__.load({
453
568
  });
454
569
  }
455
570
 
456
- /** 左栏入口按钮:使用 DSH Button,与 MCP连接器一致。 */
571
+ /**
572
+ * DSH 当前没有公开的「新会话与工作区之间」list slot。和 MCP连接器一样,
573
+ * footer slot 只托管生命周期;真实入口只依赖稳定 data-slot Portal 到工作区前。
574
+ */
575
+ function ensureTopLauncherMount() {
576
+ const workspaceSlot = document.querySelector(SIDEBAR_WORKSPACES_SELECTOR);
577
+ const parent = workspaceSlot && workspaceSlot.parentElement;
578
+ if (!workspaceSlot || !parent) return null;
579
+ let mount = parent.querySelector(TOP_MOUNT_SELECTOR);
580
+ if (!mount) {
581
+ mount = document.createElement('div');
582
+ mount.dataset.dataCleaningTopMount = 'true';
583
+ mount.className = 'dcAgentTopMount';
584
+ }
585
+ // 不与 MCP连接器各自的 MutationObserver 争抢「紧邻工作区」位置:首次挂载后
586
+ // 保持稳定顺序。MCP连接器会占据紧邻工作区的位置,本入口自然位于它上方。
587
+ if (mount.parentElement !== parent) parent.insertBefore(mount, workspaceSlot);
588
+ return mount;
589
+ }
590
+
591
+ /** 使用 DSH 原生工作区、会话和输入机打开中央对话,不构造第二套聊天界面。 */
592
+ async function startCleaningSession(ctx) {
593
+ const workspace = ctx.workspaces.list.getSnapshot();
594
+ const current = ctx.sessions.list.getSnapshot().current;
595
+ const items = Array.isArray(workspace.items) ? workspace.items : [];
596
+ const currentWorkspaceId = current === undefined
597
+ ? undefined
598
+ : items.find((item) => Array.isArray(item.sessionIds) && item.sessionIds.includes(current))?.workspaceId;
599
+ const targetWorkspaceId = currentWorkspaceId ?? workspace.recentWorkspaceId ?? items[0]?.workspaceId;
600
+ if (targetWorkspaceId === undefined) {
601
+ throw new Error('请先选择一个工作空间,再打开数据清洗智能体');
602
+ }
603
+ // rc.2 暴露 workspaces.connectWorkspace;alpha.2 把同一导航策略迁移到
604
+ // uiWorkspace.connectWorkspace,纯 workspaces controller 不再带该方法。
605
+ // 只在检测到真实方法后调用,避免把 alpha 实验面当成稳定契约。
606
+ const uiWorkspace = typeof ctx.get === 'function' ? ctx.get('uiWorkspace') : ctx.uiWorkspace;
607
+ const connectWorkspace = typeof ctx.workspaces.connectWorkspace === 'function'
608
+ ? ctx.workspaces.connectWorkspace.bind(ctx.workspaces)
609
+ : typeof uiWorkspace?.connectWorkspace === 'function'
610
+ ? uiWorkspace.connectWorkspace.bind(uiWorkspace)
611
+ : null;
612
+ const sessionId = connectWorkspace
613
+ ? await connectWorkspace(targetWorkspaceId)
614
+ : typeof ctx.sessions.create === 'function'
615
+ ? await ctx.sessions.create({ workspaceId: targetWorkspaceId })
616
+ : null;
617
+ if (!sessionId) throw new Error('当前 DSH 版本没有可用的工作区会话创建能力');
618
+ const conversation = typeof ctx.get === 'function' ? ctx.get('conversation') : ctx.conversation;
619
+ if (!conversation) throw new Error('DSH 对话服务尚未就绪,请稍后重试');
620
+ conversation.input.shell(sessionId).setDraft(DEFAULT_SESSION_PROMPT);
621
+ ctx.sessions.open(sessionId);
622
+ return sessionId;
623
+ }
624
+
625
+ function capabilityIcon(item, size = 16) {
626
+ return typeof item.icon === 'function'
627
+ ? h(item.icon, { size, 'aria-hidden': 'true' })
628
+ : h('span', { 'aria-hidden': 'true' }, item.fallback);
629
+ }
630
+
631
+ function openWorkbench(actions, step, sessionId) {
632
+ actions.openAt(step, sessionId);
633
+ startJobsPolling(actions);
634
+ }
635
+
636
+ const WORKBENCH_OPEN_EVENT = 'dsh:data-cleaning-workbench-open';
637
+
638
+ // session scope 不能复用 root scope 的 store handle。DSH 当前会分别物化 root/session
639
+ // 插槽组件,因此以 document 事件跨 scope 通知;闭包引用仅作为无 DOM 测试降级。
640
+ let rootWorkbenchActions = null;
641
+
642
+ /** 从 session scope 的 composer/header 触发 root scope 工作台。 */
643
+ function requestWorkbenchOpen(step, sessionId) {
644
+ if (typeof document !== 'undefined'
645
+ && typeof document.createEvent === 'function'
646
+ && typeof document.dispatchEvent === 'function') {
647
+ const event = document.createEvent('CustomEvent');
648
+ event.initCustomEvent(WORKBENCH_OPEN_EVENT, false, true, { step, sessionId });
649
+ if (!document.dispatchEvent(event)) return;
650
+ }
651
+ if (rootWorkbenchActions) openWorkbench(rootWorkbenchActions, step, sessionId);
652
+ }
653
+
654
+ /** 左栏入口按钮:公开 footer 托管,真实按钮 Portal 到「新会话 / 工作区」之间。 */
457
655
  function SidebarEntry(props) {
458
- const { wide, useStore, actions } = props;
656
+ const { wide, useStore, actions, startSession } = props;
459
657
  const open = useStore((state) => state.open);
460
- return react.createElement(Button, {
658
+ const [topMount, setTopMount] = react.useState(null);
659
+
660
+ react.useEffect(() => {
661
+ let disposed = false;
662
+ const ownedMounts = new Set();
663
+ const syncMount = () => {
664
+ if (disposed) return;
665
+ const mount = ensureTopLauncherMount();
666
+ if (mount) ownedMounts.add(mount);
667
+ setTopMount((current) => current === mount ? current : mount);
668
+ };
669
+ syncMount();
670
+ let observer = null;
671
+ if (typeof window.MutationObserver === 'function' && document.body) {
672
+ observer = new window.MutationObserver(syncMount);
673
+ observer.observe(document.body, { childList: true, subtree: true });
674
+ }
675
+ return () => {
676
+ disposed = true;
677
+ if (observer) observer.disconnect();
678
+ for (const mount of ownedMounts) mount.remove();
679
+ };
680
+ }, []);
681
+
682
+ const launcher = react.createElement(Button, {
461
683
  variant: 'ghost',
462
684
  className: 'dcAgentLauncher',
463
685
  'data-wide': wide,
464
686
  'aria-label': '数据清洗',
465
687
  'aria-haspopup': 'dialog',
466
688
  'aria-expanded': open,
467
- onClick: () => {
689
+ onClick: async () => {
468
690
  try {
469
- actions.open();
470
- startJobsPolling(actions);
691
+ openWorkbench(actions, 'upload');
692
+ if (typeof startSession === 'function') {
693
+ const sessionId = await startSession();
694
+ actions.setActiveSession(sessionId);
695
+ }
471
696
  } catch (error) {
697
+ actions.setError(error instanceof Error ? error.message : String(error));
472
698
  console.error('[dc-agent] open failed:', error);
473
699
  }
474
700
  },
475
701
  children: wide ? '🧹 数据清洗' : '🧹',
476
702
  });
703
+ if (!topMount || typeof reactDom.createPortal !== 'function') return launcher;
704
+ return reactDom.createPortal(h('div', {
705
+ className: 'dcAgentTopEntry',
706
+ 'data-wide': wide,
707
+ }, launcher), topMount);
708
+ }
709
+
710
+ /** Mockup 五能力入口:位于原生 composer 的交互工具行。 */
711
+ function CapabilityBar(props) {
712
+ const { sessionId } = props;
713
+ return h('div', { className: 'dcAgentCapabilities', 'aria-label': '数据清洗智能体能力' },
714
+ CAPABILITIES.map((item) => h('button', {
715
+ key: item.key,
716
+ type: 'button',
717
+ className: 'dcAgentCapability',
718
+ 'aria-label': item.label,
719
+ title: item.label,
720
+ onClick: () => requestWorkbenchOpen(item.key, sessionId),
721
+ },
722
+ capabilityIcon(item),
723
+ h('span', { className: 'dcAgentCapabilityLabel' }, item.label),
724
+ )),
725
+ );
726
+ }
727
+
728
+ /** 会话头部的可恢复入口:关闭右栏后仍可从当前会话再次打开。 */
729
+ function WorkbenchHeaderEntry(props) {
730
+ const { sessionId } = props;
731
+ return h('button', {
732
+ type: 'button',
733
+ className: 'dcAgentHeaderAction',
734
+ 'aria-label': '打开数据清洗工作台',
735
+ title: '打开数据清洗工作台',
736
+ onClick: () => requestWorkbenchOpen(null, sessionId),
737
+ },
738
+ typeof IconDataOutline16 === 'function'
739
+ ? h(IconDataOutline16, { size: 16, 'aria-hidden': 'true' })
740
+ : h('span', { 'aria-hidden': 'true' }, '▦'),
741
+ h('span', null, '清洗工作台'),
742
+ );
477
743
  }
478
744
 
479
745
  /** M3 · 三工具 tool.call.toolview 卡片元数据(wire 名 → 展示文案)。 */
@@ -606,8 +872,8 @@ window.__ModuleLoader__.load({
606
872
  });
607
873
  }
608
874
 
609
- /** 工作台主视图:header + stepper + 当前 pane。 */
610
- function WorkbenchOverlay(props) {
875
+ /** 右侧非模态工作台:中央区域始终保留 DSH 原生会话。 */
876
+ function WorkbenchDrawer(props) {
611
877
  const { useStore, actions } = props;
612
878
  const open = useStore((state) => state.open);
613
879
  const step = useStore((state) => state.step);
@@ -626,6 +892,65 @@ window.__ModuleLoader__.load({
626
892
  const qccRun = useStore((state) => state.qccRun);
627
893
  const paidConfirmed = useStore((state) => state.paidConfirmed);
628
894
  const jobs = useStore((state) => state.jobs);
895
+ const activeSessionId = useStore((state) => state.activeSessionId);
896
+
897
+ react.useEffect(() => {
898
+ rootWorkbenchActions = actions;
899
+ const handleOpen = (event) => {
900
+ event?.preventDefault?.();
901
+ const detail = event?.detail ?? {};
902
+ openWorkbench(actions, detail.step ?? null, detail.sessionId ?? null);
903
+ };
904
+ if (typeof document !== 'undefined' && typeof document.addEventListener === 'function') {
905
+ document.addEventListener(WORKBENCH_OPEN_EVENT, handleOpen);
906
+ }
907
+ return () => {
908
+ if (typeof document !== 'undefined' && typeof document.removeEventListener === 'function') {
909
+ document.removeEventListener(WORKBENCH_OPEN_EVENT, handleOpen);
910
+ }
911
+ stopJobsPolling();
912
+ if (rootWorkbenchActions === actions) rootWorkbenchActions = null;
913
+ };
914
+ }, [actions]);
915
+
916
+ // 非模态右栏会覆盖 Host 的中央画布;按实际重叠量把 composer 能力栏左移,
917
+ // 保证 Mockup 中的五个入口在窄桌面视口仍可见、可点击。展开态专注工作台,
918
+ // 不强行挤压到不足一栏的剩余区域。
919
+ react.useEffect(() => {
920
+ if (typeof document === 'undefined' || typeof document.querySelectorAll !== 'function') return undefined;
921
+ const reset = () => {
922
+ for (const bar of document.querySelectorAll('.dcAgentCapabilities')) {
923
+ bar.style?.removeProperty('--dc-agent-workbench-shift');
924
+ }
925
+ };
926
+ if (!open || expanded) {
927
+ reset();
928
+ return reset;
929
+ }
930
+ const align = () => {
931
+ const drawer = document.querySelector('.dcAgentWorkbench');
932
+ if (!drawer) return;
933
+ const drawerRect = drawer.getBoundingClientRect();
934
+ for (const bar of document.querySelectorAll('.dcAgentCapabilities')) {
935
+ const barRect = bar.getBoundingClientRect();
936
+ const overlap = Math.max(0, barRect.right - drawerRect.left + 8);
937
+ const leftRoom = Math.max(0, barRect.left - 88);
938
+ const shift = Math.min(overlap, leftRoom);
939
+ bar.style?.setProperty('--dc-agent-workbench-shift', `${-shift}px`);
940
+ }
941
+ };
942
+ const view = document.defaultView;
943
+ const frameId = typeof view?.requestAnimationFrame === 'function'
944
+ ? view.requestAnimationFrame(align)
945
+ : null;
946
+ view?.addEventListener?.('resize', align);
947
+ align();
948
+ return () => {
949
+ if (frameId !== null) view?.cancelAnimationFrame?.(frameId);
950
+ view?.removeEventListener?.('resize', align);
951
+ reset();
952
+ };
953
+ }, [open, expanded]);
629
954
 
630
955
  // DSH 的 useStore 基于 React hooks;所有订阅必须在每次渲染时以相同顺序调用。
631
956
  // 把关闭态 guard 放在全部 useStore 之后,避免首次关闭、随后打开时触发
@@ -885,7 +1210,37 @@ window.__ModuleLoader__.load({
885
1210
  );
886
1211
 
887
1212
  let pane;
888
- if (step === 'profile') {
1213
+ if (step === 'history') {
1214
+ pane = h('div', { className: 'dcAgentPane' },
1215
+ h('p', { className: 'dcAgentHint' }, '查看当前 Host 的数据清洗后台任务。任务数据仅在本机保存,不进入模型上下文。'),
1216
+ h('div', { className: 'dcAgentRow' },
1217
+ h('button', {
1218
+ type: 'button',
1219
+ className: 'dcAgentButton',
1220
+ disabled: busy,
1221
+ 'aria-label': '刷新任务历史',
1222
+ onClick: () => pollJobsOnce(actions),
1223
+ }, '刷新任务'),
1224
+ h('button', {
1225
+ type: 'button',
1226
+ className: 'dcAgentButton is-primary',
1227
+ onClick: () => actions.setStep('upload'),
1228
+ }, '新建清洗任务'),
1229
+ ),
1230
+ jobs.length ? h('div', { className: 'dcAgentPane', 'aria-label': '任务历史列表' },
1231
+ jobs.map((job) => h('section', { key: job.id, className: 'dcAgentSection' },
1232
+ h('h3', null, job.name || job.id || '数据清洗任务'),
1233
+ h('div', { className: 'dcAgentRow' },
1234
+ h('span', { className: 'dcAgentJobsPill', 'data-state': job.state ?? 'idle' }, JOB_STATE_LABEL[job.state] ?? job.state ?? '未知'),
1235
+ h('span', { className: 'dcAgentHint' }, job.createdAt ? new Date(job.createdAt).toLocaleString() : ''),
1236
+ ),
1237
+ )),
1238
+ ) : h('section', { className: 'dcAgentSection' },
1239
+ h('h3', null, '暂无后台任务'),
1240
+ h('p', { className: 'dcAgentHint' }, '上传企业名单并开始处理后,任务状态会显示在这里。'),
1241
+ ),
1242
+ );
1243
+ } else if (step === 'profile') {
889
1244
  pane = h('div', { className: 'dcAgentPane' },
890
1245
  h('p', { className: 'dcAgentHint' }, '基于已解析数据生成本地质量画像(缺失率、去重值、金额分布)。本步骤不发起企查查调用。'),
891
1246
  profile ? h('div', null,
@@ -1048,20 +1403,34 @@ window.__ModuleLoader__.load({
1048
1403
 
1049
1404
  return h('div', {
1050
1405
  className: 'dcAgentOverlay',
1051
- role: 'dialog',
1052
- 'aria-modal': 'true',
1053
- 'aria-label': '数据清洗',
1406
+ 'data-dsh-plugin': 'data-cleaning-agent',
1054
1407
  onClick: (event) => {
1055
- if (event.target === event.currentTarget) actions.close();
1408
+ if (event.target === event.currentTarget) {
1409
+ stopJobsPolling();
1410
+ actions.close();
1411
+ }
1056
1412
  },
1057
1413
  },
1058
- h('div', { className: `dcAgentWorkbench${expanded ? ' is-expanded' : ''}` },
1414
+ h('aside', {
1415
+ className: `dcAgentWorkbench${expanded ? ' is-expanded' : ''}`,
1416
+ role: 'dialog',
1417
+ 'aria-modal': 'false',
1418
+ 'aria-label': '数据清洗工作台',
1419
+ 'data-session-id': activeSessionId ?? undefined,
1420
+ onKeyDown: (event) => {
1421
+ if (event.key === 'Escape') {
1422
+ event.preventDefault();
1423
+ stopJobsPolling();
1424
+ actions.close();
1425
+ }
1426
+ },
1427
+ },
1059
1428
  h('header', { className: 'dcAgentWbHeader' },
1060
1429
  h('div', { className: 'dcAgentWbTitle' },
1061
1430
  h('span', { className: 'dcAgentWbIcon', 'aria-hidden': 'true' }, '🧹'),
1062
1431
  h('div', null,
1063
1432
  h('b', null, '数据清洗补全'),
1064
- h('small', null, '本地确定性清洗 · 企查查三域 Host Bridge'),
1433
+ h('small', null, '本地确定性清洗 · 可选企查查 MCP 补全'),
1065
1434
  ),
1066
1435
  ),
1067
1436
  h('span', {
@@ -1106,28 +1475,51 @@ window.__ModuleLoader__.load({
1106
1475
  function apply(ctx) {
1107
1476
  // eslint-disable-next-line no-console
1108
1477
  console.log('[dc-agent] client apply() ran');
1109
- const state = { applied: true, entry: 'sidebar.footer.action', overlay: 'shell.overlay', error: null };
1478
+ const state = {
1479
+ applied: true,
1480
+ entry: 'sidebar.workspaces:before(portal)',
1481
+ lifecycleSlot: 'sidebar.footer.action',
1482
+ capabilitySlot: 'conversation.input.left',
1483
+ headerSlot: 'conversation.session.header.actions',
1484
+ overlay: 'shell.overlay',
1485
+ error: null,
1486
+ };
1110
1487
  window.__DC_MVP__ = state;
1111
1488
  try {
1112
1489
  const workbenchStore = createWorkbenchStore();
1113
- ctx.effect(() => installSidebarStyles(), 'data-cleaning-agent: sidebar styles');
1490
+ ctx.effect(() => installUiStyles(), 'data-cleaning-agent: UI styles');
1114
1491
 
1115
- // 工作台:注册到 shell.overlay(与 mcp-connector 同源)。
1492
+ // 右侧工作台:additive overlay,不替换 DSH 单占位 details 面板。
1116
1493
  ctx.slots.inject('shell.overlay', () => ctx.slots.register({
1117
1494
  name: 'shell.overlay',
1118
1495
  id: 'data-cleaning-agent',
1119
1496
  order: 200,
1120
1497
  store: workbenchStore,
1121
- }, WorkbenchOverlay));
1498
+ }, WorkbenchDrawer));
1122
1499
 
1123
- // 左栏:注册到 sidebar.footer.action,order 10 排在 MCP连接器(order 0)下方。
1500
+ // 左栏:footer 只托管生命周期和 Portal 降级;实际入口显示在工作区列表前。
1124
1501
  ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register({
1125
1502
  name: 'sidebar.footer.action',
1126
1503
  id: 'data-cleaning-agent',
1127
1504
  order: 10,
1128
1505
  store: workbenchStore,
1506
+ inject: () => ({ startSession: () => startCleaningSession(ctx) }),
1129
1507
  }, SidebarEntry));
1130
1508
 
1509
+ // 原生会话输入框工具行:对齐 Mockup 的五个能力 ICON,不创建第二套聊天 UI。
1510
+ ctx.slots.inject('conversation.input.left', () => ctx.slots.register({
1511
+ name: 'conversation.input.left',
1512
+ id: 'data-cleaning-agent-capabilities',
1513
+ order: 110,
1514
+ }, CapabilityBar));
1515
+
1516
+ // 会话头部恢复入口:用户关闭右栏后可随时重新打开当前任务工作台。
1517
+ ctx.slots.inject('conversation.session.header.actions', () => ctx.slots.register({
1518
+ name: 'conversation.session.header.actions',
1519
+ id: 'data-cleaning-agent-workbench',
1520
+ order: 110,
1521
+ }, WorkbenchHeaderEntry));
1522
+
1131
1523
  // M3:三个工具的 tool.call.toolview 富化卡片(keyed by wire name,替代裸 JSON 摘要)。
1132
1524
  // 说明:分三条独立 inject(而非 generator)——测试 shim 对 inject 回调仅执行一次并 push 其返回值。
1133
1525
  const toolviewKeys = ['data_clean_rows', 'data_complete_rows', 'data_profile'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-data-cleaning-agent",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Clean, complete, and profile enterprise name lists in DeepSeek Harness — a data cleaning & completion agent plugin with local CSV/XLSX/JSON engine and optional Qichacha (QCC) MCP enrichment. Maintained by Qichacha/QCC.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -29,6 +29,7 @@
29
29
  "docs/RELEASE-0.4.0.md",
30
30
  "docs/RELEASE-0.5.0.md",
31
31
  "docs/RELEASE-0.5.1.md",
32
+ "docs/RELEASE-0.5.2.md",
32
33
  "docs/G5-HOST-BRIDGE.md",
33
34
  "docs/G5-E2E-RUNBOOK.md"
34
35
  ],