@v587d/capital-generation 2.8.3 → 2.9.1

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.
Files changed (169) hide show
  1. package/CHANGELOG.md +81 -1
  2. package/README.md +40 -19
  3. package/chart-ui/README.md +5 -4
  4. package/chart-ui/client.src.cjs +7 -16
  5. package/chart-ui/index.js +12 -33
  6. package/chart-ui/package.json +2 -2
  7. package/config/client.js +18 -39
  8. package/config/client.src.cjs +79 -217
  9. package/config/index.js +22 -72
  10. package/config/package.json +2 -2
  11. package/cordis.patch.yml +46 -51
  12. package/lib/agents/bash-guard.d.ts +9 -55
  13. package/lib/agents/bash-guard.js +27 -67
  14. package/lib/agents/preset-roster.d.ts +22 -0
  15. package/lib/agents/preset-roster.js +87 -0
  16. package/lib/agents/role-tool-policy.d.ts +46 -0
  17. package/lib/agents/role-tool-policy.js +205 -0
  18. package/lib/agents/root-tool-policy.d.ts +28 -124
  19. package/lib/agents/root-tool-policy.js +164 -237
  20. package/lib/agents/selected-skills-provider.d.ts +2 -6
  21. package/lib/agents/selected-skills-provider.js +27 -84
  22. package/lib/capital-settings.d.ts +27 -0
  23. package/lib/capital-settings.js +23 -0
  24. package/lib/chart/artifact-ref.d.ts +13 -8
  25. package/lib/chart/artifact-ref.js +27 -10
  26. package/lib/chart/downsample.d.ts +2 -5
  27. package/lib/chart/downsample.js +3 -8
  28. package/lib/chart/errors.d.ts +3 -7
  29. package/lib/chart/errors.js +1 -2
  30. package/lib/chart/event-context.d.ts +8 -0
  31. package/lib/chart/event-context.js +39 -0
  32. package/lib/chart/events.d.ts +21 -78
  33. package/lib/chart/events.js +43 -189
  34. package/lib/chart/html.d.ts +2 -5
  35. package/lib/chart/html.js +8 -15
  36. package/lib/chart/present-tool.d.ts +28 -0
  37. package/lib/chart/present-tool.js +128 -0
  38. package/lib/chart/runtime.d.ts +5 -18
  39. package/lib/chart/runtime.js +14 -34
  40. package/lib/chart/series.d.ts +10 -17
  41. package/lib/chart/series.js +22 -47
  42. package/lib/chart/source-token.d.ts +5 -13
  43. package/lib/chart/source-token.js +18 -17
  44. package/lib/chart/spec.d.ts +6 -12
  45. package/lib/chart/spec.js +7 -31
  46. package/lib/chart/tool.d.ts +7 -29
  47. package/lib/chart/tool.js +15 -25
  48. package/lib/chart-render-entry.d.ts +10 -0
  49. package/lib/chart-render-entry.js +14 -0
  50. package/lib/chart-source-entry.d.ts +13 -0
  51. package/lib/chart-source-entry.js +20 -0
  52. package/lib/collect-entry.d.ts +9 -0
  53. package/lib/collect-entry.js +10 -0
  54. package/lib/collector-runtime.d.ts +22 -0
  55. package/lib/collector-runtime.js +97 -0
  56. package/lib/data-collector/args.d.ts +6 -24
  57. package/lib/data-collector/args.js +6 -24
  58. package/lib/data-collector/dataset-tools.d.ts +7 -1
  59. package/lib/data-collector/dataset-tools.js +27 -56
  60. package/lib/data-collector/describe.d.ts +8 -33
  61. package/lib/data-collector/describe.js +17 -70
  62. package/lib/data-collector/hub.d.ts +27 -85
  63. package/lib/data-collector/hub.js +19 -85
  64. package/lib/data-collector/query-time.d.ts +7 -26
  65. package/lib/data-collector/query-time.js +8 -27
  66. package/lib/data-collector/store.d.ts +41 -117
  67. package/lib/data-collector/store.js +32 -111
  68. package/lib/data-collector/time-axis.d.ts +16 -76
  69. package/lib/data-collector/time-axis.js +25 -111
  70. package/lib/data-collector/tools.d.ts +6 -6
  71. package/lib/data-collector/tools.js +34 -43
  72. package/lib/dataset-read-entry.d.ts +9 -0
  73. package/lib/dataset-read-entry.js +7 -0
  74. package/lib/index.d.ts +7 -117
  75. package/lib/index.js +43 -422
  76. package/lib/net/eastmoney-client.d.ts +13 -65
  77. package/lib/net/eastmoney-client.js +12 -53
  78. package/lib/net/throttle.d.ts +2 -13
  79. package/lib/net/throttle.js +3 -14
  80. package/lib/ocr/client.d.ts +12 -21
  81. package/lib/ocr/client.js +23 -40
  82. package/lib/ocr/markdown.d.ts +20 -26
  83. package/lib/ocr/markdown.js +23 -36
  84. package/lib/ocr/store.d.ts +14 -47
  85. package/lib/ocr/store.js +15 -45
  86. package/lib/ocr-entry.d.ts +9 -0
  87. package/lib/ocr-entry.js +10 -0
  88. package/lib/policy-entry.d.ts +17 -0
  89. package/lib/policy-entry.js +22 -0
  90. package/lib/present-entry.d.ts +9 -0
  91. package/lib/present-entry.js +13 -0
  92. package/lib/preset-claim.d.ts +22 -0
  93. package/lib/preset-claim.js +24 -0
  94. package/lib/preset-ids.d.ts +38 -0
  95. package/lib/preset-ids.js +46 -0
  96. package/lib/retriever-config.d.ts +37 -0
  97. package/lib/retriever-config.js +36 -0
  98. package/lib/retriever-runtime.d.ts +31 -0
  99. package/lib/retriever-runtime.js +82 -0
  100. package/lib/root-extras-entry.d.ts +17 -0
  101. package/lib/root-extras-entry.js +25 -0
  102. package/lib/runtime-entry.d.ts +15 -0
  103. package/lib/runtime-entry.js +5 -0
  104. package/lib/runtime.d.ts +21 -0
  105. package/lib/runtime.js +33 -0
  106. package/lib/search-entry.d.ts +9 -0
  107. package/lib/search-entry.js +9 -0
  108. package/lib/sources/eastmoney-http.js +20 -68
  109. package/lib/sources/ftshare-rest.d.ts +6 -34
  110. package/lib/sources/ftshare-rest.js +84 -292
  111. package/lib/sources/fuyao-core.d.ts +8 -18
  112. package/lib/sources/fuyao-core.js +10 -21
  113. package/lib/sources/fuyao-rest.d.ts +2 -6
  114. package/lib/sources/fuyao-rest.js +23 -102
  115. package/lib/sources/security-code.d.ts +6 -12
  116. package/lib/sources/security-code.js +6 -12
  117. package/lib/sources/security-identity.d.ts +1 -4
  118. package/lib/sources/security-identity.js +3 -8
  119. package/lib/sources/tencent-http.js +14 -28
  120. package/lib/sources/tencent-public-core.d.ts +8 -60
  121. package/lib/sources/tencent-public-core.js +12 -77
  122. package/lib/sources/wind-mcp.d.ts +0 -5
  123. package/lib/sources/wind-mcp.js +23 -78
  124. package/lib/sources-entry.d.ts +9 -0
  125. package/lib/sources-entry.js +9 -0
  126. package/lib/time/tools.d.ts +8 -31
  127. package/lib/time/tools.js +30 -66
  128. package/lib/time-entry.d.ts +9 -0
  129. package/lib/time-entry.js +7 -0
  130. package/lib/tool-exec.d.ts +45 -11
  131. package/lib/tool-exec.js +59 -6
  132. package/lib/user-customization.d.ts +14 -0
  133. package/lib/user-customization.js +47 -0
  134. package/lib/watchlist/tools.d.ts +3 -13
  135. package/lib/watchlist/tools.js +10 -30
  136. package/lib/watchlist-entry.d.ts +9 -0
  137. package/lib/watchlist-entry.js +6 -0
  138. package/lib/web-retriever/engines.d.ts +6 -11
  139. package/lib/web-retriever/engines.js +9 -14
  140. package/lib/web-retriever/html-markdown.d.ts +14 -34
  141. package/lib/web-retriever/html-markdown.js +39 -75
  142. package/lib/web-retriever/local-fetch.d.ts +22 -58
  143. package/lib/web-retriever/local-fetch.js +33 -77
  144. package/lib/web-retriever/retriever.d.ts +6 -18
  145. package/lib/web-retriever/retriever.js +7 -7
  146. package/lib/web-retriever/sources.d.ts +37 -124
  147. package/lib/web-retriever/sources.js +64 -196
  148. package/lib/web-retriever/tools.d.ts +43 -27
  149. package/lib/web-retriever/tools.js +99 -137
  150. package/lib/web-retriever/wind-client.d.ts +14 -31
  151. package/lib/web-retriever/wind-client.js +16 -32
  152. package/locale/zh.json +1 -1
  153. package/package.json +72 -7
  154. package/preset/capital-generation/README.md +87 -53
  155. package/preset/capital-generation/agent.patch.yml +532 -337
  156. package/preset/capital-generation/skills/capital-chart-protocol/SKILL.md +6 -4
  157. package/preset/capital-generation/skills/capital-data-profile/SKILL.md +2 -2
  158. package/preset/capital-generation/skills/capital-data-protocol/SKILL.md +1 -1
  159. package/preset/capital-generation/skills/capital-equity-research/SKILL.md +4 -2
  160. package/preset/capital-generation/skills/capital-event-impact/SKILL.md +1 -1
  161. package/preset/capital-generation/skills/capital-orchestration/SKILL.md +30 -33
  162. package/preset/capital-generation/skills/capital-portfolio-review/SKILL.md +1 -1
  163. package/preset/capital-generation/skills/capital-visualization-protocol/SKILL.md +14 -16
  164. package/preset/capital-generation/skills/capital-web-evidence/SKILL.md +1 -1
  165. package/watchlist/client.js +15 -148
  166. package/watchlist/client.src.cjs +148 -662
  167. package/watchlist/holding-rules.js +11 -38
  168. package/watchlist/index.js +127 -397
  169. package/watchlist/package.json +2 -2
