dsh-data-cleaning-agent 0.9.11 → 0.9.13
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 +13 -0
- package/README.en.md +2 -2
- package/README.md +3 -3
- package/docs/RELEASE-0.9.13.md +9 -0
- package/docs/UNIFIED-HOME-1.5.4-ADOPTION.md +24 -0
- package/lib/client.js +108 -41
- package/package.json +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
# 0.9.13 · 2026-09-14
|
|
2
|
+
|
|
3
|
+
- 实际提交被 Host 接纳后自动展开所属会话工作台,草稿回填及失败发送不展开。
|
|
4
|
+
- 同会话任务凭证及接纳回调去重,手动收起后不被轮询再次拉开;后台会话不抢占当前界面。
|
|
5
|
+
- 保留 0.9.12 统一首页及品牌隔离修复。
|
|
6
|
+
|
|
7
|
+
# 0.9.12 · 2026-09-14
|
|
8
|
+
|
|
9
|
+
- Remove home subtitle and its spacing, retain native controls.
|
|
10
|
+
- Keep launcher naming stable and prevent duplicate launches.
|
|
11
|
+
- Preserve other agents’ branding and surfaces; clarify recorded progress and synchronization failures.
|
|
12
|
+
- Adopt DSH-UX-001 v1.5.4; 277 checks and 10 isolated browser scenarios pass.
|
|
13
|
+
|
|
1
14
|
# Changelog
|
|
2
15
|
|
|
3
16
|
本文件记录 `dsh-data-cleaning-agent` 的版本变更。格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [SemVer](https://semver.org/lang/zh-CN/)。
|
package/README.en.md
CHANGED
|
@@ -10,9 +10,9 @@ Related agents: [数据清洗补全](https://github.com/duhu2000/dsh-data-cleani
|
|
|
10
10
|
|
|
11
11
|
> 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.
|
|
12
12
|
>
|
|
13
|
-
> Current source version / 当前源码版本: **0.9.
|
|
13
|
+
> Current source version / 当前源码版本: **0.9.13** (stable release)
|
|
14
14
|
|
|
15
|
-
0.9.
|
|
15
|
+
0.9.13 removes the home subtitle and its spacing, keeps native workspace/mode/composer controls, prevents duplicate menu launches, and scopes branding to its own home. Task records are distinguished from live model activity. The 0.9.11 Host phase and Session restoration fixes remain intact.
|
|
16
16
|
|
|
17
17
|
0.9.10 observes the Host settling-to-hero transition so the database logo and product title appear even after delayed layout. Unknown structures do not trigger global headline rewriting. Rollback version: 0.9.9.
|
|
18
18
|
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
数据清洗补全智能体:面向 Excel/CSV/JSON 企业名单,提供数据清洗、表格清洗、清洗补全、去重、企业数据补全与字段补全,支持企查查 MCP 和结果导出。
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
|
-
dsh plugin --profile web add dsh-data-cleaning-agent@0.9.
|
|
12
|
+
dsh plugin --profile web add dsh-data-cleaning-agent@0.9.13
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
请先满足下文的 DSH、连接器及侧边栏依赖要求;安装后完整停止并重启对应 Profile。
|
|
@@ -27,9 +27,9 @@ dsh plugin --profile web add dsh-data-cleaning-agent@0.9.11
|
|
|
27
27
|
|
|
28
28
|
> 在 DeepSeek Harness 中清洗、补全、画像企业名单数据的智能体插件:本地 CSV/XLSX/JSON 引擎 + 可选企查查(Qichacha/QCC)MCP 企业数据补全,由企查查(Qichacha/QCC)团队发起并维护。
|
|
29
29
|
>
|
|
30
|
-
> 当前源码版本 / Current source version: **0.9.
|
|
30
|
+
> 当前源码版本 / Current source version: **0.9.13**(正式版本)
|
|
31
31
|
|
|
32
|
-
0.9.
|
|
32
|
+
0.9.13 统一简洁首页:移除副标题与占位,保留原生工作区、模式和输入框。菜单名称固定并防止重复创建;品牌标题仅在所属首页生效,任务记录与模型运行状态明确区分。保留 0.9.11 的真实 Host 阶段与会话切换修复。
|
|
33
33
|
|
|
34
34
|
0.9.10 补齐首页 settling → hero 阶段监听,避免首次挂载漏掉 Logo 与标题替换;兼容“探索未知之境”,未知结构不做全局标题改写。回滚版本为 0.9.9。
|
|
35
35
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# DSH-UX-001 v1.5.4 采用记录
|
|
2
|
+
|
|
3
|
+
日期:2026-09-14。基线:0.9.11 / a0ec971db0d25d42f67f99e4328cca6f49a2bc2f。
|
|
4
|
+
本轮仅本地代码与验收,不发布,不修改正式 Profile 或全局 DSH。
|
|
5
|
+
|
|
6
|
+
## 已采用
|
|
7
|
+
|
|
8
|
+
- UX-05:首页删除 ProductHome 副标题组件、挂载与 dcAgentHomeSummary 样式;没有替代空白容器。保留原生工作区/模式/输入控件以及输入框下能力菜单,不修改 npm description/搜索文案。
|
|
9
|
+
- UX-45:左菜单固定“数据清洗补全”;创建中通过 disabled/aria-busy 表示,ref 门闩阻止同一渲染周期重复创建。失败仍沿用已有工作台错误状态,菜单名称不变。
|
|
10
|
+
- UX-46:保留 0.9.11 的真实 data-phase、openState、所有权立即重读和独立 Session namespace。只接管原生已知 Hero 标题或本插件标记;不覆盖其他产品标题,不再隐藏其他产品 dock。清理仅恢复仍为本插件标题的文字,不调用会话 rename API。
|
|
11
|
+
- UX-47:任务区明确展示“任务记录”,匹配/补全记录不冒充模型实时运行证明;元数据恢复/轮询失败明确为“状态同步异常”,保留最后记录且不重新查询。已有确认、取消、失败、完成状态继续来自 Host 任务数据,实际主体进度仍来自服务端计数,不伪造百分比。
|
|
12
|
+
|
|
13
|
+
## 验证范围
|
|
14
|
+
|
|
15
|
+
- npm run check:277 项通过,包括安装包检查、正文语法与既有业务测试。
|
|
16
|
+
- 新增/调整断言:首页不含副标题组件或专属样式;同时点击只创建一个 Session;外产品标题和 dock 不被接管;既有 settling→hero、文本刷新、普通 Session、phase observer 清理继续回归。
|
|
17
|
+
- scripts/ui-layout-regression.mjs:10/10 通过,真实 React/Chromium,合成 Host DOM/API fixture,浅/深色 × 5 尺寸。验证品牌、普通会话恢复、晚挂载和无副标题;完整现有导入/映射/确认/下载测试保留。
|
|
18
|
+
- 浏览器 fixture 不等于完整真实 DSH 宿主。当前本轮未验证四包真实共装、用户正式页面或真实模型/QCC/OCR;正式版本仍为 0.9.11。
|
|
19
|
+
|
|
20
|
+
## 边界
|
|
21
|
+
|
|
22
|
+
清洗目前没有已验证的模型 running 状态接口,不新增假定宿主正在思考的状态条,也不声称掌握模型停止原因。恢复仅读取任务元数据。模型运行态由原生会话显示。
|
|
23
|
+
|
|
24
|
+
证据:`/private/tmp/dc-unified-home-check.log`、`/private/tmp/dc-unified-home-ui-final.log` 与本工作树 `_scratch/ui-layout/results.json`。最后的未知标题保护收紧由观察器行为测试覆盖,未影响布局;其后完整 277 项检查再次通过。
|
package/lib/client.js
CHANGED
|
@@ -200,14 +200,13 @@ window.__ModuleLoader__.load({
|
|
|
200
200
|
}
|
|
201
201
|
.dcAgentCapability svg { flex: 0 0 auto; }
|
|
202
202
|
|
|
203
|
-
/*
|
|
203
|
+
/* 首页无副标题或占位;菜单独立 Portal 到 composer 后,不重排共享 slot。 */
|
|
204
204
|
|
|
205
205
|
.dcAgentExperience {
|
|
206
206
|
width: 100%;
|
|
207
207
|
box-sizing: border-box;
|
|
208
208
|
}
|
|
209
209
|
.dcAgentCapabilityMount { width: 100%; padding: 8px 0; flex: none; }
|
|
210
|
-
.dcAgentHomeSummary { margin: 0 20px 8px; text-align: center; color: var(--dc-muted); font-size: 13px; line-height: 20px; }
|
|
211
210
|
.dcAgentPromptBackdrop { position: fixed; inset: 0; z-index: 2000; display: grid; place-items: center; padding: 16px; box-sizing: border-box; background: rgba(8, 15, 28, .36); pointer-events: auto; }
|
|
212
211
|
body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
213
212
|
|
|
@@ -2137,14 +2136,12 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
2137
2136
|
function watchHostedCommand(actions, sessionId, command) {
|
|
2138
2137
|
const generation = commandWatchGeneration;
|
|
2139
2138
|
const isActive = () => generation === commandWatchGeneration;
|
|
2140
|
-
let openedForExecution = false;
|
|
2141
|
-
const ownerVisible = () => typeof document === 'undefined' || [...document.querySelectorAll('.dcAgentExperience')].some(node => node.dataset.sessionId === sessionId);
|
|
2142
2139
|
void waitForQccCommand(command.commandId, 1800, async (status) => {
|
|
2143
2140
|
if (!status || status.state === 'prepared') return;
|
|
2144
2141
|
await refreshHostedTask(actions, sessionId, command.taskId, null, true).catch(() => {});
|
|
2145
|
-
if (
|
|
2146
|
-
|
|
2147
|
-
|
|
2142
|
+
if (isActive() && workflowTaskBySession.get(String(sessionId))?.id === command.taskId) {
|
|
2143
|
+
sessionWorkbenchController?.revealSubmitted(sessionId, command.commandId,
|
|
2144
|
+
workflowTaskBySession.get(String(sessionId))?.stage || 'match');
|
|
2148
2145
|
}
|
|
2149
2146
|
if (isCleaningSession(sessionId) && workflowTaskBySession.get(String(sessionId))?.id === command.taskId) actions.setCommandProgress?.({ taskId: command.taskId, ...status.progress });
|
|
2150
2147
|
}, isActive).then(async (completed) => {
|
|
@@ -2439,7 +2436,73 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
2439
2436
|
if (!sessionConversation || typeof sessionConversation.send !== 'function') {
|
|
2440
2437
|
throw new Error('当前 DSH 版本没有可用的 Session conversation.send 能力');
|
|
2441
2438
|
}
|
|
2442
|
-
await sessionConversation.send(prompt);
|
|
2439
|
+
const result = await sessionConversation.send(prompt);
|
|
2440
|
+
if (result !== false) sessionWorkbenchController?.revealSubmitted(sessionId, submittedCommandId(prompt));
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
function submittedCommandId(prompt) {
|
|
2444
|
+
return String(prompt || '').match(/\bdc[qi]-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i)?.[0] || null;
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
/** Observe the public admission settlement; drafts and failed sends never reveal a Tab. */
|
|
2448
|
+
function installSubmittedWorkbenchBridge(ctx, controller) {
|
|
2449
|
+
const sessions = typeof ctx.get === 'function' ? ctx.get('sessions') : ctx.sessions;
|
|
2450
|
+
let stopSession = () => {};
|
|
2451
|
+
let current = null;
|
|
2452
|
+
let currentFace = null;
|
|
2453
|
+
const connect = () => {
|
|
2454
|
+
const sessionId = sessions?.list?.getSnapshot?.()?.current;
|
|
2455
|
+
const face = sessionId ? sessions?.binding?.(sessionId)?.session : null;
|
|
2456
|
+
if (sessionId === current && face === currentFace) return;
|
|
2457
|
+
stopSession(); stopSession = () => {}; current = sessionId; currentFace = face;
|
|
2458
|
+
if (!sessionId) return;
|
|
2459
|
+
if (typeof face?.beginSubmission === 'function') {
|
|
2460
|
+
const original = face.beginSubmission;
|
|
2461
|
+
const wrapped = function(input) {
|
|
2462
|
+
if (!isCleaningSession(sessionId)) return original.call(this, input);
|
|
2463
|
+
let observed = false;
|
|
2464
|
+
return original.call(this, { ...input, onRetire: settlement => {
|
|
2465
|
+
try { input.onRetire?.(settlement); }
|
|
2466
|
+
finally {
|
|
2467
|
+
if (settlement.reason === 'observed' && !observed) {
|
|
2468
|
+
observed = true;
|
|
2469
|
+
controller.revealSubmitted(sessionId, submittedCommandId(input.text));
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
} });
|
|
2473
|
+
};
|
|
2474
|
+
face.beginSubmission = wrapped;
|
|
2475
|
+
stopSession = () => { if (face.beginSubmission === wrapped) face.beginSubmission = original; };
|
|
2476
|
+
return;
|
|
2477
|
+
}
|
|
2478
|
+
// Older DSH versions publish committed nodes on the Session read face.
|
|
2479
|
+
if (!face?.subscribe || !face?.getSnapshot) return;
|
|
2480
|
+
let previous = face.getSnapshot();
|
|
2481
|
+
let baseline = previous.nodes?.length ?? 0;
|
|
2482
|
+
let highestSeq = Math.max(0, ...(previous.nodes || []).map(node => Number(node.seq) || 0));
|
|
2483
|
+
stopSession = face.subscribe(() => {
|
|
2484
|
+
const snapshot = face.getSnapshot();
|
|
2485
|
+
const nodes = snapshot.nodes || [];
|
|
2486
|
+
const appended = highestSeq ? nodes.filter(node => node.seq > highestSeq) : nodes.slice(baseline);
|
|
2487
|
+
highestSeq = Math.max(highestSeq, ...nodes.map(node => Number(node.seq) || 0));
|
|
2488
|
+
baseline = nodes.length;
|
|
2489
|
+
const restoring = (previous.openState && previous.openState !== 'open') || snapshot.loadingOlder;
|
|
2490
|
+
previous = snapshot;
|
|
2491
|
+
if (restoring) return;
|
|
2492
|
+
if (!isCleaningSession(sessionId)) return;
|
|
2493
|
+
for (const node of appended) {
|
|
2494
|
+
if (node.kind !== 'user' && node.role !== 'user' && node.message?.role !== 'user') continue;
|
|
2495
|
+
const content = node.message?.content ?? node.content ?? node.blocks ?? node.parts;
|
|
2496
|
+
const prompt = node.text || (typeof content === 'string' ? content
|
|
2497
|
+
: Array.isArray(content) ? content.map(part => typeof part === 'string' ? part : part.text || '').join('\n') : '');
|
|
2498
|
+
controller.revealSubmitted(sessionId, submittedCommandId(prompt));
|
|
2499
|
+
}
|
|
2500
|
+
});
|
|
2501
|
+
};
|
|
2502
|
+
connect();
|
|
2503
|
+
const stopList = sessions?.list?.subscribe?.(connect);
|
|
2504
|
+
connect();
|
|
2505
|
+
return () => { stopSession(); stopList?.(); };
|
|
2443
2506
|
}
|
|
2444
2507
|
|
|
2445
2508
|
function setQccAgentDraft(ctx, sessionId, prompt) {
|
|
@@ -2531,6 +2594,8 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
2531
2594
|
const { wide, useStore, actions, startSession } = props;
|
|
2532
2595
|
useStore((state) => state.open);
|
|
2533
2596
|
const [topMount, setTopMount] = react.useState(null);
|
|
2597
|
+
const [opening, setOpening] = react.useState(false);
|
|
2598
|
+
const openingRef = react.useRef(false);
|
|
2534
2599
|
|
|
2535
2600
|
react.useEffect(() => {
|
|
2536
2601
|
let disposed = false;
|
|
@@ -2559,7 +2624,12 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
2559
2624
|
className: 'dcAgentLauncher',
|
|
2560
2625
|
'data-wide': wide,
|
|
2561
2626
|
'aria-label': '数据清洗补全',
|
|
2627
|
+
'aria-busy': opening,
|
|
2628
|
+
disabled: opening,
|
|
2562
2629
|
onClick: async () => {
|
|
2630
|
+
if (openingRef.current) return;
|
|
2631
|
+
openingRef.current = true;
|
|
2632
|
+
setOpening(true);
|
|
2563
2633
|
try {
|
|
2564
2634
|
if (typeof startSession === 'function') {
|
|
2565
2635
|
const sessionId = await startSession();
|
|
@@ -2568,6 +2638,9 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
2568
2638
|
} catch (error) {
|
|
2569
2639
|
actions.setError(error instanceof Error ? error.message : String(error));
|
|
2570
2640
|
console.error('[dc-agent] open failed:', error);
|
|
2641
|
+
} finally {
|
|
2642
|
+
openingRef.current = false;
|
|
2643
|
+
setOpening(false);
|
|
2571
2644
|
}
|
|
2572
2645
|
},
|
|
2573
2646
|
children: h('span', { className: 'dcAgentLauncherContent' }, h(DatabaseLogo), wide ? h('span', null, '数据清洗补全') : null),
|
|
@@ -2604,11 +2677,6 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
2604
2677
|
authorization_required: '待连接企查查', cancelled: '已取消', parse_failed: '解析失败',
|
|
2605
2678
|
};
|
|
2606
2679
|
|
|
2607
|
-
function ProductHome() {
|
|
2608
|
-
return h('p', { className: 'dcAgentHomeSummary' },
|
|
2609
|
-
'导入企业名单或图片,核验主体、补全字段,并导出可追溯结果。');
|
|
2610
|
-
}
|
|
2611
|
-
|
|
2612
2680
|
function workflowNavigationIssue(step, task, hasData, profile, qccRun, hasLocalResult = false) {
|
|
2613
2681
|
if (['upload', 'history'].includes(step)) return null;
|
|
2614
2682
|
if (step === 'download' && task?.artifacts?.length) return null;
|
|
@@ -2744,8 +2812,6 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
2744
2812
|
const changedMarks = new Map();
|
|
2745
2813
|
const changedRows = new Map();
|
|
2746
2814
|
const ownedLogos = new Map();
|
|
2747
|
-
const hiddenForeignDocks = new Map();
|
|
2748
|
-
const composerStack = marker?.parentElement?.parentElement;
|
|
2749
2815
|
// 单一会话所有权由入口 enabled 与真实宿主 data-phase=hero 共同保证,
|
|
2750
2816
|
// 与 React effect 生命周期共同保证:非清洗会话不会挂载,会话切换即走 cleanup 让位。
|
|
2751
2817
|
// 有界同步:每次 fix 的 DOM 变更计入校正预算,达到上限即断开观察器,杜绝与其它插件互相改写的死循环。
|
|
@@ -2763,7 +2829,7 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
2763
2829
|
?? hero.querySelector?.('[class*="headlineText"]');
|
|
2764
2830
|
const badge = spans.find((element) => element.dataset?.dcAgentHeroBadge === 'true')
|
|
2765
2831
|
?? spans.find((element) => ['预览版', 'Preview'].includes(element.textContent?.trim()));
|
|
2766
|
-
if (title) {
|
|
2832
|
+
if (title && ['数据清洗补全智能体', '探索未至之境', '探索未知之境', 'Into the Unknown'].includes(title.textContent?.trim())) {
|
|
2767
2833
|
if (!changedTitles.has(title)) changedTitles.set(title, title.textContent ?? '');
|
|
2768
2834
|
title.dataset.dcAgentHeroTitle = 'true';
|
|
2769
2835
|
if (title.textContent !== '数据清洗补全智能体') { title.textContent = '数据清洗补全智能体'; mutated = true; }
|
|
@@ -2791,28 +2857,12 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
2791
2857
|
}
|
|
2792
2858
|
}
|
|
2793
2859
|
}
|
|
2794
|
-
if (badge) {
|
|
2860
|
+
if (badge && title?.dataset?.dcAgentHeroTitle === 'true') {
|
|
2795
2861
|
if (!changedBadges.has(badge)) changedBadges.set(badge, badge.style?.display ?? '');
|
|
2796
2862
|
badge.dataset.dcAgentHeroBadge = 'true';
|
|
2797
2863
|
if (badge.style && badge.style.display !== 'none') { badge.style.display = 'none'; mutated = true; }
|
|
2798
2864
|
}
|
|
2799
2865
|
|
|
2800
|
-
const foreignSurfaces = composerStack?.querySelectorAll?.('[aria-label="尽调类型"], .qccDock') ?? [];
|
|
2801
|
-
for (const foreignSurface of foreignSurfaces) {
|
|
2802
|
-
let hideTarget = foreignSurface;
|
|
2803
|
-
while (hideTarget?.parentElement
|
|
2804
|
-
&& hideTarget.parentElement !== composerStack
|
|
2805
|
-
&& hideTarget.parentElement !== marker.parentElement) {
|
|
2806
|
-
hideTarget = hideTarget.parentElement;
|
|
2807
|
-
}
|
|
2808
|
-
// DSH 的 list slot 可能把多个插件放进同一个 data-slot 容器。此时只能隐藏
|
|
2809
|
-
// 对方自己的根节点,不能隐藏共享 slot;独立 host 则隐藏其直接容器。
|
|
2810
|
-
if ((hideTarget?.parentElement === composerStack || hideTarget?.parentElement === marker.parentElement)
|
|
2811
|
-
&& !hideTarget.contains?.(marker)) {
|
|
2812
|
-
if (!hiddenForeignDocks.has(hideTarget)) hiddenForeignDocks.set(hideTarget, hideTarget.style?.display ?? '');
|
|
2813
|
-
if (hideTarget.style && hideTarget.style.display !== 'none') { hideTarget.style.display = 'none'; mutated = true; }
|
|
2814
|
-
}
|
|
2815
|
-
}
|
|
2816
2866
|
if (mutated) {
|
|
2817
2867
|
corrections += 1;
|
|
2818
2868
|
if (corrections >= MAX_HERO_CORRECTIONS && observer) {
|
|
@@ -2842,9 +2892,6 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
2842
2892
|
if (badge.style) badge.style.display = originalDisplay;
|
|
2843
2893
|
delete badge.dataset.dcAgentHeroBadge;
|
|
2844
2894
|
}
|
|
2845
|
-
for (const [foreignDock, originalDisplay] of hiddenForeignDocks) {
|
|
2846
|
-
if (foreignDock.style) foreignDock.style.display = originalDisplay;
|
|
2847
|
-
}
|
|
2848
2895
|
};
|
|
2849
2896
|
}
|
|
2850
2897
|
|
|
@@ -2895,7 +2942,6 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
2895
2942
|
},
|
|
2896
2943
|
dependencyMessage ? h('p', { className: 'dcAgentError', role: 'alert' }, dependencyMessage) : null,
|
|
2897
2944
|
menuMount ? reactDom.createPortal(h(CapabilityBar, { sessionId }), menuMount) : h(CapabilityBar, { sessionId }),
|
|
2898
|
-
hero ? h(ProductHome, { sessionId }) : null,
|
|
2899
2945
|
);
|
|
2900
2946
|
}
|
|
2901
2947
|
|
|
@@ -3872,7 +3918,7 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
3872
3918
|
const taskId = workflowTaskBySession.get(key)?.id || (/^dcw-[a-zA-Z0-9-]+$/.test(saved || '') ? saved : null);
|
|
3873
3919
|
if (taskId) await refreshHostedTask(actions, activeSessionId, taskId, null, false);
|
|
3874
3920
|
}).catch((taskError) => {
|
|
3875
|
-
if (!disposed) actions.setError(taskError instanceof Error ? taskError.message : String(taskError));
|
|
3921
|
+
if (!disposed) actions.setError(`状态同步异常:${taskError instanceof Error ? taskError.message : String(taskError)}。保留最近任务记录,未重新执行查询。`);
|
|
3876
3922
|
});
|
|
3877
3923
|
}
|
|
3878
3924
|
return () => { disposed = true; };
|
|
@@ -3890,7 +3936,7 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
3890
3936
|
if (!task) return;
|
|
3891
3937
|
if (['matching', 'enriching'].includes(task.state)) timer = setTimeout(poll, 2000);
|
|
3892
3938
|
else if (step === 'match' || step === 'enrich') actions.setStep(task.stage || step);
|
|
3893
|
-
} catch (error) { if (!disposed) actions.setError(error.message); }
|
|
3939
|
+
} catch (error) { if (!disposed) actions.setError(`状态同步异常:${error.message}。保留最近任务记录,未重新执行查询。`); }
|
|
3894
3940
|
};
|
|
3895
3941
|
timer = setTimeout(poll, 2000);
|
|
3896
3942
|
return () => { disposed = true; clearTimeout(timer); };
|
|
@@ -4831,8 +4877,9 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
4831
4877
|
) : null,
|
|
4832
4878
|
h('div', { className: 'dcAgentWbBody' },
|
|
4833
4879
|
cachedTask && step !== 'history' ? h('section', { className: 'dcAgentSection', 'aria-label': '当前任务进展', 'aria-live': 'polite' },
|
|
4834
|
-
h('b', null, cachedTask.source?.type === 'image' && cachedTask.state === 'draft' ? '图片已暂存,待识别' : WORKFLOW_STATE_LABELS[cachedTask.state] ||
|
|
4880
|
+
h('b', null, cachedTask.source?.type === 'image' && cachedTask.state === 'draft' ? '图片已暂存,待识别' : `任务记录:${WORKFLOW_STATE_LABELS[cachedTask.state] || '状态未知'}`),
|
|
4835
4881
|
h('h3', null, workflowDisplayName(cachedTask)),
|
|
4882
|
+
['matching', 'enriching'].includes(cachedTask.state) ? h('p', { className: 'dcAgentHint' }, '以上为最近同步的任务记录,不代表模型仍在运行。若会话已停止,请查看原会话的确认或错误提示;不要重复启动。') : null,
|
|
4836
4883
|
h('details', null, h('summary', null, '任务详情'), h('p', { className: 'dcAgentHint' }, `任务编号:${cachedTask.id}`)),
|
|
4837
4884
|
h('p', null, `${cachedTask.source?.type === 'image' ? '图片识别' : cachedTask.source?.type === 'text' ? '粘贴名单' : cachedTask.source ? '上传表格' : '待选择数据来源'}${cachedTask.source?.fileName ? ' · ' + cachedTask.source.fileName : ''} · ${cachedTask.source?.type === 'image' && cachedTask.state === 'draft' ? '条目数待识别' : `${cachedTask.source?.rowCount || 0} 条`} · ${cachedTask.fieldSelection?.length || 0} 个补全字段`),
|
|
4838
4885
|
commandProgress?.taskId === cachedTask.id && Number.isFinite(commandProgress.totalUnique) ? h('p', null, `已处理主体 ${commandProgress.completedUnique}/${commandProgress.totalUnique}(去重后,含未匹配或失败,不代表补全成功)`) : null,
|
|
@@ -4957,6 +5004,7 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
4957
5004
|
}
|
|
4958
5005
|
let disposed = false;
|
|
4959
5006
|
const instances = new Map();
|
|
5007
|
+
const revealedCommands = new Set();
|
|
4960
5008
|
const spec = createWorkbenchStore();
|
|
4961
5009
|
const instanceFor = sessionId => {
|
|
4962
5010
|
if (!sessionId) throw new Error('请先打开数据清洗会话。');
|
|
@@ -5071,6 +5119,21 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
5071
5119
|
sessions.open(task.originSessionId);
|
|
5072
5120
|
// Navigation only: no task binding, command preparation, or draft writes.
|
|
5073
5121
|
},
|
|
5122
|
+
revealSubmitted(sessionId, commandId, step) {
|
|
5123
|
+
if (disposed || !sessionId) return false;
|
|
5124
|
+
const key = commandId && `${sessionId}:${commandId}`;
|
|
5125
|
+
if (key && revealedCommands.has(key)) return false;
|
|
5126
|
+
// Consume the event even if its owner is no longer visible. Polling must
|
|
5127
|
+
// not pull open an old task when the user later returns to its Session.
|
|
5128
|
+
if (key) revealedCommands.add(key);
|
|
5129
|
+
const sessions = typeof ctx.get === 'function' ? ctx.get('sessions') : ctx.sessions;
|
|
5130
|
+
if (sessions?.list?.getSnapshot?.()?.current !== sessionId || !isCleaningSession(sessionId)) return false;
|
|
5131
|
+
try { return controller.open(sessionId, step); }
|
|
5132
|
+
catch (error) {
|
|
5133
|
+
instanceFor(sessionId).actions.setError(error.message || String(error));
|
|
5134
|
+
return false;
|
|
5135
|
+
}
|
|
5136
|
+
},
|
|
5074
5137
|
storeFor: instanceFor,
|
|
5075
5138
|
actionsFor: sessionId => instanceFor(sessionId).actions,
|
|
5076
5139
|
open(sessionId, step) {
|
|
@@ -5086,6 +5149,7 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
5086
5149
|
},
|
|
5087
5150
|
};
|
|
5088
5151
|
sessionWorkbenchController = controller;
|
|
5152
|
+
const stopSubmittedBridge = installSubmittedWorkbenchBridge(ctx, controller);
|
|
5089
5153
|
const listeners = {
|
|
5090
5154
|
[WORKBENCH_OPEN_EVENT]: event => {
|
|
5091
5155
|
const detail = event.detail || {};
|
|
@@ -5133,6 +5197,7 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
5133
5197
|
const shared = { controller, references: 1, release() {
|
|
5134
5198
|
if (--shared.references > 0) return;
|
|
5135
5199
|
disposed = true;
|
|
5200
|
+
stopSubmittedBridge();
|
|
5136
5201
|
commandWatchGeneration += 1;
|
|
5137
5202
|
for (const remove of bindings) remove();
|
|
5138
5203
|
host.removeEventListener?.('click', previewClick, true);
|
|
@@ -5141,6 +5206,7 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
5141
5206
|
disconnectFixture();
|
|
5142
5207
|
void providerFiber?.dispose();
|
|
5143
5208
|
instances.clear();
|
|
5209
|
+
revealedCommands.clear();
|
|
5144
5210
|
if (host[CONTROLLER_KEY] === shared) delete host[CONTROLLER_KEY];
|
|
5145
5211
|
if (sessionWorkbenchController === controller) sessionWorkbenchController = null;
|
|
5146
5212
|
} };
|
|
@@ -5235,11 +5301,12 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
|
|
|
5235
5301
|
createWorkbenchStore,
|
|
5236
5302
|
WorkbenchContent,
|
|
5237
5303
|
installSessionWorkbench,
|
|
5304
|
+
sendQccAgentCommand,
|
|
5305
|
+
installSubmittedWorkbenchBridge,
|
|
5238
5306
|
DatabaseLogo,
|
|
5239
5307
|
visibleCatalogFields,
|
|
5240
5308
|
installCapabilityMount,
|
|
5241
5309
|
PromptDialog,
|
|
5242
|
-
ProductHome,
|
|
5243
5310
|
workflowNavigationIssue,
|
|
5244
5311
|
buildTaskPrompt,
|
|
5245
5312
|
DatasetReview,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-data-cleaning-agent",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.13",
|
|
4
4
|
"description": "Data cleaning and data enrichment for CSV/XLSX/JSON enterprise lists in DeepSeek Harness, including spreadsheet cleaning, deduplication, profiling, optional Qichacha MCP and exports.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
"marketing",
|
|
22
22
|
"docs/USER-GUIDE.md",
|
|
23
23
|
"docs/RELEASE-0.9.11.md",
|
|
24
|
+
"docs/RELEASE-0.9.13.md",
|
|
25
|
+
"docs/UNIFIED-HOME-1.5.4-ADOPTION.md",
|
|
24
26
|
"docs/FIRST-CONTRIBUTION.md",
|
|
25
27
|
"docs/COMPATIBILITY.md",
|
|
26
28
|
"docs/UI-V1.5.0-ADOPTION.md",
|