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.
- package/CHANGELOG.md +42 -0
- package/README.md +6 -0
- package/bin/install-codex-managed-hooks.mjs +20 -3
- package/dist/agents/baseagent-availability.js +1 -1
- package/dist/agents/baseagent.js +88 -0
- package/dist/agents/claude-runner.js +59 -12
- package/dist/agents/codex/app-server-client.js +18 -1
- package/dist/agents/codex-runner.js +262 -21
- package/dist/agents/ecagent-runner.js +47 -11
- package/dist/agents/gemini-runner.js +36 -12
- package/dist/aun/aid/client.js +4 -3
- package/dist/aun/aid/control-aid.js +270 -8
- package/dist/aun/aid/identity.js +6 -3
- package/dist/aun/aid/ownership-operation.js +58 -0
- package/dist/aun/aid/store.js +1 -0
- package/dist/aun/group/fs-download.js +67 -2
- package/dist/aun/group/history.js +20 -5
- package/dist/aun/group/operations.js +8 -2
- package/dist/aun/menu/client.js +39 -2
- package/dist/aun/menu/transport.js +1 -1
- package/dist/aun/msg/history.js +43 -4
- package/dist/aun/msg/managed-operation.js +30 -53
- package/dist/aun/msg/p2p.js +51 -15
- package/dist/aun/outbox.js +4 -0
- package/dist/channels/aun.js +260 -73
- package/dist/channels/daemon.js +6 -4
- package/dist/channels/feishu.js +3 -0
- package/dist/cli/agent-command.js +161 -223
- package/dist/cli/agent-create.js +190 -223
- package/dist/cli/agent.js +3 -1
- package/dist/cli/aid-bind-command.js +53 -0
- package/dist/cli/aun-commands.js +384 -91
- package/dist/cli/bench.js +1 -1
- package/dist/cli/cli-argv.js +74 -0
- package/dist/cli/daemon-commands.js +105 -74
- package/dist/cli/diag.js +1 -1
- package/dist/cli/fs-command.js +18 -19
- package/dist/cli/getmeta-command.js +159 -6
- package/dist/cli/index.js +6 -12
- package/dist/cli/init-channel.js +8 -55
- package/dist/cli/init.js +125 -218
- package/dist/cli/queue-command.js +11 -8
- package/dist/cli/restart-monitor.js +177 -85
- package/dist/cli/session-command.js +1 -1
- package/dist/cli/stats.js +7 -5
- package/dist/cli/thread-command.js +103 -30
- package/dist/cli/trigger-command.js +1 -1
- package/dist/cli/watch-msg-diag.js +1 -0
- package/dist/config/access-policy.js +2 -2
- package/dist/config/boot-log.js +14 -11
- package/dist/config/config-field-policy.js +9 -1
- package/dist/config/config-manager.js +176 -90
- package/dist/config/config-operation-service.js +7 -14
- package/dist/config/contact-book-store.js +142 -26
- package/dist/config/contact-book.js +4 -2
- package/dist/config/lifecycle.js +1 -1
- package/dist/config/owner-config.js +96 -0
- package/dist/config/peer-role-resolver.js +12 -18
- package/dist/config/providers-config.js +382 -0
- package/dist/config/providers-import.js +74 -0
- package/dist/config/providers-management.js +328 -0
- package/dist/config/providers.js +22 -0
- package/dist/config/resolved-config-op.js +17 -14
- package/dist/config/snapshot.js +28 -16
- package/dist/config-store.js +18 -14
- package/dist/core/agent-ipc-registration.js +21 -63
- package/dist/core/agent-runtime.js +16 -5
- package/dist/core/auth/agent-delegation.js +199 -25
- package/dist/core/auth/auth-gateway.js +44 -2
- package/dist/core/auth/authorization-denial.js +1 -1
- package/dist/core/auth/debug-context.js +2 -1
- package/dist/core/auth/delegation-authorization.js +2 -2
- package/dist/core/auth/delegation-diagnostics.js +4 -1
- package/dist/core/auth/operation-authorizer.js +10 -1
- package/dist/core/auth/operation-catalog.js +19 -1
- package/dist/core/bootstrap-service.js +43 -20
- package/dist/core/channel-loader.js +0 -1
- package/dist/core/channel-runtime.js +30 -4
- package/dist/core/command/agent-control.js +196 -37
- package/dist/core/command/cli-intent-parser.js +60 -4
- package/dist/core/command/command-handler.js +212 -60
- package/dist/core/command/connect-menu.js +1 -0
- package/dist/core/command/diag-service.js +5 -4
- package/dist/core/command/file-search.js +368 -0
- package/dist/core/command/menu-catalog.js +2 -1
- package/dist/core/command/menu-handler.js +292 -107
- package/dist/core/command/menu-protocol.js +23 -0
- package/dist/core/command/role-menu.js +7 -19
- package/dist/core/command/skill-slash.js +130 -25
- package/dist/core/command/slash-handler.js +101 -53
- package/dist/core/command/topic-slash.js +16 -5
- package/dist/core/daemon-bootstrap.js +7 -1
- package/dist/core/daemon-control.js +19 -1
- package/dist/core/daemon-lifecycle.js +2 -1
- package/dist/core/event-catalog.js +2 -0
- package/dist/core/evolagent-registry.js +13 -2
- package/dist/core/evolagent.js +3 -6
- package/dist/core/handoff/ipc-registration.js +2 -2
- package/dist/core/handoff/runtime.js +40 -6
- package/dist/core/handoff/store.js +1 -0
- package/dist/core/inference/text-inference.js +95 -6
- package/dist/core/ipc-registration.js +43 -24
- package/dist/core/memory/consolidation-context.js +4 -1
- package/dist/core/memory/consolidation.js +25 -14
- package/dist/core/message/aun-ipc-sender.js +296 -88
- package/dist/core/message/aun-request-registry.js +226 -0
- package/dist/core/message/create-status.js +33 -4
- package/dist/core/message/message-bridge.js +62 -13
- package/dist/core/message/message-log.js +0 -1
- package/dist/core/message/message-queue.js +4 -1
- package/dist/core/message/message-utils.js +8 -1
- package/dist/core/message/peer-mode.js +17 -9
- package/dist/core/message/response-engine.js +266 -124
- package/dist/core/message/retry-scheduler.js +11 -8
- package/dist/core/model/catalog-service.js +60 -18
- package/dist/core/model/model-catalog.js +112 -9
- package/dist/core/model/model-fallback.js +94 -26
- package/dist/core/ownership/owner-resolver.js +687 -0
- package/dist/core/permission/approval-gateway.js +196 -62
- package/dist/core/permission/ec-command-parser.js +138 -25
- package/dist/core/permission/filesystem-approval-scope.js +141 -0
- package/dist/core/permission/protected-paths.js +14 -1
- package/dist/core/permission/sandbox-runtime.js +171 -18
- package/dist/core/permission/tool-policy.js +13 -2
- package/dist/core/role/runtime-policy.js +3 -4
- package/dist/core/session/session-idle-monitor.js +10 -8
- package/dist/core/session/session-manager.js +72 -45
- package/dist/core/session/session-mapper.js +18 -7
- package/dist/core/session/session-renew.js +5 -4
- package/dist/core/session/session-title.js +2 -1
- package/dist/core/session/session-turn-coordinator.js +4 -2
- package/dist/core/skill/index-generator.js +2 -16
- package/dist/core/task/task-recovery-service.js +15 -2
- package/dist/core/thread/command-parser.js +28 -13
- package/dist/core/thread/index.js +1 -0
- package/dist/core/thread/ipc-operation.js +11 -8
- package/dist/core/thread/thread-operation.js +79 -44
- package/dist/core/thread/thread-route.js +54 -6
- package/dist/core/thread/thread-service.js +236 -45
- package/dist/core/thread/topic-id.js +248 -0
- package/dist/eck/kit-renderer.js +1 -1
- package/dist/eck/message-renderer.js +12 -2
- package/dist/index.js +50 -27
- package/dist/ipc.js +59 -6
- package/dist/paths.js +1 -0
- package/dist/product.js +1 -0
- package/dist/response-system/engines/v1/proactive-flow.js +10 -3
- package/dist/stats/db.js +8 -0
- package/dist/stats/writer.js +26 -4
- package/dist/trigger/authorization.js +48 -3
- package/dist/trigger/feedback.js +28 -10
- package/dist/trigger/ipc-service.js +86 -10
- package/dist/trigger/local-files.js +55 -23
- package/dist/trigger/scheduler.js +57 -11
- package/dist/trigger/system-definitions.js +1 -0
- package/dist/trigger/validation.js +44 -15
- package/dist/utils/aid-bind.js +17 -297
- package/dist/utils/avatar-upload.js +16 -4
- package/dist/utils/cross-platform.js +115 -36
- package/dist/utils/ecweb-supervisor.js +1 -1
- package/dist/utils/ecweb-utils.js +8 -1
- package/dist/utils/error-utils.js +44 -6
- package/dist/utils/instance-registry.js +12 -2
- package/dist/utils/npm-ops.js +102 -27
- package/dist/utils/package-version-info.js +72 -27
- package/dist/utils/restart-safety.js +50 -0
- package/dist/utils/upgrade-jobs.js +304 -0
- package/dist/utils/windows-autostart.js +17 -10
- package/kits/docs/INDEX.md +5 -0
- package/kits/docs/aun/ABILITY_CALLING.md +239 -0
- package/kits/docs/evolcore/INDEX.md +14 -3
- package/kits/docs/evolcore/agent-create.md +55 -141
- package/kits/docs/evolcore/agent.md +38 -16
- package/kits/docs/evolcore/aid.md +34 -0
- package/kits/docs/evolcore/config.md +20 -1
- package/kits/docs/evolcore/event.md +2 -2
- package/kits/docs/evolcore/fs.md +18 -2
- package/kits/docs/evolcore/group-collaboration.md +13 -246
- package/kits/docs/evolcore/group.md +2 -1
- package/kits/docs/evolcore/memory-consolidation-batches.md +1 -1
- package/kits/docs/evolcore/menu-protocol.md +65 -1
- package/kits/docs/evolcore/msg.md +64 -2
- package/kits/docs/evolcore/skill.md +86 -0
- package/kits/docs/evolcore/stats.md +6 -0
- package/kits/docs/evolcore/task.md +2 -0
- package/kits/docs/evolcore/thread-chain.md +145 -51
- package/kits/docs/evolcore/trigger.md +48 -3
- package/kits/eck_manifest.json +21 -0
- package/kits/eck_message_manifest.json +4 -4
- package/kits/migrations/migrate-role-config-v5.mjs +20 -7
- package/kits/rules/01-overview.md +2 -1
- package/kits/rules/03-identity.md +3 -1
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/07-memory-and-thread.md +1 -1
- package/kits/schemas/_meta.json +16 -1
- package/kits/schemas/agent-config.schema.13.json +44 -2
- package/kits/schemas/contact-book.schema.3.json +5 -0
- package/kits/schemas/daemon.schema.6.json +3 -1
- package/kits/schemas/daemon.schema.8.json +329 -0
- package/kits/schemas/defaults.schema.5.json +39 -3
- package/kits/schemas/providers.schema.1.json +76 -0
- package/kits/schemas/relation-config.schema.9.json +5 -1
- package/kits/skills/.switches.json +28 -0
- package/kits/skills/aid-ability/SKILL.md +214 -0
- package/kits/skills/ec-agent-create/SKILL.md +162 -0
- package/kits/skills/ec-group/SKILL.md +2 -1
- package/kits/skills/ec-group-collaboration/SKILL.md +228 -0
- package/kits/skills/ec-identity/SKILL.md +143 -0
- package/kits/skills/ec-skill/scripts/cli.mjs +36 -3
- package/kits/skills/ec-skill/scripts/skill.mjs +202 -15
- package/kits/skills/ec-trigger/SKILL.md +167 -0
- package/kits/skills/ec-trigger/references/examples.md +153 -0
- package/kits/skills/thread-ops/SKILL.md +108 -420
- package/kits/templates/message-fragments/thread-context.md +1 -2
- package/kits/templates/message-fragments/trigger-thread-check.md +1 -1
- package/kits/templates/roles/member.json +1 -1
- package/kits/templates/roles/visitor.json +1 -0
- package/kits/templates/system-fragments/bootstrap.md +1 -0
- package/kits/templates/system-fragments/channel.md +2 -1
- package/kits/templates/system-fragments/commands.md +5 -4
- package/kits/templates/system-fragments/identity.md +2 -0
- package/kits/templates/system-fragments/session.md +1 -1
- package/kits/templates/system-fragments/venue.md +1 -1
- package/package.json +4 -3
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import {
|
|
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' ||
|
|
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:
|
|
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')
|
|
267
|
-
return fail('INITIALIZE_OWNER_MISMATCH', 'Explicit
|
|
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]'
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
155
|
-
|
|
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',
|
|
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
|
-
|
|
222
|
-
this.
|
|
223
|
-
|
|
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
|
-
|
|
232
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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.',
|