@@ -3,49 +3,27 @@ import { readFileSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { agentSessionId, isRootAgent } from './root-tool-policy.js';
6
- // The catalog is a shipped data file outside the TypeScript root; runtime keeps this
7
- // import so the published lib resolves the same metadata beside the pinned snapshots.
6
+ // Catalog is package data outside the TS root; the runtime import keeps lib resolving it beside pinned snapshots.
8
7
  // @ts-expect-error catalog.js is intentionally maintained as package data.
9
8
  import { SELECTED_SKILL_CATALOG as CATALOG, SELECTED_SKILL_SKILL_DIRS, SELECTED_SKILL_TASK_IDS, selectedSkillsForTask } from '../../selected-skills/catalog.js';
10
9
  export const SELECTED_SKILL_CATALOG = CATALOG;
11
10
  const PROVIDER_NAME = 'capital-selected-skills';
12
- /** 本 preset 的身份:两条注册路径(`agent/created` 与 `agent-preset/selected`)共用一份。 */
13
11
  const CAPITAL_PRESET_ID = 'capital-generation';
14
- // skills 根按 catalog 的「仓库 → 上游 SKILL.md 相对路径」现算:第三颗快照的上游把 skill 放在
15
- // plugins/us-stock-analysis/skills,不在 <repo>/skills 底下。写死目录列表的表现不是报错,
16
- // 而是"用户在卡片上勾了那一格、技能永远不出现"(AGENTS.md §9.7)。
12
+ // Derive roots from the catalog: a hardcoded list silently hides checked boxes (upstream layouts differ).
17
13
  const SKILL_ROOTS = Object.entries(SELECTED_SKILL_SKILL_DIRS).map(([repository, skillsDir]) => fileURLToPath(new URL(`../../selected-skills/${repository}/${skillsDir}`, import.meta.url)));
18
- /**
19
- * 任务型内置 skill 的根目录(`lib/agents/` → 包根 → `preset/capital-generation/skills`)。
20
- *
21
- * 索引**不改磁盘**:`preset/.../SKILL.md` 一个字节都不动,正文在 `get()` 读取期拼上索引块。
22
- * 三个理由,缺一都不是洁癖:装好的包常常整棵只读(全局安装更糟);`skill-filesystem` 的 watcher
23
- * 会被写文件搅动,多会话并行时互相打架;`test/persona.test.mjs` 正钉着这些文件的 frontmatter 与
24
- * 体积,运行期改写会让那些闸门失去意义。
25
- */
14
+ /** Built-in task skill root; the index is appended at read time, never written to disk:
15
+ * installs may be read-only, watchers would churn, and tests pin frontmatter and size. */
26
16
  const TASK_SKILL_ROOT = fileURLToPath(new URL('../../preset/capital-generation/skills', import.meta.url));
27
- /**
28
- * 遮蔽候选的 rank。DSH 的 `compareIndexedCandidates` 先比 rank 再比 provider 顺序,**小的赢**;
29
- * 同层自定义根是 `CUSTOM_RANK = 300`,这里取 150 保证同层时压过 fs provider。
30
- *
31
- * 跨层本来也轮不到 rank:`dsh-agent-preset-registry` 把 preset 的 standing scope 挂成
32
- * **agent scope 的 parent**(`standingKey = scopeParentOf(agentKey)`),而 `chainLayers` 的注释写明
33
- * "farthest ancestor first, the nearest scope has the final word"、`scopeChainOf(...).reverse()` 后
34
- * 逐层 `merged.set(name, ...)` —— 本 provider 注册在 agent scope 里,是最近的一层,同名天然覆盖。
35
- * 两条机制取其一即可,取两条是因为遮蔽失败的症状是"索引静默消失",没有第二个人会发现。
36
- */
17
+ /** Smaller rank wins: 150 beats the same-layer custom root (300). The agent scope usually
18
+ * shadows anyway; both are cheap insurance because failure is silent. */
37
19
  const TASK_SKILL_SHADOW_RANK = 150;
38
20
  const INDEX_HEADING = '## 附:本轮可用的外部精选 Skill(索引)';
39
- /**
40
- * 索引块的框。它把**外部方法论**塞进了**宿主契约 skill** 的正文里,框架被拉平了,所以这四句必须
41
- * 写在块内而不是别的文档里:冲突判据、不可动摇的三处边界、以及"这里只有摘要"。
42
- */
21
+ /** Conflict precedence and the three untouchable boundaries must live inside the block. */
43
22
  const INDEX_FRAME = [
44
23
  '以下是你(用户)在设置里启用的外部精选 Skill,按能力标签与本次任务关联后列出。它们是**方法论材料**,',
45
24
  '需要正文时照常用 `skill` 工具按名加载。它们**不改变**本 skill 的证据门、工具委派路径、输出通道与报告形状;',
46
25
  '与本 skill 冲突时**以本 skill 为准**。这里只给名称与摘要,摘要不等于全文,未加载前不要照它行事。',
47
26
  ];
48
- /** 最小 frontmatter 读取:只要 `name` / `description` / 正文。完整策略解析仍归 fs provider。 */
49
27
  function readFrontmatter(raw) {
50
28
  const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(raw);
51
29
  if (match === null)
@@ -58,10 +36,7 @@ function readFrontmatter(raw) {
58
36
  }
59
37
  return { fields, body: raw.slice(match[0].length) };
60
38
  }
61
- /**
62
- * 载入三颗任务型 skill 的正文。**读不到就不遮蔽**:`list()` 不会列出这个候选,fs provider 照常
63
- * 供原文,用户看到的是"没有索引",而不是"这颗 skill 不见了"(AGENTS.md §9.7 ⑤ 失败要留余地)。
64
- */
39
+ /** Unreadable means no shadow, never a missing skill: the fs provider still serves the original. */
65
40
  function loadTaskSkillBases(root = TASK_SKILL_ROOT) {
66
41
  const bases = new Map();
67
42
  for (const name of SELECTED_SKILL_TASK_IDS) {
@@ -74,16 +49,11 @@ function loadTaskSkillBases(root = TASK_SKILL_ROOT) {
74
49
  bases.set(name, { name, description, content: parsed.body.trim(), directory: join(root, name) });
75
50
  }
76
51
  catch {
77
- // 读不到是 fail-open,不是错误路径:默认态(一颗都没启用)本来就不遮蔽任何东西。
78
52
  }
79
53
  }
80
54
  return bases;
81
55
  }
82
- /**
83
- * 索引块正文。**唯一实现**:条目集合来自 catalog 的 `selectedSkillsForTask`(与目录、闸门同源),
84
- * 描述用**运行时那一份**(上游 frontmatter,也就是会话目录里模型看到的那句话),不是设置卡片里
85
- * 策展过的中文摘要——同一个 skill 在同一段上下文里出现两种描述,只会让模型两处对照。
86
- */
56
+ /** Sole index renderer; descriptions use runtime frontmatter (what the model sees), not card summaries. */
87
57
  export function renderTaskSkillIndex(taskId, summaries) {
88
58
  if (summaries.length === 0)
89
59
  return undefined;
@@ -106,13 +76,11 @@ function selectedNames(selection) {
106
76
  function readSelection(source) {
107
77
  return typeof source === 'function' ? source() : source;
108
78
  }
109
- // 选择指纹只认 catalog 的 key 集合:pre-step 每轮拿它对比"上次交给注册表的选择",
110
- // 变了才失效目录缓存,未变就零成本放行。
79
+ // Fingerprint only the catalog key set: refresh the cache only when the selection changes.
111
80
  function selectionFingerprint(selection) {
112
81
  return SELECTED_SKILL_CATALOG.map(({ key }) => (selection[key] === true ? '1' : '0')).join('');
113
82
  }
114
- // agent/pre-step 是 dsh-agent 声明的事件,类型不在本包的 import 面里;按结构挂到注入后的
115
- // Agent context(其 scope tag 就是该 Agent,dispatch 的 scope filter 才放行这条监听)。
83
+ // `agent/pre-step` belongs to dsh-agent; attach via the injected Agent context so scope filtering admits it.
116
84
  function onPreStepEvent(ctx, listener) {
117
85
  const events = ctx;
118
86
  return events.on?.('agent/pre-step', listener);
@@ -127,19 +95,16 @@ function strictCapitalPreset(ctx, agent) {
127
95
  return false;
128
96
  }
129
97
  }
130
- /** Register explicitly selected community experiments on the Capital root Agent only. */
98
+ /** Registers selected community experiments on the Capital root Agent only. */
131
99
  export function registerRootSelectedSkills(ctx, selection, createFilesystemProvider = createFilesystemSkillProvider) {
132
- // Static callers retain the cheap all-disabled fast path. The Capital plugin
133
- // passes a reader so a settings toggle is observed at the next step of every
134
- // live root Agent: list()/get() filter against the current selection, and the
135
- // pre-step hook below refreshes the visible catalog when the selection changes.
100
+ // Static callers keep the all-off fast path; the plugin passes a reader so live toggles apply
101
+ // at the next step (list/get filter by selection, pre-step refreshes the visible catalog).
136
102
  if (typeof selection !== 'function' && selectedNames(selection).size === 0)
137
103
  return;
138
104
  const listenCtx = ctx.root ?? ctx;
139
105
  if (typeof listenCtx.on !== 'function')
140
106
  return;
141
- // 一个 root Agent 只注册一次:下面两条事件路径会在同一个 Agent 上重叠(建会话时就带 Capital
142
- // 预设 → `agent/created`;先按默认预设建、再 select 到 Capital → `agent-preset/selected`)。
107
+ // One registration per root Agent: both event paths can observe the same Agent.
143
108
  const registered = new WeakSet();
144
109
  const registerFor = async (agent) => {
145
110
  if (!agent || registered.has(agent))
@@ -154,8 +119,7 @@ export function registerRootSelectedSkills(ctx, selection, createFilesystemProvi
154
119
  ctx.logger?.warn?.('capital-generation: selected skills are unavailable (root Agent cannot inject skills)');
155
120
  return;
156
121
  }
157
- // agent/created 是串行事件、DSH 等监听器 settle 才继续创建:await 注入 Fiber,
158
- // provider 与 pre-step 钩子都在首轮 prompt 前落位(首轮 catalog 不与注册竞态)。
122
+ // agent/created is serial: DSH awaits listeners, so this inject lands before the first prompt.
159
123
  await agentCtx.inject(['skills'], (injectedCtx) => {
160
124
  const skills = injectedCtx.skills;
161
125
  if (typeof skills?.registerProvider !== 'function') {
@@ -164,9 +128,7 @@ export function registerRootSelectedSkills(ctx, selection, createFilesystemProvi
164
128
  }
165
129
  let filesystemProvider;
166
130
  let control;
167
- // 上次交给 skills 注册表的选择指纹。开关是 settings 偏好,不在 Agent 创建时定格:
168
- // 全关也照常注册 provider(否则会话创建后打开开关永远不出现),靠下面的 pre-step
169
- // 钩子每轮对齐设置。
131
+ // Register even when all toggles are off; otherwise a later enable would never surface. Pre-step aligns settings.
170
132
  let served = selectionFingerprint(readSelection(selection));
171
133
  injectedCtx.effect(() => {
172
134
  const unregister = skills.registerProvider((registration) => {
@@ -177,15 +139,11 @@ export function registerRootSelectedSkills(ctx, selection, createFilesystemProvi
177
139
  customSkillDirs: SKILL_ROOTS,
178
140
  watch: true,
179
141
  });
180
- // 遮蔽用的底稿只在注册时读一次(三份小文件,读不到就不遮蔽)。
181
142
  const taskBases = loadTaskSkillBases();
182
- // 上一次 `list()` 看到的"已启用精选"运行时摘要。`get()` 拼索引时读它,避免为了几行摘要
183
- // 再枚举一遍快照目录;为空就退回"没有索引",也就是把整颗让给 fs provider 的原文。
143
+ // Summaries from the last `list()`; `get()` reuses them instead of rescanning dirs.
184
144
  let runtimeSummaries = [];
185
145
  const indexFor = (taskId) => {
186
146
  const byName = new Map(runtimeSummaries.map((entry) => [entry.name, entry]));
187
- // 条目集合来自 catalog 的唯一实现(与目录、闸门同源),顺序取 catalog 顺序:
188
- // 同一仓库的几颗排在一起,读起来比按名字打散更像一份索引。
189
147
  const summaries = selectedSkillsForTask(taskId, [...byName.keys()])
190
148
  .map((entry) => byName.get(entry.name))
191
149
  .filter((entry) => entry !== undefined);
@@ -194,11 +152,9 @@ export function registerRootSelectedSkills(ctx, selection, createFilesystemProvi
194
152
  const provider = {
195
153
  name: PROVIDER_NAME,
196
154
  async list(options) {
197
- // 调用期现读:开关是 settings 偏好,不许在 Agent 创建时定格。
155
+ // Read at call time: toggles are settings, not fixed at Agent creation.
198
156
  const enabled = selectedNames(readSelection(selection));
199
- // 全关是默认常态:短路掉,不碰快照目录(filesystem watcher 在它的 list()
200
- // 里才启动,短路保证默认关闭的会话不起任何 watcher),**也不遮蔽任何内置 skill**:
201
- // 一颗都没启用时用户看到的必须与没有这个 provider 时逐字相同。
157
+ // All-off is the default: skip snapshot dirs (no watcher starts) and shadow nothing, matching no-provider output.
202
158
  if (enabled.size === 0) {
203
159
  runtimeSummaries = [];
204
160
  return [];
@@ -207,9 +163,7 @@ export function registerRootSelectedSkills(ctx, selection, createFilesystemProvi
207
163
  const candidates = Array.isArray(result) ? result : result.candidates;
208
164
  const selected = candidates.filter((candidate) => enabled.has(candidate.name));
209
165
  runtimeSummaries = selected.map((candidate) => ({ name: candidate.name, description: candidate.description }));
210
- // 任务型内置 skill 的遮蔽候选:只有当这颗**确实有索引可给**时才列出来。
211
- // 没有索引就整颗让给 fs provider(正文照旧),因此"索引缺失"永远不会演变成
212
- // "这颗 skill 不见了"——遮蔽是加法,不是替换。
166
+ // Shadow a task skill only when it has an index; otherwise the fs provider's original wins.
213
167
  const shadows = [...taskBases.values()]
214
168
  .filter((base) => indexFor(base.name) !== undefined)
215
169
  .map((base) => ({
@@ -228,7 +182,6 @@ export function registerRootSelectedSkills(ctx, selection, createFilesystemProvi
228
182
  get(candidate, options) {
229
183
  const base = taskBases.get(candidate.name);
230
184
  if (base !== undefined) {
231
- // 索引在**读取期**算:开关是 settings 偏好,加载这一刻才是最新的。
232
185
  const index = indexFor(base.name);
233
186
  const content = index === undefined ? base.content : `${base.content}\n\n${index}`;
234
187
  return Promise.resolve({
@@ -250,11 +203,8 @@ export function registerRootSelectedSkills(ctx, selection, createFilesystemProvi
250
203
  };
251
204
  return provider;
252
205
  });
253
- // 每轮 pre-step 重读开关:集合变了就调 provider control 的 invalidate(),失效
254
- // skills 的目录缓存(revision++),tool-skill 随后的 catalog 快照因此按最新开关
255
- // 重算;未变则 digest 不变、catalog 不重发。必须在放行 next() 之前做完——
256
- // waterfall 里 tool-skill 是 await next() 之后才快照,但监听器注册顺序不归我们
257
- // 管,先做完再放行在两种顺序下都赶得上本轮快照。设置读取失败只降级,不阻断本轮。
206
+ // Invalidate before next() so this turn's snapshot sees the change regardless of listener order.
207
+ // A settings read failure only degrades this hook.
258
208
  const stopPreStep = onPreStepEvent(injectedCtx, (_payload, next) => {
259
209
  try {
260
210
  const fingerprint = selectionFingerprint(readSelection(selection));
@@ -279,20 +229,14 @@ export function registerRootSelectedSkills(ctx, selection, createFilesystemProvi
279
229
  });
280
230
  }
281
231
  catch (error) {
282
- // selected skills 是可选能力:注入失败只降级告警,不能把整个 Agent 创建挡掉;
283
- // 同时撤掉登记,让后面的事件(下一次 select、别的注册路径)还有重试的余地。
232
+ // Optional capability: degrade with a warning, never block Agent creation; clear so a later event can retry.
284
233
  registered.delete(agent);
285
234
  ctx.logger?.warn?.(`capital-generation: selected skills are unavailable (${String(error)})`);
286
235
  }
287
236
  };
288
237
  const stop = listenCtx.on('agent/created', ({ agent }) => registerFor(agent));
289
- // 第二条路径:**先按默认预设建会话、再切到 Capital**(GUI 的真实顺序)。
290
- // 2026-10-13 复核复现:会话(header `agentPreset: "cordis"`)建好后客户端才发
291
- // `agentPresets.select('capital-generation')`,而 `agent/created` 在 select 之前就已经发完——
292
- // 只挂那一条时本插件的 `apply()` 是在**这次 recompose 的 mount 里**才跑的,监听器出生就错过了
293
- // 自己这个 Agent:设置卡 10 个开关全开、会话目录仍只有 preset 自带的 8 颗(真 boot graph 探针:
294
- // create→select 8 颗 / 建会话时 mount 同一 preset 18 颗)。preset registry 在 mount 完成后才
295
- // `session.append('agent-preset/selected')`,所以本行一定早于这条事件落位。
238
+ // Second path: GUI creates with the default preset, then selects Capital. `agent/created`
239
+ // already fired and apply() runs inside that remount, so `agent-preset/selected` arrives after mount.
296
240
  const onPresetEvent = listenCtx.on;
297
241
  const stopPreset = onPresetEvent?.('agent-preset/selected', (sessionId, agentPreset) => {
298
242
  if (agentPreset !== CAPITAL_PRESET_ID)
@@ -301,8 +245,7 @@ export function registerRootSelectedSkills(ctx, selection, createFilesystemProvi
301
245
  const agent = agents?.get?.(String(sessionId));
302
246
  if (agent === undefined)
303
247
  return;
304
- // emit 型事件不等待监听器;返回 promise 只为让单测能等到注册落地。registerFor 内部
305
- // 自兜异常(失败只降级告警并撤登记,下一次事件还有重试的余地),这里不会产生未处理拒绝。
248
+ // Emit-style event: the promise only lets tests await registration; errors are handled inside.
306
249
  return registerFor(agent);
307
250
  });
308
251
  ctx.effect?.(() => () => {
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Sole reader of the bundled `capital-config` entry. The id must match the host row,
3
+ * settings card and preset row; drift is caught by the four-way assertion in
4
+ * `test/capital-config.test.mjs`. Unreadable returns `{}` and callers fill defaults;
5
+ * only reference names are read here, never resolved secrets.
6
+ */
7
+ import type { Context } from '@deepseek-ai/cordis';
8
+ import type { RetrieverConfig } from './retriever-config.js';
9
+ import type { SelectedSkillSelection } from './agents/selected-skills-provider.js';
10
+ export declare const CAPITAL_CONFIG_ENTRY_ID = "capital-config";
11
+ /** Fields this repo consumes (the host half owns the rest). */
12
+ export interface CapitalConfigSlice {
13
+ fuyaoCredentialRef?: string;
14
+ ftshareCredentialRef?: string;
15
+ customPersona?: string;
16
+ retriever?: RetrieverConfig;
17
+ selectedSkills?: SelectedSkillSelection;
18
+ }
19
+ export declare function readCapitalConfig(ctx: Context): CapitalConfigSlice;
20
+ /** Flat credential refs needed by the collector domain (same shape as `CollectorConfig`). */
21
+ export declare function readCollectorConfig(ctx: Context): {
22
+ fuyaoCredentialRef?: string;
23
+ ftshareCredentialRef?: string;
24
+ windCredentialRef?: string;
25
+ };
26
+ /** Retriever config slice (baseURL, credential ref, Wind endpoint, local fallback switch). */
27
+ export declare function readRetrieverConfig(ctx: Context): RetrieverConfig;
@@ -0,0 +1,23 @@
1
+ export const CAPITAL_CONFIG_ENTRY_ID = 'capital-config';
2
+ export function readCapitalConfig(ctx) {
3
+ try {
4
+ const settings = ctx.get('settings');
5
+ return settings?.describe?.().find((entry) => entry?.ns === CAPITAL_CONFIG_ENTRY_ID)?.value ?? {};
6
+ }
7
+ catch {
8
+ return {};
9
+ }
10
+ }
11
+ /** Flat credential refs needed by the collector domain (same shape as `CollectorConfig`). */
12
+ export function readCollectorConfig(ctx) {
13
+ const config = readCapitalConfig(ctx);
14
+ return {
15
+ fuyaoCredentialRef: config.fuyaoCredentialRef,
16
+ ftshareCredentialRef: config.ftshareCredentialRef,
17
+ windCredentialRef: config.retriever?.windDocs?.credentialRef,
18
+ };
19
+ }
20
+ /** Retriever config slice (baseURL, credential ref, Wind endpoint, local fallback switch). */
21
+ export function readRetrieverConfig(ctx) {
22
+ return readCapitalConfig(ctx).retriever ?? {};
23
+ }
@@ -1,5 +1,8 @@
1
1
  import type { SessionLike } from '../data-collector/store.js';
2
- /** Dataset/profile 的一跳 scope 规则;token 让 nested child 复用这个 owner scope。 */
2
+ /**
3
+ * Subagent children carry `origin: 'subagent'`; a user fork/branch root also has `parentSession`
4
+ * (seed lineage) and must stay its own scope. One hop: the token lets a nested child reuse its owner.
5
+ */
3
6
  export declare function chartSessionScopeId(session: SessionLike): string;
4
7
  export interface ChartArtifactRef {
5
8
  chart_ref: string;
@@ -10,6 +13,7 @@ export interface ChartArtifactRef {
10
13
  captured_at: number | null;
11
14
  kind: string;
12
15
  axis: 'time' | 'index';
16
+ title: string;
13
17
  chart_url: string | null;
14
18
  spec_path: string;
15
19
  series_path: string;
@@ -23,13 +27,8 @@ export interface ChartArtifactRegistryOptions {
23
27
  newRef?: () => string;
24
28
  }
25
29
  /**
26
- * In-process registry for chart_ref capabilities.
27
- *
28
- * The chart files live in the workspace, while this registry keeps the
29
- * session/task binding that makes a chart safe to present to its own session
30
- * (and to derive the `deliverables/presented` owner scope).
31
- * Process restart invalidates chart_ref capabilities; chart.html remains the
32
- * explicit offline fallback.
30
+ * In-process chart_ref capabilities: session/task/workspace binding and the deliverable owner
31
+ * scope. Process restart invalidates them; chart.html remains the offline fallback.
33
32
  */
34
33
  export declare class ChartArtifactRegistry {
35
34
  private readonly records;
@@ -47,6 +46,7 @@ export declare class ChartArtifactRegistry {
47
46
  captured_at?: number | null;
48
47
  kind: string;
49
48
  axis: 'time' | 'index';
49
+ title: string;
50
50
  chart_url: string | null;
51
51
  spec_path: string;
52
52
  series_path: string;
@@ -60,6 +60,11 @@ export declare class ChartArtifactRegistry {
60
60
  task_id?: string | null;
61
61
  }): ChartArtifactRef;
62
62
  revoke(chartRef: string): boolean;
63
+ /** Charts of one session scope, for the model's final-turn delivery step; newest last. */
64
+ list(input: {
65
+ session: SessionLike;
66
+ task_id?: string | null;
67
+ }): ChartArtifactRef[];
63
68
  size(): number;
64
69
  private publicRecord;
65
70
  }
@@ -8,19 +8,17 @@ function defaultChartRef() {
8
8
  : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
9
9
  return `chart_${suffix}`;
10
10
  }
11
- /** Dataset/profile 的一跳 scope 规则;token 让 nested child 复用这个 owner scope。 */
11
+ /**
12
+ * Subagent children carry `origin: 'subagent'`; a user fork/branch root also has `parentSession`
13
+ * (seed lineage) and must stay its own scope. One hop: the token lets a nested child reuse its owner.
14
+ */
12
15
  export function chartSessionScopeId(session) {
13
- const parent = session.header?.parentSession;
16
+ const parent = session.header?.origin === 'subagent' ? session.header.parentSession : undefined;
14
17
  return typeof parent === 'string' && parent.length > 0 ? parent : session.id;
15
18
  }
16
19
  /**
17
- * In-process registry for chart_ref capabilities.
18
- *
19
- * The chart files live in the workspace, while this registry keeps the
20
- * session/task binding that makes a chart safe to present to its own session
21
- * (and to derive the `deliverables/presented` owner scope).
22
- * Process restart invalidates chart_ref capabilities; chart.html remains the
23
- * explicit offline fallback.
20
+ * In-process chart_ref capabilities: session/task/workspace binding and the deliverable owner
21
+ * scope. Process restart invalidates them; chart.html remains the offline fallback.
24
22
  */
25
23
  export class ChartArtifactRegistry {
26
24
  records = new Map();
@@ -61,6 +59,7 @@ export class ChartArtifactRegistry {
61
59
  captured_at: input.captured_at ?? null,
62
60
  kind: input.kind,
63
61
  axis: input.axis,
62
+ title: input.title,
64
63
  chart_url: input.chart_url,
65
64
  spec_path: input.spec_path,
66
65
  series_path: input.series_path,
@@ -96,12 +95,30 @@ export class ChartArtifactRegistry {
96
95
  revoke(chartRef) {
97
96
  return this.records.delete(chartRef);
98
97
  }
98
+ /** Charts of one session scope, for the model's final-turn delivery step; newest last. */
99
+ list(input) {
100
+ const now = this.now();
101
+ this.purgeExpired(now);
102
+ const scope = chartSessionScopeId(input.session);
103
+ const cwd = input.session.header?.cwd ?? null;
104
+ const records = [];
105
+ for (const record of this.records.values()) {
106
+ if (record.owner_scope_id !== scope || record.owner_cwd !== cwd)
107
+ continue;
108
+ if (input.task_id !== undefined && input.task_id !== null && record.task_id !== input.task_id)
109
+ continue;
110
+ records.push(record);
111
+ }
112
+ return records
113
+ .sort((left, right) => left.created_at - right.created_at)
114
+ .map((record) => this.publicRecord(record));
115
+ }
99
116
  size() {
100
117
  this.purgeExpired(this.now());
101
118
  return this.records.size;
102
119
  }
103
120
  publicRecord(record) {
104
- const { owner_scope_id: _ownerScope, ...publicRecord } = record;
121
+ const { owner_scope_id: _ownerScope, owner_cwd: _ownerCwd, ...publicRecord } = record;
105
122
  return publicRecord;
106
123
  }
107
124
  }
@@ -1,8 +1,5 @@
1
1
  /**
2
- * Largest-Triangle-Three-Buckets 下采样:返回**保留下来的行下标**。
3
- *
4
- * 为什么返回下标而不是点:一张图上可能同时有主序列、K 线、成交量副图与标记点,
5
- * 它们必须**共用同一组时间轴**。若各自下采样,同一时刻会在不同子图上错位。
6
- * 于是只对"主序列"跑一次 LTTB 求下标,再把这组下标套用到所有子序列上。
2
+ * LTTB downsampling returning kept row indices. All subplots share one time axis, so indices
3
+ * are computed once from the primary series and applied everywhere.
7
4
  */
8
5
  export declare function largestTriangleThreeBuckets(values: readonly number[], threshold: number): number[];
@@ -1,9 +1,6 @@
1
1
  /**
2
- * Largest-Triangle-Three-Buckets 下采样:返回**保留下来的行下标**。
3
- *
4
- * 为什么返回下标而不是点:一张图上可能同时有主序列、K 线、成交量副图与标记点,
5
- * 它们必须**共用同一组时间轴**。若各自下采样,同一时刻会在不同子图上错位。
6
- * 于是只对"主序列"跑一次 LTTB 求下标,再把这组下标套用到所有子序列上。
2
+ * LTTB downsampling returning kept row indices. All subplots share one time axis, so indices
3
+ * are computed once from the primary series and applied everywhere.
7
4
  */
8
5
  export function largestTriangleThreeBuckets(values, threshold) {
9
6
  const count = values.length;
@@ -14,7 +11,6 @@ export function largestTriangleThreeBuckets(values, threshold) {
14
11
  let anchor = 0;
15
12
  sampled.push(anchor);
16
13
  for (let bucket = 0; bucket < threshold - 2; bucket += 1) {
17
- // 下一个桶的均值,作为三角形的一个顶点。
18
14
  const averageStart = Math.floor((bucket + 1) * every) + 1;
19
15
  const averageEnd = Math.min(Math.floor((bucket + 2) * every) + 1, count);
20
16
  const averageLength = Math.max(averageEnd - averageStart, 1);
@@ -26,7 +22,6 @@ export function largestTriangleThreeBuckets(values, threshold) {
26
22
  }
27
23
  averageX /= averageLength;
28
24
  averageY /= averageLength;
29
- // 当前桶里与 anchor、均值构成最大三角形面积的点。
30
25
  const rangeStart = Math.floor(bucket * every) + 1;
31
26
  const rangeEnd = Math.min(Math.floor((bucket + 1) * every) + 1, count);
32
27
  const anchorX = anchor;
@@ -44,7 +39,7 @@ export function largestTriangleThreeBuckets(values, threshold) {
44
39
  anchor = maxAreaIndex;
45
40
  }
46
41
  sampled.push(count - 1);
47
- // 去重并保持升序:LTTB 的桶边界在极端阈值下可能产生重复下标。
42
+ // Bucket edges can yield duplicate indices at extreme thresholds; keep ascending order.
48
43
  const unique = [];
49
44
  for (const index of sampled)
50
45
  if (unique.length === 0 || index > unique[unique.length - 1])
@@ -1,16 +1,12 @@
1
1
  /**
2
- * 图表域的错误类型。
3
- *
4
- * 错误码是**模型可见契约**的一部分:模型据此修正一次即可重试(见
5
- * preset/capital-generation/skills/capital-chart-protocol/SKILL.md)。
6
- * 因此 detail 必须可操作(点名缺哪个字段、可用字段有哪些),而不是笼统的 invalid。
2
+ * Chart errors are model-visible contract: detail must name the missing field and list
3
+ * candidates, not merely say "invalid".
7
4
  */
8
5
  export type ChartErrorCode = 'chart_source_invalid' | 'chart_source_expired' | 'chart_source_scope_mismatch' | 'chart_source_not_found' | 'chart_ref_invalid' | 'chart_ref_scope_mismatch' | 'chart_ref_task_mismatch' | 'chart_spec_invalid' | 'chart_field_not_found' | 'chart_no_rows' | 'chart_too_large' | 'chart_write_failed' | 'chart_runtime_unavailable';
9
6
  export declare class ChartError extends Error {
10
7
  readonly code: ChartErrorCode;
11
- /** 可选的机器可读补充(如 available 字段名列表),会随错误一起序列化回模型。 */
8
+ /** Machine-readable extras (e.g. available fields), serialized back to the model. */
12
9
  readonly details: Record<string, unknown>;
13
10
  constructor(code: ChartErrorCode, detail: string, details?: Record<string, unknown>);
14
- /** 工具边界统一把错误转成结构化信封,让模型能一次修正。 */
15
11
  toEnvelope(): Record<string, unknown>;
16
12
  }
@@ -1,6 +1,6 @@
1
1
  export class ChartError extends Error {
2
2
  code;
3
- /** 可选的机器可读补充(如 available 字段名列表),会随错误一起序列化回模型。 */
3
+ /** Machine-readable extras (e.g. available fields), serialized back to the model. */
4
4
  details;
5
5
  constructor(code, detail, details = {}) {
6
6
  super(`${code}: ${detail}`);
@@ -8,7 +8,6 @@ export class ChartError extends Error {
8
8
  this.code = code;
9
9
  this.details = details;
10
10
  }
11
- /** 工具边界统一把错误转成结构化信封,让模型能一次修正。 */
12
11
  toEnvelope() {
13
12
  return { error: this.code, detail: this.message.slice(this.message.indexOf(':') + 2), ...this.details };
14
13
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Adapt a cordis ctx into ChartEventContext.
3
+ * Never spread ctx (`{ ...ctx }`): get/on/effect live on the prototype, so spreading drops them
4
+ * and delivery silently disappears. Forward each method with the original ctx as receiver.
5
+ * One assembly path serves both the monolith plugin and the tools/chart entry.
6
+ */
7
+ import type { ChartEventContext } from './events.js';
8
+ export declare function chartEventContextOf(ctx: unknown): ChartEventContext;
@@ -0,0 +1,39 @@
1
+ export function chartEventContextOf(ctx) {
2
+ const raw = ctx;
3
+ const context = {
4
+ get: (name) => raw.get(name),
5
+ // Failed registration returns undefined so events.ts can fall back to turn/start.
6
+ on: (event, listener) => {
7
+ if (typeof raw.on !== 'function')
8
+ return undefined;
9
+ return raw.on.call(ctx, event, (...args) => { listener(...args); });
10
+ },
11
+ onTurnStopping: (listener) => {
12
+ if (typeof raw.on !== 'function')
13
+ return undefined;
14
+ try {
15
+ return raw.on.call(ctx, 'agent/turn-stopping', (...args) => { listener(...args); });
16
+ }
17
+ catch {
18
+ return undefined;
19
+ }
20
+ },
21
+ };
22
+ // root/effect are cordis getters: forward only when present.
23
+ try {
24
+ if (raw.root !== undefined) {
25
+ Object.defineProperty(context, 'root', { get: () => raw.root, enumerable: true });
26
+ }
27
+ }
28
+ catch { /* root unavailable: events.ts falls back to self */ }
29
+ try {
30
+ if (typeof raw.effect === 'function') {
31
+ Object.defineProperty(context, 'effect', {
32
+ value: (callback, label) => raw.effect.call(ctx, callback, label),
33
+ enumerable: true,
34
+ });
35
+ }
36
+ }
37
+ catch { /* no disposer support */ }
38
+ return context;
39
+ }