evolcore 0.0.20 → 0.0.21
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 +22 -0
- package/README.md +58 -9
- package/dist/agents/baseagent.js +10 -6
- package/dist/agents/claude-runner.js +379 -108
- package/dist/agents/codex-app-server-client.js +10 -2
- package/dist/agents/codex-runner.js +402 -135
- package/dist/agents/ecagent-runner.js +171 -61
- package/dist/agents/gemini-runner.js +130 -30
- package/dist/agents/request-identity.js +25 -0
- package/dist/agents/runner-types.js +19 -0
- package/dist/aun/aid/agentmd.js +59 -2
- package/dist/aun/aid/identity.js +4 -1
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/msg/group.js +72 -6
- package/dist/aun/msg/history.js +213 -36
- package/dist/aun/msg/managed-operation.js +58 -9
- package/dist/aun/msg/p2p.js +5 -0
- package/dist/aun/outbox.js +182 -80
- package/dist/aun/service-proxy.js +43 -25
- package/dist/channels/aun.js +409 -88
- package/dist/channels/daemon.js +6 -1
- package/dist/cli/agent-command.js +4 -3
- package/dist/cli/agent.js +66 -56
- package/dist/cli/aun-commands.js +177 -42
- package/dist/cli/command-log.js +10 -11
- package/dist/cli/contact.js +1 -0
- package/dist/cli/daemon-commands.js +69 -115
- package/dist/cli/init.js +27 -15
- package/dist/cli/task-context.js +46 -0
- package/dist/cli/trigger-command.js +1 -1
- package/dist/cli/watch-logs.js +10 -3
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +16 -5
- package/dist/config/config-manager.js +135 -17
- package/dist/config/contact-operation-service.js +32 -1
- package/dist/config/contact-request-service.js +44 -0
- package/dist/config/daemon-services.js +186 -0
- package/dist/config/gateway-config.js +20 -9
- package/dist/config/role-service.js +54 -3
- package/dist/config/schema-migration.js +550 -0
- package/dist/config-store.js +151 -9
- package/dist/core/agent-application-service.js +279 -0
- package/dist/core/audit/log-integrity.js +102 -0
- package/dist/core/auth/agent-delegation.js +31 -1
- package/dist/core/auth/auth-gateway.js +33 -4
- package/dist/core/auth/authorization-audit.js +150 -2
- package/dist/core/auth/operation-authorizer.js +41 -1
- package/dist/core/auth/operation-catalog.js +9 -1
- package/dist/core/bootstrap-service.js +6 -2
- package/dist/core/causation/aun-association.js +7 -4
- package/dist/core/command/agent-control.js +56 -16
- package/dist/core/command/command-handler.js +290 -44
- package/dist/core/command/connect-menu.js +3 -4
- package/dist/core/command/group-menu.js +5 -7
- package/dist/core/command/menu-handler.js +279 -80
- package/dist/core/command/role-menu.js +21 -11
- package/dist/core/command/slash-gate.js +85 -18
- package/dist/core/command/slash-handler.js +350 -32
- package/dist/core/event-catalog.js +5 -0
- package/dist/core/evolagent.js +4 -0
- package/dist/core/handoff/dispatcher.js +4 -0
- package/dist/core/handoff/runtime.js +10 -0
- package/dist/core/handoff/store.js +32 -9
- package/dist/core/inference/text-inference.js +7 -15
- package/dist/core/message/im-renderer.js +83 -84
- package/dist/core/message/{inbound-admission.js → message-admission.js} +51 -1
- package/dist/core/message/message-bridge.js +124 -10
- package/dist/core/message/message-log.js +14 -7
- package/dist/core/message/message-queue.js +206 -16
- package/dist/core/message/message-utils.js +12 -5
- package/dist/core/message/response-engine.js +486 -68
- package/dist/core/message/send-receipt.js +1 -0
- package/dist/core/message/stream-debouncer.js +9 -2
- package/dist/core/model/model-catalog.js +23 -15
- package/dist/core/model/model-diagnostics.js +28 -10
- package/dist/core/permission/approval-gateway.js +180 -6
- package/dist/core/permission/ec-command-parser.js +410 -54
- package/dist/core/permission/mode.js +18 -3
- package/dist/core/{protected-paths.js → permission/protected-paths.js} +17 -5
- package/dist/core/permission/readonly-shell-query.js +263 -9
- package/dist/core/permission/sandbox-runtime.js +159 -1
- package/dist/core/permission/tool-policy.js +575 -21
- package/dist/core/session/session-fs-store.js +154 -5
- package/dist/core/session/session-manager.js +299 -30
- package/dist/core/session/session-renew.js +19 -12
- package/dist/core/session/session-turn-coordinator.js +11 -4
- package/dist/eck/kit-renderer.js +1 -1
- package/dist/index.js +253 -46
- package/dist/ipc.js +374 -24
- package/dist/paths.js +64 -7
- package/dist/response-system/context-builder.js +1 -7
- package/dist/trigger/anomaly-store.js +1 -0
- package/dist/trigger/feedback.js +56 -5
- package/dist/trigger/history.js +79 -4
- package/dist/trigger/legacy-session-history.js +2 -2
- package/dist/trigger/parser.js +3 -2
- package/dist/trigger/validation.js +6 -1
- package/dist/utils/atomic-write.js +27 -0
- package/dist/utils/ecweb-utils.js +16 -2
- package/dist/utils/error-utils.js +4 -1
- package/dist/utils/logger.js +21 -2
- package/dist/utils/process-tree-stats.js +24 -4
- package/dist/utils/process-tree-worker.js +31 -0
- package/dist/utils/project-path.js +1 -2
- package/kits/docs/INDEX.md +1 -1
- package/kits/docs/evolcore/INDEX.md +1 -1
- package/kits/docs/evolcore/contact.md +7 -1
- package/kits/docs/evolcore/msg.md +16 -0
- package/kits/schemas/_meta.json +4 -2
- package/kits/schemas/agent-config.schema.11.json +13 -0
- package/kits/schemas/daemon.schema.5.json +0 -1
- package/kits/schemas/daemon.schema.6.json +131 -0
- package/kits/schemas/defaults.schema.5.json +15 -3
- package/kits/schemas/migrations/README.md +3 -1
- package/kits/schemas/relation-config.schema.8.json +13 -0
- package/kits/schemas/role-config.schema.1.json +1 -2
- package/kits/schemas/single-session.schema.3.json +32 -0
- package/kits/templates/roles/admin.json +1 -0
- package/kits/templates/roles/member.json +1 -0
- package/kits/templates/roles/visitor.json +1 -0
- package/package.json +6 -3
- package/skills/eclink/SKILL.md +2 -0
- package/dist/config/aun-gateway-config.js +0 -2
|
@@ -8,6 +8,11 @@ import { resolveRuntimePermissionMode } from '../role/runtime-policy.js';
|
|
|
8
8
|
export function isCrossAgentTriggerOperationAllowed(input) {
|
|
9
9
|
return input.control || input.daemonOwner === true || input.taskAgentAid === input.targetAgentAid;
|
|
10
10
|
}
|
|
11
|
+
/** A delegated identity may authorize ordinary work, but it is not a human approval for persistent host execution. */
|
|
12
|
+
export function canApprovePersistentFullAccessTrigger(input) {
|
|
13
|
+
return input.daemonOwner
|
|
14
|
+
&& (input.source === 'direct-user' || input.source === 'management-channel');
|
|
15
|
+
}
|
|
11
16
|
export function buildAuthSubject(input) {
|
|
12
17
|
const chatType = input.chatType === 'group' ? 'group' : 'private';
|
|
13
18
|
const actorId = input.actorId;
|
|
@@ -22,17 +27,31 @@ export function buildAuthSubject(input) {
|
|
|
22
27
|
peerType: input.peerType,
|
|
23
28
|
});
|
|
24
29
|
const processOwners = input.processOwners ?? [];
|
|
30
|
+
const fullAccessAuthorization = input.trustedFullAccessAuthorization;
|
|
31
|
+
const isFullAccessRun = input.trustedProcessRole === 'fullaccess-run'
|
|
32
|
+
&& fullAccessAuthorization?.permissionMode === 'fullaccess'
|
|
33
|
+
&& fullAccessAuthorization.processRole === 'fullaccess-run'
|
|
34
|
+
&& fullAccessAuthorization.dataScope === 'daemon';
|
|
25
35
|
const isDaemonService = input.trustedProcessRole === 'daemon-service';
|
|
26
|
-
const isDaemonOwner = !isDaemonService && isProcessOwner({
|
|
36
|
+
const isDaemonOwner = !isDaemonService && !isFullAccessRun && isProcessOwner({
|
|
27
37
|
actor: roleDetail.actor,
|
|
28
38
|
processOwners,
|
|
29
39
|
});
|
|
30
40
|
const suppliedRole = input.identity?.role && input.identity.role !== 'none' ? input.identity.role : undefined;
|
|
31
41
|
const role = suppliedRole ?? roleDetail.effectiveRole ?? 'none';
|
|
32
|
-
const processRole =
|
|
42
|
+
const processRole = isFullAccessRun
|
|
43
|
+
? 'fullaccess-run'
|
|
44
|
+
: isDaemonService ? 'daemon-service' : isDaemonOwner ? 'daemon-owner' : 'none';
|
|
45
|
+
const dataScope = processRole === 'fullaccess-run'
|
|
46
|
+
|| processRole === 'daemon-service'
|
|
47
|
+
|| processRole === 'daemon-owner'
|
|
48
|
+
? 'daemon'
|
|
49
|
+
: role === 'owner' || role === 'admin' ? 'agent' : 'relation';
|
|
33
50
|
return {
|
|
34
51
|
selfAid: input.selfAid,
|
|
52
|
+
agentName: input.agentName,
|
|
35
53
|
actorId,
|
|
54
|
+
principalId: roleDetail.actor.principalId,
|
|
36
55
|
requestId: input.requestId,
|
|
37
56
|
sessionId: input.sessionId,
|
|
38
57
|
channel: input.channel,
|
|
@@ -44,6 +63,8 @@ export function buildAuthSubject(input) {
|
|
|
44
63
|
role,
|
|
45
64
|
relationRole: role,
|
|
46
65
|
processRole,
|
|
66
|
+
dataScope,
|
|
67
|
+
authorizedBy: isFullAccessRun ? fullAccessAuthorization.authorizedBy : undefined,
|
|
47
68
|
roleSource: roleDetail.source,
|
|
48
69
|
identity: input.identity ?? roleToSessionIdentity(role === 'none' ? null : role),
|
|
49
70
|
isDaemonOwner,
|
|
@@ -51,8 +72,12 @@ export function buildAuthSubject(input) {
|
|
|
51
72
|
allowAccess: processRole !== 'none' || (suppliedRole
|
|
52
73
|
? checkRoleAccess(suppliedRole, input.selfAid)
|
|
53
74
|
: roleDetail.allowAccess && checkRoleAccess(role, input.selfAid)),
|
|
54
|
-
|
|
55
|
-
??
|
|
75
|
+
canApprovePersistentFullAccess: input.canApprovePersistentFullAccess
|
|
76
|
+
?? (isDaemonOwner && !isDaemonService),
|
|
77
|
+
permissionMode: isFullAccessRun
|
|
78
|
+
? 'fullaccess'
|
|
79
|
+
: input.permissionMode
|
|
80
|
+
?? resolveRuntimePermissionMode({ selfAid: input.selfAid, role }).effectiveValue,
|
|
56
81
|
};
|
|
57
82
|
}
|
|
58
83
|
function isProcessOwner(input) {
|
|
@@ -97,6 +122,7 @@ export function authorizeOperation(params) {
|
|
|
97
122
|
peerKey: params.subject.peerKey,
|
|
98
123
|
role: params.subject.role,
|
|
99
124
|
processRole: params.subject.processRole,
|
|
125
|
+
dataScope: params.subject.dataScope,
|
|
100
126
|
isDaemonOwner: params.subject.isDaemonOwner,
|
|
101
127
|
fromControlChannel: params.subject.fromControlChannel,
|
|
102
128
|
allowExplicitRelationTarget: params.allowExplicitRelationTarget,
|
|
@@ -162,6 +188,7 @@ function auditDecision(params, decision) {
|
|
|
162
188
|
requestId: params.auditMetadata?.requestId ?? params.subject.requestId,
|
|
163
189
|
sessionId: params.auditMetadata?.sessionId ?? params.subject.sessionId,
|
|
164
190
|
agentAid: params.auditMetadata?.agentAid ?? params.subject.selfAid,
|
|
191
|
+
agentName: params.auditMetadata?.agentName ?? params.subject.agentName ?? params.subject.selfAid,
|
|
165
192
|
permissionMode: params.auditMetadata?.permissionMode
|
|
166
193
|
?? (decision.allow ? decision.permissionMode ?? params.subject.permissionMode : params.subject.permissionMode),
|
|
167
194
|
toolName: params.auditMetadata?.toolName,
|
|
@@ -178,6 +205,8 @@ function auditDecision(params, decision) {
|
|
|
178
205
|
channelId: params.subject.channelId,
|
|
179
206
|
role: decision.allow ? decision.role : params.subject.role,
|
|
180
207
|
processRole: params.subject.processRole,
|
|
208
|
+
dataScope: params.subject.dataScope,
|
|
209
|
+
authorizedBy: params.subject.authorizedBy,
|
|
181
210
|
isDaemonOwner: params.subject.isDaemonOwner,
|
|
182
211
|
fromControlChannel: params.subject.fromControlChannel,
|
|
183
212
|
decision: decision.allow ? 'allow' : 'deny',
|
|
@@ -3,12 +3,14 @@ import { LogWriter } from '../../utils/log-writer.js';
|
|
|
3
3
|
import { resolvePaths } from '../../paths.js';
|
|
4
4
|
import crypto from 'node:crypto';
|
|
5
5
|
import { buildAuthorizationEventKey } from '../audit/event-key.js';
|
|
6
|
+
import { normalizeExecutionPermissionMode } from '../permission/mode.js';
|
|
6
7
|
export function auditCommandAuthorization(event) {
|
|
7
8
|
const shouldAudit = event.source === 'menu.cli' ||
|
|
8
9
|
event.decision === 'deny' ||
|
|
9
10
|
(event.decision === 'allow' && event.dangerous) ||
|
|
10
11
|
(event.source === 'agent-tool' && event.operation.startsWith('config.')) ||
|
|
11
12
|
event.operation === 'codex.approval' ||
|
|
13
|
+
event.operation.startsWith('fullaccess.') ||
|
|
12
14
|
event.operation.startsWith('role.') ||
|
|
13
15
|
event.operation === 'cli.exec.raw';
|
|
14
16
|
if (!shouldAudit)
|
|
@@ -16,6 +18,77 @@ export function auditCommandAuthorization(event) {
|
|
|
16
18
|
const auditRecord = buildAuditRecord(event);
|
|
17
19
|
logAuditEvent(auditRecord);
|
|
18
20
|
}
|
|
21
|
+
export function auditFullAccessEvent(input) {
|
|
22
|
+
const configurationEvent = input.event === 'fullaccess.trigger.configured'
|
|
23
|
+
|| input.event === 'fullaccess.trigger.deescalated';
|
|
24
|
+
const executionEvent = input.event === 'fullaccess.execution.started'
|
|
25
|
+
|| input.event === 'fullaccess.execution.ended'
|
|
26
|
+
|| input.event === 'fullaccess.trigger.execution.started'
|
|
27
|
+
|| input.event === 'fullaccess.trigger.execution.ended';
|
|
28
|
+
// The scheduler triggers a run, but the executing subject is the task-bound
|
|
29
|
+
// fullaccess identity. Keep the original owner/configuration provenance in
|
|
30
|
+
// a separate field rather than reporting the model task as a human owner.
|
|
31
|
+
const processRole = input.processRole
|
|
32
|
+
?? (executionEvent ? 'fullaccess-run' : input.source === 'trigger' ? 'daemon-service' : 'none');
|
|
33
|
+
const actorRole = processRole === 'daemon-owner' ? 'daemon-owner' : processRole;
|
|
34
|
+
const runnerProfileActive = executionEvent && input.executionState !== 'blocked';
|
|
35
|
+
const completedEvent = input.event.endsWith('.ended') || configurationEvent;
|
|
36
|
+
auditCommandAuthorization({
|
|
37
|
+
ts: Date.now(),
|
|
38
|
+
callId: input.attemptId ?? input.runId ?? input.taskId ?? input.messageId,
|
|
39
|
+
correlationId: input.runId ?? input.taskId,
|
|
40
|
+
source: input.source === 'trigger' ? 'agent-tool' : 'slash',
|
|
41
|
+
operation: input.event,
|
|
42
|
+
// CommandScope has no separate daemon value; process is the closest
|
|
43
|
+
// existing scope for daemon-owner authorization and keeps audit records
|
|
44
|
+
// compatible with the established command-audit schema.
|
|
45
|
+
scope: 'process',
|
|
46
|
+
dangerous: true,
|
|
47
|
+
decision: input.decision ?? 'allow',
|
|
48
|
+
executed: input.executed ?? completedEvent,
|
|
49
|
+
executionState: input.executionState
|
|
50
|
+
?? (input.decision === 'deny' ? 'blocked' : completedEvent ? 'completed' : 'authorized'),
|
|
51
|
+
decisionSource: input.decision === 'deny'
|
|
52
|
+
? 'policy'
|
|
53
|
+
: configurationEvent
|
|
54
|
+
? processRole === 'daemon-owner' ? 'daemon-owner' : 'policy'
|
|
55
|
+
: processRole === 'fullaccess-run'
|
|
56
|
+
? 'fullaccess-run'
|
|
57
|
+
: input.source === 'trigger'
|
|
58
|
+
? 'trigger-definition'
|
|
59
|
+
: 'daemon-owner',
|
|
60
|
+
actorId: input.actorId,
|
|
61
|
+
processRole,
|
|
62
|
+
dataScope: input.dataScope ?? (executionEvent ? 'daemon' : undefined),
|
|
63
|
+
authorizedBy: input.authorizedBy,
|
|
64
|
+
isDaemonOwner: processRole === 'daemon-owner',
|
|
65
|
+
agentAid: input.agentAid,
|
|
66
|
+
agentName: input.agentName,
|
|
67
|
+
sessionId: input.sessionId,
|
|
68
|
+
taskId: input.taskId,
|
|
69
|
+
messageId: input.messageId,
|
|
70
|
+
permissionMode: 'fullaccess',
|
|
71
|
+
role: actorRole,
|
|
72
|
+
reason: input.reason ?? input.result ?? input.event,
|
|
73
|
+
durationMs: input.durationMs,
|
|
74
|
+
argsSummary: {
|
|
75
|
+
source: input.source,
|
|
76
|
+
triggerId: input.triggerId,
|
|
77
|
+
runId: input.runId,
|
|
78
|
+
attemptId: input.attemptId,
|
|
79
|
+
baseagent: input.baseagent,
|
|
80
|
+
model: input.model,
|
|
81
|
+
dataScope: input.dataScope ?? (executionEvent ? 'daemon' : undefined),
|
|
82
|
+
authorizedBy: input.authorizedBy,
|
|
83
|
+
...(runnerProfileActive ? {
|
|
84
|
+
sandbox: 'off',
|
|
85
|
+
approval: 'bypassed',
|
|
86
|
+
applicationPolicy: 'bypassed',
|
|
87
|
+
} : {}),
|
|
88
|
+
result: input.result,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
19
92
|
/**
|
|
20
93
|
* Record a tool-level preflight denial (e.g. protected-path block) in the
|
|
21
94
|
* structured command-audit log. This covers interactive sessions whose
|
|
@@ -45,6 +118,7 @@ export function auditToolPreflightDenial(input) {
|
|
|
45
118
|
argsSummary: input.summary ? { summary: input.summary } : undefined,
|
|
46
119
|
sessionId: input.sessionId ?? 'unknown',
|
|
47
120
|
agentAid: input.agentAid ?? 'unknown',
|
|
121
|
+
agentName: input.agentName,
|
|
48
122
|
permissionMode: input.permissionMode ?? 'unknown',
|
|
49
123
|
channel: input.channel,
|
|
50
124
|
actorId: input.actorId,
|
|
@@ -76,6 +150,7 @@ export function auditCodexApprovalDecision(input) {
|
|
|
76
150
|
correlationId: input.correlationId ?? input.requestId,
|
|
77
151
|
sessionId: input.sessionId ?? 'unknown',
|
|
78
152
|
agentAid: input.agentAid ?? 'unknown',
|
|
153
|
+
agentName: input.agentName,
|
|
79
154
|
permissionMode: input.permissionMode ?? 'unknown',
|
|
80
155
|
toolName: input.toolName,
|
|
81
156
|
approvalMethod: input.method,
|
|
@@ -84,7 +159,40 @@ export function auditCodexApprovalDecision(input) {
|
|
|
84
159
|
reason: input.reason,
|
|
85
160
|
role: input.role ?? 'unknown',
|
|
86
161
|
taskId: input.taskId,
|
|
87
|
-
argsSummary: {
|
|
162
|
+
argsSummary: {
|
|
163
|
+
method: input.method,
|
|
164
|
+
...(input.parseFailure ? { parseFailure: input.parseFailure } : {}),
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Record a runtime permission decision outside a Codex app-server approval.
|
|
170
|
+
* The shared command-audit stream distinguishes policy, human, and
|
|
171
|
+
* infrastructure denials without parsing localized log text.
|
|
172
|
+
*/
|
|
173
|
+
export function auditPermissionDecision(input) {
|
|
174
|
+
auditCommandAuthorization({
|
|
175
|
+
ts: Date.now(),
|
|
176
|
+
callId: input.requestId,
|
|
177
|
+
correlationId: input.requestId,
|
|
178
|
+
requestId: input.requestId,
|
|
179
|
+
sessionId: input.sessionId,
|
|
180
|
+
agentAid: input.agentAid,
|
|
181
|
+
agentName: input.agentName,
|
|
182
|
+
permissionMode: input.permissionMode,
|
|
183
|
+
toolName: input.toolName,
|
|
184
|
+
policyCode: input.policyCode,
|
|
185
|
+
decisionSource: input.decisionSource,
|
|
186
|
+
source: 'agent-tool',
|
|
187
|
+
operation: 'permission.runtime',
|
|
188
|
+
scope: 'filesystem',
|
|
189
|
+
dangerous: false,
|
|
190
|
+
role: input.role ?? 'unknown',
|
|
191
|
+
taskId: input.taskId,
|
|
192
|
+
decision: 'deny',
|
|
193
|
+
executed: false,
|
|
194
|
+
executionState: 'blocked',
|
|
195
|
+
reason: input.reason,
|
|
88
196
|
});
|
|
89
197
|
}
|
|
90
198
|
/** Record a fail-closed infrastructure condition before a tool can execute. */
|
|
@@ -104,6 +212,7 @@ export function auditToolInfrastructureFailure(input) {
|
|
|
104
212
|
requestId: input.callId,
|
|
105
213
|
sessionId: input.sessionId ?? 'unknown',
|
|
106
214
|
agentAid: input.agentAid ?? 'unknown',
|
|
215
|
+
agentName: input.agentName,
|
|
107
216
|
permissionMode: input.permissionMode ?? 'unknown',
|
|
108
217
|
toolName: input.toolName,
|
|
109
218
|
policyCode: input.policyCode,
|
|
@@ -140,7 +249,8 @@ function buildAuditRecord(event) {
|
|
|
140
249
|
requestId: event.requestId,
|
|
141
250
|
sessionId: event.sessionId,
|
|
142
251
|
agentAid: redactIdentifier(event.agentAid ?? event.selfAid),
|
|
143
|
-
|
|
252
|
+
agentName: resolveAgentName(event),
|
|
253
|
+
permissionMode: normalizeAuditPermissionMode(event.permissionMode),
|
|
144
254
|
toolName: event.toolName,
|
|
145
255
|
approvalMethod: event.approvalMethod,
|
|
146
256
|
policyCode: event.policyCode ?? event.code,
|
|
@@ -161,6 +271,8 @@ function buildAuditRecord(event) {
|
|
|
161
271
|
channelId: redactIdentifier(event.channelId),
|
|
162
272
|
role: event.role,
|
|
163
273
|
processRole: event.processRole,
|
|
274
|
+
dataScope: event.dataScope,
|
|
275
|
+
authorizedBy: redactIdentifier(event.authorizedBy),
|
|
164
276
|
isDaemonOwner: event.isDaemonOwner,
|
|
165
277
|
fromControlChannel: event.fromControlChannel,
|
|
166
278
|
taskId: event.taskId,
|
|
@@ -177,6 +289,26 @@ function buildAuditRecord(event) {
|
|
|
177
289
|
exitCode: event.exitCode,
|
|
178
290
|
};
|
|
179
291
|
}
|
|
292
|
+
function resolveAgentName(event) {
|
|
293
|
+
const displayName = typeof event.agentName === 'string' ? event.agentName.trim() : '';
|
|
294
|
+
if (displayName && !isUnknownAgentLabel(displayName))
|
|
295
|
+
return displayName;
|
|
296
|
+
const aid = typeof event.agentAid === 'string' ? event.agentAid.trim() : '';
|
|
297
|
+
if (aid && aid !== 'unknown')
|
|
298
|
+
return aid;
|
|
299
|
+
const selfAid = typeof event.selfAid === 'string' ? event.selfAid.trim() : '';
|
|
300
|
+
if (selfAid && selfAid !== 'unknown')
|
|
301
|
+
return selfAid;
|
|
302
|
+
return aid === 'unknown' || selfAid === 'unknown' ? 'unknown' : undefined;
|
|
303
|
+
}
|
|
304
|
+
function isUnknownAgentLabel(value) {
|
|
305
|
+
return value === 'unknown' || value === '<unknown>' || value === '<none>' || value === 'undefined' || value === 'null';
|
|
306
|
+
}
|
|
307
|
+
function normalizeAuditPermissionMode(value) {
|
|
308
|
+
if (!value || value === 'unknown')
|
|
309
|
+
return value;
|
|
310
|
+
return normalizeExecutionPermissionMode(value);
|
|
311
|
+
}
|
|
180
312
|
function redactIdentifier(value) {
|
|
181
313
|
if (!value)
|
|
182
314
|
return undefined;
|
|
@@ -212,6 +344,7 @@ function logAuditEvent(record) {
|
|
|
212
344
|
record.correlationId ? `correlation=${record.correlationId}` : null,
|
|
213
345
|
record.eventKey ? `eventKey=${record.eventKey}` : null,
|
|
214
346
|
record.sessionId ? `session=${record.sessionId}` : null,
|
|
347
|
+
record.agentName ? `agentName=${JSON.stringify(record.agentName)}` : null,
|
|
215
348
|
record.agentAid ? `agent=${record.agentAid}` : null,
|
|
216
349
|
record.permissionMode ? `permissionMode=${record.permissionMode}` : null,
|
|
217
350
|
record.toolName ? `tool=${record.toolName}` : null,
|
|
@@ -296,6 +429,21 @@ function summarizeAuthorizationArgs(operation, args) {
|
|
|
296
429
|
for (const [key, value] of Object.entries(args)) {
|
|
297
430
|
if (value === undefined)
|
|
298
431
|
continue;
|
|
432
|
+
if (key === 'parseFailure' && value && typeof value === 'object' && !Array.isArray(value)) {
|
|
433
|
+
const diagnostic = value;
|
|
434
|
+
const issue = typeof diagnostic.issue === 'string' ? diagnostic.issue : undefined;
|
|
435
|
+
const offset = typeof diagnostic.offset === 'number' && Number.isFinite(diagnostic.offset)
|
|
436
|
+
? Math.max(0, Math.floor(diagnostic.offset)) : undefined;
|
|
437
|
+
const tokenIndex = typeof diagnostic.tokenIndex === 'number' && Number.isFinite(diagnostic.tokenIndex)
|
|
438
|
+
? Math.max(0, Math.floor(diagnostic.tokenIndex)) : undefined;
|
|
439
|
+
const dialect = typeof diagnostic.dialect === 'string' ? diagnostic.dialect : undefined;
|
|
440
|
+
const inputLength = typeof diagnostic.inputLength === 'number' && Number.isFinite(diagnostic.inputLength)
|
|
441
|
+
? Math.max(0, Math.floor(diagnostic.inputLength)) : undefined;
|
|
442
|
+
if (issue && offset !== undefined && tokenIndex !== undefined && dialect && inputLength !== undefined) {
|
|
443
|
+
summary.parseFailure = { issue, offset, tokenIndex, dialect, inputLength };
|
|
444
|
+
}
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
299
447
|
if (/^(?:self|peer|peerKey|target|targetId|session|sessionId|channelId)$/i.test(key)) {
|
|
300
448
|
summary[key] = typeof value === 'string' ? redactIdentifier(value) : '[redacted]';
|
|
301
449
|
continue;
|
|
@@ -95,6 +95,20 @@ function authorizeCommandInternal(ctx, resolvedConfigCommand) {
|
|
|
95
95
|
&& (resolvedConfigCommand.operationId !== operation || resolvedConfigCommand.commandScope !== intent.scope)) {
|
|
96
96
|
return denyDecision(ctx, 'NOT_ALLOWED', 'Resolved config operation does not match the command intent', operation, intent.scope, opMeta.dangerous);
|
|
97
97
|
}
|
|
98
|
+
// A fullaccess task is deliberately a daemon-scoped runtime identity. It
|
|
99
|
+
// still has to name a known operation and pass structural command parsing,
|
|
100
|
+
// but relation/Agent role rules, source lists, and daemon-owner gates are
|
|
101
|
+
// not meaningful limits for code already approved for host-level execution.
|
|
102
|
+
if (ctx.processRole === 'fullaccess-run' && ctx.dataScope === 'daemon') {
|
|
103
|
+
return {
|
|
104
|
+
allow: true,
|
|
105
|
+
operation,
|
|
106
|
+
scope: intent.scope,
|
|
107
|
+
role: 'fullaccess-run',
|
|
108
|
+
dangerous: opMeta.dangerous,
|
|
109
|
+
matchedRule: 'fullaccess-run-daemon-scope',
|
|
110
|
+
};
|
|
111
|
+
}
|
|
98
112
|
if (ctx.chatType === 'group'
|
|
99
113
|
&& (resolvedConfigCommand ? isResolvedConfigMutation(resolvedConfigCommand) : isConfigMutationOperation(operation))
|
|
100
114
|
&& intent.scope !== 'relation') {
|
|
@@ -175,7 +189,31 @@ function authorizeCommandInternal(ctx, resolvedConfigCommand) {
|
|
|
175
189
|
if (roleDef.allowAccess === false) {
|
|
176
190
|
return denyDecision(ctx, 'ROLE_ACCESS_DENIED', `Role ${role} is not allowed to access commands`, operation, intent.scope, opMeta.dangerous);
|
|
177
191
|
}
|
|
178
|
-
|
|
192
|
+
let matchResult = matchCommandPermission(operation, opMeta.category, roleDef.commandPermissions || {}, opMeta.dangerous);
|
|
193
|
+
// Agent self-service contact add deliberately reuses the same low-trust
|
|
194
|
+
// relation boundary as ec.msg.send. Older per-Agent role files may still
|
|
195
|
+
// contain the historical contact.* deny without an explicit contact.add
|
|
196
|
+
// entry; preserve their safe current-peer/private-only send capability while
|
|
197
|
+
// allowing this new request workflow. An explicit contact.add rule wins.
|
|
198
|
+
if (operation === 'contact.add'
|
|
199
|
+
&& !Object.prototype.hasOwnProperty.call(roleDef.commandPermissions || {}, 'contact.add')) {
|
|
200
|
+
const sendPermission = roleDef.commandPermissions?.['ec.msg.send'];
|
|
201
|
+
if (sendPermission?.allow) {
|
|
202
|
+
matchResult = {
|
|
203
|
+
permission: {
|
|
204
|
+
...sendPermission,
|
|
205
|
+
scopes: ['relation'],
|
|
206
|
+
constraints: {
|
|
207
|
+
...(sendPermission.constraints ?? {}),
|
|
208
|
+
ownPeerOnly: true,
|
|
209
|
+
privateOnly: true,
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
matchedRule: 'ec.msg.send',
|
|
213
|
+
rank: 1000,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
179
217
|
if (!matchResult) {
|
|
180
218
|
return denyDecision(ctx, 'NO_PERMISSION', `Role ${role} has no permission for ${operation}`, operation, intent.scope, opMeta.dangerous);
|
|
181
219
|
}
|
|
@@ -274,6 +312,8 @@ function requiresDaemonOwner(ctx, resolvedConfigCommand) {
|
|
|
274
312
|
const targetAgentAid = stringArg(args.targetAgentAid) ?? stringArg(args.self);
|
|
275
313
|
return !!taskAgentAid && !!targetAgentAid && taskAgentAid !== targetAgentAid;
|
|
276
314
|
}
|
|
315
|
+
if (operation.startsWith('role.') && ctx.fromControlChannel)
|
|
316
|
+
return true;
|
|
277
317
|
return false;
|
|
278
318
|
}
|
|
279
319
|
function hasDaemonOwnerRole(ctx) {
|
|
@@ -55,7 +55,7 @@ const GROUP_MANAGEMENT_OPERATIONS = [
|
|
|
55
55
|
sources: ['agent-tool'],
|
|
56
56
|
}));
|
|
57
57
|
const MSG_READ_OPERATIONS = [
|
|
58
|
-
['ec.msg.history', 'Read message history for
|
|
58
|
+
['ec.msg.history', 'Read message history for a controlled private relation'],
|
|
59
59
|
['ec.msg.online', 'Read online status for an AUN private relation'],
|
|
60
60
|
].map(([id, description]) => ({
|
|
61
61
|
id,
|
|
@@ -476,6 +476,14 @@ const OPERATIONS = [
|
|
|
476
476
|
description: 'Read the Agent contact block list',
|
|
477
477
|
sources: ['agent-tool'],
|
|
478
478
|
},
|
|
479
|
+
{
|
|
480
|
+
id: 'contact.add',
|
|
481
|
+
category: 'write-agent',
|
|
482
|
+
dangerous: false,
|
|
483
|
+
defaultScopes: ['relation'],
|
|
484
|
+
description: 'Request adding the current private relation as an Agent contact',
|
|
485
|
+
sources: ['agent-tool'],
|
|
486
|
+
},
|
|
479
487
|
{
|
|
480
488
|
id: 'contact.block',
|
|
481
489
|
category: 'write-agent',
|
|
@@ -2,7 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { randomBytes } from 'crypto';
|
|
4
4
|
import { kitsTemplatesDir, agentMdPath } from '../paths.js';
|
|
5
|
-
import { resolveAgentDisplayName } from '../aun/aid/agentmd.js';
|
|
5
|
+
import { buildInitialAgentMd, resolveAgentDisplayName } from '../aun/aid/agentmd.js';
|
|
6
6
|
import { logger } from '../utils/logger.js';
|
|
7
7
|
import { loadAgent, saveAgent } from '../config-store.js';
|
|
8
8
|
import { resolveAgentLifecycle, withLifecycleForWrite } from '../config/lifecycle.js';
|
|
@@ -292,7 +292,11 @@ export class BootstrapService {
|
|
|
292
292
|
if (typeof adapter.uploadAgentMd !== 'function')
|
|
293
293
|
return;
|
|
294
294
|
const existing = fs.existsSync(agentMdPath(aid)) ? fs.readFileSync(agentMdPath(aid), 'utf-8') : '';
|
|
295
|
-
const content = existing.trim() ||
|
|
295
|
+
const content = existing.trim() || buildInitialAgentMd({
|
|
296
|
+
aid,
|
|
297
|
+
name: fallbackName,
|
|
298
|
+
type: 'codeagent',
|
|
299
|
+
});
|
|
296
300
|
try {
|
|
297
301
|
await adapter.uploadAgentMd(content);
|
|
298
302
|
logger.info(`[Bootstrap] Published agent.md for ${aid}`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { resolvePaths } from '../../paths.js';
|
|
4
|
-
import { atomicReadJson,
|
|
4
|
+
import { atomicReadJson, stableWriteJson } from '../../utils/atomic-write.js';
|
|
5
5
|
import { cloneCausation, normalizeCausation } from './context.js';
|
|
6
6
|
const DEFAULT_TTL_MS = 30 * 60 * 1000;
|
|
7
7
|
const associations = new Map();
|
|
@@ -44,9 +44,12 @@ function ensureLoaded() {
|
|
|
44
44
|
}
|
|
45
45
|
function persist() {
|
|
46
46
|
try {
|
|
47
|
-
// Keep the protected mount target
|
|
48
|
-
//
|
|
49
|
-
|
|
47
|
+
// Keep the protected mount target's inode stable. Bubblewrap binds this
|
|
48
|
+
// H-class file by pathname while constructing its namespace; an atomic
|
|
49
|
+
// rename can otherwise replace the inode during bind/remount and make
|
|
50
|
+
// sandbox initialization fail. Causation state is best-effort and small,
|
|
51
|
+
// so an in-place write is preferable to changing the protected inode.
|
|
52
|
+
stableWriteJson(associationFile(), [...associations].map(([messageId, association]) => ({
|
|
50
53
|
messageId,
|
|
51
54
|
...association,
|
|
52
55
|
})));
|
|
@@ -8,6 +8,7 @@ import { agentProjectRootFromDefaults, deriveAgentProjectPath } from '../../util
|
|
|
8
8
|
import { uploadAvatar } from '../../utils/avatar-upload.js';
|
|
9
9
|
import { agentmdGet, agentmdPut, updateAgentMdFrontmatterName } from '../../aun/aid/agentmd.js';
|
|
10
10
|
import { isValidAid } from '../../aun/aid/validation.js';
|
|
11
|
+
import { isSupportedBaseagent } from '../../agents/baseagent.js';
|
|
11
12
|
const SUPPORTED_AGENT_PATCH_FIELDS = new Set(['aid', 'name', 'avatar', 'active_baseagent', 'baseagents', 'projects', 'owners', 'chatmode', 'channels', 'channelOwners']);
|
|
12
13
|
const HIDDEN_VALUE = '[hidden]';
|
|
13
14
|
const SENSITIVE_CONFIG_KEYS = new Set([
|
|
@@ -95,6 +96,9 @@ function normalizeBaseagentsPatch(value) {
|
|
|
95
96
|
}
|
|
96
97
|
const out = {};
|
|
97
98
|
for (const [name, raw] of Object.entries(value)) {
|
|
99
|
+
if (!isSupportedBaseagent(name)) {
|
|
100
|
+
return { ok: false, error: `不支持的 baseagent: ${name}(可选: claude/codex/gemini/ecagent)` };
|
|
101
|
+
}
|
|
98
102
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
99
103
|
return { ok: false, error: `baseagents.${name} 必须是对象` };
|
|
100
104
|
}
|
|
@@ -286,11 +290,11 @@ function buildCreateProgressOnlyAgent(aid, progress) {
|
|
|
286
290
|
* 失败仅写日志 + create-status,不回传(受理即返回)。
|
|
287
291
|
* agentSet key 对照 cli/agent.ts 的 setNestedValue:
|
|
288
292
|
* model/effort → 'baseagents.<baseagent>.*';chatmode → 'chatmode'(ChatmodeBlock 对象)。 */
|
|
289
|
-
async function runCreateInBackground(opts, w) {
|
|
293
|
+
async function runCreateInBackground(opts, w, service) {
|
|
290
294
|
let curPhase = 'validating'; // 跟踪当前环节,供 catch 兜底时标注正确 phase
|
|
291
295
|
try {
|
|
292
296
|
// onPhase 把 agentCreateNonInteractive 内部环节(0-3、5)映射到进度文件
|
|
293
|
-
const
|
|
297
|
+
const createOptions = {
|
|
294
298
|
aid: opts.aid, name: opts.name, baseagent: opts.baseagent,
|
|
295
299
|
project: opts.project, owner: opts.owner,
|
|
296
300
|
onPhase: (phase, state, detail) => {
|
|
@@ -305,7 +309,10 @@ async function runCreateInBackground(opts, w) {
|
|
|
305
309
|
else if (state === 'failed')
|
|
306
310
|
w.finishFailed(phase, detail ?? 'failed');
|
|
307
311
|
},
|
|
308
|
-
}
|
|
312
|
+
};
|
|
313
|
+
const res = service
|
|
314
|
+
? await service.create(createOptions)
|
|
315
|
+
: await agentCreateNonInteractive(createOptions);
|
|
309
316
|
if (!('ok' in res) || res.ok !== true) {
|
|
310
317
|
// 硬失败:onPhase('failed') 已写终态;这里仅兜底日志(防回调未覆盖的 return 路径)
|
|
311
318
|
const err = res.error;
|
|
@@ -318,17 +325,23 @@ async function runCreateInBackground(opts, w) {
|
|
|
318
325
|
w.begin('applying_config');
|
|
319
326
|
let warned;
|
|
320
327
|
if (opts.model) {
|
|
321
|
-
const r =
|
|
328
|
+
const r = service
|
|
329
|
+
? await service.setConfig(opts.aid, `baseagents.${opts.baseagent}.model`, opts.model)
|
|
330
|
+
: await agentSet(opts.aid, `baseagents.${opts.baseagent}.model`, opts.model);
|
|
322
331
|
if (!('ok' in r) || !r.ok)
|
|
323
332
|
warned = `model: ${r.error}`;
|
|
324
333
|
}
|
|
325
334
|
if (opts.effort) {
|
|
326
|
-
const r =
|
|
335
|
+
const r = service
|
|
336
|
+
? await service.setConfig(opts.aid, `baseagents.${opts.baseagent}.effort`, opts.effort)
|
|
337
|
+
: await agentSet(opts.aid, `baseagents.${opts.baseagent}.effort`, opts.effort);
|
|
327
338
|
if (!('ok' in r) || !r.ok)
|
|
328
339
|
warned = `${warned ? warned + '; ' : ''}effort: ${r.error}`;
|
|
329
340
|
}
|
|
330
341
|
if (opts.chatmode) {
|
|
331
|
-
const r =
|
|
342
|
+
const r = service
|
|
343
|
+
? await service.setConfig(opts.aid, 'chatmode', JSON.stringify(opts.chatmode))
|
|
344
|
+
: await agentSet(opts.aid, 'chatmode', JSON.stringify(opts.chatmode));
|
|
332
345
|
if (!('ok' in r) || !r.ok)
|
|
333
346
|
warned = `${warned ? warned + '; ' : ''}chatmode: ${r.error}`;
|
|
334
347
|
}
|
|
@@ -361,7 +374,7 @@ async function runCreateInBackground(opts, w) {
|
|
|
361
374
|
/** name=agent 的 menu.action 执行。create 使用显式 owner AID;peerId 仅作为控制面鉴权主体。
|
|
362
375
|
* create 受理即返回(D3);delete/enable/disable 同步等结果。
|
|
363
376
|
* 调用方负责传入已兜底的 args.project(见 command-handler 装配)。 */
|
|
364
|
-
export async function execAgentAction(action, args, peerId, eventBus) {
|
|
377
|
+
export async function execAgentAction(action, args, peerId, eventBus, service) {
|
|
365
378
|
const a = args ?? {};
|
|
366
379
|
if (action === 'create') {
|
|
367
380
|
if (!peerId)
|
|
@@ -394,13 +407,15 @@ export async function execAgentAction(action, args, peerId, eventBus) {
|
|
|
394
407
|
project: a.project, owner,
|
|
395
408
|
model, effort, chatmode: a.chatmode,
|
|
396
409
|
eventBus,
|
|
397
|
-
}, progressWriter).catch(e => logger.error(`[agent-control] runCreateInBackground unhandled ${a.aid}: ${e?.message || e}`));
|
|
410
|
+
}, progressWriter, service).catch(e => logger.error(`[agent-control] runCreateInBackground unhandled ${a.aid}: ${e?.message || e}`));
|
|
398
411
|
return { data: { accepted: true, aid: a.aid } };
|
|
399
412
|
}
|
|
400
413
|
if (action === 'delete') {
|
|
401
414
|
if (!a.aid)
|
|
402
415
|
return { error: '缺少 aid', code: 'INVALID_ARGS' };
|
|
403
|
-
const res =
|
|
416
|
+
const res = service
|
|
417
|
+
? await service.delete(a.aid, false)
|
|
418
|
+
: await agentDelete(a.aid, false);
|
|
404
419
|
if (!('ok' in res) || res.ok !== true)
|
|
405
420
|
return { error: res.error, code: classifyError(res.error) };
|
|
406
421
|
eventBus?.publish({ type: 'agent:deleted', aid: res.aid, purged: res.purged, timestamp: Date.now() });
|
|
@@ -410,7 +425,13 @@ export async function execAgentAction(action, args, peerId, eventBus) {
|
|
|
410
425
|
if (!a.aid)
|
|
411
426
|
return { error: '缺少 aid', code: 'INVALID_ARGS' };
|
|
412
427
|
const options = { force: a.force === true };
|
|
413
|
-
const res =
|
|
428
|
+
const res = service
|
|
429
|
+
? action === 'enable'
|
|
430
|
+
? await service.enable(a.aid, options.force === true)
|
|
431
|
+
: await service.disable(a.aid, options.force === true)
|
|
432
|
+
: action === 'enable'
|
|
433
|
+
? await agentEnable(a.aid, options)
|
|
434
|
+
: await agentDisable(a.aid, options);
|
|
414
435
|
if (!('ok' in res) || res.ok !== true)
|
|
415
436
|
return { error: res.error, code: res.code || classifyError(res.error) };
|
|
416
437
|
eventBus?.publish({
|
|
@@ -424,7 +445,9 @@ export async function execAgentAction(action, args, peerId, eventBus) {
|
|
|
424
445
|
if (action === 'reload') {
|
|
425
446
|
if (!a.aid)
|
|
426
447
|
return { error: '缺少 aid', code: 'INVALID_ARGS' };
|
|
427
|
-
const res =
|
|
448
|
+
const res = service
|
|
449
|
+
? await service.reload(a.aid, { force: a.force === true })
|
|
450
|
+
: await agentReload(a.aid, { force: a.force === true });
|
|
428
451
|
if (!('ok' in res) || res.ok !== true)
|
|
429
452
|
return { error: res.error, code: res.code || classifyError(res.error) };
|
|
430
453
|
eventBus?.publish({ type: 'agent:reloaded', aid: a.aid, timestamp: Date.now() });
|
|
@@ -508,6 +531,9 @@ export async function execAgentUpdate(args) {
|
|
|
508
531
|
if (typeof p.active_baseagent !== 'string' || !p.active_baseagent.trim()) {
|
|
509
532
|
return { error: `无效 active_baseagent: ${JSON.stringify(p.active_baseagent)}(必须是非空字符串)`, code: 'INVALID_ARGS' };
|
|
510
533
|
}
|
|
534
|
+
if (!isSupportedBaseagent(p.active_baseagent.trim())) {
|
|
535
|
+
return { error: `不支持的 active_baseagent: ${p.active_baseagent}(可选: claude/codex/gemini/ecagent)`, code: 'INVALID_ARGS' };
|
|
536
|
+
}
|
|
511
537
|
config.active_baseagent = p.active_baseagent.trim();
|
|
512
538
|
touched = true;
|
|
513
539
|
}
|
|
@@ -576,7 +602,7 @@ export async function execAgentUpdate(args) {
|
|
|
576
602
|
/**
|
|
577
603
|
* project 解析与 ec agent create 保持一致:
|
|
578
604
|
* 显式值 > 默认项目根目录 + AID 前缀。
|
|
579
|
-
* 默认项目根目录由 defaults.projects.
|
|
605
|
+
* 默认项目根目录由 defaults.projects.defaultPath 或运行时根目录推导。
|
|
580
606
|
*/
|
|
581
607
|
export function resolveProjectPath(explicit, aid, defaults) {
|
|
582
608
|
if (explicit && explicit.trim())
|
|
@@ -585,13 +611,15 @@ export function resolveProjectPath(explicit, aid, defaults) {
|
|
|
585
611
|
return deriveAgentProjectPath(root, aid);
|
|
586
612
|
}
|
|
587
613
|
/** name=agent 的 menu.query:查单个 agent 详情,附构建进度(D3)。 */
|
|
588
|
-
export async function execAgentQuery(args) {
|
|
614
|
+
export async function execAgentQuery(args, service) {
|
|
589
615
|
const aid = args?.aid;
|
|
590
616
|
if (!aid)
|
|
591
617
|
return { error: '缺少 aid', code: 'INVALID_ARGS' };
|
|
592
618
|
const agentDir = path.join(resolvePaths().agentsDir, aid);
|
|
593
619
|
const progress = readCreateStatus(agentDir);
|
|
594
|
-
const res =
|
|
620
|
+
const res = service
|
|
621
|
+
? await service.show(aid)
|
|
622
|
+
: await agentShow(aid);
|
|
595
623
|
if (!('ok' in res) || res.ok !== true) {
|
|
596
624
|
const code = classifyError(res.error);
|
|
597
625
|
if (code === 'NOT_FOUND' && progress)
|
|
@@ -616,9 +644,21 @@ export async function execAgentQuery(args) {
|
|
|
616
644
|
return { data: progress ? { ...data, createProgress: progress } : data };
|
|
617
645
|
}
|
|
618
646
|
/** name=agent 的 menu.options:列出 agent(enabled 默认 / all) */
|
|
619
|
-
export async function execAgentOptions(args) {
|
|
647
|
+
export async function execAgentOptions(args, service) {
|
|
620
648
|
const scope = args?.options === 'all' ? 'all' : 'enabled';
|
|
621
|
-
const res =
|
|
649
|
+
const res = service
|
|
650
|
+
? { ok: true, agents: service.list().map(info => ({
|
|
651
|
+
aid: info.aid || info.name,
|
|
652
|
+
name: info.name,
|
|
653
|
+
personalName: info.personalName || null,
|
|
654
|
+
status: info.status || 'stopped',
|
|
655
|
+
channels: info.channels || [],
|
|
656
|
+
projectPath: info.projectPath ? toPosix(info.projectPath) : null,
|
|
657
|
+
baseagent: info.baseagent || null,
|
|
658
|
+
model: info.model || null,
|
|
659
|
+
lastActivity: info.lastActivity || null,
|
|
660
|
+
})) }
|
|
661
|
+
: await agentList();
|
|
622
662
|
if (!('ok' in res) || res.ok !== true)
|
|
623
663
|
return { error: res.error, code: classifyError(res.error) };
|
|
624
664
|
const agents = scope === 'all'
|