evolcore 0.0.37 → 0.0.39

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 (37) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/agents/codex-runner.js +3 -3
  3. package/dist/aun/aid/agentmd.js +21 -0
  4. package/dist/aun/aid/client.js +1 -1
  5. package/dist/aun/aid/ownership-operation.js +34 -9
  6. package/dist/cli/agent-create.js +7 -2
  7. package/dist/cli/agent.js +30 -37
  8. package/dist/cli/aid-bind-command.js +12 -1
  9. package/dist/cli/aun-commands.js +31 -23
  10. package/dist/cli/config.js +15 -18
  11. package/dist/cli/contact.js +9 -12
  12. package/dist/cli/ctl-command.js +3 -3
  13. package/dist/cli/diag.js +7 -3
  14. package/dist/cli/fs-command.js +5 -4
  15. package/dist/cli/handoff-command.js +11 -11
  16. package/dist/cli/help.js +18 -0
  17. package/dist/cli/init-channel.js +2 -1
  18. package/dist/cli/memory-command.js +14 -13
  19. package/dist/cli/model.js +5 -4
  20. package/dist/cli/queue-command.js +3 -3
  21. package/dist/cli/response.js +5 -4
  22. package/dist/cli/skill-command.js +13 -13
  23. package/dist/cli/task-command.js +3 -3
  24. package/dist/cli/thread-command.js +5 -3
  25. package/dist/cli/trigger-command.js +3 -3
  26. package/dist/cli/version.js +6 -2
  27. package/dist/core/auth/operation-catalog.js +1 -1
  28. package/dist/core/bootstrap-service.js +20 -12
  29. package/dist/core/command/cli-intent-parser.js +5 -2
  30. package/dist/core/command/command-handler.js +13 -4
  31. package/dist/core/command/menu-handler.js +2 -2
  32. package/dist/core/ownership/owner-resolver.js +121 -2
  33. package/dist/core/permission/protected-paths.js +51 -0
  34. package/dist/utils/npm-ops.js +22 -6
  35. package/dist/utils/welcome.js +18 -24
  36. package/kits/docs/evolcore/aid.md +33 -0
  37. package/package.json +3 -3
