evolcore 0.0.23 → 0.0.25

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 (245) hide show
  1. package/CHANGELOG.md +583 -516
  2. package/LICENSE +21 -21
  3. package/README.md +455 -453
  4. package/bin/codex-managed-acl.ps1 +160 -155
  5. package/bin/codex-managed-hook.mjs +209 -92
  6. package/bin/ec-safe-output.js +327 -245
  7. package/bin/ec.js +29 -29
  8. package/bin/install-codex-managed-hooks.mjs +555 -544
  9. package/dist/agents/baseagent-availability.js +4 -4
  10. package/dist/agents/baseagent.js +29 -1
  11. package/dist/agents/claude-runner.js +152 -14
  12. package/dist/agents/codex/app-server-client.js +18 -8
  13. package/dist/agents/codex/catalog.js +1 -1
  14. package/dist/agents/codex/diagnostics.js +215 -0
  15. package/dist/agents/codex-runner.js +445 -63
  16. package/dist/agents/ecagent-runner.js +31 -16
  17. package/dist/agents/gemini-runner.js +82 -4
  18. package/dist/agents/runner-types.js +1 -1
  19. package/dist/aun/aid/agentmd.js +56 -4
  20. package/dist/aun/aid/index.js +1 -0
  21. package/dist/aun/aid/managed-operation.js +80 -6
  22. package/dist/aun/aid/store.js +41 -0
  23. package/dist/aun/msg/managed-operation.js +6 -1
  24. package/dist/aun/outbox.js +56 -18
  25. package/dist/aun/rpc/client.js +4 -5
  26. package/dist/aun/storage/client.js +4 -1
  27. package/dist/aun/storage/index.js +1 -1
  28. package/dist/channels/aun.js +147 -76
  29. package/dist/channels/daemon.js +7 -1
  30. package/dist/channels/dingtalk.js +1 -0
  31. package/dist/channels/feishu.js +10 -4
  32. package/dist/channels/wecom.js +2 -1
  33. package/dist/cli/agent-command.js +214 -89
  34. package/dist/cli/agent-create.js +576 -0
  35. package/dist/cli/agent.js +129 -676
  36. package/dist/cli/aun-commands.js +419 -313
  37. package/dist/cli/bench.js +20 -20
  38. package/dist/cli/config.js +20 -20
  39. package/dist/cli/contact.js +10 -10
  40. package/dist/cli/ctl-command.js +22 -20
  41. package/dist/cli/daemon-commands.js +163 -32
  42. package/dist/cli/fs-command.js +67 -67
  43. package/dist/cli/handoff-command.js +7 -7
  44. package/dist/cli/index.js +79 -79
  45. package/dist/cli/link-rules.js +21 -21
  46. package/dist/cli/managed-command-guard.js +3 -0
  47. package/dist/cli/menu-client.js +11 -11
  48. package/dist/cli/net-check.js +22 -22
  49. package/dist/cli/queue-command.js +29 -29
  50. package/dist/cli/response.js +30 -30
  51. package/dist/cli/restart-monitor.js +462 -38
  52. package/dist/cli/stats.js +161 -51
  53. package/dist/cli/trigger-command.js +56 -56
  54. package/dist/config/builtin-roles.js +3 -0
  55. package/dist/config/config-manager.js +15 -0
  56. package/dist/config/config-operation-service.js +20 -3
  57. package/dist/config/resolved-config-op.js +39 -1
  58. package/dist/core/auth/auth-gateway.js +3 -2
  59. package/dist/core/auth/authorization-audit.js +72 -19
  60. package/dist/core/auth/operation-authorizer.js +125 -7
  61. package/dist/core/auth/operation-catalog.js +26 -2
  62. package/dist/core/auth/remote-access-store.js +3 -2
  63. package/dist/core/baseagent-loader.js +4 -4
  64. package/dist/core/bootstrap-messages.js +39 -18
  65. package/dist/core/bootstrap-service.js +32 -26
  66. package/dist/core/capability/capability-manager.js +8 -4
  67. package/dist/core/capability/providers/codex-capability-provider.js +1 -1
  68. package/dist/core/command/agent-control.js +60 -26
  69. package/dist/core/command/cli-intent-parser.js +18 -0
  70. package/dist/core/command/command-handler.js +173 -36
  71. package/dist/core/command/menu-catalog.js +4 -1
  72. package/dist/core/command/menu-handler.js +315 -75
  73. package/dist/core/command/menu-protocol.js +7 -2
  74. package/dist/core/command/slash-gate.js +1 -3
  75. package/dist/core/command/slash-handler.js +166 -57
  76. package/dist/core/data-migration.js +5 -15
  77. package/dist/core/event-catalog.js +8 -0
  78. package/dist/core/evolagent.js +3 -3
  79. package/dist/core/handoff/runtime.js +3 -1
  80. package/dist/core/handoff/store.js +7 -1
  81. package/dist/core/message/im-renderer.js +13 -6
  82. package/dist/core/message/message-bridge.js +5 -36
  83. package/dist/core/message/message-utils.js +6 -3
  84. package/dist/core/message/response-engine.js +85 -27
  85. package/dist/core/model/catalog-service.js +3 -1
  86. package/dist/core/model/model-catalog.js +10 -0
  87. package/dist/core/model/model-permission.js +4 -1
  88. package/dist/core/permission/approval-gateway.js +169 -33
  89. package/dist/core/permission/tool-policy.js +56 -10
  90. package/dist/core/session/adapters/codex-session-file-adapter.js +71 -3
  91. package/dist/core/session/session-manager.js +49 -13
  92. package/dist/core/session/session-renew.js +1 -0
  93. package/dist/index.js +327 -134
  94. package/dist/ipc.js +68 -21
  95. package/dist/paths.js +23 -1
  96. package/dist/stats/billing.js +18 -4
  97. package/dist/stats/db.js +159 -159
  98. package/dist/stats/eck-vars.js +9 -9
  99. package/dist/stats/query.js +139 -139
  100. package/dist/stats/role-budget.js +6 -6
  101. package/dist/stats/writer.js +51 -51
  102. package/dist/utils/atomic-write.js +26 -2
  103. package/dist/utils/avatar-upload.js +282 -23
  104. package/dist/utils/cross-platform.js +436 -1
  105. package/dist/utils/error-dict.json +160 -160
  106. package/dist/utils/error-utils.js +11 -4
  107. package/dist/utils/macos-autostart.js +22 -22
  108. package/dist/utils/model-prices.jsonl +23 -20
  109. package/dist/utils/npm-ops.js +24 -1
  110. package/dist/utils/welcome.js +22 -22
  111. package/dist/utils/windows-autostart.js +1 -1
  112. package/ecagent/LICENSE +21 -21
  113. package/ecagent/NOTICE +11 -11
  114. package/ecagent/README.md +61 -61
  115. package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
  116. package/ecagent/dist/harness/compaction/compaction.js +83 -83
  117. package/ecagent/dist/harness/messages.js +7 -7
  118. package/kits/docs/GUIDE.md +20 -20
  119. package/kits/docs/INDEX.md +66 -66
  120. package/kits/docs/aun/CHEATSHEET.md +19 -19
  121. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
  122. package/kits/docs/channels/aun.md +56 -56
  123. package/kits/docs/channels/feishu.md +56 -56
  124. package/kits/docs/context-assembly.md +365 -364
  125. package/kits/docs/eck_templates/GUIDE.template.md +22 -22
  126. package/kits/docs/eck_templates/INDEX.template.md +28 -28
  127. package/kits/docs/eck_templates/path-registry.template.md +33 -33
  128. package/kits/docs/eck_templates/runtime.template.md +19 -19
  129. package/kits/docs/evolcore/INDEX.md +71 -69
  130. package/kits/docs/evolcore/agent-create.md +170 -156
  131. package/kits/docs/evolcore/agent.md +83 -73
  132. package/kits/docs/evolcore/aid.md +64 -62
  133. package/kits/docs/evolcore/codex-windows-sandbox.md +146 -0
  134. package/kits/docs/evolcore/config.md +195 -195
  135. package/kits/docs/evolcore/contact.md +68 -68
  136. package/kits/docs/evolcore/ctl.md +46 -46
  137. package/kits/docs/evolcore/event.md +216 -216
  138. package/kits/docs/evolcore/fs-architecture.md +1215 -1215
  139. package/kits/docs/evolcore/fs.md +110 -110
  140. package/kits/docs/evolcore/group-collaboration.md +243 -243
  141. package/kits/docs/evolcore/group-fs.md +17 -17
  142. package/kits/docs/evolcore/group-rules.md +251 -251
  143. package/kits/docs/evolcore/group.md +152 -152
  144. package/kits/docs/evolcore/menu-client.md +34 -34
  145. package/kits/docs/evolcore/model.md +50 -50
  146. package/kits/docs/evolcore/msg.md +165 -165
  147. package/kits/docs/evolcore/response.md +75 -75
  148. package/kits/docs/evolcore/rpc.md +37 -37
  149. package/kits/docs/evolcore/self-summary.md +29 -29
  150. package/kits/docs/evolcore/stats.md +83 -83
  151. package/kits/docs/evolcore/storage.md +50 -50
  152. package/kits/docs/evolcore/trigger.md +575 -575
  153. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
  154. package/kits/docs/identity/PATH_OPS.md +16 -16
  155. package/kits/docs/identity/ROLE_DETAIL.md +25 -25
  156. package/kits/docs/identity/identity-tools.md +26 -26
  157. package/kits/docs/path-registry.md +62 -62
  158. package/kits/docs/prompt-loading-architecture.md +270 -269
  159. package/kits/docs/venues/aun-group.md +45 -45
  160. package/kits/docs/venues/aun-private.md +10 -10
  161. package/kits/docs/venues/client-desktop.md +10 -10
  162. package/kits/docs/venues/client-mobile.md +10 -10
  163. package/kits/docs/venues/feishu-group.md +13 -13
  164. package/kits/docs/venues/feishu-private.md +9 -9
  165. package/kits/docs/venues/group.md +25 -25
  166. package/kits/docs/venues/private.md +10 -10
  167. package/kits/eck_manifest.auxiliary.json +43 -43
  168. package/kits/eck_manifest.json +195 -183
  169. package/kits/eck_message_manifest.json +74 -74
  170. package/kits/migrations/README-role-config-v4.md +32 -32
  171. package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
  172. package/kits/migrations/migrate-role-config-v4.mjs +614 -614
  173. package/kits/migrations/migrate-role-config-v5.mjs +365 -365
  174. package/kits/migrations/rename-config-file.mjs +99 -99
  175. package/kits/rules/01-overview.md +162 -162
  176. package/kits/rules/02-navigation.md +96 -96
  177. package/kits/rules/03-identity.md +39 -37
  178. package/kits/rules/04-relation.md +75 -75
  179. package/kits/rules/05-venue.md +60 -60
  180. package/kits/rules/06-channel.md +63 -63
  181. package/kits/schemas/_meta.json +53 -53
  182. package/kits/schemas/agent-config.schema.1.json +177 -177
  183. package/kits/schemas/agent-config.schema.10.json +395 -395
  184. package/kits/schemas/agent-config.schema.11.json +421 -421
  185. package/kits/schemas/agent-config.schema.12.json +427 -427
  186. package/kits/schemas/agent-config.schema.2.json +239 -239
  187. package/kits/schemas/agent-config.schema.3.json +119 -119
  188. package/kits/schemas/agent-config.schema.4.json +208 -208
  189. package/kits/schemas/agent-config.schema.5.json +326 -326
  190. package/kits/schemas/agent-config.schema.6.json +322 -322
  191. package/kits/schemas/agent-config.schema.7.json +303 -303
  192. package/kits/schemas/agent-config.schema.8.json +304 -304
  193. package/kits/schemas/agent-config.schema.9.json +364 -364
  194. package/kits/schemas/contact-book.schema.1.json +36 -36
  195. package/kits/schemas/contact-book.schema.2.json +43 -43
  196. package/kits/schemas/contact-book.schema.3.json +67 -67
  197. package/kits/schemas/daemon.schema.1.json +91 -91
  198. package/kits/schemas/daemon.schema.2.json +101 -101
  199. package/kits/schemas/daemon.schema.3.json +123 -123
  200. package/kits/schemas/daemon.schema.4.json +136 -136
  201. package/kits/schemas/daemon.schema.5.json +135 -135
  202. package/kits/schemas/daemon.schema.6.json +131 -131
  203. package/kits/schemas/defaults.schema.1.json +81 -81
  204. package/kits/schemas/defaults.schema.2.json +85 -85
  205. package/kits/schemas/defaults.schema.3.json +73 -73
  206. package/kits/schemas/defaults.schema.4.json +107 -107
  207. package/kits/schemas/defaults.schema.5.json +119 -119
  208. package/kits/schemas/menu-exec-schema-commands.md +208 -208
  209. package/kits/schemas/migrations/README.md +30 -30
  210. package/kits/schemas/relation-config.schema.1.json +158 -158
  211. package/kits/schemas/relation-config.schema.2.json +73 -73
  212. package/kits/schemas/relation-config.schema.3.json +50 -50
  213. package/kits/schemas/relation-config.schema.4.json +47 -47
  214. package/kits/schemas/relation-config.schema.5.json +47 -47
  215. package/kits/schemas/relation-config.schema.6.json +46 -46
  216. package/kits/schemas/relation-config.schema.7.json +59 -59
  217. package/kits/schemas/relation-config.schema.8.json +103 -103
  218. package/kits/schemas/role-config.schema.1.json +201 -201
  219. package/kits/schemas/role-registry.schema.1.json +35 -35
  220. package/kits/schemas/single-session.schema.1.json +31 -31
  221. package/kits/schemas/single-session.schema.2.json +57 -57
  222. package/kits/schemas/single-session.schema.3.json +32 -32
  223. package/kits/templates/bootstrap-welcome.md +17 -17
  224. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
  225. package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
  226. package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
  227. package/kits/templates/message-fragments/inject-default.md +2 -2
  228. package/kits/templates/message-fragments/item.md +2 -2
  229. package/kits/templates/roles/admin.json +96 -82
  230. package/kits/templates/roles/member.json +97 -96
  231. package/kits/templates/roles/owner.json +9 -9
  232. package/kits/templates/roles/visitor.json +86 -85
  233. package/kits/templates/system-fragments/baseagent.md +14 -14
  234. package/kits/templates/system-fragments/bootstrap.md +38 -29
  235. package/kits/templates/system-fragments/channel.md +64 -64
  236. package/kits/templates/system-fragments/commands.md +28 -28
  237. package/kits/templates/system-fragments/identity.md +13 -13
  238. package/kits/templates/system-fragments/relation.md +19 -19
  239. package/kits/templates/system-fragments/session.md +59 -59
  240. package/kits/templates/system-fragments/venue.md +31 -31
  241. package/package.json +98 -94
  242. package/AGENTS.md +0 -62
  243. package/dist/utils/codex-cli.js +0 -43
  244. package/skills/eclink/SKILL.md +0 -84
  245. package/skills/eclink/agents/openai.yaml +0 -4
