evolcore 0.0.31 → 0.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/agents/codex-runner.js +28 -5
  3. package/dist/agents/ecagent-runner.js +4 -0
  4. package/dist/agents/gemini-runner.js +4 -0
  5. package/dist/aun/aid/store.js +0 -81
  6. package/dist/aun/group/fs-download.js +1 -10
  7. package/dist/aun/msg/managed-operation.js +130 -32
  8. package/dist/aun/service-proxy.js +12 -18
  9. package/dist/aun/storage/index.js +29 -0
  10. package/dist/channels/aun.js +10 -135
  11. package/dist/cli/agent-command.js +6 -7
  12. package/dist/cli/agent-create.js +43 -24
  13. package/dist/cli/agent.js +15 -69
  14. package/dist/cli/aun-commands.js +149 -5
  15. package/dist/cli/ctl-command.js +1 -2
  16. package/dist/cli/daemon-commands.js +1 -1
  17. package/dist/cli/diag.js +2 -2
  18. package/dist/cli/handoff-command.js +15 -5
  19. package/dist/cli/memory-command.js +85 -11
  20. package/dist/cli/model.js +14 -138
  21. package/dist/cli/queue-command.js +46 -0
  22. package/dist/cli/response.js +5 -54
  23. package/dist/cli/skill-command.js +9 -3
  24. package/dist/cli/stats.js +10 -4
  25. package/dist/cli/task-command.js +1 -0
  26. package/dist/cli/task-context.js +15 -0
  27. package/dist/cli/trigger-command.js +73 -49
  28. package/dist/config/builtin-roles.js +8 -7
  29. package/dist/core/agent-ipc-registration.js +65 -22
  30. package/dist/core/auth/agent-delegation.js +17 -1
  31. package/dist/core/auth/delegation-authorization.js +1 -0
  32. package/dist/core/auth/operation-catalog.js +11 -3
  33. package/dist/core/command/command-handler.js +164 -21
  34. package/dist/core/command/diag-service.js +5 -1
  35. package/dist/core/handoff/ipc-registration.js +38 -0
  36. package/dist/core/ipc-registration.js +71 -7
  37. package/dist/core/message/aun-ipc-sender.js +48 -4
  38. package/dist/core/message/message-queue.js +131 -3
  39. package/dist/core/message/response-engine.js +30 -2
  40. package/dist/core/permission/sandbox-runtime.js +14 -1
  41. package/dist/core/task/task-recovery-service.js +2 -0
  42. package/dist/core/task/task-resume-operation.js +11 -0
  43. package/dist/index.js +2 -2
  44. package/dist/ipc.js +146 -8
  45. package/dist/trigger/authorization.js +2 -1
  46. package/dist/trigger/ipc-service.js +49 -35
  47. package/dist/trigger/script-sandbox.js +58 -77
  48. package/kits/docs/AUTHORING.md +1 -1
  49. package/kits/docs/channels/aun.md +8 -0
  50. package/kits/docs/evolcore/INDEX.md +2 -2
  51. package/kits/docs/evolcore/task.md +2 -0
  52. package/kits/docs/evolcore/trigger.md +2 -2
  53. package/kits/rules/01-overview.md +0 -9
  54. package/kits/rules/06-channel.md +7 -1
  55. package/kits/templates/system-fragments/commands.md +1 -1
  56. package/package.json +1 -1
@@ -15,6 +15,7 @@ import { resolvePaths } from '../paths.js';
15
15
  import { ipcQuery } from '../ipc.js';
16
16
  import { isHelpFlag, wantsHelp } from './help.js';
17
17
  import { readDelegationCredential } from '../core/auth/agent-delegation.js';
18
+ import { canonicalizeEvolcoreCommandArgv } from '../core/permission/ec-command-parser.js';
18
19
  export const MEMORY_MODES = ['notes', 'generalize', 'promote', 'profile', 'forget'];
19
20
  const MEMORY_HELP = `用法: ec memory <command> [选项]
20
21
 
@@ -63,6 +64,7 @@ ec memory consolidate [--mode <notes|generalize|promote|profile|forget>]
63
64
  ec memory update consolidated/commit-convention.md --tags "rule,done"
64
65
  ec memory archive consolidated/obsolete.md`;
