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/cli/stats.js CHANGED
@@ -3,54 +3,55 @@
3
3
  */
4
4
  import { resolveRoot } from '../paths.js';
5
5
  import { wantsHelp } from './help.js';
6
+ import { readBestTaskRuntimeContext } from './task-context.js';
6
7
  import { queryAggregated, querySessionTurns, queryContextBreakdown, queryTopPeers, queryTopModels, queryMessageAggregated, queryPeerList, querySummary, queryPeerDaily, queryTaskModelCalls, querySessionModelCalls } from '../stats/query.js';
7
8
  import { getBudgetStatus } from '../stats/budget.js';
8
- const HELP = `ec stats — Token 用量与费用统计
9
-
10
- Usage: ec stats [options]
11
-
12
- 时间范围:
13
- (无参数) 今日概览
14
- --today 同上
15
- --hour 最近 24 小时,按小时分组
16
- --week 本周
17
- --month 本月
18
- --from <date> --to <date> 任意区间(YYYY-MM-DD)
19
-
20
- 维度过滤(可组合):
21
- --agent <aid> 指定 agent
22
- --peer <X> 对端(裸 AID 自动前缀 aun#,或 channel#id 格式)
23
- --model <model-id> 指定模型
24
- --session <id> 指定会话
25
-
26
- 聚合粒度:
27
- --by hour|day|week|month|model|peer|agent
28
-
29
- 快捷视图:
30
- --session <id> 单个会话明细
31
- --session <id> --last 会话最后一轮用量 + 累计(等价 status.completed)
32
- --context <id> 会话 context breakdown 细目
33
- --budget 预算状态
34
- --top-peers [--limit N] 对端排行
35
- --top-models [--limit N] 模型排行
36
- --traffic 消息收发条数与字节数
37
- --sql "<query>" 直接执行只读 SQL
38
- --rebuild 全量重建日聚合表 usage_daily(运维兜底/排查)
39
- --peers [--limit N] 私聊对端列表(带累计 token/calls/活跃日)
40
- --groups [--limit N] 群聊列表(同上)
41
- --summary 指定时间范围总消耗汇总(token/USD/CNY)
42
- --peer-detail <id> 指定对端 AID 或 peer_key,按天返回消耗明细
43
- --task-calls <taskId> 一个 task 的逐次大模型调用明细
44
- --session-calls <id> 一个会话的逐次大模型调用明细
45
-
46
- 输出格式:
47
- --format json JSON 输出
48
- --help, -h 显示此帮助
49
-
50
- 示例:
51
- ec stats
52
- ec stats --month --agent bot.agentid.pub
53
- ec stats --peer alice.aid.pub
9
+ const HELP = `ec stats — Token 用量与费用统计
10
+
11
+ Usage: ec stats [options]
12
+
13
+ 时间范围:
14
+ (无参数) 今日概览
15
+ --today 同上
16
+ --hour 最近 24 小时,按小时分组
17
+ --week 本周
18
+ --month 本月
19
+ --from <date> --to <date> 任意区间(YYYY-MM-DD)
20
+
21
+ 维度过滤(可组合):
22
+ --agent <aid> 指定 agent
23
+ --peer <X> 对端(裸 AID 自动前缀 aun#,或 channel#id 格式)
24
+ --model <model-id> 指定模型
25
+ --session <id> 指定会话
26
+
27
+ 聚合粒度:
28
+ --by hour|day|week|month|model|peer|agent
29
+
30
+ 快捷视图:
31
+ --session <id> 单个会话明细
32
+ --session <id> --last 会话最后一轮用量 + 累计(等价 status.completed)
33
+ --context <id> 会话 context breakdown 细目
34
+ --budget 预算状态
35
+ --top-peers [--limit N] 对端排行
36
+ --top-models [--limit N] 模型排行
37
+ --traffic 消息收发条数与字节数
38
+ --sql "<query>" 直接执行只读 SQL
39
+ --rebuild 全量重建日聚合表 usage_daily(运维兜底/排查)
40
+ --peers [--limit N] 私聊对端列表(带累计 token/calls/活跃日)
41
+ --groups [--limit N] 群聊列表(同上)
42
+ --summary 指定时间范围总消耗汇总(token/USD/CNY)
43
+ --peer-detail <id> 指定对端 AID 或 peer_key,按天返回消耗明细
44
+ --task-calls <taskId> 一个 task 的逐次大模型调用明细
45
+ --session-calls <id> 一个会话的逐次大模型调用明细
46
+
47
+ 输出格式:
48
+ --format json JSON 输出
49
+ --help, -h 显示此帮助
50
+
51
+ 示例:
52
+ ec stats
53
+ ec stats --month --agent bot.agentid.pub
54
+ ec stats --peer alice.aid.pub
54
55
  ec stats --session abc123 --format json`;
55
56
  const RESET = '\x1b[0m';
56
57
  const BOLD = '\x1b[1m';
@@ -102,6 +103,98 @@ function parseDateArg(s) {
102
103
  throw new Error(`Invalid date: ${s}`);
103
104
  return d.getTime();
104
105
  }
106
+ /**
107
+ * Views that read the whole stats database rather than one authorized scope.
108
+ * `--sql` is a read-only escape hatch over every table, and `--rebuild` writes.
109
+ * Neither can be narrowed to a relation, so both stay outside managed tasks.
110
+ */
111
+ const MANAGED_FORBIDDEN_FLAGS = ['sql', 'rebuild'];
112
+ /**
113
+ * Views that enumerate peers other than the caller. They are meaningful only
114
+ * for an agent-wide scope, so a relation-scoped caller cannot use them.
115
+ */
116
+ const AGENT_SCOPE_FLAGS = ['peers', 'groups', 'top-peers'];
117
+ /** Relation roles allowed to read usage across the whole agent. */
118
+ const AGENT_SCOPE_ROLES = new Set(['owner', 'admin']);
119
+ /**
120
+ * Resolve the authorized stats scope for a managed Agent task.
121
+ *
122
+ * `ec stats` reads the usage database directly, so authorization has to happen
123
+ * here: owner and admin may read the whole agent, and every other relation role
124
+ * is confined to its own relation and session. Returns undefined outside a
125
+ * managed task, leaving direct terminal usage unchanged.
126
+ */
127
+ async function resolveManagedStatsScope(flags, isJson) {
128
+ if (!process.env.EVOLCORE_SESSION_ID)
129
+ return undefined;
130
+ const runtime = await readBestTaskRuntimeContext();
131
+ const role = runtime?.peerRole?.trim();
132
+ const selfAid = runtime?.selfAid?.trim();
133
+ // Fail closed: without a resolved identity and role there is no scope to
134
+ // authorize against, and a managed caller must never fall back to full access.
135
+ if (!selfAid || !role || role === 'none') {
136
+ fail(isJson, 'NOT_ALLOWED', 'stats 在托管任务中需要已解析的关系身份与角色');
137
+ }
138
+ for (const flag of MANAGED_FORBIDDEN_FLAGS) {
139
+ if (flags[flag])
140
+ fail(isJson, 'NOT_ALLOWED', `--${flag} 在托管任务中不可用`);
141
+ }
142
+ if (AGENT_SCOPE_ROLES.has(role))
143
+ return { agentWide: true, agentAid: selfAid, role };
144
+ for (const flag of AGENT_SCOPE_FLAGS) {
145
+ if (flags[flag])
146
+ fail(isJson, 'NOT_ALLOWED', `--${flag} 仅 owner/admin 可用`);
147
+ }
148
+ const channel = runtime?.channel?.trim();
149
+ const peerId = runtime?.peerId?.trim();
150
+ if (!channel || !peerId) {
151
+ fail(isJson, 'NOT_ALLOWED', `角色 ${role} 只能查看自己的关系用量,但当前对端标识不完整`);
152
+ }
153
+ const sessionId = process.env.EVOLCORE_SESSION_ID;
154
+ return {
155
+ agentWide: false,
156
+ agentAid: selfAid,
157
+ peerKey: `${channel}#${encodeURIComponent(peerId)}`,
158
+ sessionId,
159
+ role,
160
+ };
161
+ }
162
+ /**
163
+ * Reject an explicit selector that points outside the authorized scope instead
164
+ * of silently narrowing it, so a caller never reads a value as their own.
165
+ */
166
+ function enforceManagedSelector(scope, flags, isJson) {
167
+ // owner/admin read across the host, so an explicit --agent stays available to
168
+ // them. Every other role is pinned to the agent that owns this task.
169
+ if (scope.agentWide)
170
+ return;
171
+ const requestedAgent = typeof flags.agent === 'string' ? flags.agent.trim() : undefined;
172
+ if (requestedAgent && requestedAgent !== scope.agentAid) {
173
+ fail(isJson, 'NOT_ALLOWED', `角色 ${scope.role} 只能查看 ${scope.agentAid} 的用量`);
174
+ }
175
+ const normalizePeer = (value) => value.includes('#') ? value : `aun#${encodeURIComponent(value)}`;
176
+ for (const flag of ['peer', 'peer-detail']) {
177
+ const raw = flags[flag];
178
+ if (typeof raw !== 'string' || !raw.trim())
179
+ continue;
180
+ if (normalizePeer(raw.trim()) !== scope.peerKey) {
181
+ fail(isJson, 'NOT_ALLOWED', `角色 ${scope.role} 只能查看自己的关系用量`);
182
+ }
183
+ }
184
+ for (const flag of ['session', 'context', 'session-calls']) {
185
+ const raw = flags[flag];
186
+ if (typeof raw !== 'string' || !raw.trim())
187
+ continue;
188
+ if (raw.trim() !== scope.sessionId) {
189
+ fail(isJson, 'NOT_ALLOWED', `角色 ${scope.role} 只能查看当前会话的用量`);
190
+ }
191
+ }
192
+ // A task id cannot be checked against this relation without a second lookup,
193
+ // and it addresses one runQuery across the whole agent. Keep it privileged.
194
+ if (typeof flags['task-calls'] === 'string' && flags['task-calls'].trim()) {
195
+ fail(isJson, 'NOT_ALLOWED', '--task-calls 仅 owner/admin 可用');
196
+ }
197
+ }
105
198
  export async function handleStats(args) {
106
199
  // Help
107
200
  if (wantsHelp(args)) {
@@ -130,6 +223,11 @@ export async function handleStats(args) {
130
223
  }
131
224
  }
132
225
  const isJson = flags.format === 'json';
226
+ // Authorize before any branch runs: `--rebuild` writes, and every read view
227
+ // below would otherwise see the whole database.
228
+ const managedScope = await resolveManagedStatsScope(flags, isJson);
229
+ if (managedScope)
230
+ enforceManagedSelector(managedScope, flags, isJson);
133
231
  // 全量重建日聚合表(运维兜底/排查),不依赖时间范围。
134
232
  if (flags.rebuild) {
135
233
  const { rebuildDailyRollup, getDb } = await import('../stats/db.js');
@@ -148,11 +246,11 @@ export async function handleStats(args) {
148
246
  const batch = allEvents.slice(i, i + batchSize);
149
247
  for (const event of batch) {
150
248
  const prices = resolvePrices(home, event); // 不传 gatewayPricing,全走本地 JSONL
151
- db.prepare(`
152
- UPDATE usage_events
153
- SET cost_official_usd = ?, cost_official_cny = ?,
154
- cost_gateway_usd = ?, cost_gateway_cny = ?
155
- WHERE id = ?
249
+ db.prepare(`
250
+ UPDATE usage_events
251
+ SET cost_official_usd = ?, cost_official_cny = ?,
252
+ cost_gateway_usd = ?, cost_gateway_cny = ?
253
+ WHERE id = ?
156
254
  `).run(prices.official?.usd ?? null, prices.official?.cny ?? null, prices.gateway?.usd ?? null, prices.gateway?.cny ?? null, event.id);
157
255
  updated++;
158
256
  }
@@ -223,6 +321,18 @@ export async function handleStats(args) {
223
321
  filter.model = flags.model;
224
322
  if (flags.session && typeof flags.session === 'string')
225
323
  filter.session_id = flags.session;
324
+ // Pin the authorized scope into the filter itself. Rejecting an out-of-scope
325
+ // selector is not enough: a relation-scoped caller that passes no selector at
326
+ // all would otherwise aggregate across every agent and peer in the database.
327
+ if (managedScope && !managedScope.agentWide) {
328
+ filter.agent_aid = managedScope.agentAid;
329
+ filter.peer_key = managedScope.peerKey;
330
+ // A bare peer id would take the LIKE branch below and could also match the
331
+ // same id on another channel. Pin the exact key that was authorized.
332
+ if (typeof flags['peer-detail'] === 'string' && managedScope.peerKey) {
333
+ flags['peer-detail'] = managedScope.peerKey;
334
+ }
335
+ }
226
336
  // ── 结构化查询:私聊/群聊列表、总消耗汇总、对端按天明细 ──────────────────
227
337
  if (flags.peers || flags.groups) {
228
338
  const peerType = flags.peers ? 'private' : 'group';
@@ -81,62 +81,62 @@ export async function cmdTrigger(args) {
81
81
  }
82
82
  }
83
83
  function printHelp() {
84
- console.log(`用法: ec trigger <subcommand>
85
-
86
- 命令:
87
- list [--agent <aid>] [--all] [--json]
88
- show [--agent <aid>] <triggerId> [--json]
89
- history [--agent <aid>] [triggerId] [--limit <n>] [--json]
90
- create --exec <script|trigger-session|target-session> --file <trigger.json|trigger-dir> [--enable] [--json]
91
- OR
92
- create [--agent <aid>] --exec <script|trigger-session|target-session> --cron <expr>|--event <pattern> (--prompt <text>|--prompt-file <path>) [--enable] [其他flag]
93
- set ... (create 的兼容别名)
94
- update [--agent <aid>] <triggerId> <参数...> [--if-revision <sha256:...>] [--json]
95
- enable [--agent <aid>] <triggerId>
96
- disable [--agent <aid>] <triggerId>
97
- cancel [--agent <aid>] <triggerId> (disable 的兼容别名)
98
- delete|remove|rm [--agent <aid>] <triggerId>
99
- run [--agent <aid>] <triggerId> [--dry-run] [--json]
100
- template list|show <name> [--json]
101
-
102
- Create 参数模式支持:
103
- --once | --delay <时长> | --at <ISO时间> | --cron <表达式> | --every <时长> | --event <事件模式>
104
- --exec <script|trigger-session|target-session> (必填)
105
- --prompt <文本> | --prompt-file <路径>
106
- Shell 双引号内的双引号、反引号和变量形式的美元符号都需在前面加反斜杠;trigger 保存的是还原后的原文
107
- --script-path <路径> --script-runtime <node|python|bash>
108
- --script-args <JSON> --script-timeout <时长>
109
- --feedback <target> (origin/silent 仅可通过 --file 导入)
110
- --baseagent <name> (仅 --exec trigger-session)
111
- --model <模型> --effort <low|medium|high|xhigh|max>
112
- --max-runs <次数> --max-duration <时长: 30s|15m|2h|1d>
113
- --permission <readonly|auto|request|bypass|fullaccess>(省略则继承当前身份配置;fullaccess 仅 daemon-owner 且 feature flag 开启时可用)
114
- --tz <时区> (仅 cron)
115
- --target-channel <channelKey> --target-channel-id <ID> (AUN 可简写为 aun)
116
- --target-chat-type <private|group> (AUN 目标必填;channelId 同时作为群聊 ID)
117
- --target-session <main|thread> [--target-thread-id <threadId>]
118
- --trigger-thread <by-run|by-trigger> (兼容旧值 per-run)
119
- --name <名称>
120
-
121
- 通用输出参数:
122
- --json | --format json
123
-
124
- Update 参数:
125
- --once | --delay | --at | --cron | --every | --event [--tz]
126
- --prompt <文本> | --prompt-file <路径> --script-file <路径> --name <名称>
127
- --description <文本> (空字符串删除)
128
- --model <模型> --effort <强度> --permission <权限模式|inherit>(inherit 清除覆盖)
129
- --max-runs <次数> --max-duration <时长>
130
- --concurrency <forbid|replace|allow> --missed-policy <skip|run-once|run-all>
131
- --feedback <origin|target|silent>
132
- --target-channel <channelKey> --target-channel-id <ID> (必须同时指定)
133
- --target-chat-type <private|group> --target-session <main|thread> [--target-thread-id <threadId>]
134
- --trigger-thread <by-run|by-trigger> --baseagent <name> (仅 trigger_session 之间)
135
- --on-error <fail|retry> --noop-sentinel <文本>
136
- --retry-attempts <次数> --retry-backoff <毫秒>
137
- --script-runtime <node|python|bash> --script-args <JSON> --script-timeout <时长>
138
-
139
- 省略 --agent 时使用当前 agent AID;无会话上下文或跨身份操作时需要显式指定。
84
+ console.log(`用法: ec trigger <subcommand>
85
+
86
+ 命令:
87
+ list [--agent <aid>] [--all] [--json]
88
+ show [--agent <aid>] <triggerId> [--json]
89
+ history [--agent <aid>] [triggerId] [--limit <n>] [--json]
90
+ create --exec <script|trigger-session|target-session> --file <trigger.json|trigger-dir> [--enable] [--json]
91
+ OR
92
+ create [--agent <aid>] --exec <script|trigger-session|target-session> --cron <expr>|--event <pattern> (--prompt <text>|--prompt-file <path>) [--enable] [其他flag]
93
+ set ... (create 的兼容别名)
94
+ update [--agent <aid>] <triggerId> <参数...> [--if-revision <sha256:...>] [--json]
95
+ enable [--agent <aid>] <triggerId>
96
+ disable [--agent <aid>] <triggerId>
97
+ cancel [--agent <aid>] <triggerId> (disable 的兼容别名)
98
+ delete|remove|rm [--agent <aid>] <triggerId>
99
+ run [--agent <aid>] <triggerId> [--dry-run] [--json]
100
+ template list|show <name> [--json]
101
+
102
+ Create 参数模式支持:
103
+ --once | --delay <时长> | --at <ISO时间> | --cron <表达式> | --every <时长> | --event <事件模式>
104
+ --exec <script|trigger-session|target-session> (必填)
105
+ --prompt <文本> | --prompt-file <路径>
106
+ Shell 双引号内的双引号、反引号和变量形式的美元符号都需在前面加反斜杠;trigger 保存的是还原后的原文
107
+ --script-path <路径> --script-runtime <node|python|bash>
108
+ --script-args <JSON> --script-timeout <时长>
109
+ --feedback <target> (origin/silent 仅可通过 --file 导入)
110
+ --baseagent <name> (仅 --exec trigger-session)
111
+ --model <模型> --effort <low|medium|high|xhigh|max>
112
+ --max-runs <次数> --max-duration <时长: 30s|15m|2h|1d>
113
+ --permission <readonly|auto|request|bypass|fullaccess>(省略则继承当前身份配置;fullaccess 仅 daemon-owner 且 feature flag 开启时可用)
114
+ --tz <时区> (仅 cron)
115
+ --target-channel <channelKey> --target-channel-id <ID> (AUN 可简写为 aun)
116
+ --target-chat-type <private|group> (AUN 目标必填;channelId 同时作为群聊 ID)
117
+ --target-session <main|thread> [--target-thread-id <threadId>]
118
+ --trigger-thread <by-run|by-trigger> (兼容旧值 per-run)
119
+ --name <名称>
120
+
121
+ 通用输出参数:
122
+ --json | --format json
123
+
124
+ Update 参数:
125
+ --once | --delay | --at | --cron | --every | --event [--tz]
126
+ --prompt <文本> | --prompt-file <路径> --script-file <路径> --name <名称>
127
+ --description <文本> (空字符串删除)
128
+ --model <模型> --effort <强度> --permission <权限模式|inherit>(inherit 清除覆盖)
129
+ --max-runs <次数> --max-duration <时长>
130
+ --concurrency <forbid|replace|allow> --missed-policy <skip|run-once|run-all>
131
+ --feedback <origin|target|silent>
132
+ --target-channel <channelKey> --target-channel-id <ID> (必须同时指定)
133
+ --target-chat-type <private|group> --target-session <main|thread> [--target-thread-id <threadId>]
134
+ --trigger-thread <by-run|by-trigger> --baseagent <name> (仅 trigger_session 之间)
135
+ --on-error <fail|retry> --noop-sentinel <文本>
136
+ --retry-attempts <次数> --retry-backoff <毫秒>
137
+ --script-runtime <node|python|bash> --script-args <JSON> --script-timeout <时长>
138
+
139
+ 省略 --agent 时使用当前 agent AID;无会话上下文或跨身份操作时需要显式指定。
140
140
  任务会话中的跨身份操作仅允许目标 Agent 上具备 owner/admin 权限的调用者执行 list/show/history;创建、修改、启停、删除和运行仍只允许当前 Agent。`);
141
141
  }
142
142
  async function listTriggers(args, json) {
@@ -48,7 +48,10 @@ export function getManagementCommandPermissions(role) {
48
48
  'config.unset': { allow: true, scopes: ['agent', 'relation'] },
49
49
  'role.policy.write': { allow: true, dangerous: true, scopes: ['agent'], constraints: { requireAgentAdmin: true } },
50
50
  'ec.aid.show': { allow: true, dangerous: true, scopes: ['agent'], constraints: { targetCurrentAgentOnly: true, requireAgentAdmin: true } },
51
+ 'ec.aid.lookup': { allow: true, scopes: ['agent'] },
52
+ 'ec.aid.agentmd.get': { allow: true, scopes: ['agent'] },
51
53
  'ec.aid.agentmd.put': { allow: true, dangerous: true, scopes: ['agent'], constraints: { targetCurrentAgentOnly: true, requireAgentAdmin: true } },
54
+ 'ec.aid.avatar': { allow: true, dangerous: true, scopes: ['agent'], constraints: { targetCurrentAgentOnly: true, requireAgentOwner: true } },
52
55
  'ec.fs.mkdir': { allow: true, permissionMode: 'auto', scopes: ['relation'], constraints: { groupOnly: true, currentRelationOnly: true, targetCurrentAgentOnly: true } },
53
56
  'ec.fs.upload': { allow: true, permissionMode: 'auto', scopes: ['relation'], constraints: { groupOnly: true, currentRelationOnly: true, targetCurrentAgentOnly: true } },
54
57
  'ec.fs.copy': { allow: true, permissionMode: 'auto', scopes: ['relation'], constraints: { groupOnly: true, currentRelationOnly: true, targetCurrentAgentOnly: true } },
@@ -679,6 +679,21 @@ function groupFor(target, sel) {
679
679
  function normalizeAgentConfigForWrite(value) {
680
680
  const mutable = normalizeBaseagentEffortCompat({ ...value });
681
681
  normalizeShowActivitiesCompat(mutable);
682
+ if (Object.prototype.hasOwnProperty.call(mutable, 'owners')) {
683
+ if (!Array.isArray(mutable.owners)) {
684
+ throw new ConfigError('VALIDATION_ERROR', 'owners must be an array');
685
+ }
686
+ const owners = [];
687
+ for (const rawOwner of mutable.owners) {
688
+ if (typeof rawOwner !== 'string' || !rawOwner.trim() || !isValidAid(rawOwner.trim())) {
689
+ throw new ConfigError('VALIDATION_ERROR', `Invalid owner AID: ${JSON.stringify(rawOwner)}`);
690
+ }
691
+ const owner = rawOwner.trim();
692
+ if (!owners.includes(owner))
693
+ owners.push(owner);
694
+ }
695
+ mutable.owners = owners;
696
+ }
682
697
  // AID 格式校验
683
698
  if (mutable.aid && !isValidAid(mutable.aid)) {
684
699
  throw new ConfigError('VALIDATION_ERROR', `Invalid aid "${mutable.aid}" (must be a valid multi-level domain like mybot.agentid.pub)`);
@@ -301,7 +301,15 @@ function executeSet(op, options) {
301
301
  if (!op.route)
302
302
  return { ok: false, code: 'UNKNOWN_FIELD', error: `Unknown config field: ${op.field}` };
303
303
  const selector = selectorFor(op, options);
304
- mutateConfig(op.route.target, selector, existing => {
304
+ const mutation = mutateConfig(op.route.target, selector, existing => {
305
+ if (op.field === 'owners' && op.route.target === ConfigTarget.Agent) {
306
+ const nextOwners = Array.isArray(op.value) ? op.value : [];
307
+ if (nextOwners.length === 0) {
308
+ throw new ConfigError('LAST_OWNER_PROTECTED', 'owners must contain at least one owner');
309
+ }
310
+ setNested(existing, op.field, nextOwners);
311
+ return nextOwners;
312
+ }
305
313
  if (op.route.merge === 'list') {
306
314
  const previous = getNested(existing, op.field);
307
315
  const previousItems = Array.isArray(previous) ? previous : [];
@@ -316,16 +324,18 @@ function executeSet(op, options) {
316
324
  }
317
325
  });
318
326
  setNested(existing, op.field, merged);
327
+ return merged;
319
328
  }
320
329
  else {
321
330
  setNested(existing, op.field, op.value);
331
+ return op.value;
322
332
  }
323
333
  });
324
334
  return {
325
335
  ok: true,
326
336
  subcommand: 'set',
327
337
  field: op.field,
328
- value: op.value,
338
+ value: mutation.value,
329
339
  scope: op.configScope,
330
340
  file: op.route.schema,
331
341
  };
@@ -334,7 +344,14 @@ function executeUnset(op, options) {
334
344
  if (!op.route)
335
345
  return { ok: false, code: 'UNKNOWN_FIELD', error: `Unknown config field: ${op.field}` };
336
346
  const selector = selectorFor(op, options);
337
- const { value: removed } = mutateConfig(op.route.target, selector, existing => {
347
+ const target = op.route.target;
348
+ const { value: removed } = mutateConfig(target, selector, existing => {
349
+ if (op.field === 'owners' && target === ConfigTarget.Agent) {
350
+ const current = getNested(existing, op.field);
351
+ if (Array.isArray(current) && current.length > 0) {
352
+ throw new ConfigError('LAST_OWNER_PROTECTED', 'cannot unset owners while an Agent still has owners; use owners replace with another owner');
353
+ }
354
+ }
338
355
  const parts = op.field.split('.');
339
356
  let current = existing;
340
357
  for (let i = 0; i < parts.length - 1; i++) {
@@ -2,6 +2,7 @@ import { normalizeCliArgv } from '../cli/cli-argv.js';
2
2
  import { parseConfigSelector } from '../cli/config-selector.js';
3
3
  import { parseConfigFieldValue, resolveConfigFieldRule, } from './config-field-policy.js';
4
4
  import { ConfigError, routeFieldPath } from './config-manager.js';
5
+ import { isValidAid } from '../aun/aid/validation.js';
5
6
  const SELECTOR_OPTIONS = {
6
7
  '--self': 'value',
7
8
  '--peer': 'value',
@@ -166,7 +167,13 @@ function resolveFieldCommand(argv, subcommand, options = {}) {
166
167
  const rawValue = subcommand === 'set' ? business.values[1] : undefined;
167
168
  let value;
168
169
  if (subcommand === 'set') {
169
- if (fieldRule.class === 'safe-scalar') {
170
+ if (field === 'owners') {
171
+ const parsedOwners = parseOwnersValue(rawValue);
172
+ if (!parsedOwners.ok)
173
+ return parsedOwners;
174
+ value = parsedOwners.value;
175
+ }
176
+ else if (fieldRule.class === 'safe-scalar') {
170
177
  const parsedValue = parseConfigFieldValue(field, rawValue);
171
178
  if (!parsedValue.ok)
172
179
  return { ok: false, code: 'INVALID_CONFIG_VALUE', reason: parsedValue.reason };
@@ -486,6 +493,37 @@ function coerceConfigValue(raw, route) {
486
493
  return Number(raw);
487
494
  return raw;
488
495
  }
496
+ function parseOwnersValue(raw) {
497
+ let values;
498
+ const trimmed = raw.trim();
499
+ if (trimmed.startsWith('[')) {
500
+ try {
501
+ values = JSON.parse(trimmed);
502
+ }
503
+ catch {
504
+ return { ok: false, code: 'INVALID_CONFIG_VALUE', reason: 'owners must be a valid JSON array of AIDs' };
505
+ }
506
+ if (!Array.isArray(values)) {
507
+ return { ok: false, code: 'INVALID_CONFIG_VALUE', reason: 'owners must be a JSON array of AIDs' };
508
+ }
509
+ }
510
+ else {
511
+ values = [raw];
512
+ }
513
+ if (!Array.isArray(values) || values.length === 0) {
514
+ return { ok: false, code: 'INVALID_CONFIG_VALUE', reason: 'owners must contain at least one AID' };
515
+ }
516
+ const owners = [];
517
+ for (const value of values) {
518
+ if (typeof value !== 'string' || !value.trim() || !isValidAid(value.trim())) {
519
+ return { ok: false, code: 'INVALID_CONFIG_VALUE', reason: `Invalid owner AID: ${JSON.stringify(value)}` };
520
+ }
521
+ const owner = value.trim();
522
+ if (!owners.includes(owner))
523
+ owners.push(owner);
524
+ }
525
+ return { ok: true, value: owners };
526
+ }
489
527
  function configErrorResult(error) {
490
528
  if (error instanceof ConfigError)
491
529
  return { ok: false, code: error.code, reason: error.message };
@@ -33,10 +33,11 @@ export function buildAuthSubject(input) {
33
33
  && fullAccessAuthorization.processRole === 'fullaccess-run'
34
34
  && fullAccessAuthorization.dataScope === 'daemon';
35
35
  const isDaemonService = input.trustedProcessRole === 'daemon-service';
36
- const isDaemonOwner = !isDaemonService && !isFullAccessRun && isProcessOwner({
36
+ const isTrustedDaemonOwner = input.trustedProcessRole === 'daemon-owner';
37
+ const isDaemonOwner = !isDaemonService && !isFullAccessRun && (isTrustedDaemonOwner || isProcessOwner({
37
38
  actor: roleDetail.actor,
38
39
  processOwners,
39
- });
40
+ }));
40
41
  const suppliedRole = input.identity?.role && input.identity.role !== 'none' ? input.identity.role : undefined;
41
42
  const role = suppliedRole ?? roleDetail.effectiveRole ?? 'none';
42
43
  const processRole = isFullAccessRun