evolcore 0.0.26 → 0.0.27
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 +25 -0
- package/bin/codex-managed-acl.ps1 +43 -0
- package/dist/agents/baseagent-availability.js +68 -0
- package/dist/agents/claude-runner.js +123 -22
- package/dist/agents/codex/app-server-client.js +63 -26
- package/dist/agents/codex-runner.js +316 -411
- package/dist/agents/ecagent-runner.js +170 -25
- package/dist/agents/gemini-runner.js +6 -0
- package/dist/agents/runner-types.js +50 -2
- package/dist/aun/aid/agentmd.js +73 -31
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/control-aid.js +180 -1
- package/dist/aun/aid/identity.js +3 -3
- package/dist/aun/group/history.js +303 -0
- package/dist/aun/group/identity.js +43 -0
- package/dist/aun/group/index.js +5 -0
- package/dist/aun/{msg/group.js → group/operations.js} +161 -38
- package/dist/aun/{menu-client.js → menu/client.js} +12 -7
- package/dist/aun/menu/index.js +2 -0
- package/dist/aun/menu/transport.js +49 -0
- package/dist/aun/msg/index.js +2 -1
- package/dist/aun/msg/managed-operation.js +84 -18
- package/dist/aun/msg/mention-schema.js +51 -0
- package/dist/aun/msg/p2p.js +13 -8
- package/dist/aun/msg/upload.js +43 -88
- package/dist/aun/outbox.js +29 -7
- package/dist/aun/rpc/client.js +1 -0
- package/dist/aun/storage/client.js +39 -50
- package/dist/channels/aun.js +522 -233
- package/dist/channels/daemon.js +10 -6
- package/dist/cli/agent-command.js +108 -6
- package/dist/cli/agent-create.js +515 -2
- package/dist/cli/agent.js +109 -8
- package/dist/cli/aun-commands.js +191 -42
- package/dist/cli/config.js +8 -5
- package/dist/cli/contact.js +2 -2
- package/dist/cli/ctl-command.js +2 -2
- package/dist/cli/daemon-commands.js +35 -23
- package/dist/cli/diag.js +2 -2
- package/dist/cli/fs-command.js +3 -3
- package/dist/cli/handoff-command.js +6 -6
- package/dist/cli/index.js +8 -2
- package/dist/cli/init.js +34 -1
- package/dist/cli/managed-command-guard.js +2 -1
- package/dist/cli/menu-client.js +9 -5
- package/dist/cli/model.js +3 -3
- package/dist/cli/queue-command.js +2 -2
- package/dist/cli/response.js +2 -2
- package/dist/cli/restart-monitor.js +16 -2
- package/dist/cli/task-command.js +59 -0
- package/dist/cli/task-context.js +97 -49
- package/dist/cli/trigger-command.js +20 -10
- package/dist/config/builtin-roles.js +10 -0
- package/dist/config/config-field-policy.js +11 -0
- package/dist/config/config-manager.js +32 -4
- package/dist/config/config-operation-service.js +20 -5
- package/dist/config/contact-book-store.js +1 -1
- package/dist/config/contact-book.js +1 -1
- package/dist/config/contact-operation-service.js +1 -1
- package/dist/config/peer-role-resolver.js +1 -1
- package/dist/config/resolved-config-op.js +3 -0
- package/dist/config-store.js +1 -1
- package/dist/core/agent-application-service.js +5 -0
- package/dist/core/audit/event-key.js +17 -2
- package/dist/core/audit/log-integrity.js +34 -8
- package/dist/core/auth/agent-delegation.js +48 -14
- package/dist/core/auth/auth-gateway.js +2 -0
- package/dist/core/auth/authorization-audit.js +34 -38
- package/dist/core/auth/operation-authorizer.js +67 -5
- package/dist/core/auth/operation-catalog.js +110 -0
- package/dist/core/auth/remote-access-store.js +115 -9
- package/dist/core/bootstrap-messages.js +66 -7
- package/dist/core/bootstrap-service.js +110 -19
- package/dist/core/command/agent-control.js +88 -7
- package/dist/core/command/agent-storage-menu.js +443 -0
- package/dist/core/command/cli-intent-parser.js +19 -0
- package/dist/core/command/command-handler.js +76 -39
- package/dist/core/command/data-root-resolver.js +273 -0
- package/dist/core/command/group-menu.js +7 -2
- package/dist/core/command/menu-catalog.js +34 -4
- package/dist/core/command/menu-handler.js +348 -77
- package/dist/core/command/menu-protocol.js +40 -6
- package/dist/core/command/role-menu.js +2 -2
- package/dist/core/command/slash-handler.js +37 -22
- package/dist/core/event-catalog.js +4 -0
- package/dist/core/evolagent-registry.js +28 -2
- package/dist/core/group/invite-service.js +924 -0
- package/dist/core/interaction-router.js +22 -8
- package/dist/core/message/im-renderer.js +76 -15
- package/dist/core/message/message-bridge.js +94 -11
- package/dist/core/message/message-log.js +5 -3
- package/dist/core/message/message-queue.js +73 -41
- package/dist/core/message/response-engine.js +709 -117
- package/dist/core/permission/approval-gateway.js +36 -3
- package/dist/core/permission/ec-command-parser.js +61 -29
- package/dist/core/permission/protected-paths.js +134 -16
- package/dist/core/permission/sandbox-runtime.js +72 -3
- package/dist/core/permission/tool-error-code.js +15 -0
- package/dist/core/permission/tool-policy.js +7 -7
- package/dist/core/relation/peer-identity.js +57 -6
- package/dist/core/relation/shared-data.js +106 -0
- package/dist/core/role/runtime-policy.js +64 -0
- package/dist/core/runtime-lock.js +10 -26
- package/dist/core/session/runner-task-history.js +170 -0
- package/dist/core/session/session-manager.js +34 -6
- package/dist/core/session/session-mapper.js +41 -0
- package/dist/core/session/session-turn-coordinator.js +37 -7
- package/dist/core/session/session-turns.js +48 -7
- package/dist/core/task/task-recovery-service.js +176 -0
- package/dist/core/task/task-resume-operation.js +32 -0
- package/dist/eck/group-rules-sync.js +5 -3
- package/dist/eck/kit-renderer.js +1 -0
- package/dist/index.js +308 -54
- package/dist/ipc.js +141 -15
- package/dist/paths.js +17 -4
- package/dist/product.js +2 -0
- package/dist/trigger/event-source.js +19 -0
- package/dist/trigger/feedback.js +233 -40
- package/dist/trigger/parser.js +55 -5
- package/dist/trigger/patch.js +9 -0
- package/dist/trigger/scheduler.js +30 -4
- package/dist/trigger/validation.js +20 -3
- package/dist/utils/ecweb-utils.js +1 -0
- package/dist/utils/error-utils.js +40 -2
- package/dist/utils/logger.js +23 -13
- package/dist/utils/tool-summary.js +59 -3
- package/dist/utils/welcome.js +174 -1
- package/ecagent/dist/harness/agent-harness.js +3 -0
- package/ecagent/dist/harness/session/jsonl-storage.js +21 -2
- package/kits/docs/INDEX.md +0 -6
- package/kits/docs/channels/aun.md +15 -2
- package/kits/docs/channels/feishu.md +1 -1
- package/kits/docs/context-assembly.md +29 -17
- package/kits/docs/evolcore/INDEX.md +8 -3
- package/kits/docs/evolcore/agent-create.md +39 -7
- package/kits/docs/evolcore/agent.md +33 -1
- package/kits/docs/evolcore/aid.md +9 -1
- package/kits/docs/evolcore/config.md +40 -1
- package/kits/docs/evolcore/group-collaboration.md +1 -1
- package/kits/docs/evolcore/group.md +117 -2
- package/kits/docs/evolcore/menu-client.md +16 -0
- package/kits/docs/evolcore/msg.md +3 -0
- package/kits/docs/evolcore/storage.md +3 -0
- package/kits/docs/evolcore/task.md +14 -0
- package/kits/docs/evolcore/trigger.md +51 -3
- package/kits/docs/venues/group.md +5 -0
- package/kits/eck_manifest.json +27 -0
- package/kits/rules/01-overview.md +11 -7
- package/kits/rules/04-relation.md +2 -1
- package/kits/rules/05-venue.md +1 -1
- package/kits/rules/06-channel.md +6 -4
- package/kits/schemas/agent-config.schema.12.json +25 -1
- package/kits/schemas/daemon.schema.6.json +2 -2
- package/kits/schemas/defaults.schema.5.json +1 -1
- package/kits/schemas/relation-config.schema.1.json +1 -1
- package/kits/schemas/relation-config.schema.5.json +1 -1
- package/kits/schemas/role-config.schema.1.json +14 -0
- package/kits/templates/roles/member.json +5 -0
- package/kits/templates/roles/visitor.json +5 -0
- package/kits/templates/system-fragments/baseagent.md +1 -1
- package/kits/templates/system-fragments/bootstrap.md +26 -9
- package/kits/templates/system-fragments/channel.md +8 -44
- package/kits/templates/system-fragments/commands.md +10 -2
- package/kits/templates/system-fragments/group-unread-history.md +7 -0
- package/kits/templates/system-fragments/reply.md +56 -0
- package/package.json +7 -3
- package/dist/aun/group-identity.js +0 -9
- /package/dist/aun/{group-fs-download.js → group/fs-download.js} +0 -0
- /package/dist/aun/{msg/group-index.js → group/index-rpc.js} +0 -0
package/dist/cli/task-context.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { normalizeCausation } from '../core/causation/context.js';
|
|
2
2
|
import { RUNTIME_LOCK_DIR_ENV } from '../core/runtime-lock.js';
|
|
3
3
|
import crypto from 'node:crypto';
|
|
4
|
+
import { execFileSync } from 'node:child_process';
|
|
4
5
|
import fs from 'node:fs';
|
|
5
6
|
import os from 'node:os';
|
|
6
7
|
import path from 'node:path';
|
|
8
|
+
import { readDelegationCredential } from '../core/auth/agent-delegation.js';
|
|
9
|
+
import { resolveRoot } from '../paths.js';
|
|
7
10
|
export const TASK_RUNTIME_CONTEXT_ENV = 'EVOLCORE_TASK_RUNTIME_CONTEXT';
|
|
8
11
|
export const SESSION_RUNTIME_DIR_ENV = 'EVOLCORE_SESSION_RUNTIME_DIR';
|
|
9
12
|
export const DAEMON_RUNTIME_EPOCH_ENV = 'EVOLCORE_DAEMON_RUNTIME_EPOCH';
|
|
10
13
|
export { RUNTIME_LOCK_DIR_ENV };
|
|
11
14
|
const generatedProcessManagedTempDirs = new Set();
|
|
15
|
+
const preparedWindowsSessionRuntimeDirs = new Set();
|
|
12
16
|
let processTempCleanupRegistered = false;
|
|
13
17
|
function isPlainObject(value) {
|
|
14
18
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
@@ -37,7 +41,9 @@ function normalizeTaskRuntimeContext(value) {
|
|
|
37
41
|
processRole: optionalProcessRole(value.processRole),
|
|
38
42
|
dataScope: optionalDataScope(value.dataScope),
|
|
39
43
|
authorizedBy: optionalString(value.authorizedBy),
|
|
40
|
-
executionSource: value.executionSource === 'fullaccess-command'
|
|
44
|
+
executionSource: value.executionSource === 'fullaccess-command'
|
|
45
|
+
|| value.executionSource === 'trigger'
|
|
46
|
+
|| value.executionSource === 'agent-policy'
|
|
41
47
|
? value.executionSource : undefined,
|
|
42
48
|
daemonRuntimeEpoch: optionalString(value.daemonRuntimeEpoch),
|
|
43
49
|
threadId: optionalString(value.threadId),
|
|
@@ -189,29 +195,81 @@ export function ensureProcessManagedTempDir() {
|
|
|
189
195
|
throw error;
|
|
190
196
|
}
|
|
191
197
|
}
|
|
192
|
-
function
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return
|
|
198
|
+
function canonicalHome() {
|
|
199
|
+
const configured = path.resolve(resolveRoot());
|
|
200
|
+
let canonical;
|
|
201
|
+
try {
|
|
202
|
+
canonical = fs.realpathSync.native(configured);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
canonical = configured;
|
|
206
|
+
}
|
|
207
|
+
return process.platform === 'win32' ? canonical.toLowerCase() : canonical;
|
|
208
|
+
}
|
|
209
|
+
function systemTempRoot() {
|
|
210
|
+
const testRoot = process.env.EVOLCORE_TEST === '1' && process.env.TMPDIR?.trim();
|
|
211
|
+
const candidate = testRoot && path.isAbsolute(testRoot)
|
|
212
|
+
? testRoot
|
|
213
|
+
: process.platform === 'win32'
|
|
214
|
+
? path.join(process.env.SystemRoot || process.env.WINDIR || 'C:\\Windows', 'Temp')
|
|
215
|
+
: process.platform === 'linux'
|
|
216
|
+
? '/tmp'
|
|
217
|
+
: os.tmpdir();
|
|
218
|
+
try {
|
|
219
|
+
return fs.realpathSync.native(candidate);
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
return path.resolve(candidate);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function sessionRuntimeName(sessionId) {
|
|
226
|
+
const digest = crypto.createHash('sha256')
|
|
227
|
+
.update(canonicalHome())
|
|
228
|
+
.update('\0')
|
|
229
|
+
.update(sessionId)
|
|
230
|
+
.digest('hex')
|
|
231
|
+
.slice(0, 24);
|
|
232
|
+
return `ec-session-${digest}`;
|
|
202
233
|
}
|
|
203
|
-
/**
|
|
204
|
-
*
|
|
234
|
+
/**
|
|
235
|
+
* Windows Codex sandbox accounts and ordinary runner accounts are members of
|
|
236
|
+
* BUILTIN\Users. Session isolation is enforced by the distinct path plus the
|
|
237
|
+
* runner filesystem policy, so the runtime directory only needs a portable
|
|
238
|
+
* writable ACL rather than host-user secrecy.
|
|
239
|
+
*/
|
|
240
|
+
function ensureWindowsSessionRuntimeAccess(directory) {
|
|
241
|
+
if (process.platform !== 'win32' || preparedWindowsSessionRuntimeDirs.has(directory))
|
|
242
|
+
return;
|
|
243
|
+
const icacls = path.join(process.env.SystemRoot || process.env.WINDIR || 'C:\\Windows', 'System32', 'icacls.exe');
|
|
244
|
+
execFileSync(icacls, [
|
|
245
|
+
directory,
|
|
246
|
+
'/grant',
|
|
247
|
+
'*S-1-5-32-545:(OI)(CI)M',
|
|
248
|
+
'/Q',
|
|
249
|
+
], { windowsHide: true, stdio: 'pipe' });
|
|
250
|
+
preparedWindowsSessionRuntimeDirs.add(directory);
|
|
251
|
+
}
|
|
252
|
+
/** Whether a task-provided runtime directory is a stable EvolCore session root. */
|
|
205
253
|
export function isManagedSessionRuntimeDir(directory, managedRoot) {
|
|
206
|
-
|
|
207
|
-
if (!directory || !path.isAbsolute(directory) || !tmpDir || !path.isAbsolute(tmpDir))
|
|
254
|
+
if (!directory || !path.isAbsolute(directory))
|
|
208
255
|
return false;
|
|
209
|
-
const root = path.resolve(tmpDir);
|
|
210
256
|
const resolved = path.resolve(directory);
|
|
211
|
-
const
|
|
212
|
-
|
|
257
|
+
const suppliedRoot = managedRoot?.trim();
|
|
258
|
+
const candidateRoot = suppliedRoot && path.isAbsolute(suppliedRoot)
|
|
259
|
+
? path.resolve(suppliedRoot)
|
|
260
|
+
: systemTempRoot();
|
|
261
|
+
const root = candidateRoot === resolved ? path.dirname(resolved) : candidateRoot;
|
|
262
|
+
if (path.dirname(resolved) !== root || !/^ec-session-[a-f0-9]{24}$/.test(path.basename(resolved)))
|
|
263
|
+
return false;
|
|
264
|
+
try {
|
|
265
|
+
const rootStat = fs.lstatSync(root);
|
|
266
|
+
if (!rootStat.isDirectory() || rootStat.isSymbolicLink())
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
213
270
|
return false;
|
|
214
|
-
|
|
271
|
+
}
|
|
272
|
+
return isPrivateDirectory(resolved);
|
|
215
273
|
}
|
|
216
274
|
/**
|
|
217
275
|
* Resolve the session-owned temporary root for a managed child process.
|
|
@@ -241,58 +299,43 @@ export function getManagedTaskTempDir(runtimeEnv) {
|
|
|
241
299
|
&& (!processRoot || !path.isAbsolute(processRoot) || path.resolve(configured) !== path.resolve(processRoot))
|
|
242
300
|
&& contextDir
|
|
243
301
|
&& path.resolve(contextDir) === path.resolve(configured)
|
|
244
|
-
&&
|
|
302
|
+
&& isManagedSessionRuntimeDir(configured);
|
|
245
303
|
if (!trusted) {
|
|
246
304
|
throw new Error('managed session TMPDIR is unavailable or untrusted');
|
|
247
305
|
}
|
|
248
306
|
return path.resolve(configured);
|
|
249
307
|
}
|
|
250
308
|
/**
|
|
251
|
-
* Create
|
|
252
|
-
*
|
|
253
|
-
*
|
|
309
|
+
* Create the stable private runtime directory for one EvolCore session.
|
|
310
|
+
* Its identity is independent of daemon/runner lifetimes and rotates only
|
|
311
|
+
* when EvolCore creates a new session id.
|
|
254
312
|
*/
|
|
255
313
|
export function ensureSessionRuntimeDir(sessionId) {
|
|
256
314
|
if (!sessionId)
|
|
257
315
|
return undefined;
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
if (!base || !path.isAbsolute(base) || !isPrivateDirectory(path.resolve(base))) {
|
|
261
|
-
base = ensureProcessManagedTempDir();
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
catch {
|
|
265
|
-
return undefined;
|
|
266
|
-
}
|
|
267
|
-
if (!base || !path.isAbsolute(base))
|
|
268
|
-
return undefined;
|
|
269
|
-
const root = path.resolve(base);
|
|
270
|
-
const digest = crypto.createHash('sha256').update(sessionId).digest('hex').slice(0, 24);
|
|
271
|
-
const directory = path.join(root, `evolcore-runtime-${digest}`);
|
|
316
|
+
const root = systemTempRoot();
|
|
317
|
+
const directory = path.join(root, sessionRuntimeName(sessionId));
|
|
272
318
|
try {
|
|
273
319
|
fs.mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
274
|
-
if (!isPrivateDirectory(root))
|
|
275
|
-
return undefined;
|
|
276
320
|
fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
321
|
+
ensureWindowsSessionRuntimeAccess(directory);
|
|
277
322
|
fs.mkdirSync(path.join(directory, 'locks'), { recursive: true, mode: 0o700 });
|
|
278
|
-
return isManagedSessionRuntimeDir(directory) ? directory : undefined;
|
|
323
|
+
return isManagedSessionRuntimeDir(directory, root) ? directory : undefined;
|
|
279
324
|
}
|
|
280
325
|
catch {
|
|
281
326
|
return undefined;
|
|
282
327
|
}
|
|
283
328
|
}
|
|
284
|
-
/**
|
|
285
|
-
* session's history/files area. The caller must still pass this path through
|
|
286
|
-
* the task environment before a sandboxed child can use it. */
|
|
329
|
+
/** Return the lock namespace inside the session-owned runtime directory. */
|
|
287
330
|
export function ensureRuntimeLockDir(sessionRuntimeDir) {
|
|
288
331
|
if (!sessionRuntimeDir || !path.isAbsolute(sessionRuntimeDir))
|
|
289
332
|
return undefined;
|
|
290
|
-
if (!isManagedSessionRuntimeDir(sessionRuntimeDir)
|
|
333
|
+
if (!isManagedSessionRuntimeDir(sessionRuntimeDir))
|
|
291
334
|
return undefined;
|
|
292
|
-
const parent = path.
|
|
335
|
+
const parent = path.resolve(sessionRuntimeDir);
|
|
293
336
|
if (!isPrivateDirectory(parent))
|
|
294
337
|
return undefined;
|
|
295
|
-
const directory = path.join(parent, '
|
|
338
|
+
const directory = path.join(parent, 'locks');
|
|
296
339
|
try {
|
|
297
340
|
fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
298
341
|
return isPrivateDirectory(directory) ? directory : undefined;
|
|
@@ -325,7 +368,7 @@ export function parseTaskRuntimeContext(raw) {
|
|
|
325
368
|
export function readTaskRuntimeContextFromEnv(env = process.env) {
|
|
326
369
|
return parseTaskRuntimeContext(env[TASK_RUNTIME_CONTEXT_ENV]);
|
|
327
370
|
}
|
|
328
|
-
export async function readTaskRuntimeContextFromDaemon(sessionId, timeoutMs = 500) {
|
|
371
|
+
export async function readTaskRuntimeContextFromDaemon(sessionId, timeoutMs = 500, env = process.env) {
|
|
329
372
|
if (!sessionId)
|
|
330
373
|
return undefined;
|
|
331
374
|
try {
|
|
@@ -333,7 +376,12 @@ export async function readTaskRuntimeContextFromDaemon(sessionId, timeoutMs = 50
|
|
|
333
376
|
import('../paths.js'),
|
|
334
377
|
import('../ipc.js'),
|
|
335
378
|
]);
|
|
336
|
-
const response = await ipcQuery(resolvePaths().socket, {
|
|
379
|
+
const response = await ipcQuery(resolvePaths().socket, {
|
|
380
|
+
type: 'task-runtime-context',
|
|
381
|
+
sessionId,
|
|
382
|
+
delegationToken: readDelegationCredential(env),
|
|
383
|
+
daemonRuntimeEpoch: env[DAEMON_RUNTIME_EPOCH_ENV],
|
|
384
|
+
}, timeoutMs);
|
|
337
385
|
return response?.ok && response.context ? response.context : undefined;
|
|
338
386
|
}
|
|
339
387
|
catch {
|
|
@@ -343,7 +391,7 @@ export async function readTaskRuntimeContextFromDaemon(sessionId, timeoutMs = 50
|
|
|
343
391
|
export async function readBestTaskRuntimeContext(env = process.env) {
|
|
344
392
|
const envContext = readTaskRuntimeContextFromEnv(env);
|
|
345
393
|
const sessionId = env.EVOLCORE_SESSION_ID || envContext?.sessionId;
|
|
346
|
-
return (await readTaskRuntimeContextFromDaemon(sessionId)) ?? envContext;
|
|
394
|
+
return (await readTaskRuntimeContextFromDaemon(sessionId, 500, env)) ?? envContext;
|
|
347
395
|
}
|
|
348
396
|
export function buildTaskRuntimeEnv(ctx) {
|
|
349
397
|
const clean = normalizeTaskRuntimeContext(ctx);
|
|
@@ -3,7 +3,7 @@ import path from 'path';
|
|
|
3
3
|
import { resolvePaths } from '../paths.js';
|
|
4
4
|
import { ipcQuery } from '../ipc.js';
|
|
5
5
|
import { isHelpFlag, wantsHelp } from './help.js';
|
|
6
|
-
import {
|
|
6
|
+
import { readDelegationCredential } from '../core/auth/agent-delegation.js';
|
|
7
7
|
import { readTaskRuntimeContextFromEnv } from './task-context.js';
|
|
8
8
|
import { normalizeTriggerDefinition, resolveScriptPath, } from '../trigger/validation.js';
|
|
9
9
|
export async function cmdTrigger(args) {
|
|
@@ -101,13 +101,15 @@ function printHelp() {
|
|
|
101
101
|
|
|
102
102
|
Create 参数模式支持:
|
|
103
103
|
--once | --delay <时长> | --at <ISO时间> | --cron <表达式> | --every <时长> | --event <事件模式>
|
|
104
|
+
--event-filter <JSON> (仅 --event;按事件 payload 字段匹配)
|
|
104
105
|
--exec <script|trigger-session|target-session> (必填)
|
|
105
106
|
--prompt <文本> | --prompt-file <路径>
|
|
106
107
|
Shell 双引号内的双引号、反引号和变量形式的美元符号都需在前面加反斜杠;trigger 保存的是还原后的原文
|
|
107
108
|
--script-path <路径> --script-runtime <node|python|bash>
|
|
108
109
|
--script-args <JSON> --script-timeout <时长>
|
|
109
|
-
--feedback <target> (
|
|
110
|
+
--feedback <target|event-session> (event-session 使用事件来源会话)
|
|
110
111
|
--baseagent <name> (仅 --exec trigger-session)
|
|
112
|
+
--execution-timeout <时长>(模型类 Trigger 单次执行总时限)
|
|
111
113
|
--model <模型> --effort <low|medium|high|xhigh|max>
|
|
112
114
|
--max-runs <次数> --max-duration <时长: 30s|15m|2h|1d>
|
|
113
115
|
--permission <readonly|auto|request|bypass|fullaccess>(省略则继承当前身份配置;fullaccess 仅 daemon-owner 且 feature flag 开启时可用)
|
|
@@ -125,10 +127,11 @@ Update 参数:
|
|
|
125
127
|
--once | --delay | --at | --cron | --every | --event [--tz]
|
|
126
128
|
--prompt <文本> | --prompt-file <路径> --script-file <路径> --name <名称>
|
|
127
129
|
--description <文本> (空字符串删除)
|
|
130
|
+
--execution-timeout <时长>(模型类 Trigger 单次执行总时限)
|
|
128
131
|
--model <模型> --effort <强度> --permission <权限模式|inherit>(inherit 清除覆盖)
|
|
129
132
|
--max-runs <次数> --max-duration <时长>
|
|
130
133
|
--concurrency <forbid|replace|allow> --missed-policy <skip|run-once|run-all>
|
|
131
|
-
--feedback <origin|target|silent>
|
|
134
|
+
--feedback <origin|target|event-session|silent>
|
|
132
135
|
--target-channel <channelKey> --target-channel-id <ID> (必须同时指定)
|
|
133
136
|
--target-chat-type <private|group> --target-session <main|thread> [--target-thread-id <threadId>]
|
|
134
137
|
--trigger-thread <by-run|by-trigger> --baseagent <name> (仅 trigger_session 之间)
|
|
@@ -189,6 +192,7 @@ async function showTrigger(args, json) {
|
|
|
189
192
|
if (t.execution.type === 'script')
|
|
190
193
|
console.log(`script: ${scriptCommandLabel(t.execution.script)}`);
|
|
191
194
|
console.log(`model: ${t.execution.model ?? 'inherit'}`);
|
|
195
|
+
console.log(`executionTimeoutMs: ${t.execution.timeoutMs ?? 'default'}`);
|
|
192
196
|
console.log(`effort: ${t.execution.effort ?? 'inherit'}`);
|
|
193
197
|
console.log(`permission: ${t.execution.permissionMode ?? 'inherit'}`);
|
|
194
198
|
console.log(`feedback: ${t.feedback.strategy}${t.feedback.target ? ` -> ${t.feedback.target.channelKey}/${t.feedback.target.channelId}/${t.feedback.target.session}` : ''}`);
|
|
@@ -266,13 +270,14 @@ async function createTrigger(args, json) {
|
|
|
266
270
|
}
|
|
267
271
|
const parsed = parseResult.value;
|
|
268
272
|
const agentAid = parsed.agentId ?? resolveAgentAid(args);
|
|
269
|
-
|
|
273
|
+
const eventSession = parsed.feedbackStrategy === 'event_session';
|
|
274
|
+
if (parsed.feedbackStrategy !== 'target' && !eventSession) {
|
|
270
275
|
throw new Error('CLI flag 模式仅支持 --feedback target;origin/silent 请使用 /trigger 或 --file 导入 Trigger V3 第二阶段 JSON');
|
|
271
276
|
}
|
|
272
|
-
if (!parsed.targetChannel || !parsed.targetChannelId) {
|
|
277
|
+
if (!eventSession && (!parsed.targetChannel || !parsed.targetChannelId)) {
|
|
273
278
|
throw new Error('CLI flag 模式需要显式指定 --target-channel <channelKey> --target-channel-id <id>');
|
|
274
279
|
}
|
|
275
|
-
const targetIsAun = parsed.targetChannel === 'aun' || parsed.targetChannel
|
|
280
|
+
const targetIsAun = parsed.targetChannel === 'aun' || parsed.targetChannel?.startsWith('aun#');
|
|
276
281
|
if (targetIsAun && !parsed.targetChatType) {
|
|
277
282
|
throw new Error('AUN 目标必须显式指定 --target-chat-type private 或 group');
|
|
278
283
|
}
|
|
@@ -299,6 +304,7 @@ async function createTrigger(args, json) {
|
|
|
299
304
|
type: parsed.executionType,
|
|
300
305
|
...(script ? { script } : { prompt: parsed.prompt }),
|
|
301
306
|
...(parsed.model ? { model: parsed.model } : {}),
|
|
307
|
+
...(parsed.executionTimeoutMs !== undefined ? { timeoutMs: parsed.executionTimeoutMs } : {}),
|
|
302
308
|
...(parsed.effort ? { effort: parsed.effort } : {}),
|
|
303
309
|
...(parsed.permissionMode ? { permissionMode: parsed.permissionMode } : {}),
|
|
304
310
|
...(parsed.executionType === 'trigger_session' ? {
|
|
@@ -308,7 +314,7 @@ async function createTrigger(args, json) {
|
|
|
308
314
|
onError: 'retry',
|
|
309
315
|
noopSentinel: '[[NOOP]]',
|
|
310
316
|
},
|
|
311
|
-
feedback: {
|
|
317
|
+
feedback: eventSession ? { strategy: 'event_session' } : {
|
|
312
318
|
strategy: 'target',
|
|
313
319
|
target: {
|
|
314
320
|
channelKey: parsed.targetChannel,
|
|
@@ -362,8 +368,12 @@ function sourceFromParsed(parsed) {
|
|
|
362
368
|
}
|
|
363
369
|
if (parsed.scheduleType === 'interval')
|
|
364
370
|
return { type: 'interval', everyMs: Number(parsed.scheduleValue) };
|
|
365
|
-
if (parsed.scheduleType === 'event')
|
|
366
|
-
|
|
371
|
+
if (parsed.scheduleType === 'event') {
|
|
372
|
+
const source = { type: 'event', eventPattern: parsed.scheduleValue };
|
|
373
|
+
if (parsed.eventFilter !== undefined)
|
|
374
|
+
source.filter = parsed.eventFilter;
|
|
375
|
+
return source;
|
|
376
|
+
}
|
|
367
377
|
throw new Error(`unsupported scheduleType: ${parsed.scheduleType}`);
|
|
368
378
|
}
|
|
369
379
|
function executionTypeFromFlag(args) {
|
|
@@ -565,7 +575,7 @@ function handleTemplate(args) {
|
|
|
565
575
|
}
|
|
566
576
|
async function request(cmd, timeoutMs = 10_000) {
|
|
567
577
|
const actorSessionId = process.env.EVOLCORE_SESSION_ID;
|
|
568
|
-
const delegationToken =
|
|
578
|
+
const delegationToken = readDelegationCredential();
|
|
569
579
|
let controlToken;
|
|
570
580
|
if (!actorSessionId) {
|
|
571
581
|
try {
|
|
@@ -44,6 +44,16 @@ export function getManagementCommandPermissions(role) {
|
|
|
44
44
|
'agent.baseagent.update': { allow: true, scopes: ['agent'], constraints: { requireAgentOwner: true, targetCurrentAgentOnly: true } },
|
|
45
45
|
'observable.current': { allow: true, scopes: ['agent'], constraints: { requireAgentOwner: true } },
|
|
46
46
|
'observable.update': { allow: true, scopes: ['agent'], constraints: { requireAgentOwner: true, targetCurrentAgentOnly: true } },
|
|
47
|
+
'file.root.agent': { allow: true, scopes: ['agent'], constraints: { requireAgentOwner: true, targetCurrentAgentOnly: true } },
|
|
48
|
+
'file.root.storage': { allow: true, scopes: ['agent'], constraints: { requireAgentOwner: true, targetCurrentAgentOnly: true } },
|
|
49
|
+
'file.storage.list': { allow: true, scopes: ['agent'], constraints: { requireAgentOwner: true, targetCurrentAgentOnly: true } },
|
|
50
|
+
'file.storage.query': { allow: true, scopes: ['agent'], constraints: { requireAgentOwner: true, targetCurrentAgentOnly: true } },
|
|
51
|
+
'file.storage.fetch': { allow: true, scopes: ['agent'], constraints: { requireAgentOwner: true, targetCurrentAgentOnly: true } },
|
|
52
|
+
'file.storage.quota': { allow: true, scopes: ['agent'], constraints: { requireAgentOwner: true, targetCurrentAgentOnly: true } },
|
|
53
|
+
'file.root.relation': { allow: true, scopes: ['relation', 'agent'], constraints: { targetCurrentAgentOnly: true } },
|
|
54
|
+
'file.data.list': { allow: true, scopes: ['relation', 'agent'], constraints: { targetCurrentAgentOnly: true } },
|
|
55
|
+
'file.data.query': { allow: true, scopes: ['relation', 'agent'], constraints: { targetCurrentAgentOnly: true } },
|
|
56
|
+
'file.data.fetch': { allow: true, scopes: ['relation', 'agent'], constraints: { targetCurrentAgentOnly: true } },
|
|
47
57
|
'config.get': { allow: true, scopes: ['agent', 'relation'] },
|
|
48
58
|
'config.set': { allow: true, scopes: ['agent', 'relation'] },
|
|
49
59
|
'config.unset': { allow: true, scopes: ['agent', 'relation'] },
|
|
@@ -29,14 +29,25 @@ const BASEAGENT_BEHAVIOR_FIELDS = new Set([
|
|
|
29
29
|
'useVertex',
|
|
30
30
|
]);
|
|
31
31
|
const SUPPORTED_BASEAGENTS = new Set(['claude', 'codex', 'gemini', 'ecagent']);
|
|
32
|
+
/** Public behavior fields for a runner this version does not implement. */
|
|
33
|
+
export function isUnsupportedBaseagentConfigField(fieldPath) {
|
|
34
|
+
const parts = fieldPath.split('.');
|
|
35
|
+
return parts.length === 3
|
|
36
|
+
&& parts[0] === 'baseagents'
|
|
37
|
+
&& /^[a-z][a-z0-9_-]*$/.test(parts[1])
|
|
38
|
+
&& !SUPPORTED_BASEAGENTS.has(parts[1])
|
|
39
|
+
&& BASEAGENT_BEHAVIOR_FIELDS.has(parts[2]);
|
|
40
|
+
}
|
|
32
41
|
const SENSITIVE_TOP_FIELDS = new Set([
|
|
33
42
|
'$schema_version',
|
|
34
43
|
'aid',
|
|
35
44
|
'enabled',
|
|
36
45
|
'lifecycle',
|
|
37
46
|
'initialized',
|
|
47
|
+
'initialization',
|
|
38
48
|
'owners',
|
|
39
49
|
'access',
|
|
50
|
+
'fullaccess',
|
|
40
51
|
'admins',
|
|
41
52
|
'channels',
|
|
42
53
|
'aun',
|
|
@@ -19,7 +19,8 @@ import { atomicReadJson, atomicWriteJson } from '../utils/atomic-write.js';
|
|
|
19
19
|
import { fileCache } from '../core/daemon-file-cache.js';
|
|
20
20
|
import { isValidAid } from '../aun/aid/validation.js';
|
|
21
21
|
import { normalizeAidDomain } from '../aun/aid/domain.js';
|
|
22
|
-
import { isExplicitGroupId } from '../aun/group
|
|
22
|
+
import { isExplicitGroupId } from '../aun/group/index.js';
|
|
23
|
+
import { ensureRelationSharedDirectory } from '../core/relation/shared-data.js';
|
|
23
24
|
import { parseContactAlias } from './contact-alias.js';
|
|
24
25
|
import { loadSchema, listSchemaVersions, currentVersion, isSchemaName, } from './schema-registry.js';
|
|
25
26
|
import { mergeLayers, expandVars, buildEnvResolver } from './merge.js';
|
|
@@ -881,6 +882,9 @@ export function write(target, value, sel, opts = {}) {
|
|
|
881
882
|
}
|
|
882
883
|
// 2. 写入文件
|
|
883
884
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
885
|
+
if (target === ConfigTarget.Relation && sel?.self && sel.peerKey) {
|
|
886
|
+
ensureRelationSharedDirectory(sel.self, sel.peerKey);
|
|
887
|
+
}
|
|
884
888
|
atomicWriteJson(file, normalized);
|
|
885
889
|
finishConfigWrite(target, sel, file);
|
|
886
890
|
}
|
|
@@ -1291,6 +1295,27 @@ export function resolveAgentConfig(sel, opts = {}) {
|
|
|
1291
1295
|
stripRelationRoleData(stripStaticOwnerField(relationConfig)),
|
|
1292
1296
|
];
|
|
1293
1297
|
const merged = mergeLayers(layers, fields);
|
|
1298
|
+
// A baseagent block is a configuration record rather than an atomic value:
|
|
1299
|
+
// selecting a model at Agent or relation scope must retain the defaults
|
|
1300
|
+
// gateway credentials and endpoint. Keep the generic dictionary merge
|
|
1301
|
+
// contract unchanged for every other field.
|
|
1302
|
+
merged.baseagents = mergeBaseagentBlocks(layers);
|
|
1303
|
+
return merged;
|
|
1304
|
+
}
|
|
1305
|
+
function mergeBaseagentBlocks(layers) {
|
|
1306
|
+
const merged = {};
|
|
1307
|
+
for (const layer of layers) {
|
|
1308
|
+
const baseagents = layer?.baseagents;
|
|
1309
|
+
if (!baseagents || typeof baseagents !== 'object' || Array.isArray(baseagents))
|
|
1310
|
+
continue;
|
|
1311
|
+
for (const [name, rawBlock] of Object.entries(baseagents)) {
|
|
1312
|
+
if (!rawBlock || typeof rawBlock !== 'object' || Array.isArray(rawBlock)) {
|
|
1313
|
+
merged[name] = rawBlock;
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
merged[name] = { ...(merged[name] ?? {}), ...rawBlock };
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1294
1319
|
return merged;
|
|
1295
1320
|
}
|
|
1296
1321
|
/**
|
|
@@ -1326,12 +1351,13 @@ function stripStaticOwnerField(config) {
|
|
|
1326
1351
|
if (!Object.prototype.hasOwnProperty.call(config, 'owners')
|
|
1327
1352
|
&& !Object.prototype.hasOwnProperty.call(config, 'admins')
|
|
1328
1353
|
&& !Object.prototype.hasOwnProperty.call(config, 'access')
|
|
1329
|
-
&& !Object.prototype.hasOwnProperty.call(config, 'groupSlashRequireMentions')
|
|
1354
|
+
&& !Object.prototype.hasOwnProperty.call(config, 'groupSlashRequireMentions')
|
|
1355
|
+
&& !Object.prototype.hasOwnProperty.call(config, 'fullaccess'))
|
|
1330
1356
|
return config;
|
|
1331
|
-
// groupSlashRequireMentions
|
|
1357
|
+
// fullaccess and groupSlashRequireMentions are Agent-only. Relation/defaults
|
|
1332
1358
|
// files allow forward-compatible unknown properties, so strip it before the
|
|
1333
1359
|
// generic merge chain can treat a manually written value as an override.
|
|
1334
|
-
const { owners: _owners, admins: _admins, access: _access, groupSlashRequireMentions: _groupSlashRequireMentions, ...rest } = config;
|
|
1360
|
+
const { owners: _owners, admins: _admins, access: _access, groupSlashRequireMentions: _groupSlashRequireMentions, fullaccess: _fullaccess, ...rest } = config;
|
|
1335
1361
|
return rest;
|
|
1336
1362
|
}
|
|
1337
1363
|
function stripRelationRoleData(config) {
|
|
@@ -1354,7 +1380,9 @@ export function resolveEffective(sel, opts = {}) {
|
|
|
1354
1380
|
enabled: config.enabled,
|
|
1355
1381
|
lifecycle: config.lifecycle,
|
|
1356
1382
|
initialized: config.initialized,
|
|
1383
|
+
initialization: config.initialization,
|
|
1357
1384
|
owners: config.owners,
|
|
1385
|
+
fullaccess: config.fullaccess,
|
|
1358
1386
|
aun: config.aun,
|
|
1359
1387
|
channels: config.channels ?? [],
|
|
1360
1388
|
models: config.models,
|
|
@@ -6,8 +6,9 @@ import fs from 'fs';
|
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import { resolvePaths, agentDir, agentRelationConfig } from '../paths.js';
|
|
8
8
|
import { roleMenuQuery, roleMenuUpdate } from '../core/command/role-menu.js';
|
|
9
|
-
import { parsePeerKey } from '../core/relation/peer-identity.js';
|
|
10
|
-
import { isExplicitGroupId } from '../aun/group
|
|
9
|
+
import { parsePeerKey, PeerIdentityCache } from '../core/relation/peer-identity.js';
|
|
10
|
+
import { isExplicitGroupId } from '../aun/group/index.js';
|
|
11
|
+
import { isUnsupportedBaseagentConfigField } from './config-field-policy.js';
|
|
11
12
|
export const AUTHENTICATED_CONFIG_ROLE_ENV = 'EVOLCORE_AUTHENTICATED_CONFIG_ROLE';
|
|
12
13
|
export function executeResolvedConfigCommand(command, options = {}) {
|
|
13
14
|
try {
|
|
@@ -63,7 +64,7 @@ async function executeRelationRoleCommand(op, options) {
|
|
|
63
64
|
if (!op.self || !op.peerKey) {
|
|
64
65
|
return { ok: false, code: 'PEER_REQUIRED', error: 'role requires --self <aid> --peer <peer-aid>' };
|
|
65
66
|
}
|
|
66
|
-
const target = roleTargetFromPeerKey(op.peerKey);
|
|
67
|
+
const target = roleTargetFromPeerKey(op.self, op.peerKey);
|
|
67
68
|
const context = options.roleContext ?? {
|
|
68
69
|
self: op.self,
|
|
69
70
|
actorRole: options.role || 'owner',
|
|
@@ -139,7 +140,7 @@ async function executeRelationRoleCommand(op, options) {
|
|
|
139
140
|
roleDetails: updatedDetails,
|
|
140
141
|
};
|
|
141
142
|
}
|
|
142
|
-
function roleTargetFromPeerKey(peerKey) {
|
|
143
|
+
function roleTargetFromPeerKey(self, peerKey) {
|
|
143
144
|
let parsed;
|
|
144
145
|
try {
|
|
145
146
|
parsed = parsePeerKey(peerKey);
|
|
@@ -150,7 +151,13 @@ function roleTargetFromPeerKey(peerKey) {
|
|
|
150
151
|
if (parsed.channelType !== 'aun') {
|
|
151
152
|
throw new ConfigError('NOT_SUPPORTED', 'Relation role assignments currently support AUN peers only');
|
|
152
153
|
}
|
|
153
|
-
|
|
154
|
+
const relation = read(ConfigTarget.Relation, { self, peerKey });
|
|
155
|
+
const identity = PeerIdentityCache.get('aun', parsed.channelId, agentDir(self));
|
|
156
|
+
const verifiedType = identity?.aid === parsed.channelId && identity.source === 'agentmd' && identity.verifiedAt > 0
|
|
157
|
+
? identity.type : undefined;
|
|
158
|
+
const isGroup = relation?.targetKind ? relation.targetKind === 'group'
|
|
159
|
+
: verifiedType ? verifiedType === 'group' : isExplicitGroupId(parsed.channelId);
|
|
160
|
+
return isGroup
|
|
154
161
|
? { kind: 'group', groupId: parsed.channelId }
|
|
155
162
|
: { kind: 'private', peerAid: parsed.channelId };
|
|
156
163
|
}
|
|
@@ -159,6 +166,11 @@ export function executeResolvedConfigOperation(op, options = {}) {
|
|
|
159
166
|
const missingAgent = selectedAgentError(op);
|
|
160
167
|
if (missingAgent)
|
|
161
168
|
return missingAgent;
|
|
169
|
+
if (op.configScope === 'agent'
|
|
170
|
+
&& (op.field === 'fullaccess' || op.field.startsWith('fullaccess.'))
|
|
171
|
+
&& op.subcommand !== 'get') {
|
|
172
|
+
return { ok: false, code: 'FORBIDDEN', error: 'Agent fullaccess can only be configured in ECWeb Agent settings' };
|
|
173
|
+
}
|
|
162
174
|
if (op.subcommand === 'get')
|
|
163
175
|
return executeGet(op, options);
|
|
164
176
|
if (op.subcommand === 'set')
|
|
@@ -353,6 +365,9 @@ function executeSchema(command) {
|
|
|
353
365
|
};
|
|
354
366
|
}
|
|
355
367
|
function executeGet(op, options) {
|
|
368
|
+
if (isUnsupportedBaseagentConfigField(op.field)) {
|
|
369
|
+
return { ok: false, code: 'UNSUPPORTED_FIELD', error: `Config field ${op.field} is not supported by this version` };
|
|
370
|
+
}
|
|
356
371
|
const selector = selectorFor(op, options);
|
|
357
372
|
if (op.configScope === 'process') {
|
|
358
373
|
const config = read(ConfigTarget.Process, selector) || {};
|
|
@@ -2,7 +2,7 @@ import crypto from 'crypto';
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { isValidAid } from '../aun/aid/validation.js';
|
|
5
|
-
import { isExplicitGroupId } from '../aun/group
|
|
5
|
+
import { isExplicitGroupId } from '../aun/group/index.js';
|
|
6
6
|
import { agentConfig, agentContactConfig, agentDir, resolvePaths } from '../paths.js';
|
|
7
7
|
import { atomicReadJson, atomicWrite, atomicWriteJson } from '../utils/atomic-write.js';
|
|
8
8
|
import { fileCache } from '../core/daemon-file-cache.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { agentContactConfig } from '../paths.js';
|
|
2
2
|
import { isValidAid } from '../aun/aid/validation.js';
|
|
3
|
-
import { isExplicitGroupId } from '../aun/group
|
|
3
|
+
import { isExplicitGroupId } from '../aun/group/index.js';
|
|
4
4
|
import { ConfigTarget, read, validateConfig } from './config-manager.js';
|
|
5
5
|
import { ContactMutationError, contactRevision, mutateContactBookSync } from './contact-book-store.js';
|
|
6
6
|
import { formatContactAlias, normalizeContactChannelKey, parseContactAlias, } from './contact-alias.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import { isValidAid } from '../aun/aid/validation.js';
|
|
3
|
-
import { isExplicitGroupId } from '../aun/group
|
|
3
|
+
import { isExplicitGroupId } from '../aun/group/index.js';
|
|
4
4
|
import { agentConfig } from '../paths.js';
|
|
5
5
|
import { getContactSnapshot, resolveContactView } from './contact-book.js';
|
|
6
6
|
import { ContactMutationError } from './contact-book-store.js';
|
|
@@ -10,7 +10,7 @@ import { getRoleDefinition, getRoleRank, normalizeDefaultRole, roleExists } from
|
|
|
10
10
|
import { getStoredRoleRegistry, roleRegistryExists } from './role-store.js';
|
|
11
11
|
import { resolvePrincipal } from './contact-book.js';
|
|
12
12
|
import { isValidAid } from '../aun/aid/validation.js';
|
|
13
|
-
import { isExplicitGroupId } from '../aun/group
|
|
13
|
+
import { isExplicitGroupId } from '../aun/group/index.js';
|
|
14
14
|
export function isAuthenticated(userId) {
|
|
15
15
|
return isValidAid(String(userId || '').trim());
|
|
16
16
|
}
|
|
@@ -97,6 +97,9 @@ function resolveFieldCommand(argv, subcommand, options = {}) {
|
|
|
97
97
|
reason: 'permissionMode is defined by roles; use the role policy editor',
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
|
+
if (subcommand !== 'get' && (field === 'initialization' || field.startsWith('initialization.'))) {
|
|
101
|
+
return { ok: false, code: 'INITIALIZATION_MANAGED', reason: 'initialization is owned by the creation service' };
|
|
102
|
+
}
|
|
100
103
|
const isRelationRole = field === 'role';
|
|
101
104
|
if (isRelationRole && selector.scope !== 'relation') {
|
|
102
105
|
return {
|
package/dist/config-store.js
CHANGED
|
@@ -58,7 +58,7 @@ export function loadDaemonConfig() {
|
|
|
58
58
|
return validateDaemonConfig(normalizeProcessConfigCompat(normalizeDaemonServices(sanitized)), false);
|
|
59
59
|
}
|
|
60
60
|
export function isFullAccessEnabled(config = loadDaemonConfig()) {
|
|
61
|
-
return config.fullaccess?.enabled === true;
|
|
61
|
+
return config.fullaccess?.enabled === undefined || config.fullaccess.enabled === true;
|
|
62
62
|
}
|
|
63
63
|
let eckSnapshotsConfigCache;
|
|
64
64
|
/** Parse the process-level snapshot gate once for the current daemon lifecycle. */
|
|
@@ -48,6 +48,8 @@ function errorFrom(error, fallback = 'agent operation failed') {
|
|
|
48
48
|
*/
|
|
49
49
|
export class AgentApplicationService {
|
|
50
50
|
deps;
|
|
51
|
+
initialization;
|
|
52
|
+
setInitializationService(service) { this.initialization = service; }
|
|
51
53
|
constructor(deps) {
|
|
52
54
|
this.deps = deps;
|
|
53
55
|
}
|
|
@@ -212,6 +214,8 @@ export class AgentApplicationService {
|
|
|
212
214
|
}
|
|
213
215
|
if (!config)
|
|
214
216
|
return { ok: false, error: `Agent "${aid}" not found`, code: 'NOT_FOUND' };
|
|
217
|
+
if (!enabled)
|
|
218
|
+
await this.initialization?.cancel(aid);
|
|
215
219
|
const previousEnabled = config.enabled;
|
|
216
220
|
try {
|
|
217
221
|
config.enabled = enabled;
|
|
@@ -240,6 +244,7 @@ export class AgentApplicationService {
|
|
|
240
244
|
return { ok: true, aid, enabled, reloaded: result.ok };
|
|
241
245
|
}
|
|
242
246
|
async delete(aid, purge = false) {
|
|
247
|
+
await this.initialization?.cancel(aid);
|
|
243
248
|
const paths = resolvePaths();
|
|
244
249
|
const agentDir = path.join(paths.agentsDir, aid);
|
|
245
250
|
const configPath = path.join(agentDir, 'config.json');
|
|
@@ -7,16 +7,31 @@ function stableDigest(parts) {
|
|
|
7
7
|
const material = parts.map(part => compactPart(part) ?? '-').join('\u0000');
|
|
8
8
|
return crypto.createHash('sha256').update(material).digest('hex').slice(0, 20);
|
|
9
9
|
}
|
|
10
|
+
/** Preserve distinct source IDs; fill aliases only when the producer omitted them. */
|
|
11
|
+
export function normalizeToolCorrelation(input) {
|
|
12
|
+
const callId = compactPart(input.callId);
|
|
13
|
+
const correlationId = compactPart(input.correlationId);
|
|
14
|
+
const requestId = compactPart(input.requestId);
|
|
15
|
+
return {
|
|
16
|
+
callId: callId ?? correlationId ?? requestId,
|
|
17
|
+
correlationId: correlationId ?? callId ?? requestId,
|
|
18
|
+
requestId: requestId ?? callId ?? correlationId,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
10
21
|
/** Stable across source/mirror records for one tool-call lifecycle. */
|
|
11
22
|
export function buildToolLifecycleEventKey(input) {
|
|
23
|
+
const { callId } = normalizeToolCorrelation(input);
|
|
24
|
+
// No source ID means no safe join. Never merge unrelated anonymous calls.
|
|
25
|
+
if (!callId)
|
|
26
|
+
return `tool:unlinked:${crypto.randomUUID()}`;
|
|
12
27
|
return `tool:${stableDigest([
|
|
13
28
|
input.sessionId ?? 'unknown',
|
|
14
|
-
|
|
29
|
+
callId,
|
|
15
30
|
])}`;
|
|
16
31
|
}
|
|
17
32
|
/** Stable across repeated projections of one authorization decision. */
|
|
18
33
|
export function buildAuthorizationEventKey(input) {
|
|
19
|
-
const lifecycleId = input.callId
|
|
34
|
+
const lifecycleId = normalizeToolCorrelation(input).callId;
|
|
20
35
|
if (lifecycleId) {
|
|
21
36
|
return `${buildToolLifecycleEventKey({
|
|
22
37
|
sessionId: input.sessionId,
|