evolcore 0.0.11 → 0.0.12

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 (102) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/bin/codex-managed-hook.mjs +80 -0
  3. package/dist/agents/claude-runner.js +35 -2
  4. package/dist/agents/codex-app-server-client.js +120 -14
  5. package/dist/agents/codex-runner.js +273 -35
  6. package/dist/agents/ecagent-runner.js +36 -4
  7. package/dist/agents/gemini-runner.js +1 -0
  8. package/dist/aun/aid/client.js +11 -21
  9. package/dist/aun/aid/managed-operation.js +107 -0
  10. package/dist/aun/msg/group.js +81 -12
  11. package/dist/aun/msg/managed-operation.js +804 -0
  12. package/dist/aun/msg/mention-schema.js +20 -0
  13. package/dist/aun/msg/p2p.js +7 -0
  14. package/dist/channels/aun.js +288 -108
  15. package/dist/channels/daemon.js +13 -0
  16. package/dist/cli/agent-command.js +56 -8
  17. package/dist/cli/agent.js +170 -6
  18. package/dist/cli/aun-commands.js +270 -69
  19. package/dist/cli/bench.js +12 -3
  20. package/dist/cli/daemon-commands.js +31 -31
  21. package/dist/cli/fs-command.js +60 -0
  22. package/dist/cli/handoff-command.js +3 -0
  23. package/dist/cli/index.js +30 -2
  24. package/dist/cli/managed-command-guard.js +41 -0
  25. package/dist/cli/model.js +11 -4
  26. package/dist/cli/queue-command.js +11 -1
  27. package/dist/cli/response.js +71 -0
  28. package/dist/cli/restart-monitor.js +4 -18
  29. package/dist/cli/trigger-command.js +4 -0
  30. package/dist/config/builtin-roles.js +4 -0
  31. package/dist/config/contact-book-store.js +11 -3
  32. package/dist/config/contact-request-service.js +161 -15
  33. package/dist/config/mention-mode.js +8 -24
  34. package/dist/core/auth/agent-delegation.js +0 -1
  35. package/dist/core/auth/operation-authorizer.js +30 -7
  36. package/dist/core/auth/operation-catalog.js +187 -25
  37. package/dist/core/bootstrap-messages.js +18 -0
  38. package/dist/core/bootstrap-service.js +63 -4
  39. package/dist/core/capability/providers/claude-capability-provider.js +9 -31
  40. package/dist/core/capability/providers/codex-capability-provider.js +4 -15
  41. package/dist/core/capability/skill-discovery.js +55 -0
  42. package/dist/core/channel-loader.js +1 -1
  43. package/dist/core/command/cli-intent-parser.js +9 -0
  44. package/dist/core/command/command-handler.js +467 -6
  45. package/dist/core/command/connect-menu.js +15 -0
  46. package/dist/core/command/group-menu.js +4 -4
  47. package/dist/core/command/menu-catalog.js +366 -0
  48. package/dist/core/command/menu-handler.js +147 -35
  49. package/dist/core/command/menu-protocol.js +71 -0
  50. package/dist/core/command/slash-gate.js +5 -2
  51. package/dist/core/command/slash-handler.js +41 -21
  52. package/dist/core/daemon-file-cache.js +2 -1
  53. package/dist/core/data-migration.js +9 -2
  54. package/dist/core/evolagent.js +4 -2
  55. package/dist/core/handoff/dispatcher.js +5 -2
  56. package/dist/core/handoff/runtime.js +145 -21
  57. package/dist/core/handoff/store.js +24 -0
  58. package/dist/core/handoff/types.js +1 -0
  59. package/dist/core/message/logical-queue-bridge.js +1 -0
  60. package/dist/core/message/mention-schema.js +126 -0
  61. package/dist/core/message/message-bridge.js +236 -78
  62. package/dist/core/message/message-log.js +1 -0
  63. package/dist/core/message/message-queue.js +1 -0
  64. package/dist/core/message/response-engine.js +54 -36
  65. package/dist/core/permission/ec-command-parser.js +156 -10
  66. package/dist/core/permission/sandbox-runtime.js +22 -1
  67. package/dist/core/permission/tool-policy.js +73 -30
  68. package/dist/core/protected-paths.js +7 -2
  69. package/dist/core/session/session-manager.js +21 -2
  70. package/dist/eck/manifest-engine.js +39 -13
  71. package/dist/index.js +384 -98
  72. package/dist/ipc.js +218 -9
  73. package/dist/response-system/coordinator.js +6 -4
  74. package/dist/response-system/engines/v1/proactive-flow.js +26 -8
  75. package/dist/response-system/modes/single-session/index.js +14 -1
  76. package/dist/response-system/selector.js +6 -5
  77. package/dist/trigger/anomaly-store.js +4 -0
  78. package/dist/trigger/script-executor.js +1 -0
  79. package/dist/utils/codex-app-server-registry.js +90 -0
  80. package/dist/utils/codex-cli.js +5 -1
  81. package/dist/utils/cross-platform.js +15 -0
  82. package/dist/utils/npm-ops.js +5 -12
  83. package/dist/utils/tool-summary.js +11 -0
  84. package/kits/docs/channels/aun.md +1 -1
  85. package/kits/docs/context-assembly.md +2 -2
  86. package/kits/docs/evolcore/agent.md +11 -6
  87. package/kits/docs/evolcore/aid.md +14 -0
  88. package/kits/docs/prompt-loading-architecture.md +1 -2
  89. package/kits/docs/venues/group.md +1 -1
  90. package/kits/eck_manifest.json +0 -12
  91. package/kits/rules/04-relation.md +1 -1
  92. package/kits/rules/05-venue.md +2 -2
  93. package/kits/schemas/single-session.schema.1.json +3 -3
  94. package/kits/schemas/single-session.schema.2.json +3 -3
  95. package/kits/templates/roles/admin.json +22 -0
  96. package/kits/templates/roles/member.json +27 -0
  97. package/kits/templates/roles/visitor.json +33 -3
  98. package/kits/templates/system-fragments/bootstrap.md +1 -1
  99. package/kits/templates/system-fragments/channel.md +1 -1
  100. package/kits/templates/system-fragments/session.md +1 -1
  101. package/package.json +2 -2
  102. package/dist/core/command/evol-menu-version-gate.js +0 -39
