evolcore 0.0.3 → 0.0.5

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 (148) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +45 -10
  3. package/bin/ec-safe-output.js +89 -24
  4. package/dist/agents/baseagent.js +46 -0
  5. package/dist/agents/claude-runner.js +1 -31
  6. package/dist/agents/codex-app-server-client.js +68 -0
  7. package/dist/agents/codex-runner.js +157 -5
  8. package/dist/agents/runner-types.js +2 -2
  9. package/dist/aun/aid/agentmd.js +79 -0
  10. package/dist/aun/aid/encryption-seed-policy.js +29 -0
  11. package/dist/aun/aid/index.js +1 -1
  12. package/dist/aun/aid/store.js +2 -5
  13. package/dist/channels/aun.js +220 -112
  14. package/dist/channels/daemon.js +18 -4
  15. package/dist/channels/dingtalk.js +52 -137
  16. package/dist/channels/feishu.js +56 -4
  17. package/dist/channels/qqbot.js +23 -1
  18. package/dist/channels/wechat.js +303 -155
  19. package/dist/channels/wecom.js +383 -7
  20. package/dist/cli/aun-commands.js +11 -11
  21. package/dist/cli/daemon-commands.js +46 -15
  22. package/dist/cli/handoff-command.js +2 -1
  23. package/dist/cli/init-channel.js +185 -67
  24. package/dist/cli/init.js +49 -27
  25. package/dist/cli/trigger-command.js +92 -11
  26. package/dist/config/access-policy-domain.js +18 -0
  27. package/dist/config/access-policy.js +110 -0
  28. package/dist/config/builtin-role-templates.js +27 -14
  29. package/dist/config/builtin-roles.js +25 -6
  30. package/dist/config/config-field-policy.js +17 -5
  31. package/dist/config/config-manager.js +198 -22
  32. package/dist/config/config-operation-service.js +35 -38
  33. package/dist/config/contact-bind-code.js +274 -0
  34. package/dist/config/contact-book-store.js +173 -5
  35. package/dist/config/contact-book.js +32 -0
  36. package/dist/config/contact-operation-service.js +9 -3
  37. package/dist/config/contact-request-service.js +331 -0
  38. package/dist/config/peer-role-resolver.js +3 -1
  39. package/dist/config/schema-registry.js +49 -24
  40. package/dist/config-store.js +14 -2
  41. package/dist/core/auth/agent-delegation.js +105 -11
  42. package/dist/core/auth/authorization-audit.js +6 -0
  43. package/dist/core/auth/operation-authorizer.js +8 -0
  44. package/dist/core/auth/operation-catalog.js +51 -3
  45. package/dist/core/auth/trigger-authorization.js +15 -0
  46. package/dist/core/bootstrap-service.js +2 -9
  47. package/dist/core/channel-loader.js +6 -2
  48. package/dist/core/command/command-handler.js +10 -13
  49. package/dist/core/command/connect-menu.js +229 -26
  50. package/dist/core/command/evol-menu-version-gate.js +38 -0
  51. package/dist/core/command/group-menu.js +421 -0
  52. package/dist/core/command/menu-handler.js +269 -85
  53. package/dist/core/command/menu-protocol.js +8 -2
  54. package/dist/core/command/menu-token-store.js +102 -0
  55. package/dist/core/command/role-menu.js +16 -0
  56. package/dist/core/command/slash-gate.js +1 -1
  57. package/dist/core/command/slash-handler.js +158 -31
  58. package/dist/core/daemon-file-cache.js +28 -0
  59. package/dist/core/event-catalog.js +29 -0
  60. package/dist/core/evolagent-registry.js +4 -39
  61. package/dist/core/handoff/runtime.js +162 -37
  62. package/dist/core/handoff/store.js +46 -2
  63. package/dist/core/handoff/types.js +1 -0
  64. package/dist/core/inference/text-inference.js +16 -74
  65. package/dist/core/message/file-markers.js +7 -0
  66. package/dist/core/message/im-renderer.js +18 -14
  67. package/dist/core/message/inbound-admission.js +134 -0
  68. package/dist/core/message/message-bridge.js +616 -74
  69. package/dist/core/message/message-log.js +1 -0
  70. package/dist/core/message/message-queue.js +185 -10
  71. package/dist/core/message/peer-mode.js +7 -8
  72. package/dist/core/message/response-engine.js +381 -79
  73. package/dist/core/permission/approval-gateway.js +17 -10
  74. package/dist/core/permission/ec-command-parser.js +101 -46
  75. package/dist/core/permission/tool-policy.js +69 -24
  76. package/dist/core/protected-paths.js +36 -11
  77. package/dist/core/session/session-fs-store.js +19 -4
  78. package/dist/core/session/session-manager.js +232 -4
  79. package/dist/core/session/session-mapper.js +2 -0
  80. package/dist/core/session/session-renew.js +125 -69
  81. package/dist/core/session/session-turn-coordinator.js +5 -1
  82. package/dist/eck/kit-renderer.js +12 -1
  83. package/dist/eck/message-renderer.js +79 -1
  84. package/dist/index.js +224 -89
  85. package/dist/ipc.js +81 -4
  86. package/dist/paths.js +3 -0
  87. package/dist/response-system/config-resolver.js +29 -0
  88. package/dist/response-system/coordinator.js +8 -32
  89. package/dist/response-system/engines/v1/proactive-flow.js +1 -1
  90. package/dist/response-system/index.js +1 -0
  91. package/dist/response-system/modes/single-session/index.js +7 -4
  92. package/dist/trigger/parser.js +55 -15
  93. package/dist/trigger/patch.js +4 -1
  94. package/dist/trigger/scheduler.js +441 -39
  95. package/dist/utils/cross-platform.js +8 -2
  96. package/dist/utils/error-dict.json +7 -0
  97. package/dist/utils/evolcore-version.js +21 -0
  98. package/dist/utils/logger.js +2 -2
  99. package/dist/utils/process-introspect.js +19 -3
  100. package/dist/utils/stable-semver.js +21 -0
  101. package/dist/utils/stats.js +33 -9
  102. package/kits/docs/channels/aun.md +4 -13
  103. package/kits/docs/evolcore/INDEX.md +1 -1
  104. package/kits/docs/evolcore/config.md +47 -2
  105. package/kits/docs/evolcore/contact.md +8 -3
  106. package/kits/docs/evolcore/group-rules.md +46 -4
  107. package/kits/docs/evolcore/group.md +4 -4
  108. package/kits/docs/evolcore/msg.md +5 -5
  109. package/kits/docs/evolcore/trigger.md +25 -8
  110. package/kits/docs/path-registry.md +36 -17
  111. package/kits/eck_message_manifest.json +12 -1
  112. package/kits/migrations/migrate-contact-book-v2.mjs +7 -0
  113. package/kits/rules/01-overview.md +17 -7
  114. package/kits/rules/02-navigation.md +38 -18
  115. package/kits/rules/03-identity.md +28 -24
  116. package/kits/rules/04-relation.md +44 -28
  117. package/kits/rules/05-venue.md +31 -15
  118. package/kits/rules/06-channel.md +11 -7
  119. package/kits/schemas/_meta.json +18 -7
  120. package/kits/schemas/agent-config.schema.7.json +303 -0
  121. package/kits/schemas/agent-config.schema.8.json +304 -0
  122. package/kits/schemas/agent-config.schema.9.json +364 -0
  123. package/kits/schemas/contact-book.schema.3.json +67 -0
  124. package/kits/schemas/daemon.schema.1.json +3 -2
  125. package/kits/schemas/daemon.schema.2.json +101 -0
  126. package/kits/schemas/daemon.schema.3.json +123 -0
  127. package/kits/schemas/defaults.schema.2.json +85 -0
  128. package/kits/schemas/defaults.schema.3.json +73 -0
  129. package/kits/schemas/relation-config.schema.6.json +46 -0
  130. package/kits/schemas/relation-config.schema.7.json +59 -0
  131. package/kits/schemas/single-session.schema.2.json +57 -0
  132. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -0
  133. package/kits/templates/message-fragments/handoff-request-to-target.md +14 -8
  134. package/kits/templates/message-fragments/handoff-response-to-origin.md +5 -6
  135. package/kits/templates/roles/admin.json +46 -0
  136. package/kits/templates/roles/member.json +4 -0
  137. package/kits/templates/roles/visitor.json +4 -0
  138. package/kits/templates/system-fragments/channel.md +12 -2
  139. package/kits/templates/system-fragments/identity.md +3 -1
  140. package/kits/templates/system-fragments/relation.md +1 -1
  141. package/kits/templates/system-fragments/session.md +6 -2
  142. package/package.json +4 -2
  143. package/MIGRATION-0.5.0.md +0 -378
  144. package/ROLE_ACCESS_CONTROL.md +0 -174
  145. package/dist/channels/contact-bind-code.js +0 -134
  146. package/dist/channels/wecom-card.js +0 -101
  147. package/dist/channels/wecom-onboarding.js +0 -82
  148. package/dist/channels/wecom-state.js +0 -191
