@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/chart/tool.d.ts
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* 上下文经济学(这是本工具存在的理由):
|
|
5
|
-
* - 常驻成本 = 一条工具 schema;协议细节在 skill `capital-chart-protocol` 里按需加载;
|
|
6
|
-
* - 每次调用成本 = spec(几十到一百多 token)+ 回执(几十 token);
|
|
7
|
-
* - 序列数据 **0 token**:由宿主写进 workspace 产物,浏览器走旁路取。
|
|
8
|
-
* 对照:让模型把 250 点日线写进参数 ≈ 5–8k token,还会被工具结果剪枝器截断。
|
|
9
|
-
*
|
|
10
|
-
* 权限边界(docs/dev/data-roles.md §1.1「数据调度纪律」的显式豁免):本工具是**呈现**工具,不是数据工具。
|
|
11
|
-
* 它读 Dataset 只在宿主进程内进行,回执不含任何 rows —— 因此不越过"数据内容不进主 Agent
|
|
12
|
-
* 上下文"这条真正的边界。需要数值结论仍走 data_junior 的 profile / query。
|
|
13
|
-
*/
|
|
1
|
+
/** render_chart: render data into workspace artifacts and return only a small receipt.
|
|
2
|
+
* Series costs zero model tokens and rows stay host-side; numeric conclusions still go through
|
|
3
|
+
* data_junior. */
|
|
14
4
|
import type { Context } from '@deepseek-ai/cordis';
|
|
15
5
|
import { WorkspaceDatasetStore } from '../data-collector/store.js';
|
|
16
6
|
import { type ChartSourceTokenStore } from './source-token.js';
|
|
@@ -30,7 +20,7 @@ type ToolExecLike = {
|
|
|
30
20
|
signal: AbortSignal;
|
|
31
21
|
};
|
|
32
22
|
export declare const CHART_ARTIFACT_DIR = "capital-analysis/charts";
|
|
33
|
-
/**
|
|
23
|
+
/** Receipt is small metadata only; series never appears here. */
|
|
34
24
|
export interface ChartReceipt {
|
|
35
25
|
chart_ref: string;
|
|
36
26
|
chart_id: string;
|
|
@@ -62,22 +52,9 @@ export interface RenderChartInput {
|
|
|
62
52
|
store: WorkspaceDatasetStore;
|
|
63
53
|
sourceTokens?: ChartSourceTokenStore;
|
|
64
54
|
artifacts?: ChartArtifactRegistry;
|
|
65
|
-
/**
|
|
66
|
-
* 惰性解析 host 平面图表服务(`@v587d/capital-charts` 提供)。
|
|
67
|
-
*
|
|
68
|
-
* 每次画图时解析,而不是在 `apply` 时解析一次:host 平面行与本 preset 行的挂载先后
|
|
69
|
-
* 不由我们决定,一次性解析会把"服务稍后才可用"永久固化成 `chart_url=null`。
|
|
70
|
-
* 服务始终缺席(未安装/无 Web 载体/坏掉)时返回 undefined,整条链路照常降级:产物照样
|
|
71
|
-
* 落盘、`html_path` 照样可 present,只是 `chart_url` 为 null。
|
|
72
|
-
*/
|
|
55
|
+
/** Resolve per render, not at apply: host rows may mount later; absence degrades to chart_url=null. */
|
|
73
56
|
charts?: () => ChartPublisher | undefined;
|
|
74
|
-
/**
|
|
75
|
-
* 惰性解析"图表 → 本轮交付"发布器(官方 `deliverables/presented`)。
|
|
76
|
-
*
|
|
77
|
-
* 与 `charts` 同理每次出图解析一次:宿主平面的 `sessions` / `sessionProjections`
|
|
78
|
-
* 不可用时返回 undefined,整条链路照常降级——图仍在 workspace 里,回执照样给 html_path,
|
|
79
|
-
* 只是本轮交付行不会登记这张图。
|
|
80
|
-
*/
|
|
57
|
+
/** Same lazy rule: an absent publisher skips only the deliverable row. */
|
|
81
58
|
chartEvents?: () => ChartEventPublisher | undefined;
|
|
82
59
|
now?: () => number;
|
|
83
60
|
}
|
package/lib/chart/tool.js
CHANGED
|
@@ -56,10 +56,7 @@ function normalizeStoreError(error) {
|
|
|
56
56
|
return undefined;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
/**
|
|
60
|
-
* 工具框架对 throw 的错误只保留 message;把 ChartError 的 details 在这里展开成
|
|
61
|
-
* JSON 信封,模型才能拿到 array_keys / available 等一次修正所需的信息。
|
|
62
|
-
*/
|
|
59
|
+
/** Errors keep only message; expand details into JSON so the model can self-correct. */
|
|
63
60
|
function toolVisibleError(error) {
|
|
64
61
|
const normalized = error instanceof ChartError ? error : normalizeStoreError(error);
|
|
65
62
|
if (!normalized)
|
|
@@ -126,13 +123,10 @@ export async function renderChart(input, args, exec) {
|
|
|
126
123
|
const spec = normalizeChartSpec(args.spec);
|
|
127
124
|
if (typeof args.title === 'string' && args.title.trim().length > 0)
|
|
128
125
|
spec.title = args.title.trim().slice(0, 200);
|
|
129
|
-
//
|
|
126
|
+
// The token carries the direct data-agent session; the nested child never sees raw rows.
|
|
130
127
|
let rows;
|
|
131
128
|
let meta;
|
|
132
129
|
if (datasetId !== undefined) {
|
|
133
|
-
// A chart_source_ref carries the direct data-agent session that already has
|
|
134
|
-
// access to the parent Dataset scope. The nested visualization child is
|
|
135
|
-
// only the caller of render_chart; it never receives raw rows.
|
|
136
130
|
const readSession = tokenSource?.sourceSession ?? session;
|
|
137
131
|
const { ref, rows: datasetRows } = await input.store.readPresentationRows({ session: readSession, dataset_id: datasetId, signal: exec.signal });
|
|
138
132
|
rows = datasetRows;
|
|
@@ -144,7 +138,6 @@ export async function renderChart(input, args, exec) {
|
|
|
144
138
|
rows = extracted.rows;
|
|
145
139
|
meta = { source_kind: 'path' };
|
|
146
140
|
}
|
|
147
|
-
// ── 有界化 ───────────────────────────────────────────────────────────────
|
|
148
141
|
const chartId = newChartId();
|
|
149
142
|
const payload = buildChartPayload({ chart_id: chartId, spec, rows, meta });
|
|
150
143
|
const library = loadVendoredChartLibrary();
|
|
@@ -162,8 +155,7 @@ export async function renderChart(input, args, exec) {
|
|
|
162
155
|
signal: exec.signal,
|
|
163
156
|
});
|
|
164
157
|
const pathOf = (name) => written.find((file) => file.name === name)?.path ?? `${dir}/${name}`;
|
|
165
|
-
//
|
|
166
|
-
// 登记失败(服务缺失/实现异常)不阻断出图——图已经落盘了,降级成"只有文件路径"。
|
|
158
|
+
// Register the absolute series.json path with the host route; failure degrades to file paths only.
|
|
167
159
|
let chartUrl = null;
|
|
168
160
|
const seriesFile = written.find((file) => file.name === 'series.json');
|
|
169
161
|
if (input.charts && seriesFile !== undefined) {
|
|
@@ -191,14 +183,11 @@ export async function renderChart(input, args, exec) {
|
|
|
191
183
|
html_path: pathOf('chart.html'),
|
|
192
184
|
created_at: generatedAt,
|
|
193
185
|
});
|
|
194
|
-
//
|
|
195
|
-
// a stable receipt; production apply() always injects the registry.
|
|
186
|
+
// Unit callers without the registry still get a stable receipt.
|
|
196
187
|
const chartRef = artifact?.chart_ref ?? `chart_${chartId.slice(3)}`;
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
// 是闭集,非 first-party 类型会让整份会话在冷加载时打不开(事故记录见 src/chart/events.ts)。
|
|
201
|
-
// 事件不进模型消息历史。呈现通道可选:写不进去不影响图表产物与回执。
|
|
188
|
+
// Register as this turn's deliverable via first-party deliverables/presented — never a custom
|
|
189
|
+
// event type (closed vocabulary breaks cold load; see src/chart/events.ts). Owner scope matches
|
|
190
|
+
// chart_ref; these events never enter model message history.
|
|
202
191
|
let deliverableTrace;
|
|
203
192
|
try {
|
|
204
193
|
const publisher = input.chartEvents?.();
|
|
@@ -219,10 +208,7 @@ export async function renderChart(input, args, exec) {
|
|
|
219
208
|
catch (error) {
|
|
220
209
|
deliverableTrace = `交付登记抛错(已吞掉,不影响出图):${error instanceof Error ? error.message : String(error)}`;
|
|
221
210
|
}
|
|
222
|
-
//
|
|
223
|
-
// 而宿主终端不总能拿到(cordis logger 只进内存),所以留一条可从磁盘定位的出口;
|
|
224
|
-
// 但它属于调试产物,不该出现在对外版本的 workspace 里。
|
|
225
|
-
// 需要时用 `CAPITAL_CHART_TRACE=1` 启动宿主,图表产物目录会多出 deliverable-trace.txt。
|
|
211
|
+
// Trace file only under CAPITAL_CHART_TRACE=1; a debugging aid, not a release artifact.
|
|
226
212
|
if (chartTraceEnabled()) {
|
|
227
213
|
try {
|
|
228
214
|
await input.store.writeWorkspaceFiles({
|
|
@@ -233,7 +219,7 @@ export async function renderChart(input, args, exec) {
|
|
|
233
219
|
});
|
|
234
220
|
}
|
|
235
221
|
catch {
|
|
236
|
-
//
|
|
222
|
+
// Trace write failures never affect chart output.
|
|
237
223
|
}
|
|
238
224
|
}
|
|
239
225
|
return {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `tools/chart-render` row: render_chart only. Row-level expression of "only the
|
|
3
|
+
* visualization specialist holds the chart pipeline"; shares token/artifact state with
|
|
4
|
+
* `tools/chart-source`.
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
7
|
+
export declare const name = "capital-chart-render-tools";
|
|
8
|
+
export declare const inject: string[];
|
|
9
|
+
export declare function apply(ctx: Context): void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createChartEventPublisher } from './chart/events.js';
|
|
2
|
+
import { chartEventContextOf } from './chart/event-context.js';
|
|
3
|
+
import { registerChartTool } from './chart/tool.js';
|
|
4
|
+
import { capitalDatasetStore } from './runtime.js';
|
|
5
|
+
import { capitalSharedRuntimeOf } from './chart-source-entry.js';
|
|
6
|
+
export const name = 'capital-chart-render-tools';
|
|
7
|
+
export const inject = ['tools'];
|
|
8
|
+
export function apply(ctx) {
|
|
9
|
+
const shared = capitalSharedRuntimeOf(ctx);
|
|
10
|
+
registerChartTool(ctx, {
|
|
11
|
+
store: capitalDatasetStore(ctx),
|
|
12
|
+
sourceTokens: shared.sourceTokens,
|
|
13
|
+
artifacts: shared.artifacts,
|
|
14
|
+
charts: () => ctx.get('capitalCharts'),
|
|
15
|
+
chartEvents: () => createChartEventPublisher(chartEventContextOf(ctx)),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `tools/chart-source` row: prepare_chart_source. The token store must be the same
|
|
3
|
+
* instance used by `tools/chart-render`; separate stores make `render_chart` always
|
|
4
|
+
* report `chart_source_expired` without throwing. Prefer the host-plane runtime, fall
|
|
5
|
+
* back to the root-keyed module cache.
|
|
6
|
+
*/
|
|
7
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
8
|
+
import { type CapitalShared } from './runtime.js';
|
|
9
|
+
export declare const name = "capital-chart-source-tools";
|
|
10
|
+
export declare const inject: string[];
|
|
11
|
+
export declare function apply(ctx: Context): void;
|
|
12
|
+
/** Host row first, root-keyed module cache as fallback (monolith carriers). */
|
|
13
|
+
export declare function capitalSharedRuntimeOf(ctx: Context): CapitalShared;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { registerChartSourceTool } from './chart/source-token.js';
|
|
2
|
+
import { capitalDatasetStore, capitalSharedRuntime } from './runtime.js';
|
|
3
|
+
export const name = 'capital-chart-source-tools';
|
|
4
|
+
export const inject = ['tools'];
|
|
5
|
+
export function apply(ctx) {
|
|
6
|
+
const shared = capitalSharedRuntimeOf(ctx);
|
|
7
|
+
registerChartSourceTool(ctx, { store: capitalDatasetStore(ctx), tokens: shared.sourceTokens });
|
|
8
|
+
}
|
|
9
|
+
/** Host row first, root-keyed module cache as fallback (monolith carriers). */
|
|
10
|
+
export function capitalSharedRuntimeOf(ctx) {
|
|
11
|
+
try {
|
|
12
|
+
const hostShared = ctx.get('capitalChartRuntime');
|
|
13
|
+
if (hostShared !== undefined)
|
|
14
|
+
return hostShared;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// Monolith carrier without the host row.
|
|
18
|
+
}
|
|
19
|
+
return capitalSharedRuntime(ctx);
|
|
20
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `tools/collect` row: request_data / list_capabilities / describe_capability /
|
|
3
|
+
* dc_status backed by hub + 128 sources. The preset isolates hub/store to this row;
|
|
4
|
+
* time tools live in `tools/time`.
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
7
|
+
export declare const name = "capital-collect-tools";
|
|
8
|
+
export declare const inject: string[];
|
|
9
|
+
export declare function apply(ctx: Context): void;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createCollectorRuntime } from './collector-runtime.js';
|
|
2
|
+
import { registerDataCollectorTools } from './data-collector/tools.js';
|
|
3
|
+
import { readCollectorConfig } from './capital-settings.js';
|
|
4
|
+
export const name = 'capital-collect-tools';
|
|
5
|
+
export const inject = ['tools'];
|
|
6
|
+
export function apply(ctx) {
|
|
7
|
+
const config = readCollectorConfig(ctx);
|
|
8
|
+
const { hub, diagnostics } = createCollectorRuntime(ctx, config);
|
|
9
|
+
registerDataCollectorTools(ctx, hub, diagnostics);
|
|
10
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared data_collector assembly (store + hub + 128 sources). Both `tools/collect` and
|
|
3
|
+
* the monolith need it; duplicating would drift capability registration, credential
|
|
4
|
+
* probes and time tools. Collector domain only — no dataset/chart/watchlist tools.
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
7
|
+
import { provideDataCollectorHub } from './data-collector/hub.js';
|
|
8
|
+
import { type DataCollectorDiagnostics } from './data-collector/tools.js';
|
|
9
|
+
import { capitalDatasetStore } from './runtime.js';
|
|
10
|
+
export interface CollectorConfig {
|
|
11
|
+
fuyaoCredentialRef?: string;
|
|
12
|
+
ftshareCredentialRef?: string;
|
|
13
|
+
/** Flat projection of `retriever.windDocs.credentialRef`. */
|
|
14
|
+
windCredentialRef?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface CollectorRuntime {
|
|
17
|
+
store: ReturnType<typeof capitalDatasetStore>;
|
|
18
|
+
hub: ReturnType<typeof provideDataCollectorHub>;
|
|
19
|
+
/** Injected into `dc_status` by the caller that owns the `tools/collect` row. */
|
|
20
|
+
diagnostics: DataCollectorDiagnostics;
|
|
21
|
+
}
|
|
22
|
+
export declare function createCollectorRuntime(ctx: Context, config?: CollectorConfig): CollectorRuntime;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { provideDataCollectorHub } from './data-collector/hub.js';
|
|
2
|
+
import { capitalDatasetStore } from './runtime.js';
|
|
3
|
+
import { rootPolicyProbes } from './agents/root-tool-policy.js';
|
|
4
|
+
import { rolePolicyProbes } from './agents/role-tool-policy.js';
|
|
5
|
+
import { createTencentSources } from './sources/tencent-http.js';
|
|
6
|
+
import { createEastmoneySources } from './sources/eastmoney-http.js';
|
|
7
|
+
import { createFuyaoRestSources, resolveFuyaoApiKey } from './sources/fuyao-rest.js';
|
|
8
|
+
import { createFtshareSources, resolveFtshareApiKey } from './sources/ftshare-rest.js';
|
|
9
|
+
import { createWindSources } from './sources/wind-mcp.js';
|
|
10
|
+
import { createWindCredentialResolver } from './retriever-runtime.js';
|
|
11
|
+
function diagnosticsFor(ctx, config) {
|
|
12
|
+
const fuyaoCredentialRef = config.fuyaoCredentialRef || 'FUYAO_API_KEY';
|
|
13
|
+
return {
|
|
14
|
+
probeApiKey: async () => {
|
|
15
|
+
try {
|
|
16
|
+
const credentials = ctx.get('credentials');
|
|
17
|
+
if (credentials?.resolve) {
|
|
18
|
+
const resolved = await credentials.resolve(fuyaoCredentialRef);
|
|
19
|
+
if (resolved?.value)
|
|
20
|
+
return { present: true, source: resolved.source ?? `credentials(${fuyaoCredentialRef})` };
|
|
21
|
+
}
|
|
22
|
+
const processLike = globalThis.process;
|
|
23
|
+
if (processLike?.env?.[fuyaoCredentialRef])
|
|
24
|
+
return { present: true, source: `env(${fuyaoCredentialRef})` };
|
|
25
|
+
return { present: false, source: null };
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return { present: false, source: null, error: error instanceof Error ? error.message : String(error) };
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
// dc_status readout for root/role convergence; desktop does not persist host logs.
|
|
32
|
+
// Keep the real session shape: opened root and delegated child converge differently.
|
|
33
|
+
rootPolicy: () => [
|
|
34
|
+
...rootPolicyProbes().map((probe) => ({ ...probe, allowed: [] })),
|
|
35
|
+
...rolePolicyProbes().map((probe) => ({
|
|
36
|
+
agentId: probe.agentId,
|
|
37
|
+
root: probe.root,
|
|
38
|
+
presetId: probe.presetId,
|
|
39
|
+
outcome: probe.outcome,
|
|
40
|
+
// Roles have no allow-list (family rows define the surface); report sibling
|
|
41
|
+
// denials and call-level guard rejections.
|
|
42
|
+
allowed: [],
|
|
43
|
+
denied: probe.denied,
|
|
44
|
+
failed: probe.failed,
|
|
45
|
+
})),
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Registers all 128 sources; registration is structural, credentials resolve per call. */
|
|
50
|
+
function registerSources(ctx, hub, config) {
|
|
51
|
+
ctx.effect(() => {
|
|
52
|
+
const sources = createTencentSources();
|
|
53
|
+
const disposers = sources.map((dataSource) => hub.registerSource(dataSource));
|
|
54
|
+
ctx.logger.info(`capital-generation: 已注册 ${disposers.length} 个腾讯数据源(tencent_*)`);
|
|
55
|
+
return () => { for (const dispose of disposers)
|
|
56
|
+
dispose(); };
|
|
57
|
+
}, 'capital-generation.tencent-sources()');
|
|
58
|
+
ctx.effect(() => {
|
|
59
|
+
const sources = createEastmoneySources();
|
|
60
|
+
const disposers = sources.map((dataSource) => hub.registerSource(dataSource));
|
|
61
|
+
ctx.logger.info(`capital-generation: 已注册 ${disposers.length} 个东方财富数据源(eastmoney_*)`);
|
|
62
|
+
return () => { for (const dispose of disposers)
|
|
63
|
+
dispose(); };
|
|
64
|
+
}, 'capital-generation.eastmoney-sources()');
|
|
65
|
+
ctx.effect(() => {
|
|
66
|
+
const sources = createFuyaoRestSources(() => resolveFuyaoApiKey(ctx, config.fuyaoCredentialRef || 'FUYAO_API_KEY'));
|
|
67
|
+
const disposers = sources.map((dataSource) => hub.registerSource(dataSource));
|
|
68
|
+
ctx.logger.info(`capital-generation: 已注册 ${disposers.length} 个同花顺数据源(key 每次调用现取)`);
|
|
69
|
+
return () => { for (const dispose of disposers)
|
|
70
|
+
dispose(); };
|
|
71
|
+
}, 'capital-generation.fuyao-sources()');
|
|
72
|
+
ctx.effect(() => {
|
|
73
|
+
const sources = createFtshareSources(() => resolveFtshareApiKey(ctx, config.ftshareCredentialRef || 'FTSHARE_API_KEY'));
|
|
74
|
+
const disposers = sources.map((dataSource) => hub.registerSource(dataSource));
|
|
75
|
+
ctx.logger.info(`capital-generation: 已注册 ${disposers.length} 个 FTShare 数据源(ftshare_*,key 每次调用现取)`);
|
|
76
|
+
return () => { for (const dispose of disposers)
|
|
77
|
+
dispose(); };
|
|
78
|
+
}, 'capital-generation.ftshare-sources()');
|
|
79
|
+
const wind = createWindCredentialResolver(ctx, config.windCredentialRef);
|
|
80
|
+
ctx.effect(() => {
|
|
81
|
+
const sources = createWindSources(wind.resolve);
|
|
82
|
+
const disposers = sources.map((dataSource) => hub.registerSource(dataSource));
|
|
83
|
+
ctx.logger.info(`capital-generation: 已注册 ${disposers.length} 个 Wind 数据源(wind_*,与 wind_docs 共用 ${wind.credentialRef})`);
|
|
84
|
+
return () => { for (const dispose of disposers)
|
|
85
|
+
dispose(); };
|
|
86
|
+
}, 'capital-generation.wind-sources()');
|
|
87
|
+
}
|
|
88
|
+
export function createCollectorRuntime(ctx, config = {}) {
|
|
89
|
+
const store = capitalDatasetStore(ctx);
|
|
90
|
+
// The collector preset isolates these services; keep them in the realm to avoid
|
|
91
|
+
// process-wide name collisions across presets.
|
|
92
|
+
ctx.provide('datasetStore', store);
|
|
93
|
+
const hub = provideDataCollectorHub(ctx, { store });
|
|
94
|
+
registerSources(ctx, hub, config);
|
|
95
|
+
// Builds runtime only; model tools belong to the `tools/collect` and `tools/time` rows.
|
|
96
|
+
return { store, hub, diagnostics: diagnosticsFor(ctx, config) };
|
|
97
|
+
}
|
|
@@ -1,38 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Dataset
|
|
3
|
-
*
|
|
4
|
-
* 模型经常把数组/对象参数序列化成 JSON 字符串再传(实测:`"select": "[\"date_ms\"]"`、
|
|
5
|
-
* `"group_by": "[]"`、`"aggregates": "[{...}]"`、`"limit": "1"`),于是参数其实是对的,
|
|
6
|
-
* 却报 `query_spec_invalid: select must contain 1-32 column names`,模型只能反复试错。
|
|
7
|
-
*
|
|
8
|
-
* 这里在**工具边界**做一次宽容解析,查询引擎本身保持严格:能解析成正确类型就放行,
|
|
9
|
-
* 解析不了就原样交给校验器报错(错误信息仍指向真实问题)。
|
|
10
|
-
*
|
|
11
|
-
* 本模块由 `query_dataset`(dataset-tools.ts)与 `describe_dataset`(describe.ts)共用,
|
|
12
|
-
* 保证两个工具对同一种"字符串化的 JSON"给出**完全一致**的宽容度——两份实现漂移会让模型
|
|
13
|
-
* 在换工具后重新踩同一个坑。
|
|
2
|
+
* Lenient parameter parsing at the Dataset tool boundary: models often send stringified JSON.
|
|
3
|
+
* Shared by query_dataset and describe_dataset so both accept exactly the same shapes.
|
|
14
4
|
*/
|
|
15
5
|
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
16
6
|
export declare function parseJsonContainer(value: unknown): unknown;
|
|
17
|
-
/**
|
|
7
|
+
/** Accept a real array, a JSON array string, or a single column name. */
|
|
18
8
|
export declare function coerceNameList(value: unknown): unknown;
|
|
19
|
-
/** 对象数组:接受真数组、JSON 数组字符串,或单个对象(对象本身或其 JSON 字符串)。 */
|
|
20
9
|
export declare function coerceObjectList(value: unknown): unknown;
|
|
21
10
|
export declare function coerceLimit(value: unknown): unknown;
|
|
22
11
|
export declare const QUERY_NAME_LIST_FIELDS: readonly ["select", "group_by"];
|
|
23
12
|
export declare const QUERY_OBJECT_LIST_FIELDS: readonly ["filters", "aggregates", "order_by"];
|
|
24
|
-
/**
|
|
13
|
+
/** Coerce QuerySpec array/object fields and limit to engine types; leave other fields untouched. */
|
|
25
14
|
export declare function coerceQueryShape(input: Record<string, unknown>): Record<string, unknown>;
|
|
26
|
-
/** dataset_id
|
|
15
|
+
/** Reject an invalid dataset_id at the tool boundary; never pass it to the store. */
|
|
27
16
|
export declare function readDatasetId(value: unknown): string;
|
|
28
|
-
/** task_id 是可选元数据:空值等同于未提供,只做长度上限校验。 */
|
|
29
17
|
export declare function readOptionalTaskId(value: unknown): string | undefined;
|
|
30
|
-
/** 可选的字符串参数(time_column / primary_key):非字符串一律视为未提供。 */
|
|
31
18
|
export declare function readOptionalString(value: unknown): string | undefined;
|
|
32
|
-
/**
|
|
33
|
-
* 列名过滤参数(describe_dataset 的 `columns_of_interest`)。
|
|
34
|
-
*
|
|
35
|
-
* 接受真数组 / JSON 数组字符串 / 单个列名字符串;解析结果不是数组就抛错——静默忽略一个
|
|
36
|
-
* 写错的过滤条件会让模型以为"结果已经收窄",而实际返回的是全量(或反过来),所以这里响亮失败。
|
|
37
|
-
*/
|
|
19
|
+
/** columns_of_interest: accept array/JSON/single name; a wrong shape fails loudly rather than silently widening results. */
|
|
38
20
|
export declare function readOptionalColumnList(value: unknown): string[] | undefined;
|
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
import { DatasetStoreError } from './store.js';
|
|
2
2
|
/**
|
|
3
|
-
* Dataset
|
|
4
|
-
*
|
|
5
|
-
* 模型经常把数组/对象参数序列化成 JSON 字符串再传(实测:`"select": "[\"date_ms\"]"`、
|
|
6
|
-
* `"group_by": "[]"`、`"aggregates": "[{...}]"`、`"limit": "1"`),于是参数其实是对的,
|
|
7
|
-
* 却报 `query_spec_invalid: select must contain 1-32 column names`,模型只能反复试错。
|
|
8
|
-
*
|
|
9
|
-
* 这里在**工具边界**做一次宽容解析,查询引擎本身保持严格:能解析成正确类型就放行,
|
|
10
|
-
* 解析不了就原样交给校验器报错(错误信息仍指向真实问题)。
|
|
11
|
-
*
|
|
12
|
-
* 本模块由 `query_dataset`(dataset-tools.ts)与 `describe_dataset`(describe.ts)共用,
|
|
13
|
-
* 保证两个工具对同一种"字符串化的 JSON"给出**完全一致**的宽容度——两份实现漂移会让模型
|
|
14
|
-
* 在换工具后重新踩同一个坑。
|
|
3
|
+
* Lenient parameter parsing at the Dataset tool boundary: models often send stringified JSON.
|
|
4
|
+
* Shared by query_dataset and describe_dataset so both accept exactly the same shapes.
|
|
15
5
|
*/
|
|
16
6
|
export function isRecord(value) {
|
|
17
7
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
@@ -29,7 +19,7 @@ export function parseJsonContainer(value) {
|
|
|
29
19
|
return undefined;
|
|
30
20
|
}
|
|
31
21
|
}
|
|
32
|
-
/**
|
|
22
|
+
/** Accept a real array, a JSON array string, or a single column name. */
|
|
33
23
|
export function coerceNameList(value) {
|
|
34
24
|
if (Array.isArray(value))
|
|
35
25
|
return value;
|
|
@@ -43,7 +33,6 @@ export function coerceNameList(value) {
|
|
|
43
33
|
}
|
|
44
34
|
return value;
|
|
45
35
|
}
|
|
46
|
-
/** 对象数组:接受真数组、JSON 数组字符串,或单个对象(对象本身或其 JSON 字符串)。 */
|
|
47
36
|
export function coerceObjectList(value) {
|
|
48
37
|
if (Array.isArray(value))
|
|
49
38
|
return value;
|
|
@@ -64,7 +53,7 @@ export function coerceLimit(value) {
|
|
|
64
53
|
}
|
|
65
54
|
export const QUERY_NAME_LIST_FIELDS = ['select', 'group_by'];
|
|
66
55
|
export const QUERY_OBJECT_LIST_FIELDS = ['filters', 'aggregates', 'order_by'];
|
|
67
|
-
/**
|
|
56
|
+
/** Coerce QuerySpec array/object fields and limit to engine types; leave other fields untouched. */
|
|
68
57
|
export function coerceQueryShape(input) {
|
|
69
58
|
const shape = { ...input };
|
|
70
59
|
for (const field of QUERY_NAME_LIST_FIELDS)
|
|
@@ -77,14 +66,13 @@ export function coerceQueryShape(input) {
|
|
|
77
66
|
shape.limit = coerceLimit(shape.limit);
|
|
78
67
|
return shape;
|
|
79
68
|
}
|
|
80
|
-
/** dataset_id
|
|
69
|
+
/** Reject an invalid dataset_id at the tool boundary; never pass it to the store. */
|
|
81
70
|
export function readDatasetId(value) {
|
|
82
71
|
if (typeof value !== 'string' || !/^[A-Za-z0-9_-]{1,128}$/.test(value)) {
|
|
83
72
|
throw new DatasetStoreError('dataset_id_invalid', 'dataset_id is invalid');
|
|
84
73
|
}
|
|
85
74
|
return value;
|
|
86
75
|
}
|
|
87
|
-
/** task_id 是可选元数据:空值等同于未提供,只做长度上限校验。 */
|
|
88
76
|
export function readOptionalTaskId(value) {
|
|
89
77
|
if (value === undefined || value === null || value === '')
|
|
90
78
|
return undefined;
|
|
@@ -93,16 +81,10 @@ export function readOptionalTaskId(value) {
|
|
|
93
81
|
}
|
|
94
82
|
return value;
|
|
95
83
|
}
|
|
96
|
-
/** 可选的字符串参数(time_column / primary_key):非字符串一律视为未提供。 */
|
|
97
84
|
export function readOptionalString(value) {
|
|
98
85
|
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
99
86
|
}
|
|
100
|
-
/**
|
|
101
|
-
* 列名过滤参数(describe_dataset 的 `columns_of_interest`)。
|
|
102
|
-
*
|
|
103
|
-
* 接受真数组 / JSON 数组字符串 / 单个列名字符串;解析结果不是数组就抛错——静默忽略一个
|
|
104
|
-
* 写错的过滤条件会让模型以为"结果已经收窄",而实际返回的是全量(或反过来),所以这里响亮失败。
|
|
105
|
-
*/
|
|
87
|
+
/** columns_of_interest: accept array/JSON/single name; a wrong shape fails loudly rather than silently widening results. */
|
|
106
88
|
export function readOptionalColumnList(value) {
|
|
107
89
|
if (value === undefined || value === null || value === '')
|
|
108
90
|
return undefined;
|
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
2
|
import { WorkspaceDatasetStore } from './store.js';
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Dataset read family. writeProfile is off by default: profiles are host-computed, and the tool is
|
|
5
|
+
* absent from every family row; the monolithic entry opts in explicitly.
|
|
6
|
+
*/
|
|
7
|
+
export declare function registerDatasetTools(ctx: Context, store: WorkspaceDatasetStore, options?: {
|
|
8
|
+
writeProfile?: boolean;
|
|
9
|
+
}): void;
|