evolcore 0.0.31 → 0.0.33

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 (179) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/bin/install-codex-managed-hooks.mjs +124 -37
  3. package/dist/agents/claude-runner.js +29 -2
  4. package/dist/agents/codex/app-server-client.js +3 -0
  5. package/dist/agents/codex/diagnostics.js +76 -44
  6. package/dist/agents/codex-runner.js +51 -24
  7. package/dist/agents/ecagent-runner.js +9 -13
  8. package/dist/agents/gemini-runner.js +4 -0
  9. package/dist/aun/aid/store.js +0 -81
  10. package/dist/aun/group/fs-download.js +1 -10
  11. package/dist/aun/group/operations.js +0 -1
  12. package/dist/aun/msg/history.js +1 -0
  13. package/dist/aun/msg/managed-operation.js +130 -32
  14. package/dist/aun/msg/p2p.js +0 -1
  15. package/dist/aun/outbox.js +50 -1
  16. package/dist/aun/service-proxy.js +12 -18
  17. package/dist/aun/storage/index.js +29 -0
  18. package/dist/channels/aun.js +38 -137
  19. package/dist/channels/daemon.js +257 -2
  20. package/dist/cli/agent-command.js +64 -31
  21. package/dist/cli/agent-create.js +43 -24
  22. package/dist/cli/agent.js +16 -70
  23. package/dist/cli/aun-commands.js +512 -32
  24. package/dist/cli/contact.js +8 -2
  25. package/dist/cli/ctl-command.js +1 -2
  26. package/dist/cli/daemon-commands.js +82 -13
  27. package/dist/cli/debug-mode.js +154 -0
  28. package/dist/cli/diag.js +2 -2
  29. package/dist/cli/handoff-command.js +18 -5
  30. package/dist/cli/index.js +81 -64
  31. package/dist/cli/init.js +2 -2
  32. package/dist/cli/managed-command-guard.js +5 -0
  33. package/dist/cli/memory-command.js +93 -12
  34. package/dist/cli/model.js +14 -138
  35. package/dist/cli/queue-command.js +51 -0
  36. package/dist/cli/response.js +5 -54
  37. package/dist/cli/session-command.js +194 -0
  38. package/dist/cli/skill-command.js +85 -3
  39. package/dist/cli/stats.js +10 -4
  40. package/dist/cli/task-command.js +1 -0
  41. package/dist/cli/task-context.js +16 -0
  42. package/dist/cli/thread-command.js +620 -0
  43. package/dist/cli/trigger-command.js +76 -50
  44. package/dist/config/builtin-roles.js +9 -7
  45. package/dist/config/config-field-policy.js +2 -3
  46. package/dist/config/config-manager.js +7 -2
  47. package/dist/config/resolved-config-op.js +3 -0
  48. package/dist/config-store.js +15 -0
  49. package/dist/core/agent-ipc-registration.js +65 -22
  50. package/dist/core/auth/agent-delegation.js +42 -2
  51. package/dist/core/auth/debug-context.js +139 -0
  52. package/dist/core/auth/delegation-authorization.js +1 -0
  53. package/dist/core/auth/operation-authorizer.js +24 -1
  54. package/dist/core/auth/operation-catalog.js +44 -3
  55. package/dist/core/background-session/session-cleanup.js +212 -0
  56. package/dist/core/background-session/types.js +10 -0
  57. package/dist/core/bootstrap-messages.js +1 -1
  58. package/dist/core/bootstrap-service.js +6 -3
  59. package/dist/core/command/command-handler.js +224 -45
  60. package/dist/core/command/diag-service.js +5 -1
  61. package/dist/core/command/memory-slash.js +6 -2
  62. package/dist/core/command/menu-catalog.js +48 -1
  63. package/dist/core/command/menu-handler.js +92 -16
  64. package/dist/core/command/menu-protocol.js +24 -1
  65. package/dist/core/command/skill-slash.js +322 -0
  66. package/dist/core/command/slash-gate.js +6 -4
  67. package/dist/core/command/slash-handler.js +193 -13
  68. package/dist/core/command/topic-slash.js +528 -0
  69. package/dist/core/evolagent-registry.js +20 -5
  70. package/dist/core/handoff/ipc-registration.js +38 -0
  71. package/dist/core/handoff/runtime.js +5 -11
  72. package/dist/core/handoff/store.js +5 -0
  73. package/dist/core/ipc-registration.js +210 -9
  74. package/dist/core/memory/consolidation-context.js +49 -16
  75. package/dist/core/memory/consolidation-ops-records.js +35 -0
  76. package/dist/core/memory/consolidation-queue-format.js +21 -0
  77. package/dist/core/memory/consolidation.js +174 -180
  78. package/dist/core/message/aun-ipc-sender.js +207 -8
  79. package/dist/core/message/im-renderer.js +149 -6
  80. package/dist/core/message/message-bridge.js +13 -3
  81. package/dist/core/message/message-log.js +1 -0
  82. package/dist/core/message/message-queue.js +131 -3
  83. package/dist/core/message/peer-mode.js +6 -1
  84. package/dist/core/message/response-engine.js +379 -69
  85. package/dist/core/message/subtask-tool-projection.js +140 -0
  86. package/dist/core/model/catalog-service.js +17 -12
  87. package/dist/core/permission/ec-command-parser.js +126 -7
  88. package/dist/core/permission/sandbox-runtime.js +14 -1
  89. package/dist/core/session/session-idle-monitor.js +249 -22
  90. package/dist/core/session/session-manager.js +54 -9
  91. package/dist/core/skill/command-parser.js +13 -0
  92. package/dist/core/skill/index-generator.js +4 -1
  93. package/dist/core/skill/startup.js +41 -0
  94. package/dist/core/skill/switch-resolver.js +55 -29
  95. package/dist/core/task/task-recovery-service.js +2 -0
  96. package/dist/core/task/task-resume-operation.js +11 -0
  97. package/dist/core/thread/command-parser.js +125 -0
  98. package/dist/core/thread/index.js +1 -0
  99. package/dist/core/thread/ipc-operation.js +95 -0
  100. package/dist/core/thread/thread-context-storage.js +131 -0
  101. package/dist/core/thread/thread-operation.js +258 -0
  102. package/dist/core/thread/thread-route.js +33 -0
  103. package/dist/core/thread/thread-service.js +777 -0
  104. package/dist/core/thread/types.js +1 -0
  105. package/dist/eck/message-renderer.js +35 -0
  106. package/dist/index.js +10 -7
  107. package/dist/ipc.js +234 -17
  108. package/dist/paths.js +23 -0
  109. package/dist/response-system/engines/v1/proactive-flow.js +169 -80
  110. package/dist/trigger/authorization.js +59 -29
  111. package/dist/trigger/feedback.js +7 -1
  112. package/dist/trigger/ipc-service.js +49 -35
  113. package/dist/trigger/parser.js +63 -1
  114. package/dist/trigger/script-sandbox.js +58 -77
  115. package/dist/trigger/validation.js +14 -0
  116. package/dist/utils/error-utils.js +22 -1
  117. package/kits/docs/AUTHORING.md +1 -1
  118. package/kits/docs/channels/aun.md +8 -0
  119. package/kits/docs/context-assembly.md +1 -0
  120. package/kits/docs/evolcore/INDEX.md +3 -2
  121. package/kits/docs/evolcore/agent-create.md +39 -28
  122. package/kits/docs/evolcore/agent.md +19 -4
  123. package/kits/docs/evolcore/ec-capabilities.md +4 -0
  124. package/kits/docs/evolcore/group-collaboration.md +3 -5
  125. package/kits/docs/evolcore/group.md +3 -4
  126. package/kits/docs/evolcore/memory-consolidation.md +1 -1
  127. package/kits/docs/evolcore/menu-exec-schema-commands.md +2 -2
  128. package/kits/docs/evolcore/menu-protocol.md +7 -0
  129. package/kits/docs/evolcore/msg.md +5 -8
  130. package/kits/docs/evolcore/task.md +2 -0
  131. package/kits/docs/evolcore/thread-chain.md +267 -0
  132. package/kits/docs/evolcore/trigger.md +2 -2
  133. package/kits/docs/prompt-loading-architecture.md +1 -0
  134. package/kits/eck_manifest.json +61 -26
  135. package/kits/eck_message_manifest.json +169 -10
  136. package/kits/rules/01-overview.md +0 -9
  137. package/kits/rules/05-venue.md +1 -1
  138. package/kits/rules/06-channel.md +7 -1
  139. package/kits/rules/07-memory-and-thread.md +202 -0
  140. package/kits/schemas/relation-config.schema.9.json +18 -3
  141. package/kits/schemas/role-config.schema.1.json +6 -6
  142. package/kits/schemas/single-session.schema.1.json +6 -6
  143. package/kits/schemas/single-session.schema.2.json +6 -6
  144. package/kits/schemas/single-session.schema.3.json +6 -6
  145. package/kits/skills/.switches.json +12 -1
  146. package/kits/skills/ec-group/SKILL.md +234 -0
  147. package/kits/skills/ec-skill/SKILL.md +10 -1
  148. package/kits/skills/ec-skill/scripts/cli.mjs +15 -1
  149. package/kits/skills/scrapling-article-fetch/SKILL.md +128 -0
  150. package/kits/skills/scrapling-article-fetch/scripts/check_python_env.py +54 -0
  151. package/kits/skills/scrapling-article-fetch/scripts/render_markdown_reply.py +35 -0
  152. package/kits/skills/scrapling-article-fetch/scripts/run_pipeline.sh +16 -0
  153. package/kits/skills/scrapling-article-fetch/scripts/scrapling_fetch.py +344 -0
  154. package/kits/skills/thread-ops/SKILL.md +492 -0
  155. package/kits/skills/wechat-article-media-download/SKILL.md +65 -0
  156. package/kits/skills/wechat-article-media-download/scripts/download.py +163 -0
  157. package/kits/skills/wechat-article-reader/SKILL.md +97 -0
  158. package/kits/skills/wechat-article-reader/scripts/fetch.mjs +256 -0
  159. package/kits/templates/bootstrap-welcome.md +7 -7
  160. package/kits/templates/memory-consolidation/forget.md +5 -0
  161. package/kits/templates/memory-consolidation/generalize.md +5 -0
  162. package/kits/templates/memory-consolidation/profile.md +5 -0
  163. package/kits/templates/memory-consolidation/promote.md +5 -0
  164. package/kits/templates/message-fragments/item.md +2 -1
  165. package/kits/templates/message-fragments/thread-context.md +6 -0
  166. package/kits/templates/message-fragments/trigger-session.md +2 -0
  167. package/kits/templates/message-fragments/trigger-thread-check.md +2 -0
  168. package/kits/templates/roles/member.json +2 -0
  169. package/kits/templates/roles/visitor.json +2 -0
  170. package/kits/templates/system-fragments/bootstrap.md +66 -72
  171. package/kits/templates/system-fragments/channel.md +3 -6
  172. package/kits/templates/system-fragments/commands.md +2 -1
  173. package/kits/templates/system-fragments/managed-runtime.md +15 -0
  174. package/kits/templates/system-fragments/reply.md +3 -2
  175. package/kits/templates/system-fragments/session.md +5 -14
  176. package/package.json +3 -1
  177. package/kits/docs/evolcore/group-fs.md +0 -17
  178. package/kits/docs/venues/aun-group.md +0 -45
  179. package/kits/docs/venues/group.md +0 -43