@@ -17,6 +17,8 @@ import { isRetryableError } from '../utils/error-utils.js';
17
17
  import { renderActionAsText } from '../core/interaction-router.js';
18
18
  import { buildEnvelope, sendInteractionPayload } from '../core/message/message-utils.js';
19
19
  import { resolveCodexCapabilityThreadConfigForProject } from '../core/capability/capability-manager.js';
20
+ import { AGENT_DELEGATION_TOKEN_ENV, hashDelegatedCommandArgv } from '../core/auth/agent-delegation.js';
21
+ import { classifyEvolcoreShellCommand, parseLiteralShellArgv } from '../core/permission/ec-command-parser.js';
20
22
  import { compareVersions } from '../utils/npm-ops.js';
21
23
  import { resolvePaths, resolveRoot } from '../paths.js';
22
24
  import { buildSessionTurnList } from '../core/session/session-turns.js';
@@ -111,6 +113,7 @@ const CODEX_CATALOG_FALLBACK = [
111
113
  { slug: 'gpt-5.3-codex', efforts: ['low', 'medium', 'high', 'xhigh'] },
112
114
  { slug: 'gpt-5.2', efforts: ['low', 'medium', 'high', 'xhigh'] },
113
115
  ];
116
+ const CODEX_DELEGATION_CARRIER_METADATA_KEY = '__codexDelegationCarrierV1';
114
117
  let codexCatalogCache = null;
