@v587d/capital-generation 2.8.2 → 2.9.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/CHANGELOG.md +87 -1
- package/README.md +41 -20
- package/chart-ui/README.md +1 -1
- package/chart-ui/client.src.cjs +7 -16
- package/chart-ui/index.js +12 -33
- package/chart-ui/package.json +2 -2
- package/config/client.js +18 -39
- package/config/client.src.cjs +79 -217
- package/config/index.js +22 -72
- package/config/package.json +2 -2
- package/cordis.patch.yml +46 -51
- package/lib/agents/bash-guard.d.ts +9 -55
- package/lib/agents/bash-guard.js +27 -67
- package/lib/agents/preset-roster.d.ts +22 -0
- package/lib/agents/preset-roster.js +87 -0
- package/lib/agents/role-tool-policy.d.ts +46 -0
- package/lib/agents/role-tool-policy.js +205 -0
- package/lib/agents/root-tool-policy.d.ts +28 -124
- package/lib/agents/root-tool-policy.js +164 -237
- package/lib/agents/selected-skills-provider.d.ts +2 -6
- package/lib/agents/selected-skills-provider.js +27 -84
- package/lib/capital-settings.d.ts +27 -0
- package/lib/capital-settings.js +23 -0
- package/lib/chart/artifact-ref.d.ts +3 -8
- package/lib/chart/artifact-ref.js +3 -8
- package/lib/chart/downsample.d.ts +2 -5
- package/lib/chart/downsample.js +3 -8
- package/lib/chart/errors.d.ts +3 -7
- package/lib/chart/errors.js +1 -2
- package/lib/chart/event-context.d.ts +8 -0
- package/lib/chart/event-context.js +39 -0
- package/lib/chart/events.d.ts +21 -78
- package/lib/chart/events.js +40 -189
- package/lib/chart/html.d.ts +2 -5
- package/lib/chart/html.js +8 -15
- package/lib/chart/runtime.d.ts +5 -18
- package/lib/chart/runtime.js +14 -34
- package/lib/chart/series.d.ts +10 -17
- package/lib/chart/series.js +22 -47
- package/lib/chart/source-token.d.ts +5 -13
- package/lib/chart/source-token.js +18 -17
- package/lib/chart/spec.d.ts +6 -12
- package/lib/chart/spec.js +7 -31
- package/lib/chart/tool.d.ts +6 -29
- package/lib/chart/tool.js +9 -23
- package/lib/chart-render-entry.d.ts +9 -0
- package/lib/chart-render-entry.js +17 -0
- package/lib/chart-source-entry.d.ts +13 -0
- package/lib/chart-source-entry.js +20 -0
- package/lib/collect-entry.d.ts +9 -0
- package/lib/collect-entry.js +10 -0
- package/lib/collector-runtime.d.ts +22 -0
- package/lib/collector-runtime.js +97 -0
- package/lib/data-collector/args.d.ts +6 -24
- package/lib/data-collector/args.js +6 -24
- package/lib/data-collector/dataset-tools.d.ts +7 -1
- package/lib/data-collector/dataset-tools.js +27 -56
- package/lib/data-collector/describe.d.ts +8 -33
- package/lib/data-collector/describe.js +17 -70
- package/lib/data-collector/hub.d.ts +27 -85
- package/lib/data-collector/hub.js +19 -85
- package/lib/data-collector/query-time.d.ts +7 -26
- package/lib/data-collector/query-time.js +8 -27
- package/lib/data-collector/store.d.ts +40 -117
- package/lib/data-collector/store.js +32 -111
- package/lib/data-collector/time-axis.d.ts +16 -76
- package/lib/data-collector/time-axis.js +25 -111
- package/lib/data-collector/tools.d.ts +6 -6
- package/lib/data-collector/tools.js +34 -43
- package/lib/dataset-read-entry.d.ts +9 -0
- package/lib/dataset-read-entry.js +7 -0
- package/lib/index.d.ts +7 -117
- package/lib/index.js +43 -422
- package/lib/net/eastmoney-client.d.ts +13 -65
- package/lib/net/eastmoney-client.js +12 -53
- package/lib/net/throttle.d.ts +2 -13
- package/lib/net/throttle.js +3 -14
- package/lib/ocr/client.d.ts +12 -21
- package/lib/ocr/client.js +23 -40
- package/lib/ocr/markdown.d.ts +20 -26
- package/lib/ocr/markdown.js +23 -36
- package/lib/ocr/store.d.ts +14 -47
- package/lib/ocr/store.js +15 -45
- package/lib/ocr-entry.d.ts +9 -0
- package/lib/ocr-entry.js +10 -0
- package/lib/policy-entry.d.ts +17 -0
- package/lib/policy-entry.js +22 -0
- package/lib/preset-claim.d.ts +22 -0
- package/lib/preset-claim.js +24 -0
- package/lib/preset-ids.d.ts +38 -0
- package/lib/preset-ids.js +46 -0
- package/lib/retriever-config.d.ts +37 -0
- package/lib/retriever-config.js +36 -0
- package/lib/retriever-runtime.d.ts +31 -0
- package/lib/retriever-runtime.js +82 -0
- package/lib/root-extras-entry.d.ts +17 -0
- package/lib/root-extras-entry.js +25 -0
- package/lib/runtime-entry.d.ts +15 -0
- package/lib/runtime-entry.js +5 -0
- package/lib/runtime.d.ts +21 -0
- package/lib/runtime.js +33 -0
- package/lib/search-entry.d.ts +9 -0
- package/lib/search-entry.js +9 -0
- package/lib/sources/eastmoney-http.js +20 -68
- package/lib/sources/ftshare-rest.d.ts +6 -31
- package/lib/sources/ftshare-rest.js +1194 -225
- package/lib/sources/fuyao-core.d.ts +8 -18
- package/lib/sources/fuyao-core.js +10 -21
- package/lib/sources/fuyao-rest.d.ts +2 -6
- package/lib/sources/fuyao-rest.js +23 -102
- package/lib/sources/security-code.d.ts +6 -12
- package/lib/sources/security-code.js +6 -12
- package/lib/sources/security-identity.d.ts +1 -4
- package/lib/sources/security-identity.js +3 -8
- package/lib/sources/tencent-http.js +14 -28
- package/lib/sources/tencent-public-core.d.ts +8 -60
- package/lib/sources/tencent-public-core.js +12 -77
- package/lib/sources/wind-mcp.d.ts +0 -5
- package/lib/sources/wind-mcp.js +23 -78
- package/lib/sources-entry.d.ts +9 -0
- package/lib/sources-entry.js +9 -0
- package/lib/time/tools.d.ts +8 -31
- package/lib/time/tools.js +39 -64
- package/lib/time-entry.d.ts +9 -0
- package/lib/time-entry.js +7 -0
- package/lib/tool-exec.d.ts +45 -11
- package/lib/tool-exec.js +59 -6
- package/lib/user-customization.d.ts +14 -0
- package/lib/user-customization.js +47 -0
- package/lib/watchlist/tools.d.ts +3 -13
- package/lib/watchlist/tools.js +10 -30
- package/lib/watchlist-entry.d.ts +9 -0
- package/lib/watchlist-entry.js +6 -0
- package/lib/web-retriever/engines.d.ts +6 -11
- package/lib/web-retriever/engines.js +9 -14
- package/lib/web-retriever/html-markdown.d.ts +14 -34
- package/lib/web-retriever/html-markdown.js +39 -75
- package/lib/web-retriever/local-fetch.d.ts +22 -58
- package/lib/web-retriever/local-fetch.js +33 -77
- package/lib/web-retriever/retriever.d.ts +6 -18
- package/lib/web-retriever/retriever.js +7 -7
- package/lib/web-retriever/sources.d.ts +37 -124
- package/lib/web-retriever/sources.js +64 -196
- package/lib/web-retriever/tools.d.ts +43 -27
- package/lib/web-retriever/tools.js +99 -137
- package/lib/web-retriever/wind-client.d.ts +14 -31
- package/lib/web-retriever/wind-client.js +16 -32
- package/locale/zh.json +1 -1
- package/package.json +67 -7
- package/preset/capital-generation/README.md +87 -53
- package/preset/capital-generation/agent.patch.yml +526 -337
- package/preset/capital-generation/skills/capital-data-profile/SKILL.md +2 -2
- package/preset/capital-generation/skills/capital-data-protocol/SKILL.md +1 -1
- package/preset/capital-generation/skills/capital-equity-research/SKILL.md +1 -1
- package/preset/capital-generation/skills/capital-event-impact/SKILL.md +1 -1
- package/preset/capital-generation/skills/capital-orchestration/SKILL.md +15 -19
- package/preset/capital-generation/skills/capital-portfolio-review/SKILL.md +1 -1
- package/preset/capital-generation/skills/capital-visualization-protocol/SKILL.md +9 -12
- package/preset/capital-generation/skills/capital-web-evidence/SKILL.md +1 -1
- package/watchlist/client.js +15 -148
- package/watchlist/client.src.cjs +148 -662
- package/watchlist/holding-rules.js +11 -38
- package/watchlist/index.js +127 -397
- package/watchlist/package.json +2 -2
package/lib/ocr/markdown.js
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* PaddleOCR
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 105,639 个字符里 63,232 个(60%)是 `style='...'` 样板,去掉再转成 GFM 管道表只剩 27,156。
|
|
7
|
-
* 本仓的场景恰恰是"大量表格、图形",不处理就等于**预算的六成买的是 HTML 属性**,
|
|
8
|
-
* 而 `prettifyMarkdown` 这类 flag 在上游被静默忽略(未知键也返回 Success),帮不上。
|
|
9
|
-
*
|
|
10
|
-
* 归一化只做**无损排版**:不删正文、不改数字、不做摘要。数字一律照抄上游。
|
|
2
|
+
* PaddleOCR output -> model-readable markdown (the only implementation). Upstream emits
|
|
3
|
+
* tables as inline HTML with per-cell `style=`, so on a measured 15-page report ~60% of
|
|
4
|
+
* characters were style boilerplate; `prettifyMarkdown`-style flags are silently ignored.
|
|
5
|
+
* Normalization is lossless layout only: no text, numbers or tables are dropped.
|
|
11
6
|
*/
|
|
12
7
|
const PAGE_MARKER_PREFIX = '<!-- page:';
|
|
13
|
-
/**
|
|
8
|
+
/** `&` appears in company and account names; leaving it encoded is dirty data. */
|
|
14
9
|
const NAMED_ENTITIES = { amp: '&', lt: '<', gt: '>', quot: '"', apos: "'", nbsp: ' ' };
|
|
15
10
|
function decodeEntities(value) {
|
|
16
11
|
return value.replace(/&(#\d+|#x[0-9a-f]+|[a-z]+);/giu, (whole, body) => {
|
|
@@ -32,27 +27,22 @@ function stripTags(value) {
|
|
|
32
27
|
.trim();
|
|
33
28
|
}
|
|
34
29
|
function escapeCell(value) {
|
|
35
|
-
// GFM
|
|
30
|
+
// GFM cells need escaped pipes; a split row misaligns columns silently.
|
|
36
31
|
return value.replace(/\|/gu, '\\|');
|
|
37
32
|
}
|
|
38
|
-
/**
|
|
33
|
+
/** Cell text from one `<tr>`; split on open tags, then cut at the matching close tag. */
|
|
39
34
|
function rowCells(row) {
|
|
40
35
|
const cells = [];
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
// ⚠️ 单元格标签是 `<td>` **或** `<th>`(上游表头行用 `<th>`,实测):写成 `<t(?:d|th)`
|
|
44
|
-
// 只匹配得到 `<td`,`<th>` 行切不出格、整行被 `kept` 丢掉——表头静默消失。
|
|
45
|
-
// `\b` 在这里是必要的:它让 `<thead>` / `<table>` 不被误当成单元格开标签。
|
|
36
|
+
// Upstream header rows use `<th>`; matching only `<td` silently drops whole header rows.
|
|
37
|
+
// `\b` keeps `<thead>` / `<table>` from being read as cell openers.
|
|
46
38
|
for (const segment of row.split(/<t(?:d|h)\b[^>]*>/giu).slice(1)) {
|
|
47
39
|
cells.push(stripTags(segment.split(/<\/t(?:d|h)\s*>/iu)[0] ?? ''));
|
|
48
40
|
}
|
|
49
41
|
return cells;
|
|
50
42
|
}
|
|
51
43
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* 表头按上游第一行处理(上游会把 `<thead><th>` 放第一行);列数以各行最大值为准并补齐,
|
|
55
|
-
* 因为 GFM 要求分隔行列数与表头一致,少一格整张表退化成裸文本。
|
|
44
|
+
* HTML table -> GFM pipe table. Header is upstream's first row; pad every row to the
|
|
45
|
+
* widest so GFM's separator count matches the header (a short row degrades the table).
|
|
56
46
|
*/
|
|
57
47
|
function tableToMarkdown(tableHtml) {
|
|
58
48
|
const rows = [...tableHtml.matchAll(/<tr\b[^>]*>([\s\S]*?)(?:<\/tr>|$)/giu)].map((match) => rowCells(match[1]));
|
|
@@ -64,7 +54,7 @@ function tableToMarkdown(tableHtml) {
|
|
|
64
54
|
lines.splice(1, 0, `|${' --- |'.repeat(width)}`);
|
|
65
55
|
return lines.join('\n');
|
|
66
56
|
}
|
|
67
|
-
/**
|
|
57
|
+
/** Strip styles, convert HTML tables to GFM, collapse blank lines; near-identity otherwise. */
|
|
68
58
|
export function normalizeOcrMarkdown(source) {
|
|
69
59
|
const styleMatches = source.match(/\s(?:style|class)\s*=\s*(?:"[^"]*"|'[^']*')/giu) ?? [];
|
|
70
60
|
const styleCharsRemoved = styleMatches.reduce((total, match) => total + match.length, 0);
|
|
@@ -77,8 +67,8 @@ export function normalizeOcrMarkdown(source) {
|
|
|
77
67
|
.replace(/<br\s*\/?\s*>/giu, ' ')
|
|
78
68
|
.replace(/<\/?(?:td|th|tr|thead|tbody|tfoot|table|div|span|p|font|img|a|sup|sub|b|i|u|s|em|strong)\b[^>]*>/giu, '')
|
|
79
69
|
.replace(/\s(?:style|class)\s*=\s*(?:"[^"]*"|'[^']*')/giu, '')
|
|
80
|
-
//
|
|
81
|
-
//
|
|
70
|
+
// Upstream glues headings to preceding text (`2024-11-12## 盈利预测`); require a
|
|
71
|
+
// space after `#` so "评级#买入" is not split.
|
|
82
72
|
.replace(/(?<=\S)(#{1,6}\s+\S)/gu, '\n\n$1')
|
|
83
73
|
.replace(/[ \t]+\n/gu, '\n')
|
|
84
74
|
.replace(/\n{3,}/gu, '\n\n');
|
|
@@ -92,7 +82,7 @@ function firstHeading(markdown) {
|
|
|
92
82
|
}
|
|
93
83
|
return undefined;
|
|
94
84
|
}
|
|
95
|
-
/**
|
|
85
|
+
/** Join per-page markdown and record page offsets, used by page reads and keyword lookup. */
|
|
96
86
|
export function buildOcrDocument(sourcePages) {
|
|
97
87
|
const chunks = [];
|
|
98
88
|
const pages = [];
|
|
@@ -127,8 +117,8 @@ function pageOf(pages, at) {
|
|
|
127
117
|
return pages.find((entry) => at >= entry.start && at < entry.end) ?? pages[pages.length - 1];
|
|
128
118
|
}
|
|
129
119
|
/**
|
|
130
|
-
*
|
|
131
|
-
*
|
|
120
|
+
* `"1-3"` / `"5"` / `"2-4,9"` (1-based, inclusive). Out-of-range pages fail loudly:
|
|
121
|
+
* "page absent" and "not requested" must stay distinguishable in the receipt.
|
|
132
122
|
*/
|
|
133
123
|
export function parsePageRange(spec, totalPages) {
|
|
134
124
|
const taken = [];
|
|
@@ -159,7 +149,7 @@ export function parsePageRange(spec, totalPages) {
|
|
|
159
149
|
throw new Error(`pages is empty after normalization: '${spec}'`);
|
|
160
150
|
return taken;
|
|
161
151
|
}
|
|
162
|
-
/**
|
|
152
|
+
/** Slice pages out of the stored text; never re-runs upstream. */
|
|
163
153
|
export function selectPages(document, pages) {
|
|
164
154
|
return pages.map((page) => {
|
|
165
155
|
const entry = document.pages.find((item) => item.page === page);
|
|
@@ -172,11 +162,10 @@ export function selectPages(document, pages) {
|
|
|
172
162
|
});
|
|
173
163
|
}
|
|
174
164
|
/**
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
* 归一化文本上做,不再出网。
|
|
165
|
+
* Keyword lookup over the persisted normalized text (no egress): the output budget is
|
|
166
|
+
* smaller than a whole report, so callers must be able to jump. Adjacent hits merge into
|
|
167
|
+
* one window, and windows never cross a page boundary — a miscited page is worse than
|
|
168
|
+
* fewer hits — which also keeps `<!-- page:N -->` markers out of excerpts.
|
|
180
169
|
*/
|
|
181
170
|
export function locateQuery(document, query, limit, windowChars) {
|
|
182
171
|
const haystack = document.document;
|
|
@@ -193,12 +182,10 @@ export function locateQuery(document, query, limit, windowChars) {
|
|
|
193
182
|
if (at < 0)
|
|
194
183
|
break;
|
|
195
184
|
cursor = at + needle.length;
|
|
196
|
-
//
|
|
185
|
+
// Merge adjacent hits: three occurrences of one sentence must not fill `limit`.
|
|
197
186
|
if (at < lastEnd)
|
|
198
187
|
continue;
|
|
199
188
|
const entry = pageOf(document.pages, at);
|
|
200
|
-
// 窗口**不越页**:片段被标成"第 N 页"就是要能被引用回第 N 页,跨了页就是错引(错引比少引危险)。
|
|
201
|
-
// 顺带把 `<!-- page:N -->` 存储标记挡在外面——那是落盘布局,不是给模型回传正文的东西。
|
|
202
189
|
const from = Math.max(entry?.start ?? 0, at - windowChars);
|
|
203
190
|
const to = Math.min(entry?.end ?? haystack.length, at + needle.length + windowChars);
|
|
204
191
|
hits.push({ page: entry?.page ?? 1, at, excerpt: haystack.slice(from, to).replace(/\n+/gu, ' ⏎ ') });
|
package/lib/ocr/store.d.ts
CHANGED
|
@@ -1,43 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 零花费**的第二、第三次读取;否则每次翻页都要重跑上游、再花一次 job。
|
|
7
|
-
*
|
|
8
|
-
* ```text
|
|
9
|
-
* capital-data/ocr/<doc_id>/
|
|
10
|
-
* ├── meta.json # 页偏移索引与花费口径(model / charts / job_id / created_at)
|
|
11
|
-
* └── document.md # 全篇归一化 markdown,页分隔 `<!-- page:N -->`
|
|
12
|
-
* ```
|
|
13
|
-
*
|
|
14
|
-
* `doc_id` 是**内容寻址**的:`sha1(归一化输入 + model + flags)` 前 12 位。因此"同一份文档
|
|
15
|
-
* 重复解析"自动命中缓存不再出网;`refresh` 才强制重跑(上游对同一份 PDF 两次给出过
|
|
16
|
-
* 18% 与 19% 的不同数字,重跑**不是**独立验证,引用必须带 `doc_id` 与 `created_at`)。
|
|
17
|
-
*
|
|
18
|
-
* 写入顺序是 `document.md` → `meta.json`:**meta 的在场即代表产物完整**。中途被中断
|
|
19
|
-
* (超时、取消)留下半件产物时,下一次读取按"未缓存"处理并重写(`overwrite`),
|
|
20
|
-
* 不会把模型永久锁死在一个坏 `doc_id` 上。
|
|
2
|
+
* OCR artifacts live under `capital-data/ocr/<doc_id>/`; the model only gets `doc_id`.
|
|
3
|
+
* Content-addressed ids make a repeat parse a cache hit. `document.md` is written before
|
|
4
|
+
* `meta.json`, so meta's presence means a complete artifact; half-written ones are redone.
|
|
5
|
+
* Page reads reuse the persisted text instead of re-running a paid upstream job.
|
|
21
6
|
*/
|
|
22
7
|
import type { SessionLike, WorkspaceDatasetStore } from '../data-collector/store.js';
|
|
23
8
|
import type { OcrDocument, OcrPageEntry } from './markdown.js';
|
|
24
9
|
export declare const OCR_ARTIFACT_SUBDIR = "capital-data/ocr";
|
|
25
10
|
export declare const OCR_DOC_ID_PATTERN: RegExp;
|
|
26
|
-
/**
|
|
11
|
+
/** Read cap for local documents: 100-page reports fit; fail rather than buffer a whole file. */
|
|
27
12
|
export declare const OCR_MAX_SOURCE_BYTES: number;
|
|
28
|
-
/**
|
|
13
|
+
/** On-disk metadata; field names match the receipt so disk and model see one truth. */
|
|
29
14
|
export interface OcrArtifactMeta {
|
|
30
15
|
doc_id: string;
|
|
31
|
-
/**
|
|
32
|
-
* 输入指认:`url:<http(s) URL>` 或 `file:<workspace 相对路径>`。**绝不落绝对路径**。
|
|
33
|
-
* 续查(只传 `job_id` + `doc_id`)时本机没有重算种子的输入,此时**留空而不是编一个**——
|
|
34
|
-
* 把作业号写成来源会被当成"这条 URL 就是文档出处"。
|
|
35
|
-
*/
|
|
16
|
+
/** Input identity, `url:` or `file:` (never absolute); empty on resume — never pass job_id off as provenance. */
|
|
36
17
|
source?: string;
|
|
37
18
|
source_kind: 'url' | 'file' | 'job';
|
|
38
19
|
job_id: string;
|
|
39
20
|
model: string;
|
|
40
|
-
/**
|
|
21
|
+
/** Chart-recognition flag at submit; unknown on resume, so omitted. */
|
|
41
22
|
charts?: boolean;
|
|
42
23
|
pages: number;
|
|
43
24
|
chars: number;
|
|
@@ -51,17 +32,9 @@ export interface OcrArtifact {
|
|
|
51
32
|
meta: OcrArtifactMeta;
|
|
52
33
|
document: OcrDocument;
|
|
53
34
|
}
|
|
54
|
-
/** `seed` 由各形态自己拼(URL 文本 / 文件字节),统一在此收敛成 `ocr_<12 hex>`。 */
|
|
55
35
|
export declare function deriveOcrDocId(seed: string | Uint8Array, model: string, charts: boolean): string;
|
|
56
|
-
/** 回执里给模型的 opaque 引用(与 Dataset/profile 引用同一套 `workspace://` 口径)。 */
|
|
57
36
|
export declare function ocrArtifactRef(docId: string): string;
|
|
58
|
-
/**
|
|
59
|
-
* 落盘产物**自己**坏了(调用方没做错任何事)。
|
|
60
|
-
*
|
|
61
|
-
* 为什么要单独一个类型:翻成回执的那一层要把"下一步该传什么"写清楚,而那一步需要的指认
|
|
62
|
-
* (当初解析用过的 url / file)只有这份 meta 里有。让调用方重新去猜链接,等于把一次
|
|
63
|
-
* 可机械执行的修复推回给人或模型。
|
|
64
|
-
*/
|
|
37
|
+
/** Corrupt artifact, not caller error; the repair hint needs the source only this meta holds. */
|
|
65
38
|
export declare class OcrArtifactError extends Error {
|
|
66
39
|
readonly reason: 'meta_unreadable' | 'meta_invalid' | 'document_missing';
|
|
67
40
|
readonly docId: string;
|
|
@@ -70,17 +43,15 @@ export declare class OcrArtifactError extends Error {
|
|
|
70
43
|
constructor(reason: 'meta_unreadable' | 'meta_invalid' | 'document_missing', docId: string, sourceKind: OcrArtifactMeta['source_kind'] | undefined, source: string | undefined, message: string);
|
|
71
44
|
}
|
|
72
45
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* - 抛错:meta 在但正文丢失/损坏,或半件产物——**不能**当成"没有内容",也不能静默重跑
|
|
76
|
-
* (重跑要花一次 job,用户看不到钱去哪了),交给调用方翻成带 `doc_id` 的失败回执。
|
|
46
|
+
* `undefined` = never parsed (safe to submit a job). A throw = meta present but body
|
|
47
|
+
* missing/corrupt: do not treat as empty and do not silently re-run (a re-run costs a job).
|
|
77
48
|
*/
|
|
78
49
|
export declare function readOcrArtifact(store: WorkspaceDatasetStore, input: {
|
|
79
50
|
session: SessionLike;
|
|
80
51
|
doc_id: string;
|
|
81
52
|
signal?: AbortSignal;
|
|
82
53
|
}): Promise<OcrArtifact | undefined>;
|
|
83
|
-
/**
|
|
54
|
+
/** `overwrite` only when the caller has already decided to regenerate. */
|
|
84
55
|
export declare function writeOcrArtifact(store: WorkspaceDatasetStore, input: {
|
|
85
56
|
session: SessionLike;
|
|
86
57
|
meta: OcrArtifactMeta;
|
|
@@ -91,7 +62,6 @@ export declare function writeOcrArtifact(store: WorkspaceDatasetStore, input: {
|
|
|
91
62
|
artifact_ref: string;
|
|
92
63
|
paths: string[];
|
|
93
64
|
}>;
|
|
94
|
-
/** 拼一条与盘上记录同形状的 meta(`document` 只提供算好的排版统计)。 */
|
|
95
65
|
export declare function buildArtifactMeta(input: {
|
|
96
66
|
doc_id: string;
|
|
97
67
|
source?: string;
|
|
@@ -103,11 +73,8 @@ export declare function buildArtifactMeta(input: {
|
|
|
103
73
|
createdAt: string;
|
|
104
74
|
}): OcrArtifactMeta;
|
|
105
75
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* 只接受 session workspace 内的相对路径:绝对路径(含 Windows 盘符)与 `..` 段在
|
|
109
|
-
* `normalizeWorkspaceRelativePath` 就被拒,之后仍要过沙箱归属校验。模型从 `@xxx.pdf`
|
|
110
|
-
* mention 里看到的是路径末段,因此回执话术要求它传 workspace 相对路径。
|
|
76
|
+
* `file` entry: workspace-relative only (absolute paths, drive letters and `..` are
|
|
77
|
+
* rejected downstream, then ownership is checked); @mentions show just the path tail.
|
|
111
78
|
*/
|
|
112
79
|
export declare function readOcrSourceBytes(store: WorkspaceDatasetStore, input: {
|
|
113
80
|
session: SessionLike;
|
package/lib/ocr/store.js
CHANGED
|
@@ -1,23 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 零花费**的第二、第三次读取;否则每次翻页都要重跑上游、再花一次 job。
|
|
7
|
-
*
|
|
8
|
-
* ```text
|
|
9
|
-
* capital-data/ocr/<doc_id>/
|
|
10
|
-
* ├── meta.json # 页偏移索引与花费口径(model / charts / job_id / created_at)
|
|
11
|
-
* └── document.md # 全篇归一化 markdown,页分隔 `<!-- page:N -->`
|
|
12
|
-
* ```
|
|
13
|
-
*
|
|
14
|
-
* `doc_id` 是**内容寻址**的:`sha1(归一化输入 + model + flags)` 前 12 位。因此"同一份文档
|
|
15
|
-
* 重复解析"自动命中缓存不再出网;`refresh` 才强制重跑(上游对同一份 PDF 两次给出过
|
|
16
|
-
* 18% 与 19% 的不同数字,重跑**不是**独立验证,引用必须带 `doc_id` 与 `created_at`)。
|
|
17
|
-
*
|
|
18
|
-
* 写入顺序是 `document.md` → `meta.json`:**meta 的在场即代表产物完整**。中途被中断
|
|
19
|
-
* (超时、取消)留下半件产物时,下一次读取按"未缓存"处理并重写(`overwrite`),
|
|
20
|
-
* 不会把模型永久锁死在一个坏 `doc_id` 上。
|
|
2
|
+
* OCR artifacts live under `capital-data/ocr/<doc_id>/`; the model only gets `doc_id`.
|
|
3
|
+
* Content-addressed ids make a repeat parse a cache hit. `document.md` is written before
|
|
4
|
+
* `meta.json`, so meta's presence means a complete artifact; half-written ones are redone.
|
|
5
|
+
* Page reads reuse the persisted text instead of re-running a paid upstream job.
|
|
21
6
|
*/
|
|
22
7
|
import { DatasetStoreError } from '../data-collector/store.js';
|
|
23
8
|
import { WORKSPACE_DATA_DIR } from '../data-collector/store.js';
|
|
@@ -25,12 +10,11 @@ export const OCR_ARTIFACT_SUBDIR = `${WORKSPACE_DATA_DIR}/ocr`;
|
|
|
25
10
|
export const OCR_DOC_ID_PATTERN = /^ocr_[0-9a-f]{12}$/u;
|
|
26
11
|
const META_FILE = 'meta.json';
|
|
27
12
|
const DOCUMENT_FILE = 'document.md';
|
|
28
|
-
/**
|
|
13
|
+
/** Read cap for local documents: 100-page reports fit; fail rather than buffer a whole file. */
|
|
29
14
|
export const OCR_MAX_SOURCE_BYTES = 32 * 1024 * 1024;
|
|
30
15
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* 文档撞在同一个 `doc_id` 上、把 A 的正文当 B 交出去。
|
|
16
|
+
* No `@types/node` (same `process.getBuiltinModule` idiom as hub/sources). Fail loudly:
|
|
17
|
+
* `doc_id` is a cache key, so a weaker fallback could collide two documents onto one id.
|
|
34
18
|
*/
|
|
35
19
|
function createHash(algorithm) {
|
|
36
20
|
const processLike = globalThis.process;
|
|
@@ -39,14 +23,12 @@ function createHash(algorithm) {
|
|
|
39
23
|
throw new Error('node:crypto is unavailable; cannot derive a content-addressed doc_id');
|
|
40
24
|
return hash;
|
|
41
25
|
}
|
|
42
|
-
/** `seed` 由各形态自己拼(URL 文本 / 文件字节),统一在此收敛成 `ocr_<12 hex>`。 */
|
|
43
26
|
export function deriveOcrDocId(seed, model, charts) {
|
|
44
27
|
const hash = createHash('sha1');
|
|
45
28
|
hash.update(seed);
|
|
46
29
|
hash.update(`\u0000${model}\u0000${charts ? 'charts' : 'no-charts'}`);
|
|
47
30
|
return `ocr_${hash.digest('hex').slice(0, 12)}`;
|
|
48
31
|
}
|
|
49
|
-
/** 回执里给模型的 opaque 引用(与 Dataset/profile 引用同一套 `workspace://` 口径)。 */
|
|
50
32
|
export function ocrArtifactRef(docId) {
|
|
51
33
|
return `workspace://${OCR_ARTIFACT_SUBDIR}/${docId}`;
|
|
52
34
|
}
|
|
@@ -67,13 +49,7 @@ function isNotFound(error) {
|
|
|
67
49
|
function isTooLarge(error) {
|
|
68
50
|
return error instanceof DatasetStoreError && error.code === 'dataset_too_large';
|
|
69
51
|
}
|
|
70
|
-
/**
|
|
71
|
-
* 落盘产物**自己**坏了(调用方没做错任何事)。
|
|
72
|
-
*
|
|
73
|
-
* 为什么要单独一个类型:翻成回执的那一层要把"下一步该传什么"写清楚,而那一步需要的指认
|
|
74
|
-
* (当初解析用过的 url / file)只有这份 meta 里有。让调用方重新去猜链接,等于把一次
|
|
75
|
-
* 可机械执行的修复推回给人或模型。
|
|
76
|
-
*/
|
|
52
|
+
/** Corrupt artifact, not caller error; the repair hint needs the source only this meta holds. */
|
|
77
53
|
export class OcrArtifactError extends Error {
|
|
78
54
|
reason;
|
|
79
55
|
docId;
|
|
@@ -89,10 +65,8 @@ export class OcrArtifactError extends Error {
|
|
|
89
65
|
}
|
|
90
66
|
}
|
|
91
67
|
/**
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* - 抛错:meta 在但正文丢失/损坏,或半件产物——**不能**当成"没有内容",也不能静默重跑
|
|
95
|
-
* (重跑要花一次 job,用户看不到钱去哪了),交给调用方翻成带 `doc_id` 的失败回执。
|
|
68
|
+
* `undefined` = never parsed (safe to submit a job). A throw = meta present but body
|
|
69
|
+
* missing/corrupt: do not treat as empty and do not silently re-run (a re-run costs a job).
|
|
96
70
|
*/
|
|
97
71
|
export async function readOcrArtifact(store, input) {
|
|
98
72
|
const docId = requireDocId(input.doc_id);
|
|
@@ -126,7 +100,7 @@ export async function readOcrArtifact(store, input) {
|
|
|
126
100
|
}
|
|
127
101
|
return { meta, document: { document: text, pages: meta.page_entries, chars: meta.chars, images: meta.images, tables: meta.tables, styleCharsRemoved: meta.style_chars_removed } };
|
|
128
102
|
}
|
|
129
|
-
/**
|
|
103
|
+
/** `overwrite` only when the caller has already decided to regenerate. */
|
|
130
104
|
export async function writeOcrArtifact(store, input) {
|
|
131
105
|
const docId = requireDocId(input.meta.doc_id);
|
|
132
106
|
const written = await store.writeWorkspaceFiles({
|
|
@@ -141,7 +115,7 @@ export async function writeOcrArtifact(store, input) {
|
|
|
141
115
|
});
|
|
142
116
|
return { artifact_ref: ocrArtifactRef(docId), paths: written.map((file) => file.path) };
|
|
143
117
|
}
|
|
144
|
-
/**
|
|
118
|
+
/** Re-validate our own record field by field so a hand-edited file fails loudly. */
|
|
145
119
|
function normalizeMeta(value, docId) {
|
|
146
120
|
const record = (typeof value === 'object' && value !== null ? value : {});
|
|
147
121
|
const pages = Array.isArray(record.page_entries) ? record.page_entries : [];
|
|
@@ -183,13 +157,12 @@ function normalizeMeta(value, docId) {
|
|
|
183
157
|
created_at: text('created_at'),
|
|
184
158
|
};
|
|
185
159
|
}
|
|
186
|
-
/**
|
|
160
|
+
/** Fail on an unknown provenance: defaulting it to `url` would fabricate attribution. */
|
|
187
161
|
function sourceKind(value) {
|
|
188
162
|
if (value === 'url' || value === 'file' || value === 'job')
|
|
189
163
|
return value;
|
|
190
164
|
throw new Error(`meta.source_kind 不认识:${String(value)}`);
|
|
191
165
|
}
|
|
192
|
-
/** 拼一条与盘上记录同形状的 meta(`document` 只提供算好的排版统计)。 */
|
|
193
166
|
export function buildArtifactMeta(input) {
|
|
194
167
|
return {
|
|
195
168
|
doc_id: requireDocId(input.doc_id),
|
|
@@ -208,11 +181,8 @@ export function buildArtifactMeta(input) {
|
|
|
208
181
|
};
|
|
209
182
|
}
|
|
210
183
|
/**
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
* 只接受 session workspace 内的相对路径:绝对路径(含 Windows 盘符)与 `..` 段在
|
|
214
|
-
* `normalizeWorkspaceRelativePath` 就被拒,之后仍要过沙箱归属校验。模型从 `@xxx.pdf`
|
|
215
|
-
* mention 里看到的是路径末段,因此回执话术要求它传 workspace 相对路径。
|
|
184
|
+
* `file` entry: workspace-relative only (absolute paths, drive letters and `..` are
|
|
185
|
+
* rejected downstream, then ownership is checked); @mentions show just the path tail.
|
|
216
186
|
*/
|
|
217
187
|
export async function readOcrSourceBytes(store, input) {
|
|
218
188
|
const bytes = await store.readWorkspaceBytes({
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `tools/ocr` row: PaddleOCR job -> markdown -> disk. The `url` vs `file` / `doc_id`
|
|
3
|
+
* split is a call-level guard in `root-tool-policy` (name-level deny cannot express it).
|
|
4
|
+
* Client construction is per mount; credentials resolve per call.
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
7
|
+
export declare const name = "capital-ocr-tools";
|
|
8
|
+
export declare const inject: string[];
|
|
9
|
+
export declare function apply(ctx: Context): void;
|
package/lib/ocr-entry.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { registerOcrTool } from './web-retriever/tools.js';
|
|
2
|
+
import { createRetrieverRuntime } from './retriever-runtime.js';
|
|
3
|
+
import { capitalDatasetStore } from './runtime.js';
|
|
4
|
+
import { readRetrieverConfig } from './capital-settings.js';
|
|
5
|
+
export const name = 'capital-ocr-tools';
|
|
6
|
+
export const inject = ['tools'];
|
|
7
|
+
export function apply(ctx) {
|
|
8
|
+
const runtime = createRetrieverRuntime(ctx, readRetrieverConfig(ctx));
|
|
9
|
+
registerOcrTool(ctx, { store: capitalDatasetStore(ctx), client: runtime.ocrClient });
|
|
10
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-plane Capital policy row: call-level gates that preset composition cannot express.
|
|
3
|
+
* Root convergence (visibility already closed by family rows), per-name sibling deny,
|
|
4
|
+
* call-level guards for the `ocr` url form and role `interrupt_agent`, plus shell and fs
|
|
5
|
+
* guards. Identity must be read per call: the host creates the Agent before selecting a
|
|
6
|
+
* preset. Also hides delegation-only roles from the remote roster. Listens on `ctx.root`;
|
|
7
|
+
* one registration per process.
|
|
8
|
+
*/
|
|
9
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
10
|
+
export declare const name = "capital-policy";
|
|
11
|
+
/**
|
|
12
|
+
* `tools` is a hard dependency: without `inject`, boot resolves it to undefined without
|
|
13
|
+
* throwing and every identity guard silently skips installation. Waiting for the fiber
|
|
14
|
+
* to provide it closes that boot-time race.
|
|
15
|
+
*/
|
|
16
|
+
export declare const inject: string[];
|
|
17
|
+
export declare function apply(ctx: Context): void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { registerBashGuard } from './agents/bash-guard.js';
|
|
2
|
+
import { registerPresetRosterFilter } from './agents/preset-roster.js';
|
|
3
|
+
import { registerFsWriteGuard, registerRootOcrGuard, registerRootToolPolicy } from './agents/root-tool-policy.js';
|
|
4
|
+
import { registerRoleSiblingGuard, registerRoleToolPolicy } from './agents/role-tool-policy.js';
|
|
5
|
+
export const name = 'capital-policy';
|
|
6
|
+
/**
|
|
7
|
+
* `tools` is a hard dependency: without `inject`, boot resolves it to undefined without
|
|
8
|
+
* throwing and every identity guard silently skips installation. Waiting for the fiber
|
|
9
|
+
* to provide it closes that boot-time race.
|
|
10
|
+
*/
|
|
11
|
+
export const inject = ['tools'];
|
|
12
|
+
export function apply(ctx) {
|
|
13
|
+
const policy = ctx;
|
|
14
|
+
// Preset rows now own visibility; the host keeps only call-level gates.
|
|
15
|
+
registerRootToolPolicy(policy, { deny: false });
|
|
16
|
+
registerRootOcrGuard(policy);
|
|
17
|
+
registerRoleToolPolicy(policy);
|
|
18
|
+
registerRoleSiblingGuard(policy);
|
|
19
|
+
registerBashGuard(policy);
|
|
20
|
+
registerFsWriteGuard(policy);
|
|
21
|
+
registerPresetRosterFilter(ctx);
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single reader for an Agent's preset claim. Callers differ only in the fail direction:
|
|
3
|
+
* root convergence assumes a claim, role surfaces skip convergence, tool authorization
|
|
4
|
+
* falls back to the old topology check (which is never broader). Any exception is folded
|
|
5
|
+
* into flags, never thrown, because callers sit on the Agent creation path.
|
|
6
|
+
*/
|
|
7
|
+
/** Registry surface unavailable, or `composedPreset` threw (see `failed`). */
|
|
8
|
+
export interface PresetClaimResult {
|
|
9
|
+
/** Raw `composedPreset` value; undefined means unbound Agent or a throw. */
|
|
10
|
+
readonly claim: string | undefined;
|
|
11
|
+
readonly registryMissing: boolean;
|
|
12
|
+
/** `composedPreset` threw; distinct from "no preset bound". */
|
|
13
|
+
readonly failed: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface ReadableContext {
|
|
16
|
+
get?(name: string): unknown;
|
|
17
|
+
readonly [key: string]: unknown;
|
|
18
|
+
}
|
|
19
|
+
export declare function readPresetClaim(ctx: ReadableContext | undefined, agent: {
|
|
20
|
+
ctx?: unknown;
|
|
21
|
+
} | undefined): PresetClaimResult;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single reader for an Agent's preset claim. Callers differ only in the fail direction:
|
|
3
|
+
* root convergence assumes a claim, role surfaces skip convergence, tool authorization
|
|
4
|
+
* falls back to the old topology check (which is never broader). Any exception is folded
|
|
5
|
+
* into flags, never thrown, because callers sit on the Agent creation path.
|
|
6
|
+
*/
|
|
7
|
+
export function readPresetClaim(ctx, agent) {
|
|
8
|
+
let presets;
|
|
9
|
+
try {
|
|
10
|
+
presets = ctx?.get?.('agentPresets');
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return { claim: undefined, registryMissing: true, failed: false };
|
|
14
|
+
}
|
|
15
|
+
if (presets === undefined || typeof presets.composedPreset !== 'function') {
|
|
16
|
+
return { claim: undefined, registryMissing: true, failed: false };
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
return { claim: presets.composedPreset(agent?.ctx), registryMissing: false, failed: false };
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return { claim: undefined, registryMissing: false, failed: true };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of Capital preset identity. Since 0.2.1 the preset id is the role
|
|
3
|
+
* identity used by root convergence, shell guards, role surfaces, diagnostics and tests.
|
|
4
|
+
* These strings must match `preset/capital-generation/agent.patch.yml` `config.id` and
|
|
5
|
+
* delegation `agentPreset` values; `config.id` is also session-log identity, so do not
|
|
6
|
+
* rename casually. `test/persona.test.mjs` cross-checks them.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ROOT_PRESET_ID = "capital-generation";
|
|
9
|
+
export declare const ROLE_PRESET_IDS: {
|
|
10
|
+
readonly collector: "capital-data-collector";
|
|
11
|
+
readonly junior: "capital-data-junior";
|
|
12
|
+
readonly specialist: "capital-visualization-specialist";
|
|
13
|
+
readonly retriever: "capital-web-retriever";
|
|
14
|
+
readonly analyst: "capital-data-analyst";
|
|
15
|
+
};
|
|
16
|
+
export type RolePresetId = (typeof ROLE_PRESET_IDS)[keyof typeof ROLE_PRESET_IDS];
|
|
17
|
+
/** Role preset ids (array form avoids prototype-method coupling). */
|
|
18
|
+
export declare const ROLE_PRESET_ID_LIST: readonly string[];
|
|
19
|
+
/** Every preset claimed by this plugin: root plus five roles. */
|
|
20
|
+
export declare const CAPITAL_PRESET_IDS: readonly string[];
|
|
21
|
+
export declare function isCapitalPresetId(id: string | undefined): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Roles reached only through delegation. They stay registered because delegation rows resolve
|
|
24
|
+
* `config.id`, but the host roster filter hides them from the new-session selector.
|
|
25
|
+
*/
|
|
26
|
+
export declare const INTERNAL_ROLE_PRESET_IDS: readonly string[];
|
|
27
|
+
export declare function isInternalRolePresetId(id: unknown): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Which preset may use which tool (enforced by `src/tool-exec.ts`). This replaces the old
|
|
30
|
+
* session-topology check, which rejected manually opened role sessions.
|
|
31
|
+
*
|
|
32
|
+
* The root preset must never appear in any set. These lists live at `src/` root because
|
|
33
|
+
* data and chart layers must not depend on the agents layer.
|
|
34
|
+
*/
|
|
35
|
+
export declare const DATA_COLLECTION_PRESET_IDS: readonly string[];
|
|
36
|
+
export declare const DATASET_READING_PRESET_IDS: readonly string[];
|
|
37
|
+
/** May issue `chart_source_ref` for chart reads. */
|
|
38
|
+
export declare const CHART_SOURCE_PRESET_IDS: readonly string[];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of Capital preset identity. Since 0.2.1 the preset id is the role
|
|
3
|
+
* identity used by root convergence, shell guards, role surfaces, diagnostics and tests.
|
|
4
|
+
* These strings must match `preset/capital-generation/agent.patch.yml` `config.id` and
|
|
5
|
+
* delegation `agentPreset` values; `config.id` is also session-log identity, so do not
|
|
6
|
+
* rename casually. `test/persona.test.mjs` cross-checks them.
|
|
7
|
+
*/
|
|
8
|
+
export const ROOT_PRESET_ID = 'capital-generation';
|
|
9
|
+
export const ROLE_PRESET_IDS = {
|
|
10
|
+
collector: 'capital-data-collector',
|
|
11
|
+
junior: 'capital-data-junior',
|
|
12
|
+
specialist: 'capital-visualization-specialist',
|
|
13
|
+
retriever: 'capital-web-retriever',
|
|
14
|
+
analyst: 'capital-data-analyst',
|
|
15
|
+
};
|
|
16
|
+
/** Role preset ids (array form avoids prototype-method coupling). */
|
|
17
|
+
export const ROLE_PRESET_ID_LIST = Object.values(ROLE_PRESET_IDS);
|
|
18
|
+
/** Every preset claimed by this plugin: root plus five roles. */
|
|
19
|
+
export const CAPITAL_PRESET_IDS = [ROOT_PRESET_ID, ...ROLE_PRESET_ID_LIST];
|
|
20
|
+
const CAPITAL_PRESET_ID_SET = new Set(CAPITAL_PRESET_IDS);
|
|
21
|
+
export function isCapitalPresetId(id) {
|
|
22
|
+
return id !== undefined && CAPITAL_PRESET_ID_SET.has(id);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Roles reached only through delegation. They stay registered because delegation rows resolve
|
|
26
|
+
* `config.id`, but the host roster filter hides them from the new-session selector.
|
|
27
|
+
*/
|
|
28
|
+
export const INTERNAL_ROLE_PRESET_IDS = [
|
|
29
|
+
ROLE_PRESET_IDS.junior,
|
|
30
|
+
ROLE_PRESET_IDS.specialist,
|
|
31
|
+
];
|
|
32
|
+
const INTERNAL_ROLE_PRESET_ID_SET = new Set(INTERNAL_ROLE_PRESET_IDS);
|
|
33
|
+
export function isInternalRolePresetId(id) {
|
|
34
|
+
return typeof id === 'string' && INTERNAL_ROLE_PRESET_ID_SET.has(id);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Which preset may use which tool (enforced by `src/tool-exec.ts`). This replaces the old
|
|
38
|
+
* session-topology check, which rejected manually opened role sessions.
|
|
39
|
+
*
|
|
40
|
+
* The root preset must never appear in any set. These lists live at `src/` root because
|
|
41
|
+
* data and chart layers must not depend on the agents layer.
|
|
42
|
+
*/
|
|
43
|
+
export const DATA_COLLECTION_PRESET_IDS = [ROLE_PRESET_IDS.collector];
|
|
44
|
+
export const DATASET_READING_PRESET_IDS = [ROLE_PRESET_IDS.junior, ROLE_PRESET_IDS.analyst];
|
|
45
|
+
/** May issue `chart_source_ref` for chart reads. */
|
|
46
|
+
export const CHART_SOURCE_PRESET_IDS = [ROLE_PRESET_IDS.junior];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface WindDocsConfig {
|
|
2
|
+
endpoint?: string;
|
|
3
|
+
credentialRef?: string;
|
|
4
|
+
timeoutMs?: number;
|
|
5
|
+
}
|
|
6
|
+
export interface LocalFetchConfig {
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
maxBytes?: number;
|
|
10
|
+
maxContentChars?: number;
|
|
11
|
+
maxRedirects?: number;
|
|
12
|
+
userAgent?: string;
|
|
13
|
+
}
|
|
14
|
+
/** `pollBudgetMs` must stay below the tool's declared 240000 ms timeout: the margin is for
|
|
15
|
+
* persisting the pending receipt, without which a framework timeout leaves no `doc_id`. */
|
|
16
|
+
export interface PaddleOcrConfig {
|
|
17
|
+
endpoint?: string;
|
|
18
|
+
credentialRef?: string;
|
|
19
|
+
model?: string;
|
|
20
|
+
pollBudgetMs?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface RetrieverConfig {
|
|
23
|
+
baseURL?: string;
|
|
24
|
+
credentialRef?: string;
|
|
25
|
+
windDocs?: WindDocsConfig;
|
|
26
|
+
localFetch?: LocalFetchConfig;
|
|
27
|
+
paddleOcr?: PaddleOcrConfig;
|
|
28
|
+
}
|
|
29
|
+
/** Sole default source for both the schema and `resolveLocalFetchConfig()`.
|
|
30
|
+
* timeoutMs 30000 matches dsh-web-fetch-http; 15s misreads slow proxy failover as TIMEOUT.
|
|
31
|
+
* maxContentChars applies to raw HTML before markdown conversion, so size it for HTML.
|
|
32
|
+
* userAgent is a product token: a bare version looks like a crawler to WAFs. */
|
|
33
|
+
export declare const LOCAL_FETCH_DEFAULTS: Required<LocalFetchConfig>;
|
|
34
|
+
/** Empty endpoint/model defer to `src/ocr/client.ts`; pollBudgetMs 0 defers to its DEFAULT_WAIT_BUDGET_MS. */
|
|
35
|
+
export declare const PADDLE_OCR_DEFAULTS: Required<PaddleOcrConfig>;
|
|
36
|
+
/** Consumers fill defaults themselves: `apply()` can receive a raw object that never passed the schema. */
|
|
37
|
+
export declare function resolveLocalFetchConfig(config?: LocalFetchConfig): Required<LocalFetchConfig>;
|