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
package/dist/cli/agent.js CHANGED
@@ -14,7 +14,6 @@ import { isValidChannelName } from '../core/channel-loader.js';
14
14
  import { AGENT_DELEGATION_TOKEN_ENV, readDelegationCredential } from '../core/auth/agent-delegation.js';
15
15
  import { readTaskRuntimeContextFromEnv } from './task-context.js';
16
16
  import { isValidAid } from '../aun/aid/validation.js';
17
- import { isHClassPath, isLClassPath, isSameOrDescendant } from '../core/permission/protected-paths.js';
18
17
  import { findPostBootstrapWelcomeOutbox } from '../core/bootstrap-messages.js';
19
18
  import { resolveAgentLifecycle } from '../config/lifecycle.js';
20
19
  import { initializationSummary } from './agent-create.js';
@@ -22,7 +21,7 @@ import { parseAgentWelcomeMetadata, readAgentWelcomeTemplate, renderPostBootstra
22
21
  import { RUNTIME_ENVIRONMENT_NAMES } from '../config/runtime-env.js';
23
22
  export { agentCreateInteractive, agentCreateNonInteractive, parseAgentOwnerArgs, resolveAgentCreatePlan, validateAgentCreatePlan, validateAgentOwnerOptions, } from './agent-create.js';
24
23
  /** Route agent lifecycle commands from a managed task through daemon IPC. */
25
- export async function runManagedAgentOperation(argv) {
24
+ export async function runManagedAgentOperation(argv, timeoutMs = 60_000) {
26
25
  const sessionId = process.env.EVOLCORE_SESSION_ID;
27
26
  if (!sessionId)
28
27
  return { ok: false, code: 'MANAGED_ONLY', error: 'agent operation is not running in a managed task' };
@@ -36,7 +35,7 @@ export async function runManagedAgentOperation(argv) {
36
35
  argv: ['agent', ...argv],
37
36
  sessionId,
38
37
  delegationToken,
39
- }, 60_000);
38
+ }, timeoutMs);
40
39
  if (!response)
41
40
  return { ok: false, code: 'DAEMON_UNAVAILABLE', error: 'Unable to connect to the evolcore daemon' };
42
41
  if (!response.ok)
@@ -398,37 +397,12 @@ async function agentSetEnabled(aid, enabled, options = {}) {
398
397
  return { ok: true, aid, enabled, reloaded };
399
398
  }
400
399
  async function runManagedAgentConfig(args, options) {
401
- const sessionId = process.env.EVOLCORE_SESSION_ID;
402
- if (!sessionId || options.trustedLocal)
400
+ if (!process.env.EVOLCORE_SESSION_ID || options.trustedLocal)
403
401
  return { managed: false };
404
- const delegationToken = readDelegationCredential();
405
- if (!delegationToken) {
406
- return {
407
- managed: true,
408
- error: { ok: false, code: 'DELEGATION_REQUIRED', error: `${AGENT_DELEGATION_TOKEN_ENV} is not set for the active task` },
409
- };
410
- }
411
- try {
412
- const response = await ipcQuery(resolvePaths().socket, {
413
- type: 'config.op',
414
- argv: ['config', ...args],
415
- sessionId,
416
- delegationToken,
417
- }, 10_000);
418
- if (!response) {
419
- return { managed: true, error: { ok: false, code: 'DAEMON_UNAVAILABLE', error: 'Unable to connect to the evolcore daemon' } };
420
- }
421
- if (!response.ok || !response.result?.ok) {
422
- return {
423
- managed: true,
424
- error: { ok: false, code: response.code || 'CONFIG_AUTH_FAILED', error: response.error || 'config operation failed' },
425
- };
426
- }
427
- return { managed: true, response };
428
- }
429
- catch {
430
- return { managed: true, error: { ok: false, code: 'DAEMON_UNAVAILABLE', error: 'Unable to connect to the evolcore daemon' } };
431
- }
402
+ const result = await runManagedAgentOperation(options.argv ?? args, 10_000);
403
+ return result.ok
404
+ ? { managed: true, response: { ok: true, result } }
405
+ : { managed: true, error: result };
432
406
  }
