evolcore 0.0.23 → 0.0.25

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 (245) hide show
  1. package/CHANGELOG.md +583 -516
  2. package/LICENSE +21 -21
  3. package/README.md +455 -453
  4. package/bin/codex-managed-acl.ps1 +160 -155
  5. package/bin/codex-managed-hook.mjs +209 -92
  6. package/bin/ec-safe-output.js +327 -245
  7. package/bin/ec.js +29 -29
  8. package/bin/install-codex-managed-hooks.mjs +555 -544
  9. package/dist/agents/baseagent-availability.js +4 -4
  10. package/dist/agents/baseagent.js +29 -1
  11. package/dist/agents/claude-runner.js +152 -14
  12. package/dist/agents/codex/app-server-client.js +18 -8
  13. package/dist/agents/codex/catalog.js +1 -1
  14. package/dist/agents/codex/diagnostics.js +215 -0
  15. package/dist/agents/codex-runner.js +445 -63
  16. package/dist/agents/ecagent-runner.js +31 -16
  17. package/dist/agents/gemini-runner.js +82 -4
  18. package/dist/agents/runner-types.js +1 -1
  19. package/dist/aun/aid/agentmd.js +56 -4
  20. package/dist/aun/aid/index.js +1 -0
  21. package/dist/aun/aid/managed-operation.js +80 -6
  22. package/dist/aun/aid/store.js +41 -0
  23. package/dist/aun/msg/managed-operation.js +6 -1
  24. package/dist/aun/outbox.js +56 -18
  25. package/dist/aun/rpc/client.js +4 -5
  26. package/dist/aun/storage/client.js +4 -1
  27. package/dist/aun/storage/index.js +1 -1
  28. package/dist/channels/aun.js +147 -76
  29. package/dist/channels/daemon.js +7 -1
  30. package/dist/channels/dingtalk.js +1 -0
  31. package/dist/channels/feishu.js +10 -4
  32. package/dist/channels/wecom.js +2 -1
  33. package/dist/cli/agent-command.js +214 -89
  34. package/dist/cli/agent-create.js +576 -0
  35. package/dist/cli/agent.js +129 -676
  36. package/dist/cli/aun-commands.js +419 -313
  37. package/dist/cli/bench.js +20 -20
  38. package/dist/cli/config.js +20 -20
  39. package/dist/cli/contact.js +10 -10
  40. package/dist/cli/ctl-command.js +22 -20
  41. package/dist/cli/daemon-commands.js +163 -32
  42. package/dist/cli/fs-command.js +67 -67
  43. package/dist/cli/handoff-command.js +7 -7
  44. package/dist/cli/index.js +79 -79
  45. package/dist/cli/link-rules.js +21 -21
  46. package/dist/cli/managed-command-guard.js +3 -0
  47. package/dist/cli/menu-client.js +11 -11
  48. package/dist/cli/net-check.js +22 -22
  49. package/dist/cli/queue-command.js +29 -29
  50. package/dist/cli/response.js +30 -30
  51. package/dist/cli/restart-monitor.js +462 -38
  52. package/dist/cli/stats.js +161 -51
  53. package/dist/cli/trigger-command.js +56 -56
  54. package/dist/config/builtin-roles.js +3 -0
  55. package/dist/config/config-manager.js +15 -0
  56. package/dist/config/config-operation-service.js +20 -3
  57. package/dist/config/resolved-config-op.js +39 -1
  58. package/dist/core/auth/auth-gateway.js +3 -2
  59. package/dist/core/auth/authorization-audit.js +72 -19
  60. package/dist/core/auth/operation-authorizer.js +125 -7
  61. package/dist/core/auth/operation-catalog.js +26 -2
  62. package/dist/core/auth/remote-access-store.js +3 -2
  63. package/dist/core/baseagent-loader.js +4 -4
  64. package/dist/core/bootstrap-messages.js +39 -18
  65. package/dist/core/bootstrap-service.js +32 -26
  66. package/dist/core/capability/capability-manager.js +8 -4
  67. package/dist/core/capability/providers/codex-capability-provider.js +1 -1
  68. package/dist/core/command/agent-control.js +60 -26
  69. package/dist/core/command/cli-intent-parser.js +18 -0
  70. package/dist/core/command/command-handler.js +173 -36
  71. package/dist/core/command/menu-catalog.js +4 -1
  72. package/dist/core/command/menu-handler.js +315 -75
  73. package/dist/core/command/menu-protocol.js +7 -2
  74. package/dist/core/command/slash-gate.js +1 -3
  75. package/dist/core/command/slash-handler.js +166 -57
  76. package/dist/core/data-migration.js +5 -15
  77. package/dist/core/event-catalog.js +8 -0
  78. package/dist/core/evolagent.js +3 -3
  79. package/dist/core/handoff/runtime.js +3 -1
  80. package/dist/core/handoff/store.js +7 -1
  81. package/dist/core/message/im-renderer.js +13 -6
  82. package/dist/core/message/message-bridge.js +5 -36
  83. package/dist/core/message/message-utils.js +6 -3
  84. package/dist/core/message/response-engine.js +85 -27
  85. package/dist/core/model/catalog-service.js +3 -1
  86. package/dist/core/model/model-catalog.js +10 -0
  87. package/dist/core/model/model-permission.js +4 -1
  88. package/dist/core/permission/approval-gateway.js +169 -33
  89. package/dist/core/permission/tool-policy.js +56 -10
  90. package/dist/core/session/adapters/codex-session-file-adapter.js +71 -3
  91. package/dist/core/session/session-manager.js +49 -13
  92. package/dist/core/session/session-renew.js +1 -0
  93. package/dist/index.js +327 -134
  94. package/dist/ipc.js +68 -21
  95. package/dist/paths.js +23 -1
  96. package/dist/stats/billing.js +18 -4
  97. package/dist/stats/db.js +159 -159
  98. package/dist/stats/eck-vars.js +9 -9
  99. package/dist/stats/query.js +139 -139
  100. package/dist/stats/role-budget.js +6 -6
  101. package/dist/stats/writer.js +51 -51
  102. package/dist/utils/atomic-write.js +26 -2
  103. package/dist/utils/avatar-upload.js +282 -23
  104. package/dist/utils/cross-platform.js +436 -1
  105. package/dist/utils/error-dict.json +160 -160
  106. package/dist/utils/error-utils.js +11 -4
  107. package/dist/utils/macos-autostart.js +22 -22
  108. package/dist/utils/model-prices.jsonl +23 -20
  109. package/dist/utils/npm-ops.js +24 -1
  110. package/dist/utils/welcome.js +22 -22
  111. package/dist/utils/windows-autostart.js +1 -1
  112. package/ecagent/LICENSE +21 -21
  113. package/ecagent/NOTICE +11 -11
  114. package/ecagent/README.md +61 -61
  115. package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
  116. package/ecagent/dist/harness/compaction/compaction.js +83 -83
  117. package/ecagent/dist/harness/messages.js +7 -7
  118. package/kits/docs/GUIDE.md +20 -20
  119. package/kits/docs/INDEX.md +66 -66
  120. package/kits/docs/aun/CHEATSHEET.md +19 -19
  121. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
  122. package/kits/docs/channels/aun.md +56 -56
  123. package/kits/docs/channels/feishu.md +56 -56
  124. package/kits/docs/context-assembly.md +365 -364
  125. package/kits/docs/eck_templates/GUIDE.template.md +22 -22
  126. package/kits/docs/eck_templates/INDEX.template.md +28 -28
  127. package/kits/docs/eck_templates/path-registry.template.md +33 -33
  128. package/kits/docs/eck_templates/runtime.template.md +19 -19
  129. package/kits/docs/evolcore/INDEX.md +71 -69
  130. package/kits/docs/evolcore/agent-create.md +170 -156
  131. package/kits/docs/evolcore/agent.md +83 -73
  132. package/kits/docs/evolcore/aid.md +64 -62
  133. package/kits/docs/evolcore/codex-windows-sandbox.md +146 -0
  134. package/kits/docs/evolcore/config.md +195 -195
  135. package/kits/docs/evolcore/contact.md +68 -68
  136. package/kits/docs/evolcore/ctl.md +46 -46
  137. package/kits/docs/evolcore/event.md +216 -216
  138. package/kits/docs/evolcore/fs-architecture.md +1215 -1215
  139. package/kits/docs/evolcore/fs.md +110 -110
  140. package/kits/docs/evolcore/group-collaboration.md +243 -243
  141. package/kits/docs/evolcore/group-fs.md +17 -17
  142. package/kits/docs/evolcore/group-rules.md +251 -251
  143. package/kits/docs/evolcore/group.md +152 -152
  144. package/kits/docs/evolcore/menu-client.md +34 -34
  145. package/kits/docs/evolcore/model.md +50 -50
  146. package/kits/docs/evolcore/msg.md +165 -165
  147. package/kits/docs/evolcore/response.md +75 -75
  148. package/kits/docs/evolcore/rpc.md +37 -37
  149. package/kits/docs/evolcore/self-summary.md +29 -29
  150. package/kits/docs/evolcore/stats.md +83 -83
  151. package/kits/docs/evolcore/storage.md +50 -50
  152. package/kits/docs/evolcore/trigger.md +575 -575
  153. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
  154. package/kits/docs/identity/PATH_OPS.md +16 -16
  155. package/kits/docs/identity/ROLE_DETAIL.md +25 -25
  156. package/kits/docs/identity/identity-tools.md +26 -26
  157. package/kits/docs/path-registry.md +62 -62
  158. package/kits/docs/prompt-loading-architecture.md +270 -269
  159. package/kits/docs/venues/aun-group.md +45 -45
  160. package/kits/docs/venues/aun-private.md +10 -10
  161. package/kits/docs/venues/client-desktop.md +10 -10
  162. package/kits/docs/venues/client-mobile.md +10 -10
  163. package/kits/docs/venues/feishu-group.md +13 -13
  164. package/kits/docs/venues/feishu-private.md +9 -9
  165. package/kits/docs/venues/group.md +25 -25
  166. package/kits/docs/venues/private.md +10 -10
  167. package/kits/eck_manifest.auxiliary.json +43 -43
  168. package/kits/eck_manifest.json +195 -183
  169. package/kits/eck_message_manifest.json +74 -74
  170. package/kits/migrations/README-role-config-v4.md +32 -32
  171. package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
  172. package/kits/migrations/migrate-role-config-v4.mjs +614 -614
  173. package/kits/migrations/migrate-role-config-v5.mjs +365 -365
  174. package/kits/migrations/rename-config-file.mjs +99 -99
  175. package/kits/rules/01-overview.md +162 -162
  176. package/kits/rules/02-navigation.md +96 -96
  177. package/kits/rules/03-identity.md +39 -37
  178. package/kits/rules/04-relation.md +75 -75
  179. package/kits/rules/05-venue.md +60 -60
  180. package/kits/rules/06-channel.md +63 -63
  181. package/kits/schemas/_meta.json +53 -53
  182. package/kits/schemas/agent-config.schema.1.json +177 -177
  183. package/kits/schemas/agent-config.schema.10.json +395 -395
  184. package/kits/schemas/agent-config.schema.11.json +421 -421
  185. package/kits/schemas/agent-config.schema.12.json +427 -427
  186. package/kits/schemas/agent-config.schema.2.json +239 -239
  187. package/kits/schemas/agent-config.schema.3.json +119 -119
  188. package/kits/schemas/agent-config.schema.4.json +208 -208
  189. package/kits/schemas/agent-config.schema.5.json +326 -326
  190. package/kits/schemas/agent-config.schema.6.json +322 -322
  191. package/kits/schemas/agent-config.schema.7.json +303 -303
  192. package/kits/schemas/agent-config.schema.8.json +304 -304
  193. package/kits/schemas/agent-config.schema.9.json +364 -364
  194. package/kits/schemas/contact-book.schema.1.json +36 -36
  195. package/kits/schemas/contact-book.schema.2.json +43 -43
  196. package/kits/schemas/contact-book.schema.3.json +67 -67
  197. package/kits/schemas/daemon.schema.1.json +91 -91
  198. package/kits/schemas/daemon.schema.2.json +101 -101
  199. package/kits/schemas/daemon.schema.3.json +123 -123
  200. package/kits/schemas/daemon.schema.4.json +136 -136
  201. package/kits/schemas/daemon.schema.5.json +135 -135
  202. package/kits/schemas/daemon.schema.6.json +131 -131
  203. package/kits/schemas/defaults.schema.1.json +81 -81
  204. package/kits/schemas/defaults.schema.2.json +85 -85
  205. package/kits/schemas/defaults.schema.3.json +73 -73
  206. package/kits/schemas/defaults.schema.4.json +107 -107
  207. package/kits/schemas/defaults.schema.5.json +119 -119
  208. package/kits/schemas/menu-exec-schema-commands.md +208 -208
  209. package/kits/schemas/migrations/README.md +30 -30
  210. package/kits/schemas/relation-config.schema.1.json +158 -158
  211. package/kits/schemas/relation-config.schema.2.json +73 -73
  212. package/kits/schemas/relation-config.schema.3.json +50 -50
  213. package/kits/schemas/relation-config.schema.4.json +47 -47
  214. package/kits/schemas/relation-config.schema.5.json +47 -47
  215. package/kits/schemas/relation-config.schema.6.json +46 -46
  216. package/kits/schemas/relation-config.schema.7.json +59 -59
  217. package/kits/schemas/relation-config.schema.8.json +103 -103
  218. package/kits/schemas/role-config.schema.1.json +201 -201
  219. package/kits/schemas/role-registry.schema.1.json +35 -35
  220. package/kits/schemas/single-session.schema.1.json +31 -31
  221. package/kits/schemas/single-session.schema.2.json +57 -57
  222. package/kits/schemas/single-session.schema.3.json +32 -32
  223. package/kits/templates/bootstrap-welcome.md +17 -17
  224. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
  225. package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
  226. package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
  227. package/kits/templates/message-fragments/inject-default.md +2 -2
  228. package/kits/templates/message-fragments/item.md +2 -2
  229. package/kits/templates/roles/admin.json +96 -82
  230. package/kits/templates/roles/member.json +97 -96
  231. package/kits/templates/roles/owner.json +9 -9
  232. package/kits/templates/roles/visitor.json +86 -85
  233. package/kits/templates/system-fragments/baseagent.md +14 -14
  234. package/kits/templates/system-fragments/bootstrap.md +38 -29
  235. package/kits/templates/system-fragments/channel.md +64 -64
  236. package/kits/templates/system-fragments/commands.md +28 -28
  237. package/kits/templates/system-fragments/identity.md +13 -13
  238. package/kits/templates/system-fragments/relation.md +19 -19
  239. package/kits/templates/system-fragments/session.md +59 -59
  240. package/kits/templates/system-fragments/venue.md +31 -31
  241. package/package.json +98 -94
  242. package/AGENTS.md +0 -62
  243. package/dist/utils/codex-cli.js +0 -43
  244. package/skills/eclink/SKILL.md +0 -84
  245. package/skills/eclink/agents/openai.yaml +0 -4
