evolcore 0.0.28 → 0.0.29-test.0

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 (183) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/bin/ec-safe-output.js +270 -54
  3. package/dist/agents/baseagent-availability.js +37 -1
  4. package/dist/agents/baseagent.js +19 -9
  5. package/dist/agents/claude-runner.js +171 -13
  6. package/dist/agents/codex/app-server-client.js +6 -2
  7. package/dist/agents/codex/diagnostics.js +4 -3
  8. package/dist/agents/codex-runner.js +396 -95
  9. package/dist/agents/gemini-runner.js +35 -17
  10. package/dist/aun/group/history.js +130 -90
  11. package/dist/aun/group/operations.js +1 -3
  12. package/dist/aun/menu/transport.js +5 -3
  13. package/dist/aun/msg/managed-operation.js +70 -0
  14. package/dist/aun/msg/p2p.js +1 -3
  15. package/dist/aun/msg/upload.js +2 -2
  16. package/dist/aun/outbox.js +1 -37
  17. package/dist/channels/aun.js +249 -89
  18. package/dist/channels/daemon.js +18 -1
  19. package/dist/channels/feishu.js +1 -2
  20. package/dist/cli/agent-command.js +23 -3
  21. package/dist/cli/agent-create.js +68 -2
  22. package/dist/cli/agent.js +12 -2
  23. package/dist/cli/aun-commands.js +87 -13
  24. package/dist/cli/cli-argv.js +26 -0
  25. package/dist/cli/daemon-commands.js +40 -10
  26. package/dist/cli/data-command.js +4 -4
  27. package/dist/cli/handoff-command.js +2 -0
  28. package/dist/cli/index.js +18 -1
  29. package/dist/cli/managed-command-guard.js +10 -0
  30. package/dist/cli/memory-command.js +441 -0
  31. package/dist/cli/restart-monitor.js +62 -6
  32. package/dist/cli/skill-command.js +41 -0
  33. package/dist/cli/task-context.js +2 -23
  34. package/dist/cli/trigger-command.js +9 -1
  35. package/dist/cli/watch-msg-diag.js +209 -0
  36. package/dist/config/config-field-policy.js +52 -3
  37. package/dist/config/config-manager.js +17 -4
  38. package/dist/config/contact-book-store.js +2 -2
  39. package/dist/config/mention-mode.js +7 -2
  40. package/dist/config/merge.js +28 -9
  41. package/dist/config/resolved-config-op.js +1 -1
  42. package/dist/config/runtime-env.js +137 -0
  43. package/dist/config/schema-registry.js +11 -1
  44. package/dist/config-store.js +10 -10
  45. package/dist/core/audit/event-key.js +3 -1
  46. package/dist/core/audit/log-integrity.js +4 -0
  47. package/dist/core/auth/agent-delegation.js +2 -1
  48. package/dist/core/auth/auth-gateway.js +13 -6
  49. package/dist/core/auth/authorization-audit.js +54 -19
  50. package/dist/core/auth/operation-authorizer.js +17 -0
  51. package/dist/core/auth/operation-catalog.js +9 -0
  52. package/dist/core/causation/context.js +5 -1
  53. package/dist/core/command/agent-control.js +1 -2
  54. package/dist/core/command/command-handler.js +51 -23
  55. package/dist/core/command/memory-slash.js +270 -0
  56. package/dist/core/command/menu-catalog.js +1 -0
  57. package/dist/core/command/menu-handler.js +315 -60
  58. package/dist/core/command/menu-protocol.js +42 -4
  59. package/dist/core/command/slash-gate.js +10 -10
  60. package/dist/core/command/slash-handler.js +206 -64
  61. package/dist/core/data-migration.js +282 -505
  62. package/dist/core/event-catalog.js +26 -0
  63. package/dist/core/evolagent-registry.js +6 -0
  64. package/dist/core/evolagent.js +0 -10
  65. package/dist/core/handoff/store.js +1 -20
  66. package/dist/core/inference/text-inference.js +57 -1
  67. package/dist/core/memory/consolidation-context.js +505 -0
  68. package/dist/core/memory/consolidation-ops-records.js +124 -0
  69. package/dist/core/memory/consolidation-trigger.js +394 -0
  70. package/dist/core/memory/index-manager.js +308 -0
  71. package/dist/core/memory/launch-consolidation.js +395 -0
  72. package/dist/core/memory/memory-admin.js +234 -0
  73. package/dist/core/memory/memory-document.js +100 -0
  74. package/dist/core/memory/memory-session-policy.js +41 -0
  75. package/dist/core/memory/memory-signature.js +257 -0
  76. package/dist/core/memory/memory-writer.js +432 -0
  77. package/dist/core/memory/staleness.js +117 -0
  78. package/dist/core/message/auto-mention-gate.js +454 -0
  79. package/dist/core/message/im-renderer.js +69 -16
  80. package/dist/core/message/message-bridge.js +287 -21
  81. package/dist/core/message/message-log.js +1 -0
  82. package/dist/core/message/message-queue.js +114 -27
  83. package/dist/core/message/pending-hints.js +329 -161
  84. package/dist/core/message/response-engine.js +567 -80
  85. package/dist/core/permission/ec-command-parser.js +1029 -80
  86. package/dist/core/permission/protected-paths.js +376 -126
  87. package/dist/core/permission/sandbox-runtime.js +40 -12
  88. package/dist/core/permission/tool-error-code.js +96 -13
  89. package/dist/core/permission/tool-policy.js +276 -23
  90. package/dist/core/role/runtime-policy.js +3 -7
  91. package/dist/core/session/{runner-task-history.js → runner-event-summary.js} +1 -1
  92. package/dist/core/session/session-fs-store.js +0 -76
  93. package/dist/core/session/session-idle-monitor.js +355 -0
  94. package/dist/core/session/session-manager.js +478 -17
  95. package/dist/core/session/session-mapper.js +33 -16
  96. package/dist/core/session/session-renew.js +217 -155
  97. package/dist/core/session/{session-turns.js → session-transcript.js} +35 -6
  98. package/dist/core/skill/global-watcher.js +282 -0
  99. package/dist/core/skill/index-generator.js +84 -0
  100. package/dist/core/skill/startup.js +73 -0
  101. package/dist/core/skill/switch-resolver.js +115 -0
  102. package/dist/core/task/task-activity-store.js +219 -0
  103. package/dist/eck/kit-renderer.js +30 -3
  104. package/dist/eck/manifest-engine.js +26 -4
  105. package/dist/index.js +708 -18
  106. package/dist/ipc.js +564 -2
  107. package/dist/paths.js +30 -9
  108. package/dist/response-system/engines/v1/proactive-flow.js +116 -15
  109. package/dist/response-system/modes/single-session/index.js +3 -0
  110. package/dist/trigger/feedback.js +1 -0
  111. package/dist/trigger/manager.js +0 -10
  112. package/dist/trigger/scheduler.js +11 -0
  113. package/dist/trigger/validation.js +22 -0
  114. package/dist/utils/avatar-upload.js +59 -18
  115. package/dist/utils/error-dict.json +6 -3
  116. package/dist/utils/error-utils.js +27 -0
  117. package/dist/utils/logger.js +32 -10
  118. package/dist/utils/media-cache.js +84 -0
  119. package/dist/utils/msg-diag.js +97 -0
  120. package/dist/utils/welcome.js +37 -8
  121. package/kits/docs/AUTHORING.md +314 -0
  122. package/kits/docs/GUIDE.md +2 -0
  123. package/kits/docs/INDEX.md +2 -0
  124. package/kits/docs/aun/CHEATSHEET.md +4 -0
  125. package/kits/docs/aun/SDK.md +60 -0
  126. package/kits/docs/context-assembly.md +10 -6
  127. package/kits/docs/evolcore/INDEX.md +17 -3
  128. package/kits/docs/evolcore/agent-create.md +24 -0
  129. package/kits/docs/evolcore/agent.md +5 -2
  130. package/kits/docs/evolcore/config.md +1 -1
  131. package/kits/docs/evolcore/ec-capabilities.md +364 -0
  132. package/kits/docs/evolcore/group.md +34 -18
  133. package/kits/docs/evolcore/memory-architecture.md +78 -0
  134. package/kits/docs/evolcore/memory-consolidation-batches.md +133 -0
  135. package/kits/docs/evolcore/memory-consolidation.md +263 -0
  136. package/kits/docs/evolcore/memory-tags.md +63 -0
  137. package/kits/docs/evolcore/memory.md +110 -0
  138. package/kits/docs/evolcore/menu-client.md +27 -11
  139. package/kits/{schemas → docs/evolcore}/menu-exec-schema-commands.md +12 -6
  140. package/kits/docs/evolcore/menu-protocol.md +213 -0
  141. package/kits/docs/evolcore/msg.md +2 -0
  142. package/kits/docs/evolcore/trigger.md +7 -5
  143. package/kits/docs/path-registry.md +6 -8
  144. package/kits/docs/prompt-loading-architecture.md +5 -3
  145. package/kits/docs/venues/group.md +5 -4
  146. package/kits/eck_manifest.json +294 -88
  147. package/kits/eck_manifest.memory.json +257 -0
  148. package/kits/rules/03-identity.md +8 -8
  149. package/kits/rules/05-venue.md +4 -2
  150. package/kits/rules/06-channel.md +1 -1
  151. package/kits/schemas/_meta.json +276 -47
  152. package/kits/schemas/agent-config.schema.12.json +38 -2
  153. package/kits/schemas/agent-config.schema.13.json +591 -0
  154. package/kits/schemas/daemon.schema.6.json +9 -9
  155. package/kits/schemas/defaults.schema.5.json +29 -0
  156. package/kits/schemas/relation-config.schema.8.json +14 -1
  157. package/kits/schemas/relation-config.schema.9.json +129 -0
  158. package/kits/schemas/role-config.schema.1.json +3 -3
  159. package/kits/skills/.switches.json +3 -0
  160. package/kits/skills/ec-skill/SKILL.md +217 -0
  161. package/kits/skills/ec-skill/scripts/cli.mjs +128 -0
  162. package/kits/skills/ec-skill/scripts/deps.mjs +107 -0
  163. package/kits/skills/ec-skill/scripts/frontmatter.mjs +104 -0
  164. package/kits/skills/ec-skill/scripts/skill.mjs +629 -0
  165. package/kits/skills/ec-skill/scripts/template/SKILL.md.tmpl +30 -0
  166. package/kits/templates/decision/auto-mention.md +25 -0
  167. package/kits/templates/decision/session-renew.md +22 -0
  168. package/kits/templates/memory-consolidation/forget.md +104 -0
  169. package/kits/templates/memory-consolidation/generalize.md +76 -0
  170. package/kits/templates/memory-consolidation/notes.md +133 -0
  171. package/kits/templates/memory-consolidation/profile.md +58 -0
  172. package/kits/templates/memory-consolidation/promote.md +72 -0
  173. package/kits/templates/memory-consolidation/workflow.md +75 -0
  174. package/kits/templates/system-fragments/bootstrap.md +14 -5
  175. package/kits/templates/system-fragments/commands.md +1 -0
  176. package/kits/templates/system-fragments/group-history.md +6 -0
  177. package/kits/templates/system-fragments/identity.md +0 -3
  178. package/kits/templates/system-fragments/memory.md +79 -0
  179. package/kits/templates/system-fragments/reply.md +1 -0
  180. package/kits/templates/system-fragments/venue.md +1 -1
  181. package/package.json +4 -6
  182. package/dist/trigger/legacy-session-history.js +0 -19
  183. package/kits/templates/system-fragments/group-unread-history.md +0 -7