@@ -1,27 +1,11 @@
1
- /**
2
- * mentionMode ↔ AUN 协议 dispatch_mode 词汇翻译(单一事实源)。
3
- *
4
- * 配置层用 mentionMode(disabled / mention-only),AUN 协议层用 dispatch_mode
5
- * (broadcast / mention)。二者语义等价,仅词汇不同:
6
- * - mention-only(只处理被 @ 的消息)≡ mention(仅被 @ 时响应)
7
- * - disabled(关闭 mention 过滤) ≡ broadcast(全部响应)
8
- *
9
- * 仅在协议边界(AUN resolver、render fallback)翻译;配置层、命令层一律用
10
- * mentionMode 词汇。
11
- */
12
- /** config → AUN 协议词汇;未知/未设值返回 undefined(回退服务端 dispatch_mode)。 */
13
- export function mentionModeToDispatch(mode) {
14
- if (mode === 'mention-only')
15
- return 'mention';
16
- if (mode === 'disabled')
17
- return 'broadcast';
18
- return undefined;
19
- }
20
- /** AUN 协议词汇 → config;未知返回 undefined。 */
21
- export function dispatchToMentionMode(dispatch) {
22
- if (dispatch === 'mention')
23
- return 'mention-only';
24
- if (dispatch === 'broadcast')
1
+ /** AUN `mention_mode` and EvolCore `mentionMode` share these wire values. */
2
+ export function normalizeMentionMode(mode) {
3
+ if (typeof mode !== 'string')
4
+ return undefined;
5
+ const normalized = mode.trim().toLowerCase();
6
+ if (normalized === 'disabled')
25
7
  return 'disabled';
8
+ if (normalized === 'mention-only' || normalized === 'mention_only')
9
+ return 'mention-only';
26
10
  return undefined;
27
11
  }
@@ -155,7 +155,6 @@ export function authorizeDelegatedAunMsgSend(registry, input) {
155
155
  channelId,
156
156
  chatType: grant.chatType,
157
157
  conversationId: channelId,
158
- identity: { role: grant.issuedRole, mode: 'interactive' },
159
158
  processOwners: [],
160
159
  fromControlChannel: false,
161
160
  });
@@ -173,10 +173,9 @@ function authorizeCommandInternal(ctx, resolvedConfigCommand) {
173
173
  };
174
174
  }
