evolcore 0.0.11 → 0.0.13

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 (164) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/bin/codex-managed-hook.mjs +80 -0
  3. package/bin/ec-safe-output.js +26 -7
  4. package/dist/agents/claude-runner.js +86 -7
  5. package/dist/agents/codex-app-server-client.js +174 -20
  6. package/dist/agents/codex-runner.js +375 -92
  7. package/dist/agents/ecagent-runner.js +39 -4
  8. package/dist/agents/gemini-runner.js +1 -0
  9. package/dist/aun/aid/client.js +11 -21
  10. package/dist/aun/aid/control-aid.js +36 -13
  11. package/dist/aun/aid/managed-operation.js +107 -0
  12. package/dist/aun/aid/store.js +13 -7
  13. package/dist/aun/group-fs-download.js +56 -0
  14. package/dist/aun/group-identity.js +1 -2
  15. package/dist/aun/msg/group.js +91 -44
  16. package/dist/aun/msg/index.js +1 -2
  17. package/dist/aun/msg/managed-operation.js +865 -0
  18. package/dist/aun/msg/mention-schema.js +20 -0
  19. package/dist/aun/msg/p2p.js +7 -0
  20. package/dist/aun/msg/upload.js +16 -1
  21. package/dist/aun/outbox.js +31 -2
  22. package/dist/aun/rpc/client.js +66 -0
  23. package/dist/aun/rpc/index.js +1 -2
  24. package/dist/aun/storage/{upload.js → client.js} +11 -2
  25. package/dist/aun/storage/index.js +1 -2
  26. package/dist/channels/aun.js +655 -240
  27. package/dist/channels/daemon.js +13 -0
  28. package/dist/cli/agent-command.js +56 -8
  29. package/dist/cli/agent.js +108 -31
  30. package/dist/cli/aun-commands.js +270 -69
  31. package/dist/cli/bench.js +12 -3
  32. package/dist/cli/cli-argv.js +10 -0
  33. package/dist/cli/config.js +9 -17
  34. package/dist/cli/ctl-command.js +1 -1
  35. package/dist/cli/daemon-commands.js +70 -55
  36. package/dist/cli/fs-command.js +79 -10
  37. package/dist/cli/handoff-command.js +3 -0
  38. package/dist/cli/index.js +30 -2
  39. package/dist/cli/init.js +77 -8
  40. package/dist/cli/managed-command-guard.js +41 -0
  41. package/dist/cli/model.js +11 -4
  42. package/dist/cli/queue-command.js +53 -1
  43. package/dist/cli/response.js +71 -0
  44. package/dist/cli/restart-monitor.js +7 -32
  45. package/dist/cli/trigger-command.js +11 -0
  46. package/dist/config/access-policy.js +1 -2
  47. package/dist/config/builtin-roles.js +9 -0
  48. package/dist/config/config-manager.js +21 -1
  49. package/dist/config/contact-book-store.js +11 -3
  50. package/dist/config/contact-request-service.js +161 -15
  51. package/dist/config/mention-mode.js +8 -24
  52. package/dist/config/peer-role-resolver.js +6 -2
  53. package/dist/config/role-migration-startup.js +53 -0
  54. package/dist/config/role-store.js +1 -1
  55. package/dist/core/auth/agent-delegation.js +1 -2
  56. package/dist/core/auth/auth-gateway.js +29 -1
  57. package/dist/core/auth/authorization-audit.js +15 -0
  58. package/dist/core/auth/operation-authorizer.js +31 -7
  59. package/dist/core/auth/operation-catalog.js +196 -25
  60. package/dist/core/bootstrap-messages.js +25 -0
  61. package/dist/core/bootstrap-service.js +64 -4
  62. package/dist/core/capability/providers/claude-capability-provider.js +9 -31
  63. package/dist/core/capability/providers/codex-capability-provider.js +4 -15
  64. package/dist/core/capability/skill-discovery.js +55 -0
  65. package/dist/core/channel-loader.js +1 -1
  66. package/dist/core/command/cli-intent-parser.js +9 -0
  67. package/dist/core/command/command-handler.js +650 -42
  68. package/dist/core/command/connect-menu.js +15 -0
  69. package/dist/core/command/group-menu.js +4 -4
  70. package/dist/core/command/menu-catalog.js +370 -0
  71. package/dist/core/command/menu-handler.js +230 -51
  72. package/dist/core/command/menu-protocol.js +94 -2
  73. package/dist/core/command/slash-gate.js +5 -2
  74. package/dist/core/command/slash-handler.js +99 -31
  75. package/dist/core/daemon-file-cache.js +2 -1
  76. package/dist/core/data-migration.js +9 -2
  77. package/dist/core/event-catalog.js +6 -0
  78. package/dist/core/evolagent.js +4 -2
  79. package/dist/core/handoff/dispatcher.js +5 -2
  80. package/dist/core/handoff/runtime.js +157 -22
  81. package/dist/core/handoff/store.js +29 -1
  82. package/dist/core/handoff/types.js +1 -0
  83. package/dist/core/interaction-router.js +10 -0
  84. package/dist/core/message/im-renderer.js +19 -0
  85. package/dist/core/message/logical-queue-bridge.js +1 -0
  86. package/dist/core/message/mention-schema.js +126 -0
  87. package/dist/core/message/message-bridge.js +298 -88
  88. package/dist/core/message/message-log.js +3 -0
  89. package/dist/core/message/message-queue.js +268 -39
  90. package/dist/core/message/message-utils.js +81 -4
  91. package/dist/core/message/response-engine.js +161 -77
  92. package/dist/core/message/stream-debouncer.js +10 -2
  93. package/dist/core/permission/approval-gateway.js +1 -0
  94. package/dist/core/permission/ec-command-parser.js +243 -18
  95. package/dist/core/permission/index.js +1 -1
  96. package/dist/core/permission/mode.js +15 -0
  97. package/dist/core/permission/readonly-shell-query.js +148 -8
  98. package/dist/core/permission/sandbox-runtime.js +43 -1
  99. package/dist/core/permission/tool-policy.js +322 -57
  100. package/dist/core/protected-paths.js +114 -4
  101. package/dist/core/session/session-manager.js +42 -4
  102. package/dist/core/session/session-mapper.js +20 -1
  103. package/dist/eck/detect.js +18 -1
  104. package/dist/eck/group-rules-sync.js +15 -44
  105. package/dist/eck/manifest-engine.js +39 -13
  106. package/dist/index.js +541 -180
  107. package/dist/ipc.js +232 -10
  108. package/dist/paths.js +3 -0
  109. package/dist/response-system/coordinator.js +6 -4
  110. package/dist/response-system/engines/v1/proactive-flow.js +26 -8
  111. package/dist/response-system/modes/single-session/index.js +14 -1
  112. package/dist/response-system/selector.js +6 -5
  113. package/dist/trigger/anomaly-store.js +4 -0
  114. package/dist/trigger/feedback.js +64 -11
  115. package/dist/trigger/manager.js +21 -1
  116. package/dist/trigger/parser.js +14 -2
  117. package/dist/trigger/scheduler.js +1 -0
  118. package/dist/trigger/script-executor.js +1 -0
  119. package/dist/trigger/validation.js +67 -6
  120. package/dist/utils/codex-app-server-registry.js +90 -0
  121. package/dist/utils/codex-cli.js +5 -1
  122. package/dist/utils/cross-platform.js +15 -0
  123. package/dist/utils/ecweb-supervisor.js +332 -0
  124. package/dist/utils/error-utils.js +10 -0
  125. package/dist/utils/npm-ops.js +5 -12
  126. package/dist/utils/tool-summary.js +11 -0
  127. package/kits/docs/channels/aun.md +1 -1
  128. package/kits/docs/context-assembly.md +2 -2
  129. package/kits/docs/evolcore/agent.md +11 -6
  130. package/kits/docs/evolcore/aid.md +14 -0
  131. package/kits/docs/evolcore/trigger.md +17 -1
  132. package/kits/docs/prompt-loading-architecture.md +1 -2
  133. package/kits/docs/venues/group.md +1 -1
  134. package/kits/eck_manifest.json +0 -12
  135. package/kits/migrations/migrate-contact-book-v2.mjs +1 -2
  136. package/kits/rules/04-relation.md +1 -1
  137. package/kits/rules/05-venue.md +2 -2
  138. package/kits/schemas/role-config.schema.1.json +1 -0
  139. package/kits/schemas/single-session.schema.1.json +3 -3
  140. package/kits/schemas/single-session.schema.2.json +3 -3
  141. package/kits/templates/roles/admin.json +27 -0
  142. package/kits/templates/roles/member.json +34 -1
  143. package/kits/templates/roles/visitor.json +35 -3
  144. package/kits/templates/system-fragments/bootstrap.md +1 -1
  145. package/kits/templates/system-fragments/channel.md +1 -1
  146. package/kits/templates/system-fragments/session.md +1 -1
  147. package/package.json +2 -2
  148. package/dist/aun/msg/payload-type.js +0 -27
  149. package/dist/aun/rpc/caller.js +0 -42
  150. package/dist/aun/rpc/connection.js +0 -25
  151. package/dist/aun/storage/manage.js +0 -10
  152. package/dist/config/access-policy-domain.js +0 -18
  153. package/dist/config/config-batch-get.js +0 -11
  154. package/dist/config/owner-policy.js +0 -4
  155. package/dist/config/role-config-v4-startup.js +0 -32
  156. package/dist/config/role-config-v5-startup.js +0 -27
  157. package/dist/core/auth/trigger-authorization.js +0 -15
  158. package/dist/core/command/evol-menu-version-gate.js +0 -39
  159. package/dist/core/interaction-registration.js +0 -10
  160. package/dist/core/permission/execution-sandbox.js +0 -16
  161. package/dist/core/relation/peer-key.js +0 -1
  162. package/dist/core/session/session-key.js +0 -24
  163. package/dist/eck/baseagent-caps.js +0 -18
  164. package/dist/eck/rules-loader.js +0 -28
