dsh-data-cleaning-agent 0.9.12 → 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 CHANGED
@@ -1,3 +1,9 @@
1
+ # 0.9.13 · 2026-09-14
2
+
3
+ - 实际提交被 Host 接纳后自动展开所属会话工作台,草稿回填及失败发送不展开。
4
+ - 同会话任务凭证及接纳回调去重,手动收起后不被轮询再次拉开;后台会话不抢占当前界面。
5
+ - 保留 0.9.12 统一首页及品牌隔离修复。
6
+
1
7
  # 0.9.12 · 2026-09-14
2
8
 
3
9
  - Remove home subtitle and its spacing, retain native controls.
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.12** (stable release)
13
+ > Current source version / 当前源码版本: **0.9.13** (stable release)
14
14
 
15
- 0.9.12 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.
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
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.12
27
27
 
28
28
  > 在 DeepSeek Harness 中清洗、补全、画像企业名单数据的智能体插件:本地 CSV/XLSX/JSON 引擎 + 可选企查查(Qichacha/QCC)MCP 企业数据补全,由企查查(Qichacha/QCC)团队发起并维护。
29
29
  >
30
- > 当前源码版本 / Current source version: **0.9.12**(正式版本)
30
+ > 当前源码版本 / Current source version: **0.9.13**(正式版本)
31
31
 