175
175
  function matchCommandPermission(operation, category, commandPerms, isDangerous) {
176
- const namespace = operation.split('.')[0];
177
176
  const matches = [];
178
177
  for (const [rule, permission] of Object.entries(commandPerms)) {
179
- const rank = getRuleRank(rule, permission, operation, namespace, category, isDangerous);
178
+ const rank = getRuleRank(rule, permission, operation, category, isDangerous);
180
179
  if (rank > 0) {
181
180
  matches.push({ permission, matchedRule: rule, rank });
182
181
  }
@@ -186,12 +185,17 @@ function matchCommandPermission(operation, category, commandPerms, isDangerous)
186
185
  matches.sort((a, b) => b.rank - a.rank);
187
186
  return matches[0];
188
187
  }
189
- function getRuleRank(rule, permission, operation, namespace, category, isDangerous) {
188
+ function getRuleRank(rule, permission, operation, category, isDangerous) {
190
189
  const denyOffset = permission.allow ? 0 : 1;
191
190
  if (rule === operation)
192
191
  return 1000 + denyOffset;
193
- if (rule === `${namespace}.*`)
194
- return 800 + denyOffset;
192
+ if (rule.endsWith('.*')) {
193
+ const prefix = rule.slice(0, -1);
194
+ if (operation.startsWith(prefix)) {
195
+ const depth = rule.slice(0, -2).split('.').length;
196
+ return 800 + Math.min((depth - 1) * 20, 180) + denyOffset;
197
+ }
198
+ }
195
199
  if (rule.startsWith('category:') && rule.slice('category:'.length) === category)
196
200
  return 600 + denyOffset;
197
201
  if (rule === 'dangerous:*' && isDangerous)
@@ -230,7 +234,7 @@ function checkConstraints(ctx, constraints, resolvedConfigCommand) {
230
234
  const sameGroup = constraints.currentRelationOnly ? ' and may only target that same group' : '';
231
235
  return {
232
236
  ok: false,
233
- reason: `Denied ${ctx.intent.operation}: role=${ctx.role}, source_chat=${ctx.chatType ?? 'unknown'}, target_group=${target}. This role may send group messages only from a group-chat task${sameGroup}; the target being a group is not sufficient.`,
237
+ reason: `Denied ${ctx.intent.operation}: role=${ctx.role}, source_chat=${ctx.chatType ?? 'unknown'}, target_group=${target}. This role may access group operations only from a group-chat task${sameGroup}; the target being a group is not sufficient.`,
234
238
  };
235
239
  }
236
240
  if (constraints.requireDaemonOwner && !ctx.isDaemonOwner) {
@@ -401,7 +405,7 @@ function checkCurrentRelation(ctx, resolvedConfigCommand) {
401
405
  const targetGroup = stringArg(ctx.intent.args.targetId) ?? peerIdFromArg(requestedPeer);
402
406
  return {
403
407
  ok: false,
404
- reason: `Denied ${ctx.intent.operation}: role=${ctx.role}, current_group=${currentGroup}, target_group=${targetGroup}. This role may send only to the current group; cross-group sends require admin or owner permission.`,
408
+ reason: `Denied ${ctx.intent.operation}: role=${ctx.role}, current_group=${currentGroup}, target_group=${targetGroup}. This role may access only the current group; cross-group access requires admin or owner permission.`,
405
409
  };
406
410
  }
407
411
  return { ok: false, reason: 'Only the current relation can be targeted' };
@@ -498,6 +502,25 @@ function checkOwnPeer(ctx) {
498
502
  return { ok: true };
499
503
  const requestedPeerId = argPeer ? peerIdFromArg(argPeer) : undefined;
500
504
  const requestedPeerKeyId = argPeerKey ? peerIdFromArg(argPeerKey) : undefined;
505
+ if (ctx.chatType === 'group') {
506
+ if (!expectedPeerKey)
507
+ return { ok: false, reason: 'Cannot verify peer ownership' };
508
+ const candidateKeys = new Set();
509
+ if (argPeerKey)
510
+ candidateKeys.add(argPeerKey);
511
+ if (argPeer) {
512
+ candidateKeys.add(argPeer);
513
+ const channelType = parseChannelType(expectedPeerKey);
514
+ if (channelType)
515
+ candidateKeys.add(formatPeerKey(channelType, argPeer));
516
+ const legacyChannelType = parseLegacyChannelType(expectedPeerKey);
517
+ if (legacyChannelType)
518
+ candidateKeys.add(`${legacyChannelType}::${argPeer}`);
519
+ }
520
+ return candidateKeys.has(expectedPeerKey)
521
+ ? { ok: true }
522
+ : { ok: false, reason: 'Only the current group relation can be targeted' };
523
+ }
501
524
  if (ctx.actorId) {
502
525
  if (requestedPeerId && requestedPeerId !== ctx.actorId) {
503
526
  return { ok: false, reason: 'Only the actor peer can be targeted' };
@@ -21,6 +21,82 @@ const CONFIG_MANAGEMENT_OPERATIONS = [
21
21
  description,
22
22
  sources: ['menu.cli', 'agent-tool', 'control'],
23
23
  }));
24
+ const GROUP_MANAGEMENT_OPERATIONS = [
25
+ ['ec.group.list', 'read', ['agent'], 'List groups joined by the current Agent'],
26
+ ['ec.group.info', 'read', ['relation', 'agent'], 'Read group information'],
27
+ ['ec.group.pull', 'read', ['relation', 'agent'], 'Pull group messages'],
28
+ ['ec.group.members', 'read', ['relation', 'agent'], 'List group members'],
29
+ ['ec.group.online', 'read', ['relation', 'agent'], 'List online group members'],
30
+ ['ec.group.rules.get', 'read', ['relation', 'agent'], 'Read the published group rules file'],
31
+ ['ec.group.rules.read', 'read', ['relation', 'agent'], 'Read legacy group join rules'],
32
+ ['ec.group.create', 'write-agent', ['agent'], 'Create a group as the current Agent'],
33
+ ['ec.group.ack', 'write-agent', ['agent'], 'Advance the current Agent group cursor'],
34
+ ['ec.group.update', 'write-agent', ['agent'], 'Update group metadata'],
35
+ ['ec.group.dissolve', 'write-agent', ['agent'], 'Dissolve a group'],
36
+ ['ec.group.suspend', 'write-agent', ['agent'], 'Suspend a group'],
37
+ ['ec.group.resume', 'write-agent', ['agent'], 'Resume a group'],
38
+ ['ec.group.join', 'write-agent', ['agent'], 'Request to join a group'],
39
+ ['ec.group.leave', 'write-agent', ['agent'], 'Leave a group'],
40
+ ['ec.group.invite', 'write-agent', ['agent'], 'Invite group members'],
41
+ ['ec.group.kick', 'write-agent', ['agent'], 'Remove a group member'],
42
+ ['ec.group.role', 'write-agent', ['agent'], 'Change a group member role'],
43
+ ['ec.group.owner', 'write-agent', ['agent'], 'Transfer group ownership'],
44
+ ['ec.group.ban', 'write-agent', ['agent'], 'Read or update the group ban list'],
45
+ ['ec.group.unban', 'write-agent', ['agent'], 'Remove a group ban'],
46
+ ['ec.group.rules.set', 'write-agent', ['agent'], 'Upload and publish group rules content'],
47
+ ['ec.group.rules.publish', 'write-agent', ['agent'], 'Publish the current group rules file'],
48
+ ['ec.group.rules.update', 'write-agent', ['agent'], 'Update group join rules'],
49
+ ].map(([id, category, defaultScopes, description]) => ({
50
+ id,
51
+ category,
52
+ dangerous: false,
53
+ defaultScopes,
54
+ description,
55
+ sources: ['agent-tool'],
56
+ }));
57
+ const MSG_READ_OPERATIONS = [
58
+ ['ec.msg.history', 'Read message history for an AUN private relation'],
59
+ ['ec.msg.online', 'Read online status for an AUN private relation'],
60
+ ].map(([id, description]) => ({
61
+ id,
62
+ category: 'read',
63
+ dangerous: false,
64
+ defaultScopes: ['relation', 'agent'],
65
+ description,
66
+ sources: ['agent-tool'],
67
+ }));
68
+ const GROUP_FS_READ_OPERATIONS = ['ls', 'stat', 'lstat', 'cat', 'find', 'df'].map(kind => ({
69
+ id: `ec.fs.${kind}`,
70
+ category: 'read',
71
+ dangerous: false,
72
+ defaultScopes: ['relation', 'agent'],
73
+ description: `Read current group filesystem (${kind})`,
74
+ sources: ['agent-tool'],
75
+ }));
76
+ const HANDOFF_READ_OPERATIONS = ['status', 'list', 'trace'].map(kind => ({
77
+ id: `ec.handoff.${kind}`,
78
+ category: 'read',
79
+ dangerous: false,
80
+ defaultScopes: ['relation', 'agent'],
81
+ description: `Read handoff ${kind}`,
82
+ sources: ['agent-tool'],
83
+ }));
84
+ const HANDOFF_WRITE_OPERATIONS = [{
85
+ id: 'ec.handoff.return',
86
+ category: 'write-own',
87
+ dangerous: false,
88
+ defaultScopes: ['relation', 'agent'],
89
+ description: 'Return the current handoff result',
90
+ sources: ['agent-tool'],
91
+ }];
92
+ const QUEUE_OPERATIONS = [['read', 'Read the current Agent message queue'], ['clear', 'Clear the current Agent message queue'], ['cancel', 'Cancel a current Agent queued message'], ['interrupt', 'Interrupt a current Agent session']].map(([kind, description]) => ({
93
+ id: `ec.queue.${kind}`,
94
+ category: kind === 'read' ? 'read' : 'write-agent',
95
+ dangerous: false,
96
+ defaultScopes: ['agent'],
97
+ description,
98
+ sources: ['agent-tool'],
99
+ }));
24
100
  /**
25
101
  * Operation Catalog - 所有命令操作的元数据定义
26
102
  *
@@ -34,7 +110,7 @@ const OPERATIONS = [
34
110
  dangerous: false,
35
111
  defaultScopes: ['relation', 'role', 'agent'],
36
112
  description: '列出可用模型',
37
- sources: ['slash', 'menu', 'menu.cli'],
113
+ sources: ['slash', 'menu', 'menu.cli', 'agent-tool'],
38
114
  },
39
115
  {
40
116
  id: 'model.current',
@@ -42,7 +118,7 @@ const OPERATIONS = [
42
118
  dangerous: false,
43
119
  defaultScopes: ['relation', 'role', 'agent'],
44
120
  description: '查询当前使用的模型',
45
- sources: ['slash', 'menu', 'menu.cli'],
121
+ sources: ['slash', 'menu', 'menu.cli', 'agent-tool'],
46
122
  },
47
123
  {
48
124
  id: 'model.effort.current',
@@ -50,7 +126,7 @@ const OPERATIONS = [
50
126
  dangerous: false,
51
127
  defaultScopes: ['relation', 'role', 'agent'],
52
128
  description: '查询当前推理努力度',
53
- sources: ['slash', 'menu', 'menu.cli'],
129
+ sources: ['slash', 'menu', 'menu.cli', 'agent-tool'],
54
130
  },
55
131
  {
56
132
  id: 'model.info',
@@ -74,7 +150,7 @@ const OPERATIONS = [
74
150
  dangerous: false,
75
151
  defaultScopes: ['relation', 'role', 'agent'],
76
152
  description: '切换使用的模型',
77
- sources: ['slash', 'menu', 'menu.cli'],
153
+ sources: ['slash', 'menu', 'menu.cli', 'agent-tool'],
78
154
  },
79
155
  {
80
156
  id: 'model.effort',
@@ -82,7 +158,7 @@ const OPERATIONS = [
82
158
  dangerous: false,
83
159
  defaultScopes: ['relation', 'role', 'agent'],
84
160
  description: '设置推理努力度',
85
- sources: ['slash', 'menu', 'menu.cli'],
161
+ sources: ['slash', 'menu', 'menu.cli', 'agent-tool'],
86
162
  },
87
163
  {
88
164
  id: 'model.reset',
@@ -195,7 +271,7 @@ const OPERATIONS = [
195
271
  dangerous: false,
196
272
  defaultScopes: ['relation'],
197
273
  description: '查看当前会话状态',
198
- sources: ['slash', 'menu'],
274
+ sources: ['slash', 'menu', 'agent-tool'],
199
275
  },
200
276
  {
201
277
  id: 'session.topic.current',
@@ -227,7 +303,7 @@ const OPERATIONS = [
227
303
  dangerous: false,
228
304
  defaultScopes: ['relation'],
229
305
  description: '重命名会话',
230
- sources: ['slash', 'menu'],
306
+ sources: ['slash', 'menu', 'agent-tool'],
231
307
  },
232
308
  {
233
309
  id: 'session.delete',
@@ -244,7 +320,7 @@ const OPERATIONS = [
244
320
  dangerous: false,
245
321
  defaultScopes: ['relation', 'agent'],
246
322
  description: '查看当前项目摘要',
247
- sources: ['menu'],
323
+ sources: ['menu', 'agent-tool'],
248
324
  },
249
325
  {
250
326
  id: 'file.list',
@@ -481,6 +557,14 @@ const OPERATIONS = [
481
557
  description: '列出当前 Agent 可用的 BaseAgent',
482
558
  sources: ['menu', 'control'],
483
559
  },
560
+ {
561
+ id: 'agent.baseagent.update',
562
+ category: 'write-agent',
563
+ dangerous: false,
564
+ defaultScopes: ['agent'],
565
+ description: '切换当前 Agent 使用的 BaseAgent',
566
+ sources: ['menu'],
567
+ },
484
568
  {
485
569
  id: 'agent.getConfig',
486
570
  category: 'read',
@@ -505,6 +589,14 @@ const OPERATIONS = [
505
589
  description: '重载 agent 配置',
506
590
  sources: ['slash', 'menu', 'control'],
507
591
  },
592
+ {
593
+ id: 'agent.bootstrapComplete',
594
+ category: 'write-agent',
595
+ dangerous: false,
596
+ defaultScopes: ['agent'],
597
+ description: 'Complete bootstrap for the current Agent',
598
+ sources: ['agent-tool'],
599
+ },
508
600
  {
509
601
  id: 'agent.delete',
510
602
  category: 'dangerous',
@@ -520,7 +612,7 @@ const OPERATIONS = [
520
612
  dangerous: false,
521
613
  defaultScopes: ['process'],
522
614
  description: '查看系统状态(敏感信息)',
523
- sources: ['slash', 'menu', 'menu.cli'],
615
+ sources: ['slash', 'menu', 'menu.cli', 'agent-tool'],
524
616
  },
525
617
  {
526
618
  id: 'activity.current',
@@ -530,6 +622,14 @@ const OPERATIONS = [
530
622
  description: '查看当前中间活动显示策略',
531
623
  sources: ['menu'],
532
624
  },
625
+ {
626
+ id: 'activity.update',
627
+ category: 'write-agent',
628
+ dangerous: false,
629
+ defaultScopes: ['relation', 'agent'],
630
+ description: '更新中间活动显示策略',
631
+ sources: ['menu'],
632
+ },
533
633
  {
534
634
  id: 'observable.current',
535
635
  category: 'read',
@@ -538,6 +638,14 @@ const OPERATIONS = [
538
638
  description: '查看当前 Agent 观察者模式',
539
639
  sources: ['menu', 'control'],
540
640
  },
641
+ {
642
+ id: 'observable.update',
643
+ category: 'write-agent',
644
+ dangerous: false,
645
+ defaultScopes: ['agent'],
646
+ description: '更新当前 Agent 观察者模式',
647
+ sources: ['menu'],
648
+ },
541
649
  {
542
650
  id: 'capability.read',
543
651
  category: 'read',
@@ -649,41 +757,41 @@ const OPERATIONS = [
649
757
  id: 'storage.list',
650
758
  category: 'read',
651
759
  dangerous: false,
652
- defaultScopes: ['control'],
760
+ defaultScopes: ['agent', 'control'],
653
761
  description: '列出存储对象(敏感信息)',
654
- sources: ['menu.cli', 'control'],
762
+ sources: ['menu.cli', 'agent-tool', 'control'],
655
763
  },
656
764
  {
657
765
  id: 'storage.quota',
658
766
  category: 'read',
659
767
  dangerous: false,
660
- defaultScopes: ['control'],
768
+ defaultScopes: ['agent', 'control'],
661
769
  description: '查看存储配额(敏感信息)',
662
- sources: ['menu.cli', 'control'],
770
+ sources: ['menu.cli', 'agent-tool', 'control'],
663
771
  },
664
772
  {
665
773
  id: 'storage.upload',
666
774
  category: 'write-agent',
667
775
  dangerous: true,
668
- defaultScopes: ['control'],
776
+ defaultScopes: ['agent', 'control'],
669
777
  description: '上传文件到云存储',
670
- sources: ['menu.cli', 'control'],
778
+ sources: ['menu.cli', 'agent-tool', 'control'],
671
779
  },
672
780
  {
673
781
  id: 'storage.download',
674
782
  category: 'write-agent',
675
783
  dangerous: true,
676
- defaultScopes: ['control'],
784
+ defaultScopes: ['agent', 'control'],
677
785
  description: '从云存储下载文件',
678
- sources: ['menu.cli', 'control'],
786
+ sources: ['menu.cli', 'agent-tool', 'control'],
679
787
  },
680
788
  {
681
789
  id: 'storage.delete',
682
790
  category: 'dangerous',
683
791
  dangerous: true,
684
- defaultScopes: ['control'],
792
+ defaultScopes: ['agent', 'control'],
685
793
  description: '删除云存储对象',
686
- sources: ['menu.cli', 'control'],
794
+ sources: ['menu.cli', 'agent-tool', 'control'],
687
795
  },
688
796
  // ── Gateway & Config Operations ──
689
797
  {
@@ -745,6 +853,52 @@ const OPERATIONS = [
745
853
  // ── EC Command Operations ──
746
854
  // EC daemon 入口根据结构化 IPC 请求构造 operation;Runner 不解析 EC 子命令。
747
855
  ...CONFIG_MANAGEMENT_OPERATIONS,
856
+ ...GROUP_MANAGEMENT_OPERATIONS,
857
+ ...MSG_READ_OPERATIONS,
858
+ ...GROUP_FS_READ_OPERATIONS,
859
+ ...HANDOFF_READ_OPERATIONS,
860
+ ...HANDOFF_WRITE_OPERATIONS,
861
+ ...QUEUE_OPERATIONS,
862
+ {
863
+ id: 'ec.version',
864
+ category: 'read',
865
+ dangerous: false,
866
+ defaultScopes: ['relation', 'agent', 'process'],
867
+ description: 'Read local EvolCore component versions',
868
+ sources: ['menu.cli', 'agent-tool'],
869
+ },
870
+ {
871
+ id: 'ec.aid.show',
872
+ category: 'read',
873
+ dangerous: true,
874
+ defaultScopes: ['relation', 'agent'],
875
+ description: 'Inspect the current Agent local AID identity and signing state',
876
+ sources: ['agent-tool'],
877
+ },
878
+ {
879
+ id: 'ec.aid.lookup',
880
+ category: 'read',
881
+ dangerous: false,
882
+ defaultScopes: ['relation', 'agent'],
883
+ description: 'Look up the current Agent public AID record',
884
+ sources: ['agent-tool'],
885
+ },
886
+ {
887
+ id: 'ec.aid.agentmd.get',
888
+ category: 'read',
889
+ dangerous: false,
890
+ defaultScopes: ['relation', 'agent'],
891
+ description: 'Read and verify the current Agent public agent.md',
892
+ sources: ['agent-tool'],
893
+ },
894
+ {
895
+ id: 'ec.aid.agentmd.put',
896
+ category: 'write-agent',
897
+ dangerous: true,
898
+ defaultScopes: ['agent'],
899
+ description: 'Sign and publish the current Agent local agent.md',
900
+ sources: ['agent-tool'],
901
+ },
748
902
  {
749
903
  id: 'ec.msg.send',
750
904
  category: 'write-own',
@@ -779,20 +933,28 @@ const OPERATIONS = [
779
933
  },
780
934
  {
781
935
  id: 'ec.ctl.send',
782
- category: 'write-agent',
936
+ category: 'write-own',
783
937
  dangerous: false,
784
- defaultScopes: ['control', 'agent'],
785
- description: '通过 ec ctl send 发送控制通道消息',
938
+ defaultScopes: ['relation'],
939
+ description: '通过 ec ctl send 向当前会话发送消息',
786
940
  sources: ['agent-tool'],
787
941
  },
788
942
  {
789
943
  id: 'ec.ctl.file',
790
- category: 'write-agent',
944
+ category: 'write-own',
791
945
  dangerous: false,
792
- defaultScopes: ['control', 'agent'],
793
- description: '通过 ec ctl file 发送控制通道文件',
946
+ defaultScopes: ['relation'],
947
+ description: '通过 ec ctl file 向当前会话发送项目文件',
794
948
  sources: ['agent-tool'],
795
949
  },
950
+ ...['read', 'clear', 'cancel', 'interrupt'].map(action => ({
951
+ id: `ec.ctl.queue.${action}`,
952
+ category: action === 'read' ? 'read' : 'write-own',
953
+ dangerous: false,
954
+ defaultScopes: ['relation'],
955
+ description: `${action} the current managed session queue`,
956
+ sources: ['agent-tool'],
957
+ })),
796
958
  // ── Raw CLI & Shell Execution ──
797
959
  {
798
960
  id: 'cli.exec.raw',
@@ -48,3 +48,21 @@ export function preparePostBootstrapWelcomeOutbox(aid, owner, ownerName) {
48
48
  },
49
49
  });
50
50
  }
51
+ export function hasPendingPostBootstrapWelcomeOutbox(aid) {
52
+ return !!outbox.findByDedupeKey(aid, postBootstrapWelcomeOperationId(aid));
53
+ }
54
+ /** Bind the durable completion welcome to the fresh post-bootstrap session. */
55
+ export function bindPostBootstrapWelcomeOutboxSession(aid, sessionId) {
56
+ const entry = outbox.findByDedupeKey(aid, postBootstrapWelcomeOperationId(aid));
57
+ if (!entry)
58
+ return false;
59
+ if (entry.context?.sessionId === sessionId)
60
+ return true;
61
+ return outbox.replace(aid, {
62
+ ...entry,
63
+ context: {
64
+ ...(entry.context ?? {}),
65
+ sessionId,
66
+ },
67
+ });
68
+ }
@@ -11,8 +11,12 @@ import { activeBaseagent } from './model/config-scope.js';
11
11
  import { buildEnvelope } from './message/message-utils.js';
12
12
  import { BOOTSTRAP_MESSAGE_TTL_MS, bootstrapInitialMessageOperationId } from './bootstrap-messages.js';
13
13
  /** Coordinate the write-ahead completion welcome with the lifecycle commit. */
14
- export async function completeBootstrapWithWelcome(service, aid, welcome) {
14
+ export async function completeBootstrapWithWelcome(service, aid, welcome, sessions) {
15
15
  const lifecycle = service.lifecycleOf(aid);
16
+ if (lifecycle !== 'bootstrapping' && lifecycle !== 'active') {
17
+ const result = service.completeBootstrap(aid);
18
+ return { ...result, reloaded: false };
19
+ }
16
20
  if (lifecycle === 'bootstrapping') {
17
21
  if (!welcome) {
18
22
  return {
@@ -31,13 +35,68 @@ export async function completeBootstrapWithWelcome(service, aid, welcome) {
31
35
  };
32
36
  }
33
37
  }
38
+ const welcomePending = welcome
39
+ ? await welcome.hasPendingPostBootstrapWelcome()
40
+ : false;
41
+ const needsMainSessionBoundary = lifecycle === 'bootstrapping' || welcomePending;
42
+ let mainSession;
43
+ if (needsMainSessionBoundary) {
44
+ if (!sessions) {
45
+ return {
46
+ ok: false,
47
+ code: 'MAIN_SESSION_UNAVAILABLE',
48
+ error: `No session coordinator available to create the post-bootstrap main session for ${aid}`,
49
+ reloaded: false,
50
+ };
51
+ }
52
+ try {
53
+ mainSession = await sessions.ensurePostBootstrapMainSession();
54
+ }
55
+ catch (e) {
56
+ return {
57
+ ok: false,
58
+ code: 'MAIN_SESSION_CREATE_FAILED',
59
+ error: e instanceof Error ? e.message : `Failed to create the post-bootstrap main session for ${aid}`,
60
+ reloaded: false,
61
+ };
62
+ }
63
+ if (!mainSession.sessionId) {
64
+ return {
65
+ ok: false,
66
+ code: 'MAIN_SESSION_CREATE_FAILED',
67
+ error: `Failed to create the post-bootstrap main session for ${aid}`,
68
+ reloaded: false,
69
+ };
70
+ }
71
+ if (welcomePending && welcome && !await welcome.bindPostBootstrapWelcomeToSession(mainSession.sessionId)) {
72
+ return {
73
+ ok: false,
74
+ code: 'WELCOME_SESSION_BIND_FAILED',
75
+ error: `Failed to bind the post-bootstrap welcome to session ${mainSession.sessionId}`,
76
+ reloaded: false,
77
+ };
78
+ }
79
+ }
34
80
  const result = service.completeBootstrap(aid);
35
81
  if (!result.ok)
36
82
  return { ...result, reloaded: false };
37
- if (!welcome)
38
- return { ...result, reloaded: true, welcomeQueued: false };
83
+ if (!welcome) {
84
+ return {
85
+ ...result,
86
+ reloaded: true,
87
+ welcomeQueued: false,
88
+ mainSessionId: mainSession?.sessionId,
89
+ mainSessionCreated: mainSession?.created,
90
+ };
91
+ }
39
92
  const welcomeQueued = await welcome.reconcilePostBootstrapWelcome();
40
- return { ...result, reloaded: true, welcomeQueued };
93
+ return {
94
+ ...result,
95
+ reloaded: true,
96
+ welcomeQueued,
97
+ mainSessionId: mainSession?.sessionId,
98
+ mainSessionCreated: mainSession?.created,
99
+ };
41
100
  }
42
101
  export class BootstrapService {
43
102
  agentRegistry;
@@ -2,6 +2,7 @@ import fs from 'fs';
2
2
  import os from 'os';
3
3
  import path from 'path';
4
4
  import { execFileSync } from 'child_process';
5
+ import { discoverSkillDirectories } from '../skill-discovery.js';
5
6
  function readJson(file) {
6
7
  try {
7
8
  if (!fs.existsSync(file))
@@ -12,31 +13,6 @@ function readJson(file) {
12
13
  return null;
13
14
  }
14
15
  }
15
- function listDirectories(dir) {
16
- try {
17
- if (!fs.existsSync(dir))
18
- return [];
19
- return fs.readdirSync(dir, { withFileTypes: true })
20
- .filter(entry => entry.isDirectory())
21
- .map(entry => entry.name);
22
- }
23
- catch {
24
- return [];
25
- }
26
- }
27
- function readSkillDescription(skillDir) {
28
- const file = path.join(skillDir, 'SKILL.md');
29
- try {
30
- const text = fs.readFileSync(file, 'utf-8');
31
- const firstBodyLine = text.split(/\r?\n/)
32
- .map(line => line.trim())
33
- .find(line => line && !line.startsWith('#') && !line.startsWith('---') && !line.includes(':'));
34
- return firstBodyLine ? firstBodyLine.slice(0, 180) : undefined;
35
- }
36
- catch {
37
- return undefined;
38
- }
39
- }
40
16
  function describeMcpSpec(spec) {
41
17
  const command = typeof spec?.command === 'string' ? spec.command : undefined;
42
18
  if (command)
@@ -60,14 +36,15 @@ function discoverSkills(projectPath) {
60
36
  const seen = new Set();
61
37
  const items = [];
62
38
  for (const root of roots) {
63
- for (const name of listDirectories(root.dir)) {
64
- if (seen.has(name))
39
+ for (const skill of discoverSkillDirectories(root.dir)) {
40
+ const { id } = skill;
41
+ if (seen.has(id))
65
42
  continue;
66
- seen.add(name);
43
+ seen.add(id);
67
44
  items.push({
68
- id: name,
69
- label: name,
70
- desc: readSkillDescription(path.join(root.dir, name)),
45
+ id,
46
+ label: id,
47
+ desc: skill.description,
71
48
  source: root.source,
72
49
  });
73
50
  }
@@ -138,6 +115,7 @@ async function discoverPluginsFromCli() {
138
115
  encoding: 'utf-8',
139
116
  timeout: 3000,
140
117
  stdio: ['ignore', 'pipe', 'pipe'],
118
+ windowsHide: process.platform === 'win32',
141
119
  });
142
120
  const parsed = JSON.parse(output);
143
121
  const list = Array.isArray(parsed) ? parsed : Array.isArray(parsed?.installed) ? parsed.installed : [];