433
407
  async function executeLocalAgentConfig(args) {
434
408
  initConfigManager();
@@ -448,7 +422,7 @@ async function executeLocalAgentConfig(args) {
448
422
  });
449
423
  }
450
424
  export async function agentGet(aid, key, options = {}) {
451
- const managed = await runManagedAgentConfig(['get', key, '--self', aid], options);
425
+ const managed = await runManagedAgentConfig(['get', aid, key], options);
452
426
  if (managed.managed) {
453
427
  if ('error' in managed)
454
428
  return managed.error;
@@ -472,7 +446,7 @@ export async function agentGet(aid, key, options = {}) {
472
446
  }
473
447
  }
474
448
  export async function agentSet(aid, key, rawValue, options = {}) {
475
- const managed = await runManagedAgentConfig(['set', key, rawValue, '--self', aid], options);
449
+ const managed = await runManagedAgentConfig(['set', aid, key, rawValue], options);
476
450
  if (managed.managed) {
477
451
  if ('error' in managed)
478
452
  return managed.error;
@@ -539,36 +513,7 @@ export async function agentPersona(aid, options = {}) {
539
513
  return { ok: false, code: 'INVALID_ARGS', error: '--text 与 --file 不能同时使用' };
540
514
  const managedSession = !!process.env.EVOLCORE_SESSION_ID && !options.trustedLocal;
541
515
  let text = options.text;
542
- if (options.file !== undefined) {
543
- if (managedSession) {
544
- const resolved = path.resolve(options.file);
545
- const projectRoot = path.resolve(process.cwd());
546
- const tempRoot = process.env.TMPDIR?.trim() && path.isAbsolute(process.env.TMPDIR)
547
- ? path.resolve(process.env.TMPDIR)
548
- : undefined;
549
- const allowed = isSameOrDescendant(resolved, projectRoot)
550
- || (!!tempRoot && isSameOrDescendant(resolved, tempRoot));
551
- if (!allowed || isHClassPath(resolved) || isLClassPath(resolved)) {
552
- return { ok: false, code: 'INVALID_ARGS', error: '--file 必须位于当前项目或会话 $TMPDIR,且不能读取 EvolCore 受保护路径' };
553
- }
554
- try {
555
- const stat = fs.lstatSync(resolved);
556
- if (stat.isSymbolicLink() || !stat.isFile()) {
557
- return { ok: false, code: 'INVALID_ARGS', error: '--file 必须指向当前项目或会话 $TMPDIR 内的普通文件' };
558
- }
559
- const realPath = fs.realpathSync(resolved);
560
- const realProjectRoot = fs.realpathSync(projectRoot);
561
- const realTempRoot = tempRoot ? fs.realpathSync(tempRoot) : undefined;
562
- const realAllowed = isSameOrDescendant(realPath, realProjectRoot)
563
- || (!!realTempRoot && isSameOrDescendant(realPath, realTempRoot));
564
- if (!realAllowed || isHClassPath(realPath) || isLClassPath(realPath)) {
565
- return { ok: false, code: 'INVALID_ARGS', error: '--file 解析后越出允许目录或命中 EvolCore 受保护路径' };
566
- }
567
- }
568
- catch (e) {
569
- return { ok: false, code: 'READ_FAILED', error: `无法校验 --file: ${e?.message || e}` };
570
- }
571
- }
516
+ if (options.file !== undefined && !managedSession) {
572
517
  const source = readPersonaSource(options.file);
573
518
  if (!source.ok)
574
519
  return source;
@@ -582,10 +527,10 @@ export async function agentPersona(aid, options = {}) {
582
527
  const managedArgs = options.text !== undefined
583
528
  ? ['persona', '--text', text]
584
529
  : options.file !== undefined
585
- ? ['persona', '--text', text]
530
+ ? ['persona', '--file', options.file]
586
531
  : ['persona', 'show'];
587
532
  if (managedSession) {
588
- const managed = await runManagedAgentOperation(managedArgs);
533
+ const managed = await runManagedAgentOperation(options.argv ?? managedArgs);
589
534
  if (!managed.ok)
590
535
  return managed;
591
536
  return { ok: true, aid, content: managed.content ?? null, ...(managed.path ? { path: managed.path } : {}), ...(managed.saved ? { saved: true } : {}) };
@@ -625,7 +570,7 @@ export async function agentWelcome(aid, options = {}) {
625
570
  if (readTaskRuntimeContextFromEnv()?.selfAid !== aid) {
626
571
  return { ok: false, code: 'SELF_AGENT_REQUIRED', error: 'welcome only targets the current Agent' };
627
572
  }
628
- const managed = await runManagedAgentOperation(managedArgs);
573
+ const managed = await runManagedAgentOperation(options.argv ?? managedArgs);
629
574
  if (!managed.ok)
630
575
  return managed;
631
576
  return {
@@ -803,7 +748,7 @@ export async function agentRename(aid, name) {
803
748
  return { ok: true, aid, name };
804
749
  }
805
750
  // ==================== agentReady ====================
806
- export async function agentReady(aid) {
751
+ export async function agentReady(aid, commandArgs) {
807
752
  const managedSessionId = process.env.EVOLCORE_SESSION_ID;
808
753
  const taskSelfAid = managedSessionId ? readTaskRuntimeContextFromEnv()?.selfAid : undefined;
809
754
  if (taskSelfAid && taskSelfAid.replace(/^@/, '') !== aid.replace(/^@/, '')) {
@@ -822,6 +767,7 @@ export async function agentReady(aid) {
822
767
  const result = await ipcQuery(p.socket, {
823
768
  type: 'evolagent.bootstrapComplete',
824
769
  aid,
770
+ ...(managedSessionId ? { argv: ['agent', ...(commandArgs ?? ['ready', aid])] } : {}),
825
771
  ...(managedSessionId ? { sessionId: managedSessionId, delegationToken } : {}),
826
772
  }, 30_000);
827
773
  if (!result)
@@ -4,6 +4,7 @@ import { agentMdPath, resolvePaths, resolveRoot } from '../paths.js';
4
4
  import { isExplicitGroupId, normalizeGroupAid, sameGroupAid } from '../aun/group/index.js';
5
5
  import { getArgValue, isHelpFlag, wantsHelp } from './help.js';
6
6
  import { ipcQuery } from '../ipc.js';
7
+ import { isValidAid as validSendAid } from '../aun/aid/validation.js';
7
8
  import { AGENT_DELEGATION_TOKEN_ENV, readDelegationCredential } from '../core/auth/agent-delegation.js';
8
9
  import { isManagementRole } from '../config/builtin-roles.js';
9
10
  import { readBestTaskRuntimeContext } from './task-context.js';
@@ -1088,6 +1089,7 @@ Options:
1088
1089
  }
1089
1090
  // ==================== Storage ====================
1090
1091
  export async function cmdStorage(args) {
1092
+ args = canonicalizeEvolcoreCommandArgv(['ec', 'storage', ...args]).slice(2);
1091
1093
  const sub = args[0];
1092
1094
  const aunPath = resolveAunPath(args);
1093
1095
  const formatJson = getArgValue(args, '--format') === 'json';
@@ -1142,12 +1144,9 @@ Options:
1142
1144
  console.error('用法: ec storage upload <aid> <local-file> <remote-path> [--public]');
1143
1145
  process.exit(1);
1144
1146
  }
1145
- if (!fs.existsSync(localFile)) {
1146
- console.error(`❌ 文件不存在: ${localFile}`);
1147
- process.exit(1);
1148
- }
1149
1147
  const result = await ipcQuery(resolvePaths().socket, {
1150
1148
  type: 'storage.upload',
1149
+ argv: ['storage', ...args],
1151
1150
  aid,
1152
1151
  localFile,
1153
1152
  remotePath,
@@ -1172,12 +1171,25 @@ Options:
1172
1171
  return;
1173
1172
  }
1174
1173
  const authorization = await ipcQuery(resolvePaths().socket, {
1175
- type: 'storage.auth', operation, aid, sessionId: process.env.EVOLCORE_SESSION_ID, delegationToken,
1174
+ type: 'storage.auth', argv: ['storage', ...args], operation, aid, sessionId: process.env.EVOLCORE_SESSION_ID, delegationToken,
1176
1175
  }, 10_000);
1177
1176
  if (!authorization)
1178
1177
  failManagedGroup(formatJson, 'DAEMON_UNAVAILABLE', 'Unable to connect to the evolcore daemon');
1179
1178
  if (!authorization.ok)
1180
1179
  failManagedGroup(formatJson, authorization.code || 'STORAGE_AUTH_FAILED', authorization.error || 'Storage operation denied');
1180
+ if (sub === 'ls') {
1181
+ const objects = authorization.result?.objects ?? authorization.result ?? [];
1182
+ console.log(Array.isArray(objects) && !objects.length ? '(空)' : JSON.stringify(objects, null, 2));
1183
+ }
1184
+ else if (sub === 'quota')
1185
+ console.log(JSON.stringify(authorization.result, null, 2));
1186
+ else if (formatJson)
1187
+ console.log(JSON.stringify(authorization));
1188
+ else if (sub === 'download')
1189
+ console.log(`✓ 已下载: ${authorization.localPath} (${authorization.size} bytes)`);
1190
+ else
1191
+ console.log(`✓ 已删除: ${authorization.objectKey}`);
1192
+ return;
1181
1193
  }
1182
1194
  const { storageUpload, storageDownload, storageLs, storageRm, storageQuota } = await import('../aun/storage/index.js');
1183
1195
  if (sub === 'upload') {
@@ -1398,6 +1410,8 @@ Options:
1398
1410
  renderManagedMsgResult(sub, result, formatJson);
1399
1411
  return;
1400
1412
  }
1413
+ if (process.env.EVOLCORE_SESSION_ID && sub === 'send')
1414
+ return runManagedSend(['msg', ...args], formatJson);
1401
1415
  const { msgSend, msgPull, msgAck, msgRecall, msgOnline, msgHistory } = await import('../aun/msg/index.js');
1402
1416
  const commonOpts = { aunPath, slotId: appSlot };
1403
1417
  const requireConsumerSlot = (operation) => {
@@ -1935,6 +1949,21 @@ g- 是名称的一部分,会完整保留;默认命名格式为 g-10000 至 g
1935
1949
  renderManagedGroupResult(args, result, formatJson);
1936
1950
  return;
1937
1951
  }
1952
+ if (process.env.EVOLCORE_SESSION_ID && sub === 'send') {
1953
+ if (args.includes('--aun-path') || args.includes('--app')) {
1954
+ failManagedGroup(formatJson, 'NOT_ALLOWED', '--aun-path and --app are not allowed in a managed task');
1955
+ }
1956
+ let groupId;
1957
+ try {
1958
+ groupId = normalizeGroupAid(args[2] ?? '', from.slice(from.indexOf('.') + 1));
1959
+ }
1960
+ catch (error) {
1961
+ failManagedGroup(formatJson, 'INVALID_ARGUMENT', `Invalid group AID: ${error instanceof Error ? error.message : String(error)}`);
1962
+ }
1963
+ const runtime = await readBestTaskRuntimeContext();
1964
+ validateManagedGroupScope(from, groupId, runtime, formatJson);
1965
+ return runManagedSend(['group', ...args], formatJson);
1966
+ }
1938
1967
  const { groupSend, groupPull, groupAck, groupHistory, parseGroupHistoryArgs, groupCreate, groupInfo, groupList, groupUpdate, groupDissolve, groupSuspend, groupResume, groupJoin, groupLeave, groupInvite, groupInvites, groupAcceptInvite, groupRejectInvite, groupRevokeInvite, groupKick, groupMembers, groupOnline, groupSetRole, groupTransferOwner, groupBan, groupUnban, groupBanlist, groupBlock, groupUnblock, groupBlocklist, groupReportStatus, groupMemberStatuses, groupRules, groupUpdateRules, groupRulesFileGet, groupRulesFileSet, groupRulesFilePublish, } = await import('../aun/msg/index.js');
1939
1968
  const commonOpts = { aunPath, slotId: appSlot };
1940
1969
  if (['block', 'unblock', 'blocklist', 'report-status', 'member-statuses'].includes(sub)) {
@@ -2737,6 +2766,121 @@ g- 是名称的一部分,会完整保留;默认命名格式为 g-10000 至 g
2737
2766
  process.exit(1);
2738
2767
  }
2739
2768
  // ==================== Main ====================
2769
+ async function runManagedSend(argv, formatJson) {
2770
+ const delegationToken = readDelegationCredential();
2771
+ if (!delegationToken)
2772
+ failManagedGroup(formatJson, 'DELEGATION_REQUIRED', 'Managed send requires delegation');
2773
+ const runtime = await readBestTaskRuntimeContext();
2774
+ const result = await ipcQuery(resolvePaths().socket, {
2775
+ type: 'aun-msg-send', argv, delegationToken,
2776
+ originSessionId: process.env.EVOLCORE_SESSION_ID, originMessageId: runtime?.messageId,
2777
+ }, null);
2778
+ if (!result)
2779
+ failManagedGroup(formatJson, 'AUN_DAEMON_UNAVAILABLE', 'Unable to connect to the EvolCore daemon');
2780
+ if (!result.ok)
2781
+ failManagedGroup(formatJson, result.code ?? 'AUN_SEND_FAILED', result.error ?? 'Send failed');
2782
+ if (formatJson)
2783
+ console.log(JSON.stringify(result));
2784
+ else if (result.handoff_id)
2785
+ console.log(`✓ handoff ${result.handoff_id} ${result.status === 'delivered' ? '已投递' : '已排队'} ${result.message_id ?? ''}`.trimEnd());
2786
+ else
2787
+ console.log(`✓ 已发送 message_id=${result.message_id ?? '-'} seq=${result.seq ?? result.message?.seq ?? '-'}`);
2788
+ }
2789
+ export function resolveManagedSendArgv(argv) {
2790
+ if (!['msg', 'group'].includes(argv[0]) || argv[1] !== 'send' || !validSendAid(argv[2]))
2791
+ throw new Error('Invalid managed send command');
2792
+ const scope = argv[0];
2793
+ const aid = argv[2];
2794
+ const to = scope === 'group' ? normalizeGroupAid(argv[3], aid.slice(aid.indexOf('.') + 1)) : argv[3];
2795
+ if (!to || !validSendAid(to))
2796
+ throw new Error('Invalid send target');
2797
+ const values = new Map();
2798
+ const booleans = new Set();
2799
+ const text = [];
2800
+ const mentions = [];
2801
+ const valueFlags = new Set(['--format', '--file', '--payload', '--text-from-file', '--link', '--title', '--description', '--as', '--content-type', '--text', '--transcript', '--thread', '--return', '--handoff-context', '--mention']);
2802
+ const booleanFlags = new Set(['--encrypt', '--no-encrypt', '--mention-all']);
2803
+ for (let index = 4; index < argv.length; index++) {
2804
+ const flag = argv[index];
2805
+ if (flag === '--') {
2806
+ text.push(...argv.slice(index + 1));
2807
+ break;
2808
+ }
2809
+ if (flag === '--app' || flag === '--aun-path')
2810
+ throw new Error(`${flag} is not allowed for managed sends`);
2811
+ if (booleanFlags.has(flag))
2812
+ booleans.add(flag);
2813
+ else if (valueFlags.has(flag)) {
2814
+ const value = argv[++index];
2815
+ if (value === undefined || valueFlags.has(value) || booleanFlags.has(value))
2816
+ throw new Error(`${flag} requires a value`);
2817
+ if (flag === '--mention') {
2818
+ for (const mention of value.split(',').map(item => item.trim()).filter(Boolean)) {
2819
+ if (!validSendAid(mention))
2820
+ throw new Error('Invalid mention AID');
2821
+ mentions.push({ aid: mention });
2822
+ }
2823
+ }
2824
+ else {
2825
+ if (values.has(flag))
2826
+ throw new Error(`Duplicate send option: ${flag}`);
2827
+ values.set(flag, value);
2828
+ }
2829
+ }
2830
+ else
2831
+ text.push(flag);
2832
+ }
2833
+ if (booleans.has('--encrypt') && booleans.has('--no-encrypt'))
2834
+ throw new Error('Encryption options are mutually exclusive');
2835
+ if (values.has('--format') && values.get('--format') !== 'json')
2836
+ throw new Error('--format only supports json');
2837
+ const returnPolicy = values.get('--return');
2838
+ if (returnPolicy !== undefined && !['none', 'required'].includes(returnPolicy))
2839
+ throw new Error('Invalid return policy');
2840
+ const handoffContext = values.get('--handoff-context');
2841
+ if (handoffContext !== undefined && (scope !== 'msg' || !handoffContext.trim() || handoffContext.length > 65536))
2842
+ throw new Error('Invalid private handoff context');
2843
+ if (scope === 'msg' && (mentions.length || booleans.has('--mention-all')))
2844
+ throw new Error('Mentions require a group send');
2845
+ if (scope === 'group' && (values.has('--link') || values.has('--text-from-file')))
2846
+ throw new Error('Unsupported group send body');
2847
+ const modes = ['--file', '--payload', '--text-from-file', '--link'].filter(flag => values.has(flag));
2848
+ if (modes.length > 1 || (modes.length && text.length))
2849
+ throw new Error('Message body modes are mutually exclusive');
2850
+ let payload;
2851
+ let file;
2852
+ if (values.has('--file')) {
2853
+ const as = values.get('--as');
2854
+ if (as && !['image', 'video', 'voice', 'file'].includes(as))
2855
+ throw new Error('Invalid attachment type');
2856
+ if (values.has('--transcript') && as !== 'voice')
2857
+ throw new Error('--transcript requires --as voice');
2858
+ file = { filePath: values.get('--file'), as, contentType: values.get('--content-type'), text: values.get('--text'), transcript: values.get('--transcript') };
2859
+ }
2860
+ else if (values.has('--payload')) {
2861
+ payload = JSON.parse(values.get('--payload'));
2862
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload))
2863
+ throw new Error('--payload must be a JSON object');
2864
+ }
2865
+ else if (values.has('--link')) {
2866
+ assertEcSingleLine(values.get('--title'), '--title');
2867
+ payload = { type: 'link', url: values.get('--link'), ...(values.get('--title') ? { title: values.get('--title') } : {}),
2868
+ ...(values.get('--description') ? { description: values.get('--description') } : {}) };
2869
+ }
2870
+ else if (!values.has('--text-from-file')) {
2871
+ if (!text.join(' '))
2872
+ throw new Error('Message content is required');
2873
+ payload = { type: 'text', text: text.join(' ') };
2874
+ }
2875
+ if (!file && ['--as', '--content-type', '--text', '--transcript'].some(flag => values.has(flag)))
2876
+ throw new Error('Attachment options require --file');
2877
+ if (booleans.has('--mention-all'))
2878
+ mentions.push({ scope: 'all' });
2879
+ return { aid, to, scope, payload, file, textFile: values.get('--text-from-file'),
2880
+ encrypt: booleans.has('--no-encrypt') ? false : booleans.has('--encrypt') ? true : undefined,
2881
+ thread: values.get('--thread'), returnPolicy: returnPolicy, handoffContext,
2882
+ mentions: mentions.length ? mentions : undefined };
2883
+ }
2740
2884
  /**
2741
2885
  * 收集位置参数(从 startIdx 开始)。
2742
2886
  *
@@ -41,11 +41,10 @@ export async function cmdCtl(args) {
41
41
  console.error('错误: EVOLCORE_SESSION_ID 未设置(仅在 evolcore 托管环境中可用)');
42
42
  process.exit(1);
43
43
  }
44
- const cmd = '/' + args.join(' ');
45
44
  const socketPath = resolvePaths().socket;
46
45
  const result = await ipcQuery(socketPath, {
47
46
  type: 'ctl',
48
- cmd,
47
+ argv: ['ctl', ...args],
49
48
  sessionId,
50
49
  delegationToken: readDelegationCredential(),
51
50
  }, 10_000);
@@ -154,7 +154,7 @@ async function tryDelegatedDaemonRestart(socketPath) {
154
154
  return false;
155
155
  const response = await ipcQuery(socketPath, {
156
156
  type: 'ctl',
157
- cmd: '/restart',
157
+ argv: ['ctl', 'restart'],
158
158
  sessionId,
159
159
  delegationToken,
160
160
  }, 15_000);
package/dist/cli/diag.js CHANGED
@@ -8,11 +8,11 @@ export async function cmdDiag(args) {
8
8
  console.log('用法:ec diag <问题描述> [--owner <daemonOwner-aid>] [--format json]\n ec diag stop [--format json]\n未指定 --owner 时联系本机 daemon.json.owners[0];有效诊断期间再次执行会追加到原 case。');
9
9
  return;
10
10
  }
11
+ const argv = ['diag', ...args];
11
12
  const json = args.at(-2) === '--format' && args.at(-1) === 'json';
12
- const argv = json ? args.slice(0, -2) : args;
13
13
  let result;
14
14
  try {
15
- parseDiagArgs(argv);
15
+ parseDiagArgs(args);
16
16
  result = await ipcQuery(resolvePaths().socket, {
17
17
  type: 'diag', argv,
18
18
  ...(process.env.EVOLCORE_SESSION_ID ? {
@@ -6,6 +6,7 @@ import { HANDOFF_ID_RE } from '../core/handoff/store.js';
6
6
  import { isHelpFlag, wantsHelp } from './help.js';
7
7
  import { readDelegationCredential } from '../core/auth/agent-delegation.js';
8
8
  import { canonicalizeEvolcoreCommandArgv } from '../core/permission/ec-command-parser.js';
9
+ import { MAIN_CLI_BIN } from '../product.js';
9
10
  function parseLimit(value) {
10
11
  const limit = Number(value);
11
12
  if (!value || !Number.isInteger(limit) || limit < 1 || limit > HANDOFF_QUERY_MAX_LIMIT) {
@@ -70,8 +71,9 @@ export function parseTrace(args) {
70
71
  }
71
72
  return parsed;
72
73
  }
73
- export function parseReturn(args) {
74
- args = canonicalizeEvolcoreCommandArgv(['ec', 'handoff', 'return', ...args]).slice(3);
74
+ export function parseReturn(args, options = {}) {
75
+ if (!options.canonical)
76
+ args = canonicalizeEvolcoreCommandArgv([MAIN_CLI_BIN, 'handoff', 'return', ...args]).slice(3);
75
77
  const dash = args.indexOf('--');
76
78
  if (dash >= 0) {
77
79
  const beforeDash = args.slice(0, dash);
@@ -96,8 +98,10 @@ export function parseReturn(args) {
96
98
  if (filePath && contentArgs.length > 0)
97
99
  throw new Error('HANDOFF_CONTENT_FILE_CONFLICT');
98
100
  if (filePath) {
101
+ if (options.deferFileRead)
102
+ return { handoffId, content: '' };
99
103
  try {
100
- return { handoffId, content: fs.readFileSync(filePath, 'utf8') };
104
+ return { handoffId, content: options.readFile ? options.readFile(filePath) : fs.readFileSync(filePath, 'utf8') };
101
105
  }
102
106
  catch {
103
107
  throw new Error('HANDOFF_CONTENT_FILE_READ_FAILED');
@@ -153,6 +157,7 @@ function formatTimestamp(value) {
153
157
  return new Date(value).toISOString();
154
158
  }
155
159
  export async function cmdHandoff(args) {
160
+ args = canonicalizeEvolcoreCommandArgv([MAIN_CLI_BIN, 'handoff', ...args]).slice(2);
156
161
  const sub = args[0];
157
162
  if (!sub || isHelpFlag(sub) || wantsHelp(args)) {
158
163
  console.log(`用法: ec handoff <command> [args]
@@ -170,7 +175,7 @@ Commands:
170
175
  if (sub === 'return') {
171
176
  let parsed;
172
177
  try {
173
- parsed = parseReturn(args.slice(1));
178
+ parsed = parseReturn(args.slice(1), { canonical: true, deferFileRead: !!process.env.EVOLCORE_SESSION_ID });
174
179
  }
175
180
  catch (error) {
176
181
  const message = error instanceof Error ? error.message : String(error);
@@ -185,6 +190,7 @@ Commands:
185
190
  }
186
191
  const result = await ipcQuery(resolvePaths().socket, {
187
192
  type: 'handoff-return',
193
+ argv: ['handoff', ...args],
188
194
  sessionId: process.env.EVOLCORE_SESSION_ID,
189
195
  handoffId: parsed.handoffId,
190
196
  content: parsed.content,
@@ -201,7 +207,7 @@ Commands:
201
207
  if (sub === 'recover-return') {
202
208
  let parsed;
203
209
  try {
204
- parsed = parseReturn(args.slice(1));
210
+ parsed = parseReturn(args.slice(1), { canonical: true, deferFileRead: !!process.env.EVOLCORE_SESSION_ID });
205
211
  }
206
212
  catch (error) {
207
213
  console.error(`✗ ${error instanceof Error ? error.message : String(error)}`);
@@ -215,6 +221,7 @@ Commands:
215
221
  }
216
222
  const result = await ipcQuery(resolvePaths().socket, {
217
223
  type: 'handoff-recover-return',
224
+ argv: ['handoff', ...args],
218
225
  sessionId: process.env.EVOLCORE_SESSION_ID,
219
226
  handoffId: parsed.handoffId,
220
227
  content: parsed.content,
@@ -237,6 +244,7 @@ Commands:
237
244
  }
238
245
  const result = await ipcQuery(resolvePaths().socket, {
239
246
  type: 'handoff-status', sessionId: process.env.EVOLCORE_SESSION_ID, handoffId,
247
+ argv: ['handoff', ...args],
240
248
  delegationToken: readDelegationCredential(),
241
249
  }, 5000);
242
250
  if (!result) {
@@ -264,6 +272,7 @@ Commands:
264
272
  }
265
273
  const result = await ipcQuery(resolvePaths().socket, {
266
274
  type: 'handoff-list',
275
+ argv: ['handoff', ...args],
267
276
  sessionId: process.env.EVOLCORE_SESSION_ID,
268
277
  agent: parsed.agent,
269
278
  state: parsed.state,
@@ -308,6 +317,7 @@ Commands:
308
317
  }
309
318
  const result = await ipcQuery(resolvePaths().socket, {
310
319
  type: 'handoff-trace',
320
+ argv: ['handoff', ...args],
311
321
  sessionId: process.env.EVOLCORE_SESSION_ID,
312
322
  agent: parsed.agent,
313
323
  handoffId: parsed.handoffId,