65
66
  export async function cmdMemory(args) {
67
+ args = canonicalizeEvolcoreCommandArgv(['ec', 'memory', ...args]).slice(2);
66
68
  if (args.length === 0 || isHelpFlag(args[0]) || wantsHelp(args)) {
67
69
  console.log(MEMORY_HELP);
68
70
  return;
@@ -99,13 +101,15 @@ export async function cmdMemory(args) {
99
101
  * 解析 `--key value` 与位置参数。未识别的 `--xxx` 一律报错,避免静默忽略。
100
102
  * `--debug <sessionId>` 是所有子命令的公共参数,不用每处声明。
101
103
  */
102
- function parseArgs(args, known) {
104
+ function parseArgs(args, known, throwOnUnknown = false) {
103
105
  const flags = new Map();
104
106
  const positional = [];
105
107
  for (let i = 0; i < args.length; i++) {
106
108
  const a = args[i];
107
109
  if (a.startsWith('--')) {
108
110
  if (a !== '--debug' && !known.includes(a)) {
111
+ if (throwOnUnknown)
112
+ throw new Error(`未知参数: ${a}`);
109
113
  console.error(`❌ 未知参数: ${a}`);
110
114
  process.exit(1);
111
115
  }
@@ -128,6 +132,76 @@ function parseTags(raw) {
128
132
  const tags = raw.split(',').map(t => t.trim()).filter(Boolean);
129
133
  return tags.length > 0 ? tags : undefined;
130
134
  }
135
+ export function resolveMemoryCommandArgv(argv) {
136
+ if (argv[0] !== 'memory')
137
+ throw new Error('Expected a memory command');
138
+ const noteSub = argv[1] === 'note' && ['update', 'drop', 'list'].includes(argv[2]) ? argv[2] : undefined;
139
+ const kind = noteSub ? `note.${noteSub}` : argv[1];
140
+ const known = {
141
+ note: ['--slug', '--tags', '--speakers'], 'note.update': ['--slug', '--tags'],
142
+ 'note.drop': [], 'note.list': [], create: ['--scope', '--slug', '--desc', '--tags', '--keywords', '--replaces', '--peer'],
143
+ update: ['--tags', '--touch'], archive: [], demote: ['--scope'], consolidate: ['--mode'], feedback: [],
144
+ };
145
+ if (!Object.prototype.hasOwnProperty.call(known, kind))
146
+ throw new Error('Invalid memory command');
147
+ const { flags, positional } = parseArgs(argv.slice(noteSub ? 3 : 2), known[kind], true);
148
+ const required = (value, name) => {
149
+ if (!value)
150
+ throw new Error(`${name} is required`);
151
+ return value;
152
+ };
153
+ const content = positional.join(' ').trim();
154
+ const type = `memory.${kind}`;
155
+ if (kind === 'note')
156
+ return { type, slug: required(flags.get('--slug'), '--slug'), content: required(content, 'content'),
157
+ tags: parseTags(flags.get('--tags')), speakers: parseTags(flags.get('--speakers')) };
158
+ if (kind === 'note.update') {
159
+ const newContent = positional.slice(1).join(' ').trim();
160
+ if (!flags.get('--slug') && !flags.has('--tags') && !newContent)
161
+ throw new Error('No memory note changes supplied');
162
+ return { type, slug: required(positional[0], 'slug'), newSlug: flags.get('--slug'),
163
+ tags: parseTags(flags.get('--tags')), tagsProvided: flags.has('--tags'), content: newContent || undefined };
164
+ }
165
+ if (kind === 'note.drop')
166
+ return { type, slug: required(positional[0], 'slug') };
167
+ if (kind === 'note.list')
168
+ return { type };
169
+ if (kind === 'create') {
170
+ const scope = flags.get('--scope');
171
+ if (!scope || !['relation', 'agent', 'owner'].includes(scope))
172
+ throw new Error('Invalid memory scope');
173
+ if (!flags.has('--tags'))
174
+ throw new Error('--tags is required');
175
+ if (flags.has('--peer') && scope !== 'relation')
176
+ throw new Error('--peer requires relation scope');
177
+ return { type, scope, slug: required(flags.get('--slug'), '--slug'), description: required(flags.get('--desc'), '--desc'),
178
+ content: required(content, 'content'), tags: parseTags(flags.get('--tags')), keywords: parseTags(flags.get('--keywords')),
179
+ replaces: parseTags(flags.get('--replaces')), targetPeerKey: flags.get('--peer') };
180
+ }
181
+ if (kind === 'update') {
182
+ if (!flags.has('--tags') && !flags.has('--touch'))
183
+ throw new Error('No memory changes supplied');
184
+ return { type, filePath: required(positional[0], 'path'), tags: parseTags(flags.get('--tags')), touch: flags.has('--touch') };
185
+ }
186
+ if (kind === 'archive') {
187
+ if (!positional.length)
188
+ throw new Error('Memory paths are required');
189
+ return { type, filePaths: positional };
190
+ }
191
+ if (kind === 'demote') {
192
+ const scope = flags.get('--scope') ?? 'agent';
193
+ if (scope !== 'agent' && scope !== 'owner')
194
+ throw new Error('Invalid demotion scope');
195
+ return { type, slug: required(positional[0], 'slug'), scope };
196
+ }
197
+ if (kind === 'consolidate') {
198
+ const mode = flags.get('--mode') ?? 'notes';
199
+ if (!MEMORY_MODES.includes(mode))
200
+ throw new Error('Invalid consolidation mode');
201
+ return { type, mode };
202
+ }
203
+ return { type, text: required(content, 'feedback') };
204
+ }
131
205
  /** 各子命令的一行完整用法:缺参报错时附上,模型照抄即可重试。 */
132
206
  const USAGE = {
133
207
  note: 'ec memory note --slug <slug> [--tags <tag1,tag2>] [--speakers <aid1,aid2>] "<正文>"',
@@ -193,7 +267,7 @@ async function cmdMemoryNoteCreate(args) {
193
267
  failWithUsage('必须提供记忆内容(content)', USAGE.note);
194
268
  const { socketPath, ...session } = requireManagedSession('ec memory note', flags);
195
269
  const result = await ipcQuery(socketPath, {
196
- type: 'memory.note', ...session, slug, content, tags,
270
+ type: 'memory.note', argv: ['memory', 'note', ...args], ...session, slug, content, tags,
197
271
  speakers: speakers && speakers.length > 0 ? speakers : undefined,
198
272
  }, 15_000);
199
273
  reportResult(result, (r) => {
@@ -215,7 +289,7 @@ async function cmdMemoryNoteUpdate(args) {
215
289
  }
216
290
  const { socketPath, ...session } = requireManagedSession('ec memory note update', flags);
217
291
  const result = await ipcQuery(socketPath, {
218
- type: 'memory.note.update', ...session, slug,
292
+ type: 'memory.note.update', argv: ['memory', 'note', 'update', ...args], ...session, slug,
219
293
  newSlug, tags, tagsProvided: tagsRaw !== undefined,
220
294
  content: content || undefined,
221
295
  }, 15_000);
@@ -231,7 +305,7 @@ async function cmdMemoryNoteDrop(args) {
231
305
  failWithUsage('必须提供要撤销的 slug', USAGE.noteDrop);
232
306
  const { socketPath, ...session } = requireManagedSession('ec memory note drop', flags);
233
307
  const result = await ipcQuery(socketPath, {
234
- type: 'memory.note.drop', ...session, slug,
308
+ type: 'memory.note.drop', argv: ['memory', 'note', 'drop', ...args], ...session, slug,
235
309
  }, 15_000);
236
310
  reportResult(result, (r) => {
237
311
  console.log(`✓ 已撤销: ${r.filePath ?? slug}`);
@@ -241,7 +315,7 @@ async function cmdMemoryNoteList(args) {
241
315
  const { flags } = parseArgs(args, []);
242
316
  const { socketPath, ...session } = requireManagedSession('ec memory note list', flags);
243
317
  const result = await ipcQuery(socketPath, {
244
- type: 'memory.note.list', ...session,
318
+ type: 'memory.note.list', argv: ['memory', 'note', 'list', ...args], ...session,
245
319
  }, 15_000);
246
320
  reportResult(result, (r) => {
247
321
  const notes = r.notes ?? [];
@@ -286,7 +360,7 @@ async function cmdMemoryCreate(args) {
286
360
  }
287
361
  const { socketPath, ...session } = requireManagedSession('ec memory create', flags);
288
362
  const result = await ipcQuery(socketPath, {
289
- type: 'memory.create', ...session, scope, slug, description: desc,
363
+ type: 'memory.create', argv: ['memory', 'create', ...args], ...session, scope, slug, description: desc,
290
364
  content,
291
365
  tags: parseTags(tagsRaw),
292
366
  keywords: parseTags(keywordsRaw),
@@ -312,7 +386,7 @@ async function cmdMemoryConsolidatedUpdate(args) {
312
386
  failWithUsage('至少提供 --tags 或 --touch', USAGE.update);
313
387
  const { socketPath, ...session } = requireManagedSession('ec memory update', flags);
314
388
  const result = await ipcQuery(socketPath, {
315
- type: 'memory.update', ...session, filePath,
389
+ type: 'memory.update', argv: ['memory', 'update', ...args], ...session, filePath,
316
390
  tags: parseTags(tagsRaw), touch,
317
391
  }, 15_000);
318
392
  reportResult(result, () => {
@@ -325,7 +399,7 @@ async function cmdMemoryArchive(args) {
325
399
  failWithUsage('必须提供至少一个要归档的文件路径', USAGE.archive);
326
400
  const { socketPath, ...session } = requireManagedSession('ec memory archive', flags);
327
401
  const result = await ipcQuery(socketPath, {
328
- type: 'memory.archive', ...session, filePaths: positional,
402
+ type: 'memory.archive', argv: ['memory', 'archive', ...args], ...session, filePaths: positional,
329
403
  }, 15_000);
330
404
  reportResult(result, (r) => {
331
405
  const archived = r.archived ?? positional;
@@ -353,7 +427,7 @@ async function cmdMemoryDemote(args) {
353
427
  }
354
428
  const { socketPath, ...session } = requireManagedSession('ec memory demote', flags);
355
429
  const result = await ipcQuery(socketPath, {
356
- type: 'memory.demote', ...session, slug, scope,
430
+ type: 'memory.demote', argv: ['memory', 'demote', ...args], ...session, slug, scope,
357
431
  }, 15_000);
358
432
  reportResult(result, (r) => {
359
433
  if (r.conflictArchived) {
@@ -385,7 +459,7 @@ async function cmdMemoryConsolidate(args) {
385
459
  const { socketPath, ...session } = requireManagedSession('ec memory consolidate', flags);
386
460
  console.log(`正在触发记忆整理(mode=${mode})...`);
387
461
  const result = await ipcQuery(socketPath, {
388
- type: 'memory.consolidate', ...session, mode,
462
+ type: 'memory.consolidate', argv: ['memory', 'consolidate', ...args], ...session, mode,
389
463
  }, 30_000);
390
464
  reportResult(result, (r) => {
391
465
  console.log(`✓ 整理会话已启动(${mode} 模式)`);
@@ -418,7 +492,7 @@ async function cmdMemoryFeedback(args) {
418
492
  }
419
493
  const { socketPath, ...session } = requireManagedSession('ec memory feedback', flags);
420
494
  const result = await ipcQuery(socketPath, {
421
- type: 'memory.feedback', ...session, text,
495
+ type: 'memory.feedback', argv: ['memory', 'feedback', ...args], ...session, text,
422
496
  }, 15_000);
423
497
  reportResult(result, () => {
424
498
  console.log('✓ 已记录反馈内容');
package/dist/cli/model.js CHANGED
@@ -19,10 +19,8 @@ import { resolvePaths } from '../paths.js';
19
19
  import { ipcQuery } from '../ipc.js';
20
20
  import { parsePeerKey } from '../core/relation/peer-identity.js';
21
21
  import { resolvePeerRoleDetail } from '../config/peer-role-resolver.js';
22
- import { getFieldPermission } from '../config/roles.js';
23
22
  import { AGENT_DELEGATION_TOKEN_ENV, readDelegationCredential } from '../core/auth/agent-delegation.js';
24
23
  import { formatModelCheck, runModelCheck } from '../core/model/model-diagnostics.js';
25
- import { validateRuntimeStringFieldOverride } from '../core/role/runtime-policy.js';
26
24
  const ALL_EFFORTS = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'auto'];
27
25
  const SCOPE_LABEL = {
28
26
  global: '全局', agent: 'agent级', role: '角色级', relation: '关系级',
@@ -45,29 +43,21 @@ function fail(formatJson, code, message, data) {
45
43
  }
46
44
  process.exit(1);
47
45
  }
48
- function isManagedSessionEnv(args) {
49
- void args;
50
- return !!process.env.EVOLCORE_SESSION_ID;
51
- }
52
- function isManagedTargetedWriteEnv(args) {
53
- return !!process.env.EVOLCORE_SESSION_ID
54
- && !isManagedSessionEnv(args);
55
- }
56
- async function runCtlCommand(cmd, formatJson, timeoutMs = 10_000) {
46
+ async function runManagedCliOperation(args, formatJson, timeoutMs = 10_000) {
57
47
  const sessionId = process.env.EVOLCORE_SESSION_ID;
58
48
  if (!sessionId)
59
49
  fail(formatJson, 'NO_SESSION', 'EVOLCORE_SESSION_ID 未设置');
60
- const result = await ipcQuery(resolvePaths().socket, {
61
- type: 'ctl',
62
- cmd,
63
- sessionId,
64
- delegationToken: readDelegationCredential(),
50
+ const delegationToken = readDelegationCredential();
51
+ if (!delegationToken)
52
+ fail(formatJson, 'DELEGATION_REQUIRED', `${AGENT_DELEGATION_TOKEN_ENV} 未设置`);
53
+ const response = await ipcQuery(resolvePaths().socket, {
54
+ type: 'ctl', argv: args, sessionId, delegationToken,
65
55
  }, timeoutMs);
66
- if (!result)
56
+ if (!response)
67
57
  fail(formatJson, 'DAEMON_UNAVAILABLE', '无法连接 evolcore 服务');
68
- if (!result.ok)
69
- fail(formatJson, 'CTL_FAILED', result.error || 'ctl 执行失败');
70
- return { text: String(result.result || ''), data: result.data };
58
+ if (!response.ok)
59
+ fail(formatJson, response.code || 'CTL_FAILED', response.error || '命令执行失败');
60
+ printCtlResult({ text: response.result || '', data: response.data }, formatJson);
71
61
  }
72
62
  function printCtlResult(result, formatJson) {
73
63
  if (formatJson) {
@@ -76,56 +66,6 @@ function printCtlResult(result, formatJson) {
76
66
  }
77
67
  console.log(result.text);
78
68
  }
79
- async function runManagedConfigOperation(args, formatJson) {
80
- const sessionId = process.env.EVOLCORE_SESSION_ID;
81
- if (!sessionId)
82
- fail(formatJson, 'NO_SESSION', 'EVOLCORE_SESSION_ID 未设置');
83
- const delegationToken = readDelegationCredential();
84
- if (!delegationToken) {
85
- fail(formatJson, 'DELEGATION_REQUIRED', `${AGENT_DELEGATION_TOKEN_ENV} 未设置`);
86
- }
87
- let response;
88
- try {
89
- response = await ipcQuery(resolvePaths().socket, {
90
- type: 'config.op',
91
- argv: ['config', ...args],
92
- sessionId,
93
- delegationToken,
94
- }, 10_000);
95
- }
96
- catch {
97
- response = null;
98
- }
99
- if (!response)
100
- fail(formatJson, 'DAEMON_UNAVAILABLE', '无法连接 evolcore 服务');
101
- if (!response.ok || !response.result?.ok) {
102
- fail(formatJson, response.code || 'CONFIG_AUTH_FAILED', response.error || 'config 操作失败', response.data);
103
- }
104
- }
105
- function configSelectorArgs(sel) {
106
- const args = ['--self', sel.self];
107
- if (sel.peerKey)
108
- args.push('--peer', sel.peerKey);
109
- return args;
110
- }
111
- async function setManagedModelField(sel, field, value, formatJson) {
112
- const subcommand = value === null ? ['unset', field] : ['set', field, value];
113
- await runManagedConfigOperation([...subcommand, ...configSelectorArgs(sel)], formatJson);
114
- }
115
- function preflightManagedFieldOverride(sel, role, field, value, formatJson) {
116
- if (!role)
117
- return;
118
- const policy = getFieldPermission(role, field, sel.self);
119
- if (policy?.allowOverride === false) {
120
- fail(formatJson, 'ROLE_OVERRIDE_DISABLED', `Role ${role} cannot override ${field}`);
121
- }
122
- if (value === null)
123
- return;
124
- const decision = validateRuntimeStringFieldOverride({ selfAid: sel.self, role, field, value });
125
- if (!decision.ok) {
126
- fail(formatJson, decision.code || 'ROLE_VALUE_NOT_ALLOWED', decision.message || `Value ${value} is not allowed for ${field}`);
127
- }
128
- }
129
69
  /**
130
70
  * 解析 --self / --peer 为作用域选择器。
131
71
  */
@@ -286,11 +226,6 @@ async function cmdList(args, formatJson) {
286
226
  console.log(HELP);
287
227
  return;
288
228
  }
289
- if (isManagedSessionEnv(args)) {
290
- const out = await runCtlCommand('/model list', formatJson);
291
- printCtlResult(out, formatJson);
292
- return;
293
- }
294
229
  const sel = parseSelector(args, formatJson);
295
230
  const role = explicitOrDerivedRole(args, sel);
296
231
  const effectiveSel = selectorWithRole(sel, role);
@@ -337,11 +272,6 @@ async function cmdCurrent(args, formatJson) {
337
272
  console.log(HELP);
338
273
  return;
339
274
  }
340
- if (isManagedSessionEnv(args)) {
341
- const out = await runCtlCommand('/model current', formatJson);
342
- printCtlResult(out, formatJson);
343
- return;
344
- }
345
275
  const sel = parseSelector(args, formatJson);
346
276
  const ba = activeBaseagent(sel.self);
347
277
  const role = explicitOrDerivedRole(args, sel);
@@ -372,11 +302,6 @@ async function cmdInfo(args, formatJson) {
372
302
  const modelId = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
373
303
  if (!modelId)
374
304
  fail(formatJson, 'MISSING_MODEL_ID', 'info 需要 <model-id>');
375
- if (isManagedSessionEnv(args)) {
376
- const out = await runCtlCommand(`/model info ${modelId}`, formatJson);
377
- printCtlResult(out, formatJson);
378
- return;
379
- }
380
305
  const sel = parseSelector(args, formatJson);
381
306
  const self = getArgValue(args, '--self');
382
307
  const info = await getModelInfo(modelId, self);
@@ -424,12 +349,6 @@ async function cmdUse(args, formatJson) {
424
349
  const modelId = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
425
350
  if (!modelId)
426
351
  fail(formatJson, 'MISSING_MODEL_ID', 'use 需要 <model-id>');
427
- if (isManagedSessionEnv(args)) {
428
- const effort = getArgValue(args, '--effort');
429
- const out = await runCtlCommand(`/model ${modelId}${effort ? ` ${effort}` : ''}`, formatJson);
430
- printCtlResult(out, formatJson);
431
- return;
432
- }
433
352
  const sel = parseSelector(args, formatJson);
434
353
  const role = explicitOrDerivedRole(args, sel);
435
354
  const scope = determineWriteScope(sel, formatJson);
@@ -457,19 +376,6 @@ async function cmdUse(args, formatJson) {
457
376
  fail(formatJson, 'INVALID_EFFORT', `当前模型目录未声明支持推理强度: ${effort}`);
458
377
  }
459
378
  const effortVal = effort === 'auto' ? null : (effort ?? undefined);
460
- if (isManagedTargetedWriteEnv(args)) {
461
- if (effortVal !== undefined) {
462
- const effortKey = 'effort';
463
- preflightManagedFieldOverride(sel, role, `baseagents.${ba}.${effortKey}`, effortVal, formatJson);
464
- }
465
- await setManagedModelField(sel, `baseagents.${ba}.model`, modelId, formatJson);
466
- if (effortVal !== undefined) {
467
- const effortKey = 'effort';
468
- await setManagedModelField(sel, `baseagents.${ba}.${effortKey}`, effortVal, formatJson);
469
- }
470
- describeWrite(scope, sel, modelId, effort ?? null, formatJson);
471
- return;
472
- }
473
379
  try {
474
380
  writeScope(scope, sel, ba, { model: modelId, effort: effortVal });
475
381
  }
@@ -491,11 +397,6 @@ async function cmdEffort(args, formatJson) {
491
397
  if (!ALL_EFFORTS.includes(level)) {
492
398
  fail(formatJson, 'INVALID_EFFORT', `无效推理强度: ${level}(${ALL_EFFORTS.join('/')})`);
493
399
  }
494
- if (isManagedSessionEnv(args)) {
495
- const out = await runCtlCommand(`/effort ${level}`, formatJson);
496
- printCtlResult(out, formatJson);
497
- return;
498
- }
499
400
  const sel = parseSelector(args, formatJson);
500
401
  const scope = determineWriteScope(sel, formatJson);
501
402
  const ba = activeBaseagent(sel.self);
@@ -507,13 +408,6 @@ async function cmdEffort(args, formatJson) {
507
408
  if (!model || !catalogEfforts(cat, model, ba).includes(val))
508
409
  fail(formatJson, 'INVALID_EFFORT', `当前模型目录未声明支持推理强度: ${val}`);
509
410
  }
510
- if (isManagedTargetedWriteEnv(args)) {
511
- const effortKey = 'effort';
512
- preflightManagedFieldOverride(sel, explicitOrDerivedRole(args, sel), `baseagents.${ba}.${effortKey}`, val, formatJson);
513
- await setManagedModelField(sel, `baseagents.${ba}.${effortKey}`, val, formatJson);
514
- describeWrite(scope, sel, null, level, formatJson);
515
- return;
516
- }
517
411
  try {
518
412
  writeScope(scope, sel, ba, { effort: val });
519
413
  }
@@ -529,26 +423,9 @@ async function cmdReset(args, formatJson) {
529
423
  console.log(HELP);
530
424
  return;
531
425
  }
532
- if (isManagedSessionEnv(args)) {
533
- const out = await runCtlCommand('/model reset', formatJson);
534
- printCtlResult(out, formatJson);
535
- return;
536
- }
537
426
  const sel = parseSelector(args, formatJson);
538
427
  const scope = determineWriteScope(sel, formatJson);
539
428
  const ba = activeBaseagent(sel.self);
540
- if (isManagedTargetedWriteEnv(args)) {
541
- const effortKey = 'effort';
542
- const role = explicitOrDerivedRole(args, sel);
543
- preflightManagedFieldOverride(sel, role, `baseagents.${ba}.model`, null, formatJson);
544
- preflightManagedFieldOverride(sel, role, `baseagents.${ba}.${effortKey}`, null, formatJson);
545
- await setManagedModelField(sel, `baseagents.${ba}.model`, null, formatJson);
546
- await setManagedModelField(sel, `baseagents.${ba}.${effortKey}`, null, formatJson);
547
- emit(formatJson, {
548
- ok: true, scope, self: sel.self ?? null, peerKey: sel.peerKey ?? null,
549
- }, () => `✓ 已清除 ${SCOPE_LABEL[scope]} 设置,回落上一级(该范围所有会话下条消息生效)`);
550
- return;
551
- }
552
429
  try {
553
430
  clearScope(scope, sel, ba);
554
431
  }
@@ -568,11 +445,6 @@ async function cmdCheck(args, formatJson) {
568
445
  console.log(HELP);
569
446
  return;
570
447
  }
571
- if (isManagedSessionEnv(args)) {
572
- const out = await runCtlCommand('/model check', formatJson, 60_000);
573
- printCtlResult(out, formatJson);
574
- return;
575
- }
576
448
  const sel = parseSelector(args, formatJson);
577
449
  const role = explicitOrDerivedRole(args, sel);
578
450
  const ba = activeBaseagent(sel.self);
@@ -609,5 +481,9 @@ export async function cmdModel(args) {
609
481
  && (getArgValue(args, '--self') || getArgValue(args, '--peer') || getArgValue(args, '--role'))) {
610
482
  fail(formatJson, 'NOT_ALLOWED', 'Managed model commands cannot select --self, --peer, or --role; they always use the authenticated current relation');
611
483
  }
484
+ if (process.env.EVOLCORE_SESSION_ID) {
485
+ await runManagedCliOperation(['model', ...args], formatJson, sub === 'check' ? 60_000 : 10_000);
486
+ return;
487
+ }
612
488
  await dispatch(sub, args, formatJson);
613
489
  }
@@ -2,7 +2,9 @@ import { resolvePaths } from '../paths.js';
2
2
  import { ipcQuery } from '../ipc.js';
3
3
  import { readDelegationCredential } from '../core/auth/agent-delegation.js';
4
4
  import { getArgValue, isHelpFlag, wantsHelp } from './help.js';
5
+ import { canonicalizeEvolcoreCommandArgv } from '../core/permission/ec-command-parser.js';
5
6
  export async function cmdQueue(args) {
7
+ args = canonicalizeEvolcoreCommandArgv(['ec', 'queue', ...args]).slice(2);
6
8
  if (args.length === 0 || isHelpFlag(args[0]) || wantsHelp(args)) {
7
9
  console.log(`用法: ec queue --agent <aid> [选项]
8
10
 
@@ -91,6 +93,7 @@ JSON 时间字段:
91
93
  const managed = process.env.EVOLCORE_SESSION_ID;
92
94
  const result = await ipcQuery(socketPath, managed ? {
93
95
  type: 'queue.op',
96
+ argv: ['queue', ...args],
94
97
  agent,
95
98
  action,
96
99
  messageId: cancelMsgId,
@@ -147,6 +150,49 @@ function parseQueueTime(value, flag) {
147
150
  }
148
151
  return parsed;
149
152
  }
153
+ export function parseQueueOperationArgv(argv) {
154
+ if (argv[0] !== 'queue')
155
+ throw new Error('Expected a queue command');
156
+ const flags = new Map();
157
+ const booleanFlags = new Set(['--clear', '--interrupt', '--showid', '--full']);
158
+ const valueFlags = new Set(['--agent', '--cancel', '--sessionkey', '--after', '--before', '--format']);
159
+ for (let index = 1; index < argv.length; index++) {
160
+ const flag = argv[index];
161
+ if (booleanFlags.has(flag))
162
+ flags.set(flag, 'true');
163
+ else if (valueFlags.has(flag) && argv[index + 1] && !argv[index + 1].startsWith('--'))
164
+ flags.set(flag, argv[++index]);
165
+ else
166
+ throw new Error(`Invalid queue option: ${flag}`);
167
+ }
168
+ const agent = flags.get('--agent');
169
+ if (!agent)
170
+ throw new Error('--agent is required');
171
+ if (flags.has('--format') && flags.get('--format') !== 'json')
172
+ throw new Error('--format only supports json');
173
+ if (['--clear', '--cancel', '--interrupt'].filter(flag => flags.has(flag)).length > 1)
174
+ throw new Error('Queue actions are mutually exclusive');
175
+ const time = (flag) => {
176
+ const value = flags.get(flag);
177
+ if (value === undefined)
178
+ return undefined;
179
+ const parsed = /^\d+$/.test(value) ? Number(value) : Date.parse(value);
180
+ if (!Number.isFinite(parsed))
181
+ throw new Error(`Invalid ${flag}`);
182
+ return parsed;
183
+ };
184
+ const after = time('--after');
185
+ const before = time('--before');
186
+ if (after !== undefined && before !== undefined && after >= before)
187
+ throw new Error('--after must precede --before');
188
+ if (flags.has('--interrupt') && !flags.get('--sessionkey'))
189
+ throw new Error('--interrupt requires --sessionkey');
190
+ return {
191
+ agent,
192
+ action: flags.has('--clear') ? 'clear' : flags.has('--cancel') ? 'cancel' : flags.has('--interrupt') ? 'interrupt' : undefined,
193
+ messageId: flags.get('--cancel'), sessionKey: flags.get('--sessionkey'), after, before,
194
+ };
195
+ }
150
196
  function renderQueueItemsCli(items, showId, full, aid) {
151
197
  const lines = [`${aid} — 队列 (${items.length} 条)`, ''];
152
198
  if (items.length === 0) {
@@ -57,7 +57,7 @@ function fail(formatJson, code, message) {
57
57
  }
58
58
  process.exit(1);
59
59
  }
60
- async function managedConfig(fieldArgs, formatJson) {
60
+ async function runManagedResponse(args, formatJson) {
61
61
  const sessionId = process.env.EVOLCORE_SESSION_ID;
62
62
  const delegationToken = readDelegationCredential();
63
63
  if (!sessionId)
@@ -65,62 +65,13 @@ async function managedConfig(fieldArgs, formatJson) {
65
65
  if (!delegationToken)
66
66
  fail(formatJson, 'DELEGATION_REQUIRED', `${AGENT_DELEGATION_TOKEN_ENV} is not set`);
67
67
  const response = await ipcQuery(resolvePaths().socket, {
68
- type: 'config.op',
69
- argv: ['config', ...fieldArgs],
70
- sessionId,
71
- delegationToken,
68
+ type: 'ctl', argv: ['response', ...args], sessionId, delegationToken,
72
69
  }, 10_000);
73
70
  if (!response)
74
71
  fail(formatJson, 'DAEMON_UNAVAILABLE', 'Unable to connect to the evolcore daemon');
75
- if (!response.ok || !response.result?.ok) {
76
- fail(formatJson, response.code || 'CONFIG_AUTH_FAILED', response.error || 'response config operation failed');
77
- }
78
- return response.result;
79
- }
80
- async function runManagedResponse(args, formatJson) {
81
- if (getArgValue(args, '--self') || getArgValue(args, '--peer')) {
82
- fail(formatJson, 'NOT_ALLOWED', 'Managed response commands always use the authenticated current relation');
83
- }
84
- const sub = args[0];
85
- if (sub === 'set') {
86
- const id = args[1];
87
- if (!id)
88
- fail(formatJson, 'MISSING_ID', 'set requires a mode id');
89
- if (!buildRegistry().has(id))
90
- fail(formatJson, 'UNKNOWN_MODE', `Unknown mode: ${id}`);
91
- await managedConfig(['set', RESPONSE_MODE_FIELD, id], formatJson);
92
- return emit(formatJson, { ok: true, scope: 'relation', mode: id }, () => `✓ 已设置响应模式为 ${id}(作用域: relation)`);
93
- }
94
- if (sub === 'reset') {
95
- await managedConfig(['unset', RESPONSE_MODE_FIELD], formatJson);
96
- return emit(formatJson, { ok: true, scope: 'relation' }, () => '✓ 已清除当前关系的响应模式设置');
97
- }
98
- if (sub === 'current') {
99
- const modeResult = await managedConfig(['get', RESPONSE_MODE_FIELD], formatJson);
100
- const paramsResult = await managedConfig(['get', RESPONSE_MODE_PARAMS_FIELD], formatJson);
101
- const configuredMode = typeof modeResult.value === 'string' ? modeResult.value : undefined;
102
- const mode = configuredMode ?? buildRegistry().getPreferredId();
103
- return emit(formatJson, {
104
- ok: true,
105
- scope: 'relation',
106
- responseMode: mode ?? null,
107
- source: configuredMode ? 'config' : 'preferred',
108
- sourceTarget: modeResult.source?.target ?? null,
109
- responseModeParams: paramsResult.value ?? null,
110
- }, () => `当前响应模式: ${mode ?? '(无)'}`);
111
- }
112
- if (sub === 'config') {
113
- if (args[1] === 'set') {
114
- fail(formatJson, 'NOT_ALLOWED', 'responseModeParams cannot be modified as an opaque object from a managed task');
115
- }
116
- const result = await managedConfig(['get', RESPONSE_MODE_PARAMS_FIELD], formatJson);
117
- const params = result.value ?? {};
118
- const modeId = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
119
- if (modeId)
120
- return emit(formatJson, { ok: true, mode: modeId, config: params[modeId] ?? {} }, () => `${modeId} 配置: ${JSON.stringify(params[modeId] ?? {}, null, 2)}`);
121
- return emit(formatJson, { ok: true, responseModeParams: params }, () => `所有模式配置: ${JSON.stringify(params, null, 2)}`);
122
- }
123
- fail(formatJson, 'NOT_ALLOWED', `response ${sub} is not available in a managed task`);
72
+ if (!response.ok)
73
+ fail(formatJson, response.code || 'CTL_FAILED', response.error || 'response operation failed');
74
+ emit(formatJson, response.data ?? { ok: true, result: response.result }, () => response.result || 'ok');
124
75
  }
125
76
  /** 解析 --self / --peer 为作用域选择器 */
126
77
  function parseSelector(args, formatJson) {
@@ -17,21 +17,27 @@
17
17
  import path from 'path';
18
18
  import { spawn } from 'child_process';
19
19
  import { getPackageRoot, agentDir } from '../paths.js';
20
- import { readTaskRuntimeContextFromEnv } from './task-context.js';
20
+ import { authorizeLocalCommand, readTaskRuntimeContextFromEnv, TASK_RUNTIME_CONTEXT_ENV } from './task-context.js';
21
21
  export async function cmdSkill(args) {
22
22
  const script = path.join(getPackageRoot(), 'kits', 'skills', 'ec-skill', 'scripts', 'cli.mjs');
23
23
  // 脚本在包外运行,拿不到主程序的路径工具,这里把 Agent 目录显式传进去,
24
24
  // 让它能定位 Agent 级 skills/ 与 .switches.json。
25
25
  // selfAid 优先取任务运行时上下文(托管会话的权威来源),其次环境变量。
26
26
  const env = { ...process.env };
27
- const selfAid = readTaskRuntimeContextFromEnv()?.selfAid || process.env.EVOLCORE_SELF_AID;
28
- if (selfAid && !env.EVOLCORE_AGENT_DIR) {
27
+ const runtime = process.env.EVOLCORE_SESSION_ID ? await authorizeLocalCommand(['skill', ...args]) : undefined;
28
+ const selfAid = runtime?.selfAid ?? readTaskRuntimeContextFromEnv()?.selfAid ?? process.env.EVOLCORE_SELF_AID;
29
+ if (runtime) {
30
+ env[TASK_RUNTIME_CONTEXT_ENV] = JSON.stringify(runtime);
31
+ env.EVOLCORE_SELF_AID = selfAid;
32
+ }
33
+ if (selfAid && (runtime || !env.EVOLCORE_AGENT_DIR)) {
29
34
  env.EVOLCORE_AGENT_DIR = agentDir(selfAid);
30
35
  }
31
36
  const code = await new Promise((resolve, reject) => {
32
37
  const child = spawn(process.execPath, [script, ...args], {
33
38
  stdio: 'inherit',
34
39
  env,
40
+ cwd: runtime?.projectPath,
35
41
  });
36
42
  child.on('error', reject);
37
43
  child.on('exit', (c) => resolve(c ?? 0));
package/dist/cli/stats.js CHANGED
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { resolveRoot } from '../paths.js';
5
5
  import { wantsHelp } from './help.js';
6
- import { readBestTaskRuntimeContext } from './task-context.js';
6
+ import { authorizeLocalCommand } from './task-context.js';
7
7
  import { queryAggregated, querySessionTurns, queryContextBreakdown, queryTopPeers, queryTopModels, queryMessageAggregated, queryPeerList, querySummary, queryPeerDaily, queryTaskModelCalls, querySessionModelCalls } from '../stats/query.js';
8
8
  import { getBudgetStatus } from '../stats/budget.js';
9
9
  const HELP = `ec stats — Token 用量与费用统计
@@ -124,10 +124,16 @@ const AGENT_SCOPE_ROLES = new Set(['owner', 'admin']);
124
124
  * is confined to its own relation and session. Returns undefined outside a
125
125
  * managed task, leaving direct terminal usage unchanged.
126
126
  */
127
- async function resolveManagedStatsScope(flags, isJson) {
127
+ async function resolveManagedStatsScope(flags, isJson, argv) {
128
128
  if (!process.env.EVOLCORE_SESSION_ID)
129
129
  return undefined;
130
- const runtime = await readBestTaskRuntimeContext();
130
+ let runtime;
131
+ try {
132
+ runtime = await authorizeLocalCommand(argv);
133
+ }
134
+ catch (error) {
135
+ fail(isJson, 'NOT_ALLOWED', error instanceof Error ? error.message : String(error));
136
+ }
131
137
  const role = runtime?.peerRole?.trim();
132
138
  const selfAid = runtime?.selfAid?.trim();
133
139
  // Fail closed: without a resolved identity and role there is no scope to
@@ -225,7 +231,7 @@ export async function handleStats(args) {
225
231
  const isJson = flags.format === 'json';
226
232
  // Authorize before any branch runs: `--rebuild` writes, and every read view
227
233
  // below would otherwise see the whole database.
228
- const managedScope = await resolveManagedStatsScope(flags, isJson);
234
+ const managedScope = await resolveManagedStatsScope(flags, isJson, ['stats', ...args]);
229
235
  if (managedScope)
230
236
  enforceManagedSelector(managedScope, flags, isJson);
231
237
  // 全量重建日聚合表(运维兜底/排查),不依赖时间范围。
@@ -41,6 +41,7 @@ export async function cmdTask(args) {
41
41
  }
42
42
  const result = await ipcQuery(resolvePaths().socket, {
43
43
  type: 'task.resume',
44
+ argv: ['task', ...args],
44
45
  targetSessionId,
45
46
  ...(sessionId ? { sessionId, delegationToken } : {}),
46
47
  }, 10_000);