package/dist/ipc.js CHANGED
@@ -830,12 +830,21 @@ export class IpcServer {
830
830
  || typeof cmd.toolName !== 'string' || !cmd.toolName) {
831
831
  return { ok: false, applicable: true, decision: 'deny', reason: 'invalid Codex hook request' };
832
832
  }
833
+ // Unmanaged Codex processes carry no credential and are allowed
834
+ // without consulting any runner; see bin/codex-managed-hook.mjs.
835
+ const sessionToken = typeof cmd.sessionToken === 'string' && cmd.sessionToken
836
+ ? cmd.sessionToken
837
+ : undefined;
838
+ if (!sessionToken) {
839
+ return { ok: true, applicable: true, decision: 'allow' };
840
+ }
833
841
  try {
834
842
  return await this.codexPreToolUseExecutor({
835
843
  threadId: cmd.threadId,
836
844
  toolName: cmd.toolName,
837
845
  toolInput: cmd.toolInput,
838
846
  callId: typeof cmd.callId === 'string' ? cmd.callId : undefined,
847
+ sessionToken,
839
848
  signal: requestSignal,
840
849
  });
841
850
  }
@@ -1534,12 +1543,26 @@ export class IpcServer {
1534
1543
  && crypto.timingSafeEqual(provided, this.controlToken);
1535
1544
  }
