dsh-pre-duediligence 0.1.30 → 0.1.31
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 +5 -0
- package/README.md +5 -5
- package/THIRD_PARTY_NOTICES.md +5 -0
- package/assets/fonts/LICENSE.txt +92 -0
- package/assets/fonts/NotoSansCJKsc-Regular.otf +0 -0
- package/assets/fonts/README.md +12 -0
- package/docs/RELEASE-0.1.31.md +9 -0
- package/docs/REPORT-FILES-PHASE3.md +1 -1
- package/install.sh +1 -1
- package/lib/client.js +11 -3
- package/lib/index.js +59 -9
- package/lib/index.js.map +4 -4
- package/lib/types/previsit-workflow.d.ts +6 -0
- package/lib/types/processing-status.d.ts +7 -0
- package/lib/types/tool-json.d.ts +2 -0
- package/package.json +2 -1
- package/skills/qcc-previsit-onepager/SKILL.md +9 -3
package/CHANGELOG.md
CHANGED
|
@@ -187,3 +187,8 @@
|
|
|
187
187
|
- 补齐中文产品首页、四阶段流程及局部可恢复标题,仅在访前尽调空白会话展示。
|
|
188
188
|
- 侧栏展开请求支持新会话挂载前排队,修复连续切换时右侧工作台不显示。
|
|
189
189
|
- 增加入口失败、会话归属、标题恢复和导航提交顺序回归测试。
|
|
190
|
+
# 0.1.31
|
|
191
|
+
|
|
192
|
+
- 修复 finalize/history 可选字段导致的 lossless JSON 错误,保留版本不可覆盖及相同正文幂等重试。
|
|
193
|
+
- 增加可追踪的公开工作阶段、嵌套经营资料摘要与明确的数据/摘要状态。
|
|
194
|
+
- 内置开源中文字体,PDF默认无需配置环境变量、无需联网。
|
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# dsh-pre-duediligence
|
|
2
2
|
|
|
3
|
-
0.1.
|
|
3
|
+
0.1.31 修复报告保存/历史读取的 JSON 返回错误,新增用户可见工作阶段与嵌套数据摘要;PDF 内置开源中文字体,可离线导出,无需默认配置环境变量。保留继续尽调、报告版本、补充材料、交叉核验、图片导入和多企业计划。导出为保留Markdown标记的正文版,真实上传尚未接通。详见 [发布说明](docs/RELEASE-0.1.31.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.31
|
|
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.31`:提供左侧智能体入口、原生会话首页、提示词生成器、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.31 --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.31`;仅在显式启用或原本已安装时看到 Sidebar。重启 DSH 并连接“企查查·企业工商”后,在左侧菜单点击“访前尽调”进入初始原生会话。无 Sidebar 时可继续生成提示词、回填/发送草稿并在会话中阅读报告;五项流程按钮会提示安装可选工作台。安装 Sidebar 时,右侧工作台初始仍关闭;点击业务按钮可主动打开,正式发送任务被接纳后会自动展开一次。仅回填不会展开,收起后轮询不会重新打开。其右侧“+”菜单不会列出该入口,普通会话保持 DSH 原样。
|
|
140
140
|
|
|
141
141
|
### 无 Sidebar 功能边界
|
|
142
142
|
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -21,3 +21,8 @@ Copyright (c) 2026 dsh-data-cleaning-agent plugin contributors
|
|
|
21
21
|
https://github.com/duhu2000/dsh-data-cleaning-agent
|
|
22
22
|
|
|
23
23
|
No source code from that project is bundled in this package.
|
|
24
|
+
# Noto Sans CJK SC
|
|
25
|
+
|
|
26
|
+
The unmodified Noto Sans CJK SC Regular font (© 2014-2021 Adobe) is distributed
|
|
27
|
+
under SIL Open Font License 1.1, not the package's MIT license.
|
|
28
|
+
See assets/fonts/LICENSE.txt and assets/fonts/README.md.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
This Font Software is licensed under the SIL Open Font License,
|
|
2
|
+
Version 1.1.
|
|
3
|
+
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
-----------------------------------------------------------
|
|
8
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
9
|
+
-----------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
PREAMBLE
|
|
12
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
13
|
+
development of collaborative font projects, to support the font
|
|
14
|
+
creation efforts of academic and linguistic communities, and to
|
|
15
|
+
provide a free and open framework in which fonts may be shared and
|
|
16
|
+
improved in partnership with others.
|
|
17
|
+
|
|
18
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
19
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
20
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
21
|
+
redistributed and/or sold with any software provided that any reserved
|
|
22
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
23
|
+
however, cannot be released under any other type of license. The
|
|
24
|
+
requirement for fonts to remain under this license does not apply to
|
|
25
|
+
any document created using the fonts or their derivatives.
|
|
26
|
+
|
|
27
|
+
DEFINITIONS
|
|
28
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
29
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
30
|
+
include source files, build scripts and documentation.
|
|
31
|
+
|
|
32
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
33
|
+
copyright statement(s).
|
|
34
|
+
|
|
35
|
+
"Original Version" refers to the collection of Font Software
|
|
36
|
+
components as distributed by the Copyright Holder(s).
|
|
37
|
+
|
|
38
|
+
"Modified Version" refers to any derivative made by adding to,
|
|
39
|
+
deleting, or substituting -- in part or in whole -- any of the
|
|
40
|
+
components of the Original Version, by changing formats or by porting
|
|
41
|
+
the Font Software to a new environment.
|
|
42
|
+
|
|
43
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
44
|
+
writer or other person who contributed to the Font Software.
|
|
45
|
+
|
|
46
|
+
PERMISSION & CONDITIONS
|
|
47
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
48
|
+
a copy of the Font Software, to use, study, copy, merge, embed,
|
|
49
|
+
modify, redistribute, and sell modified and unmodified copies of the
|
|
50
|
+
Font Software, subject to the following conditions:
|
|
51
|
+
|
|
52
|
+
1) Neither the Font Software nor any of its individual components, in
|
|
53
|
+
Original or Modified Versions, may be sold by itself.
|
|
54
|
+
|
|
55
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
56
|
+
redistributed and/or sold with any software, provided that each copy
|
|
57
|
+
contains the above copyright notice and this license. These can be
|
|
58
|
+
included either as stand-alone text files, human-readable headers or
|
|
59
|
+
in the appropriate machine-readable metadata fields within text or
|
|
60
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
61
|
+
|
|
62
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
63
|
+
Name(s) unless explicit written permission is granted by the
|
|
64
|
+
corresponding Copyright Holder. This restriction only applies to the
|
|
65
|
+
primary font name as presented to the users.
|
|
66
|
+
|
|
67
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
68
|
+
Software shall not be used to promote, endorse or advertise any
|
|
69
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
70
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
71
|
+
permission.
|
|
72
|
+
|
|
73
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
74
|
+
must be distributed entirely under this license, and must not be
|
|
75
|
+
distributed under any other license. The requirement for fonts to
|
|
76
|
+
remain under this license does not apply to any document created using
|
|
77
|
+
the Font Software.
|
|
78
|
+
|
|
79
|
+
TERMINATION
|
|
80
|
+
This license becomes null and void if any of the above conditions are
|
|
81
|
+
not met.
|
|
82
|
+
|
|
83
|
+
DISCLAIMER
|
|
84
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
85
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
86
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
87
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
88
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
89
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
90
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
91
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
92
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
Binary file
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Bundled Chinese PDF font
|
|
2
|
+
|
|
3
|
+
Noto Sans CJK SC Regular, version 2.004. Unmodified OpenType font.
|
|
4
|
+
Copyright © 2014-2021 Adobe (http://www.adobe.com/).
|
|
5
|
+
Licensed under SIL Open Font License 1.1; see LICENSE.txt.
|
|
6
|
+
|
|
7
|
+
Upstream: https://github.com/notofonts/noto-cjk/tree/main/Sans/OTF/SimplifiedChinese
|
|
8
|
+
Upstream Git blob SHA-1: dc15562470b4f842321894787a0d066879ccff8b
|
|
9
|
+
Size: 16437364 bytes. The downloaded asset was matched to GitHub's blob identity.
|
|
10
|
+
|
|
11
|
+
The package includes this font for offline PDF export; it is not loaded into the
|
|
12
|
+
browser bundle. No runtime font download or OS font configuration is required.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# 0.1.31 — 工具结果可靠性与可见进展
|
|
2
|
+
|
|
3
|
+
- 工具出口递归省略可选对象字段,拒绝JSON会静默破坏的非JSON值;工具测试在宿主边界执行严格往返校验。
|
|
4
|
+
- 首版 finalize / history 不再返回 undefined;相同正文重试幂等,不同正文仍不得覆盖已发布版本。遇到不确定保存结果应先读取历史确认,补充内容创建新版本。
|
|
5
|
+
- 新增 previsit_progress,保存用户可见的整理事实、风险核对、撰写报告工作摘要。查询事件、停止和同步失败优先;旧摘要标明时间,不展示内部思考或虚构百分比。单次长模型生成不能保证定时更新。
|
|
6
|
+
- 有界提取嵌套中文业务字段、记录计数和数组样本,支持JSON文本包装,排除凭据字段;查询状态与摘要缺失分开展示。老任务仅保存了旧摘要时不补造数据。
|
|
7
|
+
- PDF随包内置未修改的 Noto Sans CJK SC Regular 2.004(SIL OFL 1.1),默认离线导出。保留字体环境变量覆盖;只生成正文版,不声称复刻HTML排版。字体使安装包增大约16MB。
|
|
8
|
+
|
|
9
|
+
验收:类型检查、单元/集成回归、构建及发布流水线浏览器门禁。合成中文PDF已生成、提取文本并逐页检查。本机无头Chrome启动退出,浏览器回归以GitHub Linux门禁结果为准;未调用真实企业MCP或改写用户历史报告。
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
使用 docx 生成 OOXML ZIP、PDFKit 生成 PDF,不把 HTML 改后缀冒充 Word/PDF。当前为保真文本版:正文和 Markdown 标记原样保留,非 HTML 富排版复刻,不载入远程图片、链接资源或脚本。文件含主体、版本、任务标识;采用 Letter 纵向页面和自动换行。
|
|
8
8
|
|
|
9
|
-
PDF
|
|
9
|
+
自0.1.31起,PDF默认使用随包分发的 Noto Sans CJK SC Regular(SIL OFL 1.1),无需联网或管理员配置。可选 `DSH_PREVISIT_PDF_FONT` 指定具有中文覆盖的独立 TTF/OTF;显式配置不可读或字体无效时仍报错,不伪造成功记录。覆盖字体的授权及特殊字符显示需部署方确认。以下验收记录为三期初版历史记录。
|
|
10
10
|
|
|
11
11
|
Word 默认指定 Arial Unicode MS,可用 `DSH_PREVISIT_DOCX_FONT` 更换为部署环境已安装的中文字体家族;DOCX 不嵌入字体,打开端须有该字体或有效替代字体。当前导出不是富排版PDF/Word模板引擎。
|
|
12
12
|
|
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.31}"
|
|
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
|
@@ -1183,6 +1183,11 @@ function processingStatus(input) {
|
|
|
1183
1183
|
if (input.waiting) return { busy: false, title: "\u7B49\u5F85\u4E3B\u4F53\u786E\u8BA4 / \u7EE7\u7EED", detail: "\u8BF7\u5728\u4F1A\u8BDD\u4E2D\u786E\u8BA4\u4F01\u4E1A\u540E\u7EE7\u7EED\u3002" };
|
|
1184
1184
|
if (input.running === false) return { busy: false, title: "\u672C\u8F6E\u4F1A\u8BDD\u5DF2\u505C\u6B62\uFF0C\u4EFB\u52A1\u5C1A\u672A\u5B8C\u6210", detail: "\u8BF7\u67E5\u770B\u4F1A\u8BDD\u4E2D\u7684\u786E\u8BA4\u8BF7\u6C42\u3001\u505C\u6B62\u6216\u9519\u8BEF\u63D0\u793A\u3002" };
|
|
1185
1185
|
if (input.querying) return { busy: input.running === true, title: input.running === true ? "\u6B63\u5728\u67E5\u8BE2\u8D44\u6599" : "\u67E5\u8BE2\u7ED3\u679C\u5F85\u66F4\u65B0", detail: "\u5C1A\u672A\u6536\u5230\u8BE5\u67E5\u8BE2\u7684\u5B8C\u6210\u8BB0\u5F55\uFF0C\u7ED3\u679C\u8FD4\u56DE\u540E\u81EA\u52A8\u66F4\u65B0\u3002" };
|
|
1186
|
+
if (input.activity && Date.parse(input.activity.updatedAt) >= (input.lastResultAt ?? 0)) {
|
|
1187
|
+
const age = Math.max(0, Math.floor(((input.now ?? Date.now()) - Date.parse(input.activity.updatedAt)) / 1e3));
|
|
1188
|
+
const phase = { analysis: "\u6574\u7406\u7ECF\u8425\u4E8B\u5B9E", verification: "\u6838\u5BF9\u98CE\u9669\u4E0E\u8BC1\u636E", writing: "\u64B0\u5199\u5C3D\u8C03\u62A5\u544A" }[input.activity.phase];
|
|
1189
|
+
return { busy: input.running === true, title: `\u6700\u8FD1\u5DE5\u4F5C\u9636\u6BB5\uFF1A${phase}`, detail: `${input.activity.summary}\uFF08${age} \u79D2\u524D\u4E0A\u62A5${age >= 120 ? "\uFF0C\u9636\u6BB5\u4FE1\u606F\u53EF\u80FD\u5DF2\u8FC7\u65F6\uFF1B\u7B49\u5F85\u65B0\u7684\u5DE5\u4F5C\u6458\u8981" : ""}\uFF09\u3002${input.running === void 0 ? "\u5F53\u524D\u8FD0\u884C\u72B6\u6001\u5C1A\u672A\u786E\u8BA4\u3002" : ""}` };
|
|
1190
|
+
}
|
|
1186
1191
|
return { busy: input.running === true, title: input.running === true ? "\u5BBF\u4E3B\u663E\u793A\u4ECD\u5728\u5904\u7406 \xB7 \u6B63\u5728\u5206\u6790 / \u6574\u7406\u6750\u6599" : "\u7B49\u5F85\u4E0B\u4E00\u6B21\u4EFB\u52A1\u66F4\u65B0", detail: `\u8DDD\u6700\u8FD1\u4EFB\u52A1\u7ED3\u679C ${Math.max(0, Math.floor(input.secondsSinceResult))} \u79D2\u3002${input.secondsSinceResult >= 120 ? "\u7B49\u5F85\u65F6\u95F4\u8F83\u957F\uFF0C\u53EF\u68C0\u67E5\u4F1A\u8BDD\u8FD0\u884C\u72B6\u6001\uFF1B\u8BF7\u52FF\u91CD\u590D\u542F\u52A8\u4EFB\u52A1\u3002" : "\u5206\u6790\u9636\u6BB5\u53EF\u80FD\u6682\u65F6\u6CA1\u6709\u65B0\u7684\u5DE5\u5177\u7ED3\u679C\u3002"} \u4E0D\u63D0\u4F9B\u865A\u6784\u767E\u5206\u6BD4\u6216\u5185\u90E8\u601D\u8003\u5185\u5BB9\u3002` };
|
|
1187
1192
|
}
|
|
1188
1193
|
|
|
@@ -3095,7 +3100,8 @@ function ExecutionProgress(props) {
|
|
|
3095
3100
|
}, [props.task?.id, props.status]);
|
|
3096
3101
|
if (props.task === null || props.status === "ready") return null;
|
|
3097
3102
|
const progress = hostedLiveProgress(props.task);
|
|
3098
|
-
const
|
|
3103
|
+
const lastResultAt = Math.max(Date.parse(props.task.createdAt), ...props.task.runs.map((run) => Date.parse(run.completedAt ?? run.startedAt)));
|
|
3104
|
+
const activity = processingStatus({ running: props.modelRunning, error: props.syncError, waiting: props.task.state === "needs-entity-confirmation", querying: progress.current !== null, secondsSinceResult: (now - lastResultAt) / 1e3, ...props.task.activity ? { activity: props.task.activity } : {}, now, lastResultAt });
|
|
3099
3105
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "qccPwLiveProgress", "data-running": activity.busy, children: [
|
|
3100
3106
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "qccPwLivePulse", "aria-hidden": "true" }),
|
|
3101
3107
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "qccPwLiveCopy", role: "status", children: [
|
|
@@ -3304,10 +3310,12 @@ function OpportunityPanel(props) {
|
|
|
3304
3310
|
] }) : insights.stateUndetermined ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "qccPwMode", "data-tone": "review", children: "\u72B6\u6001\u672A\u5B9A" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "qccPwMode", "data-tone": running2 ? void 0 : "neutral", children: running2 ? "\u6B63\u5728\u7814\u5224" : finished ? "\u672A\u8BC6\u522B\u7ED3\u8BBA" : "\u5F85\u7814\u5224" })
|
|
3305
3311
|
] }),
|
|
3306
3312
|
insights.state ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { children: insights.state }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { children: "\u5C1A\u672A\u5F62\u6210\u7ECF\u8425\u7814\u5224\uFF1B\u5148\u5C55\u793A\u5DF2\u8FD4\u56DE\u7684\u7ECF\u8425\u4E8B\u5B9E\uFF0C\u4E0D\u4EE5\u67E5\u8BE2\u6210\u529F\u63A8\u65AD\u7ECF\u8425\u826F\u597D\u3002" }),
|
|
3307
|
-
Array.from(new Map((props.hostedTask?.runs ?? []).filter((run) =>
|
|
3313
|
+
Array.from(new Map((props.hostedTask?.runs ?? []).filter((run) => ["registration", "profile", "annual_reports", "changes", "financing", "bidding", "recruitment", "investments"].includes(run.dimension)).map((run) => [run.dimension, run])).values()).map((run) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
3308
3314
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h4", { children: dimensionLabel(run.dimension) }),
|
|
3315
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "qccPwNote", children: run.status === "done" ? "\u6570\u636E\u5DF2\u53D6\u5F97 \xB7 \u4EE5\u4E0B\u4E3A\u6765\u6E90\u6458\u8981\u4E0E\u6837\u672C\u5B57\u6BB5\uFF0C\u4E0D\u4EE3\u8868\u5168\u91CF\u660E\u7EC6\u6216\u6700\u7EC8\u7814\u5224" : run.status === "no-data" ? "\u67E5\u8BE2\u6210\u529F\uFF0C\u672A\u53D1\u73B0\u8BB0\u5F55" : `\u67E5\u8BE2\u72B6\u6001\uFF1A${run.status}` }),
|
|
3309
3316
|
run.result?.summary ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { children: run.result.summary }) : null,
|
|
3310
|
-
run.result?.facts.map((fact) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { children: fact }, fact))
|
|
3317
|
+
run.result?.facts.map((fact) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { children: fact }, fact)),
|
|
3318
|
+
run.status === "done" && !run.result?.summary && !run.result?.facts.some((fact) => !fact.startsWith("\u4F01\u4E1A\u540D\u79F0\uFF1A")) ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "qccPwNote", children: "\u6570\u636E\u5DF2\u8FD4\u56DE\uFF0C\u6682\u672A\u63D0\u53D6\u5230\u4E1A\u52A1\u6458\u8981\uFF1B\u4E0D\u4EE3\u8868\u65E0\u6570\u636E\uFF0C\u53EF\u5728\u539F\u4F1A\u8BDD\u67E5\u770B\u5DE5\u5177\u7ED3\u679C\u3002" }) : null
|
|
3311
3319
|
] }, run.id)),
|
|
3312
3320
|
insights.stateUndetermined ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "qccPwNote", children: "\u72B6\u6001\u672A\u5B9A\uFF1A\u516C\u5F00\u8BC1\u636E\u4E0D\u8DB3\uFF0C\u672C\u6B21\u964D\u7EA7\u4E3A\u6E05\u5355\u5F0F\u7B80\u62A5\u3002" }) : null,
|
|
3313
3321
|
insights.industryLink === null ? null : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: "qccPwNote", children: [
|
package/lib/index.js
CHANGED
|
@@ -335,8 +335,7 @@ async function renderReportFile(task, format, fontPath = process.env.DSH_PREVISI
|
|
|
335
335
|
children: [new Paragraph({ heading: HeadingLevel.TITLE, children: [new TextRun({ text: task.entity?.fullName ?? task.query, font: fontName, size: 32, color: "000000" })] }), new Paragraph({ children: [new TextRun({ text: `V${task.reportVersion ?? 1} \xB7 ${task.id}`, font: fontName, size: 20, color: "000000" })] }), ...lines.map((line) => new Paragraph({ children: [new TextRun({ text: line, font: fontName, size: 24, color: "000000" })] }))]
|
|
336
336
|
}] }));
|
|
337
337
|
}
|
|
338
|
-
|
|
339
|
-
const font = await readFile(fontPath);
|
|
338
|
+
const font = await readFile(fontPath || new URL("../assets/fonts/NotoSansCJKsc-Regular.otf", import.meta.url));
|
|
340
339
|
if (font.length > 4e7) throw new Error("\u5B57\u4F53\u6587\u4EF6\u8FC7\u5927");
|
|
341
340
|
return new Promise((resolve, reject) => {
|
|
342
341
|
const pdf = new PDFDocument({ size: "LETTER", margin: 54, info: { Title: identity, Creator: "\u8BBF\u524D\u5C3D\u8C03" } });
|
|
@@ -1013,6 +1012,15 @@ var PrevisitWorkflowStore = class {
|
|
|
1013
1012
|
});
|
|
1014
1013
|
}
|
|
1015
1014
|
finalizationQueue = Promise.resolve();
|
|
1015
|
+
reportProgress(id, activity) {
|
|
1016
|
+
const operation = this.finalizationQueue.then(() => this.update(id, (current) => {
|
|
1017
|
+
assertTaskOpen(current);
|
|
1018
|
+
return { ...current, activity };
|
|
1019
|
+
}));
|
|
1020
|
+
this.finalizationQueue = operation.catch(() => {
|
|
1021
|
+
});
|
|
1022
|
+
return operation;
|
|
1023
|
+
}
|
|
1016
1024
|
/** Serialize evidence writes with publication: no late mutation of a published version. */
|
|
1017
1025
|
addEvidence(id, kind, input) {
|
|
1018
1026
|
const operation = this.finalizationQueue.then(() => this.update(id, (record) => {
|
|
@@ -1251,11 +1259,17 @@ import { randomUUID as randomUUID4 } from "node:crypto";
|
|
|
1251
1259
|
// src/result-summary.ts
|
|
1252
1260
|
function summarizeResult(value) {
|
|
1253
1261
|
const result = { summary: "", facts: [], factors: [] };
|
|
1262
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1263
|
+
const fields = /^(经营状态|登记状态|主营业务|经营范围|所属行业|行业|注册资本|成立日期|参保人数|企业名称|产业链|产业链环节|核心业务|产品名称|产品介绍|业务介绍|企业简介|简介|行业标签|融资轮次|轮次|融资金额|融资日期|投资方|投资机构|项目名称|招标项目名称|中标项目名称|中标金额|发布日期|公告日期|招标人|中标人|职位名称|招聘职位|工作地点|薪资|招聘人数|年度|年份|年报年度|变更日期|变更项目|变更前|变更后|日期|时间|总数|记录总数|记录数|总条数|total|totalCount|count)$/;
|
|
1264
|
+
const fact = (value2) => {
|
|
1265
|
+
if (result.facts.length < 18 && !result.facts.includes(value2)) result.facts.push(value2);
|
|
1266
|
+
};
|
|
1254
1267
|
let visited = 0;
|
|
1255
|
-
const visit = (item, depth) => {
|
|
1256
|
-
if (depth > 7 || ++visited > 1e3 || item === null || typeof item !== "object") return;
|
|
1268
|
+
const visit = (item, depth, path = "") => {
|
|
1269
|
+
if (depth > 7 || ++visited > 1e3 || item === null || typeof item !== "object" || seen.has(item)) return;
|
|
1270
|
+
seen.add(item);
|
|
1257
1271
|
if (Array.isArray(item)) {
|
|
1258
|
-
item.slice(0, 80).forEach((child) => visit(child, depth + 1));
|
|
1272
|
+
item.slice(0, 80).forEach((child, index) => visit(child, depth + 1, `${path}[${index + 1}]`));
|
|
1259
1273
|
return;
|
|
1260
1274
|
}
|
|
1261
1275
|
const row = item;
|
|
@@ -1265,14 +1279,37 @@ function summarizeResult(value) {
|
|
|
1265
1279
|
if (typeof name === "string" && Number.isSafeInteger(count) && count >= 0 && !result.factors.some((f) => f.name === name)) result.factors.push({ name: name.slice(0, 100), count });
|
|
1266
1280
|
for (const [key, child] of Object.entries(row)) {
|
|
1267
1281
|
if ((key === "\u6458\u8981" || key === "summary") && typeof child === "string" && !result.summary) result.summary = child.slice(0, 1800);
|
|
1268
|
-
if (
|
|
1269
|
-
|
|
1282
|
+
if (/token|secret|password|authorization|凭据|密钥|密码|身份证|证件号码|银行账号/i.test(key)) continue;
|
|
1283
|
+
const businessLabel = /^[\u3400-\u9fff][\u3400-\u9fffA-Za-z0-9()()、/ _-]{0,39}$/.test(key);
|
|
1284
|
+
if ((fields.test(key) || businessLabel) && (typeof child === "string" || typeof child === "number")) fact(`${path.includes("[") ? `${path.slice(0, 120)} \xB7 ` : ""}${key}\uFF1A${String(child).slice(0, 500)}`);
|
|
1285
|
+
if (key === "text" && typeof child === "string" && child.length < 2e5) {
|
|
1286
|
+
try {
|
|
1287
|
+
visit(JSON.parse(child), depth + 1, path);
|
|
1288
|
+
} catch {
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
visit(child, depth + 1, path ? `${path}.${key}` : key);
|
|
1270
1292
|
}
|
|
1271
1293
|
};
|
|
1272
1294
|
visit(value, 0);
|
|
1273
1295
|
return result;
|
|
1274
1296
|
}
|
|
1275
1297
|
|
|
1298
|
+
// src/tool-json.ts
|
|
1299
|
+
function toolJson(value, seen = /* @__PURE__ */ new Set()) {
|
|
1300
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
1301
|
+
if (typeof value === "number" && Number.isFinite(value) && !Object.is(value, -0)) return value;
|
|
1302
|
+
if (typeof value !== "object" || seen.has(value)) throw new Error("\u5DE5\u5177\u7ED3\u679C\u5305\u542B\u4E0D\u53EF\u5E8F\u5217\u5316\u7684\u503C");
|
|
1303
|
+
if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) throw new Error("\u5DE5\u5177\u7ED3\u679C\u5FC5\u987B\u4E3A\u666E\u901A JSON \u5BF9\u8C61");
|
|
1304
|
+
seen.add(value);
|
|
1305
|
+
try {
|
|
1306
|
+
if (Array.isArray(value)) return Array.from(value, (item) => toolJson(item, seen));
|
|
1307
|
+
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== void 0).map(([key, item]) => [key, toolJson(item, seen)]));
|
|
1308
|
+
} finally {
|
|
1309
|
+
seen.delete(value);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1276
1313
|
// src/previsit-tools.ts
|
|
1277
1314
|
var QUERY_ROUTES = {
|
|
1278
1315
|
entity_search: ["company", "get_company_by_query"],
|
|
@@ -1417,7 +1454,7 @@ function registerPrevisitTools(ctx, workflow = new PrevisitWorkflowStore(), opti
|
|
|
1417
1454
|
};
|
|
1418
1455
|
try {
|
|
1419
1456
|
exec.signal.throwIfAborted();
|
|
1420
|
-
return await execute(object(args), exec, execution.agent);
|
|
1457
|
+
return toolJson(await execute(object(args), exec, execution.agent));
|
|
1421
1458
|
} finally {
|
|
1422
1459
|
controllers.delete(controller);
|
|
1423
1460
|
}
|
|
@@ -1521,6 +1558,19 @@ function registerPrevisitTools(ctx, workflow = new PrevisitWorkflowStore(), opti
|
|
|
1521
1558
|
beginning.delete(owner);
|
|
1522
1559
|
}
|
|
1523
1560
|
});
|
|
1561
|
+
register("previsit_progress", "\u4E0A\u62A5\u7B80\u77ED\u7684\u7528\u6237\u53EF\u89C1\u5DE5\u4F5C\u6458\u8981\u3002\u5728\u8D44\u6599\u8FD4\u56DE\u540E\u3001\u98CE\u9669\u6838\u5BF9\u53CA\u62A5\u544A\u64B0\u5199\u5F00\u59CB\u65F6\u8C03\u7528\u3002\u53EA\u9648\u8FF0\u5F53\u524D\u5DE5\u4F5C\uFF0C\u4E0D\u5305\u542B\u5185\u90E8\u601D\u8003\u3001\u63A8\u7406\u8349\u7A3F\u6216\u865A\u6784\u8FDB\u5EA6\u3002", {
|
|
1562
|
+
taskId: { type: "string" },
|
|
1563
|
+
phase: { type: "string", enum: ["analysis", "verification", "writing"] },
|
|
1564
|
+
summary: { type: "string", maxLength: 240 }
|
|
1565
|
+
}, ["taskId", "phase", "summary"], async (args, _exec, agent) => {
|
|
1566
|
+
const task = requireTask(args, agent);
|
|
1567
|
+
const phase = args.phase;
|
|
1568
|
+
if (phase !== "analysis" && phase !== "verification" && phase !== "writing") throw new Error("\u672A\u77E5\u5DE5\u4F5C\u9636\u6BB5");
|
|
1569
|
+
const summary = string(args.summary);
|
|
1570
|
+
if (!summary || summary.length > 240) throw new Error("\u5DE5\u4F5C\u6458\u8981\u987B\u4E3A1\u81F3240\u5B57");
|
|
1571
|
+
const record = await workflow.reportProgress(task.id, { phase, summary, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1572
|
+
return { taskId: task.id, activity: record.activity };
|
|
1573
|
+
});
|
|
1524
1574
|
const requireTask = (args, agent) => {
|
|
1525
1575
|
const owner = ownerOf(agent);
|
|
1526
1576
|
if (beginning.has(owner)) throw new Error("\u65B0\u4EFB\u52A1\u4ECD\u5728\u521D\u59CB\u5316\uFF0C\u8BF7\u4E32\u884C\u6267\u884C");
|
|
@@ -1788,7 +1838,7 @@ function loadBundledSkill() {
|
|
|
1788
1838
|
},
|
|
1789
1839
|
metadata: {
|
|
1790
1840
|
author: "QCC",
|
|
1791
|
-
version: "0.1.
|
|
1841
|
+
version: "0.1.31",
|
|
1792
1842
|
industry: "enterprise-services",
|
|
1793
1843
|
mcpServers: ["qcc-company", "qcc-risk", "qcc-ipr", "qcc-operation", "qcc-executive"]
|
|
1794
1844
|
}
|