evolcore 0.0.35 → 0.0.36

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 +31 -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 +134 -48
  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 +109 -34
  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
@@ -1,9 +1,9 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { loadDaemonConfig } from '../config-store.js';
4
- import { parseAgentWelcomeArgs, parseInitializationArgs, resolveInitializationPlan, readInitializationPersona, } from '../cli/agent-create.js';
3
+ import { parseAgentWelcomeArgs, parseInitializationArgs, resolveAgentCreateMode, resolveInitializationPlan, readInitializationPersona, } from '../cli/agent-create.js';
5
4
  import { trustedExecutionIdentityInput } from './auth/agent-delegation.js';
6
- import { authorizeOperation, buildAuthSubject } from './auth/auth-gateway.js';
5
+ import { authorizeFreshOperation as authorizeOperation, buildAuthSubject } from './auth/auth-gateway.js';
6
+ import { getDaemonOwners } from './ownership/owner-resolver.js';
7
7
  import { authorizationDenialData } from './auth/authorization-denial.js';
8
8
  import { parsePeerKey } from './relation/peer-identity.js';
9
9
  import { isHClassPath, isLClassPath, isSameOrDescendant } from './permission/protected-paths.js';
@@ -64,12 +64,21 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
64
64
  : sub === 'reload'
65
65
  ? (argv[2] && !argv[2].startsWith('--') ? argv[2] : undefined)
66
66
  : (argv[2] && !argv[2].startsWith('--') ? argv[2] : undefined);
67
+ let createMode;
68
+ if (sub === 'new') {
69
+ try {
70
+ createMode = resolveAgentCreateMode(argv.slice(1));
71
+ }
72
+ catch (error) {
73
+ return fail('INVALID_ARGS', error.message);
74
+ }
75
+ }
67
76
  const operation = sub === 'list' ? 'agent.list'
68
77
  : sub === 'show' ? 'agent.show'
69
78
  : sub === 'persona' ? 'agent.persona'
70
79
  : sub === 'welcome' ? 'agent.welcome'
71
80
  : sub === 'rename' ? 'agent.rename'
72
- : sub === 'initialize' || (sub === 'new' && argv.includes('--initialize')) ? 'agent.initialize'
81
+ : sub === 'initialize' || createMode === 'initialize' ? 'agent.initialize'
73
82
  : sub === 'new' ? 'agent.create'
74
83
  : sub === 'enable' ? 'agent.enable'
75
84
  : sub === 'disable' ? 'agent.disable'
@@ -77,9 +86,6 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
77
86
  : sub === 'delete' ? 'agent.delete' : undefined;
78
87
  if (!operation)
79
88
  return fail('INVALID_ARGS', `unsupported managed agent command: ${sub}`);
80
- if (sub === 'new' && !argv.includes('--non-interactive') && !argv.includes('--initialize')) {
81
- return fail('MANAGED_NON_INTERACTIVE_REQUIRED', 'interactive Agent creation is unavailable in a managed task', 'MANAGED_NON_INTERACTIVE_REQUIRED');
82
- }
83
89
  if (sub === 'delete' && argv.includes('--purge') && !argv.includes('--confirm-purge')) {
84
90
  return fail('PURGE_CONFIRMATION_REQUIRED', 'agent delete --purge requires explicit --confirm-purge', 'PURGE_CONFIRMATION_REQUIRED');
85
91
  }
@@ -98,10 +104,10 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
98
104
  channelId: conversationId,
99
105
  chatType: delegation.grant.chatType,
100
106
  conversationId,
101
- processOwners: loadDaemonConfig().owners ?? [],
107
+ processOwners: getDaemonOwners(),
102
108
  ...trustedExecutionIdentityInput(delegation.grant),
103
109
  });
