@v587d/capital-generation 2.15.0 → 2.16.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +171 -0
  2. package/README.md +7 -12
  3. package/THIRD_PARTY_NOTICES.md +1 -0
  4. package/automation/TABLER-ICONS-LICENSE +21 -0
  5. package/automation/builtins.js +237 -10
  6. package/automation/client.js +79 -17
  7. package/automation/client.src.cjs +873 -210
  8. package/automation/contract.js +4 -0
  9. package/automation/defaults.js +6 -4
  10. package/automation/index.js +12 -7
  11. package/automation/package.json +5 -2
  12. package/automation/route.js +9 -2
  13. package/automation/runner.js +33 -6
  14. package/automation/scheduler.js +34 -29
  15. package/automation/schema.js +39 -5
  16. package/automation/service.js +102 -10
  17. package/automation/settings.js +6 -0
  18. package/automation/store.js +64 -21
  19. package/automation/task-icons.js +75 -0
  20. package/automation/workspaces.js +113 -13
  21. package/chart-ui/client.js +2 -2
  22. package/chart-ui/package.json +1 -1
  23. package/config/client.js +19 -5
  24. package/config/client.src.cjs +365 -44
  25. package/config/index.js +7 -5
  26. package/config/package.json +1 -1
  27. package/lib/agents/root-tool-policy.d.ts +3 -3
  28. package/lib/agents/root-tool-policy.js +4 -2
  29. package/lib/chart/runtime.d.ts +1 -1
  30. package/lib/chart/runtime.js +95 -0
  31. package/lib/chart/series.d.ts +4 -4
  32. package/lib/chart/series.js +44 -10
  33. package/lib/data-collector/dataset-tools.js +22 -0
  34. package/lib/data-collector/describe.js +1 -0
  35. package/lib/data-collector/hub.js +5 -0
  36. package/lib/data-collector/store.d.ts +5 -0
  37. package/lib/data-collector/store.js +17 -0
  38. package/lib/data-collector/time-axis.d.ts +15 -0
  39. package/lib/data-collector/time-axis.js +73 -0
  40. package/lib/data-collector/tools.d.ts +1 -1
  41. package/lib/data-collector/tools.js +12 -1
  42. package/lib/index.d.ts +5 -0
  43. package/lib/index.js +7 -5
  44. package/lib/sources/tencent-http.js +7 -2
  45. package/lib/sources/tencent-public-core.d.ts +2 -0
  46. package/lib/sources/tencent-public-core.js +5 -0
  47. package/lib/sources/wind-mcp.js +3 -3
  48. package/lib/sources-entry.d.ts +1 -1
  49. package/lib/web-retriever/local-fetch.d.ts +1 -1
  50. package/lib/web-retriever/local-fetch.js +1 -1
  51. package/lib/web-retriever/sources.d.ts +58 -0
  52. package/lib/web-retriever/sources.js +290 -0
  53. package/lib/web-retriever/tools.js +199 -8
  54. package/lib/web-retriever/wind-client.d.ts +1 -1
  55. package/lib/web-retriever/wind-client.js +1 -1
  56. package/package.json +1 -1
  57. package/preset/capital-generation/agent.patch.yml +20 -19
  58. package/preset/capital-generation/skills/capital-chart-protocol/SKILL.md +6 -3
  59. package/preset/capital-generation/skills/capital-data-protocol/SKILL.md +9 -2
  60. package/preset/capital-generation/skills/capital-orchestration/SKILL.md +2 -1
  61. package/preset/capital-generation/skills/capital-visualization-protocol/SKILL.md +2 -0
  62. package/preset/capital-generation/skills/capital-web-evidence/SKILL.md +18 -13
  63. package/preset/capital-generation/skills/capital-web-protocol/SKILL.md +30 -15
  64. package/watchlist/package.json +1 -1
@@ -4,6 +4,9 @@
4
4
  * sessions are grouped instead of landing in the ungrouped bucket.
5
5
  */
6
6
  import { mkdir, realpath } from 'node:fs/promises'
7
+ import { join, sep } from 'node:path'
8
+
9
+ export const AUTOMATION_WORKSPACE_DIR = 'CapGen-automation-workspace'
7
10
 