32
- 0.9.12 统一简洁首页:移除副标题与占位,保留原生工作区、模式和输入框。菜单名称固定并防止重复创建;品牌标题仅在所属首页生效,任务记录与模型运行状态明确区分。保留 0.9.11 的真实 Host 阶段与会话切换修复。
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,9 @@
1
+ # 0.9.13 — 接纳任务后自动展开工作台
2
+
3
+ - 实际提交接纳后,自动展开所属 Session 的工作台,方便查看任务执行进展。
4
+ - 草稿输入与回填不展开;失败发送不伪造已启动。
5
+ - 同一任务凭证与提交回调只自动展开一次,手动收起后不会被后续轮询拉开。
6
+ - 后台会话不抢占当前会话;接纳回调观察器跟随真实绑定切换,并在卸载时释放。
7
+ - 保留 0.9.12 的统一首页、稳定菜单及真实状态提示。
8
+
9
+ 无需改动业务数据、任务存储或用户 MCP 授权。
package/lib/client.js CHANGED
@@ -2136,14 +2136,12 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
2136
2136
  function watchHostedCommand(actions, sessionId, command) {
2137
2137
  const generation = commandWatchGeneration;
2138
2138
  const isActive = () => generation === commandWatchGeneration;
2139
- let openedForExecution = false;
2140
- const ownerVisible = () => typeof document === 'undefined' || [...document.querySelectorAll('.dcAgentExperience')].some(node => node.dataset.sessionId === sessionId);
2141
2139
  void waitForQccCommand(command.commandId, 1800, async (status) => {
2142
2140
  if (!status || status.state === 'prepared') return;
2143
2141
  await refreshHostedTask(actions, sessionId, command.taskId, null, true).catch(() => {});
2144
- if (!openedForExecution && ownerVisible() && isCleaningSession(sessionId) && workflowTaskBySession.get(String(sessionId))?.id === command.taskId) {
2145
- openedForExecution = true;
2146
- openWorkbench(actions, workflowTaskBySession.get(String(sessionId))?.stage || 'match', sessionId);
2142
+ if (isActive() && workflowTaskBySession.get(String(sessionId))?.id === command.taskId) {
2143
+ sessionWorkbenchController?.revealSubmitted(sessionId, command.commandId,
2144
+ workflowTaskBySession.get(String(sessionId))?.stage || 'match');
2147
2145
  }
2148
2146
  if (isCleaningSession(sessionId) && workflowTaskBySession.get(String(sessionId))?.id === command.taskId) actions.setCommandProgress?.({ taskId: command.taskId, ...status.progress });
2149
2147
  }, isActive).then(async (completed) => {
@@ -2438,7 +2436,73 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
2438
2436
  if (!sessionConversation || typeof sessionConversation.send !== 'function') {
2439
2437
  throw new Error('当前 DSH 版本没有可用的 Session conversation.send 能力');
2440
2438
  }
2441
- 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?.(); };
2442
2506
  }
2443
2507
 
2444
2508
  function setQccAgentDraft(ctx, sessionId, prompt) {
@@ -4940,6 +5004,7 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
4940
5004
  }
4941
5005
  let disposed = false;
4942
5006
  const instances = new Map();
5007
+ const revealedCommands = new Set();
4943
5008
  const spec = createWorkbenchStore();
4944
5009
  const instanceFor = sessionId => {
4945
5010
  if (!sessionId) throw new Error('请先打开数据清洗会话。');
@@ -5054,6 +5119,21 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
5054
5119
  sessions.open(task.originSessionId);
5055
5120
  // Navigation only: no task binding, command preparation, or draft writes.
5056
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
+ },
5057
5137
  storeFor: instanceFor,
5058
5138
  actionsFor: sessionId => instanceFor(sessionId).actions,
5059
5139
  open(sessionId, step) {
@@ -5069,6 +5149,7 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
5069
5149
  },
5070
5150
  };
5071
5151
  sessionWorkbenchController = controller;
5152
+ const stopSubmittedBridge = installSubmittedWorkbenchBridge(ctx, controller);
5072
5153
  const listeners = {
5073
5154
  [WORKBENCH_OPEN_EVENT]: event => {
5074
5155
  const detail = event.detail || {};
@@ -5116,6 +5197,7 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
5116
5197
  const shared = { controller, references: 1, release() {
5117
5198
  if (--shared.references > 0) return;
5118
5199
  disposed = true;
5200
+ stopSubmittedBridge();
5119
5201
  commandWatchGeneration += 1;
5120
5202
  for (const remove of bindings) remove();
5121
5203
  host.removeEventListener?.('click', previewClick, true);
@@ -5124,6 +5206,7 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
5124
5206
  disconnectFixture();
5125
5207
  void providerFiber?.dispose();
5126
5208
  instances.clear();
5209
+ revealedCommands.clear();
5127
5210
  if (host[CONTROLLER_KEY] === shared) delete host[CONTROLLER_KEY];
5128
5211
  if (sessionWorkbenchController === controller) sessionWorkbenchController = null;
5129
5212
  } };
@@ -5218,6 +5301,8 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
5218
5301
  createWorkbenchStore,
5219
5302
  WorkbenchContent,
5220
5303
  installSessionWorkbench,
5304
+ sendQccAgentCommand,
5305
+ installSubmittedWorkbenchBridge,
5221
5306
  DatabaseLogo,
5222
5307
  visibleCatalogFields,
5223
5308
  installCapabilityMount,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-data-cleaning-agent",
3
- "version": "0.9.12",
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,7 +21,7 @@
21
21
  "marketing",
22
22
  "docs/USER-GUIDE.md",
23
23
  "docs/RELEASE-0.9.11.md",
24
- "docs/RELEASE-0.9.12.md",
24
+ "docs/RELEASE-0.9.13.md",
25
25
  "docs/UNIFIED-HOME-1.5.4-ADOPTION.md",
26
26
  "docs/FIRST-CONTRIBUTION.md",
27
27
  "docs/COMPATIBILITY.md",
@@ -1,9 +0,0 @@
1
- # 0.9.12 · 简洁首页与局部品牌保护
2
-
3
- 首页移除副标题和专属留白,使用原生工作区、模式与输入框。左栏名称保持稳定,创建中禁用并阻止重复点击。品牌适配仅改写原生默认或本产品标题,停止隐藏其他产品 dock;保留 0.9.11 的真实阶段监听及晚挂载修复。
4
-
5
- 任务区说明任务记录不代表模型实时运行;同步失败保留记录且不重复查询。
6
-
7
- 验证:277 项检查,浅/深色 × 5 尺寸合成 Host React/Chromium 回归全部通过。未验证本轮真实四包共装、生产页面或真实计费 MCP。没有生产 Profile 修改。
8
-
9
- 发布要求:PR CI → 合并 main → main CI → 新 annotated v0.9.12 → 既有 OIDC workflow → Registry/provenance 回读。旧版本和 Tag 不动。