evolclaw 3.1.11 → 3.3.0

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 (89) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +27 -2
  3. package/dist/agents/{resolve.js → baseagent.js} +34 -5
  4. package/dist/agents/claude-runner.js +120 -27
  5. package/dist/agents/codex-app-server-client.js +364 -0
  6. package/dist/agents/codex-runner.js +1069 -141
  7. package/dist/agents/gemini-runner.js +2 -2
  8. package/dist/agents/runner-types.js +28 -0
  9. package/dist/aun/aid/control-aid.js +67 -0
  10. package/dist/aun/aid/identity.js +20 -7
  11. package/dist/aun/aid/store.js +2 -2
  12. package/dist/aun/storage/download.js +1 -1
  13. package/dist/aun/storage/upload.js +13 -1
  14. package/dist/channels/aun.js +538 -325
  15. package/dist/channels/dingtalk.js +77 -140
  16. package/dist/channels/feishu.js +98 -151
  17. package/dist/channels/qqbot.js +75 -138
  18. package/dist/channels/wechat.js +75 -136
  19. package/dist/channels/wecom.js +75 -138
  20. package/dist/cli/agent.js +44 -13
  21. package/dist/cli/index.js +207 -46
  22. package/dist/cli/init-channel.js +38 -148
  23. package/dist/cli/init.js +192 -85
  24. package/dist/cli/model.js +1 -1
  25. package/dist/cli/stats.js +558 -0
  26. package/dist/cli/version.js +87 -0
  27. package/dist/cli/watch-msg.js +5 -2
  28. package/dist/config-store.js +48 -11
  29. package/dist/core/channel-loader.js +84 -82
  30. package/dist/core/command-handler.js +754 -172
  31. package/dist/core/daemon-file-cache.js +216 -0
  32. package/dist/core/evolagent-registry.js +4 -0
  33. package/dist/core/evolagent.js +28 -23
  34. package/dist/core/interaction-router.js +8 -0
  35. package/dist/core/message/command-handler-agent-control.js +215 -0
  36. package/dist/core/message/create-status.js +67 -0
  37. package/dist/core/message/im-renderer.js +35 -13
  38. package/dist/core/message/items-formatter.js +9 -1
  39. package/dist/core/message/message-bridge.js +52 -22
  40. package/dist/core/message/message-log.js +1 -0
  41. package/dist/core/message/message-processor.js +336 -68
  42. package/dist/core/message/message-queue.js +15 -8
  43. package/dist/core/message/pending-hints.js +232 -0
  44. package/dist/core/message/response-depth.js +56 -0
  45. package/dist/core/model/model-catalog.js +1 -1
  46. package/dist/core/model/model-scope.js +40 -7
  47. package/dist/core/permission.js +9 -12
  48. package/dist/core/relation/peer-identity.js +16 -1
  49. package/dist/core/session/adapters/claude-session-file-adapter.js +48 -5
  50. package/dist/core/session/adapters/codex-session-file-adapter.js +4 -2
  51. package/dist/core/session/session-manager.js +27 -13
  52. package/dist/core/session/session-title.js +26 -0
  53. package/dist/core/stats/billing.js +151 -0
  54. package/dist/core/stats/budget.js +93 -0
  55. package/dist/core/stats/db.js +314 -0
  56. package/dist/core/stats/eck-vars.js +84 -0
  57. package/dist/core/stats/index.js +10 -0
  58. package/dist/core/stats/normalizer.js +78 -0
  59. package/dist/core/stats/query.js +760 -0
  60. package/dist/core/stats/writer.js +115 -0
  61. package/dist/core/trigger/manager.js +34 -0
  62. package/dist/core/trigger/parser.js +9 -3
  63. package/dist/core/trigger/scheduler.js +20 -17
  64. package/dist/{agents → eck}/kit-renderer.js +5 -1
  65. package/dist/{agents → eck}/manifest-engine.js +127 -35
  66. package/dist/{agents → eck}/message-renderer.js +26 -1
  67. package/dist/index.js +185 -8
  68. package/dist/ipc.js +22 -0
  69. package/dist/paths.js +7 -3
  70. package/dist/utils/cross-platform.js +23 -5
  71. package/dist/utils/ecweb-pair.js +20 -0
  72. package/dist/utils/stats.js +14 -0
  73. package/kits/docs/evolclaw/INDEX.md +3 -1
  74. package/kits/docs/evolclaw/fs-architecture.md +1215 -0
  75. package/kits/docs/evolclaw/fs.md +131 -0
  76. package/kits/docs/evolclaw/group-fs.md +209 -0
  77. package/kits/docs/evolclaw/stats.md +70 -0
  78. package/kits/docs/venues/aun-group.md +29 -6
  79. package/kits/docs/venues/group.md +5 -4
  80. package/kits/eck_manifest.json +12 -0
  81. package/kits/eck_message_manifest.json +30 -3
  82. package/kits/rules/05-venue.md +1 -1
  83. package/kits/templates/message-fragments/inject-default.md +2 -0
  84. package/kits/templates/message-fragments/item.md +1 -1
  85. package/kits/templates/system-fragments/response-depth.md +16 -0
  86. package/package.json +4 -4
  87. package/dist/agents/baseagent-normalize.js +0 -19
  88. package/dist/core/relation/peer-key.js +0 -16
  89. package/dist/utils/channel-helpers.js +0 -46
