evolcore 0.0.35 → 0.0.37

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 (224) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +6 -0
  3. package/bin/install-codex-managed-hooks.mjs +20 -3
  4. package/dist/agents/baseagent-availability.js +1 -1
  5. package/dist/agents/baseagent.js +88 -0
  6. package/dist/agents/claude-runner.js +59 -12
  7. package/dist/agents/codex/app-server-client.js +18 -1
  8. package/dist/agents/codex-runner.js +262 -21
  9. package/dist/agents/ecagent-runner.js +47 -11
  10. package/dist/agents/gemini-runner.js +36 -12
  11. package/dist/aun/aid/client.js +4 -3
  12. package/dist/aun/aid/control-aid.js +270 -8
  13. package/dist/aun/aid/identity.js +6 -3
  14. package/dist/aun/aid/ownership-operation.js +58 -0
  15. package/dist/aun/aid/store.js +1 -0
  16. package/dist/aun/group/fs-download.js +67 -2
  17. package/dist/aun/group/history.js +20 -5
  18. package/dist/aun/group/operations.js +8 -2
  19. package/dist/aun/menu/client.js +39 -2
  20. package/dist/aun/menu/transport.js +1 -1
  21. package/dist/aun/msg/history.js +43 -4
  22. package/dist/aun/msg/managed-operation.js +30 -53
  23. package/dist/aun/msg/p2p.js +51 -15
  24. package/dist/aun/outbox.js +4 -0
  25. package/dist/channels/aun.js +260 -73
  26. package/dist/channels/daemon.js +6 -4
  27. package/dist/channels/feishu.js +3 -0
  28. package/dist/cli/agent-command.js +161 -223
  29. package/dist/cli/agent-create.js +190 -223
  30. package/dist/cli/agent.js +3 -1
  31. package/dist/cli/aid-bind-command.js +53 -0
  32. package/dist/cli/aun-commands.js +384 -91
  33. package/dist/cli/bench.js +1 -1
  34. package/dist/cli/cli-argv.js +74 -0
  35. package/dist/cli/daemon-commands.js +105 -74
  36. package/dist/cli/diag.js +1 -1
  37. package/dist/cli/fs-command.js +18 -19
  38. package/dist/cli/getmeta-command.js +159 -6
  39. package/dist/cli/index.js +6 -12
  40. package/dist/cli/init-channel.js +8 -55
  41. package/dist/cli/init.js +125 -218
  42. package/dist/cli/queue-command.js +11 -8
  43. package/dist/cli/restart-monitor.js +177 -85
  44. package/dist/cli/session-command.js +1 -1
  45. package/dist/cli/stats.js +7 -5
  46. package/dist/cli/thread-command.js +103 -30
  47. package/dist/cli/trigger-command.js +1 -1
  48. package/dist/cli/watch-msg-diag.js +1 -0
  49. package/dist/config/access-policy.js +2 -2
  50. package/dist/config/boot-log.js +14 -11
  51. package/dist/config/config-field-policy.js +9 -1
  52. package/dist/config/config-manager.js +176 -90
  53. package/dist/config/config-operation-service.js +7 -14
  54. package/dist/config/contact-book-store.js +142 -26
  55. package/dist/config/contact-book.js +4 -2
  56. package/dist/config/lifecycle.js +1 -1
  57. package/dist/config/owner-config.js +96 -0
  58. package/dist/config/peer-role-resolver.js +12 -18
  59. package/dist/config/providers-config.js +382 -0
  60. package/dist/config/providers-import.js +74 -0
  61. package/dist/config/providers-management.js +328 -0
  62. package/dist/config/providers.js +22 -0
  63. package/dist/config/resolved-config-op.js +17 -14
  64. package/dist/config/snapshot.js +28 -16
  65. package/dist/config-store.js +18 -14
  66. package/dist/core/agent-ipc-registration.js +21 -63
  67. package/dist/core/agent-runtime.js +16 -5
  68. package/dist/core/auth/agent-delegation.js +199 -25
  69. package/dist/core/auth/auth-gateway.js +44 -2
  70. package/dist/core/auth/authorization-denial.js +1 -1
  71. package/dist/core/auth/debug-context.js +2 -1
  72. package/dist/core/auth/delegation-authorization.js +2 -2
  73. package/dist/core/auth/delegation-diagnostics.js +4 -1
  74. package/dist/core/auth/operation-authorizer.js +10 -1
  75. package/dist/core/auth/operation-catalog.js +19 -1
  76. package/dist/core/bootstrap-service.js +43 -20
  77. package/dist/core/channel-loader.js +0 -1
  78. package/dist/core/channel-runtime.js +30 -4
  79. package/dist/core/command/agent-control.js +196 -37
  80. package/dist/core/command/cli-intent-parser.js +60 -4
  81. package/dist/core/command/command-handler.js +212 -60
  82. package/dist/core/command/connect-menu.js +1 -0
  83. package/dist/core/command/diag-service.js +5 -4
  84. package/dist/core/command/file-search.js +368 -0
  85. package/dist/core/command/menu-catalog.js +2 -1
  86. package/dist/core/command/menu-handler.js +292 -107
  87. package/dist/core/command/menu-protocol.js +23 -0
  88. package/dist/core/command/role-menu.js +7 -19
  89. package/dist/core/command/skill-slash.js +130 -25
  90. package/dist/core/command/slash-handler.js +101 -53
  91. package/dist/core/command/topic-slash.js +16 -5
  92. package/dist/core/daemon-bootstrap.js +7 -1
  93. package/dist/core/daemon-control.js +19 -1
  94. package/dist/core/daemon-lifecycle.js +2 -1
  95. package/dist/core/event-catalog.js +2 -0
  96. package/dist/core/evolagent-registry.js +13 -2
  97. package/dist/core/evolagent.js +3 -6
  98. package/dist/core/handoff/ipc-registration.js +2 -2
  99. package/dist/core/handoff/runtime.js +40 -6
  100. package/dist/core/handoff/store.js +1 -0
  101. package/dist/core/inference/text-inference.js +95 -6
  102. package/dist/core/ipc-registration.js +43 -24
  103. package/dist/core/memory/consolidation-context.js +4 -1
  104. package/dist/core/memory/consolidation.js +25 -14
  105. package/dist/core/message/aun-ipc-sender.js +296 -88
  106. package/dist/core/message/aun-request-registry.js +226 -0
  107. package/dist/core/message/create-status.js +33 -4
  108. package/dist/core/message/message-bridge.js +62 -13
  109. package/dist/core/message/message-log.js +0 -1
  110. package/dist/core/message/message-queue.js +4 -1
  111. package/dist/core/message/message-utils.js +8 -1
  112. package/dist/core/message/peer-mode.js +17 -9
  113. package/dist/core/message/response-engine.js +266 -124
  114. package/dist/core/message/retry-scheduler.js +11 -8
  115. package/dist/core/model/catalog-service.js +60 -18
  116. package/dist/core/model/model-catalog.js +112 -9
  117. package/dist/core/model/model-fallback.js +94 -26
  118. package/dist/core/ownership/owner-resolver.js +687 -0
  119. package/dist/core/permission/approval-gateway.js +196 -62
  120. package/dist/core/permission/ec-command-parser.js +138 -25
  121. package/dist/core/permission/filesystem-approval-scope.js +141 -0
  122. package/dist/core/permission/protected-paths.js +14 -1
  123. package/dist/core/permission/sandbox-runtime.js +171 -18
  124. package/dist/core/permission/tool-policy.js +13 -2
  125. package/dist/core/role/runtime-policy.js +3 -4
  126. package/dist/core/session/session-idle-monitor.js +10 -8
  127. package/dist/core/session/session-manager.js +72 -45
  128. package/dist/core/session/session-mapper.js +18 -7
  129. package/dist/core/session/session-renew.js +5 -4
  130. package/dist/core/session/session-title.js +2 -1
  131. package/dist/core/session/session-turn-coordinator.js +4 -2
  132. package/dist/core/skill/index-generator.js +2 -16
  133. package/dist/core/task/task-recovery-service.js +15 -2
  134. package/dist/core/thread/command-parser.js +28 -13
  135. package/dist/core/thread/index.js +1 -0
  136. package/dist/core/thread/ipc-operation.js +11 -8
  137. package/dist/core/thread/thread-operation.js +79 -44
  138. package/dist/core/thread/thread-route.js +54 -6
  139. package/dist/core/thread/thread-service.js +236 -45
  140. package/dist/core/thread/topic-id.js +248 -0
  141. package/dist/eck/kit-renderer.js +1 -1
  142. package/dist/eck/message-renderer.js +12 -2
  143. package/dist/index.js +50 -27
  144. package/dist/ipc.js +59 -6
  145. package/dist/paths.js +1 -0
  146. package/dist/product.js +1 -0
  147. package/dist/response-system/engines/v1/proactive-flow.js +10 -3
  148. package/dist/stats/db.js +8 -0
  149. package/dist/stats/writer.js +26 -4
  150. package/dist/trigger/authorization.js +48 -3
  151. package/dist/trigger/feedback.js +28 -10
  152. package/dist/trigger/ipc-service.js +86 -10
  153. package/dist/trigger/local-files.js +55 -23
  154. package/dist/trigger/scheduler.js +57 -11
  155. package/dist/trigger/system-definitions.js +1 -0
  156. package/dist/trigger/validation.js +44 -15
  157. package/dist/utils/aid-bind.js +17 -297
  158. package/dist/utils/avatar-upload.js +16 -4
  159. package/dist/utils/cross-platform.js +115 -36
  160. package/dist/utils/ecweb-supervisor.js +1 -1
  161. package/dist/utils/ecweb-utils.js +8 -1
  162. package/dist/utils/error-utils.js +44 -6
  163. package/dist/utils/instance-registry.js +12 -2
  164. package/dist/utils/npm-ops.js +102 -27
  165. package/dist/utils/package-version-info.js +72 -27
  166. package/dist/utils/restart-safety.js +50 -0
  167. package/dist/utils/upgrade-jobs.js +304 -0
  168. package/dist/utils/windows-autostart.js +17 -10
  169. package/kits/docs/INDEX.md +5 -0
  170. package/kits/docs/aun/ABILITY_CALLING.md +239 -0
  171. package/kits/docs/evolcore/INDEX.md +14 -3
  172. package/kits/docs/evolcore/agent-create.md +55 -141
  173. package/kits/docs/evolcore/agent.md +38 -16
  174. package/kits/docs/evolcore/aid.md +34 -0
  175. package/kits/docs/evolcore/config.md +20 -1
  176. package/kits/docs/evolcore/event.md +2 -2
  177. package/kits/docs/evolcore/fs.md +18 -2
  178. package/kits/docs/evolcore/group-collaboration.md +13 -246
  179. package/kits/docs/evolcore/group.md +2 -1
  180. package/kits/docs/evolcore/memory-consolidation-batches.md +1 -1
  181. package/kits/docs/evolcore/menu-protocol.md +65 -1
  182. package/kits/docs/evolcore/msg.md +64 -2
  183. package/kits/docs/evolcore/skill.md +86 -0
  184. package/kits/docs/evolcore/stats.md +6 -0
  185. package/kits/docs/evolcore/task.md +2 -0
  186. package/kits/docs/evolcore/thread-chain.md +145 -51
  187. package/kits/docs/evolcore/trigger.md +48 -3
  188. package/kits/eck_manifest.json +21 -0
  189. package/kits/eck_message_manifest.json +4 -4
  190. package/kits/migrations/migrate-role-config-v5.mjs +20 -7
  191. package/kits/rules/01-overview.md +2 -1
  192. package/kits/rules/03-identity.md +3 -1
  193. package/kits/rules/04-relation.md +1 -1
  194. package/kits/rules/07-memory-and-thread.md +1 -1
  195. package/kits/schemas/_meta.json +16 -1
  196. package/kits/schemas/agent-config.schema.13.json +44 -2
  197. package/kits/schemas/contact-book.schema.3.json +5 -0
  198. package/kits/schemas/daemon.schema.6.json +3 -1
  199. package/kits/schemas/daemon.schema.8.json +329 -0
  200. package/kits/schemas/defaults.schema.5.json +39 -3
  201. package/kits/schemas/providers.schema.1.json +76 -0
  202. package/kits/schemas/relation-config.schema.9.json +5 -1
  203. package/kits/skills/.switches.json +28 -0
  204. package/kits/skills/aid-ability/SKILL.md +214 -0
  205. package/kits/skills/ec-agent-create/SKILL.md +162 -0
  206. package/kits/skills/ec-group/SKILL.md +2 -1
  207. package/kits/skills/ec-group-collaboration/SKILL.md +228 -0
  208. package/kits/skills/ec-identity/SKILL.md +143 -0
  209. package/kits/skills/ec-skill/scripts/cli.mjs +36 -3
  210. package/kits/skills/ec-skill/scripts/skill.mjs +202 -15
  211. package/kits/skills/ec-trigger/SKILL.md +167 -0
  212. package/kits/skills/ec-trigger/references/examples.md +153 -0
  213. package/kits/skills/thread-ops/SKILL.md +108 -420
  214. package/kits/templates/message-fragments/thread-context.md +1 -2
  215. package/kits/templates/message-fragments/trigger-thread-check.md +1 -1
  216. package/kits/templates/roles/member.json +1 -1
  217. package/kits/templates/roles/visitor.json +1 -0
  218. package/kits/templates/system-fragments/bootstrap.md +1 -0
  219. package/kits/templates/system-fragments/channel.md +2 -1
  220. package/kits/templates/system-fragments/commands.md +5 -4
  221. package/kits/templates/system-fragments/identity.md +2 -0
  222. package/kits/templates/system-fragments/session.md +1 -1
  223. package/kits/templates/system-fragments/venue.md +1 -1
  224. package/package.json +4 -3