@@ -21,6 +21,90 @@ 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 GROUP_FS_WRITE_OPERATIONS = ['mkdir', 'upload', 'copy', 'move', 'remove'].map(kind => ({
77
+ id: `ec.fs.${kind}`,
78
+ category: 'write-own',
79
+ dangerous: false,
80
+ defaultScopes: ['relation'],
81
+ description: `Write current group filesystem (${kind})`,
82
+ sources: ['agent-tool'],
83
+ }));
84
+ const HANDOFF_READ_OPERATIONS = ['status', 'list', 'trace'].map(kind => ({
85
+ id: `ec.handoff.${kind}`,
86
+ category: 'read',
87
+ dangerous: false,
88
+ defaultScopes: ['relation', 'agent'],
89
+ description: `Read handoff ${kind}`,
90
+ sources: ['agent-tool'],
91
+ }));
92
+ const HANDOFF_WRITE_OPERATIONS = [{
93
+ id: 'ec.handoff.return',
94
+ category: 'write-own',
95
+ dangerous: false,
96
+ defaultScopes: ['relation', 'agent'],
97
+ description: 'Return the current handoff result',
98
+ sources: ['agent-tool'],
99
+ }];
100
+ 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]) => ({
101
+ id: `ec.queue.${kind}`,
102
+ category: kind === 'read' ? 'read' : 'write-agent',
103
+ dangerous: false,
104
+ defaultScopes: ['agent'],
105
+ description,
106
+ sources: ['agent-tool'],
107
+ }));
24
108
  /**
25
109
  * Operation Catalog - 所有命令操作的元数据定义
26
110
  *
@@ -34,7 +118,7 @@ const OPERATIONS = [
34
118
  dangerous: false,
35
119
  defaultScopes: ['relation', 'role', 'agent'],
36
120
  description: '列出可用模型',
37
- sources: ['slash', 'menu', 'menu.cli'],
121
+ sources: ['slash', 'menu', 'menu.cli', 'agent-tool'],
38
122
  },
39
123
  {
40
124
  id: 'model.current',
@@ -42,7 +126,7 @@ const OPERATIONS = [
42
126
  dangerous: false,
43
127
  defaultScopes: ['relation', 'role', 'agent'],
44
128
  description: '查询当前使用的模型',
45
- sources: ['slash', 'menu', 'menu.cli'],
129
+ sources: ['slash', 'menu', 'menu.cli', 'agent-tool'],
46
130
  },
47
131
  {
48
132
  id: 'model.effort.current',
@@ -50,7 +134,7 @@ const OPERATIONS = [
50
134
  dangerous: false,
51
135
  defaultScopes: ['relation', 'role', 'agent'],
52
136
  description: '查询当前推理努力度',
53
- sources: ['slash', 'menu', 'menu.cli'],
137
+ sources: ['slash', 'menu', 'menu.cli', 'agent-tool'],
54
138
  },
55
139
  {
56
140
  id: 'model.info',
@@ -74,7 +158,7 @@ const OPERATIONS = [
74
158
  dangerous: false,
75
159
  defaultScopes: ['relation', 'role', 'agent'],
76
160
  description: '切换使用的模型',
77
- sources: ['slash', 'menu', 'menu.cli'],
161
+ sources: ['slash', 'menu', 'menu.cli', 'agent-tool'],
78
162
  },
79
163
  {
80
164
  id: 'model.effort',
@@ -82,7 +166,7 @@ const OPERATIONS = [
82
166
  dangerous: false,
83
167
  defaultScopes: ['relation', 'role', 'agent'],
84
168
  description: '设置推理努力度',
85
- sources: ['slash', 'menu', 'menu.cli'],
169
+ sources: ['slash', 'menu', 'menu.cli', 'agent-tool'],
86
170
  },
87
171
  {
88
172
  id: 'model.reset',
@@ -195,7 +279,7 @@ const OPERATIONS = [
195
279
  dangerous: false,
196
280
  defaultScopes: ['relation'],
197
281
  description: '查看当前会话状态',
198
- sources: ['slash', 'menu'],
282
+ sources: ['slash', 'menu', 'agent-tool'],
199
283
  },
200
284
  {
201
285
  id: 'session.topic.current',
@@ -227,7 +311,7 @@ const OPERATIONS = [
227
311
  dangerous: false,
228
312
  defaultScopes: ['relation'],
229
313
  description: '重命名会话',
230
- sources: ['slash', 'menu'],
314
+ sources: ['slash', 'menu', 'agent-tool'],
231
315
  },
232
316
  {
233
317
  id: 'session.delete',
@@ -244,7 +328,7 @@ const OPERATIONS = [
244
328
  dangerous: false,
245
329
  defaultScopes: ['relation', 'agent'],
246
330
  description: '查看当前项目摘要',
247
- sources: ['menu'],
331
+ sources: ['menu', 'agent-tool'],
248
332
  },
249
333
  {
250
334
  id: 'file.list',
@@ -481,6 +565,14 @@ const OPERATIONS = [
481
565
  description: '列出当前 Agent 可用的 BaseAgent',
482
566
  sources: ['menu', 'control'],
483
567
  },
568
+ {
569
+ id: 'agent.baseagent.update',
570
+ category: 'write-agent',
571
+ dangerous: false,
572
+ defaultScopes: ['agent'],
573
+ description: '切换当前 Agent 使用的 BaseAgent',
574
+ sources: ['menu'],
575
+ },
484
576
  {
485
577
  id: 'agent.getConfig',
486
578
  category: 'read',
@@ -505,6 +597,14 @@ const OPERATIONS = [
505
597
  description: '重载 agent 配置',
506
598
  sources: ['slash', 'menu', 'control'],
507
599
  },
600
+ {
601
+ id: 'agent.bootstrapComplete',
602
+ category: 'write-agent',
603
+ dangerous: false,
604
+ defaultScopes: ['agent'],
605
+ description: 'Complete bootstrap for the current Agent',
606
+ sources: ['agent-tool'],
607
+ },
508
608
  {
509
609
  id: 'agent.delete',
510
610
  category: 'dangerous',
@@ -520,7 +620,7 @@ const OPERATIONS = [
520
620
  dangerous: false,
521
621
  defaultScopes: ['process'],
522
622
  description: '查看系统状态(敏感信息)',
523
- sources: ['slash', 'menu', 'menu.cli'],
623
+ sources: ['slash', 'menu', 'menu.cli', 'agent-tool'],
524
624
  },
525
625
  {
526
626
  id: 'activity.current',
@@ -530,6 +630,14 @@ const OPERATIONS = [
530
630
  description: '查看当前中间活动显示策略',
531
631
  sources: ['menu'],
532
632
  },
633
+ {
634
+ id: 'activity.update',
635
+ category: 'write-agent',
636
+ dangerous: false,
637
+ defaultScopes: ['relation', 'agent'],
638
+ description: '更新中间活动显示策略',
639
+ sources: ['menu'],
640
+ },
533
641
  {
534
642
  id: 'observable.current',
535
643
  category: 'read',
@@ -538,6 +646,14 @@ const OPERATIONS = [
538
646
  description: '查看当前 Agent 观察者模式',
539
647
  sources: ['menu', 'control'],
540
648
  },
649
+ {
650
+ id: 'observable.update',
651
+ category: 'write-agent',
652
+ dangerous: false,
653
+ defaultScopes: ['agent'],
654
+ description: '更新当前 Agent 观察者模式',
655
+ sources: ['menu'],
656
+ },
541
657
  {
542
658
  id: 'capability.read',
543
659
  category: 'read',
@@ -649,41 +765,41 @@ const OPERATIONS = [
649
765
  id: 'storage.list',
650
766
  category: 'read',
651
767
  dangerous: false,
652
- defaultScopes: ['control'],
768
+ defaultScopes: ['agent', 'control'],
653
769
  description: '列出存储对象(敏感信息)',
654
- sources: ['menu.cli', 'control'],
770
+ sources: ['menu.cli', 'agent-tool', 'control'],
655
771
  },
656
772
  {
657
773
  id: 'storage.quota',
658
774
  category: 'read',
659
775
  dangerous: false,
660
- defaultScopes: ['control'],
776
+ defaultScopes: ['agent', 'control'],
661
777
  description: '查看存储配额(敏感信息)',
662
- sources: ['menu.cli', 'control'],
778
+ sources: ['menu.cli', 'agent-tool', 'control'],
663
779
  },
664
780
  {
665
781
  id: 'storage.upload',
666
782
  category: 'write-agent',
667
783
  dangerous: true,
668
- defaultScopes: ['control'],
784
+ defaultScopes: ['agent', 'control'],
669
785
  description: '上传文件到云存储',
670
- sources: ['menu.cli', 'control'],
786
+ sources: ['menu.cli', 'agent-tool', 'control'],
671
787
  },
672
788
  {
673
789
  id: 'storage.download',
674
790
  category: 'write-agent',
675
791
  dangerous: true,
676
- defaultScopes: ['control'],
792
+ defaultScopes: ['agent', 'control'],
677
793
  description: '从云存储下载文件',
678
- sources: ['menu.cli', 'control'],
794
+ sources: ['menu.cli', 'agent-tool', 'control'],
679
795
  },
680
796
  {
681
797
  id: 'storage.delete',
682
798
  category: 'dangerous',
683
799
  dangerous: true,
684
- defaultScopes: ['control'],
800
+ defaultScopes: ['agent', 'control'],
685
801
  description: '删除云存储对象',
686
- sources: ['menu.cli', 'control'],
802
+ sources: ['menu.cli', 'agent-tool', 'control'],
687
803
  },
688
804
  // ── Gateway & Config Operations ──
689
805
  {
@@ -745,6 +861,53 @@ const OPERATIONS = [
745
861
  // ── EC Command Operations ──
746
862
  // EC daemon 入口根据结构化 IPC 请求构造 operation;Runner 不解析 EC 子命令。
747
863
  ...CONFIG_MANAGEMENT_OPERATIONS,
864
+ ...GROUP_MANAGEMENT_OPERATIONS,
865
+ ...MSG_READ_OPERATIONS,
866
+ ...GROUP_FS_READ_OPERATIONS,
867
+ ...GROUP_FS_WRITE_OPERATIONS,
868
+ ...HANDOFF_READ_OPERATIONS,
869
+ ...HANDOFF_WRITE_OPERATIONS,
870
+ ...QUEUE_OPERATIONS,
871
+ {
872
+ id: 'ec.version',
873
+ category: 'read',
874
+ dangerous: false,
875
+ defaultScopes: ['relation', 'agent', 'process'],
876
+ description: 'Read local EvolCore component versions',
877
+ sources: ['menu.cli', 'agent-tool'],
878
+ },
879
+ {
880
+ id: 'ec.aid.show',
881
+ category: 'read',
882
+ dangerous: true,
883
+ defaultScopes: ['relation', 'agent'],
884
+ description: 'Inspect the current Agent local AID identity and signing state',
885
+ sources: ['agent-tool'],
886
+ },
887
+ {
888
+ id: 'ec.aid.lookup',
889
+ category: 'read',
890
+ dangerous: false,
891
+ defaultScopes: ['relation', 'agent'],
892
+ description: 'Look up the current Agent public AID record',
893
+ sources: ['agent-tool'],
894
+ },
895
+ {
896
+ id: 'ec.aid.agentmd.get',
897
+ category: 'read',
898
+ dangerous: false,
899
+ defaultScopes: ['relation', 'agent'],
900
+ description: 'Read and verify the current Agent public agent.md',
901
+ sources: ['agent-tool'],
902
+ },
903
+ {
904
+ id: 'ec.aid.agentmd.put',
905
+ category: 'write-agent',
906
+ dangerous: true,
907
+ defaultScopes: ['agent'],
908
+ description: 'Sign and publish the current Agent local agent.md',
909
+ sources: ['agent-tool'],
910
+ },
748
911
  {
749
912
  id: 'ec.msg.send',
750
913
  category: 'write-own',
@@ -779,20 +942,28 @@ const OPERATIONS = [
779
942
  },
780
943
  {
781
944
  id: 'ec.ctl.send',
782
- category: 'write-agent',
945
+ category: 'write-own',
783
946
  dangerous: false,
784
- defaultScopes: ['control', 'agent'],
785
- description: '通过 ec ctl send 发送控制通道消息',
947
+ defaultScopes: ['relation'],
948
+ description: '通过 ec ctl send 向当前会话发送消息',
786
949
  sources: ['agent-tool'],
787
950
  },
788
951
  {
789
952
  id: 'ec.ctl.file',
790
- category: 'write-agent',
953
+ category: 'write-own',
791
954
  dangerous: false,
792
- defaultScopes: ['control', 'agent'],
793
- description: '通过 ec ctl file 发送控制通道文件',
955
+ defaultScopes: ['relation'],
956
+ description: '通过 ec ctl file 向当前会话发送项目文件',
794
957
  sources: ['agent-tool'],
795
958
  },
959
+ ...['read', 'clear', 'cancel', 'interrupt'].map(action => ({
960
+ id: `ec.ctl.queue.${action}`,
961
+ category: action === 'read' ? 'read' : 'write-own',
962
+ dangerous: false,
963
+ defaultScopes: ['relation'],
964
+ description: `${action} the current managed session queue`,
965
+ sources: ['agent-tool'],
966
+ })),
796
967
  // ── Raw CLI & Shell Execution ──
797
968
  {
798
969
  id: 'cli.exec.raw',
@@ -31,14 +31,21 @@ export function preparePostBootstrapWelcomeOutbox(aid, owner, ownerName) {
31
31
  const chatDir = chatDirPath(resolvePaths().sessionsDir, 'aun', owner, aid);
32
32
  if (hasMessageLogOperation(chatDir, operationId))
33
33
  return;
34
+ const existing = outbox.findByDedupeKey(aid, operationId);
35
+ if (existing?.delivery?.chatType === 'private' && existing.channelId === owner)
36
+ return;
37
+ if (existing)
38
+ outbox.remove(aid, existing.id);
34
39
  outbox.enqueue(aid, {
35
40
  channelId: owner,
41
+ delivery: { chatType: 'private' },
36
42
  dedupeKey: operationId,
37
43
  critical: true,
38
44
  type: 'text',
39
45
  text: renderPostBootstrapWelcome(aid, owner, ownerName),
40
46
  ttl: BOOTSTRAP_MESSAGE_TTL_MS,
41
47
  context: {
48
+ delivery: { chatType: 'private' },
42
49
  metadata: {
43
50
  source: 'daemon',
44
51
  chatmode: 'interactive',
@@ -48,3 +55,21 @@ export function preparePostBootstrapWelcomeOutbox(aid, owner, ownerName) {
48
55
  },
49
56
  });
50
57
  }
58
+ export function hasPendingPostBootstrapWelcomeOutbox(aid) {
59
+ return outbox.findByDedupeKey(aid, postBootstrapWelcomeOperationId(aid))?.delivery?.chatType === 'private';
60
+ }
61
+ /** Bind the durable completion welcome to the fresh post-bootstrap session. */
62
+ export function bindPostBootstrapWelcomeOutboxSession(aid, sessionId) {
63
+ const entry = outbox.findByDedupeKey(aid, postBootstrapWelcomeOperationId(aid));
64
+ if (!entry || entry.delivery?.chatType !== 'private')
65
+ return false;
66
+ if (entry.context?.sessionId === sessionId)
67
+ return true;
68
+ return outbox.replace(aid, {
69
+ ...entry,
70
+ context: {
71
+ ...(entry.context ?? {}),
72
+ sessionId,
73
+ },
74
+ });
75
+ }
@@ -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;
@@ -107,6 +166,7 @@ export class BootstrapService {
107
166
  channelId,
108
167
  agentName: aid,
109
168
  replyContext: {
169
+ delivery: { chatType: 'private' },
110
170
  metadata: {
111
171
  source: 'daemon',
112
172
  persistRequired: true,
@@ -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 : [];
@@ -1,19 +1,7 @@
1
- import fs from 'fs';
2
1
  import os from 'os';
3
2
  import path from 'path';
4
3
  import { execCodexCliSync } from '../../../utils/codex-cli.js';
5
- function listDirectories(dir) {
6
- try {
7
- if (!fs.existsSync(dir))
8
- return [];
9
- return fs.readdirSync(dir, { withFileTypes: true })
10
- .filter(entry => entry.isDirectory())
11
- .map(entry => entry.name);
12
- }
13
- catch {
14
- return [];
15
- }
16
- }
4
+ import { discoverSkillDirectories } from '../skill-discovery.js';
17
5
  function discoverSkills(ctx) {
18
6
  const roots = [
19
7
  { dir: path.join(ctx.projectPath, '.codex', 'skills'), source: 'project' },
@@ -23,11 +11,12 @@ function discoverSkills(ctx) {
23
11
  const seen = new Set();
24
12
  const out = [];
25
13
  for (const root of roots) {
26
- for (const id of listDirectories(root.dir)) {
14
+ for (const skill of discoverSkillDirectories(root.dir)) {
15
+ const { id } = skill;
27
16
  if (seen.has(id))
28
17
  continue;
29
18
  seen.add(id);
30
- out.push({ id, label: id, source: root.source });
19
+ out.push({ id, label: id, desc: skill.description, source: root.source });
31
20
  }
32
21
  }
33
22
  return out;
@@ -0,0 +1,55 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { parseDocument } from 'yaml';
4
+ function readSkillMetadata(skillDir) {
5
+ const skillFile = path.join(skillDir, 'SKILL.md');
6
+ try {
7
+ if (!fs.statSync(skillFile).isFile())
8
+ return undefined;
9
+ const text = fs.readFileSync(skillFile, 'utf-8');
10
+ const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
11
+ if (!match)
12
+ return undefined;
13
+ const document = parseDocument(match[1]);
14
+ if (document.errors.length > 0)
15
+ return undefined;
16
+ const metadata = document.toJS();
17
+ if (typeof metadata?.name !== 'string' || !metadata.name.trim())
18
+ return undefined;
19
+ if (typeof metadata.description !== 'string' || !metadata.description.trim())
20
+ return undefined;
21
+ return {
22
+ id: metadata.name.trim(),
23
+ description: metadata.description.trim(),
24
+ };
25
+ }
26
+ catch {
27
+ return undefined;
28
+ }
29
+ }
30
+ /** Return only directories that satisfy the Codex/Claude skill file contract. */
31
+ export function discoverSkillDirectories(root) {
32
+ try {
33
+ if (!fs.existsSync(root))
34
+ return [];
35
+ return fs.readdirSync(root, { withFileTypes: true })
36
+ .filter(entry => !entry.name.startsWith('.'))
37
+ .filter(entry => entry.isDirectory() || entry.isSymbolicLink())
38
+ .map(entry => {
39
+ const dir = path.join(root, entry.name);
40
+ try {
41
+ if (!fs.statSync(dir).isDirectory())
42
+ return undefined;
43
+ }
44
+ catch {
45
+ return undefined;
46
+ }
47
+ const metadata = readSkillMetadata(dir);
48
+ return metadata;
49
+ })
50
+ .filter((entry) => Boolean(entry));
51
+ }
52
+ catch {
53
+ return [];
54
+ }
55
+ }
@@ -196,7 +196,7 @@ export function buildReloadHooks(deps) {
196
196
  if (!handoffRuntime)
197
197
  return;
198
198
  try {
199
- await handoffRuntime.recover([aid]);
199
+ await handoffRuntime.recover([aid], { discardExpired: false });
200
200
  }
201
201
  finally {
202
202
  handoffRuntime.resumeAgent(aid);