@v587d/capital-generation 2.8.3 → 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 +54 -1
- package/README.md +40 -19
- 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 -34
- package/lib/sources/ftshare-rest.js +84 -292
- 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 +30 -66
- 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/sources/wind-mcp.js
CHANGED
|
@@ -3,39 +3,23 @@ import { isoDateFromEpoch } from '../data-collector/time-axis.js';
|
|
|
3
3
|
import { getDataTimeContract } from '../time/tools.js';
|
|
4
4
|
import { createWindClient, WIND_SERVER_ENDPOINTS } from '../web-retriever/wind-client.js';
|
|
5
5
|
/**
|
|
6
|
-
* Wind
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* (列名是 question 的函数,`output_schema` 无处锚定,§6.3)。
|
|
11
|
-
*
|
|
12
|
-
* ⛔ 下面每条解析约束都来自真报文(§6.2–§6.6 与 2026-10-06 第二轮),不是从上游文档抄的:
|
|
13
|
-
* 1. 表格工具回 `{columns:[{name}], rows:[[标量]]}`,行**不是对象**,必须按列名转置;
|
|
14
|
-
* 2. `columns[].type` 一律写 `string`,而 K 线/分钟的数值确实是字符串、EDB 的是 number——
|
|
15
|
-
* 类型元数据不可信,数值由本文件解析,解析不了就响亮失败(§9.5);
|
|
16
|
-
* 3. 时间写法一张表三种:K 线带**该行自己的**交易所偏移,EDB 是 `20260731` 紧凑串。
|
|
17
|
-
* epoch 逐行按自带偏移换算,整段按 +08:00 处理会把美股错位 12 小时(§6.4);
|
|
18
|
-
* 4. 行键全部 ASCII snake_case:`updateDate` → `update_date`、`CHANGEHANDRATE` →
|
|
19
|
-
* `turnover_rate_pct`(§10.6,映射不到的列丢弃而不是透传)。
|
|
20
|
-
*/
|
|
21
|
-
/**
|
|
22
|
-
* 数据域的上游预算。健康响应实测 0.59–2.3 秒(EDB 取数 590ms、A 股分钟 653ms、日 K 2296ms),
|
|
23
|
-
* 而"该日没有数据"与"上游故障"在线上都表现为**挂到超时**(§6.6;2026-10-06 连昨天还正常的
|
|
24
|
-
* 港股日线也挂了三次)。Hub 的执行超时是 30 秒,而 `callTool` 每次要走 initialize + tools/call
|
|
25
|
-
* 两跳,所以单次取 12 秒:健康请求有 5 倍余量,病态请求在 Hub 给出无信息的
|
|
26
|
-
* `request timed out` 之前先由本文件给出**带原因**的失败。
|
|
6
|
+
* Wind MCP: only capabilities Fuyao lacks — EDB macro/industry/FX series, range K-lines with
|
|
7
|
+
* amount/turnover/avg. No NL tools (columns depend on the question; nothing to anchor in schema).
|
|
8
|
+
* Payload quirks: tables are `{columns, rows:[scalars]}`; `columns[].type` is always `string`;
|
|
9
|
+
* timestamps carry each row's exchange offset; unmapped columns are dropped.
|
|
27
10
|
*/
|
|
11
|
+
/** 12s: healthy calls take 0.6–2.3s; hangs and no-data look identical, so fail before Hub's 30s. */
|
|
28
12
|
const WIND_SOURCE_TIMEOUT_MS = 12_000;
|
|
29
|
-
/**
|
|
13
|
+
/** Only periods with verified payloads; 60-minute hangs both times and is excluded. */
|
|
30
14
|
const KLINE_PERIOD_CODES = {
|
|
31
15
|
m1: '1', m5: '3', m15: '5', m30: '6', day: '10', week: '11', month: '12',
|
|
32
16
|
};
|
|
33
17
|
const KLINE_PERIOD_LIST = Object.keys(KLINE_PERIOD_CODES).join('/');
|
|
34
18
|
const MINUTE_PERIODS = new Set(['m1', 'm5', 'm15', 'm30']);
|
|
35
|
-
/**
|
|
19
|
+
/** Minute ranges cap at 10 days: ~240 rows/day and no count parameter gates volume. */
|
|
36
20
|
const MINUTE_MAX_SPAN_DAYS = 10;
|
|
37
21
|
const DAILY_MAX_SPAN_DAYS = 3660;
|
|
38
|
-
/**
|
|
22
|
+
/** Upstream K-line columns: MATCH is close (Wind has no CLOSE column). */
|
|
39
23
|
const KLINE_TIME = 'TIME';
|
|
40
24
|
const KLINE_OPEN = 'OPEN';
|
|
41
25
|
const KLINE_MATCH = 'MATCH';
|
|
@@ -45,7 +29,6 @@ const KLINE_TURNOVER = 'TURNOVER';
|
|
|
45
29
|
const KLINE_VOLUME = 'VOLUME';
|
|
46
30
|
const KLINE_RATE = 'CHANGEHANDRATE';
|
|
47
31
|
const KLINE_AVG = 'AVPRICE';
|
|
48
|
-
/** 缺任何一列就构不成一根 K 线:回别的列形态即"薄表"(§6.6 的不存在代码只回两列)。 */
|
|
49
32
|
const KLINE_REQUIRED = [KLINE_TIME, KLINE_OPEN, KLINE_MATCH, KLINE_HIGH, KLINE_LOW];
|
|
50
33
|
function isRecord(value) {
|
|
51
34
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
@@ -75,7 +58,6 @@ function strictDate(value, name) {
|
|
|
75
58
|
function dayDistance(from, to) {
|
|
76
59
|
return Math.round((Date.parse(`${to}T00:00:00Z`) - Date.parse(`${from}T00:00:00Z`)) / 86_400_000);
|
|
77
60
|
}
|
|
78
|
-
/** 上游紧凑日期 `20260731` → `2026-07-31`;写不出合法日历日就失败,不放行畸形标签。 */
|
|
79
61
|
function compactDate(value, context) {
|
|
80
62
|
if (value === null || value === undefined || value === '')
|
|
81
63
|
return null;
|
|
@@ -89,14 +71,13 @@ function compactDate(value, context) {
|
|
|
89
71
|
}
|
|
90
72
|
return date;
|
|
91
73
|
}
|
|
92
|
-
/** EDB
|
|
74
|
+
/** EDB period labels have no timezone; use Asia/Shanghai midnight like the Eastmoney macro tables. */
|
|
93
75
|
function reportDateMs(date) {
|
|
94
76
|
return Date.parse(`${date}T00:00:00+08:00`);
|
|
95
77
|
}
|
|
96
78
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* 一律 null,**不许当 0 参与计算**(§1.4)。认不出的字符串是形状变了,不是缺数——响亮失败。
|
|
79
|
+
* `columns[].type` is always `string`; parse here. `INVALID` = missing → null, never 0.
|
|
80
|
+
* An unparseable string means the shape changed: fail loudly.
|
|
100
81
|
*/
|
|
101
82
|
function windNumber(cell, context, field) {
|
|
102
83
|
if (cell === null || cell === undefined)
|
|
@@ -113,7 +94,6 @@ function windNumber(cell, context, field) {
|
|
|
113
94
|
throw windError(`Wind ${context} returned unparseable ${field} ${JSON.stringify(cell)}`, 'wind_invalid_response');
|
|
114
95
|
return value;
|
|
115
96
|
}
|
|
116
|
-
/** 文本单元格:空串与 INVALID 都是"没有值";其余保留上游原文(含中文列值)。 */
|
|
117
97
|
function windText(cell) {
|
|
118
98
|
if (cell === null || cell === undefined)
|
|
119
99
|
return null;
|
|
@@ -122,10 +102,7 @@ function windText(cell) {
|
|
|
122
102
|
return null;
|
|
123
103
|
return text;
|
|
124
104
|
}
|
|
125
|
-
/**
|
|
126
|
-
* `unit` 的键是 `"HIGH 单位:"`——**空格 + 全角冒号**(§6.3)。逐列把上游原文摘进行里,
|
|
127
|
-
* 不翻译也不猜币种:同一列在港美股可能是"港元"/"美元",抄下上游那三个字是唯一不骗人的做法。
|
|
128
|
-
*/
|
|
105
|
+
/** `unit` keys look like `"HIGH 单位:"` (space + fullwidth colon); copy the upstream unit text, never translate. */
|
|
129
106
|
function windUnit(unit, column) {
|
|
130
107
|
if (!isRecord(unit))
|
|
131
108
|
return null;
|
|
@@ -135,7 +112,7 @@ function windUnit(unit, column) {
|
|
|
135
112
|
}
|
|
136
113
|
return null;
|
|
137
114
|
}
|
|
138
|
-
/**
|
|
115
|
+
/** Business errors come as JSON (`error` field) or plain text; text must fail with the original text, not become 0 rows. */
|
|
139
116
|
function requirePayload(result, context) {
|
|
140
117
|
if (!result.ok) {
|
|
141
118
|
const message = result.error ?? 'unknown Wind error';
|
|
@@ -155,13 +132,9 @@ function requirePayload(result, context) {
|
|
|
155
132
|
}
|
|
156
133
|
return result.data;
|
|
157
134
|
}
|
|
158
|
-
// 正文不是 JSON 时 wind-client 原样透传 `content`。上游的参数性抱怨(实测
|
|
159
|
-
// "observation或者[beginDate、endDate]必须填一个")就是这一类文本:必须带着原文失败,
|
|
160
|
-
// 不许退化成 0 行——那是把"你说错了"报成"没有数据"。
|
|
161
135
|
const text = typeof result.content === 'string' ? result.content.trim() : '';
|
|
162
136
|
throw windError(`Wind ${context} returned a non-table response: ${text.slice(0, 400) || '(empty)'}`, 'wind_unexpected_response');
|
|
163
137
|
}
|
|
164
|
-
/** 表格解包:`{data:{columns,rows,unit}}`(K 线/分钟)与顶层直接是表身的形态都接住。 */
|
|
165
138
|
function requireTable(payload, context) {
|
|
166
139
|
const node = isRecord(payload.data) ? payload.data : payload;
|
|
167
140
|
const { columns, rows } = node;
|
|
@@ -183,9 +156,8 @@ function optionalColumnIndex(table, name) {
|
|
|
183
156
|
return table.columns.includes(name) ? table.columns.indexOf(name) : -1;
|
|
184
157
|
}
|
|
185
158
|
/**
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
* `series_begin_date`/`series_end_date` 是**该指标序列**的起止,不是本次请求的窗口。
|
|
159
|
+
* Metric metadata → row keys. Key sets vary (FX lacks unit/magnitude/currency); missing keys become
|
|
160
|
+
* null, never guessed. `series_*` bound the series, not the request window.
|
|
189
161
|
*/
|
|
190
162
|
function metricMeta(meta, context) {
|
|
191
163
|
const code = windText(meta.code);
|
|
@@ -225,11 +197,7 @@ async function executeEdbSearch(client, params, signal) {
|
|
|
225
197
|
});
|
|
226
198
|
return { data: rows, schema: edbSearchOutput };
|
|
227
199
|
}
|
|
228
|
-
/**
|
|
229
|
-
* EDB 取数:`{metrics:[{meta, date[], value[]}]}` 的**并行数组**转置成一行一期(§3.3)。
|
|
230
|
-
* 日期与数值长度不一致时不猜——多出来的日期没有值、多出来的值没有期间,两种都是把数值挂错
|
|
231
|
-
* 期间,与 §1.4 里"excelTotalCount 不可信"同一族。
|
|
232
|
-
*/
|
|
200
|
+
/** Transpose `{metrics:[{meta, date[], value[]}]}` parallel arrays into one row per period; length mismatch fails. */
|
|
233
201
|
async function executeEdbQuery(client, params, signal) {
|
|
234
202
|
const payload = requirePayload(await client.callTool('query_economic_indicator_data', {
|
|
235
203
|
question: params.indicator,
|
|
@@ -258,10 +226,7 @@ async function executeEdbQuery(client, params, signal) {
|
|
|
258
226
|
}
|
|
259
227
|
return { data: rows, schema: edbQueryOutput };
|
|
260
228
|
}
|
|
261
|
-
/**
|
|
262
|
-
* K 线时间戳**必须自带偏移**(§6.4:A股/港股 +08:00、美股 -04:00,是交易所当地时间)。
|
|
263
|
-
* 没写偏移就没法确定这是哪一刻,`Date.parse` 会按宿主本地时区猜——所以先失败。
|
|
264
|
-
*/
|
|
229
|
+
/** K-line timestamps must carry an explicit offset (exchange local time); `Date.parse` would guess host TZ. */
|
|
265
230
|
function klineInstant(value, context) {
|
|
266
231
|
const text = String(value ?? '').trim();
|
|
267
232
|
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?(?:Z|[+-]\d{2}:?\d{2})$/.test(text)) {
|
|
@@ -283,15 +248,11 @@ async function executeKline(client, params, signal) {
|
|
|
283
248
|
period: KLINE_PERIOD_CODES[period],
|
|
284
249
|
aftype: adjust === 'qfq' ? '0' : '2',
|
|
285
250
|
};
|
|
286
|
-
//
|
|
287
|
-
// 锁"当日"而不是暴露成参数:后复权以 afdate 为锚向未来放大,把锚默认成今天会得到
|
|
288
|
-
// 一个逐位等于不复权却叫 hfq 的序列,所以 hfq 根本不放(参数层已拒)。
|
|
251
|
+
// qfq needs afdate within the range or it hangs 60s. Lock to today; hfq is not exposed.
|
|
289
252
|
if (adjust === 'qfq')
|
|
290
253
|
args.afdate = isoDateFromEpoch(Date.now(), 'Asia/Shanghai');
|
|
291
254
|
const payload = requirePayload(await client.callTool('get_stock_kline', args, signal), 'get_stock_kline');
|
|
292
255
|
const table = requireTable(payload, 'get_stock_kline');
|
|
293
|
-
// 空表是合法成功:非交易日实测快回 rows=[] 且 unit=null(§6.2)——"这一区间没有行情"
|
|
294
|
-
// 与"取不到"是两种结论,前者落 0 行 Dataset,后者由上面的错误分类负责。
|
|
295
256
|
if (table.rows.length === 0)
|
|
296
257
|
return { data: [], schema: klineOutput };
|
|
297
258
|
const at = KLINE_REQUIRED.reduce((acc, name) => ({ ...acc, [name]: columnIndex(table, name, 'get_stock_kline') }), {});
|
|
@@ -412,8 +373,7 @@ function normalizeEdbSearch(params) {
|
|
|
412
373
|
return { question };
|
|
413
374
|
}
|
|
414
375
|
function normalizeEdbQuery(params) {
|
|
415
|
-
//
|
|
416
|
-
// 而实测上游对 observation "给 3 只回 2 期"(§6.7),期数不可信。
|
|
376
|
+
// observation is rejected explicitly: it returns 2 periods for 3 requested, so counts are untrustworthy.
|
|
417
377
|
if (params.observation !== undefined)
|
|
418
378
|
throw new Error('parameter observation is not supported: 实测该口径给 3 期只回 2 期,期数不可信;请给 start_date 与 end_date');
|
|
419
379
|
const raw = typeof params.indicator === 'string' ? params.indicator.trim() : '';
|
|
@@ -429,10 +389,7 @@ function normalizeEdbQuery(params) {
|
|
|
429
389
|
throw new Error('parameter start_date must not be later than end_date');
|
|
430
390
|
return { indicator: raw.replace(/\s+/g, ' '), start_date, end_date };
|
|
431
391
|
}
|
|
432
|
-
/**
|
|
433
|
-
* 代码照 Wind 的写法收下,**不补也不猜交易所后缀**(§3.3):实测美股 `AAPL.O` 与 `AAPL.N`
|
|
434
|
-
* 回完全相同的行(后缀不参与解析),所以猜一个后缀不会报错、只会静默认错标的。
|
|
435
|
-
*/
|
|
392
|
+
/** Codes are taken as Wind spells them, never with a guessed exchange suffix: AAPL.O and AAPL.N return identical rows. */
|
|
436
393
|
function normalizeWindCode(value) {
|
|
437
394
|
const raw = typeof value === 'string' ? value.trim().toUpperCase() : '';
|
|
438
395
|
if (!/^[A-Z0-9][A-Z0-9.]*\.[A-Z]{1,3}$/.test(raw) || raw.includes('..')) {
|
|
@@ -441,8 +398,7 @@ function normalizeWindCode(value) {
|
|
|
441
398
|
return raw;
|
|
442
399
|
}
|
|
443
400
|
function normalizeKline(params) {
|
|
444
|
-
//
|
|
445
|
-
// 同 tencent 港美股那两条对 start/end 的处理):负值/0 的语义只有上游蓝本的说法、没打过真报文(§6.8 第 2 条)。
|
|
401
|
+
// count is in `allowed` only to reject it with a reason: its negative/zero semantics are untested.
|
|
446
402
|
if (params.count !== undefined)
|
|
447
403
|
throw new Error('parameter count is not supported: 本能力按 start_date..end_date 取全区间,count 的正负/零语义(往后 N 条 / 往前 N 条 / 全区间)未实测,不暴露');
|
|
448
404
|
const code = normalizeWindCode(params.code);
|
|
@@ -470,11 +426,7 @@ function normalizeKline(params) {
|
|
|
470
426
|
}
|
|
471
427
|
return { code, period, adjust, start_date, end_date };
|
|
472
428
|
}
|
|
473
|
-
/**
|
|
474
|
-
* 单能力的 DataSource:客户端在**定义期**构造是安全的(它不持有 Key——每次 callTool 现解析,
|
|
475
|
-
* 也不持有会话),所以这里没有任何"装配期读到凭据才注册"的门槛(§9.7⑤,桌面端那 61 颗
|
|
476
|
-
* 静默缺席的事故就在这一条上)。
|
|
477
|
-
*/
|
|
429
|
+
/** Build at definition time is safe: no key (resolved per call), no session; registration is not gated on credentials. */
|
|
478
430
|
function createWindSource(options) {
|
|
479
431
|
const schema = {
|
|
480
432
|
capability: options.capability,
|
|
@@ -496,7 +448,7 @@ function createWindSource(options) {
|
|
|
496
448
|
timeoutMs: WIND_SOURCE_TIMEOUT_MS,
|
|
497
449
|
resolveApiKey: options.resolveApiKey,
|
|
498
450
|
});
|
|
499
|
-
//
|
|
451
|
+
// normalize runs twice (Hub enqueue and execute), so keep key names and make it idempotent.
|
|
500
452
|
const normalize = (params) => {
|
|
501
453
|
if (!isRecord(params))
|
|
502
454
|
throw new Error('params must be an object');
|
|
@@ -506,7 +458,6 @@ function createWindSource(options) {
|
|
|
506
458
|
return {
|
|
507
459
|
schema,
|
|
508
460
|
normalizeParams: normalize,
|
|
509
|
-
// 空结果是合法成功(非交易日分钟线实测快回 rows=[]),所以这道护栏只认形状、不认条数(§3.3)。
|
|
510
461
|
validateOutput: (data) => Array.isArray(data),
|
|
511
462
|
execute: (request, signal) => options.execute(client, normalize(request.params), signal),
|
|
512
463
|
};
|
|
@@ -514,18 +465,12 @@ function createWindSource(options) {
|
|
|
514
465
|
const EDB_SEARCH_DESCRIPTION = 'Wind EDB(经济数据库)指标**检索**:只找指标不取数,一行一个候选指标(code/name/unit/freq/source + 该序列的起止与更新日期)。这是 fuyao 与东财都没有的面:宏观、行业、汇率、全球序列。⛔ question 走后端 NER,字面请求不等于字面结果——实测「人民币兑美元汇率 指标」回的是**美元兑人民币**(source 为 美联储 / 台湾统计局),先核对 name 与 source 再取数。键集随指标族变化:实测汇率类没有 unit/magnitude/currency 三个键,缺的一律 null,不猜单位。取数走 wind_edb_query:确定性靠"先 search 拿 code、再按 code 取数"这两步拿到,不是靠 NL 稳定。没有匹配时返回 0 行,是合法空结果不是失败。';
|
|
515
466
|
const EDB_QUERY_DESCRIPTION = 'Wind EDB 时间序列**取数**:上游回 {meta, date[], value[]} 并行数组,本能力转置成**一行一个观测期**(meta 逐行拼接,多指标可分次取后按 code 拼对比)。⛔ indicator 一次只填**一个**指标代码或一条中文问句:2026-10-06 实测逗号分隔的多码挂满 60 秒无应答,上游"多个代码用英文逗号分隔"的说法不成立,要多个就分次调用。start_date/end_date 必填——上游在日期区间与 observation 之间二选一,而 observation 实测"给 3 期只回 2 期"、期数不可信,本能力直接拒绝它。report_date 由上游紧凑串 20260731 归一而来,report_date_ms 按 Asia/Shanghai 零点(EDB 的日期本身没有时区)。unit 是上游单位原文;后端 INVALID 一律 null,**不得当 0 参与计算**。已实测指标代码直接当 indicator 与同义中文问句回同一份序列(M0000612 = 中国 CPI 当月同比):先 wind_edb_search 拿码再回来,确定性就在这两步。';
|
|
516
467
|
const KLINE_DESCRIPTION = 'Wind K 线:单只标的按**日期区间**取 日/周/月 与 1/5/15/30 分钟。相对腾讯的增量(§6.6.1):按日期区间取 640 根之前的历史、每根带成交额/成交量/换手/均价的美股 K 线、以及分钟档;⚠️ **分钟档只有 A 股取到过行**,港股与美股的分钟到 2026-10-06 为止四次全挂、仍未证实,要港美股分时先小范围试一次。港美股日线与快照请走 tencent_*。⛔ code 必须自带交易所后缀(600519.SH / 00700.HK / AAPL.O),本能力不猜后缀——实测 AAPL.O 与 AAPL.N 回完全相同的行(后缀不参与解析),所以 BRK.B 一类双市场代码有认错标的的风险;每行的 code 只是请求写法回显,上游 K 线表里没有代码列。⛔ period 只放逐档打过真报文的七档,60 分钟实测两次都挂满超时、不提供。adjust 只有 none 与 qfq:后复权以 afdate 为锚向未来放大,锚默认成今天会得到一个逐位等于不复权却叫 hfq 的序列,所以宁可不给;qfq 的 afdate 由本能力锁成当日,因此 end_date 不得晚于当日;分钟档只允许 none。close 来自上游 MATCH 列(Wind 没有 CLOSE 列);上游多给的列不收录,行里以 output_fields 为准。数值列上游全是字符串(columns[].type 却一律写 string),解析不了即失败。time_ms 按**该行自带偏移**换算(A股/港股 +08:00、美股 -04:00 是交易所当地时间),整段按北京时间处理会让美股错位 12 小时。⚠️ volume 实测 A 股是**股**(腾讯 A 股 K 线是手,跨源先换);turnover_rate_pct 是百分数原值(0.2892 = 0.2892%),上游不给它的单位与分母口径。非交易日快回 0 行、是合法成功(实测 A 股国庆休市日 653ms 回空表);而"该日该市场分钟尚未落地"与上游故障都表现为挂到 12 秒超时(wind_upstream_timeout)——不要盲目重试同一区间,改更早日期或改走日线。积分计费:AUTH/RATE_LIMIT/后端错误一律不自动重试。';
|
|
517
|
-
/**
|
|
518
|
-
* §6.7 改口后的 P1 三条。`wind_edb_*` 是 P0(EDB 是唯一来源),`wind_stock_kline` 是 P1。
|
|
519
|
-
* 不接的一概有理由:price_indicators 与全部 NL 工具(中文键 / 列名是 question 的函数)、
|
|
520
|
-
* bond 与 fund/index(重叠或未测)、analytics_data(实测连 initialize 都挂)。
|
|
521
|
-
*/
|
|
522
468
|
export function createWindSources(resolveApiKey) {
|
|
523
469
|
return [
|
|
524
470
|
createWindSource({
|
|
525
471
|
capability: 'wind_edb_search',
|
|
526
472
|
tool: 'search_economic_indicator',
|
|
527
473
|
server: 'economic_data',
|
|
528
|
-
// 指标目录与序列发布都是日/季粒度;缓存拉长是为了少烧积分,而不是省 RTT。
|
|
529
474
|
cacheMaxAgeMs: 6 * 3_600_000,
|
|
530
475
|
summary: 'Wind EDB 宏观/行业/汇率指标检索(只找码不取数)',
|
|
531
476
|
description: EDB_SEARCH_DESCRIPTION,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `tools/retrieve-sources` row: the nine named-source tools, gated by
|
|
3
|
+
* `localFetch.enabled`. When disabled they stay registered and fail loudly per call;
|
|
4
|
+
* unregistering at boot would freeze one config read for the process lifetime.
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
7
|
+
export declare const name = "capital-retrieve-sources-tools";
|
|
8
|
+
export declare const inject: string[];
|
|
9
|
+
export declare function apply(ctx: Context): void;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createRetrieverRuntime } from './retriever-runtime.js';
|
|
2
|
+
import { registerSourceTools } from './web-retriever/tools.js';
|
|
3
|
+
import { readRetrieverConfig } from './capital-settings.js';
|
|
4
|
+
export const name = 'capital-retrieve-sources-tools';
|
|
5
|
+
export const inject = ['tools'];
|
|
6
|
+
export function apply(ctx) {
|
|
7
|
+
const runtime = createRetrieverRuntime(ctx, readRetrieverConfig(ctx));
|
|
8
|
+
registerSourceTools(ctx, { ...runtime.localFetchConfig });
|
|
9
|
+
}
|
package/lib/time/tools.d.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
2
|
import type { TimeAxisSnapshot } from '../data-collector/store.js';
|
|
3
|
-
/**
|
|
4
|
-
* 时间工具需要的 Dataset 通道。只依赖 store 的**只读探针**,
|
|
5
|
-
* 因此 time 平面不需要认识 Dataset 的写入、profile 或查询实现。
|
|
6
|
-
*/
|
|
7
3
|
export interface TimeAxisReader {
|
|
8
4
|
describeTimeAxis(input: {
|
|
9
5
|
session: unknown;
|
|
@@ -22,20 +18,14 @@ export interface DataTimeContract {
|
|
|
22
18
|
warning?: string;
|
|
23
19
|
}
|
|
24
20
|
export declare function getDataTimeContract(capability: string): DataTimeContract | undefined;
|
|
25
|
-
/** 校验并规范化 IANA 时区名;undefined 表示宿主本地时区。'UTC'/'GMT' 为合法特殊值。 */
|
|
26
21
|
export declare function resolveTimeZone(timeZone: unknown): string | undefined;
|
|
27
|
-
/** 宿主进程的本地 IANA 时区名。 */
|
|
28
22
|
export declare function localTimeZone(): string;
|
|
29
|
-
/**
|
|
30
|
-
* 差值法计算某 IANA 时区在 now 时刻的 UTC 偏移毫秒数(timeZoneName 缺失时的回退)。
|
|
31
|
-
*/
|
|
23
|
+
/** UTC offset from formatted parts; fallback when timeZoneName is unavailable. */
|
|
32
24
|
export declare function zoneOffsetMsByDiff(now: number, zone: string): number;
|
|
33
|
-
/** 当前时刻读数(内部纯函数,便于测试):now 为 epoch 毫秒。 */
|
|
34
25
|
export declare function readClock(now: number, timeZone?: string): Record<string, unknown>;
|
|
35
26
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* 必须先调用本工具,绝不凭记忆猜测。
|
|
27
|
+
* Two time tools, not role-gated: get_local_datetime is the clock reading, resolve_data_time_range
|
|
28
|
+
* is conversion. Dataset-mode authorization is enforced at call time by src/tool-exec.ts.
|
|
39
29
|
*/
|
|
40
30
|
export declare function registerTimeTool(ctx: Context, store?: TimeAxisReader): void;
|
|
41
31
|
type DateParts = {
|
|
@@ -44,13 +34,8 @@ type DateParts = {
|
|
|
44
34
|
day: number;
|
|
45
35
|
};
|
|
46
36
|
/**
|
|
47
|
-
* period
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* 最后一种不是宽容癖好:真机实测模型会这么传(会话 cf464cb6 与 8002e358 各一次,
|
|
51
|
-
* 两次都掉进 `unsupported period` 白跑一轮)。宿主对 QuerySpec 的数组/对象参数早有同一套
|
|
52
|
-
* 宽容解析(`src/data-collector/args.ts`),period 没有理由更严格。
|
|
53
|
-
* 返回值 `undefined` = 调用方没给 period(取数形态按当前日期、Dataset 形态按数据最后一天)。
|
|
37
|
+
* Sole period normalization: literal windows, {unit,count}, and stringified {unit,count} (models
|
|
38
|
+
* really send that). undefined means the caller omitted period.
|
|
54
39
|
*/
|
|
55
40
|
export declare function periodInputOf(value: unknown): string | {
|
|
56
41
|
unit: string;
|
|
@@ -58,19 +43,11 @@ export declare function periodInputOf(value: unknown): string | {
|
|
|
58
43
|
} | undefined;
|
|
59
44
|
export declare function dateParts(value: string): DateParts;
|
|
60
45
|
export declare function shiftDate(value: string, unit: 'day' | 'week' | 'month' | 'quarter' | 'year', amount: number): string;
|
|
61
|
-
/**
|
|
62
|
-
* 两个日历日之间相差的天数(同区间的 `YYYY-MM-DD` 对,按 UTC 午夜算,不受时区与夏令时影响)。
|
|
63
|
-
* 对外只有一份:`get_watchlist` 报"这条持仓标了几天"用的就是它,不许在消费方再写一遍
|
|
64
|
-
* `Date.parse(...)/86_400_000`(AGENTS.md §9.7①)。
|
|
65
|
-
*/
|
|
46
|
+
/** Whole days between two dates, computed at UTC midnight so timezone/DST cannot shift it (one implementation). */
|
|
66
47
|
export declare function dayDistance(start: string, end: string): number;
|
|
67
48
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* `resolvePeriod` 之外再认三种字面窗口:`all`(全区间)、`ytd`(年初到锚点)、
|
|
72
|
-
* `YYYY-MM`(自然月)。它们不新增相对期语义,只是把"年内/某月"这两个最常用的
|
|
73
|
-
* 分析窗口写成模型本来就会用的词。对象形态 `{unit,count}` 原样交给 `resolvePeriod`。
|
|
49
|
+
* Shared single-window resolver; also accepts all, ytd and YYYY-MM. Object periods go to
|
|
50
|
+
* resolvePeriod as-is.
|
|
74
51
|
*/
|
|
75
52
|
export declare function resolveZonedPeriod(input: unknown, anchor: string): {
|
|
76
53
|
start: string;
|