evolclaw 3.2.0 → 3.4.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 (95) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +7 -4
  3. package/dist/agents/{resolve.js → baseagent.js} +34 -5
  4. package/dist/agents/claude-runner.js +120 -31
  5. package/dist/agents/codex-app-server-client.js +364 -0
  6. package/dist/agents/codex-runner.js +1152 -140
  7. package/dist/agents/gemini-runner.js +2 -2
  8. package/dist/agents/runner-types.js +58 -0
  9. package/dist/aun/aid/store.js +1 -1
  10. package/dist/aun/outbox.js +14 -2
  11. package/dist/aun/storage/download.js +1 -1
  12. package/dist/aun/storage/upload.js +13 -1
  13. package/dist/channels/aun.js +869 -358
  14. package/dist/channels/dingtalk.js +77 -140
  15. package/dist/channels/feishu.js +125 -154
  16. package/dist/channels/qqbot.js +75 -138
  17. package/dist/channels/wechat.js +75 -136
  18. package/dist/channels/wecom.js +75 -138
  19. package/dist/cli/agent-command.js +591 -0
  20. package/dist/cli/agent.js +23 -8
  21. package/dist/cli/aun-commands.js +1444 -0
  22. package/dist/cli/ctl-command.js +78 -0
  23. package/dist/cli/daemon-commands.js +2707 -0
  24. package/dist/cli/index.js +23 -4905
  25. package/dist/cli/init.js +33 -6
  26. package/dist/cli/model.js +1 -1
  27. package/dist/cli/restart-monitor.js +539 -0
  28. package/dist/cli/stats.js +558 -0
  29. package/dist/cli/version.js +87 -0
  30. package/dist/cli/watch-logs.js +33 -0
  31. package/dist/cli/watch-msg.js +5 -2
  32. package/dist/config-store.js +12 -6
  33. package/dist/core/channel-loader.js +88 -83
  34. package/dist/core/command/command-handler.js +1189 -0
  35. package/dist/core/command/menu-handler.js +1478 -0
  36. package/dist/core/command/slash-gate.js +142 -0
  37. package/dist/core/command/slash-handler.js +2090 -0
  38. package/dist/core/evolagent-registry.js +82 -0
  39. package/dist/core/evolagent.js +17 -1
  40. package/dist/core/interaction-router.js +8 -0
  41. package/dist/core/message/command-handler-agent-control.js +63 -1
  42. package/dist/core/message/im-renderer.js +91 -51
  43. package/dist/core/message/items-formatter.js +9 -1
  44. package/dist/core/message/message-bridge.js +73 -24
  45. package/dist/core/message/message-log.js +1 -0
  46. package/dist/core/message/message-processor.js +432 -94
  47. package/dist/core/message/message-queue.js +70 -2
  48. package/dist/core/message/pending-hints.js +232 -0
  49. package/dist/core/model/model-catalog.js +1 -1
  50. package/dist/core/model/model-scope.js +2 -2
  51. package/dist/core/permission.js +25 -12
  52. package/dist/core/relation/peer-identity.js +16 -1
  53. package/dist/core/session/adapters/codex-session-file-adapter.js +4 -2
  54. package/dist/core/session/session-manager.js +86 -26
  55. package/dist/core/session/session-title.js +26 -0
  56. package/dist/core/stats/billing.js +151 -0
  57. package/dist/core/stats/budget.js +93 -0
  58. package/dist/core/stats/db.js +334 -0
  59. package/dist/core/stats/eck-vars.js +84 -0
  60. package/dist/core/stats/index.js +10 -0
  61. package/dist/core/stats/normalizer.js +78 -0
  62. package/dist/core/stats/query.js +760 -0
  63. package/dist/core/stats/writer.js +115 -0
  64. package/dist/core/trigger/manager.js +34 -0
  65. package/dist/core/trigger/parser.js +9 -3
  66. package/dist/core/trigger/scheduler.js +20 -17
  67. package/dist/data/error-dict.json +7 -0
  68. package/dist/{agents → eck}/manifest-engine.js +20 -1
  69. package/dist/{agents → eck}/message-renderer.js +24 -1
  70. package/dist/index.js +174 -9
  71. package/dist/ipc.js +116 -1
  72. package/dist/utils/cross-platform.js +58 -5
  73. package/dist/utils/ecweb-launch.js +49 -0
  74. package/dist/utils/ecweb-pair.js +20 -0
  75. package/dist/utils/error-utils.js +18 -5
  76. package/dist/utils/npm-ops.js +38 -8
  77. package/dist/utils/stats.js +77 -6
  78. package/kits/docs/evolclaw/INDEX.md +3 -1
  79. package/kits/docs/evolclaw/fs-architecture.md +1215 -0
  80. package/kits/docs/evolclaw/fs.md +131 -0
  81. package/kits/docs/evolclaw/group-fs.md +209 -0
  82. package/kits/docs/evolclaw/stats.md +70 -0
  83. package/kits/docs/venues/aun-group.md +29 -6
  84. package/kits/docs/venues/group.md +5 -4
  85. package/kits/eck_message_manifest.json +30 -3
  86. package/kits/rules/05-venue.md +1 -1
  87. package/kits/templates/message-fragments/inject-default.md +2 -0
  88. package/package.json +5 -6
  89. package/dist/agents/baseagent-normalize.js +0 -19
  90. package/dist/core/command-handler.js +0 -3876
  91. package/dist/core/relation/peer-key.js +0 -16
  92. package/dist/evolclaw-config.js +0 -11
  93. package/dist/utils/channel-helpers.js +0 -46
  94. /package/dist/core/{cache/file-cache.js → daemon-file-cache.js} +0 -0
  95. /package/dist/{agents → eck}/kit-renderer.js +0 -0