1536
1545
  }
1537
- /**
1538
- * Query the running EvolCore daemon via Unix socket.
1539
- * Returns null if the service is not running or the socket is unreachable.
1540
- */
1546
+ export class IpcTransportError extends Error {
1547
+ code;
1548
+ endpoint;
1549
+ constructor(code, endpoint) {
1550
+ const hint = code === 'EPERM' || code === 'EACCES'
1551
+ ? '权限被拒绝;Windows 下请检查 daemon 与 CLI 是否使用相同用户和提升权限级别。'
1552
+ : code === 'ETIMEDOUT'
1553
+ ? '等待 daemon 响应超时;请求可能已执行,请先检查状态再重试。'
1554
+ : code === 'ENOENT' || code === 'ECONNREFUSED'
1555
+ ? 'daemon 未运行或 IPC 端点不存在,请先执行 ec start,再重试。'
1556
+ : '请检查 daemon 进程及 IPC 端点状态。';
1557
+ super(`daemon IPC 失败 (${code}): ${endpoint}。${hint}`);
1558
+ this.code = code;
1559
+ this.endpoint = endpoint;
1560
+ this.name = 'IpcTransportError';
1561
+ }
1562
+ }
1563
+ /** Query the daemon; opt in to transport errors instead of the legacy null result. */
1541
1564
  /** Pass `null` as timeoutMs to wait without a client-side deadline. */
