dsh-pre-duediligence 0.1.33 → 0.1.35
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 +14 -0
- package/README.md +5 -5
- package/docs/RELEASE-0.1.34.md +9 -0
- package/docs/RELEASE-0.1.35.md +8 -0
- package/install.sh +1 -1
- package/lib/client.js +113 -90
- package/lib/index.js +29 -9
- package/lib/index.js.map +3 -3
- package/lib/types/composer-model.d.ts +1 -0
- package/lib/types/hosted-task-sync.d.ts +1 -1
- package/lib/types/previsit-dock.d.ts +2 -0
- package/lib/types/report-files-panel.d.ts +3 -1
- package/lib/types/report-time.d.ts +3 -0
- package/package.json +1 -1
- package/skills/qcc-previsit-onepager/SKILL.md +7 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
# 更新日志
|
|
2
2
|
## [Unreleased]
|
|
3
3
|
|
|
4
|
+
## [0.1.35] - 2026-09-15
|
|
5
|
+
|
|
6
|
+
- 任务提交后对象与目标改为只读,覆盖尚未调用 begin 的运行阶段,阻止重复启动。
|
|
7
|
+
- 提示词生成器明确访前技能路由,投资机构深度尽调不误切投委会流程;保留主体绑定保护并给出恢复步骤。
|
|
8
|
+
- 报告预览、HTML、Word/PDF 使用保存时的北京时间至秒,旧日期记录不虚构精度。
|
|
9
|
+
- 下载按钮统一为 HTML、Word、PDF,保留 PDF 主按钮。
|
|
10
|
+
|
|
11
|
+
## [0.1.34] - 2026-09-15
|
|
12
|
+
|
|
13
|
+
- 修正启动导航:主体确认前停留对象与目标,确认后展示范围,再进入实际查询阶段。
|
|
14
|
+
- 完成后的风险结论不再显示待研判;明确未发现与结论缺口分开呈现。
|
|
15
|
+
- 默认展开风险采集摘要,统一 PDF/Word/HTML 下载入口,生成后自动下载。
|
|
16
|
+
- 删除宿主技术提示及旧字体配置说明,保留手动阅读位置。
|
|
17
|
+
|
|
4
18
|
## [0.1.33] - 2026-09-15
|
|
5
19
|
|
|
6
20
|
- 分离客观数据采集与证据分析,消除跨页重复卡片。
|
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# dsh-pre-duediligence
|
|
2
2
|
|
|
3
|
-
0.1.
|
|
3
|
+
0.1.35 分离资料采集与证据核验:采集仅展示来源数据,核验前置风险研判并展示机会假设、支持、反证及证据缺口。支持有真实引用的阶段性风险分级;无需执行项目合并说明。“现场验证”改为“证据验证”,待沟通事项明确为“待客户确认”。保留报告版本、历史回看及离线中文 PDF/Word 导出。详见 [发布说明](docs/RELEASE-0.1.35.md)。
|
|
4
4
|
|
|
5
5
|
## 安装与三分钟上手
|
|
6
6
|
|
|
7
7
|
访前尽调智能体:面向拜访前调查,提供企业尽调、客户尽调、客户背景调查、工商核验与风险信息整理;使用企查查 MCP,提供固定路由连续调用、主体确认、动态进度和可下载报告。
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
|
-
dsh plugin --profile web add dsh-pre-duediligence@0.1.
|
|
10
|
+
dsh plugin --profile web add dsh-pre-duediligence@0.1.35
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
请先满足下文的 DSH 与连接器要求;Better Sidebar 仅在需要可视化工作台时选装。安装后完整停止并重启对应 Profile。
|
|
@@ -92,7 +92,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/duhu2000/dsh-pre-duediligenc
|
|
|
92
92
|
安装脚本默认向 DSH Web profile 安装两个基础 Bundle:
|
|
93
93
|
|
|
94
94
|
1. `dsh-mcp-connector@0.2.32`:提供通用 MCP 连接器与市场,通过“企查查·企业工商”完成 OAuth,并动态挂载企业、风险、知产、经营、历史和董监高 MCP。
|
|
95
|
-
2. `dsh-pre-duediligence@0.1.
|
|
95
|
+
2. `dsh-pre-duediligence@0.1.35`:提供左侧智能体入口、原生会话首页、提示词生成器、Host 执行门和 Skill。
|
|
96
96
|
|
|
97
97
|
默认不会安装 Better Sidebar。需要 Session 级可视化工作台时显式开启:
|
|
98
98
|
|
|
@@ -120,7 +120,7 @@ dsh web
|
|
|
120
120
|
|
|
121
121
|
~~~bash
|
|
122
122
|
dsh plugin --profile web add dsh-mcp-connector@0.2.32
|
|
123
|
-
dsh plugin --profile web add dsh-pre-duediligence@0.1.
|
|
123
|
+
dsh plugin --profile web add dsh-pre-duediligence@0.1.35 --allow-build=node-pty,dsh-pre-duediligence
|
|
124
124
|
dsh web
|
|
125
125
|
~~~
|
|
126
126
|
|
|
@@ -136,7 +136,7 @@ dsh web
|
|
|
136
136
|
dsh plugin --profile web list --depth 0
|
|
137
137
|
~~~
|
|
138
138
|
|
|
139
|
-
应始终看到所选基线的 Connector 和 `dsh-pre-duediligence@0.1.
|
|
139
|
+
应始终看到所选基线的 Connector 和 `dsh-pre-duediligence@0.1.35`;仅在显式启用或原本已安装时看到 Sidebar。重启 DSH 并连接“企查查·企业工商”后,在左侧菜单点击“访前尽调”进入初始原生会话。无 Sidebar 时可继续生成提示词、回填/发送草稿并在会话中阅读报告;五项流程按钮会提示安装可选工作台。安装 Sidebar 时,右侧工作台初始仍关闭;点击业务按钮可主动打开,正式发送任务被接纳后会自动展开一次。仅回填不会展开,收起后轮询不会重新打开。其右侧“+”菜单不会列出该入口,普通会话保持 DSH 原样。
|
|
140
140
|
|
|
141
141
|
### 无 Sidebar 功能边界
|
|
142
142
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# v0.1.34
|
|
2
|
+
|
|
3
|
+
- 新任务自动展开对象与目标;主体绑定前不进入资料采集。已确认主体先展示范围,再按真实查询进入采集或核验。用户手动浏览后保留当前页,报告完成不强制跳转。
|
|
4
|
+
- 完成后的风险摘要区分本次明确未发现、已发现事项与结论未明确;兼容加粗表格级别和明确的红线文字结论,缺省项不自动判零。
|
|
5
|
+
- 风险明细与采集覆盖默认展示客观摘要,仅长字段保留折叠。
|
|
6
|
+
- 当前报告底部统一下载 PDF(主要)、Word、HTML;PDF/Word 一次点击生成并下载,失败可重试。历史详情使用同一格式入口。
|
|
7
|
+
- 移除底部宿主技术提示及过时的管理员字体配置说明。PDF 内置字体保持不变。
|
|
8
|
+
|
|
9
|
+
更新不会修改已保存报告正文或重跑历史查询。旧报告缺少明确分级时如实显示结论缺口,需继续尽调补充,不以完成状态代替核验。
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# 0.1.35
|
|
2
|
+
|
|
3
|
+
- 已发送的任务在对象与目标页显示只读状态,包含模型运行但尚未创建任务记录的阶段;阻止重复开始。
|
|
4
|
+
- 提示词生成器明确访前技能与主体绑定路由,投资机构/深度/完整报告仍走访前流程;保留原始 MCP 防绕过保护,错误提供恢复步骤。未修改全局投资技能。
|
|
5
|
+
- 当前、历史 HTML 预览及下载、Word/PDF 使用保存时间,显示北京时间至秒;仅有日期的历史记录保留精度。原报告不修改,衍生文件使用新渲染缓存标识。
|
|
6
|
+
- 下载顺序 HTML、Word、PDF,PDF 为主按钮。
|
|
7
|
+
|
|
8
|
+
首页初始引导由总账统一规划,不属于本次变更。模型实际技能选择仍需在真实 DSH 投资机构场景回归;自动化测试不是生产宿主验收。
|
package/install.sh
CHANGED
|
@@ -5,7 +5,7 @@ set -euo pipefail
|
|
|
5
5
|
readonly PROFILE_NAME="${DSH_PROFILE:-web}"
|
|
6
6
|
readonly BASELINE="${DSH_PREVISIT_BASELINE:-stable}"
|
|
7
7
|
readonly WORKBENCH_MODE="${DSH_PREVISIT_WORKBENCH:-off}"
|
|
8
|
-
readonly PREVISIT_SPEC="${DSH_PRE_DUEDILIGENCE_SPEC:-dsh-pre-duediligence@0.1.
|
|
8
|
+
readonly PREVISIT_SPEC="${DSH_PRE_DUEDILIGENCE_SPEC:-dsh-pre-duediligence@0.1.35}"
|
|
9
9
|
readonly LEGACY_PREVISIT_NAME="qcc-previsit-dsh"
|
|
10
10
|
readonly LEGACY_QCC_OAUTH_NAME="qcc-dsh-mcp-oauth"
|
|
11
11
|
|
package/lib/client.js
CHANGED
|
@@ -234,6 +234,10 @@ function optionById(options, id) {
|
|
|
234
234
|
function hasSelection(selection) {
|
|
235
235
|
return selection.role !== void 0 || selection.purpose !== void 0 || selection.focus.length > 0 || selection.budget !== void 0 || selection.output !== void 0;
|
|
236
236
|
}
|
|
237
|
+
function routePrevisitPrompt(text) {
|
|
238
|
+
if (!text.trim() || text.includes("\u8BF7\u4F7F\u7528 qcc-previsit-onepager")) return text;
|
|
239
|
+
return text + "\n\u8BF7\u4F7F\u7528 qcc-previsit-onepager Skill \u6267\u884C\u8BBF\u524D\u5C3D\u8C03\uFF1B\u6295\u8D44\u673A\u6784\u89D2\u8272\u3001\u6DF1\u5EA6\u5C3D\u8C03\u548C\u5B8C\u6574\u62A5\u544A\u4E5F\u6CBF\u7528\u672C\u6D41\u7A0B\uFF0C\u4E0D\u5207\u6362\u4E3A ic-memo-qcc\u3002\u5148 previsit_begin\uFF0C\u518D\u901A\u8FC7 previsit_query \u7684 entity_search \u6838\u5B9E\u4E3B\u4F53\uFF0C\u4F7F\u7528 previsit_confirm_entity \u7ED1\u5B9A\u540E\u7EE7\u7EED\u67E5\u8BE2\u3002";
|
|
240
|
+
}
|
|
237
241
|
function composeFullSentence(selection, company = "") {
|
|
238
242
|
if (!hasSelection(selection) && company.trim() === "") {
|
|
239
243
|
return "";
|
|
@@ -378,6 +382,22 @@ async function createPrevisitSession(ctx) {
|
|
|
378
382
|
return created;
|
|
379
383
|
}
|
|
380
384
|
|
|
385
|
+
// src/report-time.ts
|
|
386
|
+
function formatReportTime(value) {
|
|
387
|
+
if (!value) return "";
|
|
388
|
+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(value)) return value;
|
|
389
|
+
const date = new Date(value);
|
|
390
|
+
if (!Number.isFinite(date.getTime())) return value;
|
|
391
|
+
const parts = new Intl.DateTimeFormat("en-GB", { timeZone: "Asia/Shanghai", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", hourCycle: "h23" }).formatToParts(date);
|
|
392
|
+
const get = (key) => parts.find((part) => part.type === key)?.value ?? "";
|
|
393
|
+
return `${get("year")}-${get("month")}-${get("day")} ${get("hour")}:${get("minute")}:${get("second")}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09`;
|
|
394
|
+
}
|
|
395
|
+
function reportTextWithTime(markdown, completedAt) {
|
|
396
|
+
const stamp = "\u751F\u6210\u65F6\u95F4\uFF1A" + formatReportTime(completedAt);
|
|
397
|
+
const pattern = /生成时间[::]\s*\d{4}-\d{2}-\d{2}(?:[ T]\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?(?:(北京时间))?/g;
|
|
398
|
+
return pattern.test(markdown) ? markdown.replace(pattern, stamp) : stamp + "\n\n" + markdown;
|
|
399
|
+
}
|
|
400
|
+
|
|
381
401
|
// src/report-export.ts
|
|
382
402
|
function contentText(value) {
|
|
383
403
|
if (typeof value === "string") return value;
|
|
@@ -639,11 +659,11 @@ footer{padding:14px 26px 20px;border-top:1px solid var(--line);color:var(--muted
|
|
|
639
659
|
function deriveMeta(md, meta) {
|
|
640
660
|
const first = /^#?\s*(?:访前尽调报告|拜访作战卡)\s*·\s*([^\n锚]+)/m.exec(md);
|
|
641
661
|
const anchorLine = /锚定主体:([^\n]+?)(?:\s*生成时间|$)/m.exec(md);
|
|
642
|
-
const whenLine = /生成时间[::]\s*([
|
|
662
|
+
const whenLine = /生成时间[::]\s*(\d{4}-\d{2}-\d{2}(?:[ T]\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?(?:(北京时间))?)/.exec(md);
|
|
643
663
|
return {
|
|
644
664
|
company: meta?.company || (first?.[1]?.trim() ?? "\u8BBF\u524D\u5C3D\u8C03\u62A5\u544A"),
|
|
645
665
|
anchor: (anchorLine?.[1] ?? "").trim().replace(/[||·\s]+$/, ""),
|
|
646
|
-
when: meta?.generatedAt ||
|
|
666
|
+
when: formatReportTime(meta?.generatedAt || whenLine?.[1])
|
|
647
667
|
};
|
|
648
668
|
}
|
|
649
669
|
function wrapPrevisitReport(bodyHtml, m) {
|
|
@@ -658,6 +678,7 @@ function wrapPrevisitReport(bodyHtml, m) {
|
|
|
658
678
|
</div></body></html>`;
|
|
659
679
|
}
|
|
660
680
|
function buildPrevisitReportHtml(cardMarkdown, meta) {
|
|
681
|
+
if (meta?.generatedAt) cardMarkdown = reportTextWithTime(cardMarkdown, meta.generatedAt);
|
|
661
682
|
const m = deriveMeta(cardMarkdown, meta);
|
|
662
683
|
const body = cardMarkdown.replace(/^#\s*(?:访前尽调报告|拜访作战卡).*$/m, "").replace(/^锚定主体:.*$/m, "").trim();
|
|
663
684
|
return wrapPrevisitReport(renderCardMarkdown(body), m);
|
|
@@ -1160,11 +1181,18 @@ function parseCardInsights(md) {
|
|
|
1160
1181
|
const t = line.trim();
|
|
1161
1182
|
if (!t.startsWith("|")) continue;
|
|
1162
1183
|
const cells = t.split("|").slice(1, -1).map((c) => c.trim());
|
|
1163
|
-
const level = cells[0] ?? "";
|
|
1184
|
+
const level = strip(cells[0] ?? "");
|
|
1164
1185
|
const hit = ["\u7EA2\u7EBF", "\u5173\u6CE8", "\u4FE1\u606F"].find((l) => level.startsWith(l));
|
|
1165
1186
|
if (hit === void 0) continue;
|
|
1166
1187
|
risks.push({ level: hit, text: strip(cells[1] ?? "") });
|
|
1167
1188
|
}
|
|
1189
|
+
for (const line of riskSrc.split("\n")) {
|
|
1190
|
+
if (line.trim().startsWith("|")) continue;
|
|
1191
|
+
const text = strip(line).replace(/^[\s#>\-•]+/, "");
|
|
1192
|
+
const match = /^(红线|关注|信息)(?:事项|风险)?\s*[::]\s*(.+)$/.exec(text);
|
|
1193
|
+
if (match) risks.push({ level: match[1], text: match[2] });
|
|
1194
|
+
else if (/^(?:本次(?:核验|核查|尽调)?(?:范围内)?[,, ]*)?未发现(?:重大)?红线(?:风险|事项)?[。;;]?$/.test(text)) risks.push({ level: "\u7EA2\u7EBF", text: "\u672C\u6B21\u672A\u53D1\u73B0\u7EA2\u7EBF" });
|
|
1195
|
+
}
|
|
1168
1196
|
return {
|
|
1169
1197
|
found: true,
|
|
1170
1198
|
state: stateUndetermined ? null : state,
|
|
@@ -1189,12 +1217,13 @@ function RiskSummary({ task, insights, running }) {
|
|
|
1189
1217
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "qccPwNote", children: published ? "\u5DF2\u53D1\u5E03\u62A5\u544A\u7ED3\u8BBA\uFF1B\u4E0B\u65B9\u4FDD\u7559\u5728\u7EBF\u6838\u9A8C\u8FC7\u7A0B\u4E0E\u4F9D\u636E\u3002" : available ? "\u9636\u6BB5\u6027\u5224\u65AD \xB7 \u968F\u5DF2\u4FDD\u5B58\u5206\u6790\u8BB0\u5F55\u66F4\u65B0\uFF0C\u5C1A\u975E\u6700\u7EC8\u7ED3\u8BBA\u3002" : running ? "\u98CE\u9669\u5206\u6790\u4E2D\uFF0C\u6682\u672A\u5F62\u6210\u5206\u7EA7\uFF1B\u4E0D\u4EE5\u67E5\u8BE2\u5B8C\u6210\u63A8\u65AD\u65E0\u98CE\u9669\u3002" : "\u5C1A\u672A\u53D6\u5F97\u98CE\u9669\u5206\u7EA7\uFF0C\u4E0D\u4EE3\u8868\u7EA2\u7EBF\u4E3A\u96F6\u3002" }),
|
|
1190
1218
|
available ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "qccPwRiskTiles", children: ["\u7EA2\u7EBF", "\u5173\u6CE8", "\u4FE1\u606F"].map((level) => {
|
|
1191
1219
|
const reportRows = insights.risks.filter((row) => row.level === level);
|
|
1192
|
-
const
|
|
1193
|
-
const
|
|
1220
|
+
const unresolved = reportRows.some((row) => /^(?:证据不足|结论未明确|尚未形成|待核验|未完成核验)/.test(row.text));
|
|
1221
|
+
const items = published ? reportRows.filter((row) => isRiskFindingText(row.text) && !/^(?:证据不足|结论未明确|尚未形成|待核验|未完成核验)/.test(row.text)).map((row) => ({ id: row.text, title: "", text: row.text, time: "", refs: [] })) : live.filter((row) => row.riskLevel === level).map((row) => ({ id: row.id, title: row.title, text: row.summary, time: row.updatedAt, refs: row.evidenceIds }));
|
|
1222
|
+
const noFinding = published && !unresolved && !items.length && reportRows.some((row) => /^(?:无[。;;]?|本次.*未发现.*|未发现.*|暂无(?:红线|该级别事项).*)$/.test(row.text.trim()));
|
|
1194
1223
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "qccPwRiskTile", "data-level": level, "data-clear": noFinding, "data-empty": !items.length, children: [
|
|
1195
1224
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "qccPwRiskTileTop", children: [
|
|
1196
1225
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: level === "\u7EA2\u7EBF" ? "\u7EA2\u7EBF \xB7 \u4F18\u5148\u6838\u5B9E" : level }),
|
|
1197
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: items.length ? items.length : noFinding ?
|
|
1226
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: items.length ? items.length : noFinding ? `\u672C\u6B21\u6838\u9A8C\u672A\u53D1\u73B0${level === "\u7EA2\u7EBF" ? "\u7EA2\u7EBF" : "\u8BE5\u7EA7\u522B\u4E8B\u9879"}` : running && !task?.reportReady ? "\u5206\u6790\u4E2D" : "\u7ED3\u8BBA\u672A\u660E\u786E" })
|
|
1198
1227
|
] }),
|
|
1199
1228
|
items.map((row) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
1200
1229
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { children: row.title }),
|
|
@@ -1208,7 +1237,7 @@ function RiskSummary({ task, insights, running }) {
|
|
|
1208
1237
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { children: row.refs.map((ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { children: ref }, ref)) })
|
|
1209
1238
|
] }) : null
|
|
1210
1239
|
] }, row.id)),
|
|
1211
|
-
!items.length ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: noFinding ? "\
|
|
1240
|
+
!items.length ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: noFinding ? "\u672C\u6B21\u62A5\u544A\u660E\u786E\u672A\u53D1\u73B0\u8BE5\u7EA7\u522B\u4E8B\u9879\uFF1B\u4EC5\u9650\u5DF2\u6838\u9A8C\u8303\u56F4\uFF0C\u4E0D\u4EE3\u8868\u98CE\u9669\u7EDD\u5BF9\u4E0D\u5B58\u5728\u3002" : running && !task?.reportReady ? "\u6B63\u5728\u6574\u7406\u8BE5\u7EA7\u522B\u7ED3\u8BBA\u3002" : "\u62A5\u544A\u672A\u63D0\u4F9B\u53EF\u8BC6\u522B\u7684\u8BE5\u7EA7\u522B\u7ED3\u8BBA\uFF0C\u53EF\u67E5\u770B\u539F\u62A5\u544A\u6216\u7EE7\u7EED\u8865\u5145\u6838\u9A8C\uFF1B\u4E0D\u4EE3\u8868\u4EFB\u52A1\u4ECD\u5728\u8FD0\u884C\uFF0C\u4E5F\u4E0D\u80FD\u89E3\u91CA\u4E3A\u96F6\u98CE\u9669\u3002" }) : null
|
|
1212
1241
|
] }, level);
|
|
1213
1242
|
}) }) : null
|
|
1214
1243
|
] });
|
|
@@ -1368,6 +1397,8 @@ function selectHostedTask(records, active, dismissedTaskIds) {
|
|
|
1368
1397
|
}
|
|
1369
1398
|
function hostedTaskView(task) {
|
|
1370
1399
|
if (task.reportReady || HOSTED_TERMINAL.has(task.state)) return "output";
|
|
1400
|
+
if (!task.entity || task.state === "needs-entity-confirmation" || task.state === "needs-entity-search") return "target";
|
|
1401
|
+
if (!task.runs.some((run) => run.dimension !== "entity_search")) return "scope";
|
|
1371
1402
|
if (task.stage === "output" || task.state === "finalizing") {
|
|
1372
1403
|
const lastRun = [...task.runs].reverse().find((run) => run.dimension !== "entity_search");
|
|
1373
1404
|
if (lastRun !== void 0) return VERIFY_RUN.test(`${lastRun.dimension} ${lastRun.toolName ?? ""}`) ? "verify" : "collect";
|
|
@@ -1416,7 +1447,8 @@ function syncHostedTaskState(state, record, adopted, locateCurrentStage) {
|
|
|
1416
1447
|
};
|
|
1417
1448
|
const nextTask = captureId !== void 0 && captureId !== record.id ? { ...nextTaskBase, captureId } : nextTaskBase;
|
|
1418
1449
|
const nextCompany = record.entity?.fullName ?? record.query;
|
|
1419
|
-
const
|
|
1450
|
+
const desiredView = hostedTaskView(record);
|
|
1451
|
+
const nextView = locateCurrentStage && !record.reportReady && !HOSTED_TERMINAL.has(record.state) && state.view !== "history" ? state.view === "target" && record.entity && (desiredView === "collect" || desiredView === "verify") ? "scope" : desiredView : state.view;
|
|
1420
1452
|
if (previous?.id === nextTask.id && previous.captureId === nextTask.captureId && previous.company === nextTask.company && previous.prompt === nextTask.prompt && previous.createdAt === nextTask.createdAt && previous.nodeBaseline === nextTask.nodeBaseline && previous.seenRunning === nextTask.seenRunning && state.company === nextCompany && state.view === nextView) return state;
|
|
1421
1453
|
return { ...state, task: nextTask, company: nextCompany, view: nextView };
|
|
1422
1454
|
}
|
|
@@ -1571,60 +1603,44 @@ function PrevisitLogo(props) {
|
|
|
1571
1603
|
// src/report-files-panel.tsx
|
|
1572
1604
|
var import_react2 = require("react");
|
|
1573
1605
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1574
|
-
function
|
|
1575
|
-
const
|
|
1576
|
-
const
|
|
1577
|
-
(
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
};
|
|
1595
|
-
}, [url]);
|
|
1606
|
+
async function downloadReportFormat(taskId, sessionId, format) {
|
|
1607
|
+
const root = `/previsit/api/tasks/${encodeURIComponent(taskId)}/files`;
|
|
1608
|
+
const query = `?sessionId=${encodeURIComponent(sessionId)}`;
|
|
1609
|
+
const response = await fetch(root + query, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ format }) });
|
|
1610
|
+
const result = await response.json();
|
|
1611
|
+
if (!response.ok || !result.ok || !result.file?.id || !result.file.fileName) throw new Error(result.message ?? "\u62A5\u544A\u751F\u6210\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
|
|
1612
|
+
const file = await fetch(`${root}/${encodeURIComponent(result.file.id)}${query}`);
|
|
1613
|
+
if (!file.ok) throw new Error("\u62A5\u544A\u5DF2\u751F\u6210\uFF0C\u4F46\u4E0B\u8F7D\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
|
|
1614
|
+
const url = URL.createObjectURL(await file.blob());
|
|
1615
|
+
const anchor = document.createElement("a");
|
|
1616
|
+
anchor.href = url;
|
|
1617
|
+
anchor.download = result.file.fileName;
|
|
1618
|
+
document.body.append(anchor);
|
|
1619
|
+
anchor.click();
|
|
1620
|
+
anchor.remove();
|
|
1621
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
1622
|
+
}
|
|
1623
|
+
function ReportFilesPanel({ taskId, sessionId, onHtml }) {
|
|
1624
|
+
const [busy, setBusy] = (0, import_react2.useState)(null);
|
|
1625
|
+
const [error, setError] = (0, import_react2.useState)("");
|
|
1596
1626
|
const generate = async (format) => {
|
|
1597
|
-
|
|
1627
|
+
if (busy) return;
|
|
1628
|
+
setBusy(format);
|
|
1598
1629
|
setError("");
|
|
1599
1630
|
try {
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
setFiles((previous) => [...previous.filter((f) => f.id !== body.file.id), body.file]);
|
|
1604
|
-
} catch (e) {
|
|
1605
|
-
setError(e instanceof Error ? e.message : String(e));
|
|
1631
|
+
await downloadReportFormat(taskId, sessionId, format);
|
|
1632
|
+
} catch (cause) {
|
|
1633
|
+
setError(cause instanceof Error ? cause.message : "\u4E0B\u8F7D\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
|
|
1606
1634
|
} finally {
|
|
1607
|
-
setBusy(
|
|
1635
|
+
setBusy(null);
|
|
1608
1636
|
}
|
|
1609
1637
|
};
|
|
1610
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("
|
|
1611
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("
|
|
1612
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("
|
|
1613
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "
|
|
1614
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("
|
|
1615
|
-
|
|
1616
|
-
error ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { role: "alert", children: error }) : null,
|
|
1617
|
-
files.map((file) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { children: [
|
|
1618
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("a", { href: `/previsit/api/tasks/${encodeURIComponent(taskId)}/files/${file.id}?sessionId=${encodeURIComponent(sessionId)}`, download: file.fileName, children: file.fileName }),
|
|
1619
|
-
" \xB7 ",
|
|
1620
|
-
file.size,
|
|
1621
|
-
" \u5B57\u8282"
|
|
1622
|
-
] }, file.id)),
|
|
1623
|
-
deliveries.map((d) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { children: [
|
|
1624
|
-
"\u4EA4\u4ED8\u7533\u8BF7\uFF1A",
|
|
1625
|
-
d.destination,
|
|
1626
|
-
" \xB7 \u5F85\u914D\u7F6E\uFF0C\u5C1A\u672A\u4E0A\u4F20"
|
|
1627
|
-
] }, d.id))
|
|
1638
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "qccPwDownloadActions", "aria-label": "\u4E0B\u8F7D\u62A5\u544A", children: [
|
|
1639
|
+
onHtml ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "qccPwSecondary", disabled: busy !== null, onClick: onHtml, children: "\u4E0B\u8F7D HTML" }) : null,
|
|
1640
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "qccPwSecondary", disabled: busy !== null, onClick: () => void generate("docx"), children: busy === "docx" ? "\u6B63\u5728\u751F\u6210 Word\u2026" : "\u4E0B\u8F7D Word" }),
|
|
1641
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "qccPwPrimary", disabled: busy !== null, onClick: () => void generate("pdf"), children: busy === "pdf" ? "\u6B63\u5728\u751F\u6210 PDF\u2026" : "\u4E0B\u8F7D PDF" }),
|
|
1642
|
+
busy ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { role: "status", children: "\u751F\u6210\u5B8C\u6210\u540E\u81EA\u52A8\u4E0B\u8F7D" }) : null,
|
|
1643
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { role: "alert", children: error }) : null
|
|
1628
1644
|
] });
|
|
1629
1645
|
}
|
|
1630
1646
|
|
|
@@ -1941,7 +1957,7 @@ function usePrevisitComposer(args) {
|
|
|
1941
1957
|
setError(void 0);
|
|
1942
1958
|
};
|
|
1943
1959
|
const startTask = async () => {
|
|
1944
|
-
if (inFlight.current) return;
|
|
1960
|
+
if (inFlight.current || args.blocked || isolated && store.get(sessionId).task !== void 0) return;
|
|
1945
1961
|
const lifetimeToken = lifetime.current;
|
|
1946
1962
|
const current = store.get(sessionId);
|
|
1947
1963
|
const text = (isolated ? composeFullSentence(current.selection, current.company) : args.readDraft()).trim();
|
|
@@ -1987,6 +2003,12 @@ function PrevisitFields(props) {
|
|
|
1987
2003
|
}));
|
|
1988
2004
|
const { actions: a } = props;
|
|
1989
2005
|
const st = a.state;
|
|
2006
|
+
if (props.locked) return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "qccDockBody", role: "status", children: [
|
|
2007
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { children: "\u4EFB\u52A1\u5DF2\u63D0\u4EA4" }),
|
|
2008
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: "\u8BF7\u67E5\u770B\u4EFB\u52A1\u8FDB\u5C55\uFF1B\u5982\u9700\u786E\u8BA4\u4E3B\u4F53\uFF0C\u8BF7\u5728\u5BF9\u8BDD\u4E2D\u9009\u62E9\u3002\u65E0\u9700\u91CD\u590D\u63D0\u4EA4\u3002" }),
|
|
2009
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: st.task?.company ?? st.company }),
|
|
2010
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: "\u672C\u6B21\u8BBE\u7F6E\u4EE5\u5DF2\u53D1\u9001\u7684\u4EFB\u52A1\u4E3A\u51C6\u3002\u5F00\u59CB\u53E6\u4E00\u9879\u8C03\u67E5\u8BF7\u4F7F\u7528\u300C\u65B0\u7684\u5C3D\u8C03\u300D\u3002" })
|
|
2011
|
+
] });
|
|
1990
2012
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(DropZone, { className: "qccDockBody", onFile: (file) => {
|
|
1991
2013
|
if (props.send !== void 0) void imports.accept(file);
|
|
1992
2014
|
}, children: [
|
|
@@ -2259,7 +2281,7 @@ function PrevisitPromptGenerator(props) {
|
|
|
2259
2281
|
setOpen(false);
|
|
2260
2282
|
window.setTimeout(() => triggerRef.current?.focus(), 0);
|
|
2261
2283
|
};
|
|
2262
|
-
const generated = composeFullSentence(selection, company);
|
|
2284
|
+
const generated = routePrevisitPrompt(composeFullSentence(selection, company));
|
|
2263
2285
|
const commit = (mode) => {
|
|
2264
2286
|
if (props.inputActions === void 0) {
|
|
2265
2287
|
setError("\u5F53\u524D\u4F1A\u8BDD\u8F93\u5165\u6846\u5C1A\u672A\u5C31\u7EEA\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
|
|
@@ -3093,6 +3115,7 @@ html[data-theme="dark"] .qccPrevisitHeroLogo,html.dark .qccPrevisitHeroLogo{colo
|
|
|
3093
3115
|
.qccPwScopeList{display:grid;gap:0;margin:0}.qccPwScopeList>div{display:grid;grid-template-columns:96px 1fr;gap:12px;padding:10px 0;border-bottom:1px solid var(--qcc-border)}.qccPwScopeList>div:last-child{border-bottom:0}.qccPwScopeList dt{color:var(--qcc-secondary);font-size:12px}.qccPwScopeList dd{margin:0;color:var(--qcc-text);font-size:12px}
|
|
3094
3116
|
.qccPwPrompt{max-height:220px;margin:0;padding:11px;border-radius:8px;background:var(--qcc-page);color:var(--qcc-secondary);font:11px/1.6 ui-monospace,SFMono-Regular,Menlo,monospace;white-space:pre-wrap;overflow:auto}
|
|
3095
3117
|
.qccPwFooter{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:11px 16px;border-top:1px solid var(--qcc-border);background:var(--qcc-surface);flex:none}.qccPwFooterHint{min-width:0;color:var(--qcc-secondary);font-size:10px}.qccPwFooterHint[data-tone="error"]{color:var(--qcc-danger)}.qccPwFooterActions{display:flex;gap:8px;flex:none}.qccPwPrimary,.qccPwSecondary{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:34px;padding:0 13px;border:1px solid var(--qcc-border);border-radius:8px;background:var(--qcc-surface);color:var(--qcc-text);font:inherit;font-size:12px;cursor:pointer}.qccPwPrimary{border-color:var(--qcc-action);background:var(--qcc-action);color:var(--qcc-action-text)}.qccPwPrimary:hover{border-color:var(--qcc-action-hover);background:var(--qcc-action-hover)}.qccPwPrimary[aria-disabled="true"]{border-color:var(--qcc-border);background:var(--qcc-page);color:var(--qcc-secondary)}.qccPwPrimary[aria-disabled="true"]:hover{border-color:var(--qcc-border);background:var(--qcc-page);color:var(--qcc-secondary)}.qccPwSecondary:hover{border-color:var(--qcc-brand);background:var(--qcc-selected);color:var(--qcc-action)}
|
|
3118
|
+
.qccPwDownloadActions{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap;min-width:0}.qccPwDownloadActions [role="alert"],.qccPwDownloadActions [role="status"]{flex-basis:100%;font-size:12px;overflow-wrap:anywhere}.qccPwDownloadActions [role="alert"]{color:var(--qcc-danger)}.qccPwFooterActions{flex:1;flex-wrap:wrap;justify-content:space-between;min-width:0}.qccPwFooterHint:empty{display:none}.qccPwFooterActions>.qccPwDownloadActions{margin-left:auto}.qccPwDownloadActions button:disabled{opacity:.6;cursor:wait}
|
|
3096
3119
|
.qccPwReportCard{padding:0;overflow:hidden}.qccPwReportFrame{display:block;width:100%;min-height:480px;border:0;background:#fff}
|
|
3097
3120
|
|
|
3098
3121
|
.qccDockBody{display:grid;gap:12px}.qccDockRow{display:grid;grid-template-columns:68px 1fr;align-items:start;gap:10px}.qccDockLabel{padding-top:7px;color:var(--qcc-secondary);font-size:12px}.qccDockCompany{width:100%;min-height:38px;padding:8px 10px;border:1px solid var(--qcc-border);border-radius:8px;background:var(--qcc-surface);color:var(--qcc-text);font:inherit;font-size:16px;box-sizing:border-box}.qccDockChips{display:flex;flex-wrap:wrap;gap:7px}.qccDockChip{min-height:32px;padding:0 10px;border:1px solid var(--qcc-border);border-radius:8px;background:var(--qcc-surface);color:var(--qcc-secondary);font:inherit;font-size:11px;cursor:pointer}.qccDockChip[data-selected="true"]{border-color:var(--qcc-brand);background:var(--qcc-selected);color:var(--qcc-action)}.qccDockFoot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding-top:4px}.qccDockHint{color:var(--qcc-secondary);font-size:10px;line-height:1.45}.qccDockHint[data-tone="error"]{color:var(--qcc-danger)}.qccDockActions{display:flex;gap:7px;flex:none}.qccDockBtn{min-height:34px;padding:0 11px;border:1px solid var(--qcc-border);border-radius:8px;background:var(--qcc-surface);color:var(--qcc-text);font:inherit;font-size:11px;cursor:pointer}.qccDockPrimary{border-color:var(--qcc-action);background:var(--qcc-action);color:var(--qcc-action-text)}
|
|
@@ -3280,7 +3303,8 @@ function SetupPanel(props) {
|
|
|
3280
3303
|
},
|
|
3281
3304
|
start: props.start,
|
|
3282
3305
|
onStarted: props.onStarted,
|
|
3283
|
-
draftMode: "isolated"
|
|
3306
|
+
draftMode: "isolated",
|
|
3307
|
+
blocked: props.busy || props.task !== void 0
|
|
3284
3308
|
});
|
|
3285
3309
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("section", { className: "qccPwPanel", children: [
|
|
3286
3310
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("header", { className: "qccPwPageHeading", children: [
|
|
@@ -3291,7 +3315,7 @@ function SetupPanel(props) {
|
|
|
3291
3315
|
] }),
|
|
3292
3316
|
props.task === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "qccPwTaskId", children: taskDisplayLabel(props.task.id) })
|
|
3293
3317
|
] }),
|
|
3294
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "qccPwCard qccPwSetupCard", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PrevisitFields, { actions, idPrefix: `qccPw-${props.sessionId}`, sessionId: props.sessionId, send: props.send }) }),
|
|
3318
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "qccPwCard qccPwSetupCard", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PrevisitFields, { actions, locked: props.busy || props.task !== void 0, idPrefix: `qccPw-${props.sessionId}`, sessionId: props.sessionId, send: props.send }) }),
|
|
3295
3319
|
props.task === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "qccPwCard", children: [
|
|
3296
3320
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "qccPwCardHeader", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { children: "\u5DF2\u53D1\u9001\u7684\u4EFB\u52A1" }) }) }),
|
|
3297
3321
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("pre", { className: "qccPwPrompt", children: props.task.prompt })
|
|
@@ -3408,8 +3432,8 @@ function OpportunityPanel(props) {
|
|
|
3408
3432
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ScanFindings, { task: props.hostedTask }),
|
|
3409
3433
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { children: "\u5BA2\u89C2\u6570\u636E\u6458\u8981" }),
|
|
3410
3434
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CollectionCards, { task: props.hostedTask }),
|
|
3411
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("
|
|
3412
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("
|
|
3435
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("section", { className: "qccPwCard", children: [
|
|
3436
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { children: "\u98CE\u9669\u660E\u7EC6\u4E0E\u91C7\u96C6\u8986\u76D6" }),
|
|
3413
3437
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CollectionCards, { task: props.hostedTask, verification: true })
|
|
3414
3438
|
] })
|
|
3415
3439
|
] });
|
|
@@ -3541,7 +3565,7 @@ function HistoryPanel(props) {
|
|
|
3541
3565
|
const item = props.selected;
|
|
3542
3566
|
const status = hostedStatus(item);
|
|
3543
3567
|
const origin = hostedTaskOrigin(item);
|
|
3544
|
-
const reportHtml = selectedReport === void 0 || selectedReport === "" ? null : buildPrevisitReportHtml(selectedReport);
|
|
3568
|
+
const reportHtml = selectedReport === void 0 || selectedReport === "" ? null : buildPrevisitReportHtml(selectedReport, item.completedAt ? { generatedAt: item.completedAt } : void 0);
|
|
3545
3569
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("section", { className: "qccPwPanel", children: [
|
|
3546
3570
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("header", { className: "qccPwPageHeading qccPwHistoryHeading", children: [
|
|
3547
3571
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
|
|
@@ -3605,17 +3629,7 @@ function HistoryPanel(props) {
|
|
|
3605
3629
|
}, children: continuing ? "\u6B63\u5728\u63D0\u4EA4\u2026" : "\u521B\u5EFA\u8865\u5145\u4EFB\u52A1" }),
|
|
3606
3630
|
continueError ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { role: "alert", children: continueError }) : null
|
|
3607
3631
|
] }) : null,
|
|
3608
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "qccPwHistoryActions", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
3609
|
-
"button",
|
|
3610
|
-
{
|
|
3611
|
-
type: "button",
|
|
3612
|
-
className: "qccPwPrimary",
|
|
3613
|
-
disabled: !item.reportReady || !origin.complete || props.downloadingTaskId === item.id,
|
|
3614
|
-
title: !origin.complete ? "\u65E7\u8BB0\u5F55\u7F3A\u5C11\u6765\u6E90 Session\uFF0C\u65E0\u6CD5\u5B89\u5168\u4E0B\u8F7D" : item.reportReady ? "\u4E0B\u8F7D\u5DF2\u4FDD\u5B58\u7684 HTML \u62A5\u544A" : "\u62A5\u544A\u5C1A\u672A\u751F\u6210",
|
|
3615
|
-
onClick: () => props.onDownload(item),
|
|
3616
|
-
children: props.downloadingTaskId === item.id ? "\u6B63\u5728\u4E0B\u8F7D\u2026" : "\u4E0B\u8F7D\u62A5\u544A \u2193"
|
|
3617
|
-
}
|
|
3618
|
-
) })
|
|
3632
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "qccPwHistoryActions", children: item.reportReady && origin.complete ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ReportFilesPanel, { taskId: item.id, sessionId: item.sessionId, onHtml: () => props.onDownload(item) }, item.id) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "\u62A5\u544A\u5C1A\u672A\u751F\u6210\u6216\u8BB0\u5F55\u6765\u6E90\u4E0D\u5B8C\u6574\uFF0C\u6682\u4E0D\u53EF\u4E0B\u8F7D\u3002" }) })
|
|
3619
3633
|
] }),
|
|
3620
3634
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("nav", { className: "qccPwStages", "aria-label": "\u5386\u53F2\u4EFB\u52A1\u9636\u6BB5", role: "tablist", children: PREVISIT_PHASES.map((phase) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", role: "tab", "aria-selected": historyPhase === phase, className: "qccPwStage", "data-selected": historyPhase === phase, onClick: () => setHistoryPhase(phase), children: [
|
|
3621
3635
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "qccPwStageIcon", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, { name: phase }) }),
|
|
@@ -3648,7 +3662,6 @@ function HistoryPanel(props) {
|
|
|
3648
3662
|
historyPhase === "verify" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(RiskPanel, { task: void 0, hostedTask: item, status, events: hostedToolEvents(item), insights: parseCardInsights(selectedReport ?? "") }) : null,
|
|
3649
3663
|
historyPhase !== "output" ? null : reportHtml === null ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Feedback, { tone: "notice", title: item.reportReady ? "\u6B63\u5728\u8BFB\u53D6\u62A5\u544A" : "\u62A5\u544A\u5C1A\u672A\u751F\u6210", children: item.reportReady ? "\u5DF2\u627E\u5230\u62A5\u544A\u5236\u54C1\uFF0C\u4F46\u6B63\u6587\u6682\u672A\u8FD4\u56DE\uFF1B\u8BF7\u8FD4\u56DE\u6E05\u5355\u540E\u91CD\u8BD5\u3002" : "\u4EFB\u52A1\u8BE6\u60C5\u5DF2\u6062\u590D\uFF0C\u62A5\u544A\u751F\u6210\u540E\u53EF\u5728\u8FD9\u91CC\u67E5\u770B\u5E76\u4E0B\u8F7D\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "qccPwCard qccPwReportCard", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ReportViewer, { html: reportHtml }) }),
|
|
3650
3664
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MaterialPanel, { task: item }),
|
|
3651
|
-
item.reportReady && origin.complete ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ReportFilesPanel, { taskId: item.id, sessionId: item.sessionId }, item.id) : null,
|
|
3652
3665
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "qccPwNote", children: "\u5386\u53F2\u8BE6\u60C5\u53EA\u8BFB\u53D6\u8BE5\u4EFB\u52A1\u6240\u5C5E Session \u7684 Host \u5236\u54C1\uFF0C\u4E0D\u4F1A\u91CD\u65B0\u8C03\u7528\u4F01\u67E5\u67E5\uFF0C\u4E5F\u4E0D\u4F1A\u8986\u76D6\u5F53\u524D\u4F1A\u8BDD\u4EFB\u52A1\u3002" })
|
|
3653
3666
|
] });
|
|
3654
3667
|
}
|
|
@@ -3718,7 +3731,12 @@ function PrevisitWorkbenchTab(props) {
|
|
|
3718
3731
|
const setView = (view) => {
|
|
3719
3732
|
locatePrevisitView(props.shared, sessionId, view);
|
|
3720
3733
|
};
|
|
3721
|
-
const
|
|
3734
|
+
const manualNavigation = (0, import_react9.useRef)(false);
|
|
3735
|
+
const navigationTask = (0, import_react9.useRef)();
|
|
3736
|
+
const setPhase = (next) => {
|
|
3737
|
+
manualNavigation.current = true;
|
|
3738
|
+
setView(next);
|
|
3739
|
+
};
|
|
3722
3740
|
const [runtime, setRuntime] = (0, import_react9.useState)(EMPTY_RUNTIME);
|
|
3723
3741
|
const [plans, setPlans] = (0, import_react9.useState)([]);
|
|
3724
3742
|
const [dismissedPlanIds, setDismissedPlanIds] = (0, import_react9.useState)([]);
|
|
@@ -3758,14 +3776,16 @@ function PrevisitWorkbenchTab(props) {
|
|
|
3758
3776
|
setHostedTask(record);
|
|
3759
3777
|
setHostError(void 0);
|
|
3760
3778
|
if (record !== null) {
|
|
3779
|
+
if (navigationTask.current !== void 0 && navigationTask.current !== record.id) manualNavigation.current = false;
|
|
3780
|
+
navigationTask.current = record.id;
|
|
3761
3781
|
const snapshot = props.ctx.sessions.binding?.(sessionId)?.session.getSnapshot();
|
|
3762
3782
|
const adopted = snapshot === void 0 ? null : adoptTaskFromSnapshot(snapshot, sessionId, current.minimumNodeBaseline);
|
|
3763
3783
|
const desiredView = hostedTaskView(record);
|
|
3764
3784
|
const location = `${record.id}:${desiredView}`;
|
|
3765
3785
|
const awaitingPlan = snapshot === void 0 ? void 0 : derivePlans(snapshot).find((plan) => plan.taskIds.length === 0 && !dismissedPlanIds.includes(plan.id) && !records.some((task2) => task2.planId === plan.id));
|
|
3766
|
-
const shouldLocate = awaitingPlan === void 0 && lastHostedLocation.current !== location;
|
|
3786
|
+
const shouldLocate = !manualNavigation.current && awaitingPlan === void 0 && lastHostedLocation.current !== location;
|
|
3767
3787
|
props.shared.update(sessionId, (state) => syncHostedTaskState(state, record, adopted, shouldLocate));
|
|
3768
|
-
lastHostedLocation.current =
|
|
3788
|
+
lastHostedLocation.current = `${record.id}:${props.shared.get(sessionId).view}`;
|
|
3769
3789
|
} else {
|
|
3770
3790
|
lastHostedLocation.current = void 0;
|
|
3771
3791
|
}
|
|
@@ -3889,8 +3909,9 @@ function PrevisitWorkbenchTab(props) {
|
|
|
3889
3909
|
const status = hostStatus ?? deriveWorkbenchStatus(progressInput);
|
|
3890
3910
|
const phaseStates = derivePhaseStates(progressInput);
|
|
3891
3911
|
const insights = (0, import_react9.useMemo)(() => parseCardInsights(cardText), [cardText]);
|
|
3892
|
-
const reportHtml = (0, import_react9.useMemo)(() => cardText === null ? null : buildPrevisitReportHtml(cardText), [cardText]);
|
|
3912
|
+
const reportHtml = (0, import_react9.useMemo)(() => cardText === null ? null : buildPrevisitReportHtml(cardText, hostedTask?.completedAt ? { generatedAt: hostedTask.completedAt } : void 0), [cardText, hostedTask?.completedAt]);
|
|
3893
3913
|
const newTask = () => {
|
|
3914
|
+
manualNavigation.current = false;
|
|
3894
3915
|
props.shared.update(sessionId, (state) => ({
|
|
3895
3916
|
...state,
|
|
3896
3917
|
task: void 0,
|
|
@@ -4024,15 +4045,17 @@ function PrevisitWorkbenchTab(props) {
|
|
|
4024
4045
|
] }) : null,
|
|
4025
4046
|
shared.view !== "history" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ExecutionProgress, { task: hostedTask, status, modelRunning: runtime.available ? runtime.running : void 0, syncError: hostError ?? runtime.lastAgentError ?? void 0 }) : null,
|
|
4026
4047
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "qccPwBody", children: [
|
|
4027
|
-
shared.view === "output" && hostedTask?.reportReady ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ReportFilesPanel, { taskId: hostedTask.id, sessionId: hostedTask.sessionId }, hostedTask.id) : null,
|
|
4028
4048
|
shared.view !== "history" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MaterialPanel, { task: hostedTask }) : null,
|
|
4029
4049
|
shared.view === "target" && pendingPlan !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PlanCard, { plan: pendingPlan, onConfirm: async (message) => {
|
|
4030
4050
|
await props.startPrompt(sessionId, message, true);
|
|
4031
4051
|
setDismissedPlanIds((ids) => [...ids, pendingPlan.id]);
|
|
4032
4052
|
}, onDismiss: () => setDismissedPlanIds((ids) => [...ids, pendingPlan.id]) }, pendingPlan.id) : null,
|
|
4033
|
-
shared.view === "target" && pendingPlan === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SetupPanel, { sessionId, store: props.shared, task, input: resolveSessionInput(props.ctx, sessionId), send: async (prompt) => {
|
|
4053
|
+
shared.view === "target" && pendingPlan === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SetupPanel, { busy: runtime.running, sessionId, store: props.shared, task, input: resolveSessionInput(props.ctx, sessionId), send: async (prompt) => {
|
|
4034
4054
|
await props.startPrompt(sessionId, prompt, true);
|
|
4035
|
-
}, start: (prompt) => props.startPrompt(sessionId, prompt), onStarted: () =>
|
|
4055
|
+
}, start: (prompt) => props.startPrompt(sessionId, prompt), onStarted: () => {
|
|
4056
|
+
manualNavigation.current = false;
|
|
4057
|
+
setView("target");
|
|
4058
|
+
} }) : null,
|
|
4036
4059
|
shared.view === "scope" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ScopePanel, { state: shared, task, hostedTask }) : null,
|
|
4037
4060
|
shared.view === "collect" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(OpportunityPanel, { task, hostedTask, status, events: effectiveEvents, insights }) : null,
|
|
4038
4061
|
shared.view === "verify" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(RiskPanel, { task, hostedTask, status, events: effectiveEvents, insights }) : null,
|
|
@@ -4057,24 +4080,24 @@ function PrevisitWorkbenchTab(props) {
|
|
|
4057
4080
|
void downloadHistoryReport(record);
|
|
4058
4081
|
},
|
|
4059
4082
|
onContinue: async (record, intent, requestId) => {
|
|
4083
|
+
manualNavigation.current = false;
|
|
4060
4084
|
if (hostedTask && !HOSTED_TERMINAL.has(hostedTask.state)) throw new Error("\u8BF7\u5148\u5B8C\u6210\u5F53\u524D\u4EFB\u52A1");
|
|
4061
4085
|
const prompt = `\u8BF7\u57FA\u4E8E\u5386\u53F2\u62A5\u544A\u521B\u5EFA\u8865\u5145\u5C3D\u8C03\uFF0C\u4E0D\u4FEE\u6539\u539F\u62A5\u544A\u3002\u8C03\u7528 previsit_continue\uFF0CparentTaskId=${record.id}\uFF0CrequestId=${requestId}\uFF0Cintent=${JSON.stringify(intent)}\u3002\u6CBF\u7528\u5DF2\u786E\u8BA4\u4E3B\u4F53\uFF0C\u6309\u8FD4\u56DE\u57FA\u7840\u62A5\u544A\u8865\u5145\u5DF2\u652F\u6301\u7EF4\u5EA6\uFF1B\u65E7\u8BC1\u636E\u4FDD\u7559\u539F\u65E5\u671F\u3002\u4F7F\u7528\u65B0\u4EFB\u52A1ID\u5B8C\u6210 previsit_finalize\uFF0C\u751F\u6210\u5B8C\u6574\u66F4\u65B0\u62A5\u544A\u3002`;
|
|
4062
4086
|
const baseline = await props.startPrompt(sessionId, prompt);
|
|
4063
|
-
props.shared.update(sessionId, (state) => ({ ...state, view: "
|
|
4087
|
+
props.shared.update(sessionId, (state) => ({ ...state, view: "scope", dismissedTaskIds: [.../* @__PURE__ */ new Set([...state.dismissedTaskIds, ...state.task ? [state.task.id] : [], ...hostedTask ? [hostedTask.id] : []])], task: { id: requestId, prompt, company: record.entity?.fullName ?? record.query, createdAt: (/* @__PURE__ */ new Date()).toISOString(), nodeBaseline: baseline, seenRunning: false, selection: state.selection } }));
|
|
4064
4088
|
}
|
|
4065
4089
|
}
|
|
4066
4090
|
) : null
|
|
4067
4091
|
] }),
|
|
4068
4092
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("footer", { className: "qccPwFooter", children: [
|
|
4069
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "qccPwFooterHint", "data-tone": downloadNote === void 0 && hostError === void 0 ? void 0 : "error", children: downloadNote ?? hostError ?? "
|
|
4093
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "qccPwFooterHint", "data-tone": downloadNote === void 0 && hostError === void 0 ? void 0 : "error", children: downloadNote ?? hostError ?? "" }),
|
|
4070
4094
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "qccPwFooterActions", children: [
|
|
4071
4095
|
shared.view !== "target" && task !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "qccPwSecondary", onClick: newTask, children: "\u65B0\u7684\u5C3D\u8C03" }) : null,
|
|
4072
|
-
shared.view === "output" ? /* @__PURE__ */ (0, import_jsx_runtime12.
|
|
4096
|
+
shared.view === "output" && hostedTask?.reportReady ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ReportFilesPanel, { taskId: hostedTask.id, sessionId: hostedTask.sessionId, onHtml: () => {
|
|
4073
4097
|
void downloadReport();
|
|
4074
|
-
},
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
] }) : null
|
|
4098
|
+
} }, hostedTask.id) : shared.view === "output" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "qccPwSecondary", "aria-disabled": status !== "ready" || cardText === null, onClick: () => {
|
|
4099
|
+
void downloadReport();
|
|
4100
|
+
}, children: "\u4E0B\u8F7D HTML" }) : null
|
|
4078
4101
|
] })
|
|
4079
4102
|
] })
|
|
4080
4103
|
] });
|
|
@@ -4121,7 +4144,7 @@ function apply(ctx) {
|
|
|
4121
4144
|
};
|
|
4122
4145
|
const submissions = installSubmissionReveal(ctx, (sessionId) => {
|
|
4123
4146
|
try {
|
|
4124
|
-
openForSession(sessionId, "
|
|
4147
|
+
openForSession(sessionId, "target");
|
|
4125
4148
|
} catch (cause) {
|
|
4126
4149
|
unavailableNotice?.remove();
|
|
4127
4150
|
const notice = document.createElement("div");
|