evolcore 0.0.22 → 0.0.24

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 (117) hide show
  1. package/CHANGELOG.md +74 -14
  2. package/README.md +4 -0
  3. package/bin/codex-managed-acl.ps1 +160 -0
  4. package/bin/codex-managed-hook.mjs +127 -10
  5. package/bin/ec-safe-output.js +85 -3
  6. package/bin/install-codex-managed-hooks.mjs +384 -95
  7. package/dist/agents/baseagent-availability.js +48 -0
  8. package/dist/agents/baseagent.js +30 -2
  9. package/dist/agents/claude-runner.js +179 -240
  10. package/dist/agents/{codex-app-server-client.js → codex/app-server-client.js} +90 -20
  11. package/dist/{utils/codex-app-server-registry.js → agents/codex/app-server-registry.js} +2 -2
  12. package/dist/agents/codex/catalog.js +70 -0
  13. package/dist/agents/codex/diagnostics.js +369 -0
  14. package/dist/agents/codex-runner.js +839 -192
  15. package/dist/agents/ecagent-runner.js +27 -34
  16. package/dist/agents/gemini-runner.js +62 -8
  17. package/dist/agents/runner-types.js +1 -1
  18. package/dist/aun/aid/managed-operation.js +28 -6
  19. package/dist/aun/aid/store.js +43 -0
  20. package/dist/aun/menu-client.js +272 -0
  21. package/dist/aun/msg/group.js +65 -4
  22. package/dist/aun/msg/index.js +1 -1
  23. package/dist/aun/msg/managed-operation.js +57 -5
  24. package/dist/aun/outbox.js +56 -18
  25. package/dist/aun/rpc/client.js +7 -1
  26. package/dist/channels/aun.js +178 -77
  27. package/dist/channels/daemon.js +7 -1
  28. package/dist/channels/dingtalk.js +1 -0
  29. package/dist/channels/feishu.js +10 -4
  30. package/dist/channels/wechat.js +81 -10
  31. package/dist/channels/wecom.js +2 -1
  32. package/dist/cli/agent-command.js +29 -3
  33. package/dist/cli/agent-create.js +538 -0
  34. package/dist/cli/agent.js +26 -675
  35. package/dist/cli/aun-commands.js +108 -24
  36. package/dist/cli/command-log.js +21 -2
  37. package/dist/cli/ctl-command.js +2 -0
  38. package/dist/cli/daemon-commands.js +289 -61
  39. package/dist/cli/index.js +22 -2
  40. package/dist/cli/init.js +2 -10
  41. package/dist/cli/managed-command-guard.js +3 -0
  42. package/dist/cli/menu-client.js +95 -0
  43. package/dist/cli/model.js +23 -8
  44. package/dist/cli/restart-monitor.js +432 -8
  45. package/dist/cli/stats.js +110 -0
  46. package/dist/cli/task-context.js +12 -2
  47. package/dist/cli/trigger-command.js +9 -1
  48. package/dist/config/builtin-roles.js +2 -0
  49. package/dist/config/schema-migration.js +14 -2
  50. package/dist/core/auth/agent-delegation.js +10 -7
  51. package/dist/core/auth/auth-gateway.js +3 -2
  52. package/dist/core/auth/authorization-audit.js +75 -18
  53. package/dist/core/auth/operation-authorizer.js +63 -3
  54. package/dist/core/auth/operation-catalog.js +14 -2
  55. package/dist/core/auth/remote-access-store.js +136 -0
  56. package/dist/core/baseagent-loader.js +4 -4
  57. package/dist/core/bootstrap-messages.js +39 -18
  58. package/dist/core/bootstrap-service.js +32 -26
  59. package/dist/core/capability/capability-manager.js +8 -4
  60. package/dist/core/capability/providers/codex-capability-provider.js +1 -1
  61. package/dist/core/command/agent-control.js +7 -2
  62. package/dist/core/command/command-handler.js +128 -30
  63. package/dist/core/command/daemon-diagnostics.js +448 -0
  64. package/dist/core/command/menu-catalog.js +6 -1
  65. package/dist/core/command/menu-handler.js +410 -98
  66. package/dist/core/command/menu-protocol.js +123 -0
  67. package/dist/core/command/slash-gate.js +4 -6
  68. package/dist/core/command/slash-handler.js +199 -70
  69. package/dist/core/data-migration.js +5 -15
  70. package/dist/core/event-catalog.js +3 -0
  71. package/dist/core/handoff/runtime.js +3 -1
  72. package/dist/core/handoff/store.js +7 -1
  73. package/dist/core/message/im-renderer.js +13 -6
  74. package/dist/core/message/message-bridge.js +5 -36
  75. package/dist/core/message/message-queue.js +20 -15
  76. package/dist/core/message/message-utils.js +6 -3
  77. package/dist/core/message/response-engine.js +147 -94
  78. package/dist/core/model/catalog-service.js +310 -0
  79. package/dist/core/model/model-catalog.js +134 -334
  80. package/dist/core/model/model-diagnostics.js +4 -5
  81. package/dist/core/model/model-fallback.js +9 -16
  82. package/dist/core/model/model-permission.js +5 -2
  83. package/dist/core/permission/approval-gateway.js +183 -32
  84. package/dist/core/permission/protected-paths.js +63 -4
  85. package/dist/core/permission/sandbox-runtime.js +4 -2
  86. package/dist/core/permission/tool-policy.js +257 -31
  87. package/dist/core/runtime-lock.js +13 -8
  88. package/dist/core/session/adapters/codex-session-file-adapter.js +71 -3
  89. package/dist/core/session/session-manager.js +49 -13
  90. package/dist/core/session/session-renew.js +1 -0
  91. package/dist/index.js +214 -57
  92. package/dist/ipc.js +191 -102
  93. package/dist/paths.js +23 -1
  94. package/dist/stats/billing.js +18 -4
  95. package/dist/trigger/manager.js +3 -1
  96. package/dist/trigger/parser.js +77 -13
  97. package/dist/trigger/patch.js +104 -1
  98. package/dist/utils/atomic-write.js +26 -2
  99. package/dist/utils/cross-platform.js +436 -1
  100. package/dist/utils/ecweb-supervisor.js +11 -2
  101. package/dist/utils/ecweb-utils.js +8 -26
  102. package/dist/utils/error-utils.js +16 -2
  103. package/dist/utils/model-prices.jsonl +23 -20
  104. package/dist/utils/npm-ops.js +70 -15
  105. package/dist/utils/windows-autostart.js +1 -1
  106. package/kits/docs/evolcore/INDEX.md +1 -0
  107. package/kits/docs/evolcore/agent-create.md +13 -0
  108. package/kits/docs/evolcore/group-collaboration.md +7 -2
  109. package/kits/docs/evolcore/menu-client.md +34 -0
  110. package/kits/migrations/migrate-contact-book-v2.mjs +9 -1
  111. package/kits/migrations/migrate-role-config-v4.mjs +11 -5
  112. package/kits/migrations/migrate-role-config-v5.mjs +8 -2
  113. package/kits/templates/roles/admin.json +2 -0
  114. package/kits/templates/roles/visitor.json +1 -0
  115. package/kits/templates/system-fragments/bootstrap.md +8 -1
  116. package/package.json +16 -4
  117. package/dist/utils/codex-cli.js +0 -43
