dsh-data-cleaning-agent 0.6.2 → 0.7.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/lib/client.js CHANGED
@@ -113,7 +113,9 @@ window.__ModuleLoader__.load({
113
113
  .dcAgentWorkbench {
114
114
  display: flex;
115
115
  flex-direction: column;
116
- width: min(510px, calc(100vw - 72px));
116
+ width: min(460px, 42vw);
117
+ max-width: calc(100vw - 420px);
118
+ min-width: 360px;
117
119
  height: 100vh;
118
120
  border: 1px solid light-dark(#d5dae4, #2a3038);
119
121
  border-radius: 12px 0 0 12px;
@@ -397,8 +399,8 @@ window.__ModuleLoader__.load({
397
399
  .dcAgentWbHeader {
398
400
  display: flex;
399
401
  align-items: center;
400
- gap: 12px;
401
- padding: 14px 18px;
402
+ gap: 8px;
403
+ padding: 12px 14px;
402
404
  border-bottom: 1px solid light-dark(#e4e8f0, #303947);
403
405
  }
404
406
 
@@ -421,8 +423,18 @@ window.__ModuleLoader__.load({
421
423
  background: light-dark(#edf4ff, #172841);
422
424
  }
423
425
 
426
+ .dcAgentWbTitle b,
427
+ .dcAgentWbTitle small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
424
428
  .dcAgentWbTitle b { display: block; font-size: 15px; }
425
- .dcAgentWbTitle small { display: block; color: light-dark(#697386, #aab5c7); font-size: 11px; }
429
+ .dcAgentWbTitle small { display: block; max-width: 190px; color: light-dark(#697386, #aab5c7); font-size: 11px; }
430
+
431
+ .dcAgentWbActions {
432
+ display: flex;
433
+ align-items: center;
434
+ gap: 6px;
435
+ flex: 0 0 auto;
436
+ margin-left: auto;
437
+ }
426
438
 
427
439
  .dcAgentQccBadge {
428
440
  flex: 0 0 auto;
@@ -562,6 +574,12 @@ window.__ModuleLoader__.load({
562
574
  }
563
575
  .dcAgentTable th { color: light-dark(#697386, #aab5c7); font-weight: 600; }
564
576
  .dcAgentTable td.num { font-variant-numeric: tabular-nums; }
577
+ .dcAgentTableWrap { max-width: 100%; overflow-x: auto; }
578
+ .dcAgentTableWrap .dcAgentTable { min-width: 720px; table-layout: auto; }
579
+ .dcAgentTableWrap .dcAgentTable th,
580
+ .dcAgentTableWrap .dcAgentTable td { min-width: 112px; white-space: nowrap; }
581
+ .dcAgentTableWrap .dcAgentTable th:first-child,
582
+ .dcAgentTableWrap .dcAgentTable td:first-child { min-width: 190px; }
565
583
 
566
584
  .dcAgentError {
567
585
  padding: 10px 12px;
@@ -705,15 +723,46 @@ window.__ModuleLoader__.load({
705
723
  .dcAgentMappingRow > b { text-align: center; color: light-dark(#8792a3, #8290a5); }
706
724
  .dcAgentRulesGrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
707
725
  .dcAgentFieldGroup { display: grid; gap: 7px; margin-top: 12px; }
708
- .dcAgentFieldGroup > b { font-size: 12px; }
726
+ .dcAgentFieldGroupHead { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
727
+ .dcAgentFieldGroupHead > div:first-child { display: grid; gap: 2px; min-width: 0; }
728
+ .dcAgentFieldGroupHead b { font-size: 12px; }
729
+ .dcAgentFieldGroupActions { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }
730
+ .dcAgentFieldAction {
731
+ padding: 2px 7px;
732
+ border: 0;
733
+ border-radius: 6px;
734
+ background: transparent;
735
+ color: light-dark(#2869e6, #8cb3ff);
736
+ font: inherit;
737
+ font-size: 11px;
738
+ cursor: pointer;
739
+ }
740
+ .dcAgentFieldAction:hover, .dcAgentFieldAction:focus-visible { background: light-dark(#edf4ff, #172841); }
709
741
  .dcAgentPreviewTable { margin-top: 12px; overflow: auto; border: 1px solid light-dark(#e4e8f0, #303947); border-radius: 10px; }
710
742
  .dcAgentHistoryTask { display: block; width: 100%; padding: 10px; text-align: left; }
711
743
  .dcAgentHistoryTask h3 { margin: 0; font-size: 12px; }
744
+ /*
745
+ * DSH 的 root.side slot 是覆盖层。桌面端工作台打开时,让带稳定标记的
746
+ * 会话滚动区主动让出同宽空间,确保消息流和中央 composer 不被遮挡。
747
+ * 移动端仍保留全屏工作台,不压缩底层会话。
748
+ */
749
+ @media (min-width: 1041px) {
750
+ body:has(.dcAgentWorkbench) [data-conversation-scroll] {
751
+ box-sizing: border-box;
752
+ padding-right: min(460px, 42vw);
753
+ }
754
+ }
755
+ @media (min-width: 761px) and (max-width: 1040px) {
756
+ body:has(.dcAgentWorkbench) [data-conversation-scroll] {
757
+ box-sizing: border-box;
758
+ padding-right: min(420px, 48vw);
759
+ }
760
+ }
712
761
  @media (max-width: 760px) {
713
- .dcAgentWorkbench, .dcAgentWorkbench.is-expanded { width: 100vw; border-radius: 0; }
762
+ .dcAgentWorkbench, .dcAgentWorkbench.is-expanded { width: 100vw; max-width: none; min-width: 0; border-radius: 0; }
714
763
  .dcAgentOverlay { background: rgba(8, 10, 14, 0.22); pointer-events: auto; }
715
764
  .dcAgentStepper { grid-template-columns: repeat(2, minmax(0, 1fr)); }
716
- .dcAgentQccBadge { display: none; }
765
+ .dcAgentQccBadge, .dcAgentJobsPill { display: none; }
717
766
  .dcAgentCandidate { grid-template-columns: 1fr; }
718
767
  .dcAgentCapabilities { justify-content: flex-start; padding-inline: 12px; }
719
768
  .dcAgentCapability { min-width: 92px; }
@@ -725,6 +774,10 @@ window.__ModuleLoader__.load({
725
774
  .dcAgentMappingRow { grid-template-columns: 1fr; }
726
775
  .dcAgentMappingRow > b { display: none; }
727
776
  }
777
+ @media (min-width: 761px) and (max-width: 1040px) {
778
+ .dcAgentWorkbench { width: 420px; max-width: 48vw; min-width: 360px; }
779
+ .dcAgentQccBadge, .dcAgentJobsPill { display: none; }
780
+ }
728
781
  `;
729
782
 
730
783
  /** 安装入口、会话能力按钮与右侧工作台样式。 */
@@ -792,30 +845,101 @@ window.__ModuleLoader__.load({
792
845
  ['validate_identity', '主体标识校验'],
793
846
  ['complete_fields', '补全所选企业字段'],
794
847
  ];
848
+ const SELF_RISK_FIELD_OPTIONS = [
849
+ ['dishonest', '失信信息'], ['judgment_debtor', '被执行人'], ['consumption_restriction', '限制高消费'],
850
+ ['terminated_case', '终本案件'], ['judicial_document', '裁判文书'], ['case_filing', '立案信息'],
851
+ ['hearing_announcement', '开庭公告'], ['court_announcement', '法院公告'], ['service_notice', '送达公告'],
852
+ ['bankruptcy_reorganization', '破产重整'], ['equity_freeze', '股权冻结'], ['judicial_auction', '司法拍卖'],
853
+ ['valuation_inquiry', '询价评估'], ['pre_litigation_mediation', '诉前调解'], ['exit_restriction', '限制出境'],
854
+ ['administrative_penalty', '行政处罚'], ['operating_exception', '经营异常'], ['serious_violation', '严重违法'],
855
+ ['environmental_penalty', '环保处罚'], ['abnormal_taxpayer', '税务非正常户'], ['tax_arrears', '欠税公告'],
856
+ ['tax_violation', '税收违法'], ['disciplinary_list', '惩戒名单'], ['default_matter', '违约事项'],
857
+ ['guarantee', '担保信息'], ['equity_pledge_registration', '股权出质'], ['stock_pledge', '股权质押'],
858
+ ['chattel_mortgage', '动产抵押'], ['land_mortgage', '土地抵押'], ['simple_cancellation', '简易注销'],
859
+ ['cancellation_filing', '注销备案'], ['liquidation', '清算信息'], ['labor_arbitration', '劳动仲裁'],
860
+ ['public_notice', '公示催告'], ['property_reward_notice', '财产悬赏公告'],
861
+ ];
862
+ const RELATED_RISK_FIELD_OPTIONS = [
863
+ ['dishonest', '失信被执行人'], ['judgment_debtor', '被执行人'], ['consumption_restriction', '限制高消费'],
864
+ ['serious_violation', '严重违法'], ['tax_violation', '税收违法'], ['administrative_penalty', '行政处罚'],
865
+ ['bankruptcy_reorganization', '破产重整'], ['terminated_case', '终本案件'], ['equity_freeze', '股权冻结'],
866
+ ['tax_arrears', '欠税公告'], ['operating_exception', '经营异常'],
867
+ ];
868
+ const RELATED_RISK_KEY_FIELD_OPTIONS = [
869
+ ['dishonest', '失信被执行人'], ['serious_violation', '严重违法'], ['bankruptcy_reorganization', '破产重整'],
870
+ ['tax_violation', '税收违法'], ['judgment_debtor', '被执行人'], ['terminated_case', '终本案件'],
871
+ ['equity_freeze', '股权冻结'],
872
+ ];
795
873
  const FIELD_GROUPS = [
796
- ['identity', '基础工商信息', [
874
+ ['company_registration', '企业工商信息', [
797
875
  ['company_name', '企业名称'], ['credit_no', '统一社会信用代码'], ['reg_no', '注册号'],
798
- ['reg_status', '登记状态'], ['legal_rep', '法定代表人'], ['reg_capital', '注册资本'],
876
+ ['org_no', '组织机构代码'], ['tax_no', '纳税人识别号'], ['reg_status', '登记状态'],
877
+ ['legal_rep', '法定代表人'], ['reg_capital', '注册资本'],
799
878
  ['paid_capital', '实缴资本'], ['establish_date', '成立日期'], ['company_type', '企业类型'],
800
- ['registration_authority', '登记机关'], ['former_name', '曾用名'], ['english_name', '英文名'],
801
- ]],
802
- ['contact', '地址与联系方式', [
803
- ['registered_address', '注册地址'], ['province', '省份地区'], ['city', '城市'],
804
- ['district', '区县'], ['phone', '电话'], ['email', '邮箱'], ['website', '官网'],
805
- ]],
806
- ['operation', '经营信息', [
807
- ['business_scope', '经营范围'], ['industry_category', '国标行业'], ['industry_large', '一级行业'],
808
- ['industry_middle', '二级行业'], ['operating_period', '营业期限'], ['company_size', '企业规模'],
809
- ['company_profile', '企业简介'],
810
- ]],
811
- ['risk', '风险摘要', [
812
- ['risk_summary', '风险摘要'], ['operating_exception', '经营异常摘要'], ['administrative_penalty', '行政处罚摘要'],
813
- ]],
814
- ['ipr', '知识产权摘要', [
815
- ['trademark_summary', '商标摘要'], ['patent_summary', '专利摘要'], ['software_copyright_summary', '软件著作权摘要'],
816
- ]],
879
+ ['approval_date', '核准日期'], ['registration_authority', '登记机关'],
880
+ ['taxpayer_qualification', '纳税人资质'], ['payment_line_no', '支付系统行号'],
881
+ ['import_export_company_code', '进出口企业代码'], ['short_name', '企业简称'], ['english_name', '英文名'],
882
+ ['registered_address', '注册地址'], ['mailing_address', '通信地址'], ['region', '所属地区'],
883
+ ['business_scope', '经营范围'], ['industry_category', '国标行业'],
884
+ ['operating_period', '营业期限'], ['company_size', '人员规模'], ['insured_count', '参保人数'],
885
+ ['branch_insured_count', '分支机构参保人数'],
886
+ ], 'get_company_registration_info'],
887
+ ['company_profile', '企业简介', [
888
+ ['qcc_industry', '企查查行业'], ['company_profile', '企业简介'],
889
+ ['industry_chain_overview', '产业链概览'],
890
+ ], 'get_company_profile'],
891
+ ['contact_info', '联系方式', [
892
+ ['contact_preferred_phone', '首选联系电话'], ['contact_phone_invalid_flag', '首选电话无效标记'],
893
+ ['contact_phone_tags', '首选电话标签'], ['contact_preferred_email', '首选邮箱'],
894
+ ['contact_official_website', '官方网站'], ['contact_official_website_icp', '官网 ICP 备案'],
895
+ ], 'get_contact_info'],
896
+ ['listing_info', '上市信息', [
897
+ ['listing_date', '上市日期'], ['listing_short_name', '股票简称'], ['listing_stock_code', '股票代码'],
898
+ ['listing_exchange', '上市交易所'], ['listing_board', '上市板块'], ['listing_former_short_name', '上市曾用名'],
899
+ ['listing_total_market_value', '总市值'], ['listing_total_shares', '总股本'], ['listing_predicted_pe', '预测市盈率'],
900
+ ['listing_float_market_value', '流通值'], ['listing_float_shares', '流通股'], ['listing_pb_ratio', '市净率'],
901
+ ['listing_eps', 'EPS'], ['listing_voting_rights_difference', '表决权差异'],
902
+ ['listing_registration_based', '是否注册制'],
903
+ ], 'get_listing_info'],
904
+ ['tax_invoice_info', '税务开票信息', [
905
+ ['tax_company_name', '税务主体名称'], ['tax_identification_no', '税务纳税人识别号'],
906
+ ['tax_company_type', '税务企业类型'], ['tax_business_status', '税务经营状态'],
907
+ ['invoice_address', '开票地址'], ['invoice_phone', '开票联系电话'],
908
+ ['invoice_bank', '开户行'], ['invoice_bank_account', '开户行账号'],
909
+ ], 'get_tax_invoice_info'],
910
+ ['import_export_credit', '进出口信用', [
911
+ ['import_export_credit_no', '进出口统一社会信用代码'], ['import_export_customs', '所在地海关'],
912
+ ['import_export_admin_division', '进出口行政区划'], ['import_export_address', '进出口备案地址'],
913
+ ['import_export_economic_area', '经济区划'], ['import_export_trade_type', '经营类别'],
914
+ ['import_export_statistical_economic_area', '统计经济区划'], ['import_export_industry', '进出口行业种类'],
915
+ ['import_export_ecommerce_type', '跨境贸易电子商务类型'], ['import_export_credit_grade', '海关信用等级'],
916
+ ['import_export_filing_date', '进出口备案日期'],
917
+ ], 'get_import_export_credit'],
918
+ ['company_risk_scan', '企业自身风险扫描', [
919
+ ['risk_recorded_factor_count', '风险有记录因子数'], ['risk_no_record_factor_count', '风险无记录因子数'],
920
+ ['risk_hit_summary', '企业自身风险命中摘要'],
921
+ ...SELF_RISK_FIELD_OPTIONS.map(([id, label]) => [`risk_${id}_count`, `${label}条目数`]),
922
+ ], 'get_company_risk_scan'],
923
+ ['company_related_risk_scan', '企业关联风险扫描', [
924
+ ['related_risk_party_count', '有风险关联方数'], ['related_risk_summary', '企业关联风险摘要'],
925
+ ...RELATED_RISK_FIELD_OPTIONS.map(([id, label]) => [`related_risk_${id}_count`, `关联风险-${label}条目数`]),
926
+ ...RELATED_RISK_KEY_FIELD_OPTIONS.map(([id, label]) => [`related_risk_${id}_party_count`, `关联风险-${label}命中关联方数`]),
927
+ ], 'get_company_related_risk_scan'],
817
928
  ];
818
929
  const ENRICHMENT_OPTIONS = FIELD_GROUPS.flatMap(([, , fields]) => fields);
930
+ const INPUT_ONLY_MAPPING_OPTIONS = [['phone', '联系电话']];
931
+ const RESULT_FIELD_LABELS = new Map([
932
+ ...ENRICHMENT_OPTIONS,
933
+ ...INPUT_ONLY_MAPPING_OPTIONS,
934
+ ['biz_status', '经营状态'],
935
+ ['risk_tags', '风险标签'],
936
+ ['qcc_match_status', '匹配状态'],
937
+ ['qcc_source', '数据来源'],
938
+ ['qcc_error', '错误原因'],
939
+ ['match_status', '匹配状态'],
940
+ ['input_company', '原始企业名称'],
941
+ ['candidate_company', '候选企业名称'],
942
+ ]);
819
943
  const MATCH_ANCHOR_OPTIONS = [
820
944
  ['company_name', '企业名称'], ['credit_no', '统一社会信用代码'], ['reg_no', '注册号'],
821
945
  ];
@@ -852,9 +976,13 @@ window.__ModuleLoader__.load({
852
976
  function isKnownCleaningDraft(value) {
853
977
  if (value === DEFAULT_SESSION_PROMPT) return true;
854
978
  if (typeof value !== 'string') return false;
855
- return value.startsWith('请执行一项企业名单数据清洗补全任务。')
979
+ const wizardDraft = value.startsWith('请执行一项企业名单数据清洗补全任务。')
856
980
  && value.includes('企查查连接、套餐额度和费用均由当前用户自己的账号承担。')
857
981
  && value.includes('提供结果和待复核清单的导出。');
982
+ const executionDraft = value.startsWith('请执行已在「数据清洗补全工作台」确认的企业数据任务。')
983
+ && value.includes('安全任务凭证:dcq-')
984
+ && value.includes('data_cleaning_qcc_run');
985
+ return wizardDraft || executionDraft;
858
986
  }
859
987
 
860
988
  function clearCleaningDraft(ctx, sessionId, onlyDefault = false) {
@@ -1029,12 +1157,27 @@ window.__ModuleLoader__.load({
1029
1157
  };
1030
1158
  }
1031
1159
 
1160
+ /**
1161
+ * 工作台粘贴区兼容「每行一个企业名称 / 信用代码」。
1162
+ * 无分隔符且首行不是显式表头时,不得把第一家企业误当 CSV 表头。
1163
+ */
1164
+ function plainEntityListDataset(text) {
1165
+ const lines = String(text ?? '').split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
1166
+ if (!lines.length || lines.some((line) => /[,\t;]/.test(line))) return null;
1167
+ const explicitHeader = /^(?:name|company|company_name|credit_no|creditcode|unified_credit_code|主体标识|企业名称|公司名称|单位名称|统一社会信用代码|信用代码|注册号)$/i;
1168
+ if (explicitHeader.test(lines[0])) return null;
1169
+ return entriesToDataset(lines);
1170
+ }
1171
+
1172
+ function resultFieldLabel(key) {
1173
+ return RESULT_FIELD_LABELS.get(String(key ?? '')) ?? String(key ?? '');
1174
+ }
1175
+
1032
1176
  const MAPPING_PATTERNS = [
1033
1177
  ['company_name', /^(name|company|company_name|企业名称|公司名称|单位名称)$/i],
1034
1178
  ['credit_no', /^(credit_no|creditCode|unified_credit_code|统一社会信用代码|信用代码)$/i],
1035
1179
  ['reg_no', /^(reg_no|registration_no|注册号)$/i],
1036
1180
  ['phone', /^(phone|mobile|tel|telephone|联系电话|手机号码|手机号)$/i],
1037
- ['province', /^(province|省份|省份地区)$/i],
1038
1181
  ['registered_address', /^(address|registered_address|注册地址|企业地址|公司地址)$/i],
1039
1182
  ];
1040
1183
 
@@ -1131,10 +1274,8 @@ window.__ModuleLoader__.load({
1131
1274
  return task;
1132
1275
  }
1133
1276
 
1134
- async function ensureWorkflowTask(actions, sessionId, draft = {}) {
1277
+ async function createWorkflowTask(actions, sessionId, draft = {}) {
1135
1278
  const key = String(sessionId || 'unassigned');
1136
- const cached = workflowTaskBySession.get(key);
1137
- if (cached) return cacheWorkflowTask(actions, sessionId, cached);
1138
1279
  let pending = workflowTaskCreationBySession.get(key);
1139
1280
  if (!pending) {
1140
1281
  pending = requestJson('/data-cleaning/api/workflow/tasks', 'POST', {
@@ -1155,6 +1296,29 @@ window.__ModuleLoader__.load({
1155
1296
  return cacheWorkflowTask(actions, sessionId, response.task);
1156
1297
  }
1157
1298
 
1299
+ async function ensureWorkflowTask(actions, sessionId, draft = {}) {
1300
+ const key = String(sessionId || 'unassigned');
1301
+ const cached = workflowTaskBySession.get(key);
1302
+ if (cached) return cacheWorkflowTask(actions, sessionId, cached);
1303
+ return createWorkflowTask(actions, sessionId, draft);
1304
+ }
1305
+
1306
+ const EDITABLE_WORKFLOW_STATES = new Set(['draft', 'uploaded', 'parse_failed']);
1307
+
1308
+ /**
1309
+ * 完成 / 已确认规则的任务不能就地改写。用户再次录入名单时,
1310
+ * 在同一清洗会话内自动创建新 taskId,避免把新名单写入旧任务。
1311
+ */
1312
+ async function ensureEditableWorkflowTask(actions, sessionId, draft = {}, preferredTask) {
1313
+ const key = String(sessionId || 'unassigned');
1314
+ const current = workflowTaskBySession.get(key) ?? preferredTask ?? null;
1315
+ if (!current) return createWorkflowTask(actions, sessionId, draft);
1316
+ if (EDITABLE_WORKFLOW_STATES.has(current.state)) return cacheWorkflowTask(actions, sessionId, current);
1317
+ workflowTaskBySession.delete(key);
1318
+ actions.resetRunForNewDataset?.();
1319
+ return createWorkflowTask(actions, sessionId, draft);
1320
+ }
1321
+
1158
1322
  /** 同一会话的 Host 写操作串行化,确保 expectedRevision 不因 UI 事件竞态失效。 */
1159
1323
  function queueWorkflowOperation(sessionId, operation) {
1160
1324
  const key = String(sessionId || 'unassigned');
@@ -1273,6 +1437,15 @@ window.__ModuleLoader__.load({
1273
1437
  draft.paidConfirmed = false;
1274
1438
  draft.error = null;
1275
1439
  },
1440
+ resetRunForNewDataset: (draft) => {
1441
+ draft.profile = null;
1442
+ draft.clean = null;
1443
+ draft.complete = null;
1444
+ draft.qccEstimate = null;
1445
+ draft.qccRun = null;
1446
+ draft.paidConfirmed = false;
1447
+ draft.error = null;
1448
+ },
1276
1449
  setDataset: (draft, dataset) => { draft.dataset = dataset; draft.error = null; },
1277
1450
  setProfile: (draft, profile) => { draft.profile = profile; draft.error = null; },
1278
1451
  setClean: (draft, clean) => { draft.clean = clean; draft.error = null; },
@@ -1379,6 +1552,18 @@ window.__ModuleLoader__.load({
1379
1552
  await sessionConversation.send(prompt);
1380
1553
  }
1381
1554
 
1555
+ function setQccAgentDraft(ctx, sessionId, prompt) {
1556
+ const conversation = typeof ctx.get === 'function' ? ctx.get('conversation') : ctx.conversation;
1557
+ const shell = conversation?.input?.shell?.(sessionId);
1558
+ if (!shell || typeof shell.setDraft !== 'function') {
1559
+ throw new Error('当前 DSH 版本没有可用的会话草稿回填能力');
1560
+ }
1561
+ const accepted = shell.setDraft(prompt);
1562
+ if (accepted === false) throw new Error('任务说明未能回填到当前对话框');
1563
+ ctx.sessions?.open?.(sessionId);
1564
+ return true;
1565
+ }
1566
+
1382
1567
  function capabilityIcon(item, size = 16) {
1383
1568
  return typeof item.icon === 'function'
1384
1569
  ? h(item.icon, { size, 'aria-hidden': 'true' })
@@ -1819,6 +2004,15 @@ window.__ModuleLoader__.load({
1819
2004
  }),
1820
2005
  label,
1821
2006
  ));
2007
+ const setPromptFieldGroup = (fields, enabledGroup) => {
2008
+ const groupIds = new Set(fields.map(([id]) => id));
2009
+ const selected = new Set(enrichmentKeys);
2010
+ for (const id of groupIds) {
2011
+ if (enabledGroup) selected.add(id); else selected.delete(id);
2012
+ }
2013
+ const catalogOrder = FIELD_GROUPS.flatMap(([, , groupFields]) => groupFields.map(([id]) => id));
2014
+ setEnrichmentKeys(catalogOrder.filter((id) => selected.has(id)));
2015
+ };
1822
2016
 
1823
2017
  return h('div', { className: 'dcAgentPromptLayer' },
1824
2018
  h('button', {
@@ -1896,13 +2090,22 @@ window.__ModuleLoader__.load({
1896
2090
  ) : null,
1897
2091
  wizardStep === 3 ? h('div', { className: 'dcAgentWizardPane' },
1898
2092
  h('h4', null, '选择清洗目标和补全字段'),
1899
- h('p', null, '仅展示当前企业数据范围;最终可用字段以当前用户连接能力为准。'),
2093
+ h('p', null, '共 128 个一企一行字段:基础 30、第一批 40、第二批 58。最终可用字段以当前用户连接的 QCC MCP 能力为准。'),
1900
2094
  h('div', { className: 'dcAgentPromptGroup' },
1901
2095
  h('b', null, '清洗目标'),
1902
2096
  h('div', { className: 'dcAgentPromptChoices' }, choice(CLEANING_OPTIONS, cleaningKeys, setCleaningKeys)),
1903
2097
  ),
1904
- FIELD_GROUPS.map(([groupId, label, fields]) => h('div', { className: 'dcAgentPromptGroup', key: groupId },
1905
- h('b', null, label),
2098
+ FIELD_GROUPS.map(([groupId, label, fields, sourceTool]) => h('div', { className: 'dcAgentPromptGroup', key: groupId },
2099
+ h('div', { className: 'dcAgentFieldGroupHead' },
2100
+ h('div', null,
2101
+ h('b', null, `${label} · ${fields.filter(([id]) => enrichmentKeys.includes(id)).length}/${fields.length}`),
2102
+ sourceTool ? h('small', { className: 'dcAgentHint' }, `来源:${sourceTool}`) : null,
2103
+ ),
2104
+ h('div', { className: 'dcAgentFieldGroupActions' },
2105
+ h('button', { type: 'button', className: 'dcAgentFieldAction', onClick: () => setPromptFieldGroup(fields, true) }, '全选'),
2106
+ h('button', { type: 'button', className: 'dcAgentFieldAction', onClick: () => setPromptFieldGroup(fields, false) }, '清空'),
2107
+ ),
2108
+ ),
1906
2109
  h('div', { className: 'dcAgentPromptChoices' }, choice(fields, enrichmentKeys, setEnrichmentKeys)),
1907
2110
  )),
1908
2111
  ) : null,
@@ -2054,6 +2257,8 @@ window.__ModuleLoader__.load({
2054
2257
  }
2055
2258
  return;
2056
2259
  }
2260
+ const entityList = plainEntityListDataset(trimmed);
2261
+ if (entityList) return entityList;
2057
2262
  return api('/data-cleaning/api/mvp/parse', { filename: 'data.csv', content: trimmed });
2058
2263
  }
2059
2264
 
@@ -2085,9 +2290,19 @@ window.__ModuleLoader__.load({
2085
2290
  }
2086
2291
 
2087
2292
  async function persistParsedWorkflow(actions, sessionId, result, source = {}, task) {
2088
- let current = task ?? await ensureWorkflowTask(actions, sessionId);
2293
+ let current = await ensureEditableWorkflowTask(actions, sessionId, {}, task);
2089
2294
  const stagingKey = `session:${sessionId || 'unassigned'}`;
2090
2295
  moveRuntime(stagingKey, current.id);
2296
+ // handleParse 可能先把新数据写入旧 task runtime。无论是否新建 taskId,
2297
+ // 都以本次解析结果覆盖目标 runtime,避免复用已完成任务的一行旧数据。
2298
+ const target = runtimeFor(current.id);
2299
+ target.rows = Array.isArray(result.rows) ? result.rows : [];
2300
+ target.headers = Array.isArray(result.headers) ? result.headers : [];
2301
+ target.source = {
2302
+ type: source.type || result.fmt || 'csv',
2303
+ fileName: source.fileName || '',
2304
+ sizeBytes: source.sizeBytes || 0,
2305
+ };
2091
2306
  if (['draft', 'uploaded', 'parse_failed'].includes(current.state)) {
2092
2307
  current = await workflowAction(actions, sessionId, current, 'upload', {
2093
2308
  source: {
@@ -2107,6 +2322,7 @@ window.__ModuleLoader__.load({
2107
2322
  function WorkbenchDrawer(props) {
2108
2323
  const { useStore, actions } = props;
2109
2324
  const sendSessionCommand = props.sendSessionCommand;
2325
+ const setSessionDraft = props.setSessionDraft;
2110
2326
  const open = useStore((state) => state.open);
2111
2327
  const step = useStore((state) => state.step);
2112
2328
  const expanded = useStore((state) => state.expanded);
@@ -2173,7 +2389,7 @@ window.__ModuleLoader__.load({
2173
2389
  if (draft.fieldSelection) actions.setFieldSelection(draft.fieldSelection);
2174
2390
  for (const [key, value] of Object.entries(draft.matchRules || {})) actions.setMatchRule(key, value);
2175
2391
  void queueWorkflowOperation(detail.sessionId, async () => {
2176
- const task = await ensureWorkflowTask(actions, detail.sessionId, draft);
2392
+ const task = await ensureEditableWorkflowTask(actions, detail.sessionId, draft);
2177
2393
  return updateWorkflowTask(actions, detail.sessionId, task, draft);
2178
2394
  }).catch((eventError) => actions.setError(eventError instanceof Error ? eventError.message : String(eventError)));
2179
2395
  };
@@ -2258,6 +2474,9 @@ window.__ModuleLoader__.load({
2258
2474
  const runtimeKey = cachedTask?.id ?? `session:${activeSessionId || 'unassigned'}`;
2259
2475
  const runtime = runtimeFor(runtimeKey);
2260
2476
  const lastCsv = runtime.lastCsv;
2477
+ const qccPreviewColumns = Array.isArray(qccRun?.rows) && qccRun.rows.length
2478
+ ? Object.keys(qccRun.rows[0] || {}).slice(0, 8)
2479
+ : [];
2261
2480
  const fieldByPattern = (pattern) => runtime.headers.find((field) => pattern.test(field)) ?? null;
2262
2481
  const phoneField = fieldByPattern(/^(phone|mobile|tel|telephone|联系电话|手机号码|手机号)$/i);
2263
2482
  const amountField = fieldByPattern(/^(amount|price|金额|注册资本)$/i);
@@ -2412,14 +2631,18 @@ window.__ModuleLoader__.load({
2412
2631
 
2413
2632
  const estimateQcc = async () => {
2414
2633
  const uniqueCompanies = new Set(runtime.rows.map((row) => String(row?.[nameField] ?? '').trim()).filter(Boolean)).size;
2415
- const requiresProfile = fieldSelection.some((field) => ['company_profile', 'industry_large', 'industry_middle'].includes(field));
2416
- const callsPerCompany = requiresProfile ? 3 : 2;
2634
+ const selected = fieldSelection.length ? fieldSelection : DEFAULT_ENRICHMENT_KEYS;
2635
+ const selectedGroups = uiFieldGroups.filter(([, , fields]) => fields.some(([id]) => selected.includes(id)));
2636
+ const sourceTools = [...new Set(selectedGroups.map(([, , , sourceTool]) => sourceTool).filter(Boolean))];
2637
+ const callsPerCompany = 1 + sourceTools.length;
2638
+ const estimatedCalls = uniqueCompanies * callsPerCompany;
2417
2639
  actions.setQccEstimate({
2418
2640
  uniqueCompanies,
2419
- tools: requiresProfile ? ['主体查询', '工商信息', '企业画像'] : ['主体查询', '工商信息'],
2420
- estimatedCalls: uniqueCompanies * callsPerCompany,
2641
+ tools: ['主体查询', ...selectedGroups.map(([, label]) => label)],
2642
+ sourceTools,
2643
+ estimatedCalls,
2421
2644
  maxCalls: 300,
2422
- withinLimit: runtime.rows.length <= 100,
2645
+ withinLimit: runtime.rows.length <= 100 && estimatedCalls <= 300,
2423
2646
  estimateType: 'upper-bound',
2424
2647
  });
2425
2648
  };
@@ -2491,15 +2714,47 @@ window.__ModuleLoader__.load({
2491
2714
  return completedCommand.run;
2492
2715
  };
2493
2716
 
2717
+ const prepareEditableQccCommand = async (payload) => {
2718
+ if (!activeSessionId || typeof setSessionDraft !== 'function') {
2719
+ throw new Error('当前会话无法回填可编辑的数据清洗补全任务说明');
2720
+ }
2721
+ const prepared = await api('/data-cleaning/api/g5/commands', {
2722
+ ...payload,
2723
+ confirmPaidCalls: true,
2724
+ });
2725
+ const command = prepared?.command;
2726
+ if (!command?.commandId || !command?.prompt) throw new Error('Host 未返回有效的企查查任务命令');
2727
+ setSessionDraft(activeSessionId, command.prompt);
2728
+ actions.close();
2729
+
2730
+ // 发送权仍在用户手中:草稿回填不会调用 QCC。用户发送后,
2731
+ // Agent-owned 工具改变 command 状态;本地安全轮询只读取结果并回写工作流。
2732
+ void waitForQccCommand(command.commandId, 1800).then(async (completedCommand) => {
2733
+ if (completedCommand.state === 'failed') {
2734
+ const commandError = new Error(completedCommand.error?.message || '智能体企查查任务执行失败');
2735
+ commandError.code = completedCommand.error?.code;
2736
+ throw commandError;
2737
+ }
2738
+ if (!completedCommand.run) throw new Error('智能体企查查任务完成但结果已失效');
2739
+ await queueWorkflowOperation(activeSessionId, () => applyQccRun(completedCommand.run));
2740
+ }).catch(async (commandError) => {
2741
+ if (['QCC_NOT_CONNECTED', 'QCC_TOOL_UNAVAILABLE', 'QCC_AUTH_REQUIRED'].includes(commandError?.code)) {
2742
+ const latest = workflowTaskBySession.get(String(activeSessionId || 'unassigned')) ?? cachedTask;
2743
+ try { await workflowAction(actions, activeSessionId, latest, 'authorization-required'); } catch (_workflowError) {}
2744
+ }
2745
+ actions.setError(commandError instanceof Error ? commandError.message : String(commandError));
2746
+ });
2747
+ return command;
2748
+ };
2749
+
2494
2750
  const runQcc = async () => {
2495
2751
  if (busy || !qccEstimate || !paidConfirmed || !qccEstimate.withinLimit) return;
2496
2752
  actions.setBusy(true);
2497
2753
  actions.setError(null);
2498
2754
  try {
2499
- const current = await workflowAction(actions, activeSessionId, cachedTask, 'match-start');
2500
- const r = await executePreparedQccCommand({
2755
+ await prepareEditableQccCommand({
2501
2756
  kind: 'enrich',
2502
- taskId: current.id,
2757
+ taskId: cachedTask.id,
2503
2758
  rows: runtime.rows,
2504
2759
  headers: runtime.headers,
2505
2760
  nameField,
@@ -2507,11 +2762,7 @@ window.__ModuleLoader__.load({
2507
2762
  includeRisk: false,
2508
2763
  concurrency: 2,
2509
2764
  });
2510
- await applyQccRun(r);
2511
2765
  } catch (err) {
2512
- if (['QCC_NOT_CONNECTED', 'QCC_TOOL_UNAVAILABLE', 'QCC_AUTH_REQUIRED'].includes(err?.code)) {
2513
- try { await workflowAction(actions, activeSessionId, cachedTask, 'authorization-required'); } catch (_workflowError) {}
2514
- }
2515
2766
  actions.setError(err instanceof Error ? err.message : String(err));
2516
2767
  } finally {
2517
2768
  actions.setBusy(false);
@@ -2733,9 +2984,20 @@ window.__ModuleLoader__.load({
2733
2984
  h('b', { className: tone ? `is-${tone}` : null }, displayStatValue(value))
2734
2985
  );
2735
2986
  const uiFieldGroups = Array.isArray(workflowContract?.fieldCatalog)
2736
- ? workflowContract.fieldCatalog.map((group) => [group.id, group.label, (group.fields || []).map((field) => [field.id, field.label])])
2987
+ ? workflowContract.fieldCatalog.map((group) => [group.id, group.label, (group.fields || []).map((field) => [field.id, field.label]), group.sourceTool])
2737
2988
  : FIELD_GROUPS;
2738
- const targetFields = uiFieldGroups.flatMap(([, , fields]) => fields);
2989
+ const inputOnlyMappingFields = Array.isArray(workflowContract?.inputOnlyMappingFields)
2990
+ ? workflowContract.inputOnlyMappingFields.map((field) => [field.id, field.label])
2991
+ : INPUT_ONLY_MAPPING_OPTIONS;
2992
+ const targetFields = [...uiFieldGroups.flatMap(([, , fields]) => fields), ...inputOnlyMappingFields];
2993
+ const setWorkbenchFieldGroup = (fields, enabledGroup) => {
2994
+ const selected = new Set(fieldSelection);
2995
+ for (const [id] of fields) {
2996
+ if (enabledGroup) selected.add(id); else selected.delete(id);
2997
+ }
2998
+ const catalogOrder = uiFieldGroups.flatMap(([, , groupFields]) => groupFields.map(([id]) => id));
2999
+ actions.setFieldSelection(catalogOrder.filter((id) => selected.has(id)));
3000
+ };
2739
3001
 
2740
3002
  let pane;
2741
3003
  if (step === 'history') {
@@ -2808,8 +3070,22 @@ window.__ModuleLoader__.load({
2808
3070
  ),
2809
3071
  h('section', { className: 'dcAgentSection' },
2810
3072
  h('h3', null, `输出字段 · 已选 ${fieldSelection.length}`),
2811
- uiFieldGroups.map(([groupId, label, fields]) => h('div', { key: groupId, className: 'dcAgentFieldGroup' },
2812
- h('b', null, label),
3073
+ h('div', { className: 'dcAgentRow', 'aria-label': '补全字段批次概览' },
3074
+ h('span', { className: 'dcAgentChip' }, '基础字段 30'),
3075
+ h('span', { className: 'dcAgentChip' }, '第一批 40'),
3076
+ h('span', { className: 'dcAgentChip' }, '第二批 58'),
3077
+ ),
3078
+ uiFieldGroups.map(([groupId, label, fields, sourceTool]) => h('div', { key: groupId, className: 'dcAgentFieldGroup' },
3079
+ h('div', { className: 'dcAgentFieldGroupHead' },
3080
+ h('div', null,
3081
+ h('b', null, `${label} · ${fields.filter(([id]) => fieldSelection.includes(id)).length}/${fields.length}`),
3082
+ sourceTool ? h('small', { className: 'dcAgentHint' }, `来源:${sourceTool}`) : null,
3083
+ ),
3084
+ h('div', { className: 'dcAgentFieldGroupActions' },
3085
+ h('button', { type: 'button', className: 'dcAgentFieldAction', onClick: () => setWorkbenchFieldGroup(fields, true) }, '全选'),
3086
+ h('button', { type: 'button', className: 'dcAgentFieldAction', onClick: () => setWorkbenchFieldGroup(fields, false) }, '清空'),
3087
+ ),
3088
+ ),
2813
3089
  h('div', { className: 'dcAgentChips' }, fields.map(([key, fieldLabel]) => h('label', { key, className: `dcAgentChip${fieldSelection.includes(key) ? ' is-selected' : ''}` },
2814
3090
  h('input', { type: 'checkbox', checked: fieldSelection.includes(key), onChange: () => actions.toggleField(key) }), ` ${fieldLabel}`,
2815
3091
  ))),
@@ -2860,14 +3136,14 @@ window.__ModuleLoader__.load({
2860
3136
  );
2861
3137
  } else if (step === 'match') {
2862
3138
  pane = h('div', { className: 'dcAgentPane' },
2863
- h('p', { className: 'dcAgentHint' }, '通过当前用户连接的企查查 MCP 定位企业主体并补全基础工商字段。多候选始终由人工选择;界面不生成虚构置信度。'),
3139
+ h('p', { className: 'dcAgentHint' }, '通过当前用户连接的企查查 MCP 定位企业主体,并按已选维度补全一企一行字段。多候选始终由人工选择;界面不生成虚构置信度。'),
2864
3140
  h('section', { className: 'dcAgentSection' },
2865
3141
  h('h3', null, '匹配依据'),
2866
- h('div', { className: 'dcAgentChips' }, mappings.filter((mapping) => ['company_name', 'credit_no', 'reg_no'].includes(mapping.targetField)).map((mapping) => h('span', { key: mapping.sourceField, className: 'dcAgentChip' }, `${mapping.sourceField} → ${ENRICHMENT_OPTIONS.find(([id]) => id === mapping.targetField)?.[1] || mapping.targetField}`))),
3142
+ h('div', { className: 'dcAgentChips' }, mappings.filter((mapping) => ['company_name', 'credit_no', 'reg_no'].includes(mapping.targetField)).map((mapping) => h('span', { key: mapping.sourceField, className: 'dcAgentChip' }, `${mapping.sourceField} → ${resultFieldLabel(mapping.targetField)}`))),
2867
3143
  h('p', { className: 'dcAgentHint' }, '统一社会信用代码等强标识优先;名称匹配不明确时进入人工核验队列。'),
2868
3144
  ),
2869
3145
  h('section', { className: 'dcAgentSection' },
2870
- h('h3', null, '企查查基础企业能力'),
3146
+ h('h3', null, '企查查一企一行补全能力'),
2871
3147
  h('div', { className: 'dcAgentRow' },
2872
3148
  h('button', { type: 'button', className: 'dcAgentButton', disabled: busy, onClick: loadQccCapabilities }, busy ? '检测中…' : '检测企查查连接'),
2873
3149
  qccCapabilities ? h('span', { className: 'dcAgentHint' }, qccCapabilities.capabilities?.ready ? '主体查询与工商信息工具已就绪' : '连接未就绪或基础工具不完整') : null,
@@ -2886,8 +3162,9 @@ window.__ModuleLoader__.load({
2886
3162
  h('input', { type: 'checkbox', checked: paidConfirmed, 'aria-label': '确认使用当前用户的企查查账号额度', onChange: (event) => actions.setPaidConfirmed(event.target.checked) }),
2887
3163
  '我已核对企业数量和调用上界,并确认使用自己连接的企查查 MCP 账号;额度或费用由该账号自行承担',
2888
3164
  ),
3165
+ h('p', { className: 'dcAgentHint' }, '下一步只会把中文任务说明回填到中央对话框。你可先阅读、补充备注,点击发送后才开始企查查调用。'),
2889
3166
  h('div', { className: 'dcAgentRow' },
2890
- h('button', { type: 'button', className: 'dcAgentButton is-primary', disabled: busy || !paidConfirmed || !qccEstimate.withinLimit, onClick: runQcc }, busy ? '执行中…' : '开始主体匹配'),
3167
+ h('button', { type: 'button', className: 'dcAgentButton is-primary', disabled: busy || !paidConfirmed || !qccEstimate.withinLimit, onClick: runQcc }, busy ? '生成中…' : '生成可编辑任务说明'),
2891
3168
  ),
2892
3169
  ) : null,
2893
3170
  ),
@@ -2902,10 +3179,10 @@ window.__ModuleLoader__.load({
2902
3179
  Array.isArray(qccRun.rows) && qccRun.rows.length ? h('div', { className: 'dcAgentTableWrap' },
2903
3180
  h('table', { className: 'dcAgentTable', 'aria-label': '匹配补全结果预览' },
2904
3181
  h('thead', null, h('tr', null,
2905
- Object.keys(qccRun.rows[0] || {}).slice(0, 6).map((key) => h('th', { key }, key)),
3182
+ qccPreviewColumns.map((key) => h('th', { key }, resultFieldLabel(key))),
2906
3183
  )),
2907
3184
  h('tbody', null, qccRun.rows.slice(0, 8).map((row, index) => h('tr', { key: `${index}-${row?.qcc_credit_no || row?.credit_no || ''}` },
2908
- Object.keys(qccRun.rows[0] || {}).slice(0, 6).map((key) => h('td', { key }, String(row?.[key] ?? ''))),
3185
+ qccPreviewColumns.map((key) => h('td', { key }, String(row?.[key] ?? ''))),
2909
3186
  ))),
2910
3187
  ),
2911
3188
  h('p', { className: 'dcAgentHint' }, `显示前 ${Math.min(8, qccRun.rows.length)} 行;完整结果在下载阶段生成耐久制品。`),
@@ -2926,7 +3203,7 @@ window.__ModuleLoader__.load({
2926
3203
  h('p', { className: 'dcAgentHint' }, '执行本地确定性清洗并核对本任务所选补全字段。外部字段以当前用户企查查 MCP 的真实返回为准,缺失值不会被编造。'),
2927
3204
  h('section', { className: 'dcAgentSection' },
2928
3205
  h('h3', null, `已选补全字段 · ${fieldSelection.length}`),
2929
- h('div', { className: 'dcAgentChips' }, fieldSelection.map((id) => h('span', { className: 'dcAgentChip', key: id }, ENRICHMENT_OPTIONS.find(([fieldId]) => fieldId === id)?.[1] || id))),
3206
+ h('div', { className: 'dcAgentChips' }, fieldSelection.map((id) => h('span', { className: 'dcAgentChip', key: id }, resultFieldLabel(id)))),
2930
3207
  ),
2931
3208
  h('section', { className: 'dcAgentSection' },
2932
3209
  h('h3', null, '本地清洗预处理'),
@@ -3047,26 +3324,29 @@ window.__ModuleLoader__.load({
3047
3324
  h('small', null, cachedTask ? `${WORKFLOW_STATE_LABELS[cachedTask.state] || cachedTask.state} · ${cachedTask.id.slice(0, 18)}` : '正在创建 Host taskId…'),
3048
3325
  ),
3049
3326
  ),
3050
- h('span', {
3051
- className: 'dcAgentQccBadge',
3052
- title: qccRun ? `任务 ${qccRun.runId}` : '仅在当前用户确认使用自己的企查查账号后调用',
3053
- }, qccRun ? `QCC · ${qccRun.state}` : qccCapabilities ? (qccCapabilities.capabilities?.ready ? 'QCC · 已连接' : 'QCC · 能力不完整') : 'QCC · 待检测'),
3054
- h('span', { className: 'dcAgentJobsPill', 'data-state': jobsPill(jobs).state, title: '后台任务状态' }, jobsPill(jobs).label),
3055
- h('button', {
3056
- className: 'dcAgentWbClose',
3057
- type: 'button',
3058
- 'aria-label': expanded ? '收起工作台' : '展开工作台',
3059
- onClick: () => actions.toggleExpanded(),
3060
- }, expanded ? '↘' : '↖'),
3061
- h('button', {
3062
- className: 'dcAgentWbClose',
3063
- type: 'button',
3064
- 'aria-label': '关闭',
3065
- onClick: () => {
3066
- stopJobsPolling();
3067
- actions.close();
3068
- },
3069
- }, '✕'),
3327
+ h('div', { className: 'dcAgentWbActions' },
3328
+ h('span', {
3329
+ className: 'dcAgentQccBadge',
3330
+ title: qccRun ? `任务 ${qccRun.runId}` : '仅在当前用户确认使用自己的企查查账号后调用',
3331
+ }, qccRun ? `QCC · ${qccRun.state}` : qccCapabilities ? (qccCapabilities.capabilities?.ready ? 'QCC · 已连接' : 'QCC · 能力不完整') : 'QCC · 待检测'),
3332
+ h('span', { className: 'dcAgentJobsPill', 'data-state': jobsPill(jobs).state, title: '后台任务状态' }, jobsPill(jobs).label),
3333
+ h('button', {
3334
+ className: 'dcAgentWbClose',
3335
+ type: 'button',
3336
+ 'aria-label': expanded ? '收起工作台' : '展开工作台',
3337
+ onClick: () => actions.toggleExpanded(),
3338
+ }, expanded ? '' : '↖'),
3339
+ h('button', {
3340
+ className: 'dcAgentWbClose',
3341
+ type: 'button',
3342
+ title: '关闭工作台(Esc)',
3343
+ 'aria-label': '关闭',
3344
+ onClick: () => {
3345
+ stopJobsPolling();
3346
+ actions.close();
3347
+ },
3348
+ }, '✕'),
3349
+ ),
3070
3350
  ),
3071
3351
  h('nav', { className: 'dcAgentStepper', 'aria-label': '清洗流程' },
3072
3352
  STEPS.map((st) => h('button', {
@@ -3114,6 +3394,7 @@ window.__ModuleLoader__.load({
3114
3394
  store: workbenchStore,
3115
3395
  inject: () => ({
3116
3396
  sendSessionCommand: (sessionId, prompt) => sendQccAgentCommand(ctx, sessionId, prompt),
3397
+ setSessionDraft: (sessionId, prompt) => setQccAgentDraft(ctx, sessionId, prompt),
3117
3398
  }),
3118
3399
  }, WorkbenchDrawer));
3119
3400
 
@@ -3182,8 +3463,11 @@ window.__ModuleLoader__.load({
3182
3463
  isKnownCleaningDraft,
3183
3464
  markCleaningSession,
3184
3465
  qualitySummaryFor,
3466
+ plainEntityListDataset,
3467
+ resultFieldLabel,
3185
3468
  rewriteHeroChrome,
3186
3469
  ensureWorkflowTask,
3470
+ ensureEditableWorkflowTask,
3187
3471
  queueWorkflowOperation,
3188
3472
  };
3189
3473
  return module.exports;