@@ -13,7 +13,7 @@ import { createInterface } from 'readline';
13
13
  import fs from 'fs';
14
14
  import path from 'path';
15
15
  import os from 'os';
16
- import { resolveGoogleConfig } from './resolve.js';
16
+ import { resolveGoogleConfig } from './baseagent.js';
17
17
  import { commandExists } from '../utils/cross-platform.js';
18
18
  import { GeminiSessionFileAdapter } from '../core/session/adapters/gemini-session-file-adapter.js';
19
19
  import { logger } from '../utils/logger.js';
@@ -41,7 +41,7 @@ const GEMINI_MODELS = [
41
41
  // ── Gemini Runner ──
42
42
  export class GeminiRunner {
43
43
  name = 'gemini';
44
- capabilities = { clear: true, compact: false, fork: false };
44
+ capabilities = { clear: true, compact: false, fork: false, askUserQuestion: false, planApproval: false, fileRewind: 'unsupported' };
45
45
  resolved;
46
46
  model;
47
47
  activeProcesses = new Map();
@@ -0,0 +1,28 @@
1
+ // ── 类型守卫 ──
2
+ export function hasModelSwitcher(agent) {
3
+ return typeof agent.setModel === 'function' && typeof agent.listModels === 'function';
4
+ }
5
+ export function hasPermissionController(agent) {
6
+ return typeof agent.setMode === 'function' && typeof agent.listModes === 'function';
7
+ }
8
+ export function hasCompact(agent) {
9
+ return typeof agent.compact === 'function';
10
+ }
11
+ // ── Token usage helper functions ──
12
+ export function numericToken(value) {
13
+ return typeof value === 'number' && Number.isFinite(value) ? value : 0;
14
+ }
15
+ export function contextTokensForUsage(usage, isClaudeModel) {
16
+ if (!usage)
17
+ return 0;
18
+ if (!isClaudeModel)
19
+ return numericToken(usage.input_tokens);
20
+ return numericToken(usage.input_tokens)
21
+ + numericToken(usage.cache_creation_input_tokens)
22
+ + numericToken(usage.cache_read_input_tokens);
23
+ }
24
+ export function usageForContext(usage) {
25
+ const iterations = Array.isArray(usage?.iterations) ? usage.iterations : undefined;
26
+ const lastIteration = iterations?.slice().reverse().find(it => contextTokensForUsage(it, true) > 0);
27
+ return lastIteration ?? usage;
28
+ }
@@ -0,0 +1,67 @@
1
+ import crypto from 'crypto';
2
+ import { aidCreate } from './index.js';
3
+ import { getAidStore, SLOT } from './store.js';
4
+ import { logger } from '../../utils/logger.js';
5
+ const MAX_ATTEMPTS = 5;
6
+ /** 生成候选控制 AID:ec + 5位随机数字 + .agentid.pub */
7
+ export function candidateAid() {
8
+ const n = crypto.randomInt(10000, 100000); // 5 位:10000-99999
9
+ return `ec${n}.agentid.pub`;
10
+ }
11
+ /**
12
+ * 候选 AID 是否已在 PKI 注册。
13
+ *
14
+ * 不用 store.exists(它走 HTTP HEAD /pki/cert/<aid>)——部分 Gateway 实现
15
+ * (Python websockets HTTP 处理)对 HEAD 直接空响应断连(curl 52 / socket hang up),
16
+ * 导致 exists 误报"网关不可达"。改用 store.resolve(走 GET),语义等价且 GET 正常返回:
17
+ * - resolve ok → 证书存在(HTTP 200)→ 已注册
18
+ * - CERT_NOT_FOUND → 404 → 未注册
19
+ * - 其它 error → 真·网络错误,向上抛出供 fail-fast
20
+ * skipAgentMd:true 避免多拉一次 agent.md(控制 AID 本就不传 agent.md)。
21
+ */
22
+ async function candidateExists(store, candidate) {
23
+ const r = await store.resolve(candidate, { skipAgentMd: true });
24
+ if (r.ok)
25
+ return true;
26
+ if (r.error?.code === 'CERT_NOT_FOUND')
27
+ return false;
28
+ throw new Error(`Gateway 不可达,无法查重控制 AID:${r.error?.message ?? 'unknown'}`);
29
+ }
30
+ /**
31
+ * 生成控制 AID:循环候选 → candidateExists 查重(权威 PKI 判据)→ 不冲突则 aidCreate。
32
+ * - 查重走 GET 证书(见 candidateExists;不拉 agent.md,控制 AID 本就不传 agent.md)
33
+ * - fail-fast:查重探测失败(网关不可达)立即抛错,不掩盖成"均冲突"
34
+ * - agent.md 不上传:aidCreate 仅注册身份 + 写私钥,不调 agentmdPut
35
+ */
36
+ export async function generateControlAid() {
37
+ const store = await getAidStore({ slotId: SLOT.cli });
38
+ try {
39
+ for (let i = 0; i < MAX_ATTEMPTS; i++) {
40
+ const candidate = candidateAid();
41
+ if (await candidateExists(store, candidate)) {
42
+ logger.info(`[control-aid] ${candidate} 已注册,重试 (${i + 1}/${MAX_ATTEMPTS})`);
43
+ continue;
44
+ }
45
+ const created = await aidCreate(candidate);
46
+ // 清理 aidCreate 内部另开的 client/store——关闭失败不可丢弃已注册的 AID(否则下次 init
47
+ // 会把它当冲突,白白消耗一次重试)。close 异常降级为 warn。
48
+ try {
49
+ await created.client?.close?.();
50
+ }
51
+ catch (e) {
52
+ logger.warn(`[control-aid] client.close() 失败(非致命): ${e}`);
53
+ }
54
+ try {
55
+ await created.store?.close?.();
56
+ }
57
+ catch (e) {
58
+ logger.warn(`[control-aid] store.close() 失败(非致命): ${e}`);
59
+ }
60
+ return { aid: created.aid, gateway: created.gateway };
61
+ }
62
+ throw new Error(`无法生成控制 AID:连续 ${MAX_ATTEMPTS} 次候选均冲突`);
63
+ }
64
+ finally {
65
+ store.close();
66
+ }
67
+ }
@@ -443,6 +443,7 @@ export async function probePkiRecoverability(aid, opts) {
443
443
  }
444
444
  // ==================== Lookup ====================
445
445
  export async function aidLookup(aid) {
446
+ // gateway:well-known 探测(保留,供 aid lookup 命令展示)
446
447
  let gateway = '';
447
448
  try {
448
449
  const gwResp = await fetch(`https://${aid}/.well-known/aun-gateway`, { redirect: 'follow' });
@@ -464,16 +465,28 @@ export async function aidLookup(aid) {
464
465
  }
465
466
  }
466
467
  catch { /* ignore */ }
468
+ const { agentmdGet } = await import('./agentmd.js');
469
+ const store = await getAidStore({ slotId: SLOT.cli });
467
470
  try {
468
- const resp = await fetch(`https://${aid}/agent.md`, { redirect: 'follow' });
469
- if (resp.ok) {
470
- const content = await resp.text();
471
- return { exists: true, aid, gateway, content };
471
+ // 权威注册判据:PKI 证书 HEAD(与 agent.md 无关)
472
+ const existsResult = await store.exists(aid);
473
+ if (!existsResult.ok) {
474
+ return { exists: false, aid, gateway, error: existsResult.error?.message ?? 'exists check failed' };
472
475
  }
473
- return { exists: false, aid, gateway, error: `agent_md_not_found` };
476
+ const exists = existsResult.data.exists;
477
+ if (!exists) {
478
+ return { exists: false, aid, gateway };
479
+ }
480
+ // 已注册:尽力拉 agent.md content(无 agent.md 不影响 exists)
481
+ let content;
482
+ try {
483
+ content = await agentmdGet(aid, { store });
484
+ }
485
+ catch { /* registered but no agent.md — content stays undefined */ }
486
+ return { exists, aid, gateway, content };
474
487
  }
475
- catch (e) {
476
- return { exists: false, aid, gateway, error: String(e.message || e) };
488
+ finally {
489
+ store.close();
477
490
  }
478
491
  }
479
492
  function lifecycleLogPath(aid) {
@@ -39,10 +39,10 @@ export class AidLoadError extends Error {
39
39
  */
40
40
  export async function getAidStore(opts) {
41
41
  const { aunPath: defaultAunPath } = await import('../../paths.js');
42
- const { loadProcessConfig } = await import('../../config-store.js');
42
+ const { loadEvolclawConfig } = await import('../../config-store.js');
43
43
  const { AIDStore } = await import('@agentunion/fastaun');
44
44
  const aunPath = opts.aunPath ?? defaultAunPath();
45
- const encryptionSeed = loadProcessConfig().aun?.encryptionSeed
45
+ const encryptionSeed = loadEvolclawConfig().aun?.encryptionSeed
46
46
  ?? process.env.AUN_ENCRYPTION_SEED
47
47
  ?? 'evol';
48
48
  const caCertPath = path.join(aunPath, 'CA', 'root', 'root.crt');
@@ -11,7 +11,7 @@ export async function storageDownload(aid, url, localPath, opts) {
11
11
  const ownerAid = cleaned.slice(0, slashIdx);
12
12
  const objectKey = cleaned.slice(slashIdx + 1);
13
13
  const ticketResult = await rpcCall(aid, 'storage.create_download_ticket', {
14
- owner: ownerAid,
14
+ owner_aid: ownerAid,
15
15
  object_key: objectKey,
16
16
  }, { aunPath: opts?.aunPath });
17
17
  if (!ticketResult.ok) {
@@ -27,9 +27,21 @@ export async function storageUpload(aid, localFile, remotePath, opts) {
27
27
  const completeResult = await rpcCall(aid, 'storage.complete_upload', {
28
28
  object_key: remotePath,
29
29
  sha256,
30
+ is_private: !(opts?.isPublic),
30
31
  }, { aunPath: opts?.aunPath });
31
32
  if (!completeResult.ok) {
32
33
  return { ok: false, objectKey: remotePath, error: JSON.stringify(completeResult.error) };
33
34
  }
34
- return { ok: true, objectKey: remotePath };
35
+ // 公开上传时获取可访问的 URL
36
+ let publicUrl;
37
+ if (opts?.isPublic) {
38
+ const ticketResult = await rpcCall(aid, 'storage.create_download_ticket', {
39
+ object_key: remotePath,
40
+ expire_in_seconds: 86400 * 30, // 30天
41
+ }, { aunPath: opts?.aunPath });
42
+ if (ticketResult.ok) {
43
+ publicUrl = `https://${aid}/storage/${remotePath}`;
44
+ }
45
+ }
46
+ return { ok: true, objectKey: remotePath, publicUrl };
35
47
  }