@@ -0,0 +1,137 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { execFileSync } from 'child_process';
4
+ import { agentDir, resolvePaths } from '../paths.js';
5
+ export const RUNTIME_ENVIRONMENT_NAMES = [
6
+ 'ANTHROPIC_BASE_URL',
7
+ 'ANTHROPIC_AUTH_TOKEN',
8
+ 'ANTHROPIC_API_KEY',
9
+ ];
10
+ const cache = new Map();
11
+ const overrides = new Map();
12
+ const startupValues = new Map();
13
+ function captureStartupValues() {
14
+ startupValues.clear();
15
+ for (const name of RUNTIME_ENVIRONMENT_NAMES)
16
+ startupValues.set(name, process.env[name]);
17
+ }
18
+ captureStartupValues();
19
+ function isSafeAid(aid) {
20
+ const labels = aid.split('.');
21
+ return aid.length > 0 && aid.length <= 256 && path.basename(aid) === aid
22
+ && labels.length >= 3
23
+ && labels.every(label => /^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/u.test(label));
24
+ }
25
+ function normalizeScope(aid) {
26
+ if (!aid || aid === '*')
27
+ return '*';
28
+ if (!isSafeAid(aid))
29
+ throw new Error('Invalid agent aid');
30
+ return aid;
31
+ }
32
+ function envFileValue(file, name) {
33
+ try {
34
+ for (const line of fs.readFileSync(file, 'utf8').split(/\r?\n/u)) {
35
+ const match = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/u);
36
+ if (!match || match[1] !== name)
37
+ continue;
38
+ const value = match[2].trim();
39
+ return value.length > 1 && ((value[0] === '"' && value.at(-1) === '"')
40
+ || (value[0] === "'" && value.at(-1) === "'")) ? value.slice(1, -1) : value;
41
+ }
42
+ }
43
+ catch { }
44
+ return undefined;
45
+ }
46
+ function windowsValue(hive, name) {
47
+ if (process.platform !== 'win32')
48
+ return undefined;
49
+ const registryPath = hive === 'user' ? 'HKCU\\Environment'
50
+ : 'HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment';
51
+ try {
52
+ const output = execFileSync(path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'reg.exe'), ['query', registryPath, '/v', name], {
53
+ encoding: 'utf8', windowsHide: true, timeout: 1500, stdio: ['ignore', 'pipe', 'ignore'],
54
+ });
55
+ const line = output.split(/\r?\n/u).find(entry => entry.trimStart().startsWith(`${name} `));
56
+ return line?.match(/\sREG_[A-Z0-9_]+\s+(.+?)\s*$/u)?.[1];
57
+ }
58
+ catch {
59
+ return undefined;
60
+ }
61
+ }
62
+ function readOverride(name, aid) {
63
+ return overrides.get(normalizeScope(aid))?.get(name) ?? overrides.get('*')?.get(name);
64
+ }
65
+ export function readRuntimeEnvironmentVariable(name, aid, projectPath) {
66
+ if (!RUNTIME_ENVIRONMENT_NAMES.includes(name)) {
67
+ throw new Error('Unsupported gateway environment variable');
68
+ }
69
+ const typedName = name;
70
+ const scope = normalizeScope(aid);
71
+ const cacheKey = `${scope}:${projectPath || ''}:${typedName}`;
72
+ const now = Date.now();
73
+ const hit = cache.get(cacheKey);
74
+ if (hit && hit.expires > now)
75
+ return hit.result;
76
+ const agentValue = aid && isSafeAid(aid)
77
+ ? envFileValue(path.join(agentDir(aid), '.env'), typedName)
78
+ : undefined;
79
+ const projectValue = projectPath
80
+ ? envFileValue(path.join(projectPath, '.env'), typedName)
81
+ : undefined;
82
+ const rootValue = envFileValue(path.join(resolvePaths().root, '.env'), typedName);
83
+ const localSelections = [
84
+ [readOverride(typedName, aid), 'ipc-process'],
85
+ [agentValue, 'agent-env'],
86
+ [projectValue, 'project-env'],
87
+ [rootValue, 'root-env'],
88
+ ];
89
+ const local = localSelections.find(([value]) => value !== undefined && value !== '');
90
+ if (local) {
91
+ const result = { name: typedName, value: local[0], source: local[1], checkedAt: now };
92
+ cache.set(cacheKey, { result, expires: now + 3000 });
93
+ return result;
94
+ }
95
+ const startup = startupValues.get(typedName);
96
+ const user = windowsValue('user', typedName);
97
+ const machine = windowsValue('machine', typedName);
98
+ const selections = [
99
+ [user && user !== startup ? user : undefined, 'windows-user'],
100
+ [machine && machine !== startup ? machine : undefined, 'windows-machine'],
101
+ [startup, 'process-startup'],
102
+ [user, 'windows-user'],
103
+ [machine, 'windows-machine'],
104
+ ];
105
+ const selected = selections.find(([value]) => value !== undefined && value !== '');
106
+ const result = {
107
+ name: typedName,
108
+ value: selected?.[0],
109
+ source: selected?.[1] ?? 'unset',
110
+ checkedAt: now,
111
+ };
112
+ cache.set(cacheKey, { result, expires: now + 3000 });
113
+ return result;
114
+ }
115
+ export function setRuntimeEnvironmentOverride(aid, values) {
116
+ const scope = normalizeScope(aid);
117
+ const next = new Map(overrides.get(scope));
118
+ for (const name of RUNTIME_ENVIRONMENT_NAMES) {
119
+ if (!Object.prototype.hasOwnProperty.call(values, name))
120
+ continue;
121
+ const value = values[name]?.trim();
122
+ if (value)
123
+ next.set(name, value);
124
+ else
125
+ next.delete(name);
126
+ }
127
+ if (next.size)
128
+ overrides.set(scope, next);
129
+ else
130
+ overrides.delete(scope);
131
+ cache.clear();
132
+ }
133
+ export function resetRuntimeEnvironmentForTests() {
134
+ overrides.clear();
135
+ cache.clear();
136
+ captureStartupValues();
137
+ }
@@ -99,12 +99,20 @@ function schemaFilePath(name, version) {
99
99
  return path.join(kitsSchemasDir(), `${name}.schema.${version}.json`);
100
100
  }