@@ -2,6 +2,7 @@ import { getRunnerCatalog, catalogEfforts, assertCatalogSelectable, } from '../c
2
2
  // Ecagent/OpenAI API 不认识 Claude Code SDK 的窗口后缀,必须传入基础模型 ID。
3
3
  import { stripContextWindowSuffix } from '../core/model/context-window-suffix.js';
4
4
  import fs from 'fs';
5
+ import { assertOwnershipAuthorization } from '../core/ownership/owner-resolver.js';
5
6
  import path from 'path';
6
7
  import { AgentHarness, JsonlSessionRepo, NodeExecutionEnv, } from '../../ecagent/dist/node.js';
7
8
  import { Type, createModels, createProvider, } from '../../ecagent/dist/runtime/index.js';
@@ -16,6 +17,7 @@ import { ErrorType } from '../utils/error-utils.js';
16
17
  import { summarizeToolInputForAudit } from '../utils/tool-summary.js';
17
18
  import { auditToolPreflightDenial } from '../core/auth/authorization-audit.js';
18
19
  import { resolveEcagentConfig } from './baseagent.js';
20
+ import { resolveProviderServiceForBaseagent } from '../config/providers.js';
19
21
  import { buildModelRequestHeaders } from './request-identity.js';
20
22
  import { appendRequestPath } from './request-identity.js';