@@ -40,6 +40,8 @@ const BACKOFF_DELAY_MS = 30_000;
40
40
  const RETRY_DELAY_MS = 2_000;
41
41
  const TYPING_TICKET_TTL_MS = 5 * 60 * 1000; // 5 min cache
42
42
  const SESSION_EXPIRED_ERRCODE = -14;
43
+ const INVALID_SYNC_CURSOR_ERRCODE = -1;
44
+ const MAX_SYNC_CURSOR_BYTES = 64 * 1024;
43
45
  export class WechatApiError extends Error {
44
46
  endpoint;
45
47
  ret;
@@ -71,6 +73,26 @@ export class WechatContextTokenRefreshRequiredError extends Error {
71
73
  }
72
74
  }
73
75
  }
76
+ /**
77
+ * iLink cursors are opaque text, so validation deliberately stays conservative:
78
+ * reject values that cannot be safely round-tripped through the UTF-8 state file
79
+ * without imposing a specific encoding on future protocol versions.
80
+ */
81
+ export function validateWechatSyncCursor(cursor) {
82
+ if (typeof cursor !== 'string') {
83
+ return { valid: false, reason: 'invalid_type' };
84
+ }
85
+ if (Buffer.byteLength(cursor, 'utf8') > MAX_SYNC_CURSOR_BYTES) {
86
+ return { valid: false, reason: 'too_large' };
87
+ }
88
+ if (cursor.includes('\uFFFD')) {
89
+ return { valid: false, reason: 'invalid_utf8' };
90
+ }
91
+ if (/[\s\u0000-\u001F\u007F-\u009F]/u.test(cursor)) {
92
+ return { valid: false, reason: 'invalid_character' };
93
+ }
94
+ return { valid: true };
95
+ }
74
96
  /** Classify fetch/undici failures using the categories exposed by Tencent 2.4.6. */