@@ -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,58 @@
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
+ }
29
+ /** Models whose base ID uses a 1M context window when sent to the SDK with [1m]. */
30
+ export const ONE_M_CONTEXT_MODEL_PREFIXES = ['claude-opus-4-8', 'claude-sonnet-4-6'];
31
+ const ONE_M_CONTEXT_MODEL_ALIASES = ['opus', 'sonnet'];
32
+ const CLAUDE_CONTEXT_MODEL_ALIASES = ['opus', 'sonnet', 'haiku'];
33
+ export function isClaudeContextUsageModel(model) {
34
+ if (!model)
35
+ return false;
36
+ const baseModel = model.replace(/\[1m\]$/i, '');
37
+ return /^claude-/i.test(baseModel) || CLAUDE_CONTEXT_MODEL_ALIASES.includes(baseModel);
38
+ }
39
+ export function isOneMillionContextModel(model) {
40
+ if (!model)
41
+ return false;
42
+ if (ONE_M_CONTEXT_MODEL_ALIASES.includes(model))
43
+ return true;
44
+ if (/\[1m\]$/i.test(model))
45
+ return true;
46
+ if (/deepseek-v4/i.test(model))
47
+ return true;
48
+ const baseModel = model.replace(/\[1m\]$/i, '');
49
+ return ONE_M_CONTEXT_MODEL_PREFIXES.some(prefix => baseModel === prefix || baseModel.startsWith(`${prefix}-`));
50
+ }
51
+ /** Real context window size: 1M models = 1000000, otherwise 200000. */
52
+ export function realContextWindowForModel(model) {
53
+ return isOneMillionContextModel(model) ? 1000000 : 200000;
54
+ }
55
+ /** autoCompact trigger threshold: 1M models = 900000, otherwise 200000. */
56
+ export function autoCompactWindowForModel(model) {
57
+ return isOneMillionContextModel(model) ? 900000 : 200000;
58
+ }
@@ -39,7 +39,7 @@ 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 { loadEvolclawConfig } = await import('../../evolclaw-config.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
45
  const encryptionSeed = loadEvolclawConfig().aun?.encryptionSeed
@@ -59,10 +59,14 @@ export function enqueue(aid, opts) {
59
59
  aid,
60
60
  channelId: opts.channelId,
61
61
  type: opts.type,
62
+ contentKind: opts.contentKind,
63
+ payload: opts.payload,
62
64
  text: opts.text,
63
65
  filePath: opts.filePath,
66
+ logText: opts.logText,
64
67
  context: opts.context,
65
68
  ttl: opts.ttl ?? DEFAULT_TTL,
69
+ postSend: opts.postSend,
66
70
  };
67
71
  const dir = outboxDir();
68
72
  fs.mkdirSync(dir, { recursive: true });
@@ -97,6 +101,7 @@ export async function drain(aid, sender) {
97
101
  const entries = readEntries(aid);
98
102
  if (entries.length === 0)
99
103
  return { sent: 0, expired: 0, failed: 0 };
104
+ const drainedIds = new Set(entries.map(e => e.id));
100
105
  let sent = 0;
101
106
  let expired = 0;
102
107
  let failed = 0;
@@ -107,21 +112,28 @@ export async function drain(aid, sender) {
107
112
  continue;
108
113
  }
109
114
  try {
115
+ entry.attempts = (entry.attempts ?? 0) + 1;
110
116
  const ok = await sender(entry);
111
117
  if (ok) {
112
118
  sent++;
113
119
  }
114
120
  else {
115
121
  failed++;
122
+ entry.lastError = 'sender returned false';
116
123
  remaining.push(entry);
117
124
  }
118
125
  }
119
- catch {
126
+ catch (e) {
120
127
  failed++;
128
+ entry.lastError = e instanceof Error ? e.message : String(e);
121
129
  remaining.push(entry);
122
130
  }
123
131
  }
124
- writeEntries(aid, remaining);
132
+ const current = readEntries(aid);
133
+ const currentIds = new Set(current.map(e => e.id));
134
+ const retainedNewEntries = current.filter(e => !drainedIds.has(e.id));
135
+ const retainedFailedEntries = remaining.filter(e => currentIds.has(e.id));
136
+ writeEntries(aid, [...retainedFailedEntries, ...retainedNewEntries]);
125
137
  return { sent, expired, failed };
126
138
  }
127
139
  export function hasPending(aid) {
@@ -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
  }