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
@@ -1,10 +1,8 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
1
  import { getEventCatalog } from '../core/event-catalog.js';
4
2
  import { authorizeOperation, canApprovePersistentFullAccessTrigger, isCrossAgentTriggerOperationAllowed, } from '../core/auth/auth-gateway.js';
5
3
  import { auditFullAccessEvent } from '../core/auth/authorization-audit.js';
6
4
  import { assertTriggerLocalFileAuthority, daemonPrivilegedTriggerReason, } from './authorization.js';
7
- import { decodeTriggerScriptFile, resolveTriggerLocalPath } from './local-files.js';
5
+ import { decodeTriggerScriptFile } from './local-files.js';
8
6
  import { applyTriggerPatch } from './patch.js';
9
7
  import { definitionRevision, normalizeTriggerDefinition, splitScriptCommand } from './validation.js';
10
8
  export function createTriggerBaseagentResolver({ daemonTriggerOwnerAid, getAgentName, getDaemonAgentName, agentMap, }) {
@@ -51,6 +49,9 @@ export function materializeTriggerCreateBaseagent(input, agentAid, dependencies)
51
49
  }
52
50
  export function createTriggerIpcExecutor(dependencies) {
53
51
  return async (cmd) => {
52
+ let approvedActor;
53
+ let approvedAgentAid;
54
+ let approvedOperation;
54
55
  const schedulerFor = (agentAid) => {
55
56
  const scheduler = dependencies.getScheduler(agentAid);
56
57
  if (!scheduler)
@@ -102,7 +103,10 @@ export function createTriggerIpcExecutor(dependencies) {
102
103
  return { ...definition, authorizedBy };
103
104
  };
104
105
  const authorizeTrigger = async (agentAid, operation, triggerId, contextOnly = false) => {
105
- const actor = await dependencies.authenticateActor(agentAid, cmd.actorSessionId, cmd.delegationToken, cmd.delegationCommandHash, cmd.controlToken, contextOnly);
106
+ if (approvedActor && (agentAid !== approvedAgentAid || operation !== approvedOperation)) {
107
+ throw new Error('Trigger operation does not match its approved argv');
108
+ }
109
+ const actor = approvedActor ?? await dependencies.authenticateActor(agentAid, cmd.actorSessionId, cmd.delegationToken, cmd.delegationCommandHash, cmd.controlToken, contextOnly);
106
110
  if (actor.permissionMode === 'readonly' && !actor.fullAccess
107
111
  && !['trigger.list', 'trigger.show', 'trigger.history', 'trigger.eventCatalog'].includes(operation)) {
108
112
  throw new Error('readonly tasks cannot modify or run Triggers');
@@ -160,38 +164,48 @@ export function createTriggerIpcExecutor(dependencies) {
160
164
  }
161
165
  return actor;
162
166
  };
163
- switch (cmd.type) {
164
- case 'trigger.authorizeLocalFiles': {
165
- if (cmd.operation !== 'trigger.create' && cmd.operation !== 'trigger.update') {
166
- throw new Error('local file authorization only supports Trigger create/update');
167
- }
168
- const agentAid = requireAgent(cmd.agentAid);
169
- if (cmd.operation === 'trigger.update' && (typeof cmd.triggerId !== 'string' || !cmd.triggerId)) {
170
- throw new Error('missing triggerId');
171
- }
172
- if (typeof cmd.actorSessionId !== 'string' || !cmd.actorSessionId) {
173
- throw new Error('local Trigger file authorization requires a managed task');
174
- }
175
- const actor = await authorizeTrigger(agentAid, cmd.operation, cmd.triggerId, true);
176
- assertTriggerLocalFileAuthority(actor);
177
- if (actor.fullAccess)
178
- return { ok: true, fullAccess: true };
179
- const session = await dependencies.getSessionById(cmd.actorSessionId);
180
- if (!session)
181
- throw new Error('Trigger actor session is unavailable');
182
- if (typeof cmd.cwd !== 'string' || !path.isAbsolute(cmd.cwd))
183
- throw new Error('missing absolute command cwd');
184
- const fileScope = {
185
- selfAid: agentAid,
186
- projectPath: fs.realpathSync.native(path.resolve(session.projectPath)),
187
- sessionRuntimeDir: actor.sessionRuntimeDir,
188
- cwd: cmd.cwd,
189
- };
190
- fileScope.cwd = resolveTriggerLocalPath(cmd.cwd, fileScope);
191
- if (!fs.statSync(fileScope.cwd).isDirectory())
192
- throw new Error('command cwd must be a directory');
193
- return { ok: true, fullAccess: false, fileScope };
167
+ if (cmd.type === 'trigger.op') {
168
+ if (!Array.isArray(cmd.argv) || cmd.argv.some((value) => typeof value !== 'string')
169
+ || typeof cmd.actorSessionId !== 'string' || !cmd.actorSessionId) {
170
+ throw new Error('Managed Trigger requires argv and actorSessionId');
171
+ }
172
+ const session = await dependencies.getSessionById(cmd.actorSessionId);
173
+ if (!session?.selfAID)
174
+ throw new Error('Trigger actor session is unavailable');
175
+ const { resolveTriggerCommandArgv, buildTriggerCreateRequest, buildTriggerUpdateRequest } = await import('../cli/trigger-command.js');
176
+ const argv = cmd.argv;
177
+ const command = resolveTriggerCommandArgv(argv, session.selfAID);
178
+ cmd = { ...command, actorSessionId: cmd.actorSessionId, delegationToken: cmd.delegationToken,
179
+ delegationCommandHash: cmd.delegationCommandHash };
180
+ approvedAgentAid = requireAgent(command.agentAid);
181
+ approvedOperation = command.type;
182
+ approvedActor = await authorizeTrigger(approvedAgentAid, approvedOperation, command.triggerId);
183
+ const args = argv.slice(2);
184
+ const usesFiles = args.some(value => ['--file', '--prompt-file', '--script-path', '--script-file']
185
+ .some(flag => value === flag || value.startsWith(`${flag}=`)));
186
+ if (usesFiles)
187
+ assertTriggerLocalFileAuthority(approvedActor);
188
+ const fileScope = approvedActor.fullAccess ? undefined : {
189
+ selfAid: approvedAgentAid, projectPath: session.projectPath,
190
+ sessionRuntimeDir: approvedActor.sessionRuntimeDir, cwd: session.projectPath,
191
+ };
192
+ if (command.type === 'trigger.create') {
193
+ const parsed = await buildTriggerCreateRequest(args, fileScope, approvedAgentAid);
194
+ if (parsed.definition?.agentAid !== approvedAgentAid)
195
+ throw new Error('Imported Trigger does not match the authorized Agent');
196
+ cmd = { ...cmd, ...parsed };
197
+ }
198
+ else if (command.type === 'trigger.update') {
199
+ cmd = { ...cmd, ...await buildTriggerUpdateRequest(args, fileScope, approvedAgentAid) };
200
+ }
201
+ else if (usesFiles) {
202
+ throw new Error('File inputs are only valid for Trigger create/update');
194
203
  }
204
+ }
205
+ else if (cmd.actorSessionId) {
206
+ throw new Error('Managed callers must use trigger.op with approved argv');
207
+ }
208
+ switch (cmd.type) {
195
209
  case 'trigger.list': {
196
210
  const agentAid = requireAgent(cmd.agentAid);
197
211
  const actor = await authorizeTrigger(agentAid, 'trigger.list');
@@ -11,7 +11,7 @@ const TRIGGER_UPDATE_FLAGS = new Set([
11
11
  ]);
12
12
  const TRIGGER_CREATE_FLAGS = new Set([
13
13
  'once', 'delay', 'at', 'cron', 'every', 'event', 'event-filter', 'tz',
14
- 'exec', 'prompt', 'name', 'agent',
14
+ 'exec', 'prompt', 'name', 'agent', 'label',
15
15
  'script-path', 'script-runtime', 'script-args', 'script-timeout', 'execution-timeout',
16
16
  'feedback', 'target-channel', 'target-channel-id', 'target-chat-type', 'target-session', 'target-thread-id',
17
17
  'trigger-thread', 'baseagent',
@@ -68,6 +68,60 @@ export function parseDuration(s) {
68
68
  const multiplier = unit === 'd' ? 86_400_000 : unit === 'h' ? 3_600_000 : unit === 'm' ? 60_000 : 1_000;
69
69
  return amount * multiplier;
70
70
  }
71
+ /**
72
+ * T10: 从原始 args 里提取 `--label key=value`(支持多次出现,值内可用逗号分隔多对)。
73
+ * 约定:labels 若存在则必须含 `kind`(渲染模板路由 key)。
74
+ */
75
+ export function extractLabels(args) {
76
+ const tokens = [];
77
+ if (Array.isArray(args)) {
78
+ for (let i = 0; i < args.length; i++) {
79
+ const a = args[i];
80
+ if (a === '--label') {
81
+ const next = args[i + 1];
82
+ if (next === undefined || next.startsWith('--'))
83
+ return { error: '--label 不能为空(格式:--label key=value)' };
84
+ tokens.push(next);
85
+ i += 1;
86
+ }
87
+ else if (a.startsWith('--label=')) {
88
+ tokens.push(a.slice('--label='.length));
89
+ }
90
+ }
91
+ }
92
+ else {
93
+ const re = /--label(?:=(\S+)|(?:\s+(?!--)(\S+)))?/g;
94
+ let m;
95
+ while ((m = re.exec(args)) !== null) {
96
+ const v = m[1] ?? m[2];
97
+ if (v === undefined)
98
+ return { error: '--label 不能为空(格式:--label key=value)' };
99
+ tokens.push(v.replace(/^["']|["']$/g, ''));
100
+ }
101
+ }
102
+ if (tokens.length === 0)
103
+ return { labels: {} };
104
+ const labels = {};
105
+ for (const token of tokens) {
106
+ for (const pair of token.split(',')) {
107
+ const trimmed = pair.trim();
108
+ if (!trimmed)
109
+ continue;
110
+ const eq = trimmed.indexOf('=');
111
+ if (eq <= 0)
112
+ return { error: `--label 格式无效: "${trimmed}"(应为 key=value)` };
113
+ const key = trimmed.slice(0, eq).trim();
114
+ const value = trimmed.slice(eq + 1);
115
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
116
+ return { error: `--label key 名无效: "${key}"(只允许字母、数字、下划线,且不以数字开头)` };
117
+ }
118
+ labels[key] = value;
119
+ }
120
+ }
121
+ if (!('kind' in labels))
122
+ return { error: '--label 必须包含 kind(如 --label kind=default)' };
123
+ return { labels };
124
+ }
71
125
  export function parseIsoDate(s) {
72
126
  const d = new Date(s);
73
127
  return isNaN(d.getTime()) ? null : d.getTime();
@@ -496,6 +550,9 @@ export function parseTriggerSet(args) {
496
550
  if (!parsed.ok)
497
551
  return { ok: false, error: withPromptFileHint(parsed.error, args) };
498
552
  const value = parsed.value;
553
+ const labelsResult = extractLabels(args);
554
+ if ('error' in labelsResult)
555
+ return { ok: false, error: labelsResult.error };
499
556
  return {
500
557
  ok: true,
501
558
  value: {
@@ -525,6 +582,7 @@ export function parseTriggerSet(args) {
525
582
  model: value.model,
526
583
  effort: value.effort,
527
584
  permissionMode: value.permissionMode,
585
+ ...(Object.keys(labelsResult.labels).length > 0 ? { labels: labelsResult.labels } : {}),
528
586
  },
529
587
  };
530
588
  }
@@ -538,6 +596,9 @@ export function parseTriggerSetArgv(args, opts = {}) {
538
596
  if (!parsed.ok)
539
597
  return { ok: false, error: withPromptFileHint(parsed.error, args, opts) };
540
598
  const value = parsed.value;
599
+ const labelsResult = extractLabels(args);
600
+ if ('error' in labelsResult)
601
+ return { ok: false, error: labelsResult.error };
541
602
  return {
542
603
  ok: true,
543
604
  value: {
@@ -567,6 +628,7 @@ export function parseTriggerSetArgv(args, opts = {}) {
567
628
  model: value.model,
568
629
  effort: value.effort,
569
630
  permissionMode: value.permissionMode,
631
+ ...(Object.keys(labelsResult.labels).length > 0 ? { labels: labelsResult.labels } : {}),
570
632
  },
571
633
  };
572
634
  }
@@ -6,28 +6,16 @@ import { pathToFileURL } from 'node:url';
6
6
  import { spawn } from 'node:child_process';
7
7
  import { resolveCodexCliPath } from '../agents/baseagent.js';
8
8
  import { ensureSessionRuntimeDir } from '../cli/task-context.js';
9
- import { resolveBubblewrapPath } from '../core/permission/sandbox-runtime.js';
10
- import { classifyProtectedPath, getExistingHClassMaskTargets, getExistingLClassReadOnlyTargets, isSameOrDescendant, } from '../core/permission/protected-paths.js';
9
+ import { buildBubblewrapCommand, buildHClassGuardCommand, resolveBubblewrapPath, } from '../core/permission/sandbox-runtime.js';
10
+ import { resolvePhaseOneExecutionSandbox } from '../core/permission/mode.js';
11
+ import { sanitizeShellExecutionEnvironment } from '../core/permission/shell-environment.js';
12
+ import { buildCodexProtectedFilesystemRules, classifyProtectedPath, getExistingRelationAttachmentDirs, isSameOrDescendant, } from '../core/permission/protected-paths.js';
11
13
  import { resolveRoot } from '../paths.js';
12
14
  import { buildCliLaunchCommand, killProcess, resolveCommandPath } from '../utils/cross-platform.js';
13
15
  import { TRIGGER_LOCAL_FILE_LIMIT } from './local-files.js';
14
- const SAFE_ENVIRONMENT_KEYS = [
15
- 'ComSpec', 'COMSPEC', 'LANG', 'LC_ALL', 'OS', 'PATH', 'PATHEXT',
16
- 'ProgramData', 'ProgramFiles', 'ProgramFiles(x86)', 'ProgramW6432',
17
- 'SystemDrive', 'SystemRoot', 'WINDIR',
18
- ];
19
- const SYSTEM_READ_ROOTS = ['/usr/bin', '/usr/lib', '/usr/lib64', '/usr/local/bin', '/usr/local/lib', '/usr/share/locale', '/usr/share/zoneinfo', '/bin', '/sbin', '/lib', '/lib64'];
20
- const SYSTEM_READ_FILES = ['/etc/ld.so.cache', '/etc/localtime', '/etc/passwd', '/etc/group'];
21
- const CONTROL_DIRECTORY_NAMES = ['agents', 'AIDs', 'aids', 'data', 'CA', '.snapshots', 'logs', 'backups'];
22
16
  const WINDOWS_SANDBOX_SETUP_VERSION = 5;
23
- const WINDOWS_SANDBOX_ENV_EXCLUDES = [
17
+ const SCRIPT_ENVIRONMENT_EXCLUDES = [
24
18
  'CODEX_HOME',
25
- 'EVOLCORE_DELEGATION_TOKEN',
26
- 'EVOLCORE_CODEX_SESSION_TOKEN',
27
- 'EVOLCORE_TASK_RUNTIME_CONTEXT',
28
- 'EVOLCORE_INSTANCE_SOCKET',
29
- 'EVOLCORE_DAEMON_SOCKET',
30
- 'EVOLCORE_IPC_SOCKET',
31
19
  'OPENAI_API_KEY',
32
20
  'CODEX_API_KEY',
33
21
  'ANTHROPIC_API_KEY',
@@ -48,6 +36,8 @@ export function buildTriggerScriptLaunch(input) {
48
36
  if (!authorization.permissionMode || authorization.permissionMode === 'fullaccess') {
49
37
  throw new Error('scoped Trigger execution requires an effective non-fullaccess permission mode');
50
38
  }
39
+ const sandboxDecision = resolvePhaseOneExecutionSandbox(authorization.permissionMode, authorization.role);
40
+ const readonly = sandboxDecision.permissionMode === 'readonly';
51
41
  if (process.platform !== 'linux' && process.platform !== 'win32') {
52
42
  throw new Error('scoped Trigger script isolation is unavailable on this host');
53
43
  }
@@ -108,9 +98,12 @@ export function buildTriggerScriptLaunch(input) {
108
98
  fs.writeFileSync(scriptPath, scriptBytes, { flag: 'wx', mode: 0o400 });
109
99
  preserveNodeModuleType(runtime, input.scriptPath, snapshotDir);
110
100
  const env = buildSandboxEnvironment(workDir);
111
- const blockedPaths = protectedScriptPaths(root, input.trigger.agentAid);
112
101
  const scriptArgs = [scriptPath, ...input.scriptArgs];
113
102
  if (windowsBackend) {
103
+ const filesystem = buildCodexProtectedFilesystemRules(root, {
104
+ selfAid: input.trigger.agentAid,
105
+ denyLClassRead: readonly,
106
+ });
114
107
  const systemRoot = process.env.SystemRoot || process.env.WINDIR || 'C:\\Windows';
115
108
  const readRoots = [
116
109
  path.join(systemRoot, 'System32'),
@@ -122,25 +115,37 @@ export function buildTriggerScriptLaunch(input) {
122
115
  snapshotDir,
123
116
  ].filter(value => fs.existsSync(value));
124
117
  const entries = [
125
- { path: { type: 'special', value: { kind: 'root' } }, access: 'deny' },
118
+ { path: { type: 'special', value: { kind: 'root' } },
119
+ access: sandboxDecision.state === 'off' ? 'write' : 'read' },
126
120
  ...readRoots.map(value => filesystemEntry(value, 'read')),
127
- filesystemEntry(projectPath, authorization.permissionMode === 'readonly' ? 'read' : 'write'),
121
+ filesystemEntry(projectPath, readonly ? 'read' : 'write'),
122
+ filesystemEntry(snapshotDir, 'read'),
128
123
  filesystemEntry(workDir, 'write'),
129
- ...blockedPaths.map(value => filesystemEntry(value, 'deny')),
124
+ filesystemEntry(windowsBackend.home, 'deny'),
125
+ ...Object.entries(filesystem).flatMap(([value, access]) => typeof access === 'number' ? [] : [{
126
+ path: /[*?[\]]/.test(value)
127
+ ? { type: 'glob_pattern', pattern: value }
128
+ : { type: 'path', path: value },
129
+ access,
130
+ }]),
131
+ ...getExistingRelationAttachmentDirs(root, {
132
+ selfAid: input.trigger.agentAid,
133
+ onlyOtherAgents: true,
134
+ }).map(value => filesystemEntry(value, 'deny')),
130
135
  ];
131
136
  const sandboxState = JSON.stringify({
132
137
  permissionProfile: {
133
138
  type: 'managed',
134
- file_system: { type: 'restricted', entries },
135
- network: 'restricted',
139
+ file_system: { type: 'restricted', entries, glob_scan_max_depth: filesystem.glob_scan_max_depth },
140
+ network: 'enabled',
136
141
  },
137
142
  sandboxCwd: pathToFileURL(projectPath).href,
138
143
  });
139
144
  if (sandboxState.length > 20_000)
140
145
  throw new Error('scoped Windows Trigger filesystem policy exceeds the command-line budget');
141
- const shellEnvironmentPolicy = `shell_environment_policy={inherit="all",exclude=${JSON.stringify(WINDOWS_SANDBOX_ENV_EXCLUDES)},include_only=[],set={}}`;
146
+ const shellEnvironmentPolicy = `shell_environment_policy={inherit="all",exclude=${JSON.stringify(getScriptEnvironmentExcludes())},include_only=[],set={}}`;
142
147
  const sandboxArgs = [
143
- 'sandbox', '--sandbox-state-json', sandboxState, '--sandbox-state-disable-network',
148
+ 'sandbox', '--sandbox-state-json', sandboxState,
144
149
  '-c', 'windows.sandbox="elevated"',
145
150
  '-c', shellEnvironmentPolicy,
146
151
  ];
@@ -154,40 +159,26 @@ export function buildTriggerScriptLaunch(input) {
154
159
  launch.prepare = signal => verifyWindowsReadIsolation(launch, sandboxArgs, runDir, workDir, signal);
155
160
  return launch;
156
161
  }
157
- const args = [
158
- '--die-with-parent', '--new-session', '--unshare-all', '--cap-drop', 'ALL',
159
- '--tmpfs', '/', '--proc', '/proc', '--dev', '/dev', '--tmpfs', '/tmp',
160
- ];
161
- const readRoots = [...SYSTEM_READ_ROOTS, ...SYSTEM_READ_FILES, runtimeDir]
162
- .filter(value => fs.existsSync(value));
163
- const visibleRoots = [...readRoots, projectPath];
164
- const directoryMountpoints = new Set();
165
- for (const value of [...visibleRoots, ...blockedPaths, runDir, workDir].filter(value => fs.existsSync(value))) {
166
- const stat = fs.lstatSync(value);
167
- let current = stat.isDirectory() ? path.resolve(value) : path.dirname(path.resolve(value));
168
- while (current !== path.parse(current).root) {
169
- directoryMountpoints.add(current);
170
- current = path.dirname(current);
171
- }
172
- }
173
- for (const value of [...directoryMountpoints].sort((left, right) => left.length - right.length)) {
174
- args.push('--dir', value);
175
- }
176
- for (const value of new Set(readRoots))
177
- args.push('--ro-bind', value, value);
178
- args.push(authorization.permissionMode === 'readonly' ? '--ro-bind' : '--bind', projectPath, projectPath);
179
- for (const value of blockedPaths) {
180
- if (!visibleRoots.some(directory => isSameOrDescendant(value, directory)) || !fs.existsSync(value))
181
- continue;
182
- const stat = fs.lstatSync(value);
183
- if (stat.isDirectory())
184
- args.push('--tmpfs', value, '--remount-ro', value);
185
- else
186
- args.push('--ro-bind', '/dev/null', value);
187
- }
188
- args.push('--ro-bind', runDir, runDir, '--bind', workDir, workDir, '--remount-ro', '/');
189
- args.push('--chdir', projectPath, '--', runtime, ...scriptArgs);
190
- return { command: bubblewrap, args, cwd: projectPath, env, cleanup };
162
+ const launch = sandboxDecision.state === 'off'
163
+ ? buildHClassGuardCommand(runtime, scriptArgs, root, { selfAid: input.trigger.agentAid })
164
+ : buildBubblewrapCommand(runtime, scriptArgs, {
165
+ projectPath,
166
+ projectWritable: !readonly,
167
+ root,
168
+ selfAid: input.trigger.agentAid,
169
+ exposeDaemonIpc: false,
170
+ denyLClassRead: readonly,
171
+ });
172
+ if (!launch)
173
+ throw new Error('bubblewrap is required for scoped Trigger script isolation');
174
+ const separator = launch.args.indexOf('--');
175
+ if (separator < 0)
176
+ throw new Error('bubblewrap launch is missing its command separator');
177
+ const runMountArgs = ['--ro-bind', runDir, runDir, '--bind', workDir, workDir];
178
+ if (!launch.args.slice(0, separator).includes('--chdir'))
179
+ runMountArgs.push('--chdir', projectPath);
180
+ launch.args.splice(separator, 0, ...runMountArgs);
181
+ return { ...launch, cwd: projectPath, env, cleanup };
191
182
  }
192
183
  catch (error) {
193
184
  cleanup();
@@ -331,30 +322,20 @@ function preserveNodeModuleType(runtime, scriptPath, snapshotDir) {
331
322
  }
332
323
  function buildSandboxEnvironment(workDir) {
333
324
  const env = {};
334
- for (const key of SAFE_ENVIRONMENT_KEYS) {
335
- const value = process.env[key];
336
- if (typeof value === 'string' && value.length > 0)
325
+ const excluded = new Set(getScriptEnvironmentExcludes().map(key => key.toUpperCase()));
326
+ for (const [key, value] of Object.entries(sanitizeShellExecutionEnvironment(process.env))) {
327
+ if (!excluded.has(key.toUpperCase())
328
+ && typeof value === 'string')
337
329
  env[key] = value;
338
330
  }
339
- const homeDir = process.platform === 'win32' ? path.join(workDir, 'home') : workDir;
340
- if (homeDir !== workDir)
341
- fs.mkdirSync(homeDir, { mode: 0o700 });
342
331
  return {
343
- ...env, HOME: homeDir, USERPROFILE: homeDir,
344
- APPDATA: path.join(workDir, 'appdata'), LOCALAPPDATA: path.join(workDir, 'localappdata'),
332
+ ...env,
345
333
  TMPDIR: workDir, TEMP: workDir, TMP: workDir, EVOLCORE_TRIGGER_SANDBOX: '1',
346
334
  };
347
335
  }
348
- function protectedScriptPaths(root, selfAid) {
349
- const directoryRoots = CONTROL_DIRECTORY_NAMES.map(name => path.join(root, name));
350
- const targets = [
351
- ...directoryRoots,
352
- ...getExistingHClassMaskTargets(root, { selfAid }).map(target => target.path),
353
- ...getExistingLClassReadOnlyTargets(root, { selfAid }).map(target => target.path),
354
- path.join(root, 'daemon.json'), path.join(root, '.env'), path.join(root, '.device_id'),
355
- path.join(root, '.lock'), path.join(root, 'daemon.pid'),
356
- ];
357
- return [...new Set(targets)].filter(candidate => (directoryRoots.includes(candidate) || !directoryRoots.some(directory => isSameOrDescendant(candidate, directory))));
336
+ function getScriptEnvironmentExcludes() {
337
+ const dynamic = Object.keys(process.env).filter(key => key.toUpperCase().startsWith('EVOLCORE_'));
338
+ return [...new Set([...SCRIPT_ENVIRONMENT_EXCLUDES, ...dynamic])];
358
339
  }
359
340
  function filesystemEntry(value, access) {
360
341
  return { path: { type: 'path', path: path.resolve(value) }, access };
@@ -252,6 +252,20 @@ function normalizeV3Phase2(raw, opts) {
252
252
  reliability: normalizeReliability(raw.reliability),
253
253
  limits: normalizeLimits(raw.limits),
254
254
  };
255
+ // T10: labels 字典原样保留,只做类型校验(如果存在必须是 Record<string, string>)
256
+ const rawLabels = raw.labels;
257
+ if (rawLabels !== undefined && rawLabels !== null) {
258
+ if (typeof rawLabels !== 'object' || Array.isArray(rawLabels)) {
259
+ throw new Error('labels must be a plain object');
260
+ }
261
+ const labels = {};
262
+ for (const [k, v] of Object.entries(rawLabels)) {
263
+ if (typeof v !== 'string')
264
+ throw new Error(`labels.${k} must be a string`);
265
+ labels[k] = v;
266
+ }
267
+ definition.labels = labels;
268
+ }
255
269
  validateTriggerSemantics(definition);
256
270
  return definition;
257
271
  }
@@ -466,7 +466,7 @@ export function getErrorMessage(error, terminalReason, includeEmoji = true) {
466
466
  }
467
467
  }
468
468
  // 回退到原有的错误消息匹配逻辑
469
- const msg = error?.message || String(error);
469
+ const msg = resolveErrorMessage(error);
470
470
  const warnPrefix = includeEmoji ? '⚠️ ' : '';
471
471
  const errPrefix = includeEmoji ? '❌ ' : '';
472
472
  if (/invalid (?:jsonl )?session file/i.test(msg)) {
@@ -512,5 +512,26 @@ export function getErrorMessage(error, terminalReason, includeEmoji = true) {
512
512
  if (msg.includes('timeout')) {
513
513
  return `${warnPrefix}请求超时,请重试`;
514
514
  }
515
+ if (msg)
516
+ return `${errPrefix}暂无法判定错误原因:${msg}`;
515
517
  return `${errPrefix}处理消息时出错,请稍后重试`;
516
518
  }
519
+ function resolveErrorMessage(error) {
520
+ if (typeof error === 'string')
521
+ return error.trim();
522
+ if (error === null || error === undefined)
523
+ return '';
524
+ if (typeof error === 'object') {
525
+ const message = 'message' in error ? String(error.message ?? '').trim() : '';
526
+ if (message)
527
+ return message;
528
+ const code = 'code' in error ? String(error.code ?? '').trim() : '';
529
+ if (code)
530
+ return code;
531
+ if ('message' in error)
532
+ return '';
533
+ const stringified = String(error).trim();
534
+ return stringified === '[object Object]' || stringified === 'Error' ? '' : stringified;
535
+ }
536
+ return String(error).trim();
537
+ }
@@ -111,7 +111,7 @@ ECK 的 manifest 机制决定了**不同位置的内容有不同的成本量级*
111
111
 
112
112
  写入 `rules/` 前自问:**这条信息如果不注入,agent 会在多少比例的对话里做错事?**
113
113
 
114
- - 每轮都需要(如"不得调用 `ec restart`")→ 进 `rules/`
114
+ - 每轮都需要(如"不得停止或重启承载当前会话的 EvolCore 实例")→ 进 `rules/`
115
115
  - 偶发场景才需要(如"Menu 消息有哪些字段")→ 留 `docs/`,靠 `INDEX.md` 触发词按需命中
116
116
 
117
117
  答不上来就别进 `rules/`。最常见的隐性浪费是把偶发查阅的内容塞进规则文件,之后每轮对话都在白烧 token。
@@ -42,6 +42,14 @@ AUN keystore seed 由 EvolCore 使用固定应用值管理,不再提供配置
42
42
 
43
43
  > Gateway 不在配置里手填:连接时从 AID 自动发现(见下)。Keystore 默认落在 `$EVOLCORE_HOME`。
44
44
 
45
+ ## SDK 同步边界
46
+
47
+ `@agentunion/fastaun` 管理消息/群消息的 A/T/H 水位、ACK、Push/Tail/Forward、Gap Fill、多页同步、重连恢复和 Pull Gate。EvolCore 只消费公开消息/撤回/同步事件,负责业务路由、持久化和 `(namespace, message_id)` 应用幂等;`seq` 只用于顺序、诊断和日志。菜单、普通文本、volatile 消息和应用去重记录均不能用来推导或回写 SDK 游标。
48
+
49
+ 不得访问或 monkey patch SDK 私有成员,包括 `_seqTracker`、`_pullV2`、`_pullGroupV2`、`_publishAppEvent`、`_dispatcher`、`_identity`、`_auth`、`_tokenStore`、`_v2E2EE`。需要补同步、确认消息或调试时,应先检查 SDK 公开 API;缺少能力应向 SDK 增补公开契约,而不是在渠道适配层改写同步状态。显式查询使用公开 RPC/facade 与独立 slot,不得推进 daemon 消费游标。
50
+
51
+ `fastaun 0.5.18` 公开 `sender_device_id`,但隐藏发送方 slot。EvolCore 的菜单 token 当前仅按设备绑定,不声称具备同设备多 slot 隔离。
52
+
45
53
  ## 特有机制
46
54
 
47
55
  - **隐式创建**:aun 渠道由顶层 `aid` 派生,无独立配置实例;这是它与飞书等渠道最大的不同。
@@ -311,6 +311,7 @@ coding 场景(无 channel/无身份)下,`chatType`、`channel`、`selfAid`
311
311
  | 55 | commands | fragment | active 且 channel≠null | ✓ |
312
312
  | 60 | session | fragment | active | ✓ |
313
313
  | 70 | baseagent | fragment | active 且 baseAgent≠null | ✓ |
314
+ | 80 | managed-runtime-notice | fragment | active | ✓ |
314
315
 
315
316
  Bootstrap 只加载引导与有渠道时的最小通信上下文 `[reply]`,不加载完整身份/关系/渠道能力、个人记忆或普通任务命令。
316
317
  `[reply]` 展示 selfAid、peerId、peerType、channel、chatType、chatMode;未知身份显示 unknown,回复方式只取运行时已解析的 chatMode,不从身份或私聊场景重新推断。命令经当前会话 delegated sender / daemon IPC 发送,遵守现有身份、目标、话题与加密约束。
@@ -14,14 +14,14 @@
14
14
  2. **不确定有没有命令**时,直接 `ec <猜测的子命令> --help` 试一下(如 `ec stats --help`)
15
15
  3. **已知场景需求**(如"要给对端发消息")→ 看下方分类表 → 定位到对应命令集
16
16
 
17
- 托管 Agent 任务禁止调用 `ec start`、`ec stop`、`ec restart` 或 `ec restart-monitor`。这些进程生命周期命令只能由宿主终端执行;源码改动后 Agent 只应完成构建并报告需要宿主重启。
17
+ 生命周期保护仅针对承载当前会话的 EvolCore 实例;其他实例按用户授权和目标端权限处理,不限管理通道。完整规则见 `$KITS_RULES/06-channel.md`。
18
18
 
19
19
  ## 服务运维类(本地,无需 AUN)
20
20
 
21
21
  | 命令 | 用途 | 触发词 |
22
22
  |------|------|--------|
23
23
  | `ec --version` / `-v` / `-V` | 查看版本号 | 版本 |
24
- | `ec restart` | 宿主终端重启服务(托管 Agent 禁止调用) | 重启 |
24
+ | `ec restart` | 重启目标 daemon(承载当前托管会话的实例受生命周期保护) | 重启 |
25
25
  | `ec status` | 查看运行状态、进程、会话、渠道连接 | 状态/查看状态 |
26
26
  | `ec logs` | 查看日志(`--level` `--module` `--raw` `--no-color`) | 日志/查看日志 |
27
27
  | `ec diagnose` | 诊断启动环境 | 诊断/环境诊断 |
@@ -33,6 +33,7 @@ Windows 上 Codex 托管任务出现 `helper_sandbox_lock_failed`、`SetNamedSec
33
33
  | 命令集 | 用途 | 触发词 | 适用场景 | 文档 |
34
34
  |--------|------|--------|----------|------|
35
35
  | `ec msg` | 私聊收发消息 | 回复/发消息/拉取/撤回/查在线 | 有对端(peerId) | `msg.md` |
36
+ | `ec thread` | 话题管理与话题链 | 话题/thread/跨会话/委托/话题链/ThreadOrigin/summary/删除话题 | 有对端(私聊或群聊) | `thread-chain.md` |
36
37
  | `ec group` | 群聊收发与群管理 | 群发/建群/本地群历史/邀请/踢人/退群/群成员/角色/封禁/屏蔽/成员状态/规则/@提及模式/协作 | 群聊(groupId) | `group.md`;完整协作流程见 `group-collaboration.md` |
37
38
  | `/invites` / `ec group invites` | 收到群邀请、Owner 卡片审核与恢复 | 入群邀请/接受邀请/拒绝邀请/审核卡片/待处理邀请 | 当前 Agent Owner 的 AUN 私聊 | `group.md` |
38
39
  | `ec group rules` | 群规则文件发布与上下文注入 | 群规则/工作流程/职责分工/rules.md/发布规则 | AUN 群聊(groupId) | `group-rules.md` |