75
97
  export function classifyFetchError(err) {
76
98
  if (err instanceof Error && err.name === 'AbortError') {
@@ -196,6 +218,7 @@ export class WechatChannel {
196
218
  pendingContextMessages = new Map();
197
219
  typingTicketCache = new Map();
198
220
  getUpdatesBuf = '';
221
+ lastRejectedSyncCursor;
199
222
  syncBufPath;
200
223
  contextTokensPath;
201
224
  onSessionExpired;
@@ -243,14 +266,24 @@ export class WechatChannel {
243
266
  throw new Error('WeChat reauthentication required; run: ec init wechat');
244
267
  }
245
268
  // 恢复游标
269
+ this.getUpdatesBuf = '';
270
+ this.lastRejectedSyncCursor = undefined;
246
271
  try {
247
272
  if (fs.existsSync(this.syncBufPath)) {
248
- this.getUpdatesBuf = fs.readFileSync(this.syncBufPath, 'utf-8');
249
- logger.info(`[WeChat] Restored sync cursor (${this.getUpdatesBuf.length} bytes)`);
273
+ const restoredCursor = fs.readFileSync(this.syncBufPath, 'utf-8');
274
+ const validation = validateWechatSyncCursor(restoredCursor);
275
+ if (validation.valid) {
276
+ this.getUpdatesBuf = restoredCursor;
277
+ logger.info(`[WeChat] Restored sync cursor (${Buffer.byteLength(restoredCursor, 'utf8')} bytes)`);
278
+ }
279
+ else {
280
+ this.clearSyncCursor(`persisted cursor is invalid (${validation.reason})`, restoredCursor);
281
+ }
250
282
  }
251
283
  }
252
- catch {
253
- // ignore
284
+ catch (err) {
285
+ logger.warn(`[WeChat] Failed to restore sync cursor; starting from an empty cursor: ${err instanceof Error ? err.message : String(err)}`);
286
+ this.getUpdatesBuf = '';
254
287
  }
255
288
  // 通知 ilink 后端:bot 上线
256
289
  try {
@@ -431,13 +464,22 @@ export class WechatChannel {
431
464
  }
432
465
  consecutiveFailures = 0;
433
466
  // 保存游标
434
- if (resp.get_updates_buf) {
435
- this.getUpdatesBuf = resp.get_updates_buf;
436
- try {
437
- fs.writeFileSync(this.syncBufPath, this.getUpdatesBuf, 'utf-8');
467
+ if (resp.get_updates_buf !== undefined) {
468
+ const validation = validateWechatSyncCursor(resp.get_updates_buf);
469
+ if (!validation.valid) {
470
+ this.clearSyncCursor(`server returned an invalid cursor (${validation.reason})`, typeof resp.get_updates_buf === 'string' ? resp.get_updates_buf : undefined);
438
471
  }
439
- catch {
440
- // best-effort
472
+ else if (resp.get_updates_buf) {
473
+ if (resp.get_updates_buf !== this.lastRejectedSyncCursor) {
474
+ this.lastRejectedSyncCursor = undefined;
475
+ }
476
+ this.getUpdatesBuf = resp.get_updates_buf;
477
+ try {
478
+ fs.writeFileSync(this.syncBufPath, this.getUpdatesBuf, 'utf-8');
479
+ }
480
+ catch {
481
+ // best-effort
482
+ }
441
483
  }
442
484
  }
443
485
  // 处理消息
@@ -450,6 +492,17 @@ export class WechatChannel {
450
492
  return;
451
493
  if (this.isSessionPaused())
452
494
  return;
495
+ if (err instanceof WechatApiError &&
496
+ err.endpoint === 'ilink/bot/getupdates' &&
497
+ err.hasCode(INVALID_SYNC_CURSOR_ERRCODE) &&
498
+ this.getUpdatesBuf &&
499
+ this.getUpdatesBuf !== this.lastRejectedSyncCursor) {
500
+ const rejectedCursor = this.getUpdatesBuf;
501
+ this.clearSyncCursor('getUpdates returned ret=-1', rejectedCursor);
502
+ consecutiveFailures = 0;
503
+ logger.warn('[WeChat] Retrying getUpdates immediately with an empty sync cursor');
504
+ continue;
505
+ }
453
506
  consecutiveFailures++;
454
507
  if (err instanceof WechatApiError) {
455
508
  logger.error(`[WeChat] getUpdates API error (${consecutiveFailures}/${MAX_CONSECUTIVE_FAILURES}): ${err.message}`);
@@ -470,6 +523,24 @@ export class WechatChannel {
470
523
  }
471
524
  }
472
525
  }
526
+ clearSyncCursor(reason, rejectedCursor) {
527
+ if (rejectedCursor)
528
+ this.lastRejectedSyncCursor = rejectedCursor;
529
+ this.getUpdatesBuf = '';
530
+ try {
531
+ let persistedAction = 'no persisted cursor';
532
+ if (fs.existsSync(this.syncBufPath)) {
533
+ const quarantinePath = `${this.syncBufPath}.corrupt-${Date.now()}`;
534
+ fs.renameSync(this.syncBufPath, quarantinePath);
535
+ persistedAction = `quarantined as ${path.basename(quarantinePath)}`;
536
+ }
537
+ logger.warn(`[WeChat] Cleared sync cursor: ${reason} (${persistedAction})`);
538
+ }
539
+ catch (err) {
540
+ logger.warn(`[WeChat] Cleared in-memory sync cursor but failed to quarantine persisted cursor: ` +
541
+ `${err instanceof Error ? err.message : String(err)}`);
542
+ }
543
+ }
473
544
  // ── Inbound Message Handler ──────────────────────────────────────────
474
545
  async handleInboundMessage(msg) {
475
546
  if (msg.message_type !== MSG_TYPE_USER)
@@ -1461,7 +1461,8 @@ export class WecomChannelPlugin {
1461
1461
  return;
1462
1462
  case 'status.started':
1463
1463
  case 'status.queued':
1464
- case 'status.progress': return;
1464
+ case 'status.progress':
1465
+ case 'status.notice': return;
1465
1466
  case 'status.requires_action':
1466
1467
  await channel.sendMessage(channelId, '等待 owner 审批', envelope.replyContext);
1467
1468
  return;
@@ -69,6 +69,7 @@ Options:
69
69
  return;
70
70
  }
71
71
  const { agentList, agentShow, agentCreateInteractive, agentCreateNonInteractive, agentReload, agentEnable, agentDisable, agentGet, agentSet, agentRename, agentDelete, agentReady, runManagedAgentOperation, } = await import('./agent.js');
72
+ const { parseAgentOwnerArgs, resolveAgentCreatePlan, validateAgentCreatePlan } = await import('./agent-create.js');
72
73
  if (process.env.EVOLCORE_SESSION_ID && !wantsHelp(args) && !new Set(['get', 'set', 'ready']).has(sub)) {
73
74
  if (sub === 'new' && !args.includes('--non-interactive')) {
74
75
  const error = {
@@ -210,6 +211,8 @@ Options:
210
211
  --baseagent <claude|codex|gemini|ecagent>
211
212
  默认: defaults 配置,否则检测本机可用基座
212
213
  --owner <aid> 默认: daemon.json 第一个 owner
214
+ --owners <json-array> 多个 Owner;与 --owner 互斥
215
+ --handoff <context> 当前任务的 Bootstrap 上下文
213
216
  --name <display-name> 默认: package.json name,否则目录名
214
217
  --description <text> 默认: package.json description,否则使用 EvolCore 默认描述
215
218
  --dry-run 只预览最终参数,不注册 AID、不写文件
@@ -223,6 +226,15 @@ Options:
223
226
  ec agent new mybot.agentid.pub --non-interactive --project ../project --baseagent claude`);
224
227
  return;
225
228
  }
229
+ if (args.includes('--handoff')) {
230
+ const result = { ok: false, code: 'MANAGED_ONLY', error: '--handoff requires a managed Agent task' };
231
+ if (formatJson)
232
+ console.log(JSON.stringify(result));
233
+ else
234
+ console.error(result.error);
235
+ process.exitCode = 1;
236
+ return;
237
+ }
226
238
  const name = args[1];
227
239
  const nonInteractive = args.includes('--non-interactive');
228
240
  if (nonInteractive) {
@@ -234,12 +246,25 @@ Options:
234
246
  const idx = args.indexOf(flag);
235
247
  return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : undefined;
236
248
  };
237
- const { resolveAgentCreatePlan, validateAgentCreatePlan } = await import('./agent.js');
249
+ let ownerOptions;
250
+ try {
251
+ ownerOptions = parseAgentOwnerArgs(args);
252
+ }
253
+ catch (error) {
254
+ const result = { ok: false, error: error instanceof Error ? error.message : String(error) };
255
+ if (formatJson)
256
+ console.log(JSON.stringify(result));
257
+ else
258
+ console.error(`❌ ${result.error}`);
259
+ process.exitCode = 1;
260
+ return;
261
+ }
238
262
  const plan = resolveAgentCreatePlan({
239
263
  aid: name,
240
264
  project: getArg('--project'),
241
265
  baseagent: getArg('--baseagent'),
242
- owner: getArg('--owner'),
266
+ ...ownerOptions,
267
+ handoff: getArg('--handoff'),
243
268
  name: getArg('--name'),
244
269
  description: getArg('--description'),
245
270
  force: args.includes('--force'),
@@ -263,7 +288,7 @@ Options:
263
288
  console.log(` AID: ${plan.aid}`);
264
289
  console.log(` Project: ${plan.project}`);
265
290
  console.log(` Baseagent: ${plan.baseagent} (${plan.baseagentSource})`);
266
- console.log(` Owner: ${plan.owner || '未设置'} (${plan.ownerSource})`);
291
+ console.log(` Owner: ${plan.owners?.join(', ') || plan.owner || '未设置'} (${plan.ownerSource})`);
267
292
  console.log(` Name: ${plan.name} (${plan.nameSource})`);
268
293
  if (plan.description)
269
294
  console.log(` Description: ${plan.description} (${plan.descriptionSource})`);
@@ -276,6 +301,7 @@ Options:
276
301
  baseagent: plan.baseagent,
277
302
  project: plan.project,
278
303
  owner: plan.owner,
304
+ owners: plan.owners,
279
305
  name: plan.name,
280
306
  description: plan.description,
281
307
  force: args.includes('--force'),