evolcore 0.0.10 → 0.0.12
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 +44 -0
- package/README.md +3 -3
- package/bin/codex-managed-hook.mjs +80 -0
- package/dist/agents/baseagent.js +4 -0
- package/dist/agents/claude-runner.js +158 -44
- package/dist/agents/codex-app-server-client.js +153 -23
- package/dist/agents/codex-runner.js +331 -43
- package/dist/agents/ecagent-runner.js +53 -6
- package/dist/agents/gemini-runner.js +1 -0
- package/dist/agents/request-identity.js +55 -0
- package/dist/aun/aid/client.js +11 -21
- package/dist/aun/aid/managed-operation.js +107 -0
- package/dist/aun/msg/group.js +81 -12
- package/dist/aun/msg/managed-operation.js +804 -0
- package/dist/aun/msg/mention-schema.js +20 -0
- package/dist/aun/msg/p2p.js +7 -0
- package/dist/aun/outbox.js +28 -31
- package/dist/channels/aun.js +418 -235
- package/dist/channels/daemon.js +13 -0
- package/dist/cli/agent-command.js +72 -17
- package/dist/cli/agent.js +249 -22
- package/dist/cli/aun-commands.js +270 -69
- package/dist/cli/bench.js +12 -3
- package/dist/cli/daemon-commands.js +52 -33
- package/dist/cli/fs-command.js +60 -0
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +106 -63
- package/dist/cli/init-cancel.js +208 -0
- package/dist/cli/init-channel.js +343 -195
- package/dist/cli/init.js +21 -9
- package/dist/cli/managed-command-guard.js +41 -0
- package/dist/cli/model.js +11 -4
- package/dist/cli/queue-command.js +11 -1
- package/dist/cli/response.js +71 -0
- package/dist/cli/restart-monitor.js +4 -18
- package/dist/cli/trigger-command.js +4 -0
- package/dist/config/builtin-roles.js +5 -0
- package/dist/config/contact-book-store.js +11 -3
- package/dist/config/contact-request-service.js +161 -15
- package/dist/config/gateway-config.js +26 -10
- package/dist/config/mention-mode.js +8 -24
- package/dist/core/agent-reload-coordinator.js +53 -0
- package/dist/core/auth/agent-delegation.js +0 -1
- package/dist/core/auth/operation-authorizer.js +62 -154
- package/dist/core/auth/operation-catalog.js +264 -22
- package/dist/core/bootstrap-messages.js +68 -0
- package/dist/core/bootstrap-service.js +144 -10
- package/dist/core/capability/providers/claude-capability-provider.js +9 -31
- package/dist/core/capability/providers/codex-capability-provider.js +4 -15
- package/dist/core/capability/skill-discovery.js +55 -0
- package/dist/core/channel-loader.js +24 -7
- package/dist/core/command/agent-control.js +14 -11
- package/dist/core/command/cli-intent-parser.js +9 -0
- package/dist/core/command/command-handler.js +467 -6
- package/dist/core/command/connect-menu.js +15 -0
- package/dist/core/command/group-menu.js +4 -4
- package/dist/core/command/menu-catalog.js +366 -0
- package/dist/core/command/menu-handler.js +213 -110
- package/dist/core/command/menu-protocol.js +71 -0
- package/dist/core/command/slash-gate.js +5 -2
- package/dist/core/command/slash-handler.js +45 -25
- package/dist/core/daemon-file-cache.js +2 -1
- package/dist/core/data-migration.js +9 -2
- package/dist/core/evolagent-registry.js +125 -35
- package/dist/core/evolagent.js +12 -5
- package/dist/core/handoff/dispatcher.js +5 -2
- package/dist/core/handoff/runtime.js +145 -21
- package/dist/core/handoff/store.js +24 -0
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/inference/text-inference.js +38 -4
- package/dist/core/message/logical-queue-bridge.js +1 -0
- package/dist/core/message/mention-schema.js +126 -0
- package/dist/core/message/message-bridge.js +237 -79
- package/dist/core/message/message-log.js +23 -0
- package/dist/core/message/message-queue.js +20 -4
- package/dist/core/message/response-engine.js +54 -36
- package/dist/core/model/model-catalog.js +143 -24
- package/dist/core/model/model-diagnostics.js +28 -10
- package/dist/core/permission/ec-command-parser.js +156 -10
- package/dist/core/permission/index.js +1 -0
- package/dist/core/permission/readonly-shell-query.js +532 -0
- package/dist/core/permission/sandbox-runtime.js +22 -1
- package/dist/core/permission/shell-environment.js +46 -0
- package/dist/core/permission/tool-policy.js +292 -111
- package/dist/core/protected-paths.js +17 -9
- package/dist/core/runner-reload-transaction.js +57 -0
- package/dist/core/session/session-manager.js +21 -2
- package/dist/eck/manifest-engine.js +39 -13
- package/dist/index.js +644 -180
- package/dist/ipc.js +246 -19
- package/dist/response-system/coordinator.js +6 -4
- package/dist/response-system/engines/v1/proactive-flow.js +26 -8
- package/dist/response-system/modes/single-session/index.js +14 -1
- package/dist/response-system/selector.js +6 -5
- package/dist/trigger/anomaly-store.js +4 -0
- package/dist/trigger/script-executor.js +1 -0
- package/dist/utils/aid-bind.js +3 -8
- package/dist/utils/codex-app-server-registry.js +90 -0
- package/dist/utils/codex-cli.js +5 -1
- package/dist/utils/cross-platform.js +15 -0
- package/dist/utils/log-writer.js +6 -10
- package/dist/utils/logger.js +5 -5
- package/dist/utils/npm-ops.js +5 -12
- package/dist/utils/tool-summary.js +11 -0
- package/kits/docs/channels/aun.md +1 -1
- package/kits/docs/context-assembly.md +2 -2
- package/kits/docs/evolcore/agent.md +11 -6
- package/kits/docs/evolcore/aid.md +14 -0
- package/kits/docs/evolcore/msg.md +13 -0
- package/kits/docs/prompt-loading-architecture.md +1 -2
- package/kits/docs/venues/group.md +1 -1
- package/kits/eck_manifest.json +0 -12
- package/kits/rules/01-overview.md +9 -0
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/05-venue.md +2 -2
- package/kits/schemas/agent-config.schema.3.json +1 -1
- package/kits/schemas/agent-config.schema.4.json +1 -1
- package/kits/schemas/relation-config.schema.2.json +1 -1
- package/kits/schemas/role-config.schema.1.json +1 -1
- package/kits/schemas/single-session.schema.1.json +3 -3
- package/kits/schemas/single-session.schema.2.json +3 -3
- package/kits/templates/roles/admin.json +27 -0
- package/kits/templates/roles/member.json +44 -0
- package/kits/templates/roles/visitor.json +40 -2
- package/kits/templates/system-fragments/bootstrap.md +12 -6
- package/kits/templates/system-fragments/channel.md +6 -0
- package/kits/templates/system-fragments/session.md +2 -0
- package/package.json +3 -2
- package/skills/eclink/SKILL.md +15 -3
- package/skills/eclink/agents/openai.yaml +3 -3
- package/dist/core/command/evol-menu-version-gate.js +0 -39
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import crypto from 'crypto';
|
|
2
2
|
import fs from 'fs';
|
|
3
|
-
import path from 'path';
|
|
4
3
|
import { isValidAid } from '../aun/aid/validation.js';
|
|
5
4
|
import { agentContactOperations } from '../paths.js';
|
|
6
5
|
import { listStaticAgentAdmins, listStaticAgentOwners } from './peer-role-resolver.js';
|
|
@@ -12,6 +11,7 @@ export const CONTACT_REQUEST_MAX_PER_WINDOW = 3;
|
|
|
12
11
|
export const CONTACT_REQUEST_MAX_PENDING = 100;
|
|
13
12
|
export const CONTACT_REQUEST_MAX_RETAINED = 1000;
|
|
14
13
|
export const CONTACT_REQUEST_NOTE_MAX_LENGTH = 500;
|
|
14
|
+
export const CONTACT_REQUEST_ID_MAX_LENGTH = 128;
|
|
15
15
|
export async function mutateContactWithOperation(input) {
|
|
16
16
|
const at = new Date(input.now ?? Date.now()).toISOString();
|
|
17
17
|
const logFile = agentContactOperations(input.selfAid);
|
|
@@ -26,7 +26,7 @@ export async function mutateContactWithOperation(input) {
|
|
|
26
26
|
mutation: input.mutation,
|
|
27
27
|
actor: input.actorId,
|
|
28
28
|
value: undefined,
|
|
29
|
-
beforeCommit: ({ contactRevision: nextRevision }) =>
|
|
29
|
+
beforeCommit: ({ contactRevision: nextRevision }) => operationLogChange(logFile, [operationRecord({
|
|
30
30
|
at,
|
|
31
31
|
op: input.operation,
|
|
32
32
|
primaryId: input.primaryId,
|
|
@@ -73,7 +73,17 @@ export async function submitContactRequest(input) {
|
|
|
73
73
|
&& Date.parse(record.at) > windowStart
|
|
74
74
|
&& Date.parse(record.at) <= nowMs)).length;
|
|
75
75
|
if (recentCount >= CONTACT_REQUEST_MAX_PER_WINDOW) {
|
|
76
|
-
return preparedWithExpirations(expiration, {
|
|
76
|
+
return preparedWithExpirations(expiration, {
|
|
77
|
+
code: 'rate-limited',
|
|
78
|
+
...(effectiveEntry?.status === 'pending' && effectiveEntry.pendingRequest
|
|
79
|
+
? {
|
|
80
|
+
requestId: effectiveEntry.pendingRequest.id,
|
|
81
|
+
submittedAt: effectiveEntry.pendingRequest.submittedAt,
|
|
82
|
+
expiresAt: effectiveEntry.pendingRequest.expiresAt,
|
|
83
|
+
}
|
|
84
|
+
: {}),
|
|
85
|
+
contactRevision: '',
|
|
86
|
+
}, logFile);
|
|
77
87
|
}
|
|
78
88
|
const counts = countRequestRecordsAfterExpirations(contact, nowMs);
|
|
79
89
|
const isExistingPending = effectiveEntry?.status === 'pending';
|
|
@@ -103,13 +113,14 @@ export async function submitContactRequest(input) {
|
|
|
103
113
|
mutation: { type: 'batch', mutations },
|
|
104
114
|
actor: applicantAid,
|
|
105
115
|
value,
|
|
106
|
-
beforeCommit: ({ contactRevision }) =>
|
|
116
|
+
beforeCommit: ({ contactRevision }) => operationLogChange(logFile, [
|
|
107
117
|
...expiration.events.map(event => ({ ...event, contactRevision })),
|
|
108
118
|
operationRecord({
|
|
109
119
|
at: nowIso,
|
|
110
120
|
op,
|
|
111
121
|
primaryId: applicantAid,
|
|
112
122
|
requestId,
|
|
123
|
+
expiresAt,
|
|
113
124
|
sourceChannelKey: input.sourceChannelKey,
|
|
114
125
|
...(note ? { note } : {}),
|
|
115
126
|
contactRevision,
|
|
@@ -143,6 +154,10 @@ export async function reviewContactRequest(input) {
|
|
|
143
154
|
if (!entry || entry.status !== 'pending' || entry.pendingRequest?.id !== input.requestId) {
|
|
144
155
|
return preparedNoop({ code: 'request-stale', contactRevision });
|
|
145
156
|
}
|
|
157
|
+
const requestMetadata = {
|
|
158
|
+
requestId: entry.pendingRequest.id,
|
|
159
|
+
expiresAt: entry.pendingRequest.expiresAt,
|
|
160
|
+
};
|
|
146
161
|
if (Date.parse(entry.pendingRequest.expiresAt) <= nowMs) {
|
|
147
162
|
const mutation = {
|
|
148
163
|
type: 'expire-request',
|
|
@@ -153,8 +168,8 @@ export async function reviewContactRequest(input) {
|
|
|
153
168
|
return {
|
|
154
169
|
mutation,
|
|
155
170
|
actor: input.actorId,
|
|
156
|
-
value: { code: 'request-expired', contactRevision },
|
|
157
|
-
beforeCommit: ({ contactRevision: nextRevision }) =>
|
|
171
|
+
value: { code: 'request-expired', contactRevision, ...requestMetadata },
|
|
172
|
+
beforeCommit: ({ contactRevision: nextRevision }) => operationLogChange(logFile, [operationRecord({
|
|
158
173
|
at: nowIso,
|
|
159
174
|
op: 'expire',
|
|
160
175
|
primaryId: input.primaryId,
|
|
@@ -177,8 +192,8 @@ export async function reviewContactRequest(input) {
|
|
|
177
192
|
now: nowIso,
|
|
178
193
|
},
|
|
179
194
|
actor: input.actorId,
|
|
180
|
-
value: { code, contactRevision },
|
|
181
|
-
beforeCommit: ({ contactRevision: nextRevision }) =>
|
|
195
|
+
value: { code, contactRevision, ...requestMetadata },
|
|
196
|
+
beforeCommit: ({ contactRevision: nextRevision }) => operationLogChange(logFile, [operationRecord({
|
|
182
197
|
at: nowIso,
|
|
183
198
|
op: input.decision === 'approve' ? 'approve' : input.decision === 'reject' ? 'reject' : 'block',
|
|
184
199
|
primaryId: input.primaryId,
|
|
@@ -208,7 +223,7 @@ export async function expirePendingContactRequests(selfAid, now = Date.now()) {
|
|
|
208
223
|
mutation: { type: 'batch', mutations: expiration.mutations },
|
|
209
224
|
actor: 'contact-request-expiry',
|
|
210
225
|
value: expiration.mutations.length,
|
|
211
|
-
beforeCommit: ({ contactRevision }) =>
|
|
226
|
+
beforeCommit: ({ contactRevision }) => operationLogChange(logFile, expiration.events.map(event => ({ ...event, contactRevision }))),
|
|
212
227
|
};
|
|
213
228
|
});
|
|
214
229
|
return result.value;
|
|
@@ -226,6 +241,91 @@ export async function listPendingContactRequests(selfAid, now = Date.now()) {
|
|
|
226
241
|
}))));
|
|
227
242
|
return result.value;
|
|
228
243
|
}
|
|
244
|
+
/** Return only the authenticated applicant's own admission/request state. */
|
|
245
|
+
export async function queryOwnContactRequestStatus(input) {
|
|
246
|
+
const applicantAid = String(input.applicantAid || '').trim();
|
|
247
|
+
if (!isValidAid(applicantAid))
|
|
248
|
+
return protocolResult('NOT_ALLOWED');
|
|
249
|
+
const now = input.now ?? Date.now();
|
|
250
|
+
const rawRequestId = input.requestId;
|
|
251
|
+
if (rawRequestId !== undefined && typeof rawRequestId !== 'string')
|
|
252
|
+
return protocolResult('NOT_ALLOWED');
|
|
253
|
+
const requestedRequestId = rawRequestId?.trim();
|
|
254
|
+
if (requestedRequestId !== undefined
|
|
255
|
+
&& (!requestedRequestId || requestedRequestId.length > CONTACT_REQUEST_ID_MAX_LENGTH)) {
|
|
256
|
+
return protocolResult('NOT_ALLOWED');
|
|
257
|
+
}
|
|
258
|
+
const owners = listAgentOwners(input.selfAid);
|
|
259
|
+
const result = await mutateContactBookPrepared(input.selfAid, ({ contact }) => {
|
|
260
|
+
const logFile = agentContactOperations(input.selfAid);
|
|
261
|
+
const expiration = collectExpiredMutations(contact, now);
|
|
262
|
+
const entry = effectiveEntryAfterExpirations(contact.contacts[applicantAid], now);
|
|
263
|
+
const operations = readOperationLog(logFile).filter(record => record.primaryId === applicantAid);
|
|
264
|
+
const latestRequestOperation = [...operations].reverse().find(record => (!!record.requestId
|
|
265
|
+
&& (record.op === 'request' || record.op === 'resubmit' || record.op === 'approve'
|
|
266
|
+
|| record.op === 'reject' || record.op === 'expire' || record.op === 'block')));
|
|
267
|
+
if (requestedRequestId !== undefined && latestRequestOperation?.requestId !== requestedRequestId) {
|
|
268
|
+
return preparedNoop(protocolResult('NOT_ALLOWED'));
|
|
269
|
+
}
|
|
270
|
+
const requestMetadata = requestMetadataFor(operations, latestRequestOperation?.requestId);
|
|
271
|
+
let value;
|
|
272
|
+
if (owners.includes(applicantAid)
|
|
273
|
+
|| (entry && (entry.status === undefined || entry.status === 'active'))) {
|
|
274
|
+
value = protocolResult('APPROVED', requestMetadata);
|
|
275
|
+
}
|
|
276
|
+
else if (entry?.status === 'blocked') {
|
|
277
|
+
value = protocolResult('NOT_ALLOWED');
|
|
278
|
+
}
|
|
279
|
+
else if (entry?.status === 'pending' && entry.pendingRequest) {
|
|
280
|
+
value = protocolResult('PENDING', {
|
|
281
|
+
requestId: entry.pendingRequest.id,
|
|
282
|
+
expiresAt: entry.pendingRequest.expiresAt,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
else if (entry?.status === 'declined') {
|
|
286
|
+
value = protocolResult('EXPIRED', {
|
|
287
|
+
requestId: latestRequestOperation?.op === 'expire' ? latestRequestOperation.requestId : requestMetadata.requestId,
|
|
288
|
+
expiresAt: entry.declinedAt ?? requestMetadata.expiresAt,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
else if (latestRequestOperation?.op === 'reject') {
|
|
292
|
+
value = protocolResult('REJECTED', requestMetadata);
|
|
293
|
+
}
|
|
294
|
+
else if (latestRequestOperation?.op === 'expire') {
|
|
295
|
+
value = protocolResult('EXPIRED', requestMetadata);
|
|
296
|
+
}
|
|
297
|
+
else if (owners.length === 0) {
|
|
298
|
+
value = protocolResult('NOT_ALLOWED');
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
value = protocolResult('CONTACT_REQUIRED');
|
|
302
|
+
}
|
|
303
|
+
if (expiration.mutations.length === 0)
|
|
304
|
+
return preparedNoop(value);
|
|
305
|
+
return {
|
|
306
|
+
mutation: { type: 'batch', mutations: expiration.mutations },
|
|
307
|
+
actor: 'contact-request-expiry',
|
|
308
|
+
value,
|
|
309
|
+
beforeCommit: ({ contactRevision }) => operationLogChange(logFile, expiration.events.map(event => ({ ...event, contactRevision }))),
|
|
310
|
+
};
|
|
311
|
+
});
|
|
312
|
+
return result.value;
|
|
313
|
+
}
|
|
314
|
+
export function contactRequestSubmissionProtocolResult(result) {
|
|
315
|
+
const metadata = { requestId: result.requestId, expiresAt: result.expiresAt };
|
|
316
|
+
switch (result.code) {
|
|
317
|
+
case 'submitted':
|
|
318
|
+
case 'resubmitted':
|
|
319
|
+
return protocolResult('SUBMITTED', metadata);
|
|
320
|
+
case 'already-contact':
|
|
321
|
+
case 'owner':
|
|
322
|
+
return protocolResult('APPROVED', metadata);
|
|
323
|
+
case 'rate-limited':
|
|
324
|
+
return protocolResult('RATE_LIMITED', metadata);
|
|
325
|
+
default:
|
|
326
|
+
return protocolResult('NOT_ALLOWED', metadata);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
229
329
|
export function findContactRequestNote(selfAid, requestId) {
|
|
230
330
|
const records = readOperationLog(agentContactOperations(selfAid));
|
|
231
331
|
return [...records].reverse().find(record => (record.requestId === requestId
|
|
@@ -237,6 +337,24 @@ function normalizeNote(value) {
|
|
|
237
337
|
return null;
|
|
238
338
|
return note;
|
|
239
339
|
}
|
|
340
|
+
function requestMetadataFor(operations, requestId) {
|
|
341
|
+
if (!requestId)
|
|
342
|
+
return {};
|
|
343
|
+
const submitted = [...operations].reverse().find(record => (record.requestId === requestId && (record.op === 'request' || record.op === 'resubmit')));
|
|
344
|
+
const submittedAt = submitted ? Date.parse(submitted.at) : Number.NaN;
|
|
345
|
+
return {
|
|
346
|
+
requestId,
|
|
347
|
+
expiresAt: submitted?.expiresAt
|
|
348
|
+
?? (Number.isFinite(submittedAt) ? new Date(submittedAt + CONTACT_REQUEST_TTL_MS).toISOString() : undefined),
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function protocolResult(status, metadata = {}) {
|
|
352
|
+
return {
|
|
353
|
+
status,
|
|
354
|
+
requestId: metadata.requestId ?? null,
|
|
355
|
+
expiresAt: metadata.expiresAt ?? null,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
240
358
|
function collectExpiredMutations(book, now) {
|
|
241
359
|
const mutations = [];
|
|
242
360
|
const events = [];
|
|
@@ -286,7 +404,7 @@ function preparedWithExpirations(expiration, value, logFile) {
|
|
|
286
404
|
mutation: { type: 'batch', mutations: expiration.mutations },
|
|
287
405
|
actor: 'contact-request-expiry',
|
|
288
406
|
value,
|
|
289
|
-
beforeCommit: ({ contactRevision }) =>
|
|
407
|
+
beforeCommit: ({ contactRevision }) => operationLogChange(logFile, expiration.events.map(event => ({ ...event, contactRevision }))),
|
|
290
408
|
};
|
|
291
409
|
}
|
|
292
410
|
function preparedNoop(value) {
|
|
@@ -310,7 +428,14 @@ function readOperationLog(file) {
|
|
|
310
428
|
const record = JSON.parse(line);
|
|
311
429
|
if (!record || typeof record !== 'object' || typeof record.at !== 'string'
|
|
312
430
|
|| !Number.isFinite(Date.parse(record.at)) || typeof record.op !== 'string'
|
|
313
|
-
||
|
|
431
|
+
|| !['request', 'resubmit', 'approve', 'reject', 'expire', 'block', 'unblock', 'manual-add'].includes(record.op)
|
|
432
|
+
|| typeof record.primaryId !== 'string' || !isValidAid(record.primaryId)
|
|
433
|
+
|| (record.requestId !== undefined && typeof record.requestId !== 'string')
|
|
434
|
+
|| (record.expiresAt !== undefined && (!isValidIsoDate(record.expiresAt)))
|
|
435
|
+
|| (record.actorId !== undefined && typeof record.actorId !== 'string')
|
|
436
|
+
|| (record.sourceChannelKey !== undefined && typeof record.sourceChannelKey !== 'string')
|
|
437
|
+
|| (record.note !== undefined && (typeof record.note !== 'string' || record.note.length > CONTACT_REQUEST_NOTE_MAX_LENGTH))
|
|
438
|
+
|| typeof record.contactRevision !== 'string')
|
|
314
439
|
throw new Error('invalid record');
|
|
315
440
|
records.push(record);
|
|
316
441
|
}
|
|
@@ -320,11 +445,32 @@ function readOperationLog(file) {
|
|
|
320
445
|
}
|
|
321
446
|
return records;
|
|
322
447
|
}
|
|
323
|
-
function
|
|
448
|
+
function isValidIsoDate(value) {
|
|
449
|
+
return Number.isFinite(Date.parse(value));
|
|
450
|
+
}
|
|
451
|
+
function operationLogChange(file, records) {
|
|
324
452
|
if (records.length === 0)
|
|
325
|
-
return;
|
|
326
|
-
|
|
327
|
-
|
|
453
|
+
return undefined;
|
|
454
|
+
// Validate the existing log while the Contact Book lock is held. The log is
|
|
455
|
+
// part of the same journal transaction as contact.json, so a malformed log
|
|
456
|
+
// must fail closed before either file is changed.
|
|
457
|
+
readOperationLog(file);
|
|
458
|
+
let before;
|
|
459
|
+
try {
|
|
460
|
+
before = fs.readFileSync(file, 'utf8');
|
|
461
|
+
}
|
|
462
|
+
catch (error) {
|
|
463
|
+
if (error?.code === 'ENOENT')
|
|
464
|
+
before = null;
|
|
465
|
+
else
|
|
466
|
+
throw error;
|
|
467
|
+
}
|
|
468
|
+
const prefix = before && !before.endsWith('\n') ? `${before}\n` : (before ?? '');
|
|
469
|
+
return {
|
|
470
|
+
file,
|
|
471
|
+
before,
|
|
472
|
+
after: `${prefix}${records.map(record => JSON.stringify(record)).join('\n')}\n`,
|
|
473
|
+
};
|
|
328
474
|
}
|
|
329
475
|
function operationRecord(input) {
|
|
330
476
|
return { id: `evt_${crypto.randomUUID().replace(/-/g, '')}`, ...input };
|
|
@@ -16,9 +16,11 @@ import path from 'path';
|
|
|
16
16
|
import { resolvePaths } from '../paths.js';
|
|
17
17
|
import { saveDefaultsSafe, saveAgent } from '../config-store.js';
|
|
18
18
|
import { resolveAnthropicConfig, resolveEcagentConfig, resolveOpenaiConfig } from '../agents/baseagent.js';
|
|
19
|
+
import { buildModelRequestHeaders } from '../agents/request-identity.js';
|
|
19
20
|
import { resolvePriceRow } from '../stats/billing.js';
|
|
20
21
|
import { ipcQuery } from '../ipc.js';
|
|
21
22
|
import { logger } from '../utils/logger.js';
|
|
23
|
+
import { ConfigTarget, read as readConfig } from './config-manager.js';
|
|
22
24
|
/** 已知 baseagent 类型(网关可管理范围)。 */
|
|
23
25
|
const GATEWAY_TYPES = ['claude', 'codex', 'gemini', 'ecagent'];
|
|
24
26
|
const DISPLAY_NAMES = {
|
|
@@ -522,8 +524,10 @@ export async function gatewayDelete(args) {
|
|
|
522
524
|
}
|
|
523
525
|
/** 解析某 scope/type 的真实 baseUrl + apiKey(在 daemon 内展开 $ENV,不出站)。 */
|
|
524
526
|
function resolveGatewayCreds(scope, type) {
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
+
const expandedConfig = scope === 'defaults'
|
|
528
|
+
? readConfig(ConfigTarget.Defaults, undefined, { expand: true, cache: true })
|
|
529
|
+
: readConfig(ConfigTarget.Agent, { self: scope }, { expand: true, cache: true });
|
|
530
|
+
const block = expandedConfig?.baseagents?.[type];
|
|
527
531
|
if (!block)
|
|
528
532
|
return { error: `${scope}/${type} 未配置`, code: 'NOT_FOUND' };
|
|
529
533
|
const expanded = expandEnv(block);
|
|
@@ -531,15 +535,27 @@ function resolveGatewayCreds(scope, type) {
|
|
|
531
535
|
const synth = { agents: { [type]: expanded } };
|
|
532
536
|
if (type === 'codex') {
|
|
533
537
|
const r = resolveOpenaiConfig(synth, expanded);
|
|
534
|
-
return {
|
|
538
|
+
return {
|
|
539
|
+
baseUrl: r.baseUrl,
|
|
540
|
+
apiKey: r.apiKey,
|
|
541
|
+
headers: buildModelRequestHeaders({ baseagent: 'codex', baseUrl: r.baseUrl, agentAid: scope === 'defaults' ? undefined : scope, configuredHeaders: r.headers }),
|
|
542
|
+
};
|
|
535
543
|
}
|
|
536
544
|
else if (type === 'claude') {
|
|
537
545
|
const r = resolveAnthropicConfig(synth, expanded);
|
|
538
|
-
return {
|
|
546
|
+
return {
|
|
547
|
+
baseUrl: r.baseUrl,
|
|
548
|
+
apiKey: r.apiKey,
|
|
549
|
+
headers: buildModelRequestHeaders({ baseagent: 'claude', baseUrl: r.baseUrl, agentAid: scope === 'defaults' ? undefined : scope, configuredHeaders: r.headers }),
|
|
550
|
+
};
|
|
539
551
|
}
|
|
540
552
|
else if (type === 'ecagent') {
|
|
541
553
|
const r = resolveEcagentConfig(synth, expanded);
|
|
542
|
-
return {
|
|
554
|
+
return {
|
|
555
|
+
baseUrl: r.baseUrl,
|
|
556
|
+
apiKey: r.apiKey,
|
|
557
|
+
headers: buildModelRequestHeaders({ baseagent: 'ecagent', baseUrl: r.baseUrl, agentAid: scope === 'defaults' ? undefined : scope, configuredHeaders: r.headers }),
|
|
558
|
+
};
|
|
543
559
|
}
|
|
544
560
|
return { error: 'gemini 暂不支持 HTTP 探测(CLI 后端)', code: 'NOT_SUPPORTED' };
|
|
545
561
|
}
|
|
@@ -556,7 +572,7 @@ export async function gatewayTest(args) {
|
|
|
556
572
|
const creds = resolveGatewayCreds(scope, type);
|
|
557
573
|
if ('error' in creds)
|
|
558
574
|
return creds;
|
|
559
|
-
const { baseUrl, apiKey } = creds;
|
|
575
|
+
const { baseUrl, apiKey, headers } = creds;
|
|
560
576
|
if (!baseUrl)
|
|
561
577
|
return { error: '未配置 baseUrl(或为官方占位地址)', code: 'INVALID_ARGS' };
|
|
562
578
|
const start = Date.now();
|
|
@@ -566,7 +582,7 @@ export async function gatewayTest(args) {
|
|
|
566
582
|
const base = baseUrl.replace(/\/+$/, '');
|
|
567
583
|
const modelsUrl = base.endsWith('/v1') ? `${base}/models` : `${base}/v1/models`;
|
|
568
584
|
const resp = await fetch(modelsUrl, {
|
|
569
|
-
headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : {},
|
|
585
|
+
headers: { ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}), ...headers },
|
|
570
586
|
signal: controller.signal,
|
|
571
587
|
});
|
|
572
588
|
clearTimeout(timer);
|
|
@@ -614,7 +630,7 @@ export async function gatewayModels(args) {
|
|
|
614
630
|
const creds = resolveGatewayCreds(scope, type);
|
|
615
631
|
if ('error' in creds)
|
|
616
632
|
return creds;
|
|
617
|
-
const { baseUrl, apiKey } = creds;
|
|
633
|
+
const { baseUrl, apiKey, headers } = creds;
|
|
618
634
|
if (!baseUrl)
|
|
619
635
|
return { error: '未配置 baseUrl(或为官方占位地址)', code: 'INVALID_ARGS' };
|
|
620
636
|
let arr = [];
|
|
@@ -623,7 +639,7 @@ export async function gatewayModels(args) {
|
|
|
623
639
|
const controller = new AbortController();
|
|
624
640
|
const timer = setTimeout(() => controller.abort(), 8000);
|
|
625
641
|
const resp = await fetch(`${baseUrl.replace(/\/+$/, '')}/v1/models`, {
|
|
626
|
-
headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : {},
|
|
642
|
+
headers: { ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}), ...headers },
|
|
627
643
|
signal: controller.signal,
|
|
628
644
|
});
|
|
629
645
|
clearTimeout(timer);
|
|
@@ -906,7 +922,7 @@ function expandEnv(obj) {
|
|
|
906
922
|
if (typeof obj === 'string') {
|
|
907
923
|
if (obj.startsWith(ENV_PREFIX))
|
|
908
924
|
return process.env[obj.slice(ENV_PREFIX.length)] ?? '';
|
|
909
|
-
return obj;
|
|
925
|
+
return obj.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_, name) => process.env[name] ?? '');
|
|
910
926
|
}
|
|
911
927
|
if (Array.isArray(obj))
|
|
912
928
|
return obj.map(expandEnv);
|
|
@@ -1,27 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* - disabled(关闭 mention 过滤) ≡ broadcast(全部响应)
|
|
8
|
-
*
|
|
9
|
-
* 仅在协议边界(AUN resolver、render fallback)翻译;配置层、命令层一律用
|
|
10
|
-
* mentionMode 词汇。
|
|
11
|
-
*/
|
|
12
|
-
/** config → AUN 协议词汇;未知/未设值返回 undefined(回退服务端 dispatch_mode)。 */
|
|
13
|
-
export function mentionModeToDispatch(mode) {
|
|
14
|
-
if (mode === 'mention-only')
|
|
15
|
-
return 'mention';
|
|
16
|
-
if (mode === 'disabled')
|
|
17
|
-
return 'broadcast';
|
|
18
|
-
return undefined;
|
|
19
|
-
}
|
|
20
|
-
/** AUN 协议词汇 → config;未知返回 undefined。 */
|
|
21
|
-
export function dispatchToMentionMode(dispatch) {
|
|
22
|
-
if (dispatch === 'mention')
|
|
23
|
-
return 'mention-only';
|
|
24
|
-
if (dispatch === 'broadcast')
|
|
1
|
+
/** AUN `mention_mode` and EvolCore `mentionMode` share these wire values. */
|
|
2
|
+
export function normalizeMentionMode(mode) {
|
|
3
|
+
if (typeof mode !== 'string')
|
|
4
|
+
return undefined;
|
|
5
|
+
const normalized = mode.trim().toLowerCase();
|
|
6
|
+
if (normalized === 'disabled')
|
|
25
7
|
return 'disabled';
|
|
8
|
+
if (normalized === 'mention-only' || normalized === 'mention_only')
|
|
9
|
+
return 'mention-only';
|
|
26
10
|
return undefined;
|
|
27
11
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export class AgentReloadBusyError extends Error {
|
|
2
|
+
aid;
|
|
3
|
+
busyCount;
|
|
4
|
+
code = 'AGENT_BUSY';
|
|
5
|
+
constructor(aid, busyCount) {
|
|
6
|
+
super(`Agent "${aid}" has ${busyCount} task(s) in progress`);
|
|
7
|
+
this.aid = aid;
|
|
8
|
+
this.busyCount = busyCount;
|
|
9
|
+
this.name = 'AgentReloadBusyError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class AgentReloadCoordinator {
|
|
13
|
+
deps;
|
|
14
|
+
inFlight = new Map();
|
|
15
|
+
constructor(deps) {
|
|
16
|
+
this.deps = deps;
|
|
17
|
+
}
|
|
18
|
+
busyCount(aid) {
|
|
19
|
+
return Math.max(0, this.deps.getBusyCount(aid));
|
|
20
|
+
}
|
|
21
|
+
runExclusive(aid, operation, options = {}) {
|
|
22
|
+
const previous = this.inFlight.get(aid) ?? Promise.resolve();
|
|
23
|
+
const pending = previous
|
|
24
|
+
.catch(() => undefined)
|
|
25
|
+
.then(async () => {
|
|
26
|
+
const releaseWorkGate = await this.deps.acquireWorkGate?.(aid);
|
|
27
|
+
try {
|
|
28
|
+
const busyCount = this.busyCount(aid);
|
|
29
|
+
if (!options.force && busyCount > 0)
|
|
30
|
+
throw new AgentReloadBusyError(aid, busyCount);
|
|
31
|
+
if (options.force && busyCount > 0)
|
|
32
|
+
await this.deps.forceReload?.(aid);
|
|
33
|
+
return await operation();
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
await releaseWorkGate?.();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
this.inFlight.set(aid, pending);
|
|
40
|
+
return pending.finally(() => {
|
|
41
|
+
if (this.inFlight.get(aid) === pending)
|
|
42
|
+
this.inFlight.delete(aid);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
reload(aid, options = {}) {
|
|
46
|
+
return this.runExclusive(aid, async () => {
|
|
47
|
+
if (!this.deps.registry.get(aid))
|
|
48
|
+
throw new Error(`Agent "${aid}" not found`);
|
|
49
|
+
await this.deps.beforeReload?.();
|
|
50
|
+
await this.deps.registry.reload(aid, this.deps.hooks);
|
|
51
|
+
}, options);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -155,7 +155,6 @@ export function authorizeDelegatedAunMsgSend(registry, input) {
|
|
|
155
155
|
channelId,
|
|
156
156
|
chatType: grant.chatType,
|
|
157
157
|
conversationId: channelId,
|
|
158
|
-
identity: { role: grant.issuedRole, mode: 'interactive' },
|
|
159
158
|
processOwners: [],
|
|
160
159
|
fromControlChannel: false,
|
|
161
160
|
});
|