104
- const decision = authorizeOperation({
110
+ const decision = await authorizeOperation({
105
111
  source: 'agent-tool',
106
112
  subject,
107
113
  intent: {
@@ -256,15 +262,15 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
256
262
  agentInitializationService.resume(parsed.aid, actor);
257
263
  return { ok: true, result: await agentInitializationService.wait(parsed.aid, parsed.wait) };
258
264
  }
259
- const createPermission = authorizeOperation({
265
+ const createPermission = await authorizeOperation({
260
266
  source: 'agent-tool', subject,
261
267
  intent: { operation: 'agent.create', scope: 'control', source: 'agent-tool', args: { aid: parsed.aid } },
262
268
  auditMetadata: { taskId: delegation.grant.taskId, messageId: delegation.grant.messageId },
263
269
  });
264
270
  if (!createPermission.allow)
265
271
  return fail(createPermission.code, createPermission.reason);
266
- if (!argv.includes('--owner') && !argv.includes('--owners'))
267
- return fail('INITIALIZE_OWNER_MISMATCH', 'Explicit Owner required');
272
+ if (!argv.includes('--owner'))
273
+ return fail('INITIALIZE_OWNER_MISMATCH', 'Explicit --owner is required');
268
274
  const plan = resolveInitializationPlan(argv.slice(1), {
269
275
  projectCwd: taskSession.projectPath, tempDir: delegation.grant.sessionRuntimeDir,
270
276
  });
@@ -284,8 +290,6 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
284
290
  }
285
291
  plan.avatarFile = real;
286
292
  }
287
- if (!plan.owners.includes(actor))
288
- return fail('INITIALIZE_OWNER_MISMATCH', 'The authenticated requesting Owner must be retained');
289
293
  const result = agentInitializationService.start(plan, actor, parsed.dryRun);
290
294
  return { ok: true, result: parsed.dryRun ? result : await agentInitializationService.wait(parsed.aid, parsed.wait) };
291
295
  }
@@ -305,6 +309,8 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
305
309
  catch (error) {
306
310
  return fail('INVALID_ARGS', error instanceof Error ? error.message : String(error));
307
311
  }
312
+ if (argv.includes('--handoff'))
313
+ return fail('INVALID_ARGS', 'Agent creation --handoff has been removed');
308
314
  let tags;
309
315
  try {
310
316
  tags = parseAgentTags(value('--tags'));
@@ -317,7 +323,6 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
317
323
  project: value('--project'),
318
324
  baseagent: value('--baseagent'),
319
325
  ...ownerOptions,
320
- handoff: value('--handoff'),
321
326
  name: value('--name'),
322
327
  description: value('--description'),
323
328
  type: value('--type'),
@@ -329,56 +334,13 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
329
334
  const validationError = validateAgentCreatePlan(plan);
330
335
  if (validationError)
331
336
  return fail('INVALID_ARGS', validationError);
332
- if (argv.includes('--handoff')) {
333
- if (!delegation.grant.messageId)
334
- return fail('INVALID_ARGS', '--handoff requires an originating message');
335
- if (!plan.handoff?.trim())
336
- return fail('INVALID_ARGS', '--handoff requires non-empty context');
337
- if ((plan.owners?.[0] ?? plan.owner) !== taskAid) {
338
- return fail('INVALID_ARGS', '--handoff requires the creating Agent to be the first Owner');
339
- }
340
- if (plan.configExists)
341
- return fail('INVALID_ARGS', '--handoff requires a new Agent');
342
- }
343
337
  if (argv.includes('--dry-run'))
344
338
  return { ok: true, result: { ok: true, dryRun: true, plan } };
345
- let handoffId;
346
- if (plan.handoff) {
347
- try {
348
- const created = await handoffRuntime.createOutbound({
349
- selfAid: taskAid,
350
- to: aid,
351
- originSessionId: sessionId,
352
- originMessageId: delegation.grant.messageId,
353
- payload: { type: 'text', text: plan.handoff },
354
- encrypt: true,
355
- targetChatType: 'private',
356
- explicitReturnPolicy: 'required',
357
- contextOnly: true,
358
- originAuthorization: {
359
- actorId: delegation.grant.actorId,
360
- channelKey: delegation.grant.channel,
361
- channelType: delegation.grant.channelType,
362
- chatType: delegation.grant.chatType,
363
- peerKey: delegation.grant.peerKey,
364
- approvalInteractionPolicy: delegation.grant.approvalInteractionPolicy,
365
- },
366
- causation: responseEngine.getTaskRuntimeContext(sessionId)?.causation,
367
- });
368
- handoffId = created.handoff?.handoff_id;
369
- if (!handoffId)
370
- return fail('INVALID_ARGS', '--handoff requires a different target session');
371
- }
372
- catch (error) {
373
- return fail('HANDOFF_PREPARE_FAILED', error instanceof Error ? error.message : String(error));
374
- }
375
- }
376
339
  const createOptions = {
377
340
  aid: plan.aid,
378
341
  project: plan.project,
379
342
  baseagent: plan.baseagent,
380
343
  owner: plan.owner,
381
- owners: plan.owners,
382
344
  name: plan.name,
383
345
  description: plan.description,
384
346
  type: plan.type,
@@ -416,15 +378,11 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
416
378
  ? await agentApplicationService.create(createOptions)
417
379
  : await agentCreateNonInteractive(createOptions);
418
380
  if (!result.ok) {
419
- if (handoffId)
420
- handoffRuntime.store.discard(taskAid, handoffId, 'operation_failed');
421
381
  return fail(result.code ?? 'INTERNAL', result.error);
422
382
  }
423
- return { ok: true, result: { ...result, configPath: '[redacted]', ...(handoffId ? { handoff_id: handoffId } : {}) } };
383
+ return { ok: true, result: { ...result, configPath: '[redacted]' } };
424
384
  }
425
385
  catch (error) {
426
- if (handoffId)
427
- handoffRuntime.store.discard(taskAid, handoffId, 'operation_failed');
428
386
  return fail('INTERNAL', error instanceof Error ? error.message : String(error));
429
387
  }
430
388
  }
@@ -8,7 +8,7 @@ import { logger } from '../utils/logger.js';
8
8
  import fs from 'fs';
9
9
  import path from 'path';
10
10
  import { resolvePaths } from '../paths.js';
11
- import { loadDaemonConfig } from '../config-store.js';
11
+ import { ownerResolver, OWNERSHIP_AUTHORIZATION_MAX_AGE_MS } from './ownership/owner-resolver.js';
12
12
  import { AgentInitializationService, readInitialization } from '../cli/agent-create.js';
13
13
  export function loadStartupAgentRegistry(agentRegistry) {
14
14
  agentRegistry.loadAll();
@@ -216,11 +216,22 @@ export function createAgentInitializationRuntime({ agentRegistry, agentMap, chan
216
216
  && instance.channel.getAidState?.()?.aid === aid
217
217
  && instance.channel.getAidState?.()?.status === 'connected');
218
218
  return new AgentInitializationService({
219
- register: async (aid) => {
219
+ register: async (aid, ownerAid) => {
220
220
  const { aidCreate } = await import('../aun/aid/identity.js');
221
- const created = await aidCreate(aid);
221
+ // Do not leave a pending SDK Ownership intent while initialization is
222
+ // still publishing agent.md and its optional avatar.
223
+ const created = await aidCreate(aid, { type: 'agent' });
222
224
  await created.client.close();
223
225
  },
226
+ bindOwner: async (aid, ownerAid) => {
227
+ await ownerResolver.bind(aid, ownerAid);
228
+ },
229
+ checkOwner: async (aid, expectedOwnerAid) => {
230
+ const view = await ownerResolver.refresh(aid, { maxAgeMs: OWNERSHIP_AUTHORIZATION_MAX_AGE_MS });
231
+ if (view.consensusStatus === 'consensus')
232
+ return view.ownerAid === expectedOwnerAid ? 'confirmed' : 'conflict';
233
+ return 'pending';
234
+ },
224
235
  publish: async (aid, content) => {
225
236
  const { agentmdPut } = await import('../aun/aid/agentmd.js');
226
237
  await agentmdPut(content, { aid });
@@ -231,7 +242,8 @@ export function createAgentInitializationRuntime({ agentRegistry, agentMap, chan
231
242
  return { content: card.content, verified: card.verification.status === 'verified' };
232
243
  },
233
244
  hotLoad: async (aid) => {
234
- await hotLoad(aid);
245
+ if (!isConnected(aid))
246
+ await hotLoad(aid);
235
247
  const until = Date.now() + 20_000;
236
248
  while (Date.now() < until) {
237
249
  if (isConnected(aid))
@@ -241,7 +253,6 @@ export function createAgentInitializationRuntime({ agentRegistry, agentMap, chan
241
253
  throw Object.assign(new Error('AUN connection unavailable'), { code: 'INITIALIZE_CHANNEL_UNAVAILABLE' });
242
254
  },
243
255
  activate,
244
- authorize: actor => actor === 'local-control' || (loadDaemonConfig().owners ?? []).includes(actor),
245
256
  usable: aid => {
246
257
  const agent = agentRegistry.get(aid);
247
258
  return !!agent && agent.config.enabled !== false && agentMap.has(`${aid}::${agent.baseagent}`) && isConnected(aid);
@@ -1,6 +1,7 @@
1
1
  import { assertAgentPolicyAuthorization } from '../role/runtime-policy.js';
2
2
  import crypto from 'crypto';
3
3
  import { authorizeOperation, buildAuthSubject } from './auth-gateway.js';
4
+ import { refreshOwnershipForAuthorization } from '../ownership/owner-resolver.js';
4
5
  import { formatPeerKey, parsePeerKey } from '../relation/peer-identity.js';
5
6
  import { logger } from '../../utils/logger.js';
6
7
  import { canonicalizeEvolcoreCommandArgv } from '../permission/ec-command-parser.js';
@@ -10,6 +11,7 @@ import { DelegationDiagnostics, } from './delegation-diagnostics.js';
10
11
  export const AGENT_DELEGATION_TOKEN_ENV = 'EVOLCORE_DELEGATION_TOKEN';
11
12
  export const CODEX_SESSION_TOKEN_ENV = 'EVOLCORE_CODEX_SESSION_TOKEN';
12
13
  export const AGENT_DELEGATION_COMMAND_TTL_MS = 5 * 60_000;
14
+ const INTERNAL_DELEGATION_TTL_MS = 30_000;
13
15
  /**
14
16
  * Resolve the credential carried by an EC CLI process. A live task token has
15
17
  * priority; persistent Codex threads fall back to their session credential.
@@ -28,6 +30,8 @@ export class AgentDelegationRegistry {
28
30
  activeHashBySession = new Map();
29
31
  sessionTokenHashesBySession = new Map();
30
32
  approvedCommands = new Map();
33
+ approvedCommandBindings = new Map();
34
+ internalGrants = new Map();
31
35
  debugRegistry = new DebugDelegationRegistry();
32
36
  constructor() {
33
37
  this.diagnostics = new DelegationDiagnostics(this.runtimeEpoch);
@@ -57,6 +61,28 @@ export class AgentDelegationRegistry {
57
61
  });
58
62
  return token;
59
63
  }
64
+ /**
65
+ * Issue a short-lived in-process grant for a daemon-owned adapter handoff.
66
+ * The returned bearer is never placed in an environment, prompt, or IPC
67
+ * payload. It is deliberately separate from the active task grant so an
68
+ * adapter operation cannot revoke the credentials used by the base agent.
69
+ */
70
+ issueInternal(input, ttlMs = INTERNAL_DELEGATION_TTL_MS) {
71
+ if (input.executionIdentity && !isValidFullAccessExecutionIdentity(input.executionIdentity)) {
72
+ throw new Error('invalid fullaccess task execution identity');
73
+ }
74
+ const token = crypto.randomBytes(32).toString('base64url');
75
+ const tokenHash = hashDelegationToken(token);
76
+ const grant = { ...input, tokenHash };
77
+ const expiresAt = Date.now() + Math.max(1, Math.min(ttlMs, INTERNAL_DELEGATION_TTL_MS));
78
+ this.grantsByHash.set(tokenHash, grant);
79
+ this.internalGrants.set(tokenHash, { grant, expiresAt });
80
+ const release = () => {
81
+ this.internalGrants.delete(tokenHash);
82
+ this.grantsByHash.delete(tokenHash);
83
+ };
84
+ return { token, release };
85
+ }
60
86
  registerSessionCredential(input) {
61
87
  const activeTokenHash = this.activeHashBySession.get(input.sessionId);
62
88
  const grant = activeTokenHash ? this.grantsByHash.get(activeTokenHash) : undefined;
@@ -102,6 +128,14 @@ export class AgentDelegationRegistry {
102
128
  }
103
129
  const tokenHash = hashDelegationToken(token);
104
130
  const grant = this.grantsByHash.get(tokenHash);
131
+ const internal = this.internalGrants.get(tokenHash);
132
+ if (internal && internal.expiresAt <= Date.now()) {
133
+ this.internalGrants.delete(tokenHash);
134
+ this.grantsByHash.delete(tokenHash);
135
+ }
136
+ if (internal && internal.expiresAt > Date.now() && internal.grant.sessionId === sessionId) {
137
+ return { ok: true, grant: internal.grant };
138
+ }
105
139
  if (grant
106
140
  && grant.sessionId === sessionId
107
141
  && this.activeHashBySession.get(sessionId) === tokenHash) {
@@ -134,7 +168,8 @@ export class AgentDelegationRegistry {
134
168
  if (!grant || grant.taskId !== input.taskId)
135
169
  return false;
136
170
  const sessionToken = input.sessionToken ?? input.carrierToken;
137
- if (!sessionToken || !isDelegationCommandHash(input.commandHash))
171
+ if (!sessionToken || !isDelegationCommandHash(input.commandHash)
172
+ || !input.approvalId || input.approvalId.length > 512 || input.approvalId.includes('\0'))
138
173
  return false;
139
174
  this.deleteExpiredApprovedCommands();
140
175
  const sessionTokenHash = hashDelegationToken(sessionToken);
@@ -142,26 +177,74 @@ export class AgentDelegationRegistry {
142
177
  if (credential?.taskId !== input.taskId || credential.runtimeEpoch !== this.runtimeEpoch) {
143
178
  this.registerSessionCredential({ sessionToken, sessionId: input.sessionId, taskId: input.taskId });
144
179
  }
180
+ const bindingKey = approvedCommandBindingKey(sessionTokenHash, input.sessionId, input.approvalId);
181
+ const existingBinding = this.approvedCommandBindings.get(bindingKey);
182
+ if (existingBinding) {
183
+ if (existingBinding.activeTokenHash !== activeTokenHash
184
+ || existingBinding.taskId !== input.taskId
185
+ || existingBinding.expiresAt <= Date.now()) {
186
+ this.approvedCommandBindings.delete(bindingKey);
187
+ }
188
+ else {
189
+ return existingBinding.commandHash === input.commandHash;
190
+ }
191
+ }
145
192
  const key = approvedCommandKey(sessionTokenHash, input.sessionId, input.commandHash);
146
193
  const existing = this.approvedCommands.get(key);
147
194
  const ttlMs = Math.max(1, Math.min(input.ttlMs ?? AGENT_DELEGATION_COMMAND_TTL_MS, AGENT_DELEGATION_COMMAND_TTL_MS));
148
- this.approvedCommands.set(key, {
195
+ const expiresAt = Date.now() + ttlMs;
196
+ const command = existing && existing.activeTokenHash === activeTokenHash
197
+ && existing.taskId === input.taskId
198
+ ? existing
199
+ : {
200
+ sessionTokenHash,
201
+ sessionId: input.sessionId,
202
+ taskId: input.taskId,
203
+ activeTokenHash,
204
+ commandHash: input.commandHash,
205
+ approvalKeys: new Set(),
206
+ };
207
+ command.approvalKeys.add(bindingKey);
208
+ this.approvedCommands.set(key, command);
209
+ this.approvedCommandBindings.set(bindingKey, {
149
210
  sessionTokenHash,
150
211
  sessionId: input.sessionId,
151
212
  taskId: input.taskId,
213
+ approvalId: input.approvalId,
152
214
  activeTokenHash,
153
215
  commandHash: input.commandHash,
154
- expiresAt: Date.now() + ttlMs,
155
- remainingUses: existing
156
- && existing.activeTokenHash === activeTokenHash
157
- && existing.expiresAt > Date.now()
158
- ? existing.remainingUses + 1
159
- : 1,
216
+ expiresAt,
217
+ consumed: false,
160
218
  });
161
- this.recordApproval('approval.issued', this.approvedCommands.get(key), 'active');
219
+ this.recordApproval('approval.issued', command, 'active');
162
220
  return true;
163
221
  }
222
+ /** Remove an approval that its provider has completed without consuming. */
223
+ revokeApprovedCommand(sessionId, taskId, approvalId) {
224
+ for (const [bindingKey, binding] of this.approvedCommandBindings) {
225
+ if (binding.sessionId !== sessionId || binding.taskId !== taskId || binding.approvalId !== approvalId)
226
+ continue;
227
+ const commandKey = approvedCommandKey(binding.sessionTokenHash, binding.sessionId, binding.commandHash);
228
+ const command = this.approvedCommands.get(commandKey);
229
+ // Record before removing the binding so the diagnostic keeps its real expiry.
230
+ if (command && command.approvalKeys.size === 1 && !binding.consumed) {
231
+ this.recordApproval('approval.revoked', command, 'revoked', 'tool_completed');
232
+ }
233
+ this.approvedCommandBindings.delete(bindingKey);
234
+ if (!command)
235
+ continue;
236
+ command.approvalKeys.delete(bindingKey);
237
+ if (command.approvalKeys.size === 0)
238
+ this.approvedCommands.delete(commandKey);
239
+ }
240
+ }
164
241
  revokeTask(sessionId, taskId, reason = 'revoked') {
242
+ for (const [hash, internal] of this.internalGrants) {
243
+ if (internal.grant.sessionId === sessionId && internal.grant.taskId === taskId) {
244
+ this.internalGrants.delete(hash);
245
+ this.grantsByHash.delete(hash);
246
+ }
247
+ }
165
248
  const tokenHash = this.activeHashBySession.get(sessionId);
166
249
  if (!tokenHash)
167
250
  return;
@@ -171,6 +254,12 @@ export class AgentDelegationRegistry {
171
254
  this.revokeSession(sessionId, reason);
172
255
  }
173
256
  revokeSession(sessionId, reason = 'revoked') {
257
+ for (const [hash, internal] of this.internalGrants) {
258
+ if (internal.grant.sessionId === sessionId) {
259
+ this.internalGrants.delete(hash);
260
+ this.grantsByHash.delete(hash);
261
+ }
262
+ }
174
263
  const tokenHash = this.activeHashBySession.get(sessionId);
175
264
  for (const sessionTokenHash of this.sessionTokenHashesBySession.get(sessionId) ?? []) {
176
265
  const credential = this.diagnostics.find('session', sessionTokenHash, sessionId);
@@ -202,11 +291,6 @@ export class AgentDelegationRegistry {
202
291
  const approved = this.approvedCommands.get(key);
203
292
  if (!approved)
204
293
  return undefined;
205
- if (approved.expiresAt <= Date.now()) {
206
- this.recordApproval('approval.expired', approved, 'expired');
207
- this.approvedCommands.delete(key);
208
- return undefined;
209
- }
210
294
  const activeTokenHash = this.activeHashBySession.get(sessionId);
211
295
  const grant = activeTokenHash ? this.grantsByHash.get(activeTokenHash) : undefined;
212
296
  if (!grant
@@ -216,20 +300,55 @@ export class AgentDelegationRegistry {
216
300
  this.approvedCommands.delete(key);
217
301
  return undefined;
218
302
  }
303
+ const now = Date.now();
304
+ const binding = [...approved.approvalKeys]
305
+ .map(approvalKey => this.approvedCommandBindings.get(approvalKey))
306
+ .find(candidate => candidate
307
+ && !candidate.consumed
308
+ && candidate.expiresAt > now
309
+ && candidate.activeTokenHash === activeTokenHash
310
+ && candidate.taskId === grant.taskId
311
+ && candidate.commandHash === commandHash);
312
+ if (!binding) {
313
+ const pending = [...approved.approvalKeys]
314
+ .map(approvalKey => this.approvedCommandBindings.get(approvalKey))
315
+ .filter(candidate => candidate && !candidate.consumed);
316
+ if (pending.length > 0 && pending.every(candidate => candidate.expiresAt <= now)) {
317
+ this.recordApproval('approval.expired', approved, 'expired');
318
+ }
319
+ return undefined;
320
+ }
219
321
  this.diagnostics.accepted(sessionId, sessionTokenHash, commandHash);
220
322
  if (consume) {
221
- approved.remainingUses--;
222
- this.recordApproval('approval.consumed', approved, approved.remainingUses <= 0 ? 'consumed' : 'active');
223
- if (approved.remainingUses <= 0)
224
- this.approvedCommands.delete(key);
323
+ binding.consumed = true;
324
+ const remainingUses = this.approvalStats(approved).remainingUses;
325
+ this.recordApproval('approval.consumed', approved, remainingUses <= 0 ? 'consumed' : 'active');
225
326
  }
226
327
  return grant;
227
328
  }
228
329
  deleteExpiredApprovedCommands() {
229
330
  const now = Date.now();
331
+ const expiredUnconsumed = new Set();
332
+ for (const [key, binding] of this.approvedCommandBindings) {
333
+ if (binding.expiresAt > now)
334
+ continue;
335
+ if (!binding.consumed)
336
+ expiredUnconsumed.add(key);
337
+ this.approvedCommandBindings.delete(key);
338
+ }
230
339
  for (const [key, approved] of this.approvedCommands) {
231
- if (approved.expiresAt <= now) {
232
- this.recordApproval('approval.expired', approved, 'expired');
340
+ let expiredPending = false;
341
+ for (const approvalKey of approved.approvalKeys) {
342
+ if (this.approvedCommandBindings.has(approvalKey))
343
+ continue;
344
+ if (expiredUnconsumed.has(approvalKey))
345
+ expiredPending = true;
346
+ approved.approvalKeys.delete(approvalKey);
347
+ }
348
+ if (approved.approvalKeys.size === 0) {
349
+ // Fully consumed commands already recorded 'consumed'; keep that cause.
350
+ if (expiredPending)
351
+ this.recordApproval('approval.expired', approved, 'expired');
233
352
  this.approvedCommands.delete(key);
234
353
  }
235
354
  }
@@ -241,16 +360,35 @@ export class AgentDelegationRegistry {
241
360
  this.approvedCommands.delete(key);
242
361
  }
243
362
  }
363
+ for (const [key, binding] of this.approvedCommandBindings) {
364
+ if (binding.sessionId === sessionId)
365
+ this.approvedCommandBindings.delete(key);
366
+ }
244
367
  }
245
368
  recordApproval(event, approved, status, revocationReason) {
246
369
  this.diagnostics.put(event, {
247
370
  kind: 'approval', tokenHash: approved.sessionTokenHash, sessionId: approved.sessionId,
248
371
  taskId: approved.taskId, runtimeEpoch: this.runtimeEpoch, status, credentialType: 'codex-session',
249
372
  activeTokenHash: approved.activeTokenHash, commandHash: approved.commandHash,
250
- expiresAt: approved.expiresAt, remainingUses: approved.remainingUses,
373
+ ...this.approvalStats(approved),
251
374
  ...(revocationReason ? { revocationReason } : {}),
252
375
  });
253
376
  }
377
+ /** Aggregate per-approvalId bindings into the command-level diagnostic shape. */
378
+ approvalStats(approved) {
379
+ const now = Date.now();
380
+ let expiresAt = 0;
381
+ let remainingUses = 0;
382
+ for (const approvalKey of approved.approvalKeys) {
383
+ const binding = this.approvedCommandBindings.get(approvalKey);
384
+ if (!binding)
385
+ continue;
386
+ expiresAt = Math.max(expiresAt, binding.expiresAt);
387
+ if (!binding.consumed && binding.expiresAt > now)
388
+ remainingUses++;
389
+ }
390
+ return { expiresAt: expiresAt || now, remainingUses };
391
+ }
254
392
  recordDiagnosticFailure(cause, sessionId, token, commandHash) {
255
393
  const tokenHash = token ? hashDelegationToken(token) : undefined;
256
394
  let diagnosticCause = cause;
@@ -264,13 +402,30 @@ export class AgentDelegationRegistry {
264
402
  this.diagnostics.close();
265
403
  }
266
404
  }
267
- export function authorizeDelegatedAunMsgSend(registry, input) {
268
- if (!input.sessionId) {
405
+ export async function authorizeDelegatedAunMsgSend(registry, input) {
406
+ if (!input.sessionId && !input.isSystemEvent) {
269
407
  return { ok: false, code: 'DELEGATION_REQUIRED', reason: 'Origin session is required' };
270
408
  }
409
+ // 系统事件豁免:不校验 delegation token,直接通过,但不构造 grant(调用方不应依赖它)。
410
+ if (input.isSystemEvent) {
411
+ return { ok: true, grant: null };
412
+ }
413
+ if (!input.sessionId) {
414
+ throw new Error('[agent-delegation] Unreachable: sessionId checked earlier');
415
+ }
271
416
  const validation = registry.validate(input.delegationToken, input.sessionId, input.delegationCommandHash);
272
- if (!validation.ok)
273
- return validation;
417
+ if (!validation.ok) {
418
+ return {
419
+ ok: false,
420
+ code: validation.code,
421
+ reason: validation.reason,
422
+ detailCode: delegationFailureDetailCode(validation.diagnosticCause),
423
+ retryable: false,
424
+ sent: false,
425
+ diagnosticCause: validation.diagnosticCause,
426
+ ...(validation.diagnosticEventId ? { diagnosticEventId: validation.diagnosticEventId } : {}),
427
+ };
428
+ }
274
429
  const grant = validation.grant;
275
430
  if (grant.selfAid !== input.aid && !hasTrustedFullAccessDelegation(grant)) {
276
431
  return { ok: false, code: 'INVALID_DELEGATION', reason: 'Delegation self agent does not match sender' };
@@ -285,6 +440,7 @@ export function authorizeDelegatedAunMsgSend(registry, input) {
285
440
  catch {
286
441
  return { ok: false, code: 'INVALID_DELEGATION', reason: 'Delegation relation key is invalid' };
287
442
  }
443
+ await refreshOwnershipForAuthorization(grant.selfAid);
288
444
  const builtSubject = buildAuthSubject({
289
445
  selfAid: grant.selfAid,
290
446
  actorId: grant.actorId,
@@ -364,9 +520,27 @@ function hashDelegationToken(token) {
364
520
  function approvedCommandKey(sessionTokenHash, sessionId, commandHash) {
365
521
  return JSON.stringify([sessionTokenHash, sessionId, commandHash]);
366
522
  }
523
+ function approvedCommandBindingKey(sessionTokenHash, sessionId, approvalId) {
524
+ return JSON.stringify([sessionTokenHash, sessionId, approvalId]);
525
+ }
367
526
  function isDelegationCommandHash(value) {
368
527
  return /^[a-f0-9]{64}$/.test(value);
369
528
  }
529
+ function delegationFailureDetailCode(cause) {
530
+ switch (cause) {
531
+ case 'missing_token': return 'DELEGATION_TOKEN_MISSING';
532
+ case 'unknown_token': return 'DELEGATION_TOKEN_UNKNOWN';
533
+ case 'session_mismatch': return 'DELEGATION_SESSION_MISMATCH';
534
+ case 'task_replaced': return 'DELEGATION_TASK_REPLACED';
535
+ case 'task_revoked': return 'DELEGATION_TASK_REVOKED';
536
+ case 'daemon_restarted': return 'DELEGATION_DAEMON_RESTARTED';
537
+ case 'approval_not_found':
538
+ case 'approval_expired':
539
+ case 'approval_consumed':
540
+ case 'command_hash_mismatch':
541
+ return 'APPROVED_COMMAND_UNAVAILABLE';
542
+ }
543
+ }
370
544
  export function hashDelegatedCommandArgv(argv) {
371
545
  if (argv.length === 0 || argv.some(value => typeof value !== 'string' || value.includes('\0')))
372
546
  return undefined;
@@ -4,6 +4,8 @@ import { formatPeerKey } from '../relation/peer-identity.js';
4
4
  import { authorizeCommand, authorizeResolvedConfigCommand } from './operation-authorizer.js';
5
5
  import { auditCommandAuthorization } from './authorization-audit.js';
6
6
  import { hasTrustedPrincipal } from './authenticated-actor.js';
7
+ import { ownerResolver, refreshOwnershipForAuthorization, OWNERSHIP_CACHE_MAX_AGE_MS } from '../ownership/owner-resolver.js';
8
+ import { getOperationMeta } from './operation-catalog.js';
7
9
  import { resolveRuntimePermissionMode } from '../role/runtime-policy.js';
8
10
  export function isCrossAgentTriggerOperationAllowed(input) {
9
11
  return input.control || input.daemonOwner === true || input.taskAgentAid === input.targetAgentAid;
@@ -26,7 +28,7 @@ export function buildAuthSubject(input) {
26
28
  conversationId,
27
29
  peerType: input.peerType,
28
30
  });
29
- const processOwners = input.processOwners ?? [];
31
+ const processOwners = ownerResolver.daemonOwners();
30
32
  const fullAccessAuthorization = input.trustedFullAccessAuthorization;
31
33
  const isFullAccessRun = input.trustedProcessRole === 'fullaccess-run'
32
34
  && fullAccessAuthorization?.permissionMode === 'fullaccess'
@@ -67,7 +69,7 @@ export function buildAuthSubject(input) {
67
69
  dataScope,
68
70
  authorizedBy: isFullAccessRun ? fullAccessAuthorization.authorizedBy : undefined,
69
71
  roleSource: roleDetail.source,
70
- identity: input.identity ?? roleToSessionIdentity(role === 'none' ? null : role),
72
+ identity: { ...input.identity, ...roleToSessionIdentity(role === 'none' ? null : role) },
71
73
  isDaemonOwner,
72
74
  fromControlChannel: !!input.fromControlChannel,
73
75
  ecwebManagement: input.ecwebManagement === true && isTrustedDaemonOwner && input.fromControlChannel === true,
@@ -99,6 +101,46 @@ export function authorizeAccess(subject) {
99
101
  subject,
100
102
  };
101
103
  }
104
+ export async function authorizeFreshOperation(params) {
105
+ const subject = params.subject;
106
+ if (subject.processRole !== 'daemon-service' && !subject.ecwebManagement) {
107
+ const configCommand = params.resolvedConfigCommand;
108
+ const changesOwner = configCommand?.kind === 'field' && configCommand.subcommand !== 'get'
109
+ && ['owner', 'owners'].includes(configCommand.field.split('.')[0]);
110
+ const force = changesOwner || params.intent.operation === 'ec.aid.bind' || params.intent.operation === 'config.restore';
111
+ const operation = getOperationMeta(params.intent.operation);
112
+ const sensitive = force || params.intent.dangerous || operation?.dangerous || operation?.category !== 'read'
113
+ || configCommand?.dangerous || subject.processRole === 'fullaccess-run';
114
+ await refreshOwnershipForAuthorization(subject.selfAid, {
115
+ force,
116
+ ...(sensitive ? {} : { maxAgeMs: OWNERSHIP_CACHE_MAX_AGE_MS }),
117
+ includeDaemon: subject.isDaemonOwner || subject.processRole === 'fullaccess-run'
118
+ || params.intent.scope === 'process' || params.intent.scope === 'control' || force,
119
+ });
120
+ const current = buildAuthSubject({
121
+ selfAid: subject.selfAid, agentName: subject.agentName, actorId: subject.actorId,
122
+ channel: subject.channel, channelType: subject.channelType, channelId: subject.channelId,
123
+ chatType: subject.chatType, conversationId: subject.conversationId,
124
+ fromControlChannel: subject.fromControlChannel,
125
+ identity: subject.role === 'owner' && subject.principalId ? undefined : subject.identity,
126
+ });
127
+ if (subject.processRole === 'fullaccess-run') {
128
+ if (!subject.authorizedBy || !ownerResolver.daemonOwners().includes(subject.authorizedBy)) {
129
+ return { allow: false, code: 'OWNER_UNAVAILABLE', reason: 'Current protocol Owner no longer authorizes this fullaccess execution', subject };
130
+ }
131
+ }
132
+ else {
133
+ Object.assign(subject, {
134
+ role: current.role, relationRole: current.relationRole, roleSource: current.roleSource,
135
+ principalId: current.principalId, processRole: current.processRole, dataScope: current.dataScope,
136
+ identity: current.identity, isDaemonOwner: current.isDaemonOwner, allowAccess: current.allowAccess,
137
+ permissionMode: current.permissionMode,
138
+ canApprovePersistentFullAccess: subject.canApprovePersistentFullAccess === false ? false : current.canApprovePersistentFullAccess,
139
+ });
140
+ }
141
+ }
142
+ return authorizeOperation(params);
143
+ }
102
144
  export function authorizeOperation(params) {
103
145
  const access = authorizeAccess(params.subject);
104
146
  if (!access.allow) {
@@ -1,5 +1,5 @@
1
1
  const NEXT_STEPS = {
2
- DAEMON_OWNER_REQUIRED: 'Ask an AID listed in daemon.json.owners to run this operation from the daemon control channel or ECWeb.',
2
+ DAEMON_OWNER_REQUIRED: 'Ask the verified protocol Owner of the daemon Control AID to run this operation from the daemon control channel or ECWeb. Check with ec aid bind status; first binding requires the local host CLI.',
3
3
  TARGET_SELF_ONLY: 'Target the current Agent, or ask a DaemonOwner to perform the cross-Agent operation.',
4
4
  MANAGED_NON_INTERACTIVE_REQUIRED: 'Use the structured non-interactive Agent command (for example `ec agent new <aid> --non-interactive`) or ECWeb.',
5
5
  PURGE_CONFIRMATION_REQUIRED: 'Run the purge from the daemon control plane and add --confirm-purge after verifying the target Agent.',