@@ -117,7 +117,12 @@ export class OwnerResolver {
117
117
  if (subject.client !== client || subject.generation !== generation)
118
118
  return;
119
119
  const record = this.record(payload);
120
- if (!record || record.sub_aid !== subjectAid)
120
+ if (!record)
121
+ return;
122
+ this.trackBindingRequest(subjectAid, subject, event, record);
123
+ // When this AID is the prospective owner, the request concerns the
124
+ // remote sub's ownership, not this AID's own Owner authorization.
125
+ if (record.sub_aid !== subjectAid)
121
126
  return;
122
127
  if (typeof record.operation_id === 'string') {
123
128
  const key = JSON.stringify([event, record.operation_id, record.step, record.status,
@@ -215,6 +220,9 @@ export class OwnerResolver {
215
220
  clearTimeout(subject.pendingRefreshTimer);
216
221
  subject.pendingRefreshTimer = undefined;
217
222
  subject.taskEvents.clear();
223
+ subject.requests.clear();
224
+ subject.decisionTasks.clear();
225
+ delete subject.view.decisionTask;
218
226
  for (const subscription of subject.subscriptions.splice(0)) {
219
227
  try {
220
228
  subscription.unsubscribe();
@@ -275,6 +283,7 @@ export class OwnerResolver {
275
283
  snapshot(subjectAid) {
276
284
  const subject = this.subject(subjectAid);
277
285
  const view = JSON.parse(JSON.stringify(subject.view));
286
+ view.requests = [...subject.requests.values()].map(request => ({ ...request }));
278
287
  if (view.consensusStatus === 'consensus' && !this.owner(subjectAid)) {
279
288
  view.ownerAid = undefined;
280
289
  view.synchronization = { status: 'unavailable' };
@@ -421,6 +430,52 @@ export class OwnerResolver {
421
430
  subject.binding = undefined;
422
431
  }
423
432
  }
433
+ async decide(subjectAid, operationId, decision, reason) {
434
+ this.assertLocalSubject(subjectAid);
435
+ const subject = this.subject(subjectAid);
436
+ const client = subject.client;
437
+ if (!client || !subject.view.connected) {
438
+ throw new OwnershipError('SUBJECT_UNAVAILABLE', 'Subject AID SDK client is not ready');
439
+ }
440
+ const request = subject.requests.get(operationId) ?? subject.decisionTasks.get(operationId);
441
+ if (!request) {
442
+ throw new OwnershipError('REQUEST_NOT_FOUND', 'No SDK-verified binding request matches this operation ID; use bind status');
443
+ }
444
+ const generation = subject.generation;
445
+ logger.info(`[ownership] ${decision} requested local=${subjectAid} operationId=${operationId}`);
446
+ try {
447
+ // The SDK revalidates the stored request, decision and current relation
448
+ // before any protocol mutation; this projection is only a discovery aid.
449
+ const result = decision === 'accept'
450
+ ? await client.acceptOwnershipRequest(operationId)
451
+ : await client.rejectOwnershipRequest(operationId, reason);
452
+ if (subject.client !== client || subject.generation !== generation) {
453
+ throw new OwnershipError('CLIENT_REPLACED', 'Subject SDK client changed during approval; query status before retrying');
454
+ }
455
+ const task = this.bindingTask(result);
456
+ if (!task || !this.sameBindingTask(task, request)) {
457
+ throw new OwnershipError('TASK_MISMATCH', 'SDK returned a different ownership decision task');
458
+ }
459
+ subject.view.decisionTask = task;
460
+ subject.decisionTasks.set(operationId, task);
461
+ subject.requests.delete(operationId);
462
+ if (request.sub_aid === subjectAid) {
463
+ this.invalidate(subject, 'Binding decision submitted; fresh consensus is required');
464
+ this.schedule(subjectAid, subject);
465
+ }
466
+ logger.info(`[ownership] ${decision} result ${JSON.stringify(task)}`);
467
+ if (task.status === 'error' || task.status === 'stopped' || (decision === 'accept' && task.status === 'rejected')) {
468
+ throw Object.assign(new OwnershipError(task.code ?? 'OWNERSHIP_DECISION_FAILED', task.reason ?? `Ownership task ${task.status}`), { retryable: task.retryable });
469
+ }
470
+ return this.snapshot(subjectAid);
471
+ }
472
+ catch (error) {
473
+ logger.warn(`[ownership] ${decision} failed ${JSON.stringify({
474
+ localAid: subjectAid, operationId, error: error instanceof Error ? error.message : String(error),
475
+ })}`);
476
+ throw error;
477
+ }
478
+ }
424
479
  stop() {
425
480
  for (const subjectAid of this.subjects.keys())
426
481
  this.detachClient(subjectAid);
@@ -500,7 +555,8 @@ export class OwnerResolver {
500
555
  }
501
556
  const task = saved?.subjectAid === subjectAid ? this.task(saved.task) : undefined;
502
557
  subject = {
503
- generation: 0, epoch: 0, query: 0, subscriptions: [], taskEvents: new Set(), persistenceError,
558
+ generation: 0, epoch: 0, query: 0, subscriptions: [], taskEvents: new Set(),
559
+ requests: new Map(), decisionTasks: new Map(), persistenceError,
504
560
  view: {
505
561
  subjectAid, consensusStatus: 'unavailable', authorizationStatus: 'unavailable', connected: false, revision: 0,
506
562
  task: task?.sub_aid === subjectAid ? task : undefined,
@@ -639,6 +695,69 @@ export class OwnerResolver {
639
695
  record(value) {
640
696
  return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
641
697
  }
698
+ trackBindingRequest(localAid, subject, event, record) {
699
+ const task = this.bindingTask(record);
700
+ if (!task || (task.sub_aid !== localAid && task.owner_aid !== localAid))
701
+ return;
702
+ const request = subject.requests.get(task.operation_id);
703
+ const decisionTask = subject.decisionTasks.get(task.operation_id);
704
+ if ((request && !this.sameBindingTask(task, request))
705
+ || (decisionTask && !this.sameBindingTask(task, decisionTask)))
706
+ return;
707
+ if (event === 'ownership.request') {
708
+ const requester = record.requester_aid;
709
+ const remote = task.sub_aid === localAid ? task.owner_aid : task.sub_aid;
710
+ if (task.status !== 'pending' || task.step !== 'approval' || requester !== remote || decisionTask)
711
+ return;
712
+ subject.requests.set(task.operation_id, {
713
+ ...task, requester_aid: remote, local_role: task.sub_aid === localAid ? 'sub' : 'owner',
714
+ });
715
+ logger.info(`[ownership] inbound binding ${JSON.stringify({
716
+ localAid, operationId: task.operation_id, subAid: task.sub_aid, ownerAid: task.owner_aid,
717
+ })}`);
718
+ }
719
+ else {
720
+ const awaitingApproval = task.step === 'approval'
721
+ && (task.status === 'pending' || (task.status === 'error' && task.retryable === true))
722
+ && !['ownership.completed', 'ownership.rejected', 'ownership.stopped'].includes(event);
723
+ if (request && awaitingApproval)
724
+ subject.requests.set(task.operation_id, { ...request, ...task });
725
+ else
726
+ subject.requests.delete(task.operation_id);
727
+ }
728
+ const retryableDecision = task.step !== 'approval'
729
+ && (task.status === 'pending' || (task.status === 'error' && task.retryable === true))
730
+ && !['ownership.completed', 'ownership.rejected', 'ownership.stopped'].includes(event);
731
+ if (decisionTask || retryableDecision) {
732
+ subject.decisionTasks.set(task.operation_id, task);
733
+ subject.view.decisionTask = task;
734
+ }
735
+ else if (subject.view.decisionTask?.operation_id === task.operation_id) {
736
+ subject.view.decisionTask = task;
737
+ }
738
+ }
739
+ sameBindingTask(left, right) {
740
+ return left.operation_id === right.operation_id && left.sub_aid === right.sub_aid
741
+ && left.owner_aid === right.owner_aid && left.base_relation_version === right.base_relation_version
742
+ && left.target_relation_version === right.target_relation_version;
743
+ }
744
+ /** Only public task fields are exposed; raw SDK records remain SDK-owned. */
745
+ bindingTask(value) {
746
+ const task = this.task(value);
747
+ if (!task || !CREATION_BIND_OPERATION_ID.test(task.operation_id) || task.new_owner_aid !== undefined
748
+ || task.sub_aid === task.owner_aid || !Number.isSafeInteger(task.base_relation_version)
749
+ || task.base_relation_version < 0 || !Number.isSafeInteger(task.target_relation_version)
750
+ || task.target_relation_version !== task.base_relation_version + 1)
751
+ return undefined;
752
+ return {
753
+ operation_id: task.operation_id, operation: 'bind', sub_aid: task.sub_aid, owner_aid: task.owner_aid,
754
+ base_relation_version: task.base_relation_version, target_relation_version: task.target_relation_version,
755
+ status: task.status, step: task.step,
756
+ ...(typeof task.code === 'string' ? { code: task.code } : {}),
757
+ ...(typeof task.reason === 'string' ? { reason: task.reason } : {}),
758
+ ...(typeof task.retryable === 'boolean' ? { retryable: task.retryable } : {}),
759
+ };
760
+ }
642
761
  task(value) {
643
762
  const record = this.record(value);
644
763
  if (!record || typeof record.operation_id !== 'string' || !record.operation_id
@@ -787,6 +787,57 @@ export function compactWindowsCodexFilesystemRules(filesystem, platform = proces
787
787
  }
788
788
  return compacted;
789
789
  }
790
+ /** Remove Linux mount targets already hidden by a read-only parent deny. */
791
+ export function compactCodexLinuxFilesystemRules(filesystem, platform = process.platform) {
792
+ if (platform !== 'linux')
793
+ return filesystem;
794
+ const entries = Object.entries(filesystem);
795
+ const denyDirectories = entries
796
+ .filter(([target, access]) => {
797
+ if (access !== 'deny' || CODEX_GLOB_MAGIC.test(target))
798
+ return false;
799
+ try {
800
+ return fs.lstatSync(target).isDirectory();
801
+ }
802
+ catch {
803
+ return false;
804
+ }
805
+ })
806
+ .map(([target]) => target);
807
+ if (denyDirectories.length === 0)
808
+ return filesystem;
809
+ const exceptionTargets = entries.flatMap(([target, access]) => {
810
+ if (access !== 'read' && access !== 'write')
811
+ return [];
812
+ const globIndex = target.search(CODEX_GLOB_MAGIC);
813
+ if (globIndex < 0)
814
+ return [target];
815
+ const prefix = target.slice(0, target.lastIndexOf('/', globIndex));
816
+ return prefix ? [prefix] : [];
817
+ });
818
+ let compacted;
819
+ for (const [target, access] of entries) {
820
+ if (access !== 'deny')
821
+ continue;
822
+ const recursive = target.endsWith('/**');
823
+ const candidate = recursive ? target.slice(0, -3) : target;
824
+ if (CODEX_GLOB_MAGIC.test(candidate))
825
+ continue;
826
+ const covered = denyDirectories.some(parent => {
827
+ if (candidate === parent)
828
+ return recursive && target !== parent;
829
+ return isSameOrDescendant(candidate, parent);
830
+ });
831
+ if (!covered)
832
+ continue;
833
+ const hasException = exceptionTargets.some(exception => isSameOrDescendant(exception, candidate) || isSameOrDescendant(candidate, exception));
834
+ if (hasException)
835
+ continue;
836
+ compacted ??= { ...filesystem };
837
+ delete compacted[target];
838
+ }
839
+ return compacted ?? filesystem;
840
+ }
790
841
  /**
791
842
  * Codex 0.154 serializes resolved Windows deny-read targets into a base64
792
843
  * command-line argument for codex-windows-sandbox-setup.exe. Recursive file
@@ -394,20 +394,36 @@ export async function preparePackageCache(target, registry, options = {}) {
394
394
  const run = (args) => new Promise((resolve, reject) => {
395
395
  let failure = null;
396
396
  let output = '';
397
+ let errorOutput = '';
397
398
  const child = execFile(npm.command, [...npm.argsPrefix, ...args, '--registry', registry, ...npmConfigArgs(options.npmConfig), '--ignore-scripts'], {
398
399
  timeout: 180_000, windowsHide: isWindows, signal: options.signal, maxBuffer: 8 * 1024 * 1024,
399
- }, (error, stdout) => { failure = error; output = String(stdout ?? ''); });
400
+ }, (error, stdout, stderr) => {
401
+ failure = error;
402
+ output = String(stdout ?? '');
403
+ errorOutput = String(stderr ?? '');
404
+ });
400
405
  child.once('error', error => { failure = error; });
401
406
  child.once('close', code => {
402
- if (failure || code !== 0 || options.signal?.aborted)
403
- reject(new Error(options.signal?.aborted
404
- ? 'Package cache preparation timed out or was cancelled' : 'npm cache preparation failed; check registry connectivity, credentials, disk space and permissions'));
405
- else
407
+ if (!failure && code === 0 && !options.signal?.aborted) {
406
408
  resolve(output);
409
+ return;
410
+ }
411
+ if (options.signal?.aborted) {
412
+ reject(new Error('Package cache preparation timed out or was cancelled'));
413
+ return;
414
+ }
415
+ const codeText = failure && 'code' in failure && failure.code ? ` (${String(failure.code)})` : '';
416
+ const detail = (errorOutput || failure?.stderr || failure?.message || '')
417
+ .replace(/\s+/g, ' ').trim().slice(0, 500);
418
+ reject(new Error(`npm cache preparation failed${codeText}${detail ? `: ${detail}` : ''}`));
407
419
  });
408
420
  });
409
421
  options.onPhase?.('downloading');
410
- await run(['cache', 'add', spec, '--prefer-offline']);
422
+ // Revalidate registry metadata before resolving the exact version. A stale
423
+ // install-v1 packument can otherwise report ETARGET even when the registry
424
+ // already contains the selected release. The tarball itself remains cached
425
+ // and is reused when npm can do so.
426
+ await run(['cache', 'add', spec, '--prefer-online']);
411
427
  options.onPhase?.('tarball-check');
412
428
  const metadata = JSON.parse(await run(['view', spec, 'name', 'version', 'dist', '--json', '--offline']));
413
429
  const [packed] = JSON.parse(await run(['pack', spec, '--dry-run', '--json', '--offline']));
@@ -183,36 +183,30 @@ export function parseAgentWelcomeMetadata(content) {
183
183
  * 生成统一的欢迎消息文本
184
184
  */
185
185
  export function generateWelcomeMessage(options) {
186
- const channelDesc = getChannelDescription(options.channelType);
187
186
  const greeting = options.ownerName
188
187
  ? `👋 你好,${options.ownerName}!`
189
188
  : '👋 你好!';
190
189
  const agentIntro = options.agentName
191
- ? `我是 ${options.agentName},你的 AI Agent 网关。`
192
- : '我是您的 AI Agent 网关。';
193
- const channelInfo = `已成功连接到${channelDesc}。`;
190
+ ? `我是 ${options.agentName},很高兴认识你。`
191
+ : '我是你的 AI 助手,很高兴认识你。';
194
192
  const channelSpecificNote = getChannelSpecificNote(options.channelType);
195
- const bindingNote = options.includeBindingNote
196
- ? `\n\n🔐 **绑定说明**:\n- 这条消息表示您的设备已和此 Agent 绑定\n- 只有您能看到和使用这个 Agent\n- AUN 提供端到端加密和跨设备同步`
193
+ // Owner 才会收到:说明可以随时通过对话调整 Agent 的设定
194
+ const ownerNote = options.includeBindingNote
195
+ ? '\n\n🎨 我的名字、性格和说话风格都可以由你来定,想调整时直接告诉我。'
197
196
  : '';
198
- return `🎉 欢迎使用 EvolCore!
197
+ return `${greeting}
199
198
 
200
- ${greeting}
199
+ ${agentIntro}有什么需要,直接用平常说话的方式告诉我就行,不用记任何指令。
201
200
 
202
- ${agentIntro}${channelInfo}
201
+ 我可以帮你:
202
+ - 💬 解答问题、出主意、一起梳理思路
203
+ - 📝 写文案、改文档、整理总结
204
+ - ⏰ 定时提醒、到点跟进事情
205
+ - 🧠 记住你的习惯和偏好,下次不用重复交代${channelSpecificNote}${ownerNote}
203
206
 
204
- 📋 **日常使用方法**:
207
+ 想看更多玩法,随时发送 /help。
205
208
 
206
- 1. **查看帮助**:发送 \`/help\` 查看所有可用命令
207
- 2. **查看状态**:发送 \`/status\` 查看当前会话状态
208
- 3. **会话管理**:发送 \`/session\` 查看和切换会话
209
-
210
- 💡 **提示**:
211
- - 直接发送消息即可与 Claude/Codex 对话
212
- - 支持多会话管理,每个会话独立上下文
213
- - 所有命令以 \`/\` 开头${channelSpecificNote}${bindingNote}
214
-
215
- 现在就可以开始工作了!`;
209
+ 那我们从哪儿开始?`;
216
210
  }
217
211
  export function renderPostBootstrapWelcomeMessage(options) {
218
212
  const validation = validateWelcomeTemplate(options.template);
@@ -306,15 +300,15 @@ function getChannelDescription(channelType) {
306
300
  function getChannelSpecificNote(channelType) {
307
301
  switch (channelType) {
308
302
  case 'aun':
309
- return '\n- AUN 提供端到端加密和跨设备同步';
303
+ return '';
310
304
  case 'feishu':
311
- return '\n- 群聊中使用 @机器人 来触发对话';
305
+ return '\n- 👥 拉我进群,@我 就能一起讨论';
312
306
  case 'wechat':
313
307
  return '';
314
308
  case 'dingtalk':
315
- return '\n- 群聊中使用 @机器人 来触发对话';
309
+ return '\n- 👥 拉我进群,@我 就能一起讨论';
316
310
  case 'qqbot':
317
- return '\n- 群聊中使用 @机器人 来触发对话';
311
+ return '\n- 👥 拉我进群,@我 就能一起讨论';
318
312
  case 'wecom':
319
313
  return '';
320
314
  default:
@@ -12,7 +12,11 @@
12
12
  ec aid show <当前 self AID> [--format json]
13
13
  ec aid lookup <任意合法 AID> [--format json]
14
14
  ec aid agentmd get <任意合法 AID> [--format json]
15
+ ec aid bind status [--subject <本地AID>] [--format json]
15
16
  # 仅 request / bypass
17
+ ec aid bind <ownerAid> [--subject <本地AID>] [--format json]
18
+ ec aid bind accept <operationId> [--subject <本地AID>] [--format json]
19
+ ec aid bind reject <operationId> [--reason '<原因>'] [--subject <本地AID>] [--format json]
16
20
  ec aid agentmd put <当前 self AID> [--format json]
17
21
  ec aid agentmd set <当前 self AID> <name|type|description|version|tags> <value> [--format json]
18
22
  ec aid avatar <当前 self AID> (--file <path> | --url <https-url>) [--format json]
@@ -33,6 +37,7 @@ init 不等待人工审批、不启动 daemon,也不把该目标写入 `daemon
33
37
  容器入口随后启动 daemon,由 SDK 恢复同一任务,审批事件触发新鲜共识核验后才产生协议 Owner 权限。
34
38
 
35
39
  `bindingStatus` 为 `submitted` 仅表示 SDK 已确认邀约 `message.send` 回执;它不代表 Owner 已审批。
40
+
36
41
  `already_bound` 必须经过当前共识核验。旧版本留下的注册意图会由 SDK 恢复原任务,
37
42
  初始化复用原 `operation_id`,不重新注册身份或创建第二个绑定任务。
38
43
  提交超时返回 `unknown`,错误结果中的 `initialized: true` 表示配置和控制身份仍然保留;
@@ -40,6 +45,30 @@ init 不等待人工审批、不启动 daemon,也不把该目标写入 `daemon
40
45
  相同注册意图复用原 operation_id,不同目标返回冲突,`--force` 不覆盖协议 Owner。
41
46
  实现与验证说明见 `docs/init-control-aid-owner-registration.md`。
42
47
 
48
+ ## 收到绑定邀约后确认或拒绝
49
+
50
+ 先查询接收方本地 AID 的待确认邀约,再按确切的 `operation_id` 作决定:
51
+
52
+ ```bash
53
+ ec aid bind status --subject <接收方本地AID> --format json
54
+ ec aid bind accept <operationId> --subject <接收方本地AID> --format json
55
+ ec aid bind reject <operationId> --subject <接收方本地AID> --reason '用户拒绝' --format json
56
+ ```
57
+
58
+ `--subject` 始终指定执行审批的本地 AID。省略时,独立 CLI 使用 daemon Control AID,托管会话使用当前 self AID。
59
+ 例如 B 请求由 A 担任 Owner,在 A 上确认时应选 `--subject A`。A 的状态中 `requests` 展示 SDK 已验证的
60
+ 待确认绑定任务,包括 `operation_id`、`sub_aid`、`owner_aid`、`requester_aid`、本端 `local_role` 和关系版本。
61
+ 本端既可以是拟任 Owner,也可以是接受 Owner 绑定的 sub;转移、解绑及未验证请求不通过该入口审批。
62
+
63
+ CLI 经 daemon 已有的 SDK 客户端调用 `acceptOwnershipRequest` / `rejectOwnershipRequest`,不新建连接。
64
+ 请求的真实性、当前关系、固定参与方和决定一致性由 SDK 在执行时复核。未处理的邀约保持待审批。
65
+ 请求摘要只保存在当前客户端的内存中,daemon 重启或更换客户端后由 SDK 从持久任务重新发出事件恢复。
66
+
67
+ 审批回执的 `decisionTask` 是这次提交决定的任务;`pending` 表示仍在推进,`completed` 表示 SDK 已核验任务完成。
68
+ 接受回执本身不授予权限,Owner 权限仍由新鲜 Ownership 共识确定。后续 SDK 事件更新当前进程的 `decisionTask`。
69
+ 托管会话审批需要当前 Agent Owner 或 daemon Owner,以及 `request` / `bypass` 任务;admin 仅可查询。
70
+ 选择另一个本地 AID 需要 daemon Owner。Fullaccess 执行不额外授予协议绑定或审批权限。
71
+
43
72
  ## Ownership 共识缓存
44
73
 
45
74
  Agent 的首个配置 Owner 可在该 Agent 的 AUN 私聊中发送纯 `/bind`,由 Agent
@@ -86,6 +115,10 @@ ec aid agentmd put <aid>
86
115
  ec aid agentmd get <aid>
87
116
  ec aid agentmd set <aid> <name|type|description|version|tags> <value>
88
117
  ec aid avatar <aid> (--file <path> | --url <https-url>)
118
+ ec aid bind <ownerAid> [--subject <subjectAid>]
119
+ ec aid bind status [--subject <subjectAid>]
120
+ ec aid bind accept <operationId> [--subject <subjectAid>]
121
+ ec aid bind reject <operationId> [--reason '<text>'] [--subject <subjectAid>]
89
122
 
90
123
  # 删除指定本地 AID(无网络注销)
91
124
  ec aid delete <aid>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evolcore",
3
- "version": "0.0.37",
3
+ "version": "0.0.39",
4
4
  "description": "AI Agent gateway connecting Claude, Codex, Gemini, and the bundled ecagent runner to messaging channels with multi-project session management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -54,14 +54,14 @@
54
54
  "prepublishOnly": "npm test"
55
55
  },
56
56
  "dependencies": {
57
- "@agentunion/fastaun": "0.5.22",
57
+ "@agentunion/fastaun": "0.5.23",
58
58
  "@anthropic-ai/claude-agent-sdk": "0.3.300",
59
59
  "@modelcontextprotocol/sdk": "^1.29.0",
60
60
  "ajv": "^8.20.0",
61
61
  "chokidar": "^5.0.0",
62
62
  "cron-parser": "^5.5.0",
63
63
  "dotenv": "^17.4.2",
64
- "ec-web": "0.0.36",
64
+ "ec-web": "0.0.39",
65
65
  "ignore": "7.0.5",
66
66
  "image-type": "^6.0.0",
67
67
  "partial-json": "0.1.7",