101
101
  function extractFields(raw) {
102
+ return extractFieldsFromProps(raw.properties || {});
103
+ }
104
+ function extractFieldsFromProps(props) {
102
105
  const fields = new Map();
103
- const props = raw.properties || {};
104
106
  for (const [name, spec] of Object.entries(props)) {
105
107
  if (name === '$schema_version')
106
108
  continue;
107
109
  const merge = spec['x-merge'] || inferMerge(spec);
110
+ const mergeLogic = spec['x-merge-logic'] === 'AND' ? 'AND' : undefined;
111
+ // 递归提取嵌套 object 字段的子字段,用于 mergeLayers 感知嵌套字段的 x-merge-logic
112
+ const childProps = spec.properties;
113
+ const children = (childProps && typeof childProps === 'object')
114
+ ? extractFieldsFromProps(childProps)
115
+ : undefined;
108
116
  fields.set(name, {
109
117
  name,
110
118
  type: spec.type,
@@ -112,6 +120,8 @@ function extractFields(raw) {
112
120
  enum: spec.enum,
113
121
  default: spec.default,
114
122
  description: spec.description,
123
+ ...(mergeLogic ? { mergeLogic } : {}),
124
+ ...(children && children.size > 0 ? { children } : {}),
115
125
  });
116
126
  }
117
127
  return fields;
@@ -57,8 +57,8 @@ export function loadDaemonConfig() {
57
57
  const sanitized = stripLegacyAunGatewayConfig(raw ?? {}, resolvePaths().daemonConfig, message => logger.warn(message));
58
58
  return validateDaemonConfig(normalizeProcessConfigCompat(normalizeDaemonServices(sanitized)), false);
59
59
  }
60
- export function isFullAccessEnabled(config = loadDaemonConfig()) {
61
- return config.fullaccess?.enabled === undefined || config.fullaccess.enabled === true;
60
+ export function isRestartRecoveryEnabled(config = loadDaemonConfig()) {
61
+ return config.restartRecovery?.enabled === true;
62
62
  }
63
63
  let eckSnapshotsConfigCache;
64
64
  /** Parse the process-level snapshot gate once for the current daemon lifecycle. */
@@ -91,16 +91,16 @@ export function saveDaemonConfig(value) {
91
91
  atomicWriteJson(configPath, validateDaemonConfig({ ...normalized, $schema_version: currentVersion('daemon') }, validateAidDomain));
92
92
  }
93
93
  function validateDaemonConfig(value, validateAidDomain) {
94
- if (value.fullaccess !== undefined) {
95
- if (!value.fullaccess || typeof value.fullaccess !== 'object' || Array.isArray(value.fullaccess)) {
96
- throw new Error('daemon.json.fullaccess must be an object');
94
+ if (value.restartRecovery !== undefined) {
95
+ if (!value.restartRecovery || typeof value.restartRecovery !== 'object' || Array.isArray(value.restartRecovery)) {
96
+ throw new Error('daemon.json.restartRecovery must be an object');
97
97
  }
98
- const unknownFullAccessFields = Object.keys(value.fullaccess).filter(key => key !== 'enabled');
99
- if (unknownFullAccessFields.length > 0) {
100
- throw new Error(`daemon.json.fullaccess contains unsupported fields: ${unknownFullAccessFields.join(', ')}`);
98
+ const unknownRestartRecoveryFields = Object.keys(value.restartRecovery).filter(key => key !== 'enabled');
99
+ if (unknownRestartRecoveryFields.length > 0) {
100
+ throw new Error('daemon.json.restartRecovery contains unsupported fields: ' + unknownRestartRecoveryFields.join(', '));
101
101
  }
102
- if (value.fullaccess.enabled !== undefined && typeof value.fullaccess.enabled !== 'boolean') {
103
- throw new Error('daemon.json.fullaccess.enabled must be a boolean');
102
+ if (value.restartRecovery.enabled !== undefined && typeof value.restartRecovery.enabled !== 'boolean') {
103
+ throw new Error('daemon.json.restartRecovery.enabled must be a boolean');
104
104
  }
105
105
  }
106
106
  const minEvolVersion = value.aun?.minEvolVersion;
@@ -40,5 +40,7 @@ export function buildAuthorizationEventKey(input) {
40
40
  requestId: input.requestId,
41
41
  })}:authorization:${stableDigest([input.operation])}`;
42
42
  }
43
- return `authorization:${stableDigest([input.sessionId ?? 'unknown', input.operation])}`;
43
+ // Anonymous authorizations cannot be joined safely. Keep them as distinct
44
+ // records so daily analysis never merges unrelated calls by session/operation.
45
+ return `authorization:unlinked:${crypto.randomUUID()}`;
44
46
  }
@@ -137,6 +137,10 @@ function stableHash(value) {
137
137
  permissionMode: firstDefined(value, nested, ['permissionMode', 'permission_mode']),
138
138
  isError: firstDefined(value, nested, ['isError', 'is_error']) ?? (value.ok === false || nested?.ok === false ? true : undefined),
139
139
  errorCode: firstDefined(value, nested, ['errorCode', 'error_code']),
140
+ errorDetailCode: firstDefined(value, nested, ['errorDetailCode', 'detail_code', 'detailCode']),
141
+ classificationMissing: firstDefined(value, nested, ['classificationMissing', 'classification_missing']),
142
+ classificationConflict: firstDefined(value, nested, ['classificationConflict', 'classification_conflict']),
143
+ exitCode: firstDefined(value, nested, ['exitCode', 'exit_code']),
140
144
  decision: firstDefined(value, nested, ['decision']),
141
145
  decisionSource: firstDefined(value, nested, ['decisionSource', 'decision_source']),
142
146
  policyCode: firstDefined(value, nested, ['policyCode', 'policy_code']),
@@ -3,6 +3,7 @@ import crypto from 'crypto';
3
3
  import { authorizeOperation, buildAuthSubject } from './auth-gateway.js';
4
4
  import { formatPeerKey, parsePeerKey } from '../relation/peer-identity.js';
5
5
  import { logger } from '../../utils/logger.js';
6
+ import { canonicalizeEvolcoreCommandArgv } from '../permission/ec-command-parser.js';
6
7
  export const AGENT_DELEGATION_TOKEN_ENV = 'EVOLCORE_DELEGATION_TOKEN';
7
8
  export const CODEX_SESSION_TOKEN_ENV = 'EVOLCORE_CODEX_SESSION_TOKEN';
8
9
  export const AGENT_DELEGATION_COMMAND_TTL_MS = 5 * 60_000;
@@ -279,5 +280,5 @@ export function hashDelegatedCommandArgv(argv) {
279
280
  export function hashCurrentDelegatedCommand(processArgv = process.argv) {
280
281
  if (processArgv.length < 3)
281
282
  return undefined;
282
- return hashDelegatedCommandArgv(['ec', ...processArgv.slice(2)]);
283
+ return hashDelegatedCommandArgv(canonicalizeEvolcoreCommandArgv(['ec', ...processArgv.slice(2)]));
283
284
  }
@@ -76,6 +76,7 @@ export function buildAuthSubject(input) {
76
76
  : roleDetail.allowAccess && checkRoleAccess(role, input.selfAid)),
77
77
  canApprovePersistentFullAccess: input.canApprovePersistentFullAccess
78
78
  ?? (isDaemonOwner && !isDaemonService),
79
+ auditMetadata: input.auditMetadata,
79
80
  permissionMode: isFullAccessRun
80
81
  ? 'fullaccess'
81
82
  : input.permissionMode
@@ -184,9 +185,12 @@ function resolveRoleDetail(input) {
184
185
  });
185
186
  }
186
187
  function auditDecision(params, decision) {
188
+ const roleDenial = !decision.allow && decision.code === 'ROLE_ACCESS_DENIED';
187
189
  auditCommandAuthorization({
188
190
  ts: Date.now(),
189
191
  ...params.auditMetadata,
192
+ ...params.subject.auditMetadata,
193
+ eventPhase: params.auditMetadata?.eventPhase ?? 'final',
190
194
  correlationId: params.auditMetadata?.correlationId ?? params.auditMetadata?.requestId ?? params.subject.requestId,
191
195
  requestId: params.auditMetadata?.requestId ?? params.subject.requestId,
192
196
  sessionId: params.auditMetadata?.sessionId ?? params.subject.sessionId,
@@ -198,13 +202,16 @@ function auditDecision(params, decision) {
198
202
  errorCode: params.auditMetadata?.errorCode
199
203
  ?? (decision.allow
200
204
  ? undefined
201
- : params.auditMetadata?.decisionSource === 'approval'
202
- ? 'USER_DENIED'
203
- : params.auditMetadata?.decisionSource === 'infrastructure'
204
- ? 'EXECUTION_FAILED'
205
- : 'POLICY_DENIED'),
205
+ : roleDenial
206
+ ? 'ROLE_DENIED'
207
+ : params.auditMetadata?.decisionSource === 'approval'
208
+ ? 'USER_DENIED'
209
+ : params.auditMetadata?.decisionSource === 'infrastructure'
210
+ ? 'EXECUTION_FAILED'
211
+ : 'POLICY_DENIED'),
206
212
  policyCode: params.auditMetadata?.policyCode ?? (decision.allow ? undefined : decision.code),
207
- decisionSource: params.auditMetadata?.decisionSource ?? (decision.allow ? 'command' : 'policy'),
213
+ decisionSource: params.auditMetadata?.decisionSource
214
+ ?? (decision.allow ? 'command' : roleDenial ? 'role' : 'policy'),
208
215
  source: params.source,
209
216
  operation: params.intent.operation,
210
217
  scope: params.intent.scope,
@@ -4,6 +4,7 @@ import { resolvePaths } from '../../paths.js';
4
4
  import crypto from 'node:crypto';
5
5
  import { buildAuthorizationEventKey, buildToolLifecycleEventKey, normalizeToolCorrelation } from '../audit/event-key.js';
6
6
  import { normalizeExecutionPermissionMode } from '../permission/mode.js';
7
+ import { normalizeToolErrorClassification } from '../permission/tool-error-code.js';
7
8
  export function auditCommandAuthorization(event) {
8
9
  const shouldAudit = event.source === 'menu.cli' ||
9
10
  event.decision === 'deny' ||
@@ -101,6 +102,13 @@ export function auditFullAccessEvent(input) {
101
102
  * denials otherwise only reach `logger.warn` and `TriggerExecutionAnomaly`.
102
103
  */
103
104
  export function auditToolPreflightDenial(input) {
105
+ const classification = normalizeToolErrorClassification({
106
+ errorCode: 'POLICY_DENIED',
107
+ decisionSource: 'policy',
108
+ policyCode: input.policyCode,
109
+ executed: false,
110
+ executionState: 'blocked',
111
+ });
104
112
  auditCommandAuthorization({
105
113
  ts: Date.now(),
106
114
  eventKey: buildToolLifecycleEventKey(input),
@@ -114,7 +122,8 @@ export function auditToolPreflightDenial(input) {
114
122
  executed: false,
115
123
  executionState: 'blocked',
116
124
  decisionSource: 'policy',
117
- errorCode: 'POLICY_DENIED',
125
+ errorCode: classification.errorCode,
126
+ ...(classification.errorDetailCode ? { errorDetailCode: classification.errorDetailCode } : {}),
118
127
  toolName: input.toolName,
119
128
  policyCode: input.policyCode,
120
129
  protectionClass: input.protectionClass ?? protectionClassForPolicy(input.policyCode),
@@ -158,6 +167,7 @@ export function auditCodexApprovalDecision(input) {
158
167
  errorCode: input.decision === 'deny'
159
168
  ? input.decisionSource === 'approval' ? 'USER_DENIED' : input.decisionSource === 'policy' ? 'POLICY_DENIED' : infrastructureErrorCode(input.policyCode)
160
169
  : undefined,
170
+ ...(input.errorDetailCode ? { errorDetailCode: input.errorDetailCode } : {}),
161
171
  sessionId: input.sessionId ?? 'unknown',
162
172
  agentAid: input.agentAid ?? 'unknown',
163
173
  agentName: input.agentName,
@@ -313,14 +323,25 @@ function infrastructureErrorCode(policyCode) {
313
323
  }
314
324
  function buildAuditRecord(event) {
315
325
  const executed = event.executed ?? false;
316
- const decisionSource = event.decisionSource ?? (event.decision === 'deny' ? 'policy' : event.decision === 'pending' ? 'approval' : 'command');
317
- const errorCode = event.errorCode ?? (event.decision === 'deny'
318
- ? decisionSource === 'approval'
319
- ? 'USER_DENIED'
320
- : decisionSource === 'policy'
321
- ? 'POLICY_DENIED'
322
- : 'EXECUTION_FAILED'
323
- : undefined);
326
+ const roleDenial = event.decision === 'deny'
327
+ && (event.errorCode === 'ROLE_DENIED'
328
+ || event.code === 'ROLE_ACCESS_DENIED'
329
+ || event.policyCode === 'ROLE_ACCESS_DENIED');
330
+ const decisionSource = event.decisionSource
331
+ ?? (roleDenial
332
+ ? 'role'
333
+ : event.decision === 'deny' ? 'policy' : event.decision === 'pending' ? 'approval' : 'command');
334
+ const executionFailed = executed
335
+ && (event.executionState === 'failed' || (event.exitCode !== undefined && event.exitCode !== 0));
336
+ const rawErrorCode = event.errorCode ?? (event.decision === 'deny'
337
+ ? decisionSource === 'role'
338
+ ? 'ROLE_DENIED'
339
+ : decisionSource === 'approval'
340
+ ? 'USER_DENIED'
341
+ : decisionSource === 'policy'
342
+ ? 'POLICY_DENIED'
343
+ : 'EXECUTION_FAILED'
344
+ : executionFailed ? 'EXECUTION_FAILED' : undefined);
324
345
  const executionState = event.executionState
325
346
  ?? (event.decision === 'deny'
326
347
  ? 'blocked'
@@ -329,25 +350,35 @@ function buildAuditRecord(event) {
329
350
  : executed
330
351
  ? (event.exitCode === undefined || event.exitCode === 0 ? 'completed' : 'failed')
331
352
  : 'authorized');
353
+ const classification = rawErrorCode !== undefined
354
+ ? normalizeToolErrorClassification({
355
+ errorCode: rawErrorCode,
356
+ errorDetailCode: event.errorDetailCode,
357
+ decisionSource,
358
+ executed,
359
+ executionState,
360
+ exitCode: event.exitCode,
361
+ policyCode: event.policyCode,
362
+ })
363
+ : undefined;
364
+ const errorCode = classification?.errorCode;
332
365
  const ecwebRequestId = event.source === 'ecweb' ? event.ecwebRequestId : undefined;
333
366
  const requestId = event.requestId ?? ecwebRequestId;
334
367
  const correlationId = event.correlationId ?? event.callId ?? requestId;
335
- const lifecycleId = event.callId ?? correlationId ?? requestId;
336
368
  return {
369
+ type: 'authorization:audit',
337
370
  ts: event.ts,
338
371
  eventKey: event.eventKey ?? (ecwebRequestId
339
372
  ? `ecweb:${crypto.createHash('sha256').update(JSON.stringify([
340
373
  ecwebRequestId, event.targetAgentAid ?? event.selfAid, event.operation,
341
374
  ])).digest('hex').slice(0, 20)}`
342
- : lifecycleId
343
- ? buildAuthorizationEventKey({
344
- sessionId: event.sessionId,
345
- callId: event.callId,
346
- correlationId,
347
- requestId,
348
- operation: event.operation,
349
- })
350
- : undefined),
375
+ : buildAuthorizationEventKey({
376
+ sessionId: event.sessionId,
377
+ callId: event.callId,
378
+ correlationId,
379
+ requestId,
380
+ operation: event.operation,
381
+ })),
351
382
  eventPhase: event.eventPhase,
352
383
  callId: event.callId,
353
384
  correlationId,
@@ -360,6 +391,9 @@ function buildAuditRecord(event) {
360
391
  toolName: event.toolName,
361
392
  approvalMethod: event.approvalMethod,
362
393
  errorCode,
394
+ errorDetailCode: classification?.errorDetailCode,
395
+ classificationMissing: classification?.classificationMissing || event.classificationMissing,
396
+ classificationConflict: classification?.classificationConflict || event.classificationConflict,
363
397
  policyCode: event.policyCode ?? event.code,
364
398
  protectionClass: event.protectionClass,
365
399
  matchedPath: event.matchedPath,
@@ -459,6 +493,7 @@ function logAuditEvent(record) {
459
493
  record.correlationId ? `correlation=${record.correlationId}` : null,
460
494
  record.eventKey ? `eventKey=${record.eventKey}` : null,
461
495
  record.eventPhase ? `eventPhase=${record.eventPhase}` : null,
496
+ record.sessionScope ? `sessionScope=${record.sessionScope}` : null,
462
497
  record.sessionId ? `session=${record.sessionId}` : null,
463
498
  record.agentName ? `agentName=${JSON.stringify(record.agentName)}` : null,
464
499
  record.agentAid ? `agent=${record.agentAid}` : null,
@@ -41,6 +41,10 @@ const DAEMON_SERVICE_OPERATIONS = new Set([
41
41
  'system.restart',
42
42
  'system.upgrade',
43
43
  ]);
44
+ const DAEMON_OWNER_MODEL_OPTION_READ_OPERATIONS = new Set([
45
+ 'model.list',
46
+ 'model.effort.current',
47
+ ]);
44
48
  export function authorizeCommand(ctx) {
45
49
  return authorizeCommandInternal(ctx);
46
50
  }
@@ -186,6 +190,19 @@ function authorizeCommandInternal(ctx, resolvedConfigCommand) {
186
190
  matchedRule: 'daemon-service',
187
191
  };
188
192
  }
193
+ if (ctx.processRole === 'daemon-owner'
194
+ && ctx.fromControlChannel
195
+ && (intent.scope === 'relation' || intent.scope === 'agent')
196
+ && DAEMON_OWNER_MODEL_OPTION_READ_OPERATIONS.has(operation)) {
197
+ return {
198
+ allow: true,
199
+ operation,
200
+ scope: intent.scope,
201
+ role: 'daemon-owner',
202
+ dangerous: opMeta.dangerous,
203
+ matchedRule: 'daemon-owner-model-options-read',
204
+ };
205
+ }
189
206
  if (requiresDaemonOwner(ctx, resolvedConfigCommand)) {
190
207
  if (!hasDaemonOwnerRole(ctx)) {
191
208
  const reasonCode = daemonOwnerDenialReasonCode(ctx);
@@ -53,6 +53,7 @@ const GROUP_MANAGEMENT_OPERATIONS = [
53
53
  ['ec.group.blocklist', 'read', ['agent'], 'Read the group block list'],
54
54
  ['ec.group.report-status', 'write-own', ['relation', 'agent'], 'Report the current Agent group status'],
55
55
  ['ec.group.member-statuses', 'read', ['relation', 'agent'], 'Read reported group member statuses'],
56
+ ['ec.group.mention', 'write-own', ['relation', 'agent'], 'Read or update the group mention mode'],
56
57
  ['ec.group.rules.set', 'write-agent', ['agent'], 'Upload and publish group rules content'],
57
58
  ['ec.group.rules.publish', 'write-agent', ['agent'], 'Publish the current group rules file'],
58
59
  ['ec.group.rules.update', 'write-agent', ['agent'], 'Update group join rules'],
@@ -799,6 +800,14 @@ const OPERATIONS = [
799
800
  description: '查看当前中间活动显示策略',
800
801
  sources: ['menu'],
801
802
  },
803
+ {
804
+ id: 'task.activity.read',
805
+ category: 'read',
806
+ dangerous: false,
807
+ defaultScopes: ['relation'],
808
+ description: '读取当前关系内任务的 Activity 列表和详情',
809
+ sources: ['menu'],
810
+ },
802
811
  {
803
812
  id: 'activity.update',
804
813
  category: 'write-agent',
@@ -1,6 +1,10 @@
1
1
  import crypto from 'crypto';
2
2
  import { CAUSATION_VERSION, MAX_TRIGGER_CAUSATION_DEPTH, } from './types.js';
3
- const ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
3
+ // Trigger identifiers may be daemon-owned and conventionally begin with
4
+ // underscores (for example, `__session-idle-check`). Internal trace/span IDs
5
+ // are still generated with alphabetic prefixes, so accepting `_` here only
6
+ // broadens validation for persisted/propagated trigger and run IDs.
7
+ const ID_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9._:-]{0,127}$/;
4
8
  function newId(prefix) {
5
9
  return `${prefix}_${crypto.randomUUID().replace(/-/g, '')}`;
6
10
  }
@@ -750,8 +750,7 @@ export async function execAgentQuery(args, service, subject) {
750
750
  canConfigure: subject?.ecwebManagement === true
751
751
  && subject?.isDaemonOwner === true
752
752
  && subject.canApprovePersistentFullAccess !== false
753
- && subject.chatType !== 'group'
754
- && fullaccessStatus.hostEnabled,
753
+ && subject.chatType !== 'group',
755
754
  },
756
755
  };
757
756
  // 叠加构建进度(create 受理后、ready 前可见;ready 后文件仍在,可反映软失败 warn)