21
23
  import { resolveModelSpec } from '../stats/billing.js';
@@ -790,6 +792,32 @@ export class EcagentRunner {
790
792
  setSendPrompt(fn) { this.sendPromptFn = fn; }
791
793
  setPermissionContext(sessionId, context) { this.permissionContexts.set(sessionId, context); }
792
794
  setCompactStartCallback(callback) { this.onCompactStart = callback; }
795
+ currentProviderConfig(providerOverride) {
796
+ const providerId = providerOverride ?? this.config.providerId;
797
+ if (!providerId)
798
+ return this.config;
799
+ const provider = resolveProviderServiceForBaseagent(providerId, 'ecagent', this.config.providerServiceType);
800
+ if (provider.style !== 'openai-compatible') {
801
+ throw new Error(`Provider ${providerId} uses unsupported ecagent style: ${provider.style}`);
802
+ }
803
+ if (!provider.apiKey)
804
+ throw new Error(`Provider ${providerId} has no API key`);
805
+ return {
806
+ ...this.config,
807
+ apiKey: provider.apiKey,
808
+ baseUrl: provider.baseUrl,
809
+ headers: buildModelRequestHeaders({
810
+ baseagent: 'ecagent',
811
+ baseUrl: provider.baseUrl,
812
+ agentAid: this.agentAid,
813
+ configuredHeaders: provider.headers,
814
+ }),
815
+ queryParams: provider.queryParams,
816
+ providerServiceType: provider.serviceType,
817
+ providerStyle: provider.style,
818
+ modelOverrides: provider.modelOverrides,
819
+ };
820
+ }
793
821
  listModes() {
794
822
  return [
795
823
  { key: 'readonly', nameZh: '只读', description: '只读操作自动执行,写入直接拒绝', available: true },
@@ -891,6 +919,7 @@ export class EcagentRunner {
891
919
  };
892
920
  let completeSent = false;
893
921
  try {
922
+ const connection = this.currentProviderConfig(request.modelOverride?.provider);
894
923
  await fs.promises.mkdir(request.projectPath, { recursive: true });
895
924
  const previousRuntime = this.runtimes.get(request.sessionId);
896
925
  const runtime = await this.getOrCreateRuntime(request.sessionId, request.projectPath, request.initialAgentSessionId, request.modelOverride);
@@ -908,19 +937,19 @@ export class EcagentRunner {
908
937
  if (fullaccess && !hasTrustedFullAccessContext(this.permissionContexts.get(request.sessionId))) {
909
938
  throw new Error('Ecagent fullaccess requires a trusted per-call execution authorization');
910
939
  }
911
- const model = createModel(modelId, this.config.baseUrl);
940
+ const model = createModel(modelId, connection.baseUrl);
912
941
  const nodeEnv = new NodeExecutionEnv({ cwd: request.projectPath, shellEnv: { ...process.env, ...request.runtimeEnv, EVOLCORE_SESSION_ID: request.sessionId, EVOLCORE_HOME: resolveRoot() } });
913
942
  const models = createModels();
914
- const transport = createOpenAiTransport(this.config);
943
+ const transport = createOpenAiTransport(connection);
915
944
  models.setProvider(createProvider({
916
945
  id: PROVIDER_ID,
917
946
  name: 'EvolCore Gateway',
918
- baseUrl: this.config.baseUrl,
947
+ baseUrl: connection.baseUrl,
919
948
  models: [model],
920
949
  auth: { apiKey: {
921
950
  name: 'EvolCore gateway',
922
- login: async () => ({ type: 'api_key', key: this.config.apiKey }),
923
- resolve: async () => ({ auth: { apiKey: this.config.apiKey }, source: 'baseagents.ecagent.apiKey' }),
951
+ login: async () => ({ type: 'api_key', key: connection.apiKey }),
952
+ resolve: async () => ({ auth: { apiKey: connection.apiKey }, source: 'provider' }),
924
953
  } },
925
954
  api: transport,
926
955
  }));
@@ -933,9 +962,9 @@ export class EcagentRunner {
933
962
  tools: createTools(nodeEnv, request.projectPath, request.runtimeEnv, request.modelOverride?.bootstrapAgentMdPath, fullaccess, request.modelOverride?.personalMdPath, this.permissionContexts.get(request.sessionId)?.systemSessionGrants),
934
963
  systemPrompt: [DEFAULT_SYSTEM_PROMPT, request.systemPromptAppend].filter(Boolean).join('\n\n'),
935
964
  streamOptions: {
936
- timeoutMs: this.config.timeoutMs,
937
- maxRetries: this.config.maxRetries,
938
- maxRetryDelayMs: this.config.maxRetryDelayMs,
965
+ timeoutMs: connection.timeoutMs,
966
+ maxRetries: connection.maxRetries,
967
+ maxRetryDelayMs: connection.maxRetryDelayMs,
939
968
  },
940
969
  });
941
970
  runtime.harness = harness;
@@ -1214,6 +1243,12 @@ export class EcagentRunner {
1214
1243
  }
1215
1244
  async authorizeTool(sessionId, projectPath, mode, toolName, input, turn) {
1216
1245
  const permissionContext = this.permissionContexts.get(sessionId);
1246
+ try {
1247
+ await assertOwnershipAuthorization(permissionContext);
1248
+ }
1249
+ catch (error) {
1250
+ return { block: true, reason: error instanceof Error ? error.message : String(error) };
1251
+ }
1217
1252
  const recordBlockedOperation = async (policyCode, operationInput = input, matchedPath) => {
1218
1253
  const summary = summarizeToolInputForAudit(toolName, operationInput).slice(0, 512);
1219
1254
  auditToolPreflightDenial({
@@ -1418,12 +1453,13 @@ export class EcagentRunner {
1418
1453
  const compactEffort = modelOverride?.effortMode === 'model_default'
1419
1454
  ? undefined
1420
1455
  : modelOverride?.effort ?? this.effort;
1421
- const model = createModel(compactModel, this.config.baseUrl);
1456
+ const connection = this.currentProviderConfig();
1457
+ const model = createModel(compactModel, connection.baseUrl);
1422
1458
  const env = new NodeExecutionEnv({ cwd: runtime.projectPath });
1423
1459
  const models = createModels();
1424
- const transport = createOpenAiTransport(this.config);
1460
+ const transport = createOpenAiTransport(connection);
1425
1461
  models.setProvider(createProvider({
1426
- id: PROVIDER_ID, auth: { apiKey: { name: 'EvolCore gateway', login: async () => ({ type: 'api_key', key: this.config.apiKey }), resolve: async () => ({ auth: { apiKey: this.config.apiKey } }) } },
1462
+ id: PROVIDER_ID, auth: { apiKey: { name: 'EvolCore gateway', login: async () => ({ type: 'api_key', key: connection.apiKey }), resolve: async () => ({ auth: { apiKey: connection.apiKey } }) } },
1427
1463
  models: [model], api: transport,
1428
1464
  }));
1429
1465
  const harness = new AgentHarness({ env, session: runtime.session, models, model, thinkingLevel: normalizeThinkingLevel(compactEffort), tools: [] });
@@ -1,4 +1,4 @@
1
- import { getCatalog, assertCatalogSelectable } from '../core/model/model-catalog.js';
1
+ import { getRunnerCatalog, assertCatalogSelectable } from '../core/model/model-catalog.js';
2
2
  // Gemini API 不认识 Claude Code SDK 的窗口后缀,必须传入基础模型 ID。
3
3
  import { stripContextWindowSuffix } from '../core/model/context-window-suffix.js';
4
4
  /**
@@ -20,6 +20,7 @@ import os from 'os';
20
20
  import crypto from 'crypto';
21
21
  import { hasTrustedFullAccessContext } from './runner-types.js';
22
22
  import { resolveGoogleConfig } from './baseagent.js';
23
+ import { resolveEffective } from '../config/config-manager.js';
23
24
  import { commandExists } from '../utils/cross-platform.js';
24
25
  import { GeminiSessionFileAdapter } from '../core/session/adapters/gemini-session-file-adapter.js';
25
26
  import { logger } from '../utils/logger.js';
@@ -103,6 +104,7 @@ function isGeminiPermissionDenial(event) {
103
104
  return /(?:\btool\b.{0,80}\bis not (?:allowed|permitted|authorized)\b|not (?:allowed|permitted|authorized).{0,80}(?:policy|approval)|(?:denied|rejected|blocked).{0,80}(?:by|under|due to).{0,40}(?:policy|approval)|(?:policy|approval).{0,80}(?:denied|rejected|blocked)|(?:approval|confirmation).{0,80}required.{0,80}(?:headless|non[- ]interactive))/i.test(detail);
104
105
  }
105
106
  const GEMINI_H_CLASS_PATTERNS = [
107
+ String.raw `(?:^|[/\\"])providers\.json(?:_|__|\.lock(?:\.[^/\\"]*)?)?(?:[/\\"]|$)`,
106
108
  String.raw `evolcore\.json`,
107
109
  String.raw `(?:^|[/\\"])data(?:[/\\"]|$)`,
108
110
  String.raw `(?:^|[/\\"])(?:AIDs|aids)(?:[/\\"]|$)`,
@@ -316,6 +318,8 @@ export class GeminiRunner {
316
318
  name = 'gemini';
317
319
  capabilities = { fullaccess: true, clear: true, compact: false, fork: false, forkAtTurn: false, askUserQuestion: false, planApproval: false, fileRewind: 'unsupported' };
318
320
  resolved;
321
+ config;
322
+ agentAid;
319
323
  model;
320
324
  activeProcesses = new Map();
321
325
  activeStreams = new Map();
@@ -323,7 +327,9 @@ export class GeminiRunner {
323
327
  onSessionIdUpdate;
324
328
  currentMode = 'readonly';
325
329
  permissionContexts = new Map();
326
- constructor(config, callbacks) {
330
+ constructor(config, callbacks, runtime) {
331
+ this.config = config;
332
+ this.agentAid = runtime?.agentAid;
327
333
  this.resolved = resolveGoogleConfig(config);
328
334
  this.model = stripContextWindowSuffix(this.resolved.model);
329
335
  this.onSessionIdUpdate = callbacks.onSessionIdUpdate;
@@ -331,7 +337,10 @@ export class GeminiRunner {
331
337
  // ── ModelSwitcher ──
332
338
  setModel(model) { this.model = stripContextWindowSuffix(model); }
333
339
  getModel() { return this.model; }
334
- async listModels() { const catalog = await getCatalog(undefined, 'gemini'); assertCatalogSelectable(catalog); return catalog.models.map(m => m.id); }
340
+ getModelCatalog() {
341
+ return getRunnerCatalog('gemini', this.agentAid, this.currentProviderConfig());
342
+ }
343
+ async listModels() { const catalog = await this.getModelCatalog(); assertCatalogSelectable(catalog); return catalog.models.map(m => m.id); }
335
344
  // ── Effort (not applicable) ──
336
345
  setEffort(_effort) { }
337
346
  getEffort() { return undefined; }
@@ -351,14 +360,29 @@ export class GeminiRunner {
351
360
  setPermissionContext(sessionId, context) {
352
361
  this.permissionContexts.set(sessionId, context);
353
362
  }
354
- buildAgentEnv(sessionId, runtimeEnv) {
363
+ currentProviderConfig(providerOverride) {
364
+ const config = this.agentAid
365
+ ? { agents: { gemini: resolveEffective({ self: this.agentAid }, { cache: true, expand: false }).baseagents?.gemini ?? this.config.agents?.gemini } }
366
+ : this.config;
367
+ return resolveGoogleConfig(config, providerOverride ? { provider: providerOverride } : undefined);
368
+ }
369
+ buildAgentEnv(sessionId, runtimeEnv, resolved = this.currentProviderConfig()) {
355
370
  const env = {
356
371
  ...process.env,
357
372
  EVOLCORE_SESSION_ID: sessionId,
358
373
  ...(runtimeEnv ?? {}),
359
374
  };
360
- if (this.resolved.apiKey)
361
- env.GOOGLE_API_KEY = this.resolved.apiKey;
375
+ if (resolved.apiKey) {
376
+ env.GEMINI_API_KEY = resolved.apiKey;
377
+ env.GOOGLE_API_KEY = resolved.apiKey;
378
+ }
379
+ if (resolved.providerId && resolved.baseUrl) {
380
+ env.GOOGLE_GEMINI_BASE_URL = resolved.baseUrl;
381
+ env.GOOGLE_GENAI_USE_VERTEXAI = 'false';
382
+ env.GOOGLE_GENAI_USE_GCA = 'false';
383
+ env.GEMINI_CLI_CUSTOM_HEADERS = Object.entries(resolved.headers ?? {})
384
+ .map(([name, value]) => `${name}: ${value}`).join(', ');
385
+ }
362
386
  return env;
363
387
  }
364
388
  // ── Stream management ──
@@ -380,6 +404,8 @@ export class GeminiRunner {
380
404
  error.code = 'TOPIC_TURN_LEASE_REQUIRED';
381
405
  throw error;
382
406
  }
407
+ const resolved = this.currentProviderConfig(modelOverride?.provider);
408
+ const env = this.buildAgentEnv(sessionId, runtimeEnv, resolved);
383
409
  const topicAgentSessionId = topicBinding ? modelOverride?.backend?.agentSessionId ?? null : undefined;
384
410
  const cachedSessionId = topicBinding
385
411
  ? (topicAgentSessionId && this.activeSessions.get(sessionId) === topicAgentSessionId ? topicAgentSessionId : undefined)
@@ -410,7 +436,7 @@ export class GeminiRunner {
410
436
  const geminiHome = path.join(os.homedir(), '.gemini');
411
437
  fs.mkdirSync(geminiHome, { recursive: true, mode: 0o700 });
412
438
  const selfAid = this.permissionContexts.get(sessionId)?.selfAid;
413
- const sandboxProbe = executionPermissionMode === 'fullaccess' ? undefined : buildBubblewrapCommand(this.resolved.cliPath, [], {
439
+ const sandboxProbe = executionPermissionMode === 'fullaccess' ? undefined : buildBubblewrapCommand(resolved.cliPath, [], {
414
440
  projectPath,
415
441
  selfAid,
416
442
  writablePaths: [
@@ -481,8 +507,7 @@ export class GeminiRunner {
481
507
  args.push('-r', geminiSessionId);
482
508
  }
483
509
  // Spawn subprocess
484
- const env = this.buildAgentEnv(sessionId, runtimeEnv);
485
- const sandboxedCommand = executionPermissionMode === 'fullaccess' ? undefined : buildBubblewrapCommand(this.resolved.cliPath, args, {
510
+ const sandboxedCommand = executionPermissionMode === 'fullaccess' ? undefined : buildBubblewrapCommand(resolved.cliPath, args, {
486
511
  projectPath,
487
512
  selfAid,
488
513
  writablePaths: [
@@ -494,7 +519,7 @@ export class GeminiRunner {
494
519
  ],
495
520
  readonlyPaths: tempFiles,
496
521
  });
497
- const child = spawn(sandboxedCommand?.command ?? this.resolved.cliPath, sandboxedCommand?.args ?? args, {
522
+ const child = spawn(sandboxedCommand?.command ?? resolved.cliPath, sandboxedCommand?.args ?? args, {
498
523
  cwd: projectPath,
499
524
  stdio: ['pipe', 'pipe', 'pipe'],
500
525
  env,
@@ -934,10 +959,9 @@ export class GeminiAgentPlugin {
934
959
  }
935
960
  createAgent(agent, callbacks) {
936
961
  const override = agent.config.baseagents?.gemini;
937
- const syntheticConfig = { agents: { gemini: override } };
938
962
  const merged = {
939
963
  agents: { gemini: { ...(override || {}) } },
940
964
  };
941
- return { evolagentName: agent.name, baseagent: 'gemini', agent: new GeminiRunner(merged, callbacks) };
965
+ return { evolagentName: agent.name, baseagent: 'gemini', agent: new GeminiRunner(merged, callbacks, { agentAid: agent.aid, agentConfig: agent.config }) };
942
966
  }
943
967
  }
@@ -39,7 +39,7 @@ export function suppressSdkLogs() {
39
39
  };
40
40
  }
41
41
  // ==================== SDK package ====================
42
- export const MIN_AUN_CORE_SDK = [0, 5, 20];
42
+ export const MIN_AUN_CORE_SDK = [0, 5, 22];
43
43
  export const AUN_CORE_SDK_PKG = '@agentunion/fastaun';
44
44
  function compareVersion(a, min) {
45
45
  const parts = a.split('.').map(n => parseInt(n, 10));
@@ -91,7 +91,7 @@ export function isAunSdkReady() {
91
91
  return !!(installed && isAunSdkVersionOk(installed.version));
92
92
  }
93
93
  // ==================== CA Root ====================
94
- export async function downloadCaRoot(aunPath, gatewayUrl, indent = '') {
94
+ export async function downloadCaRoot(aunPath, gatewayUrl, indent = '', silent = false) {
95
95
  const caDir = path.join(aunPath, 'CA', 'root');
96
96
  const caCertPath = path.join(caDir, 'root.crt');
97
97
  if (fs.existsSync(caCertPath))
@@ -112,7 +112,8 @@ export async function downloadCaRoot(aunPath, gatewayUrl, indent = '') {
112
112
  return false;
113
113
  }
114
114
  fs.writeFileSync(caCertPath, body);
115
- console.log(`${indent}✓ CA 根证书已下载`);
115
+ if (!silent)
116
+ console.log(`${indent}✓ CA 根证书已下载`);
116
117
  return true;
117
118
  }
118
119
  catch (e) {
@@ -4,8 +4,11 @@ import path from 'path';
4
4
  import { parseDocument } from 'yaml';
5
5
  import { aidCreate } from './index.js';
6
6
  import { DEFAULT_AID_DOMAIN, normalizeAidDomain } from './domain.js';
7
- import { getAidStore, SLOT } from './store.js';
8
- import { validateAgentMdDocument, verifyAgentMdUpload } from '@agentunion/fastaun';
7
+ import { getAidStore, loadClient, SLOT } from './store.js';
8
+ import { LocalTokenStore, validateAgentMdDocument, verifyAgentMdUpload, } from '@agentunion/fastaun';
9
+ import { AUN_KEYSTORE_ENCRYPTION_SEED } from './encryption-seed-policy.js';
10
+ import { isValidAid } from './validation.js';
11
+ import { OwnerResolver } from '../../core/ownership/owner-resolver.js';
9
12
  import { agentMdPath, resolvePaths } from '../../paths.js';
10
13
  import { PeerIdentityCache } from '../../core/relation/peer-identity.js';
11
14
  import { agentmdPut, buildInitialAgentMd, invalidateAgentDisplayName, stripAgentMdSignature } from './agentmd.js';
@@ -107,10 +110,10 @@ function clearControlAgentPeerCaches(aid, root = resolvePaths().root) {
107
110
  /**
108
111
  * Ensure a daemon/control AID has a signed host agent.md.
109
112
  *
110
- * Registration in fastaun creates a default `type: agent` card. This helper
111
- * upgrades that card, supplements cards missing on older installations, and
112
- * is idempotent once the local card is canonical. Upload errors are allowed to
113
- * propagate to the caller so startup can log them without becoming blocked.
113
+ * Registration in fastaun creates an initial card. This helper upgrades that
114
+ * card, supplements cards missing on older installations, and is idempotent
115
+ * once the local card is canonical. Upload errors are allowed to propagate to
116
+ * the caller so startup can log them without becoming blocked.
114
117
  */
115
118
  export async function ensureControlAgentMd(aid, opts = {}) {
116
119
  const normalizedAid = String(aid ?? '').trim().replace(/^@/, '');
@@ -241,7 +244,7 @@ async function candidateExists(store, candidate) {
241
244
  * - fail-fast:查重探测失败(网关不可达)立即抛错,不掩盖成"均冲突"
242
245
  * - 注册后补发控制主机 agent.md;失败只告警,不丢弃已经注册的 AID
243
246
  */
244
- export async function generateControlAid(aidDomain) {
247
+ export async function generateControlAid(aidDomain, options = {}) {
245
248
  const finalAidDomain = aidDomain === undefined
246
249
  ? resolveControlAidDomain()
247
250
  : normalizeAidDomain(aidDomain, 'aidDomain');
@@ -255,7 +258,9 @@ export async function generateControlAid(aidDomain) {
255
258
  continue;
256
259
  }
257
260
  try {
258
- const created = await aidCreate(candidate);
261
+ const created = await aidCreate(candidate, {
262
+ type: 'host', ...(options.silent ? { silent: true } : {}),
263
+ });
259
264
  try {
260
265
  await ensureControlAgentMd(created.aid, { store: created.store });
261
266
  }
@@ -279,6 +284,12 @@ export async function generateControlAid(aidDomain) {
279
284
  return { aid: created.aid, gateway: created.gateway };
280
285
  }
281
286
  catch (e) {
287
+ // Registration may already have saved credentials before authentication
288
+ // failed. Keep that identity so the completed AID can be retried.
289
+ if (store.load(candidate).ok) {
290
+ logger.warn(`[control-aid] ${candidate} 已注册,保留身份以恢复初始化: ${e}`);
291
+ return { aid: candidate, gateway: '' };
292
+ }
282
293
  // 查重与实际注册之间存在 TOCTOU 竞态;注册接口也可能因服务端暂时失败而拒绝,
283
294
  // 这些情况都换一个候选继续尝试。查重阶段的网关错误仍在 candidateExists 中 fail-fast。
284
295
  lastRegistrationError = e;
@@ -304,3 +315,254 @@ export async function generateControlAid(aidDomain) {
304
315
  store.close();
305
316
  }
306
317
  }
318
+ const REGISTRATION_OPERATION_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
319
+ /** Submit an explicit Owner binding after the AID and its final agent.md exist. */
320
+ export async function submitControlAidRegistration(aid, owner, options = {}) {
321
+ const ownerAid = owner.toLowerCase();
322
+ if (!isValidAid(aid) || !isValidAid(ownerAid) || aid.toLowerCase() === ownerAid) {
323
+ return { status: 'failed', error: { code: 'INVALID_OWNER', message: 'A distinct valid Owner AID is required' } };
324
+ }
325
+ const deadline = Date.now() + (options.timeoutMs ?? 15_000);
326
+ let records;
327
+ let store;
328
+ let client;
329
+ let operationId;
330
+ let task;
331
+ let connecting = false;
332
+ let sentOperationIds = new Set();
333
+ let invitationObserver;
334
+ const resolver = new OwnerResolver();
335
+ async function bounded(operation, timeoutMs = deadline - Date.now()) {
336
+ let timer;
337
+ try {
338
+ return await Promise.race([operation, new Promise((_resolve, reject) => {
339
+ timer = setTimeout(() => reject(Object.assign(new Error('Registration invitation submission timed out'), {
340
+ code: 'BIND_SUBMIT_TIMEOUT',
341
+ })), Math.max(0, timeoutMs));
342
+ })]);
343
+ }
344
+ finally {
345
+ if (timer)
346
+ clearTimeout(timer);
347
+ }
348
+ }
349
+ function readTask() {
350
+ const raw = records.loadOwnershipTask(aid, operationId)?.task;
351
+ if (!raw)
352
+ return;
353
+ if (raw.operation_id !== operationId || raw.operation !== 'bind' || raw.sub_aid !== aid
354
+ || raw.owner_aid !== ownerAid || raw.initiator_aid !== aid || raw.new_owner_aid !== undefined
355
+ || !['pending', 'completed', 'rejected', 'stopped', 'error'].includes(String(raw.status))
356
+ || typeof raw.step !== 'string' || !Number.isSafeInteger(raw.base_relation_version)
357
+ || raw.target_relation_version !== Number(raw.base_relation_version) + 1) {
358
+ throw Object.assign(new Error('SDK registration task does not match its original intent'), { code: 'TASK_MISMATCH' });
359
+ }
360
+ task = {
361
+ operation_id: operationId, operation: 'bind', sub_aid: aid, owner_aid: ownerAid,
362
+ base_relation_version: raw.base_relation_version,
363
+ target_relation_version: raw.target_relation_version,
364
+ status: raw.status, step: raw.step,
365
+ ...(typeof raw.code === 'string' ? { code: raw.code } : {}),
366
+ ...(typeof raw.reason === 'string' ? { reason: raw.reason } : {}),
367
+ ...(typeof raw.retryable === 'boolean' ? { retryable: raw.retryable } : {}),
368
+ };
369
+ }
370
+ function result(status, error) {
371
+ return { status, ...(operationId ? { operationId } : {}), ...(task ? { task } : {}), ...(error ? { error } : {}) };
372
+ }
373
+ try {
374
+ records = new LocalTokenStore(resolvePaths().root, { encryptionSeed: AUN_KEYSTORE_ENCRYPTION_SEED });
375
+ const intent = records.loadMetadata(aid)?.registration_intent;
376
+ const registrationIntentOwner = typeof intent?.owner === 'string' ? intent.owner.toLowerCase() : undefined;
377
+ // Preserve an existing SDK registration task and let the same SDK client
378
+ // resume it during connect before OwnerResolver checks/binds the subject.
379
+ if (intent && Object.prototype.hasOwnProperty.call(intent, 'owner')) {
380
+ if (!registrationIntentOwner) {
381
+ return result('unknown', {
382
+ code: 'REGISTRATION_INTENT_PRESENT',
383
+ message: 'Control AID has an invalid SDK registration Owner intent',
384
+ });
385
+ }
386
+ if (registrationIntentOwner !== ownerAid) {
387
+ return result('failed', { code: 'OWNER_INTENT_CONFLICT', message: 'Control AID was registered for a different Owner' });
388
+ }
389
+ if (typeof intent.operation_id === 'string' && REGISTRATION_OPERATION_ID.test(intent.operation_id)) {
390
+ operationId = intent.operation_id;
391
+ readTask();
392
+ }
393
+ if (!operationId) {
394
+ return result('unknown', {
395
+ code: 'REGISTRATION_INTENT_PRESENT',
396
+ message: 'Control AID has an SDK registration intent without a valid operation ID',
397
+ });
398
+ }
399
+ }
400
+ if (options.agentMdError) {
401
+ return result('failed', { code: 'CONTROL_AGENTMD_NOT_READY', message: options.agentMdError });
402
+ }
403
+ store = await bounded(getAidStore({ slotId: SLOT.cli }));
404
+ const ownershipTasks = records.listOwnershipTasks(aid).map(snapshot => snapshot.task);
405
+ const conflictingTask = ownershipTasks.find(candidate => candidate.sub_aid === aid
406
+ && (candidate.status === 'pending' || candidate.status === 'error' && candidate.retryable === true)
407
+ && !(candidate.operation === 'bind' && candidate.owner_aid === ownerAid
408
+ && candidate.initiator_aid === aid && candidate.new_owner_aid === undefined));
409
+ if (conflictingTask) {
410
+ return result('failed', { code: 'BIND_CONFLICT', message: 'A different active ownership task must be resolved first' });
411
+ }
412
+ client = await bounded(loadClient(store, aid));
413
+ connecting = true;
414
+ sentOperationIds = new Set();
415
+ invitationObserver = observeInvitationSend(client, ownerAid, sentOperationIds);
416
+ await bounded(client.connect({
417
+ connection_kind: 'long', auto_reconnect: false, background_sync: false,
418
+ connect_timeout: 10, call_timeout: 10,
419
+ }));
420
+ if (registrationIntentOwner === ownerAid && operationId) {
421
+ readTask();
422
+ if ((!task || task.status === 'pending') && !sentOperationIds.has(operationId)) {
423
+ await bounded(invitationObserver.waitFor(operationId));
424
+ readTask();
425
+ }
426
+ if (!task) {
427
+ return result('unknown', {
428
+ code: 'BIND_TASK_NOT_PERSISTED',
429
+ message: 'SDK did not expose the recovered registration task after connect',
430
+ });
431
+ }
432
+ if (task?.status === 'completed') {
433
+ const consensus = await bounded(client.checkOwnershipConsensus(aid));
434
+ if (consensus.status === 'consensus' && consensus.sub_aid === aid && consensus.owner_aid === ownerAid) {
435
+ return result('already_bound');
436
+ }
437
+ return result('failed', {
438
+ code: 'CONSENSUS_REQUIRED',
439
+ message: 'A completed binding task has no current verified Owner consensus',
440
+ });
441
+ }
442
+ if (task && (['rejected', 'stopped', 'error'].includes(task.status) || task.code)) {
443
+ return result('failed', { code: task.code ?? 'BIND_SUBMIT_FAILED', message: task.reason ?? `Binding task ${task.status}` });
444
+ }
445
+ if (sentOperationIds.has(operationId))
446
+ return result('submitted');
447
+ return result('unknown', {
448
+ code: 'BIND_SUBMIT_UNKNOWN',
449
+ message: 'SDK is recovering the original registration task; no matching message.send acknowledgement was observed',
450
+ });
451
+ }
452
+ // Reuse the same service as agent-create, including its fresh consensus
453
+ // check, explicit SDK bind call, and persistent task projection.
454
+ resolver.attachClient(aid, client, true);
455
+ const view = await bounded(resolver.bind(aid, ownerAid));
456
+ if (view.consensusStatus === 'consensus' && view.ownerAid === ownerAid) {
457
+ return result('already_bound');
458
+ }
459
+ const returnedTask = view.task;
460
+ if (!returnedTask || returnedTask.operation !== 'bind' || returnedTask.sub_aid !== aid
461
+ || returnedTask.owner_aid !== ownerAid || returnedTask.new_owner_aid !== undefined
462
+ || typeof returnedTask.operation_id !== 'string' || !REGISTRATION_OPERATION_ID.test(returnedTask.operation_id)) {
463
+ throw Object.assign(new Error('SDK returned a binding task for a different subject or Owner'), { code: 'TASK_MISMATCH' });
464
+ }
465
+ operationId = returnedTask.operation_id;
466
+ readTask();
467
+ if (!task) {
468
+ throw Object.assign(new Error('SDK binding task was not persisted'), { code: 'BIND_TASK_NOT_PERSISTED' });
469
+ }
470
+ if (task.status === 'completed') {
471
+ const consensus = await bounded(client.checkOwnershipConsensus(aid));
472
+ if (consensus.status === 'consensus' && consensus.sub_aid === aid && consensus.owner_aid === ownerAid) {
473
+ return result('already_bound');
474
+ }
475
+ return result('failed', { code: 'CONSENSUS_REQUIRED', message: 'Completed binding has no current verified Owner consensus' });
476
+ }
477
+ if (['rejected', 'stopped', 'error'].includes(task.status)) {
478
+ return result('failed', { code: task.code ?? 'BIND_SUBMIT_FAILED', message: task.reason ?? `Binding task ${task.status}` });
479
+ }
480
+ if (task.code) {
481
+ return result('failed', { code: task.code, message: task.reason ?? 'SDK binding did not finish submitting the invitation' });
482
+ }
483
+ if (!sentOperationIds.has(operationId)) {
484
+ return result('unknown', { code: 'BIND_SUBMIT_UNKNOWN', message: 'SDK persisted the binding task without confirming its message.send acknowledgement' });
485
+ }
486
+ return result('submitted');
487
+ }
488
+ catch (error) {
489
+ const details = error;
490
+ const code = typeof details.stringCode === 'string' ? details.stringCode
491
+ : typeof details.code === 'string' ? details.code : undefined;
492
+ const uncertain = connecting && (code === 'BIND_SUBMIT_TIMEOUT' || !code
493
+ || resolver.snapshot(aid).requestStatus === 'interrupted');
494
+ return result(uncertain ? 'unknown' : 'failed', {
495
+ code: uncertain ? 'BIND_SUBMIT_UNKNOWN' : code ?? 'BIND_SUBMIT_FAILED',
496
+ message: details.message ?? String(error),
497
+ });
498
+ }
499
+ finally {
500
+ resolver.stop();
501
+ invitationObserver?.restore();
502
+ if (client) {
503
+ try {
504
+ await bounded(client.close(), 2_000);
505
+ }
506
+ catch (error) {
507
+ logger.warn(`[control-aid] ${aid} invitation client cleanup: ${error}`);
508
+ }
509
+ }
510
+ try {
511
+ store?.close();
512
+ }
513
+ finally {
514
+ records?.close();
515
+ }
516
+ }
517
+ }
518
+ function observeInvitationSend(client, ownerAid, sentOperationIds) {
519
+ const target = client;
520
+ const original = target.call;
521
+ if (typeof original !== 'function')
522
+ return { waitFor: async () => false, restore: () => { } };
523
+ const waiters = new Map();
524
+ const bound = original.bind(client);
525
+ const wrapped = async (method, params) => {
526
+ const response = await bound(method, params);
527
+ const recipient = typeof params?.to === 'string' ? params.to.toLowerCase() : undefined;
528
+ const payloadValue = params?.payload;
529
+ if (method === 'message.send' && recipient === ownerAid
530
+ && payloadValue && typeof payloadValue === 'object' && !Array.isArray(payloadValue)) {
531
+ const payload = payloadValue;
532
+ const result = response && typeof response === 'object' && !Array.isArray(response)
533
+ ? response : undefined;
534
+ if (payload.action === 'bind.request' && typeof payload.operation_id === 'string'
535
+ && typeof result?.message_id === 'string') {
536
+ sentOperationIds.add(payload.operation_id);
537
+ const pending = waiters.get(payload.operation_id);
538
+ if (pending) {
539
+ waiters.delete(payload.operation_id);
540
+ for (const settle of pending)
541
+ settle(true);
542
+ }
543
+ }
544
+ }
545
+ return response;
546
+ };
547
+ target.call = wrapped;
548
+ return {
549
+ waitFor: operationId => {
550
+ if (sentOperationIds.has(operationId))
551
+ return Promise.resolve(true);
552
+ return new Promise(resolve => {
553
+ const pending = waiters.get(operationId) ?? new Set();
554
+ pending.add(resolve);
555
+ waiters.set(operationId, pending);
556
+ });
557
+ },
558
+ restore: () => {
559
+ if (target.call === wrapped)
560
+ target.call = original;
561
+ for (const pending of waiters.values()) {
562
+ for (const settle of pending)
563
+ settle(false);
564
+ }
565
+ waiters.clear();
566
+ },
567
+ };
568
+ }
@@ -224,7 +224,10 @@ export async function aidCreate(aid, opts) {
224
224
  // 新注册流程:AIDStore.register → downloadCaRoot → load → authenticate
225
225
  const store = await getAidStore({ slotId: SLOT.cli, aunPath });
226
226
  try {
227
- const regResult = await store.register(aid, { type: 'agent' });
227
+ const regResult = await store.register(aid, {
228
+ type: opts?.type ?? 'agent',
229
+ ...(opts?.owner?.trim() ? { owner: opts.owner.trim() } : {}),
230
+ });
228
231
  if (!regResult.ok) {
229
232
  const e = new Error(regResult.error.message);
230
233
  e.code = regResult.error.code;
@@ -236,11 +239,11 @@ export async function aidCreate(aid, opts) {
236
239
  try {
237
240
  const { GatewayDiscovery } = await import('@agentunion/fastaun');
238
241
  const discovery = new GatewayDiscovery({});
239
- gatewayUrl = await discovery.discover(`https://${aid}/.well-known/aun-gateway`);
242
+ gatewayUrl = await discovery.discover(`https://${aid}/.well-known/aun-gateway`, 15_000);
240
243
  }
241
244
  catch { /* fall through */ }
242
245
  if (gatewayUrl) {
243
- await downloadCaRoot(aunPath, gatewayUrl);
246
+ await downloadCaRoot(aunPath, gatewayUrl, '', opts?.silent === true);
244
247
  }
245
248
  // 重建 store(CA 可能刚下载,需要 rootCaPath 生效)
246
249
  store.close();