@@ -18,14 +18,21 @@ import { appendRequestPath } from './request-identity.js';
18
18
  import { getManagedTaskTempDir } from '../cli/task-context.js';
19
19
  import { hasTrustedFullAccessContext } from './runner-types.js';
20
20
  const PROVIDER_ID = 'evolcore-gateway';
21
- const DEFAULT_SYSTEM_PROMPT = `You are ecagent, the coding agent built into EvolCore.
22
- Work directly in the current project. Inspect relevant files before changing them, keep edits scoped, and verify the result.
21
+ const DEFAULT_SYSTEM_PROMPT = `You are ecagent, the coding agent built into EvolCore.
22
+ Work directly in the current project. Inspect relevant files before changing them, keep edits scoped, and verify the result.
23
23
  Use the provided tools for filesystem and shell work. Never claim a tool action succeeded unless its result confirms success.`;
24
24
  const MAX_READ_BYTES = 1024 * 1024;
25
25
  const MAX_TOOL_OUTPUT = 200_000;
26
26
  const MAX_SCAN_FILES = 5_000;
27
27
  const MAX_SEARCH_RESULTS = 500;
28
28
  const DEFAULT_BASH_TIMEOUT_MS = 120_000;
29
+ /**
30
+ * 规范化模型 ID:去掉 Claude Code SDK 专有的上下文窗口后缀 [1m]/[200k]
31
+ * Ecagent/OpenAI API 不认识这些后缀,必须传入标准模型 ID
32
+ */
33
+ function stripContextWindowSuffix(modelId) {
34
+ return modelId.replace(/\[(?:1m|200k)\]$/i, '');
35
+ }
29
36
  class AsyncEventQueue {
30
37
  values = [];
31
38
  waiters = [];
@@ -461,26 +468,27 @@ function isWithin(root, candidate) {
461
468
  const relative = path.relative(root, candidate);
462
469
  return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
463
470
  }
464
- export function resolveEcagentToolPath(projectPath, candidate, forWrite = false, bootstrapAgentMdPath, fullaccess = false) {
471
+ export function resolveEcagentToolPath(projectPath, candidate, forWrite = false, bootstrapAgentMdPath, fullaccess = false, personalMdPath) {
465
472
  const root = fs.realpathSync(projectPath);
466
473
  const resolved = path.resolve(root, candidate || '.');
467
474
  const allowedBootstrapFile = bootstrapAgentMdPath
468
475
  ? path.resolve(bootstrapAgentMdPath)
469
476
  : undefined;
470
- const isBootstrapAgentMd = allowedBootstrapFile === resolved;
471
- if (!fullaccess && !isWithin(root, resolved) && !isBootstrapAgentMd) {
477
+ const allowedPersonalFile = personalMdPath ? path.resolve(personalMdPath) : undefined;
478
+ const isManagedIdentityFile = resolved === allowedBootstrapFile || resolved === allowedPersonalFile;
479
+ if (!fullaccess && !isWithin(root, resolved) && !isManagedIdentityFile) {
472
480
  throw new Error(`Path is outside the project: ${candidate}`);
473
481
  }
474
- if (isBootstrapAgentMd) {
482
+ if (isManagedIdentityFile) {
475
483
  const parent = path.dirname(resolved);
476
484
  const canonicalParent = fs.realpathSync(parent);
477
485
  if (!isWithin(parent, canonicalParent) || !isWithin(canonicalParent, parent)) {
478
- throw new Error(`Bootstrap agent.md parent resolves outside its declared path: ${candidate}`);
486
+ throw new Error(`Managed identity file parent resolves outside its declared path: ${candidate}`);
479
487
  }
480
488
  if (fs.existsSync(resolved)) {
481
489
  const canonicalFile = fs.realpathSync(resolved);
482
490
  if (!isWithin(resolved, canonicalFile) || !isWithin(canonicalFile, resolved)) {
483
- throw new Error(`Bootstrap agent.md resolves outside its declared path: ${candidate}`);
491
+ throw new Error(`Managed identity file resolves outside its declared path: ${candidate}`);
484
492
  }
485
493
  }
486
494
  return resolved;
@@ -543,7 +551,7 @@ async function walkFiles(root, signal) {
543
551
  function textResult(text, details = {}) {
544
552
  return { content: [{ type: 'text', text: truncate(text) }], details };
545
553
  }
546
- function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullaccess = false) {
554
+ function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullaccess = false, personalMdPath) {
547
555
  const read = {
548
556
  name: 'Read', label: 'Read', description: 'Read a UTF-8 text file from the project.',
549
557
  parameters: Type.Object({
@@ -552,7 +560,7 @@ function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullacc
552
560
  limit: Type.Optional(Type.Number({ minimum: 1 })),
553
561
  }),
554
562
  async execute(_id, params, signal) {
555
- const file = resolveEcagentToolPath(projectPath, params.file_path, false, bootstrapAgentMdPath, fullaccess);
563
+ const file = resolveEcagentToolPath(projectPath, params.file_path, false, bootstrapAgentMdPath, fullaccess, personalMdPath);
556
564
  const stat = await fs.promises.stat(file);
557
565
  if (stat.size > MAX_READ_BYTES)
558
566
  throw new Error(`File exceeds ${MAX_READ_BYTES} byte read limit`);
@@ -618,7 +626,7 @@ function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullacc
618
626
  name: 'Write', label: 'Write', description: 'Create or completely overwrite a project file.',
619
627
  parameters: Type.Object({ file_path: Type.String(), content: Type.String() }), executionMode: 'sequential',
620
628
  async execute(_id, params, signal) {
621
- const file = resolveEcagentToolPath(projectPath, params.file_path, true, bootstrapAgentMdPath, fullaccess);
629
+ const file = resolveEcagentToolPath(projectPath, params.file_path, true, bootstrapAgentMdPath, fullaccess, personalMdPath);
622
630
  await fs.promises.mkdir(path.dirname(file), { recursive: true });
623
631
  await fs.promises.writeFile(file, params.content, { encoding: 'utf8', signal });
624
632
  return textResult(`Wrote ${Buffer.byteLength(params.content)} bytes to ${path.relative(projectPath, file)}`, { file });
@@ -631,7 +639,7 @@ function createTools(env, projectPath, runtimeEnv, bootstrapAgentMdPath, fullacc
631
639
  replace_all: Type.Optional(Type.Boolean()),
632
640
  }), executionMode: 'sequential',
633
641
  async execute(_id, params, signal) {
634
- const file = resolveEcagentToolPath(projectPath, params.file_path, false, bootstrapAgentMdPath, fullaccess);
642
+ const file = resolveEcagentToolPath(projectPath, params.file_path, false, bootstrapAgentMdPath, fullaccess, personalMdPath);
635
643
  const value = await fs.promises.readFile(file, { encoding: 'utf8', signal });
636
644
  if (!params.old_string)
637
645
  throw new Error('old_string must not be empty');
@@ -714,7 +722,7 @@ export class EcagentRunner {
714
722
  configuredHeaders: config.headers,
715
723
  }),
716
724
  };
717
- this.model = config.model;
725
+ this.model = stripContextWindowSuffix(config.model);
718
726
  this.effort = config.effort;
719
727
  this.agentAid = runtime?.agentAid;
720
728
  this.onSessionIdUpdate = callbacks?.onSessionIdUpdate;
@@ -724,7 +732,7 @@ export class EcagentRunner {
724
732
  throw new Error('ecagent requires an EvolCore Agent AID');
725
733
  return agentSessionsDir(this.agentAid);
726
734
  }
727
- setModel(model) { this.model = model; }
735
+ setModel(model) { this.model = stripContextWindowSuffix(model); }
728
736
  getModel() { return this.model; }
729
737
  resolveModelId(model) { return model; }
730
738
  setEffort(effort) { this.effort = effort; }
@@ -851,7 +859,7 @@ export class EcagentRunner {
851
859
  models,
852
860
  model,
853
861
  thinkingLevel: normalizeThinkingLevel(effort),
854
- tools: createTools(nodeEnv, request.projectPath, request.runtimeEnv, request.modelOverride?.bootstrapAgentMdPath, fullaccess),
862
+ tools: createTools(nodeEnv, request.projectPath, request.runtimeEnv, request.modelOverride?.bootstrapAgentMdPath, fullaccess, request.modelOverride?.personalMdPath),
855
863
  systemPrompt: [DEFAULT_SYSTEM_PROMPT, request.systemPromptAppend].filter(Boolean).join('\n\n'),
856
864
  streamOptions: {
857
865
  timeoutMs: this.config.timeoutMs,
@@ -995,6 +1003,13 @@ export class EcagentRunner {
995
1003
  return runtime;
996
1004
  }
997
1005
  mapHarnessEvent(queue, event, stats, model, effort) {
1006
+ if (event.type === 'message_update'
1007
+ && (event.assistantMessageEvent.type === 'thinking_start'
1008
+ || event.assistantMessageEvent.type === 'thinking_delta'
1009
+ || event.assistantMessageEvent.type === 'thinking_end')) {
1010
+ queue.push({ type: 'heartbeat', source: 'reasoning', turn: stats.numTurns + 1 });
1011
+ return;
1012
+ }
998
1013
  if (event.type === 'message_update' && event.assistantMessageEvent.type === 'text_delta') {
999
1014
  if (!stats.firstTextAt)
1000
1015
  stats.firstTextAt = Date.now();
@@ -1202,7 +1217,7 @@ export class EcagentRunner {
1202
1217
  ? cached
1203
1218
  : await this.openExistingRuntime(agentSessionId, cwd);
1204
1219
  this.onCompactStart?.(sessionId);
1205
- const compactModel = modelOverride?.model || this.model;
1220
+ const compactModel = stripContextWindowSuffix(modelOverride?.model || this.model);
1206
1221
  const compactEffort = modelOverride?.effortMode === 'model_default'
1207
1222
  ? undefined
1208
1223
  : modelOverride?.effort ?? this.effort;
@@ -24,6 +24,7 @@ import { normalizeExecutionPermissionMode, normalizePermissionMode } from '../co
24
24
  import { workspaceContainsHClassPaths } from '../core/permission/protected-paths.js';
25
25
  import { buildBubblewrapCommand } from '../core/permission/sandbox-runtime.js';
26
26
  import { getManagedTaskTempDir } from '../cli/task-context.js';
27
+ import { auditPermissionLifecycle } from '../core/auth/authorization-audit.js';
27
28
  // Strip ANSI escape codes from Gemini CLI text output.
28
29
  // Gemini embeds raw terminal colors from tool stdout (e.g. vitest, npm)
29
30
  // into its assistant text, unlike Claude SDK which strips them internally.
@@ -32,6 +33,13 @@ const ANSI_RE = /\x1b\[[0-9;]*[a-zA-Z]|\x1b\].*?(?:\x1b\\|\x07)/g;
32
33
  function stripAnsi(text) {
33
34
  return text.replace(ANSI_RE, '');
34
35
  }
36
+ /**
37
+ * 规范化模型 ID:去掉 Claude Code SDK 专有的上下文窗口后缀 [1m]/[200k]
38
+ * Gemini API 不认识这些后缀,必须传入标准模型 ID
39
+ */
40
+ function stripContextWindowSuffix(modelId) {
41
+ return modelId.replace(/\[(?:1m|200k)\]$/i, '');
42
+ }
35
43
  // ── MIME → 扩展名映射 ──
36
44
  const MIME_EXT = {
37
45
  'image/png': '.png',
@@ -139,6 +147,7 @@ const GEMINI_EC_COMMAND_PATTERNS = [
139
147
  // quoted arguments.
140
148
  String.raw `\x00"command":"[ ]*ec[ ]+(?:[^"\x00;&|$()<>\x60\r\n\\]|\\(?:\\|"))*"\x00`,
141
149
  ];
150
+ const GEMINI_BOOTSTRAP_PERSONA_COMMAND_PATTERN = String.raw `^\{\x00"command":"[ ]*ec[ ]+agent[ ]+persona(?:[ ]+show|[ ]+--(?:text|file)[ ]+(?:[^"\x00;&|$()<>\x60\r\n\\]|\\(?:\\|"))+)[ ]*"\x00\}$`;
142
151
  export function resolveGeminiPermissionProfile(value, bootstrap = false) {
143
152
  if (value === 'fullaccess')
144
153
  return { mode: 'fullaccess', approvalMode: 'yolo' };
@@ -178,6 +187,9 @@ function geminiExactShellCommandPattern(command) {
178
187
  // is_background or additional_permissions during bootstrap.
179
188
  return `^\\{\\x00"command":"[ ]*${escapeGeminiArgsRegex(command)}[ ]*"\\x00\\}$`;
180
189
  }
190
+ function geminiManagedIdentityPathPattern(filePath) {
191
+ return geminiJsonFieldEqualsPattern(['file_path'], path.resolve(filePath));
192
+ }
181
193
  function appendGeminiPolicyRule(lines, toolName, decision, priority, argsPattern) {
182
194
  lines.push('[[rule]]');
183
195
  lines.push(`toolName = ${Array.isArray(toolName) ? JSON.stringify(toolName) : JSON.stringify(toolName)}`);
@@ -186,7 +198,7 @@ function appendGeminiPolicyRule(lines, toolName, decision, priority, argsPattern
186
198
  lines.push(`decision = "${decision}"`);
187
199
  lines.push(`priority = ${priority}`, '');
188
200
  }
189
- export function buildGeminiAdminPolicy(profile, bootstrapAgentMdPath) {
201
+ export function buildGeminiAdminPolicy(profile, bootstrapAgentMdPath, personalMdPath) {
190
202
  const lines = [];
191
203
  if (bootstrapAgentMdPath) {
192
204
  const bootstrap = normalizeGeminiBootstrapAgentMdPath(bootstrapAgentMdPath);
@@ -204,6 +216,12 @@ export function buildGeminiAdminPolicy(profile, bootstrapAgentMdPath) {
204
216
  appendGeminiPolicyRule(lines, GEMINI_BOOTSTRAP_FILE_TOOLS, 'allow', 990, writePathPattern);
205
217
  appendGeminiPolicyRule(lines, 'run_shell_command', 'allow', 990, geminiExactShellCommandPattern(`ec aid agentmd put ${bootstrap.aid}`));
206
218
  appendGeminiPolicyRule(lines, 'run_shell_command', 'allow', 990, geminiExactShellCommandPattern(`ec agent ready ${bootstrap.aid}`));
219
+ appendGeminiPolicyRule(lines, 'run_shell_command', 'allow', 990, GEMINI_BOOTSTRAP_PERSONA_COMMAND_PATTERN);
220
+ if (personalMdPath) {
221
+ const personalPathPattern = geminiManagedIdentityPathPattern(personalMdPath);
222
+ appendGeminiPolicyRule(lines, 'read_file', 'allow', 989, personalPathPattern);
223
+ appendGeminiPolicyRule(lines, GEMINI_BOOTSTRAP_FILE_TOOLS, 'allow', 989, personalPathPattern);
224
+ }
207
225
  // These are Gemini's non-I/O task bookkeeping tools. They do not expose
208
226
  // project files, shell, network, MCP, or user-interaction capabilities.
209
227
  appendGeminiPolicyRule(lines, GEMINI_BOOTSTRAP_LIFECYCLE_TOOLS, 'allow', 950);
@@ -223,6 +241,11 @@ export function buildGeminiAdminPolicy(profile, bootstrapAgentMdPath) {
223
241
  for (const pattern of GEMINI_L_CLASS_PATTERNS) {
224
242
  appendGeminiPolicyRule(lines, GEMINI_L_CLASS_WRITE_TOOLS, 'deny', 997, pattern);
225
243
  }
244
+ if (personalMdPath) {
245
+ const personalPathPattern = geminiManagedIdentityPathPattern(personalMdPath);
246
+ appendGeminiPolicyRule(lines, 'read_file', 'allow', 989, personalPathPattern);
247
+ appendGeminiPolicyRule(lines, GEMINI_BOOTSTRAP_FILE_TOOLS, 'allow', 989, personalPathPattern);
248
+ }
226
249
  if (profile.mode === 'readonly') {
227
250
  appendGeminiPolicyRule(lines, GEMINI_READONLY_TOOLS, 'allow', 950);
228
251
  appendGeminiPolicyRule(lines, '*', 'deny', 900);
@@ -286,11 +309,11 @@ export class GeminiRunner {
286
309
  permissionContexts = new Map();
287
310
  constructor(config, callbacks) {
288
311
  this.resolved = resolveGoogleConfig(config);
289
- this.model = this.resolved.model;
312
+ this.model = stripContextWindowSuffix(this.resolved.model);
290
313
  this.onSessionIdUpdate = callbacks.onSessionIdUpdate;
291
314
  }
292
315
  // ── ModelSwitcher ──
293
- setModel(model) { this.model = model; }
316
+ setModel(model) { this.model = stripContextWindowSuffix(model); }
294
317
  getModel() { return this.model; }
295
318
  async listModels() { const catalog = await getCatalog(undefined, 'gemini'); assertCatalogSelectable(catalog); return catalog.models.map(m => m.id); }
296
319
  // ── Effort (not applicable) ──
@@ -357,6 +380,9 @@ export class GeminiRunner {
357
380
  const bootstrapAgentMdPath = modelOverride?.bootstrapAgentMdPath
358
381
  ? normalizeGeminiBootstrapAgentMdPath(modelOverride.bootstrapAgentMdPath).filePath
359
382
  : undefined;
383
+ const personalMdPath = modelOverride?.personalMdPath
384
+ ? path.resolve(modelOverride.personalMdPath)
385
+ : undefined;
360
386
  const permissionProfile = resolveGeminiPermissionProfile(requestedPermissionMode, !!bootstrapAgentMdPath);
361
387
  const callModel = modelOverride?.model || this.model;
362
388
  const managedTaskTmpDir = getManagedTaskTempDir(runtimeEnv);
@@ -374,6 +400,7 @@ export class GeminiRunner {
374
400
  ...(runtimeDir ? [runtimeDir] : []),
375
401
  ...(runtimeLockDir ? [runtimeLockDir] : []),
376
402
  ...(bootstrapAgentMdPath ? [bootstrapAgentMdPath] : []),
403
+ ...(personalMdPath ? [personalMdPath] : []),
377
404
  ],
378
405
  });
379
406
  if (executionPermissionMode !== 'fullaccess' && !sandboxProbe && workspaceContainsHClassPaths(projectPath)) {
@@ -402,7 +429,7 @@ export class GeminiRunner {
402
429
  }
403
430
  else {
404
431
  const policyPath = path.join(managedTmpDir, `evolcore-gemini-permission-${crypto.randomUUID()}.toml`);
405
- fs.writeFileSync(policyPath, buildGeminiAdminPolicy(permissionProfile, bootstrapAgentMdPath), { mode: 0o600, flag: 'wx' });
432
+ fs.writeFileSync(policyPath, buildGeminiAdminPolicy(permissionProfile, bootstrapAgentMdPath, personalMdPath), { mode: 0o600, flag: 'wx' });
406
433
  tempFiles.push(policyPath);
407
434
  args.push(...buildGeminiPermissionArgs(permissionProfile, policyPath, !!sandboxProbe));
408
435
  }
@@ -444,6 +471,7 @@ export class GeminiRunner {
444
471
  ...(runtimeDir ? [runtimeDir] : []),
445
472
  ...(runtimeLockDir ? [runtimeLockDir] : []),
446
473
  ...(bootstrapAgentMdPath ? [bootstrapAgentMdPath] : []),
474
+ ...(personalMdPath ? [personalMdPath] : []),
447
475
  ],
448
476
  readonlyPaths: tempFiles,
449
477
  });
@@ -687,6 +715,56 @@ export class GeminiRunner {
687
715
  logger.info(`[GeminiRunner] interaction.auto_resolved kind=${normalizeGeminiToolName(toolName)} session=${sessionId}`);
688
716
  return;
689
717
  }
718
+ const lifecycleRequestId = requestId ?? `gemini-approval-${crypto.randomUUID()}`;
719
+ const auditSummary = `${toolName} runtime authorization denied by Gemini headless policy`;
720
+ auditPermissionLifecycle({
721
+ phase: 'request',
722
+ requestId: lifecycleRequestId,
723
+ sessionId,
724
+ toolName,
725
+ decision: 'pending',
726
+ decisionSource: 'approval',
727
+ permissionMode: context.permissionMode,
728
+ agentAid: context.selfAid,
729
+ agentName: context.agentName,
730
+ role: context.role,
731
+ taskId: context.taskId,
732
+ summary: auditSummary,
733
+ });
734
+ auditPermissionLifecycle({
735
+ phase: 'response',
736
+ requestId: lifecycleRequestId,
737
+ sessionId,
738
+ toolName,
739
+ decision: 'deny',
740
+ decisionSource: 'policy',
741
+ policyCode: 'permission_mode_denied',
742
+ reason: auditSummary,
743
+ permissionMode: context.permissionMode,
744
+ agentAid: context.selfAid,
745
+ agentName: context.agentName,
746
+ role: context.role,
747
+ taskId: context.taskId,
748
+ summary: auditSummary,
749
+ response: 'deny',
750
+ });
751
+ auditPermissionLifecycle({
752
+ phase: 'final',
753
+ requestId: lifecycleRequestId,
754
+ sessionId,
755
+ toolName,
756
+ decision: 'deny',
757
+ decisionSource: 'policy',
758
+ policyCode: 'permission_mode_denied',
759
+ reason: auditSummary,
760
+ permissionMode: context.permissionMode,
761
+ agentAid: context.selfAid,
762
+ agentName: context.agentName,
763
+ role: context.role,
764
+ taskId: context.taskId,
765
+ summary: auditSummary,
766
+ response: 'deny',
767
+ });
690
768
  try {
691
769
  await context.recordExecutionAnomaly?.({
692
770
  code: 'runtime_permission_denied',
@@ -63,7 +63,7 @@ export function usageForContext(usage) {
63
63
  return lastIteration ?? usage;
64
64
  }
65
65
  /** Models whose base ID uses a 1M context window when sent to the SDK with [1m]. */
66
- export const ONE_M_CONTEXT_MODEL_PREFIXES = ['claude-opus-4-8', 'claude-sonnet-4-6'];
66
+ export const ONE_M_CONTEXT_MODEL_PREFIXES = ['claude-opus-5', 'claude-opus-4-8', 'claude-sonnet-4-6'];
67
67
  const ONE_M_CONTEXT_MODEL_ALIASES = ['opus', 'sonnet'];
68
68
  const CLAUDE_CONTEXT_MODEL_ALIASES = ['opus', 'sonnet', 'haiku'];
69
69
  export function isClaudeContextUsageModel(model) {
@@ -4,6 +4,51 @@ import { parseDocument } from 'yaml';
4
4
  import { getAidStore, loadAid, SLOT } from './store.js';
5
5
  import { agentMdPath, aidLocalDir, resolveRoot } from '../../paths.js';
6
6
  export const DEFAULT_AGENT_DESCRIPTION = 'EvolCore AI agent.';
7
+ export const AGENT_TAG_MIN_COUNT = 3;
8
+ export const AGENT_TAG_MAX_COUNT = 6;
9
+ export const AGENT_TAG_MAX_LENGTH = 50;
10
+ const AGENT_TAG_PATTERN = /^[\p{L}\p{N}]+$/u;
11
+ /** Return a human-readable validation error for one tag, or null when valid. */
12
+ export function validateAgentTag(tag) {
13
+ if (typeof tag !== 'string' || !tag.trim())
14
+ return 'must be a non-empty string';
15
+ const normalized = tag.trim();
16
+ if (Array.from(normalized).length > AGENT_TAG_MAX_LENGTH) {
17
+ return `must be at most ${AGENT_TAG_MAX_LENGTH} characters`;
18
+ }
19
+ if (!AGENT_TAG_PATTERN.test(normalized)) {
20
+ return 'must contain only Unicode letters and numbers';
21
+ }
22
+ return null;
23
+ }
24
+ /**
25
+ * Validate an agent.md tags value. Count constraints are optional because
26
+ * existing published agent.md files may contain fewer than three tags.
27
+ */
28
+ export function validateAgentTags(tags, options = {}) {
29
+ if (!Array.isArray(tags))
30
+ return ['must be an array of strings'];
31
+ const errors = [];
32
+ const normalized = new Set();
33
+ tags.forEach((tag, index) => {
34
+ const error = validateAgentTag(tag);
35
+ if (error) {
36
+ errors.push(`tag ${index + 1} ${error}`);
37
+ return;
38
+ }
39
+ const value = tag.trim();
40
+ normalized.add(value);
41
+ });
42
+ const minCount = options.minCount;
43
+ const maxCount = options.maxCount;
44
+ if (minCount !== undefined && normalized.size < minCount) {
45
+ errors.push(`must contain at least ${minCount} unique tags`);
46
+ }
47
+ if (maxCount !== undefined && normalized.size > maxCount) {
48
+ errors.push(`must contain at most ${maxCount} unique tags`);
49
+ }
50
+ return errors;
51
+ }
7
52
  const displayNameCache = new Map();
8
53
  const displayNamePending = new Map();
9
54
  const displayNameRevision = new Map();
@@ -87,7 +132,12 @@ export function buildInitialAgentMd(opts) {
87
132
  const agentName = opts.name?.trim() || opts.aid.split('.')[0];
88
133
  const agentType = opts.type || 'ai';
89
134
  const description = opts.description?.trim() || DEFAULT_AGENT_DESCRIPTION;
90
- return `---\naid: ${yamlDoubleQuote(opts.aid)}\nname: ${yamlDoubleQuote(agentName)}\ntype: ${yamlDoubleQuote(agentType)}\nversion: "1.0.0"\ndescription: ${yamlDoubleQuote(description)}\ntags:\n - evolcore\n---\n`;
135
+ const rawTags = opts.tags?.length ? opts.tags : ['evolcore'];
136
+ const tagErrors = validateAgentTags(rawTags);
137
+ if (tagErrors.length > 0)
138
+ throw new Error(`Invalid agent.md tags: ${tagErrors.join('; ')}`);
139
+ const tags = [...new Set(rawTags.map(tag => tag.trim()))];
140
+ return `---\naid: ${yamlDoubleQuote(opts.aid)}\nname: ${yamlDoubleQuote(agentName)}\ntype: ${yamlDoubleQuote(agentType)}\nversion: "1.0.0"\ndescription: ${yamlDoubleQuote(description)}\ntags:\n${tags.map(tag => ` - ${yamlDoubleQuote(tag)}\n`).join('')}---\n`;
91
141
  }
92
142
  function ensureTrailingNewline(content) {
93
143
  return content.endsWith('\n') ? content : `${content}\n`;
@@ -145,9 +195,11 @@ export function validateAgentMdFrontmatter(content, expectedAid) {
145
195
  if (description && description.length > 100) {
146
196
  errors.push('agent.md frontmatter description must be at most 100 characters');
147
197
  }
148
- if (values.tags !== undefined && (!Array.isArray(values.tags)
149
- || values.tags.some(tag => typeof tag !== 'string' || !tag.trim()))) {
150
- errors.push('agent.md frontmatter tags must be an array of non-empty strings');
198
+ if (values.tags !== undefined) {
199
+ const tagErrors = validateAgentTags(values.tags);
200
+ if (tagErrors.length > 0) {
201
+ errors.push(`agent.md frontmatter tags ${tagErrors.join('; ')}`);
202
+ }
151
203
  }
152
204
  return { valid: errors.length === 0, errors };
153
205
  }
@@ -1,3 +1,4 @@
1
+ export { AGENT_TAG_MIN_COUNT, AGENT_TAG_MAX_COUNT, AGENT_TAG_MAX_LENGTH, validateAgentTag, validateAgentTags, } from './agentmd.js';
1
2
  export { isValidAid, aidList, aidListVerified, aidCreate, aidShow, aidDelete, aidLookup, verifySignAbility, probePkiRecoverability, appendAidLifecycle, readAidLifecycle } from './identity.js';
2
3
  export { DEFAULT_AGENT_DESCRIPTION, buildInitialAgentMd, validateAgentMdFrontmatter, agentmdGet, agentmdPut, agentmdSync, invalidateAgentDisplayName, parseAgentDisplayName, refreshAgentDisplayName, resolveAgentDisplayName, stripAgentMdSignature, updateAgentMdFrontmatterAvatar, updateAgentMdFrontmatterName, } from './agentmd.js';
3
4
  export { MIN_AUN_CORE_SDK, AUN_CORE_SDK_PKG, isAunSdkVersionOk, resolveAunCoreSdkPkg, ensureAunSdk, isAunSdkReady, downloadCaRoot, suppressSdkLogs, } from './client.js';
@@ -2,6 +2,7 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { resolveRoot } from '../../paths.js';
4
4
  import { agentmdGet, agentmdPut } from './agentmd.js';
5
+ import { uploadAvatarBuffer, uploadAvatarUrl } from '../../utils/avatar-upload.js';
5
6
  import { aidLookup, aidShow } from './identity.js';
6
7
  import { isValidAid } from './validation.js';
7
8
  export class ManagedAidOperationError extends Error {
@@ -28,13 +29,42 @@ function parseAid(value) {
28
29
  return { ok: false, code: 'INVALID_AID', reason: `invalid AID: ${aid}` };
29
30
  return { ok: true, aid };
30
31
  }
32
+ function parseAvatar(args) {
33
+ const aidResult = parseAid(args[1]);
34
+ if (!aidResult.ok)
35
+ return aidResult;
36
+ const sourceFlag = args[2];
37
+ const sourceValue = args[3];
38
+ if (sourceFlag !== '--file' && sourceFlag !== '--url') {
39
+ return { ok: false, code: 'INVALID_ARGUMENT', reason: 'avatar requires exactly one of --file <path> or --url <https-url>' };
40
+ }
41
+ if (!sourceValue || sourceValue.startsWith('-')) {
42
+ return { ok: false, code: 'INVALID_ARGUMENT', reason: `${sourceFlag} requires a non-empty value` };
43
+ }
44
+ if (args.length !== 4 && !(args.length === 6 && args[4] === '--format' && args[5] === 'json')) {
45
+ return { ok: false, code: 'NOT_ALLOWED', reason: 'avatar only supports an optional --format json suffix' };
46
+ }
47
+ return {
48
+ ok: true,
49
+ command: {
50
+ kind: 'avatar',
51
+ aid: aidResult.aid,
52
+ ...(sourceFlag === '--file' ? { filePath: sourceValue } : { url: sourceValue }),
53
+ operationId: 'ec.aid.avatar',
54
+ canonicalArgv: ['aid', 'avatar', aidResult.aid, sourceFlag, sourceValue],
55
+ },
56
+ };
57
+ }
31
58
  /**
32
- * The daemon accepts only the self-scoped AID commands exposed to managed
33
- * tasks. Parsing here is deliberately narrower than the standalone CLI.
59
+ * The daemon accepts only the narrowly scoped AID commands exposed to managed
60
+ * tasks. Public lookup/get may name any valid AID; local show/publish remain
61
+ * subject to the self-AID boundary enforced by the command handler.
34
62
  */
35
63
  export function resolveManagedAidOperation(argv) {
36
64
  const args = argv[0] === 'aid' ? argv.slice(1) : argv;
37
65
  const subcommand = args[0];
66
+ if (subcommand === 'avatar')
67
+ return parseAvatar(args);
38
68
  if ((subcommand === 'show' || subcommand === 'lookup') && hasOnlyOptionalJsonFormat(args, 1)) {
39
69
  const parsedAid = parseAid(args[1]);
40
70
  if (!parsedAid.ok)
@@ -67,7 +97,21 @@ export function resolveManagedAidOperation(argv) {
67
97
  return {
68
98
  ok: false,
69
99
  code: 'NOT_ALLOWED',
70
- reason: 'Managed AID operations only allow exact self-scoped show, lookup, agentmd get, or agentmd put commands with an optional --format json suffix',
100
+ reason: 'Managed AID operations only allow exact show, lookup, agentmd get, agentmd put, or avatar commands with supported options',
101
+ };
102
+ }
103
+ export function managedAidShowViewForRole(role) {
104
+ return role === 'owner' || role === 'admin' ? 'full' : 'redacted';
105
+ }
106
+ export function redactAidShowResult(value) {
107
+ return {
108
+ aid: value.aid,
109
+ redacted: true,
110
+ hasAgentMd: value.hasAgentMd,
111
+ certExpiresAt: value.certExpiresAt,
112
+ certSubject: value.certSubject,
113
+ certExpired: value.certExpired,
114
+ agentMdSignature: value.agentMdSignature,
71
115
  };
72
116
  }
73
117
  const defaultDependencies = () => ({
@@ -78,14 +122,23 @@ const defaultDependencies = () => ({
78
122
  agentmdPut,
79
123
  existsSync: fs.existsSync,
80
124
  readFileSync: fs.readFileSync,
125
+ uploadAvatarBuffer,
126
+ uploadAvatarUrl,
81
127
  });
82
128
  /** Execute a validated AID operation from the daemon-owned host filesystem. */
83
- export async function executeManagedAidOperation(command, overrides = {}) {
129
+ export async function executeManagedAidOperation(command, options = {}) {
130
+ const { showView = 'full', ...overrides } = options;
84
131
  const deps = { ...defaultDependencies(), ...overrides };
85
132
  const aunPath = deps.root;
86
133
  switch (command.kind) {
87
- case 'show':
88
- return { kind: command.kind, aid: command.aid, value: await deps.aidShow(command.aid, { aunPath }) };
134
+ case 'show': {
135
+ const value = await deps.aidShow(command.aid, { aunPath });
136
+ return {
137
+ kind: command.kind,
138
+ aid: command.aid,
139
+ value: showView === 'redacted' ? redactAidShowResult(value) : value,
140
+ };
141
+ }
89
142
  case 'lookup':
90
143
  return { kind: command.kind, aid: command.aid, value: await deps.aidLookup(command.aid) };
91
144
  case 'agentmd.get':
@@ -103,5 +156,26 @@ export async function executeManagedAidOperation(command, overrides = {}) {
103
156
  await deps.agentmdPut(content, { aid: command.aid, aunPath });
104
157
  return { kind: command.kind, aid: command.aid, path: localPath };
105
158
  }
159
+ case 'avatar': {
160
+ const result = command.url
161
+ ? await deps.uploadAvatarUrl(command.aid, command.url, { aunPath })
162
+ : await deps.uploadAvatarBuffer(command.aid, options.avatarBuffer ?? deps.readFileSync(command.filePath), { aunPath });
163
+ if (!result.ok) {
164
+ throw new ManagedAidOperationError(result.code ?? 'UPLOAD_FAILED', result.error ?? 'avatar upload failed');
165
+ }
166
+ return {
167
+ kind: 'avatar',
168
+ aid: command.aid,
169
+ avatar: result.publicUrl,
170
+ objectKey: result.objectKey,
171
+ sha256: result.sha256,
172
+ mime: result.mime,
173
+ width: result.width,
174
+ height: result.height,
175
+ aspectRatio: result.aspectRatio,
176
+ };
177
+ }
178
+ default:
179
+ throw new ManagedAidOperationError('NOT_ALLOWED', `Unsupported managed AID operation: ${command.kind}`);
106
180
  }
107
181
  }
@@ -7,6 +7,7 @@ import { AUN_KEYSTORE_ENCRYPTION_SEED } from './encryption-seed-policy.js';
7
7
  // pages so a large backlog is drained without monopolizing the sync loop.
8
8
  const FULL_GROUP_PULL_MAX_PAGES = 10;
9
9
  const GROUP_PULL_PAGINATION_PATCH = Symbol('evolcore.groupPullPaginationPatch');
10
+ const RELIABLE_MESSAGE_DELIVERY_PATCH = Symbol('evolcore.reliableMessageDeliveryPatch');
10
11
  /**
11
12
  * Slot 命名约定(基于 fastaun 0.4.3 隔离键语义)。
12
13
  *
@@ -106,6 +107,46 @@ export function enableFullGroupPullPagination(client) {
106
107
  target._pullGroupV2 = wrappedPull;
107
108
  return true;
108
109
  }
110
+ /**
111
+ * Make SDK pull delivery wait until EvolCore's application handlers finish.
112
+ *
113
+ * fastaun 0.5.13 publishes message events with dispatcher.enqueue(), then
114
+ * advances its sequence tracker immediately. That is a transport watermark,
115
+ * so a daemon restart can acknowledge a message before the application has
116
+ * durably claimed it. The SDK keeps _publishAppEvent private, but the public
117
+ * client is intentionally adapted here in one place: preserve the SDK's
118
+ * normal publication path and wait for its dispatcher queue for pulled
119
+ * message events before the pull operation commits the sequence.
120
+ */
121
+ export function enableReliableMessageDelivery(client) {
122
+ const target = client;
123
+ if (target[RELIABLE_MESSAGE_DELIVERY_PATCH])
124
+ return false;
125
+ const publish = target._publishAppEvent;
126
+ const dispatcher = target._dispatcher;
127
+ if (typeof publish !== 'function' || !dispatcher || typeof dispatcher.flush !== 'function')
128
+ return false;
129
+ const wrappedPublish = async function (event, payload, source, ...rest) {
130
+ const result = await publish.call(this, event, payload, source, ...rest);
131
+ // Pull/recovery paths must not advance SeqTracker until the application
132
+ // handlers have completed. Push delivery has no pull commit to gate and
133
+ // remains asynchronous as intended by the SDK.
134
+ const reliable = (event === 'message.received' || event === 'group.message_created')
135
+ && source !== 'push'
136
+ && source !== 'inline-push';
137
+ if (reliable)
138
+ await dispatcher.flush();
139
+ return result;
140
+ };
141
+ Object.defineProperty(target, RELIABLE_MESSAGE_DELIVERY_PATCH, {
142
+ configurable: false,
143
+ enumerable: false,
144
+ value: true,
145
+ writable: false,
146
+ });
147
+ target._publishAppEvent = wrappedPublish;
148
+ return true;
149
+ }
109
150
  /** 加载本地 AID 值对象(用于离线签名/验签,无需连接)。load 失败抛 AidLoadError。 */
110
151
  export function loadAid(store, aid) {
111
152
  const r = store.load(aid);
@@ -797,7 +797,12 @@ export function resolveManagedFsOperation(argv) {
797
797
  const destination = remoteOrError(positionals[1]);
798
798
  if (!('groupId' in destination))
799
799
  return destination;
800
- const source = positionals[0].includes(':') ? remoteOrError(positionals[0]) : undefined;
800
+ // Windows absolute paths contain a drive-letter colon, but are local
801
+ // upload sources rather than managed group references.
802
+ const isWindowsAbsolutePath = /^[A-Za-z]:[\\/]/.test(positionals[0]);
803
+ const source = positionals[0].includes(':') && !isWindowsAbsolutePath
804
+ ? remoteOrError(positionals[0])
805
+ : undefined;
801
806
  if (source && !('groupId' in source))
802
807
  return source;
803
808
  if (source && source.groupId !== destination.groupId)