8
11
  export class WorkspaceError extends Error {
9
12
  constructor(code, message) {
@@ -15,6 +18,7 @@ export class WorkspaceError extends Error {
15
18
 
16
19
  const DEFAULT_REGISTRATIONS = new WeakMap()
17
20
  const MAX_SESSIONS_TO_ADOPT = 500
21
+ const MAX_WORKSPACE_ID_CHARS = 200
18
22
 
19
23
  function service(ctx, name) {
20
24
  try {
@@ -24,15 +28,54 @@ function service(ctx, name) {
24
28
  }
25
29
  }
26
30
 
31
+ /** Directory segment safe on every platform; the raw profile name never becomes a path. */
32
+ export function safeSegment(value) {
33
+ return String(value).replace(/[^A-Za-z0-9._-]+/g, '_').slice(0, 120)
34
+ }
35
+
36
+ export function defaultWorkspacePath(profile = {}) {
37
+ const home = profile.home ?? profile.dir ?? process.cwd()
38
+ return join(home, AUTOMATION_WORKSPACE_DIR, safeSegment(profile.name ?? 'default'))
39
+ }
40
+
41
+ export function defaultWorkspaceTitle(displayName) {
42
+ return `CapGen 投研自动化(${displayName})`
43
+ }
44
+
45
+ /** `~` only for paths under the user's home; anything else stays absolute so it is unambiguous. */
46
+ export function workspaceDisplayPath(path, home) {
47
+ if (typeof path !== 'string' || typeof home !== 'string' || home.length === 0) return path
48
+ const prefix = home.endsWith(sep) ? home : `${home}${sep}`
49
+ return path.startsWith(prefix) ? `~${path.slice(home.length)}` : path
50
+ }
51
+
27
52
  export function createWorkspaceCatalog(options) {
28
- const { ctx, displayName, runDir, logger } = options
29
- let defaultEntity
30
- let defaultPromise
53
+ const { ctx, displayName, runDir, displayHome, getDefaultWorkspaceId, logger } = options
54
+ let dedicatedEntity
55
+ let dedicatedPromise
56
+ let warnedDefaultId
31
57
 
32
58
  function registry() {
33
59
  return service(ctx, 'workspaceRegistry')
34
60
  }
35
61
 
62
+ function configuredDefaultId() {
63
+ let value
64
+ try {
65
+ value = typeof getDefaultWorkspaceId === 'function' ? getDefaultWorkspaceId() : getDefaultWorkspaceId
66
+ } catch {
67
+ value = undefined
68
+ }
69
+ if (typeof value !== 'string') return ''
70
+ return value.trim().slice(0, MAX_WORKSPACE_ID_CHARS)
71
+ }
72
+
73
+ function warnDefaultUnusable(id, reason) {
74
+ if (warnedDefaultId === id) return
75
+ warnedDefaultId = id
76
+ logger?.warn?.(`capital-automation: 默认工作区 ${id} ${reason},回退到专属默认工作区。`)
77
+ }
78
+
36
79
  async function adoptSessions(entity) {
37
80
  if (entity.sessionIds.length > 0) return
38
81
  const persistence = service(ctx, 'sessionPersistence')
@@ -66,8 +109,8 @@ export function createWorkspaceCatalog(options) {
66
109
  }
67
110
 
68
111
  async function ensureDefault() {
69
- if (defaultEntity !== undefined) return defaultEntity
70
- if (defaultPromise !== undefined) return defaultPromise
112
+ if (dedicatedEntity !== undefined) return dedicatedEntity
113
+ if (dedicatedPromise !== undefined) return dedicatedPromise
71
114
  const current = registry()
72
115
  if (current === undefined) return undefined
73
116
  let registrations = DEFAULT_REGISTRATIONS.get(current)
@@ -85,7 +128,7 @@ export function createWorkspaceCatalog(options) {
85
128
  }
86
129
  if (created === undefined) {
87
130
  try {
88
- created = await current.create(runDir, `CapGen 投研自动化(${displayName})`)
131
+ created = await current.create(runDir, defaultWorkspaceTitle(displayName))
89
132
  } catch (error) {
90
133
  const recovered = typeof current.resolveByPath === 'function'
91
134
  ? await current.resolveByPath(runDir).catch(() => undefined)
@@ -94,35 +137,92 @@ export function createWorkspaceCatalog(options) {
94
137
  created = recovered
95
138
  }
96
139
  }
97
- defaultEntity = created
140
+ dedicatedEntity = created
98
141
  await adoptSessions(created)
99
142
  return created
100
143
  })()
101
144
  registrations.set(runDir, sharedPromise)
102
145
  }
103
- defaultPromise = sharedPromise
146
+ dedicatedPromise = sharedPromise
104
147
  try {
105
- return await sharedPromise
148
+ const created = await sharedPromise
149
+ if (created !== undefined) dedicatedEntity = created
150
+ return created
106
151
  } catch (error) {
107
152
  logger?.warn?.(`capital-automation: 无法注册默认工作区:${String(error)}`)
108
153
  return undefined
109
154
  } finally {
110
155
  if (registrations.get(runDir) === sharedPromise) registrations.delete(runDir)
111
- if (defaultEntity === undefined) defaultPromise = undefined
156
+ if (dedicatedEntity === undefined) dedicatedPromise = undefined
112
157
  }
113
158
  }
114
159
 
160
+ /**
161
+ * A configured default is honoured only while it is registered and its directory is usable.
162
+ * A stale id must degrade to the dedicated workspace instead of breaking every empty task.
163
+ */
164
+ async function configuredDefaultEntity() {
165
+ const id = configuredDefaultId()
166
+ if (id === '') return undefined
167
+ const entity = registry()?.get?.(id)
168
+ if (entity === undefined) {
169
+ warnDefaultUnusable(id, '不存在或已被移除')
170
+ return undefined
171
+ }
172
+ let status
173
+ try {
174
+ status = await entity.status?.()
175
+ } catch {
176
+ status = 'unavailable'
177
+ }
178
+ if (status !== undefined && status !== 'ok') {
179
+ warnDefaultUnusable(id, '目录不可用')
180
+ return undefined
181
+ }
182
+ warnedDefaultId = undefined
183
+ return entity
184
+ }
185
+
186
+ async function effectiveDefault() {
187
+ const configured = await configuredDefaultEntity()
188
+ if (configured !== undefined) return configured
189
+ return ensureDefault()
190
+ }
191
+
115
192
  async function list() {
193
+ await ensureDefault()
116
194
  const current = registry()
117
195
  if (current === undefined) return []
118
- const preferred = await ensureDefault()
196
+ const preferred = await effectiveDefault()
197
+ const dedicatedId = dedicatedEntity?.id
119
198
  return current.list().map((entity) => ({
120
199
  id: entity.id,
121
200
  title: entity.title,
122
201
  default: preferred !== undefined && entity.id === preferred.id,
202
+ ...(entity.id === dedicatedId ? { dedicated: true } : {}),
123
203
  }))
124
204
  }
125
205
 
206
+ /** Settings-card projection only: it carries paths, unlike the task page's `/meta`. */
207
+ async function details() {
208
+ await ensureDefault()
209
+ const current = registry()
210
+ if (current === undefined) return { workspaces: [], defaultWorkspaceId: null }
211
+ const preferred = await effectiveDefault()
212
+ const dedicatedId = dedicatedEntity?.id
213
+ return {
214
+ workspaces: current.list().map((entity) => ({
215
+ id: entity.id,
216
+ title: entity.title,
217
+ path: entity.path,
218
+ displayPath: workspaceDisplayPath(entity.path, displayHome),
219
+ default: preferred !== undefined && entity.id === preferred.id,
220
+ dedicated: entity.id === dedicatedId,
221
+ })),
222
+ defaultWorkspaceId: preferred?.id ?? null,
223
+ }
224
+ }
225
+
126
226
  async function validate(id) {
127
227
  if (typeof id !== 'string' || id.length === 0) return
128
228
  const current = registry()
@@ -145,10 +245,10 @@ export function createWorkspaceCatalog(options) {
145
245
  const entity = registry().get(id)
146
246
  return { workspaceId: entity.id, path: entity.path }
147
247
  }
148
- const preferred = await ensureDefault()
248
+ const preferred = await effectiveDefault()
149
249
  if (preferred !== undefined) return { workspaceId: preferred.id, path: preferred.path }
150
250
  return { workspaceId: undefined, path: runDir }
151
251
  }
152
252
 
153
- return { list, validate, resolve, runDir }
253
+ return { list, details, validate, resolve, runDir }
154
254
  }