1542
- export function ipcQuery(socketPath, cmd, timeoutMs = 3000) {
1565
+ export async function ipcQuery(socketPath, cmd, timeoutMs = 3000, options = {}) {
1543
1566
  const endpoint = normalizeIpcEndpoint(socketPath);
1544
1567
  let socketCandidates = [endpoint];
1545
1568
  if (isNamedPipe(endpoint)) {
@@ -1547,22 +1570,35 @@ export function ipcQuery(socketPath, cmd, timeoutMs = 3000) {
1547
1570
  if (endpoint === paths.socket)
1548
1571
  socketCandidates = getWindowsInstanceSocketPathCandidates(paths.root);
1549
1572
  }
1550
- if (socketCandidates.length <= 1)
1551
- return ipcQueryOnce(endpoint, cmd, timeoutMs);
1552
- return (async () => {
1553
- const startedAt = Date.now();
1554
- for (const candidate of socketCandidates) {
1555
- const remainingTimeout = timeoutMs === null
1556
- ? null
1557
- : Math.max(0, timeoutMs - (Date.now() - startedAt));
1558
- if (remainingTimeout === 0)
1559
- return null;
1573
+ const startedAt = Date.now();
1574
+ let failure;
1575
+ for (const candidate of socketCandidates) {
1576
+ const remainingTimeout = timeoutMs === null
1577
+ ? null
1578
+ : Math.max(0, timeoutMs - (Date.now() - startedAt));
1579
+ if (remainingTimeout === 0) {
1580
+ failure ??= new IpcTransportError('ETIMEDOUT', candidate);
1581
+ break;
1582
+ }
1583
+ try {
1560
1584
  const response = await ipcQueryOnce(candidate, cmd, remainingTimeout);
1561
1585
  if (response !== null)
1562
1586
  return response;
1563
1587
  }
1564
- return null;
1565
- })();
1588
+ catch (error) {
1589
+ if (!(error instanceof IpcTransportError))
1590
+ throw error;
1591
+ // A missing legacy pipe must not hide a permission failure on the canonical pipe.
1592
+ if (!failure || ['ENOENT', 'ECONNREFUSED'].includes(failure.code))
1593
+ failure = error;
1594
+ // Only connection failures are safe to retry against a legacy endpoint.
1595
+ if (!['ENOENT', 'ECONNREFUSED', 'EPERM', 'EACCES'].includes(error.code))
1596
+ break;
1597
+ }
1598
+ }
1599
+ if (options.throwOnError && failure)
1600
+ throw failure;
1601
+ return null;
1566
1602
  }
1567
1603
  function ipcQueryOnce(socketPath, cmd, timeoutMs = 3000) {
1568
1604
  let authenticatedCmd = cmd;
@@ -1587,7 +1623,7 @@ function ipcQueryOnce(socketPath, cmd, timeoutMs = 3000) {
1587
1623
  ? { daemonRuntimeEpoch: process.env[DAEMON_RUNTIME_EPOCH_ENV] }
1588
1624
  : {}),
1589
1625
  };
1590
- return new Promise((resolve) => {
1626
+ return new Promise((resolve, reject) => {
1591
1627
  const startedAtMono = performance.now();
1592
1628
  const queryId = nextIpcQueryId(request);
1593
1629
  const commandType = ipcLogToken(request.type);
@@ -1606,7 +1642,7 @@ function ipcQueryOnce(socketPath, cmd, timeoutMs = 3000) {
1606
1642
  settled = true;
1607
1643
  logPhase('timeout', ` timeout_ms=${timeoutMs}`);
1608
1644
  conn.destroy();
1609
- resolve(null);
1645
+ reject(new IpcTransportError('ETIMEDOUT', socketPath));
1610
1646
  }, timeoutMs);
1611
1647
  conn.on('connect', () => {
1612
1648
  if (settled)
@@ -1632,7 +1668,10 @@ function ipcQueryOnce(socketPath, cmd, timeoutMs = 3000) {
1632
1668
  parse = 'invalid_json';
1633
1669
  }
1634
1670
  logPhase('response', ` bytes=${idx + 1} parse=${parse}`);
1635
- resolve(response);
1671
+ if (parse === 'invalid_json')
1672
+ reject(new IpcTransportError('EPROTO', socketPath));
1673
+ else
1674
+ resolve(response);
1636
1675
  conn.destroy();
1637
1676
  }
1638
1677
  });
@@ -1643,7 +1682,15 @@ function ipcQueryOnce(socketPath, cmd, timeoutMs = 3000) {
1643
1682
  if (timer)
1644
1683
  clearTimeout(timer);
1645
1684
  logPhase('error', ` code=${error.code ?? 'unknown'}`);
1646
- resolve(null);
1685
+ reject(new IpcTransportError(error.code ?? 'UNKNOWN', socketPath));
1686
+ });
1687
+ conn.on('close', () => {
1688
+ if (settled)
1689
+ return;
1690
+ settled = true;
1691
+ if (timer)
1692
+ clearTimeout(timer);
1693
+ reject(new IpcTransportError('ECONNRESET', socketPath));
1647
1694
  });
1648
1695
  });
1649
1696
  }
package/dist/paths.js CHANGED
@@ -129,11 +129,22 @@ export function agentHandoffDir(aid) {
129
129
  return path.join(agentDataDir(aid), 'handoff');
130
130
  }
131
131
  export function agentOutboxPath(aid) {
132
- return path.join(agentDataDir(aid), 'outbox.jsonl');
132
+ return path.join(resolvePaths().outboxDir, encodeURIComponent(aid), 'outbox.jsonl');
133
133
  }
134
134
  export function agentActivityOutboxPath(aid) {
135
+ return path.join(resolvePaths().outboxDir, encodeURIComponent(aid), 'activity-outbox.jsonl');
136
+ }
137
+ /** Pre-v4 per-Agent outbox paths retained only for compatibility import. */
138
+ export function legacyAgentOutboxPath(aid) {
139
+ return path.join(agentDataDir(aid), 'outbox.jsonl');
140
+ }
141
+ export function legacyAgentActivityOutboxPath(aid) {
135
142
  return path.join(agentDataDir(aid), 'activity-outbox.jsonl');
136
143
  }
144
+ /** Earliest global outbox layout: data/outbox/<aid>.jsonl. */
145
+ export function legacyFlatOutboxPath(aid) {
146
+ return path.join(resolvePaths().outboxDir, `${aid}.jsonl`);
147
+ }
137
148
  /** Directory-safe channel instance key used by connector runtime state. */
138
149
  function encodePathKey(value) {
139
150
  return encodeURIComponent(value);
@@ -283,6 +294,17 @@ function migrateFromAun() {
283
294
  const markerPath = path.join(newRoot, MIGRATION_MARKER);
284
295
  if (fs.existsSync(markerPath))
285
296
  return;
297
+ // Test workers use isolated temporary homes. Copying the developer's
298
+ // machine-wide ~/.aun tree into every fresh fixture is both unnecessary and
299
+ // can make process-boundary tests exceed their timeout. Production roots
300
+ // retain the normal one-time migration behavior.
301
+ if (process.env.EVOLCORE_TEST === '1') {
302
+ try {
303
+ fs.writeFileSync(markerPath, new Date().toISOString(), 'utf-8');
304
+ }
305
+ catch { }
306
+ return;
307
+ }
286
308
  const oldRoot = path.join(os.homedir(), '.aun');
287
309
  if (!fs.existsSync(oldRoot) || oldRoot === newRoot) {
288
310
  try {
@@ -1,11 +1,13 @@
1
1
  /**
2
- * billing.ts — 读 model-prices.jsonl,按 billing_fn 调对应算法函数计算费用。
2
+ * billing.ts — 读 model-prices.jsonl,按模型参数中的 billing_fn 计算费用。
3
3
  * 费用查询时实时计算,不存入 DB(价格可能变动)。
4
4
  */
5
5
  import fs from 'fs';
6
6
  import path from 'path';
7
7
  import { getPackageRoot } from '../paths.js';
8
- // 价格表缓存(进程内,5 分钟 TTL)
8
+ // 模型参数缓存(进程内,5 分钟 TTL)
9
+ let _parameterCache = null;
10
+ let _parameterCacheTs = 0;
9
11
  let _priceCache = null;
10
12
  let _priceCacheTs = 0;
11
13
  const PRICE_CACHE_TTL = 5 * 60 * 1000;
@@ -44,6 +46,14 @@ function _loadJsonlMerged(evolcoreHome, filename) {
44
46
  }
45
47
  return results;
46
48
  }
49
+ function loadModelParameters(evolcoreHome) {
50
+ if (_parameterCache && Date.now() - _parameterCacheTs < PRICE_CACHE_TTL)
51
+ return _parameterCache;
52
+ const params = _loadJsonlMerged(evolcoreHome, 'model-prices.jsonl');
53
+ _parameterCache = params;
54
+ _parameterCacheTs = Date.now();
55
+ return params;
56
+ }
47
57
  let _aliasCache = null;
48
58
  let _aliasCacheTs = 0;
49
59
  function loadAliases(evolcoreHome) {
@@ -64,7 +74,8 @@ function loadPrices(evolcoreHome) {
64
74
  const now = Date.now();
65
75
  if (_priceCache && now - _priceCacheTs < PRICE_CACHE_TTL)
66
76
  return _priceCache;
67
- _priceCache = _loadJsonlMerged(evolcoreHome, 'model-prices.jsonl');
77
+ _priceCache = loadModelParameters(evolcoreHome).filter((record) => (typeof record.billing_fn === 'string'
78
+ && (record.currency === 'USD' || record.currency === 'CNY')));
68
79
  _priceCacheTs = now;
69
80
  return _priceCache;
70
81
  }
@@ -140,13 +151,16 @@ export function calcCost(evolcoreHome, event) {
140
151
  return {};
141
152
  return fn(event, priceRow);
142
153
  }
154
+ // ── Model Specs ──────────────────────────────────────────────────────────────
143
155
  let _specCache = null;
144
156
  let _specCacheTs = 0;
145
157
  function loadSpecs(evolcoreHome) {
146
158
  const now = Date.now();
147
159
  if (_specCache && now - _specCacheTs < PRICE_CACHE_TTL)
148
160
  return _specCache;
149
- _specCache = _loadJsonlMerged(evolcoreHome, 'model-specs.jsonl');
161
+ _specCache = loadModelParameters(evolcoreHome).filter((record) => (typeof record.context_window === 'number'
162
+ && typeof record.max_input_tokens === 'number'
163
+ && typeof record.max_output_tokens === 'number'));
150
164
  _specCacheTs = now;
151
165
  return _specCache;
152
166
  }
package/dist/stats/db.js CHANGED
@@ -85,134 +85,134 @@ export function openReadonlyDb(dbPath) {
85
85
  }
86
86
  }
87
87
  function _initTables(db) {
88
- db.exec(`
89
- CREATE TABLE IF NOT EXISTS usage_events (
90
- id INTEGER PRIMARY KEY AUTOINCREMENT,
91
- ts INTEGER NOT NULL,
92
- agent_aid TEXT NOT NULL,
93
- peer_key TEXT NOT NULL,
94
- usage_subject_key TEXT NOT NULL DEFAULT '',
95
- role TEXT NOT NULL DEFAULT '',
96
- peer_type TEXT,
97
- session_id TEXT,
98
- model TEXT NOT NULL,
99
- billing_fn TEXT NOT NULL,
100
- input_tokens INTEGER NOT NULL DEFAULT 0,
101
- output_tokens INTEGER NOT NULL DEFAULT 0,
102
- cache_creation_tokens INTEGER NOT NULL DEFAULT 0,
103
- cache_read_tokens INTEGER NOT NULL DEFAULT 0,
104
- cache_hit_tokens INTEGER,
105
- cache_miss_tokens INTEGER,
106
- image_tokens INTEGER,
107
- total_context_tokens INTEGER,
108
- turns INTEGER NOT NULL DEFAULT 1,
109
- duration_ms INTEGER,
110
- context_window_pct REAL,
111
- cost_official_usd REAL,
112
- cost_official_cny REAL,
113
- cost_gateway_usd REAL,
114
- cost_gateway_cny REAL
115
- );
116
- CREATE INDEX IF NOT EXISTS idx_ue_ts ON usage_events(ts);
117
- CREATE INDEX IF NOT EXISTS idx_ue_agent_ts ON usage_events(agent_aid, ts);
118
- CREATE INDEX IF NOT EXISTS idx_ue_peer_ts ON usage_events(agent_aid, peer_key, ts);
119
- CREATE INDEX IF NOT EXISTS idx_ue_model_ts ON usage_events(model, ts);
120
- CREATE INDEX IF NOT EXISTS idx_ue_session_ts ON usage_events(session_id, ts);
121
-
122
- CREATE TABLE IF NOT EXISTS context_breakdown (
123
- id INTEGER PRIMARY KEY AUTOINCREMENT,
124
- ts INTEGER NOT NULL,
125
- agent_aid TEXT NOT NULL,
126
- session_id TEXT NOT NULL,
127
- turn_count INTEGER NOT NULL,
128
- model TEXT NOT NULL,
129
- max_tokens INTEGER NOT NULL,
130
- system_prompt INTEGER,
131
- system_tools INTEGER,
132
- mcp_tools INTEGER,
133
- custom_agents INTEGER,
134
- memory_files INTEGER,
135
- skills INTEGER,
136
- messages INTEGER,
137
- free_space INTEGER,
138
- total_estimated INTEGER
139
- );
140
- CREATE INDEX IF NOT EXISTS idx_cb_session ON context_breakdown(session_id, ts);
141
- CREATE INDEX IF NOT EXISTS idx_cb_agent ON context_breakdown(agent_aid, ts);
142
-
143
- CREATE TABLE IF NOT EXISTS message_events (
144
- id INTEGER PRIMARY KEY AUTOINCREMENT,
145
- ts INTEGER NOT NULL,
146
- agent_aid TEXT NOT NULL,
147
- peer_key TEXT NOT NULL,
148
- direction TEXT NOT NULL,
149
- msg_type TEXT,
150
- bytes INTEGER NOT NULL DEFAULT 0,
151
- encrypted INTEGER DEFAULT 0,
152
- chatmode TEXT
153
- );
154
- CREATE INDEX IF NOT EXISTS idx_me_ts ON message_events(ts);
155
- CREATE INDEX IF NOT EXISTS idx_me_agent_ts ON message_events(agent_aid, ts);
156
- CREATE INDEX IF NOT EXISTS idx_me_peer_ts ON message_events(agent_aid, peer_key, ts);
157
-
158
- -- 按天预聚合表:grain = 天 × agent × peer × session × model × billing_fn。
159
- -- 由 writer.ts 写时增量 UPSERT 维护,rebuildDailyRollup() 全量重建纠偏。
160
- -- 保留 model+billing_fn 维度以便查询时仍按 calcCost 现算成本。
161
- -- 日级行很小,永留主库、不参与年度归档。
162
- CREATE TABLE IF NOT EXISTS usage_daily (
163
- day TEXT NOT NULL, -- 'YYYY-MM-DD' localtime
164
- agent_aid TEXT NOT NULL,
165
- peer_key TEXT NOT NULL,
166
- peer_type TEXT NOT NULL DEFAULT '',-- 'private' | 'group',由 peer_key 唯一决定
167
- session_id TEXT NOT NULL DEFAULT '',
168
- model TEXT NOT NULL,
169
- billing_fn TEXT NOT NULL,
170
- input_tokens INTEGER NOT NULL DEFAULT 0,
171
- output_tokens INTEGER NOT NULL DEFAULT 0,
172
- cache_creation_tokens INTEGER NOT NULL DEFAULT 0,
173
- cache_read_tokens INTEGER NOT NULL DEFAULT 0,
174
- cache_hit_tokens INTEGER NOT NULL DEFAULT 0,
175
- cache_miss_tokens INTEGER NOT NULL DEFAULT 0,
176
- image_tokens INTEGER NOT NULL DEFAULT 0,
177
- total_context_tokens INTEGER NOT NULL DEFAULT 0,
178
- turns INTEGER NOT NULL DEFAULT 0,
179
- calls INTEGER NOT NULL DEFAULT 0,
180
- cost_official_usd REAL,
181
- cost_official_cny REAL,
182
- cost_gateway_usd REAL,
183
- cost_gateway_cny REAL,
184
- PRIMARY KEY (day, agent_aid, peer_key, session_id, model, billing_fn)
185
- );
186
- CREATE INDEX IF NOT EXISTS idx_ud_day ON usage_daily(day);
187
- CREATE INDEX IF NOT EXISTS idx_ud_session ON usage_daily(session_id);
188
-
189
- -- 大模型调用明细:每次大模型调用一行(尽力而为;非 Claude 拿不到逐次时降级为单行累计)。
190
- -- 关联三个 ID:task_id(一次 runQuery)、session_id(evolcore 会话)、agent_session_id(SDK 会话)。
191
- CREATE TABLE IF NOT EXISTS model_calls (
192
- id INTEGER PRIMARY KEY AUTOINCREMENT,
193
- ts INTEGER NOT NULL,
194
- task_id TEXT NOT NULL,
195
- session_id TEXT,
196
- agent_session_id TEXT,
197
- agent_aid TEXT NOT NULL,
198
- peer_key TEXT NOT NULL,
199
- call_index INTEGER NOT NULL,
200
- model TEXT NOT NULL,
201
- request_id TEXT,
202
- message_id TEXT,
203
- input_tokens INTEGER NOT NULL DEFAULT 0,
204
- output_tokens INTEGER NOT NULL DEFAULT 0,
205
- cache_creation_tokens INTEGER NOT NULL DEFAULT 0,
206
- cache_read_tokens INTEGER NOT NULL DEFAULT 0,
207
- context_tokens INTEGER,
208
- max_tokens INTEGER,
209
- auto_compact_tokens INTEGER,
210
- degraded INTEGER NOT NULL DEFAULT 0
211
- );
212
- CREATE INDEX IF NOT EXISTS idx_mc_task ON model_calls(task_id);
213
- CREATE INDEX IF NOT EXISTS idx_mc_session ON model_calls(session_id, ts);
214
- CREATE INDEX IF NOT EXISTS idx_mc_agentsession ON model_calls(agent_session_id);
215
- CREATE INDEX IF NOT EXISTS idx_mc_ts ON model_calls(ts);
88
+ db.exec(`
89
+ CREATE TABLE IF NOT EXISTS usage_events (
90
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
91
+ ts INTEGER NOT NULL,
92
+ agent_aid TEXT NOT NULL,
93
+ peer_key TEXT NOT NULL,
94
+ usage_subject_key TEXT NOT NULL DEFAULT '',
95
+ role TEXT NOT NULL DEFAULT '',
96
+ peer_type TEXT,
97
+ session_id TEXT,
98
+ model TEXT NOT NULL,
99
+ billing_fn TEXT NOT NULL,
100
+ input_tokens INTEGER NOT NULL DEFAULT 0,
101
+ output_tokens INTEGER NOT NULL DEFAULT 0,
102
+ cache_creation_tokens INTEGER NOT NULL DEFAULT 0,
103
+ cache_read_tokens INTEGER NOT NULL DEFAULT 0,
104
+ cache_hit_tokens INTEGER,
105
+ cache_miss_tokens INTEGER,
106
+ image_tokens INTEGER,
107
+ total_context_tokens INTEGER,
108
+ turns INTEGER NOT NULL DEFAULT 1,
109
+ duration_ms INTEGER,
110
+ context_window_pct REAL,
111
+ cost_official_usd REAL,
112
+ cost_official_cny REAL,
113
+ cost_gateway_usd REAL,
114
+ cost_gateway_cny REAL
115
+ );
116
+ CREATE INDEX IF NOT EXISTS idx_ue_ts ON usage_events(ts);
117
+ CREATE INDEX IF NOT EXISTS idx_ue_agent_ts ON usage_events(agent_aid, ts);
118
+ CREATE INDEX IF NOT EXISTS idx_ue_peer_ts ON usage_events(agent_aid, peer_key, ts);
119
+ CREATE INDEX IF NOT EXISTS idx_ue_model_ts ON usage_events(model, ts);
120
+ CREATE INDEX IF NOT EXISTS idx_ue_session_ts ON usage_events(session_id, ts);
121
+
122
+ CREATE TABLE IF NOT EXISTS context_breakdown (
123
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
124
+ ts INTEGER NOT NULL,
125
+ agent_aid TEXT NOT NULL,
126
+ session_id TEXT NOT NULL,
127
+ turn_count INTEGER NOT NULL,
128
+ model TEXT NOT NULL,
129
+ max_tokens INTEGER NOT NULL,
130
+ system_prompt INTEGER,
131
+ system_tools INTEGER,
132
+ mcp_tools INTEGER,
133
+ custom_agents INTEGER,
134
+ memory_files INTEGER,
135
+ skills INTEGER,
136
+ messages INTEGER,
137
+ free_space INTEGER,
138
+ total_estimated INTEGER
139
+ );
140
+ CREATE INDEX IF NOT EXISTS idx_cb_session ON context_breakdown(session_id, ts);
141
+ CREATE INDEX IF NOT EXISTS idx_cb_agent ON context_breakdown(agent_aid, ts);
142
+
143
+ CREATE TABLE IF NOT EXISTS message_events (
144
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
145
+ ts INTEGER NOT NULL,
146
+ agent_aid TEXT NOT NULL,
147
+ peer_key TEXT NOT NULL,
148
+ direction TEXT NOT NULL,
149
+ msg_type TEXT,
150
+ bytes INTEGER NOT NULL DEFAULT 0,
151
+ encrypted INTEGER DEFAULT 0,
152
+ chatmode TEXT
153
+ );
154
+ CREATE INDEX IF NOT EXISTS idx_me_ts ON message_events(ts);
155
+ CREATE INDEX IF NOT EXISTS idx_me_agent_ts ON message_events(agent_aid, ts);
156
+ CREATE INDEX IF NOT EXISTS idx_me_peer_ts ON message_events(agent_aid, peer_key, ts);
157
+
158
+ -- 按天预聚合表:grain = 天 × agent × peer × session × model × billing_fn。
159
+ -- 由 writer.ts 写时增量 UPSERT 维护,rebuildDailyRollup() 全量重建纠偏。
160
+ -- 保留 model+billing_fn 维度以便查询时仍按 calcCost 现算成本。
161
+ -- 日级行很小,永留主库、不参与年度归档。
162
+ CREATE TABLE IF NOT EXISTS usage_daily (
163
+ day TEXT NOT NULL, -- 'YYYY-MM-DD' localtime
164
+ agent_aid TEXT NOT NULL,
165
+ peer_key TEXT NOT NULL,
166
+ peer_type TEXT NOT NULL DEFAULT '',-- 'private' | 'group',由 peer_key 唯一决定
167
+ session_id TEXT NOT NULL DEFAULT '',
168
+ model TEXT NOT NULL,
169
+ billing_fn TEXT NOT NULL,
170
+ input_tokens INTEGER NOT NULL DEFAULT 0,
171
+ output_tokens INTEGER NOT NULL DEFAULT 0,
172
+ cache_creation_tokens INTEGER NOT NULL DEFAULT 0,
173
+ cache_read_tokens INTEGER NOT NULL DEFAULT 0,
174
+ cache_hit_tokens INTEGER NOT NULL DEFAULT 0,
175
+ cache_miss_tokens INTEGER NOT NULL DEFAULT 0,
176
+ image_tokens INTEGER NOT NULL DEFAULT 0,
177
+ total_context_tokens INTEGER NOT NULL DEFAULT 0,
178
+ turns INTEGER NOT NULL DEFAULT 0,
179
+ calls INTEGER NOT NULL DEFAULT 0,
180
+ cost_official_usd REAL,
181
+ cost_official_cny REAL,
182
+ cost_gateway_usd REAL,
183
+ cost_gateway_cny REAL,
184
+ PRIMARY KEY (day, agent_aid, peer_key, session_id, model, billing_fn)
185
+ );
186
+ CREATE INDEX IF NOT EXISTS idx_ud_day ON usage_daily(day);
187
+ CREATE INDEX IF NOT EXISTS idx_ud_session ON usage_daily(session_id);
188
+
189
+ -- 大模型调用明细:每次大模型调用一行(尽力而为;非 Claude 拿不到逐次时降级为单行累计)。
190
+ -- 关联三个 ID:task_id(一次 runQuery)、session_id(evolcore 会话)、agent_session_id(SDK 会话)。
191
+ CREATE TABLE IF NOT EXISTS model_calls (
192
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
193
+ ts INTEGER NOT NULL,
194
+ task_id TEXT NOT NULL,
195
+ session_id TEXT,
196
+ agent_session_id TEXT,
197
+ agent_aid TEXT NOT NULL,
198
+ peer_key TEXT NOT NULL,
199
+ call_index INTEGER NOT NULL,
200
+ model TEXT NOT NULL,
201
+ request_id TEXT,
202
+ message_id TEXT,
203
+ input_tokens INTEGER NOT NULL DEFAULT 0,
204
+ output_tokens INTEGER NOT NULL DEFAULT 0,
205
+ cache_creation_tokens INTEGER NOT NULL DEFAULT 0,
206
+ cache_read_tokens INTEGER NOT NULL DEFAULT 0,
207
+ context_tokens INTEGER,
208
+ max_tokens INTEGER,
209
+ auto_compact_tokens INTEGER,
210
+ degraded INTEGER NOT NULL DEFAULT 0
211
+ );
212
+ CREATE INDEX IF NOT EXISTS idx_mc_task ON model_calls(task_id);
213
+ CREATE INDEX IF NOT EXISTS idx_mc_session ON model_calls(session_id, ts);
214
+ CREATE INDEX IF NOT EXISTS idx_mc_agentsession ON model_calls(agent_session_id);
215
+ CREATE INDEX IF NOT EXISTS idx_mc_ts ON model_calls(ts);
216
216
  `);
217
217
  // ── Schema Migration: 添加 cost 列 ───────────────────────────────────────
218
218
  try {
@@ -220,17 +220,17 @@ function _initTables(db) {
220
220
  const hasCostCols = cols.some((c) => c.name === 'cost_official_usd');
221
221
  if (!hasCostCols) {
222
222
  logger.info('[StatsDB] Migrating schema: adding cost columns to usage_events and usage_daily...');
223
- db.exec(`
224
- ALTER TABLE usage_events ADD COLUMN cost_official_usd REAL;
225
- ALTER TABLE usage_events ADD COLUMN cost_official_cny REAL;
226
- ALTER TABLE usage_events ADD COLUMN cost_gateway_usd REAL;
227
- ALTER TABLE usage_events ADD COLUMN cost_gateway_cny REAL;
223
+ db.exec(`
224
+ ALTER TABLE usage_events ADD COLUMN cost_official_usd REAL;
225
+ ALTER TABLE usage_events ADD COLUMN cost_official_cny REAL;
226
+ ALTER TABLE usage_events ADD COLUMN cost_gateway_usd REAL;
227
+ ALTER TABLE usage_events ADD COLUMN cost_gateway_cny REAL;
228
228
  `);
229
- db.exec(`
230
- ALTER TABLE usage_daily ADD COLUMN cost_official_usd REAL;
231
- ALTER TABLE usage_daily ADD COLUMN cost_official_cny REAL;
232
- ALTER TABLE usage_daily ADD COLUMN cost_gateway_usd REAL;
233
- ALTER TABLE usage_daily ADD COLUMN cost_gateway_cny REAL;
229
+ db.exec(`
230
+ ALTER TABLE usage_daily ADD COLUMN cost_official_usd REAL;
231
+ ALTER TABLE usage_daily ADD COLUMN cost_official_cny REAL;
232
+ ALTER TABLE usage_daily ADD COLUMN cost_gateway_usd REAL;
233
+ ALTER TABLE usage_daily ADD COLUMN cost_gateway_cny REAL;
234
234
  `);
235
235
  logger.info('[StatsDB] Migration complete. Run "ec stats --rebuild" to backfill historical costs.');
236
236
  }
@@ -295,26 +295,26 @@ export function rebuildDailyRollup(evolcoreHome) {
295
295
  try {
296
296
  db.exec('BEGIN');
297
297
  db.exec('DELETE FROM usage_daily');
298
- db.exec(`
299
- INSERT INTO usage_daily
300
- (day, agent_aid, peer_key, peer_type, session_id, model, billing_fn,
301
- input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens,
302
- cache_hit_tokens, cache_miss_tokens, image_tokens, total_context_tokens,
303
- turns, calls,
304
- cost_official_usd, cost_official_cny, cost_gateway_usd, cost_gateway_cny)
305
- SELECT
306
- strftime('%Y-%m-%d', ts/1000, 'unixepoch', 'localtime') AS day,
307
- agent_aid, peer_key, MAX(COALESCE(peer_type, '')),
308
- COALESCE(session_id, ''), model, billing_fn,
309
- SUM(input_tokens), SUM(output_tokens),
310
- SUM(cache_creation_tokens), SUM(cache_read_tokens),
311
- SUM(COALESCE(cache_hit_tokens, 0)), SUM(COALESCE(cache_miss_tokens, 0)),
312
- SUM(COALESCE(image_tokens, 0)), SUM(COALESCE(total_context_tokens, 0)),
313
- SUM(turns), COUNT(*),
314
- SUM(COALESCE(cost_official_usd, 0)), SUM(COALESCE(cost_official_cny, 0)),
315
- SUM(COALESCE(cost_gateway_usd, 0)), SUM(COALESCE(cost_gateway_cny, 0))
316
- FROM usage_events
317
- GROUP BY day, agent_aid, peer_key, COALESCE(session_id, ''), model, billing_fn
298
+ db.exec(`
299
+ INSERT INTO usage_daily
300
+ (day, agent_aid, peer_key, peer_type, session_id, model, billing_fn,
301
+ input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens,
302
+ cache_hit_tokens, cache_miss_tokens, image_tokens, total_context_tokens,
303
+ turns, calls,
304
+ cost_official_usd, cost_official_cny, cost_gateway_usd, cost_gateway_cny)
305
+ SELECT
306
+ strftime('%Y-%m-%d', ts/1000, 'unixepoch', 'localtime') AS day,
307
+ agent_aid, peer_key, MAX(COALESCE(peer_type, '')),
308
+ COALESCE(session_id, ''), model, billing_fn,
309
+ SUM(input_tokens), SUM(output_tokens),
310
+ SUM(cache_creation_tokens), SUM(cache_read_tokens),
311
+ SUM(COALESCE(cache_hit_tokens, 0)), SUM(COALESCE(cache_miss_tokens, 0)),
312
+ SUM(COALESCE(image_tokens, 0)), SUM(COALESCE(total_context_tokens, 0)),
313
+ SUM(turns), COUNT(*),
314
+ SUM(COALESCE(cost_official_usd, 0)), SUM(COALESCE(cost_official_cny, 0)),
315
+ SUM(COALESCE(cost_gateway_usd, 0)), SUM(COALESCE(cost_gateway_cny, 0))
316
+ FROM usage_events
317
+ GROUP BY day, agent_aid, peer_key, COALESCE(session_id, ''), model, billing_fn
318
318
  `);
319
319
  db.exec('COMMIT');
320
320
  const row = db.prepare('SELECT COUNT(*) AS n FROM usage_daily').get();
@@ -345,7 +345,7 @@ export function archiveOldData(evolcoreHome) {
345
345
  const cutoffYear = now.getFullYear(); // e.g. 2026
346
346
  const cutoffTs = Date.UTC(cutoffYear, 0, 1); // 2026-01-01 00:00:00 UTC
347
347
  // 找出主库中所有早于 cutoff 的年份
348
- const years = db.prepare(`SELECT DISTINCT CAST(strftime('%Y', ts/1000, 'unixepoch') AS INTEGER) AS y
348
+ const years = db.prepare(`SELECT DISTINCT CAST(strftime('%Y', ts/1000, 'unixepoch') AS INTEGER) AS y
349
349
  FROM usage_events WHERE ts < ? ORDER BY y`).all(cutoffTs).map((r) => r.y);
350
350
  if (years.length === 0)
351
351
  return;
@@ -27,15 +27,15 @@ function _calcTodayCosts(evolcoreHome, agentAid, peerKey) {
27
27
  params.push(peerKey);
28
28
  }
29
29
  try {
30
- const row = db.prepare(`
31
- SELECT
32
- SUM(input_tokens) AS input,
33
- SUM(output_tokens) AS output,
34
- SUM(cache_read_tokens) AS cacheRead,
35
- COUNT(*) AS calls,
36
- COALESCE(SUM(cost_gateway_usd), 0) AS usd,
37
- COALESCE(SUM(cost_gateway_cny), 0) AS cny
38
- FROM usage_events WHERE ${conds.join(' AND ')}
30
+ const row = db.prepare(`
31
+ SELECT
32
+ SUM(input_tokens) AS input,
33
+ SUM(output_tokens) AS output,
34
+ SUM(cache_read_tokens) AS cacheRead,
35
+ COUNT(*) AS calls,
36
+ COALESCE(SUM(cost_gateway_usd), 0) AS usd,
37
+ COALESCE(SUM(cost_gateway_cny), 0) AS cny
38
+ FROM usage_events WHERE ${conds.join(' AND ')}
39
39
  `).get(...params);
40
40
  return {
41
41
  usd: row?.usd ?? 0,