@@ -7,10 +7,11 @@ import { readDelegationCredential } from '../core/auth/agent-delegation.js';
7
7
  import { readTaskRuntimeContextFromEnv } from './task-context.js';
8
8
  import { assertEcSingleLine } from './cli-argv.js';
9
9
  import { canonicalizeEvolcoreCommandArgv } from '../core/permission/ec-command-parser.js';
10
+ import { MAIN_CLI_BIN } from '../product.js';
10
11
  import { readTriggerLocalFile, resolveTriggerLocalPath } from '../trigger/local-files.js';
11
12
  import { normalizeTriggerDefinition, resolveScriptPath, safeRelativePath, } from '../trigger/validation.js';
12
13
  export async function cmdTrigger(args) {
13
- args = canonicalizeEvolcoreCommandArgv(['ec', 'trigger', ...args]).slice(2);
14
+ args = canonicalizeEvolcoreCommandArgv([MAIN_CLI_BIN, 'trigger', ...args]).slice(2);
14
15
  const sub = args[0];
15
16
  if (sub === 'create' || sub === 'set' || sub === 'update') {
16
17
  const nameIndex = args.indexOf('--name');
@@ -41,29 +42,27 @@ export async function cmdTrigger(args) {
41
42
  return;
42
43
  }
43
44
  try {
44
- let fileScope;
45
- if (process.env.EVOLCORE_SESSION_ID
46
- && (hasOption(rest, '--file') || hasOption(rest, '--prompt-file') || hasOption(rest, '--script-path') || hasOption(rest, '--script-file'))) {
47
- if (!['create', 'set', 'update'].includes(sub))
48
- throw new Error('local file inputs are only valid for Trigger create/update');
49
- const agentAid = resolveAgentAid(rest);
50
- const triggerId = sub === 'update'
51
- ? positional(rest, 0, ['--agent', '--format', '--if-revision', '--prompt-file', '--script-file'])
52
- : undefined;
53
- const authorization = await request({
54
- type: 'trigger.authorizeLocalFiles',
55
- agentAid,
56
- operation: sub === 'update' ? 'trigger.update' : 'trigger.create',
57
- ...(triggerId ? { triggerId } : {}),
58
- cwd: process.cwd(),
59
- });
60
- if (authorization.fullAccess !== true) {
61
- if (!authorization.fileScope || typeof authorization.fileScope.projectPath !== 'string'
62
- || typeof authorization.fileScope.cwd !== 'string' || authorization.fileScope.selfAid !== agentAid) {
63
- throw new Error('daemon did not provide a valid Trigger file scope');
64
- }
65
- fileScope = authorization.fileScope;
45
+ if (process.env.EVOLCORE_SESSION_ID) {
46
+ const response = await request({ type: 'trigger.op', argv: ['trigger', ...args] }, sub === 'run' ? 120_000 : 30_000);
47
+ if (json)
48
+ return printJson(response);
49
+ if (sub === 'list')
50
+ return listTriggers(rest, false, response);
51
+ if (sub === 'show')
52
+ return showTrigger(rest, false, response);
53
+ if (sub === 'history')
54
+ return showHistory(rest, false, response);
55
+ if (sub === 'run') {
56
+ console.log(`${response.result.status}${response.result.reason ? ` (${response.result.reason})` : ''}: ${response.result.runId}`);
57
+ if (response.result.error)
58
+ console.log(response.result.error);
59
+ }
60
+ else {
61
+ const verb = { create: 'created', set: 'created', update: 'updated', enable: 'enabled',
62
+ disable: 'disabled', cancel: 'disabled', delete: 'deleted', remove: 'deleted', rm: 'deleted' }[sub];
63
+ console.log(`✓ ${verb} ${response.trigger.id}${['create', 'set', 'update'].includes(sub) ? ` (${response.trigger.name})` : ''}`);
66
64
  }
65
+ return;
67
66
  }
68
67
  switch (sub) {
69
68
  case 'list':
@@ -77,10 +76,10 @@ export async function cmdTrigger(args) {
77
76
  return;
78
77
  case 'create':
79
78
  case 'set':
80
- await createTrigger(rest, json, fileScope);
79
+ await createTrigger(rest, json);
81
80
  return;
82
81
  case 'update':
83
- await updateTrigger(rest, json, fileScope);
82
+ await updateTrigger(rest, json);
84
83
  return;
85
84
  case 'enable':
86
85
  case 'disable':
@@ -113,7 +112,7 @@ function printHelp() {
113
112
  console.log(`用法: ec trigger <subcommand>
114
113
 
115
114
  命令:
116
- list [--agent <aid>] [--all] [--json]
115
+ list [--agent <aid>] [--all] [--thread-id <threadId>] [--json]
117
116
  show [--agent <aid>] <triggerId> [--json]
118
117
  history [--agent <aid>] [triggerId] [--limit <n>] [--json]
119
118
  create --exec <script|trigger-session|target-session> --file <trigger.json|trigger-dir> [--enable] [--json]
@@ -175,9 +174,10 @@ Update 参数:
175
174
  省略 --agent 时使用当前 agent AID;无会话上下文或跨身份操作时需要显式指定。
176
175
  任务会话中的跨身份操作仅允许目标 Agent 上具备 owner/admin 权限的调用者执行 list/show/history;创建、修改、启停、删除和运行仍只允许当前 Agent。`);
177
176
  }
178
- async function listTriggers(args, json) {
177
+ async function listTriggers(args, json, response) {
179
178
  const agentAid = resolveAgentAid(args);
180
- const res = await request({ type: 'trigger.list', agentAid, all: hasFlag(args, '--all') });
179
+ const threadId = flagValue(args, '--thread-id');
180
+ const res = response ?? await request({ type: 'trigger.list', agentAid, all: hasFlag(args, '--all'), ...(threadId ? { threadId } : {}) });
181
181
  if (json)
182
182
  return printJson(res);
183
183
  const triggers = res.triggers;
@@ -189,10 +189,10 @@ async function listTriggers(args, json) {
189
189
  console.log(`${t.enabled ? 'on ' : 'off'} ${t.id} ${t.name} ${sourceLabel(t)}`);
190
190
  }
191
191
  }
192
- async function showTrigger(args, json) {
192
+ async function showTrigger(args, json, response) {
193
193
  const agentAid = resolveAgentAid(args);
194
194
  const triggerId = positional(args, 0, ['--agent', '--format']);
195
- const res = await request({ type: 'trigger.show', agentAid, triggerId });
195
+ const res = response ?? await request({ type: 'trigger.show', agentAid, triggerId });
196
196
  if (json)
197
197
  return printJson(res);
198
198
  const t = res.definition;
@@ -239,7 +239,7 @@ async function showTrigger(args, json) {
239
239
  }
240
240
  }
241
241
  }
242
- async function showHistory(args, json) {
242
+ async function showHistory(args, json, response) {
243
243
  const agentAid = resolveAgentAid(args);
244
244
  const triggerId = optionalPositional(args, 0, ['--agent', '--limit', '--format']);
245
245
  const rawLimit = flagValue(args, '--limit');
@@ -247,7 +247,7 @@ async function showHistory(args, json) {
247
247
  if (!Number.isInteger(limit) || limit <= 0 || limit > 10_000) {
248
248
  throw new Error('--limit 必须是 1 到 10000 的整数');
249
249
  }
250
- const res = await request({ type: 'trigger.history', agentAid, triggerId, limit });
250
+ const res = response ?? await request({ type: 'trigger.history', agentAid, triggerId, limit });
251
251
  if (json)
252
252
  return printJson(res);
253
253
  const events = Array.isArray(res.events) ? res.events : [];
@@ -266,6 +266,12 @@ async function showHistory(args, json) {
266
266
  }
267
267
  }
268
268
  async function createTrigger(args, json, fileScope) {
269
+ const res = await request(await buildTriggerCreateRequest(args, fileScope), 30_000);
270
+ if (json)
271
+ return printJson(res);
272
+ console.log(`✓ created ${res.trigger.id} (${res.trigger.name})`);
273
+ }
274
+ export async function buildTriggerCreateRequest(args, fileScope, defaultAgentAid) {
269
275
  if (!hasOption(args, '--exec')) {
270
276
  throw new Error('创建 trigger 必须显式指定 --exec script|trigger-session|target-session');
271
277
  }
@@ -280,16 +286,12 @@ async function createTrigger(args, json, fileScope) {
280
286
  if (definition.execution.type !== executionType) {
281
287
  throw new Error(`--exec ${flagValue(args, '--exec')} 与 trigger.json 的 execution.type=${definition.execution.type} 不一致`);
282
288
  }
283
- const res = await request({
289
+ return {
284
290
  type: 'trigger.create',
285
291
  definition,
286
292
  files,
287
293
  enable: hasFlag(args, '--enable') ? true : undefined,
288
- }, 30_000);
289
- if (json)
290
- return printJson(res);
291
- console.log(`✓ created ${res.trigger.id} (${res.trigger.name})`);
292
- return;
294
+ };
293
295
  }
294
296
  // Flag mode: use parser
295
297
  const usesPromptFile = hasOption(args, '--prompt-file');
@@ -302,7 +304,7 @@ async function createTrigger(args, json, fileScope) {
302
304
  throw new Error(parseResult.error);
303
305
  }
304
306
  const parsed = parseResult.value;
305
- const agentAid = parsed.agentId ?? resolveAgentAid(args);
307
+ const agentAid = parsed.agentId ?? defaultAgentAid ?? resolveAgentAid(args);
306
308
  const eventSession = parsed.feedbackStrategy === 'event_session';
307
309
  if (parsed.feedbackStrategy !== 'target' && !eventSession) {
308
310
  throw new Error('CLI flag 模式仅支持 --feedback target;origin/silent 请使用 /trigger 或 --file 导入 Trigger V3 第二阶段 JSON');
@@ -332,6 +334,7 @@ async function createTrigger(args, json, fileScope) {
332
334
  enabled: hasFlag(args, '--enable'),
333
335
  createdAt: now,
334
336
  updatedAt: now,
337
+ ...(parsed.labels ? { labels: parsed.labels } : {}),
335
338
  source: sourceFromParsed(parsed),
336
339
  execution: {
337
340
  type: parsed.executionType,
@@ -364,15 +367,12 @@ async function createTrigger(args, json, fileScope) {
364
367
  relativePath: script.path.replace(/\\/g, '/'),
365
368
  contentBase64: readTriggerLocalFile(parsed.scriptPath, fileScope).toString('base64'),
366
369
  }] : [];
367
- const res = await request({
370
+ return {
368
371
  type: 'trigger.create',
369
372
  definition,
370
373
  files,
371
374
  enable: hasFlag(args, '--enable') ? true : undefined,
372
- }, 30_000);
373
- if (json)
374
- return printJson(res);
375
- console.log(`✓ created ${res.trigger.id} (${res.trigger.name})`);
375
+ };
376
376
  }
377
377
  function triggerCreateArgs(args) {
378
378
  const out = [];
@@ -453,7 +453,13 @@ function shellQuoteArg(value) {
453
453
  return /[\s"'\\]/.test(value) ? `"${value.replace(/(["\\$`])/g, '\\$1')}"` : value;
454
454
  }
455
455
  async function updateTrigger(args, json, fileScope) {
456
- const agentAid = resolveAgentAid(args);
456
+ const res = await request(await buildTriggerUpdateRequest(args, fileScope), 30_000);
457
+ if (json)
458
+ return printJson(res);
459
+ console.log(`✓ updated ${res.trigger.id} (${res.trigger.name})`);
460
+ }
461
+ export async function buildTriggerUpdateRequest(args, fileScope, defaultAgentAid) {
462
+ const agentAid = flagValue(args, '--agent') ?? defaultAgentAid ?? resolveAgentAid(args);
457
463
  const triggerId = positional(args, 0, ['--agent', '--format', '--if-revision', '--prompt-file', '--script-file']);
458
464
  const usesPromptFile = hasOption(args, '--prompt-file');
459
465
  const usesScriptFile = hasOption(args, '--script-file');
@@ -469,7 +475,7 @@ async function updateTrigger(args, json, fileScope) {
469
475
  const scriptFileBase64 = scriptFilePath
470
476
  ? readTriggerLocalFile(scriptFilePath, fileScope).toString('base64')
471
477
  : undefined;
472
- const res = await request({
478
+ return {
473
479
  type: 'trigger.update',
474
480
  agentAid,
475
481
  triggerId,
@@ -477,10 +483,30 @@ async function updateTrigger(args, json, fileScope) {
477
483
  ...(usesPromptFile ? { promptFile: true } : {}),
478
484
  ...(scriptFileBase64 !== undefined ? { scriptFileBase64 } : {}),
479
485
  ...(expectedRevision ? { expectedRevision } : {}),
480
- }, 30_000);
481
- if (json)
482
- return printJson(res);
483
- console.log(`✓ updated ${res.trigger.id} (${res.trigger.name})`);
486
+ };
487
+ }
488
+ export function resolveTriggerCommandArgv(argv, defaultAgentAid) {
489
+ if (argv[0] !== 'trigger')
490
+ throw new Error('Expected a trigger command');
491
+ const sub = argv[1];
492
+ const args = argv.slice(2);
493
+ const types = {
494
+ list: 'trigger.list', show: 'trigger.show', history: 'trigger.history', create: 'trigger.create',
495
+ set: 'trigger.create', update: 'trigger.update', enable: 'trigger.setEnabled', disable: 'trigger.setEnabled',
496
+ cancel: 'trigger.cancel', delete: 'trigger.delete', remove: 'trigger.delete', rm: 'trigger.delete', run: 'trigger.run',
497
+ };
498
+ if (!types[sub])
499
+ throw new Error(`Unknown trigger command: ${sub}`);
500
+ const agentAid = flagValue(args, '--agent') ?? defaultAgentAid;
501
+ const valueFlags = ['--agent', '--format', '--limit', '--if-revision', '--prompt-file', '--script-file'];
502
+ const triggerId = ['list', 'create', 'set'].includes(sub) ? undefined : optionalPositional(args, 0, valueFlags);
503
+ if (!['list', 'history', 'create', 'set'].includes(sub) && !triggerId)
504
+ throw new Error('missing triggerId');
505
+ const limit = Number(flagValue(args, '--limit') ?? 100);
506
+ if (!Number.isInteger(limit) || limit <= 0 || limit > 10_000)
507
+ throw new Error('Invalid history limit');
508
+ return { type: types[sub], agentAid, triggerId, limit, all: hasFlag(args, '--all'),
509
+ enabled: sub === 'enable', dryRun: hasFlag(args, '--dry-run') };
484
510
  }
485
511
  function triggerUpdateArgs(args) {
486
512
  const out = [];
@@ -42,6 +42,7 @@ export function getManagementCommandPermissions(role) {
42
42
  'role.policy.read': { allow: true, scopes: ['agent'] },
43
43
  'role.relation.read': { allow: true, scopes: ['agent'] },
44
44
  'agent.baseagent.update': { allow: true, scopes: ['agent'], constraints: { requireAgentOwner: true, targetCurrentAgentOnly: true } },
45
+ 'agent.rename': { allow: true, dangerous: true, scopes: ['agent'], constraints: { requireAgentOwner: true, targetCurrentAgentOnly: true } },
45
46
  'observable.current': { allow: true, scopes: ['agent'], constraints: { requireAgentOwner: true } },
46
47
  'observable.update': { allow: true, scopes: ['agent'], constraints: { requireAgentOwner: true, targetCurrentAgentOnly: true } },
47
48
  'file.root.agent': { allow: true, scopes: ['agent'], constraints: { requireAgentOwner: true, targetCurrentAgentOnly: true } },
@@ -64,17 +65,18 @@ export function getManagementCommandPermissions(role) {
64
65
  'ec.aid.agentmd.put': { allow: true, dangerous: true, scopes: ['agent'], constraints: { targetCurrentAgentOnly: true, requireAgentAdmin: true } },
65
66
  'ec.aid.agentmd.set': { allow: true, dangerous: true, scopes: ['agent'], constraints: { targetCurrentAgentOnly: true, requireAgentAdmin: true } },
66
67
  'ec.aid.avatar': { allow: true, dangerous: true, scopes: ['agent'], constraints: { targetCurrentAgentOnly: true, requireAgentOwner: true } },
67
- 'ec.fs.mkdir': { allow: true, permissionMode: 'auto', scopes: ['relation'], constraints: { groupOnly: true, currentRelationOnly: true, targetCurrentAgentOnly: true } },
68
- 'ec.fs.upload': { allow: true, permissionMode: 'auto', scopes: ['relation'], constraints: { groupOnly: true, currentRelationOnly: true, targetCurrentAgentOnly: true } },
69
- 'ec.fs.copy': { allow: true, permissionMode: 'auto', scopes: ['relation'], constraints: { groupOnly: true, currentRelationOnly: true, targetCurrentAgentOnly: true } },
70
- 'ec.fs.move': { allow: true, permissionMode: 'auto', scopes: ['relation'], constraints: { groupOnly: true, currentRelationOnly: true, targetCurrentAgentOnly: true } },
71
- 'ec.fs.remove': { allow: true, permissionMode: 'auto', scopes: ['relation'], constraints: { groupOnly: true, currentRelationOnly: true, targetCurrentAgentOnly: true } },
72
- 'ec.fs.getfacl': { allow: true, scopes: ['relation'], constraints: { groupOnly: true, currentRelationOnly: true, targetCurrentAgentOnly: true } },
73
- 'ec.fs.setfacl': { allow: true, permissionMode: 'auto', scopes: ['relation'], constraints: { groupOnly: true, currentRelationOnly: true, targetCurrentAgentOnly: true } },
68
+ 'ec.fs.mkdir': { allow: true, permissionMode: 'auto', scopes: ['relation', 'agent'] },
69
+ 'ec.fs.upload': { allow: true, permissionMode: 'auto', scopes: ['relation', 'agent'] },
70
+ 'ec.fs.copy': { allow: true, permissionMode: 'auto', scopes: ['relation', 'agent'] },
71
+ 'ec.fs.move': { allow: true, permissionMode: 'auto', scopes: ['relation', 'agent'] },
72
+ 'ec.fs.remove': { allow: true, permissionMode: 'auto', scopes: ['relation', 'agent'] },
73
+ 'ec.fs.getfacl': { allow: true, scopes: ['relation', 'agent'] },
74
+ 'ec.fs.setfacl': { allow: true, permissionMode: 'auto', scopes: ['relation', 'agent'] },
74
75
  '*': { allow: true },
75
76
  // Reload is valid from an Agent's own channel and the daemon control plane.
76
77
  // The control-channel gate still requires a daemon owner for cross-Agent reloads.
77
78
  'agent.reload': { allow: true, dangerous: true, scopes: ['agent', 'control'], constraints: { targetCurrentAgentOnly: true, requireAgentAdmin: true } },
79
+ 'skill.refresh': { allow: true, scopes: ['agent', 'control'], constraints: { targetCurrentAgentOnly: true, requireAgentAdmin: true } },
78
80
  'dangerous:*': { allow: true, dangerous: true, constraints: { requireDaemonOwner: true } },
79
81
  };
80
82
  }
@@ -3,7 +3,6 @@ const BEHAVIOR_TOP_FIELDS = new Set([
3
3
  'chatmode',
4
4
  'mentionMode',
5
5
  'autoMentionQuietWindowMs',
6
- 'sessionIdleSec',
7
6
  'groupSlashRequireMentions',
8
7
  'responseMode',
9
8
  'responseModeParams',
@@ -128,8 +127,6 @@ export function resolveConfigFieldRule(fieldPath, scope) {
128
127
  return scalar(field, 'mention-mode');
129
128
  if (field === 'autoMentionQuietWindowMs')
130
129
  return scalar(field, 'nonnegative-number');
131
- if (field === 'sessionIdleSec')
132
- return scalar(field, 'finite-number');
133
130
  if (field === 'groupSlashRequireMentions')
134
131
  return scalar(field, 'boolean');
135
132
  if (field === 'show_activities')
@@ -142,6 +139,8 @@ export function resolveConfigFieldRule(fieldPath, scope) {
142
139
  return scalar('configSnapshots', 'boolean');
143
140
  if (field === 'restartRecovery.enabled')
144
141
  return scalar('restartRecovery.enabled', 'boolean');
142
+ if (field === 'skills.watch')
143
+ return scalar('skills.watch', 'boolean');
145
144
  if (top === 'chatmode') {
146
145
  if (parts.length === 1)
147
146
  return readonlyObject('chatmode');
@@ -1464,6 +1464,9 @@ export function routeField(topField, scope) {
1464
1464
  */
1465
1465
  export function routeFieldPath(fieldPath, scope) {
1466
1466
  const topField = fieldPath.split('.')[0];
1467
+ if (topField === 'sessionLoop') {
1468
+ throw new ConfigError('SESSION_LOOP_MANAGED', 'sessionLoop is managed through the dedicated /loop or session Menu entry');
1469
+ }
1467
1470
  if (scope === 'process')
1468
1471
  return routeIn(DAEMON_SCHEMA_NAME, ConfigTarget.Process, topField);
1469
1472
  if (scope === 'defaults') {
@@ -1541,8 +1544,10 @@ export function listFields(scope) {
1541
1544
  const out = [];
1542
1545
  const add = (name, target) => {
1543
1546
  const s = loadSchema(name);
1544
- for (const f of s.fields.keys())
1545
- out.push(mkRoute(s, target, f));
1547
+ for (const f of s.fields.keys()) {
1548
+ if (f !== 'sessionLoop')
1549
+ out.push(mkRoute(s, target, f));
1550
+ }
1546
1551
  };
1547
1552
  if (scope === 'process') {
1548
1553
  add(DAEMON_SCHEMA_NAME, ConfigTarget.Process);
@@ -90,6 +90,9 @@ function resolveFieldCommand(argv, subcommand, options = {}) {
90
90
  const field = business.values[0].trim();
91
91
  if (!field)
92
92
  return { ok: false, code: 'MISSING_ARG', reason: `config ${subcommand} requires a field` };
93
+ if (field === 'sessionLoop' || field.startsWith('sessionLoop.')) {
94
+ return { ok: false, code: 'SESSION_LOOP_MANAGED', reason: 'sessionLoop is managed through the dedicated /loop or session Menu entry' };
95
+ }
93
96
  if (field === 'permissionMode') {
94
97
  return {
95
98
  ok: false,
@@ -60,6 +60,9 @@ export function loadDaemonConfig() {
60
60
  export function isRestartRecoveryEnabled(config = loadDaemonConfig()) {
61
61
  return config.restartRecovery?.enabled === true;
62
62
  }
63
+ export function isSkillWatchEnabled(config = loadDaemonConfig()) {
64
+ return config.skills?.watch === true;
65
+ }
63
66
  let eckSnapshotsConfigCache;
64
67
  /** Parse the process-level snapshot gate once for the current daemon lifecycle. */
65
68
  export function initializeEckSnapshotsConfig(config = loadDaemonConfig()) {
@@ -103,6 +106,18 @@ function validateDaemonConfig(value, validateAidDomain) {
103
106
  throw new Error('daemon.json.restartRecovery.enabled must be a boolean');
104
107
  }
105
108
  }
109
+ if (value.skills !== undefined) {
110
+ if (!value.skills || typeof value.skills !== 'object' || Array.isArray(value.skills)) {
111
+ throw new Error('daemon.json.skills must be an object');
112
+ }
113
+ const unknownSkillsFields = Object.keys(value.skills).filter(key => key !== 'watch');
114
+ if (unknownSkillsFields.length > 0) {
115
+ throw new Error('daemon.json.skills contains unsupported fields: ' + unknownSkillsFields.join(', '));
116
+ }
117
+ if (value.skills.watch !== undefined && typeof value.skills.watch !== 'boolean') {
118
+ throw new Error('daemon.json.skills.watch must be a boolean');
119
+ }
120
+ }
106
121
  const minEvolVersion = value.aun?.minEvolVersion;
107
122
  if (minEvolVersion !== undefined && !parseStableSemver(minEvolVersion)) {
108
123
  throw new Error('daemon.json.aun.minEvolVersion must use stable X.Y.Z format');
@@ -1,13 +1,14 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { loadDaemonConfig } from '../config-store.js';
4
- import { parseAgentWelcomeArgs, parseInitializationArgs, resolveInitializationPlan, } from '../cli/agent-create.js';
4
+ import { parseAgentWelcomeArgs, parseInitializationArgs, resolveInitializationPlan, readInitializationPersona, } from '../cli/agent-create.js';
5
5
  import { trustedExecutionIdentityInput } from './auth/agent-delegation.js';
6
6
  import { authorizeOperation, buildAuthSubject } from './auth/auth-gateway.js';
7
7
  import { authorizationDenialData } from './auth/authorization-denial.js';
8
8
  import { parsePeerKey } from './relation/peer-identity.js';
9
9
  import { isHClassPath, isLClassPath, isSameOrDescendant } from './permission/protected-paths.js';
10
- export function registerAgentOperationExecutor({ ipcServer, sessionManager, agentDelegationRegistry, eventBus, responseEngine, handoffRuntime, agentApplicationService, agentInitializationService, }) {
10
+ import { isValidAid } from '../aun/aid/validation.js';
11
+ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agentDelegationRegistry, eventBus, responseEngine, handoffRuntime, agentApplicationService, agentInitializationService, executeConfigOperation, }) {
11
12
  ipcServer.setAgentOperationExecutor(async (argv, sessionId, delegationToken, delegationCommandHash) => {
12
13
  const fail = (code, error, reasonCode) => ({
13
14
  ok: false,
@@ -17,9 +18,16 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
17
18
  });
18
19
  if (!Array.isArray(argv) || argv[0] !== 'agent')
19
20
  return fail('INVALID_REQUEST', 'agent.op requires an agent command');
20
- const sub = argv[1];
21
+ const shorthand = !!argv[1] && isValidAid(argv[1]);
22
+ const sub = shorthand ? 'show' : argv[1];
21
23
  if (!sub)
22
24
  return fail('INVALID_ARGS', 'missing agent subcommand');
25
+ if (sub === 'get' || sub === 'set') {
26
+ const result = await executeConfigOperation(argv, sessionId, delegationToken, delegationCommandHash);
27
+ return result.ok
28
+ ? { ok: true, result: result.result }
29
+ : { ok: false, code: result.code ?? 'CONFIG_OPERATION_FAILED', error: result.error ?? 'config operation failed' };
30
+ }
23
31
  const value = (flag) => {
24
32
  const index = argv.indexOf(flag);
25
33
  return index >= 0 && index + 1 < argv.length && !argv[index + 1].startsWith('--') ? argv[index + 1] : undefined;
@@ -31,6 +39,8 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
31
39
  if (!taskSession || taskSession.selfAID !== delegation.grant.selfAid) {
32
40
  return fail('INVALID_DELEGATION', 'delegation does not match the current session');
33
41
  }
42
+ if (shorthand)
43
+ argv = ['agent', 'show', ...argv.slice(1)];
34
44
  const taskAid = delegation.grant.selfAid;
35
45
  let welcomeArgs;
36
46
  if (sub === 'welcome') {
@@ -58,12 +68,13 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
58
68
  : sub === 'show' ? 'agent.show'
59
69
  : sub === 'persona' ? 'agent.persona'
60
70
  : sub === 'welcome' ? 'agent.welcome'
61
- : sub === 'initialize' || (sub === 'new' && argv.includes('--initialize')) ? 'agent.initialize'
62
- : sub === 'new' ? 'agent.create'
63
- : sub === 'enable' ? 'agent.enable'
64
- : sub === 'disable' ? 'agent.disable'
65
- : sub === 'reload' ? 'agent.reload'
66
- : sub === 'delete' ? 'agent.delete' : undefined;
71
+ : sub === 'rename' ? 'agent.rename'
72
+ : sub === 'initialize' || (sub === 'new' && argv.includes('--initialize')) ? 'agent.initialize'
73
+ : sub === 'new' ? 'agent.create'
74
+ : sub === 'enable' ? 'agent.enable'
75
+ : sub === 'disable' ? 'agent.disable'
76
+ : sub === 'reload' ? 'agent.reload'
77
+ : sub === 'delete' ? 'agent.delete' : undefined;
67
78
  if (!operation)
68
79
  return fail('INVALID_ARGS', `unsupported managed agent command: ${sub}`);
69
80
  if (sub === 'new' && !argv.includes('--non-interactive') && !argv.includes('--initialize')) {
@@ -95,7 +106,7 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
95
106
  subject,
96
107
  intent: {
97
108
  operation,
98
- scope: operation === 'agent.show' || operation === 'agent.persona' || operation === 'agent.welcome'
109
+ scope: operation === 'agent.show' || operation === 'agent.persona' || operation === 'agent.welcome' || operation === 'agent.rename'
99
110
  ? 'agent'
100
111
  : operation === 'agent.reload' && targetAid
101
112
  ? 'agent'
@@ -170,15 +181,23 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
170
181
  if (!hasFormatSuffix && rawPersonaArgs.includes('--format')) {
171
182
  return fail('INVALID_ARGS', 'persona 仅支持末尾的 --format json');
172
183
  }
173
- if (personaArgs.length === 2 && personaArgs[0] === '--file') {
174
- return fail('INVALID_ARGS', '托管 persona --file 必须由 CLI 读取后作为文本提交');
175
- }
176
184
  const wantsShow = personaArgs.length === 1 && personaArgs[0] === 'show';
177
- const text = personaArgs.length === 2 && personaArgs[0] === '--text'
185
+ let text = personaArgs.length === 2 && personaArgs[0] === '--text'
178
186
  ? personaArgs[1]
179
187
  : undefined;
180
- if (!wantsShow && !(personaArgs.length === 2 && personaArgs[0] === '--text')) {
181
- return fail('INVALID_ARGS', '用法: ec agent persona show | --text <text>');
188
+ const file = personaArgs.length === 2 && personaArgs[0] === '--file' ? personaArgs[1] : undefined;
189
+ if (file !== undefined) {
190
+ try {
191
+ text = readInitializationPersona(file, true, {
192
+ projectCwd: taskSession.projectPath, tempDir: delegation.grant.sessionRuntimeDir,
193
+ });
194
+ }
195
+ catch (error) {
196
+ return fail('INVALID_ARGS', error.message);
197
+ }
198
+ }
199
+ if (!wantsShow && !(personaArgs.length === 2 && (personaArgs[0] === '--text' || personaArgs[0] === '--file'))) {
200
+ return fail('INVALID_ARGS', '用法: ec agent persona show | --text <text> | --file <path>');
182
201
  }
183
202
  if (text === undefined && !wantsShow)
184
203
  return fail('INVALID_ARGS', 'persona --text 缺少参数');
@@ -211,12 +230,32 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
211
230
  ...(result.sha256 ? { sha256: result.sha256 } : {}),
212
231
  } };
213
232
  }
233
+ if (operation === 'agent.rename') {
234
+ const rawRenameArgs = argv.slice(2);
235
+ const hasFormatSuffix = rawRenameArgs.length >= 2
236
+ && rawRenameArgs.at(-2) === '--format'
237
+ && rawRenameArgs.at(-1) === 'json';
238
+ const renameArgs = hasFormatSuffix ? rawRenameArgs.slice(0, -2) : rawRenameArgs;
239
+ if (!hasFormatSuffix && rawRenameArgs.includes('--format')) {
240
+ return fail('INVALID_ARGS', 'rename 仅支持末尾的 --format json');
241
+ }
242
+ if (renameArgs.length !== 2 || !renameArgs[0] || !renameArgs[1]) {
243
+ return fail('INVALID_ARGS', '用法: ec agent rename <aid> <name>');
244
+ }
245
+ if (/[\x00-\x1f\x7f]/u.test(renameArgs[1])) {
246
+ return fail('INVALID_ARGS', 'name 不允许换行或控制字符');
247
+ }
248
+ const result = await execAgentAction('update', { aid: targetAid ?? taskAid, patch: { name: renameArgs[1] } }, delegation.grant.actorId, eventBus, agentApplicationService, subject);
249
+ return 'error' in result ? fail(result.code, result.error) : { ok: true, result: { ok: true, ...(result.data ?? {}) } };
250
+ }
214
251
  if (operation === 'agent.initialize') {
215
252
  try {
216
253
  const parsed = parseInitializationArgs(argv.slice(1));
217
254
  const actor = delegation.grant.actorId;
218
- if (parsed.resume)
219
- return { ok: true, result: agentInitializationService.resume(parsed.aid, actor) };
255
+ if (parsed.resume) {
256
+ agentInitializationService.resume(parsed.aid, actor);
257
+ return { ok: true, result: await agentInitializationService.wait(parsed.aid, parsed.wait) };
258
+ }
220
259
  const createPermission = authorizeOperation({
221
260
  source: 'agent-tool', subject,
222
261
  intent: { operation: 'agent.create', scope: 'control', source: 'agent-tool', args: { aid: parsed.aid } },
@@ -226,14 +265,16 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
226
265
  return fail(createPermission.code, createPermission.reason);
227
266
  if (!argv.includes('--owner') && !argv.includes('--owners'))
228
267
  return fail('INITIALIZE_OWNER_MISMATCH', 'Explicit Owner required');
229
- const plan = resolveInitializationPlan(argv.slice(1));
268
+ const plan = resolveInitializationPlan(argv.slice(1), {
269
+ projectCwd: taskSession.projectPath, tempDir: delegation.grant.sessionRuntimeDir,
270
+ });
230
271
  if (plan.avatarFile?.startsWith('attachment:')) {
231
272
  const { readRelationAttachment } = await import('../utils/media-cache.js');
232
273
  plan.avatarFile = readRelationAttachment(taskAid, delegation.grant.peerKey, plan.avatarFile.slice('attachment:'.length)).attachment.filePath;
233
274
  }
234
275
  else if (plan.avatarFile) {
235
276
  const session = await sessionManager.getSessionById(sessionId);
236
- const candidate = path.resolve(plan.avatarFile);
277
+ const candidate = path.resolve(taskSession.projectPath, plan.avatarFile);
237
278
  const roots = [session?.projectPath, delegation.grant.sessionRuntimeDir]
238
279
  .filter((root) => !!root && path.isAbsolute(root))
239
280
  .map(root => fs.realpathSync(root));
@@ -245,7 +286,8 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
245
286
  }
246
287
  if (!plan.owners.includes(actor))
247
288
  return fail('INITIALIZE_OWNER_MISMATCH', 'The authenticated requesting Owner must be retained');
248
- return { ok: true, result: agentInitializationService.start(plan, actor, parsed.dryRun) };
289
+ const result = agentInitializationService.start(plan, actor, parsed.dryRun);
290
+ return { ok: true, result: parsed.dryRun ? result : await agentInitializationService.wait(parsed.aid, parsed.wait) };
249
291
  }
250
292
  catch (error) {
251
293
  return fail(error.code ?? 'INVALID_ARGS', error.message);
@@ -283,7 +325,7 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
283
325
  avatarFile: value('--avatar-file'),
284
326
  avatarUrl: value('--avatar-url'),
285
327
  force: argv.includes('--force'),
286
- });
328
+ }, taskSession.projectPath);
287
329
  const validationError = validateAgentCreatePlan(plan);
288
330
  if (validationError)
289
331
  return fail('INVALID_ARGS', validationError);
@@ -319,6 +361,7 @@ export function registerAgentOperationExecutor({ ipcServer, sessionManager, agen
319
361
  channelType: delegation.grant.channelType,
320
362
  chatType: delegation.grant.chatType,
321
363
  peerKey: delegation.grant.peerKey,
364
+ approvalInteractionPolicy: delegation.grant.approvalInteractionPolicy,
322
365
  },
323
366
  causation: responseEngine.getTaskRuntimeContext(sessionId)?.causation,
324
367
  });