115
118
  // The permission bridge is version-locked to the v2 schema audited below:
116
119
  // named profiles, approvalsReviewer, additional/network approval context, and
@@ -211,6 +214,7 @@ export class CodexRunner {
211
214
  threadExternalToolFingerprints = new Map();
212
215
  threadExternalToolBoundaries = new Map();
213
216
  trackedApprovalItems = new Map();
217
+ threadDelegationCarriers = new Map();
214
218
  pendingInterrupts = new Map();
215
219
  appServerClient = null;
216
220
  onSessionIdUpdate;
@@ -257,12 +261,98 @@ export class CodexRunner {
257
261
  }
258
262
  return this.appServerClient;
259
263
  }
264
+ createDelegationCarrier() {
265
+ return randomBytes(32).toString('base64url');
266
+ }
267
+ approvedEvolcoreCommandArgv(toolInput) {
268
+ const explicitArgv = Array.isArray(toolInput.commandArgv)
269
+ && toolInput.commandArgv.every(value => typeof value === 'string')
270
+ ? toolInput.commandArgv
271
+ : undefined;
272
+ if (explicitArgv?.[0] === 'ec')
273
+ return explicitArgv;
274
+ const command = typeof toolInput.command === 'string' ? toolInput.command : '';
275
+ const classification = classifyEvolcoreShellCommand(command);
276
+ if (classification.kind === 'bounded-output')
277
+ return classification.command.argv;
278
+ if (classification.kind !== 'literal')
279
+ return undefined;
280
+ const argv = parseLiteralShellArgv(command);
281
+ return argv?.[0] === 'ec' ? argv : undefined;
282
+ }
283
+ armApprovedDelegationCommand(sessionId, toolInput) {
284
+ const threadId = this.activeSessions.get(sessionId);
285
+ const carrierToken = threadId ? this.threadDelegationCarriers.get(threadId) : undefined;
286
+ const argv = this.approvedEvolcoreCommandArgv(toolInput);
287
+ if (!carrierToken || !argv)
288
+ return false;
289
+ const commandHash = hashDelegatedCommandArgv(argv);
290
+ const arm = this.permissionContexts.get(sessionId)?.armApprovedDelegationCommand;
291
+ if (!commandHash || !arm)
292
+ return false;
293
+ return arm(carrierToken, commandHash);
294
+ }
295
+ async readThreadDelegationCarrier(appServer, threadId) {
296
+ try {
297
+ const result = await appServer.threadShellCommand(threadId, `node -e "process.stdout.write(process.env.${AGENT_DELEGATION_TOKEN_ENV}||String())"`);
298
+ const carrier = result.output.trim();
299
+ if (result.exitCode !== undefined && result.exitCode !== 0)
300
+ return undefined;
301
+ return /^[A-Za-z0-9_-]{32,128}$/.test(carrier) ? carrier : undefined;
302
+ }
303
+ catch (error) {
304
+ logger.warn(`[CodexRunner] failed to recover delegation carrier for loaded thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`);
305
+ return undefined;
306
+ }
307
+ }
308
+ async loadPersistedDelegationCarrier(sessionManager, sessionId, threadId) {
309
+ if (typeof sessionManager?.getSessionById !== 'function')
310
+ return undefined;
311
+ try {
312
+ const session = await sessionManager.getSessionById(sessionId);
313
+ const persisted = session?.metadata?.[CODEX_DELEGATION_CARRIER_METADATA_KEY];
314
+ if (!persisted || typeof persisted !== 'object' || Array.isArray(persisted))
315
+ return undefined;
316
+ if (persisted.threadId !== threadId || typeof persisted.carrier !== 'string')
317
+ return undefined;
318
+ return /^[A-Za-z0-9_-]{32,128}$/.test(persisted.carrier)
319
+ ? persisted.carrier
320
+ : undefined;
321
+ }
322
+ catch (error) {
323
+ logger.debug(`[CodexRunner] failed to load persisted delegation carrier: session=${sessionId} error=${error}`);
324
+ return undefined;
325
+ }
326
+ }
327
+ async persistDelegationCarrier(sessionManager, sessionId, threadId, carrier) {
328
+ if (typeof sessionManager?.getSessionById !== 'function'
329
+ || typeof sessionManager?.updateSession !== 'function')
330
+ return;
331
+ try {
332
+ const session = await sessionManager.getSessionById(sessionId);
333
+ if (!session)
334
+ return;
335
+ const current = session.metadata?.[CODEX_DELEGATION_CARRIER_METADATA_KEY];
336
+ if (current?.threadId === threadId && current?.carrier === carrier)
337
+ return;
338
+ await sessionManager.updateSession(sessionId, {
339
+ metadata: {
340
+ ...(session.metadata ?? {}),
341
+ [CODEX_DELEGATION_CARRIER_METADATA_KEY]: { threadId, carrier },
342
+ },
343
+ });
344
+ }
345
+ catch (error) {
346
+ logger.warn(`[CodexRunner] failed to persist delegation carrier: session=${sessionId} thread=${threadId} error=${error}`);
347
+ }
348
+ }
260
349
  resetAppServerClient() {
261
350
  const client = this.appServerClient;
262
351
  this.appServerClient = null;
263
352
  this.threadExternalToolFingerprints.clear();
264
353
  this.threadExternalToolBoundaries.clear();
265
354
  this.trackedApprovalItems.clear();
355
+ this.threadDelegationCarriers.clear();
266
356
  client?.close().catch(error => {
267
357
  logger.debug(`[CodexRunner] Failed to close stale app-server client: ${error}`);
268
358
  });
@@ -656,6 +746,7 @@ export class CodexRunner {
656
746
  // ── Core: runQuery ──
657
747
  async runQuery(sessionId, prompt, projectPath, initialAgentSessionId, images, systemPromptAppend, sessionManager, modelOverride, runtimeEnv) {
658
748
  let agentSessionId = initialAgentSessionId || this.activeSessions.get(sessionId);
749
+ const resumingThread = !!agentSessionId;
659
750
  const callModel = modelOverride?.model || this.model;
660
751
  const callEffort = modelOverride?.effort ?? this.effort;
661
752
  // per-call 权限模式:优先 override(message-processor 解析后传入),缺省回落实例级 currentMode。
@@ -674,13 +765,24 @@ export class CodexRunner {
674
765
  const externalToolConfig = await this.resolveExternalToolApprovalConfig(appServer, projectPath, capabilityConfig, callMode);
675
766
  const managedTempInstruction = this.managedTempInstruction(sessionId);
676
767
  const developerInstructions = [systemPromptAppend, managedTempInstruction].filter(Boolean).join('\n\n') || undefined;
768
+ let knownDelegationCarrier = agentSessionId
769
+ ? this.threadDelegationCarriers.get(agentSessionId)
770
+ : undefined;
771
+ if (agentSessionId && !knownDelegationCarrier) {
772
+ knownDelegationCarrier = await this.loadPersistedDelegationCarrier(sessionManager, sessionId, agentSessionId);
773
+ if (knownDelegationCarrier) {
774
+ this.threadDelegationCarriers.set(agentSessionId, knownDelegationCarrier);
775
+ }
776
+ }
777
+ const delegationCarrierCandidate = knownDelegationCarrier ?? this.createDelegationCarrier();
778
+ const hasTaskDelegation = typeof runtimeEnv?.[AGENT_DELEGATION_TOKEN_ENV] === 'string';
677
779
  const threadOptions = {
678
780
  model: callModel,
679
781
  effort: callEffort,
680
782
  approvalPolicy: effectiveApprovalPolicy,
681
783
  approvalsReviewer: 'user',
682
784
  ...(executionSandbox.sandbox ? { sandbox: executionSandbox.sandbox } : {}),
683
- config: this.mergeThreadConfig(executionSandbox.permissionProfileConfig, runtimeEnv ? { shell_environment_policy: { set: runtimeEnv } } : undefined, capabilityConfig, externalToolConfig, this.buildEvolcoreShellEnvironmentConfig(sessionId), this.buildLifecycleLockdownConfig()),
785
+ config: this.mergeThreadConfig(executionSandbox.permissionProfileConfig, runtimeEnv ? { shell_environment_policy: { set: runtimeEnv } } : undefined, capabilityConfig, externalToolConfig, this.buildEvolcoreShellEnvironmentConfig(sessionId, delegationCarrierCandidate), this.buildLifecycleLockdownConfig()),
684
786
  ...(developerInstructions ? { developerInstructions } : {}),
685
787
  };
686
788
  logger.info(`[CodexRunner] runQuery permMode=${requestedPermissionMode}->${callMode} ` +
@@ -688,9 +790,39 @@ export class CodexRunner {
688
790
  const threadResponse = agentSessionId
689
791
  ? await appServer.threadResume(agentSessionId, projectPath, threadOptions)
690
792
  : await appServer.threadStart(projectPath, threadOptions);
691
- const threadId = threadResponse.thread?.id || agentSessionId;
793
+ let threadId = threadResponse.thread?.id || agentSessionId;
692
794
  if (!threadId)
693
795
  throw new Error('Codex app-server did not return a thread id');
796
+ if (!resumingThread) {
797
+ this.threadDelegationCarriers.set(threadId, delegationCarrierCandidate);
798
+ await this.persistDelegationCarrier(sessionManager, sessionId, threadId, delegationCarrierCandidate);
799
+ }
800
+ else if (hasTaskDelegation && !knownDelegationCarrier) {
801
+ let recoveredCarrier = await this.readThreadDelegationCarrier(appServer, threadId);
802
+ if (!recoveredCarrier) {
803
+ const staleThreadId = threadId;
804
+ logger.warn(`[CodexRunner] loaded thread has no recoverable delegation carrier; forking once to preserve history: thread=${staleThreadId}`);
805
+ const forked = await appServer.threadFork(staleThreadId, projectPath, undefined, {
806
+ model: callModel,
807
+ effort: callEffort,
808
+ approvalPolicy: effectiveApprovalPolicy,
809
+ approvalsReviewer: 'user',
810
+ ...(executionSandbox.sandbox ? { sandbox: executionSandbox.sandbox } : {}),
811
+ config: threadOptions.config,
812
+ });
813
+ const forkedThreadId = forked.thread?.id;
814
+ if (!forkedThreadId)
815
+ throw new Error('Codex delegation-carrier recovery fork did not return a thread id');
816
+ threadId = forkedThreadId;
817
+ recoveredCarrier = delegationCarrierCandidate;
818
+ }
819
+ this.threadDelegationCarriers.set(threadId, recoveredCarrier);
820
+ await this.persistDelegationCarrier(sessionManager, sessionId, threadId, recoveredCarrier);
821
+ }
822
+ else if (knownDelegationCarrier) {
823
+ this.threadDelegationCarriers.set(threadId, knownDelegationCarrier);
824
+ await this.persistDelegationCarrier(sessionManager, sessionId, threadId, knownDelegationCarrier);
825
+ }
694
826
  agentSessionId = threadId;
695
827
  this.activeSessions.set(sessionId, threadId);
696
828
  this.threadProjectPaths.set(threadId, path.resolve(projectPath));
@@ -829,6 +961,7 @@ export class CodexRunner {
829
961
  const previousThreadId = this.activeSessions.get(sessionId);
830
962
  if (previousThreadId && previousThreadId !== agentSessionId) {
831
963
  this.threadProjectPaths.delete(previousThreadId);
964
+ this.threadDelegationCarriers.delete(previousThreadId);
832
965
  this.unbindExternalToolBoundary(previousThreadId);
833
966
  this.clearTrackedApprovalItemsForThread(previousThreadId);
834
967
  }
@@ -852,6 +985,7 @@ export class CodexRunner {
852
985
  this.cleanupSessionTempDir(sessionId);
853
986
  if (threadId) {
854
987
  this.threadProjectPaths.delete(threadId);
988
+ this.threadDelegationCarriers.delete(threadId);
855
989
  this.unbindExternalToolBoundary(threadId);
856
990
  }
857
991
  this.clearTrackedApprovalItemsForThread(threadId);
@@ -884,6 +1018,7 @@ export class CodexRunner {
884
1018
  this.chatModes.delete(sessionId);
885
1019
  this.cleanupSessionTempDir(sessionId);
886
1020
  this.threadProjectPaths.delete(threadId);
1021
+ this.threadDelegationCarriers.delete(threadId);
887
1022
  this.unbindExternalToolBoundary(threadId);
888
1023
  this.clearTrackedApprovalItemsForThread(threadId);
889
1024
  this.onSessionIdUpdate?.(sessionId, '');
@@ -906,15 +1041,18 @@ export class CodexRunner {
906
1041
  const executionSandbox = this.buildExecutionSandboxOptions(_sessionId, compactMode, _projectPath);
907
1042
  const capabilityConfig = await this.resolveCapabilityThreadConfig(_projectPath);
908
1043
  const externalToolConfig = await this.resolveExternalToolApprovalConfig(appServer, _projectPath, capabilityConfig, compactMode);
1044
+ const compactDelegationCarrier = this.threadDelegationCarriers.get(agentSessionId)
1045
+ ?? this.createDelegationCarrier();
909
1046
  await appServer.threadResume(agentSessionId, _projectPath, {
910
1047
  model: this.model,
911
1048
  effort: this.effort,
912
1049
  approvalPolicy: compactPolicy,
913
1050
  approvalsReviewer: 'user',
914
1051
  ...(executionSandbox.sandbox ? { sandbox: executionSandbox.sandbox } : {}),
915
- config: this.mergeThreadConfig(executionSandbox.permissionProfileConfig, capabilityConfig, externalToolConfig, this.buildEvolcoreShellEnvironmentConfig(_sessionId), this.buildLifecycleLockdownConfig()),
1052
+ config: this.mergeThreadConfig(executionSandbox.permissionProfileConfig, capabilityConfig, externalToolConfig, this.buildEvolcoreShellEnvironmentConfig(_sessionId, compactDelegationCarrier), this.buildLifecycleLockdownConfig()),
916
1053
  });
917
1054
  this.threadProjectPaths.set(agentSessionId, path.resolve(_projectPath));
1055
+ this.threadDelegationCarriers.set(agentSessionId, compactDelegationCarrier);
918
1056
  this.bindExternalToolBoundary(agentSessionId, externalToolConfig);
919
1057
  return await this.startAndWaitForCompact(appServer, agentSessionId);
920
1058
  }
@@ -1054,17 +1192,19 @@ export class CodexRunner {
1054
1192
  const capabilityConfig = await this.resolveCapabilityThreadConfig(projectPath);
1055
1193
  const appServer = this.getAppServerClient();
1056
1194
  const externalToolConfig = await this.resolveExternalToolApprovalConfig(appServer, projectPath, capabilityConfig, mode);
1195
+ const delegationCarrier = this.createDelegationCarrier();
1057
1196
  const response = await appServer.threadFork(agentSessionId, projectPath, title, {
1058
1197
  model: this.model,
1059
1198
  effort: this.effort,
1060
1199
  approvalPolicy: this.toApprovalPolicy(mode),
1061
1200
  approvalsReviewer: 'user',
1062
1201
  ...(executionSandbox.sandbox ? { sandbox: executionSandbox.sandbox } : {}),
1063
- config: this.mergeThreadConfig(executionSandbox.permissionProfileConfig, capabilityConfig, externalToolConfig, this.buildEvolcoreShellEnvironmentConfig(sessionKey), this.buildLifecycleLockdownConfig()),
1202
+ config: this.mergeThreadConfig(executionSandbox.permissionProfileConfig, capabilityConfig, externalToolConfig, this.buildEvolcoreShellEnvironmentConfig(sessionKey, delegationCarrier), this.buildLifecycleLockdownConfig()),
1064
1203
  });
1065
1204
  const forkedThreadId = response.thread?.id;
1066
1205
  if (!forkedThreadId)
1067
1206
  throw new Error('Codex fork did not return a thread id');
1207
+ this.threadDelegationCarriers.set(forkedThreadId, delegationCarrier);
1068
1208
  this.threadProjectPaths.set(forkedThreadId, path.resolve(projectPath));
1069
1209
  this.bindExternalToolBoundary(forkedThreadId, externalToolConfig);
1070
1210
  return forkedThreadId;
@@ -1227,6 +1367,16 @@ export class CodexRunner {
1227
1367
  logger.info(`[CodexRunner] app-server approval request id=${request.id} method=${request.method} session=${sessionKey} mode=${sessionMode} tool=${toolName} summary=${summary}`);
1228
1368
  try {
1229
1369
  const decision = await this.resolvePermissionDecision(sessionKey, toolName, toolInput, summary, reason, workspacePath, operationCwd);
1370
+ if (decision !== 'deny'
1371
+ && (request.method === 'item/commandExecution/requestApproval' || request.method === 'execCommandApproval')
1372
+ && this.approvedEvolcoreCommandArgv(toolInput)) {
1373
+ if (!this.armApprovedDelegationCommand(sessionKey, toolInput)) {
1374
+ // Approval policy and delegation authority are separate boundaries.
1375
+ // If no ticket can be armed, the CLI still fails closed at IPC token
1376
+ // validation instead of changing an otherwise valid tool decision.
1377
+ logger.warn(`[CodexRunner] EC command approved without a task-bound delegation ticket: session=${sessionKey}`);
1378
+ }
1379
+ }
1230
1380
  const response = this.toAppServerApprovalResponse(request.method, decision, toolInput, unattended);
1231
1381
  logger.info(`[CodexRunner] app-server approval response id=${request.id} method=${request.method} decision=${decision} response=${JSON.stringify(response)}`);
1232
1382
  return response;
@@ -2610,13 +2760,14 @@ export class CodexRunner {
2610
2760
  return undefined;
2611
2761
  return '临时文件必须使用环境变量 `$TMPDIR`。它指向当前 EvolCore 会话的受管临时工作区;不要直接使用 `/tmp`,也不要使用其他会话的临时目录。';
2612
2762
  }
2613
- buildEvolcoreShellEnvironmentConfig(sessionId) {
2763
+ buildEvolcoreShellEnvironmentConfig(sessionId, delegationCarrier) {
2614
2764
  const managedTempDir = this.ensureSessionTempDir(sessionId);
2615
2765
  return {
2616
2766
  shell_environment_policy: {
2617
2767
  set: {
2618
2768
  EVOLCORE_SESSION_ID: sessionId,
2619
2769
  EVOLCORE_HOME: resolveRoot(),
2770
+ ...(delegationCarrier ? { [AGENT_DELEGATION_TOKEN_ENV]: delegationCarrier } : {}),
2620
2771
  ...(managedTempDir ? { TMPDIR: managedTempDir } : {}),
2621
2772
  },
2622
2773
  },
@@ -2635,6 +2786,7 @@ export class CodexRunner {
2635
2786
  this.threadExternalToolFingerprints.clear();
2636
2787
  this.threadExternalToolBoundaries.clear();
2637
2788
  this.trackedApprovalItems.clear();
2789
+ this.threadDelegationCarriers.clear();
2638
2790
  this.pendingInterrupts.clear();
2639
2791
  this.permissionContexts.clear();
2640
2792
  this.chatModes.clear();
@@ -69,7 +69,7 @@ export function isOneMillionContextModel(model) {
69
69
  export function realContextWindowForModel(model) {
70
70
  return isOneMillionContextModel(model) ? 1000000 : 200000;
71
71
  }
72
- /** autoCompact trigger threshold: 180000 for all models (conservative for cache limits). */
72
+ /** autoCompact trigger threshold: 350000 for all models. */
73
73
  export function autoCompactWindowForModel(_model) {
74
- return 180000;
74
+ return 350000;
75
75
  }
@@ -2,6 +2,85 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { getAidStore, loadAid, SLOT } from './store.js';
4
4
  import { agentMdPath, aidLocalDir, resolveRoot } from '../../paths.js';
5
+ const displayNameCache = new Map();
6
+ const displayNamePending = new Map();
7
+ const displayNameRevision = new Map();
8
+ function normalizeAid(aid) {
9
+ const value = String(aid || '').trim();
10
+ return value.startsWith('@') ? value.slice(1) : value;
11
+ }
12
+ /** Extract the display name from an agent.md YAML frontmatter block. */
13
+ export function parseAgentDisplayName(content) {
14
+ const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)?.[1];
15
+ if (!frontmatter)
16
+ return undefined;
17
+ const name = frontmatter.match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1]?.trim();
18
+ return name || undefined;
19
+ }
20
+ function readLocalAgentDisplayName(aid) {
21
+ try {
22
+ return parseAgentDisplayName(fs.readFileSync(agentMdPath(aid), 'utf-8'));
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ }
28
+ /**
29
+ * Resolve an agent.md display name without blocking on the network.
30
+ * Local content is returned immediately; a missing value starts one shared
31
+ * background refresh and remains undefined until a later lookup.
32
+ */
33
+ export function resolveAgentDisplayName(aid) {
34
+ const normalizedAid = normalizeAid(aid);
35
+ if (!normalizedAid)
36
+ return undefined;
37
+ const cached = displayNameCache.get(normalizedAid);
38
+ if (cached)
39
+ return cached;
40
+ const localName = readLocalAgentDisplayName(normalizedAid);
41
+ if (localName) {
42
+ displayNameCache.set(normalizedAid, localName);
43
+ return localName;
44
+ }
45
+ void refreshAgentDisplayName(normalizedAid);
46
+ return undefined;
47
+ }
48
+ /** Fetch agent.md once and update the shared display-name cache. */
49
+ export function refreshAgentDisplayName(aid) {
50
+ const normalizedAid = normalizeAid(aid);
51
+ if (!normalizedAid)
52
+ return Promise.resolve(undefined);
53
+ const existing = displayNamePending.get(normalizedAid);
54
+ if (existing)
55
+ return existing;
56
+ const revision = displayNameRevision.get(normalizedAid) ?? 0;
57
+ let pending;
58
+ pending = agentmdGet(normalizedAid)
59
+ .then(content => {
60
+ const name = typeof content === 'string' ? parseAgentDisplayName(content) : undefined;
61
+ if ((displayNameRevision.get(normalizedAid) ?? 0) !== revision)
62
+ return undefined;
63
+ if (name)
64
+ displayNameCache.set(normalizedAid, name);
65
+ return name;
66
+ })
67
+ .catch(() => undefined)
68
+ .finally(() => {
69
+ if (displayNamePending.get(normalizedAid) === pending) {
70
+ displayNamePending.delete(normalizedAid);
71
+ }
72
+ });
73
+ displayNamePending.set(normalizedAid, pending);
74
+ return pending;
75
+ }
76
+ export function invalidateAgentDisplayName(aid) {
77
+ const normalizedAid = normalizeAid(aid);
78
+ if (!normalizedAid)
79
+ return;
80
+ displayNameCache.delete(normalizedAid);
81
+ displayNamePending.delete(normalizedAid);
82
+ displayNameRevision.set(normalizedAid, (displayNameRevision.get(normalizedAid) ?? 0) + 1);
83
+ }
5
84
  export function buildInitialAgentMd(opts) {
6
85
  const agentName = opts.aid.split('.')[0];
7
86
  const agentType = opts.type || 'ai';
@@ -0,0 +1,29 @@
1
+ export const AUN_KEYSTORE_ENCRYPTION_SEED = 'evol';
2
+ export class AunEncryptionSeedPolicyError extends Error {
3
+ code = 'CUSTOM_AUN_ENCRYPTION_SEED_FORBIDDEN';
4
+ constructor(sources) {
5
+ super(`检测到自定义 AUN encryptionSeed(来源:${sources.join('、')})。` +
6
+ 'AUN encryptionSeed 由 EvolCore 内置管理,不允许用户自行设置。' +
7
+ '请停止操作并联系管理人员处理后再启动 EvolCore。');
8
+ this.name = 'AunEncryptionSeedPolicyError';
9
+ }
10
+ }
11
+ /**
12
+ * Reject values that would make an existing AUN keystore incompatible with
13
+ * EvolCore's fixed application seed. Explicit legacy copies of the fixed value
14
+ * remain readable so users can remove the obsolete setting without migration.
15
+ */
16
+ export function assertAunEncryptionSeedPolicy(configuredValue, environmentValue) {
17
+ const customSources = [];
18
+ if (configuredValue !== undefined
19
+ && configuredValue !== null
20
+ && configuredValue !== AUN_KEYSTORE_ENCRYPTION_SEED) {
21
+ customSources.push('daemon.json aun.encryptionSeed');
22
+ }
23
+ if (environmentValue !== undefined
24
+ && environmentValue !== AUN_KEYSTORE_ENCRYPTION_SEED) {
25
+ customSources.push('环境变量 AUN_ENCRYPTION_SEED');
26
+ }
27
+ if (customSources.length > 0)
28
+ throw new AunEncryptionSeedPolicyError(customSources);
29
+ }
@@ -1,4 +1,4 @@
1
1
  export { isValidAid, aidList, aidListVerified, aidCreate, aidShow, aidDelete, aidLookup, verifySignAbility, probePkiRecoverability, appendAidLifecycle, readAidLifecycle } from './identity.js';
2
- export { buildInitialAgentMd, agentmdGet, agentmdPut, agentmdSync, stripAgentMdSignature, updateAgentMdFrontmatterAvatar, updateAgentMdFrontmatterName } from './agentmd.js';
2
+ export { buildInitialAgentMd, agentmdGet, agentmdPut, agentmdSync, invalidateAgentDisplayName, parseAgentDisplayName, refreshAgentDisplayName, resolveAgentDisplayName, stripAgentMdSignature, updateAgentMdFrontmatterAvatar, updateAgentMdFrontmatterName, } from './agentmd.js';
3
3
  export { MIN_AUN_CORE_SDK, AUN_CORE_SDK_PKG, isAunSdkVersionOk, resolveAunCoreSdkPkg, ensureAunSdk, isAunSdkReady, downloadCaRoot, suppressSdkLogs, } from './client.js';
4
4
  export { getAidStore, loadClient, loadAid, AidLoadError, SLOT } from './store.js';
@@ -1,5 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import { AUN_KEYSTORE_ENCRYPTION_SEED } from './encryption-seed-policy.js';
3
4
  /**
4
5
  * Slot 命名约定(基于 fastaun 0.4.3 隔离键语义)。
5
6
  *
@@ -39,16 +40,12 @@ export class AidLoadError extends Error {
39
40
  */
40
41
  export async function getAidStore(opts) {
41
42
  const { aunPath: defaultAunPath } = await import('../../paths.js');
42
- const { loadDaemonConfig } = await import('../../config-store.js');
43
43
  const { AIDStore } = await import('@agentunion/fastaun');
44
44
  const aunPath = opts.aunPath ?? defaultAunPath();
45
- const encryptionSeed = loadDaemonConfig().aun?.encryptionSeed
46
- ?? process.env.AUN_ENCRYPTION_SEED
47
- ?? 'evol';
48
45
  const caCertPath = path.join(aunPath, 'CA', 'root', 'root.crt');
49
46
  const storeOpts = {
50
47
  aunPath,
51
- encryptionSeed,
48
+ encryptionSeed: AUN_KEYSTORE_ENCRYPTION_SEED,
52
49
  slotId: opts.slotId,
53
50
  debug: opts.debug ?? false,
54
51
  };