evolcore 0.0.28 → 0.0.29
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.
- package/CHANGELOG.md +12 -0
- package/bin/ec-safe-output.js +270 -54
- package/dist/agents/baseagent-availability.js +37 -1
- package/dist/agents/baseagent.js +19 -9
- package/dist/agents/claude-runner.js +171 -13
- package/dist/agents/codex/app-server-client.js +6 -2
- package/dist/agents/codex/diagnostics.js +4 -3
- package/dist/agents/codex-runner.js +396 -95
- package/dist/agents/gemini-runner.js +35 -17
- package/dist/aun/group/history.js +130 -90
- package/dist/aun/group/operations.js +1 -3
- package/dist/aun/menu/transport.js +5 -3
- package/dist/aun/msg/managed-operation.js +70 -0
- package/dist/aun/msg/p2p.js +1 -3
- package/dist/aun/msg/upload.js +2 -2
- package/dist/aun/outbox.js +1 -37
- package/dist/channels/aun.js +249 -89
- package/dist/channels/daemon.js +18 -1
- package/dist/channels/feishu.js +1 -2
- package/dist/cli/agent-command.js +23 -3
- package/dist/cli/agent-create.js +68 -2
- package/dist/cli/agent.js +12 -2
- package/dist/cli/aun-commands.js +87 -13
- package/dist/cli/cli-argv.js +26 -0
- package/dist/cli/daemon-commands.js +40 -10
- package/dist/cli/data-command.js +4 -4
- package/dist/cli/handoff-command.js +2 -0
- package/dist/cli/index.js +18 -1
- package/dist/cli/managed-command-guard.js +10 -0
- package/dist/cli/memory-command.js +441 -0
- package/dist/cli/restart-monitor.js +62 -6
- package/dist/cli/skill-command.js +41 -0
- package/dist/cli/task-context.js +2 -23
- package/dist/cli/trigger-command.js +9 -1
- package/dist/cli/watch-msg-diag.js +209 -0
- package/dist/config/config-field-policy.js +52 -3
- package/dist/config/config-manager.js +17 -4
- package/dist/config/contact-book-store.js +2 -2
- package/dist/config/mention-mode.js +7 -2
- package/dist/config/merge.js +28 -9
- package/dist/config/resolved-config-op.js +1 -1
- package/dist/config/runtime-env.js +137 -0
- package/dist/config/schema-registry.js +11 -1
- package/dist/config-store.js +10 -10
- package/dist/core/audit/event-key.js +3 -1
- package/dist/core/audit/log-integrity.js +4 -0
- package/dist/core/auth/agent-delegation.js +2 -1
- package/dist/core/auth/auth-gateway.js +13 -6
- package/dist/core/auth/authorization-audit.js +54 -19
- package/dist/core/auth/operation-authorizer.js +17 -0
- package/dist/core/auth/operation-catalog.js +9 -0
- package/dist/core/causation/context.js +5 -1
- package/dist/core/command/agent-control.js +1 -2
- package/dist/core/command/command-handler.js +51 -23
- package/dist/core/command/memory-slash.js +270 -0
- package/dist/core/command/menu-catalog.js +1 -0
- package/dist/core/command/menu-handler.js +315 -60
- package/dist/core/command/menu-protocol.js +42 -4
- package/dist/core/command/slash-gate.js +10 -10
- package/dist/core/command/slash-handler.js +206 -64
- package/dist/core/data-migration.js +282 -505
- package/dist/core/event-catalog.js +26 -0
- package/dist/core/evolagent-registry.js +6 -0
- package/dist/core/evolagent.js +0 -10
- package/dist/core/handoff/store.js +1 -20
- package/dist/core/inference/text-inference.js +57 -1
- package/dist/core/memory/consolidation-context.js +505 -0
- package/dist/core/memory/consolidation-ops-records.js +124 -0
- package/dist/core/memory/consolidation-trigger.js +394 -0
- package/dist/core/memory/index-manager.js +308 -0
- package/dist/core/memory/launch-consolidation.js +395 -0
- package/dist/core/memory/memory-admin.js +234 -0
- package/dist/core/memory/memory-document.js +100 -0
- package/dist/core/memory/memory-session-policy.js +41 -0
- package/dist/core/memory/memory-signature.js +257 -0
- package/dist/core/memory/memory-writer.js +432 -0
- package/dist/core/memory/staleness.js +117 -0
- package/dist/core/message/auto-mention-gate.js +454 -0
- package/dist/core/message/im-renderer.js +69 -16
- package/dist/core/message/message-bridge.js +287 -21
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +114 -27
- package/dist/core/message/pending-hints.js +329 -161
- package/dist/core/message/response-engine.js +567 -80
- package/dist/core/permission/ec-command-parser.js +1029 -80
- package/dist/core/permission/protected-paths.js +376 -126
- package/dist/core/permission/sandbox-runtime.js +40 -12
- package/dist/core/permission/tool-error-code.js +96 -13
- package/dist/core/permission/tool-policy.js +276 -23
- package/dist/core/role/runtime-policy.js +3 -7
- package/dist/core/session/{runner-task-history.js → runner-event-summary.js} +1 -1
- package/dist/core/session/session-fs-store.js +0 -76
- package/dist/core/session/session-idle-monitor.js +355 -0
- package/dist/core/session/session-manager.js +478 -17
- package/dist/core/session/session-mapper.js +33 -16
- package/dist/core/session/session-renew.js +217 -155
- package/dist/core/session/{session-turns.js → session-transcript.js} +35 -6
- package/dist/core/skill/global-watcher.js +282 -0
- package/dist/core/skill/index-generator.js +84 -0
- package/dist/core/skill/startup.js +73 -0
- package/dist/core/skill/switch-resolver.js +115 -0
- package/dist/core/task/task-activity-store.js +219 -0
- package/dist/eck/kit-renderer.js +30 -3
- package/dist/eck/manifest-engine.js +26 -4
- package/dist/index.js +708 -18
- package/dist/ipc.js +564 -2
- package/dist/paths.js +30 -9
- package/dist/response-system/engines/v1/proactive-flow.js +116 -15
- package/dist/response-system/modes/single-session/index.js +3 -0
- package/dist/trigger/feedback.js +1 -0
- package/dist/trigger/manager.js +0 -10
- package/dist/trigger/scheduler.js +11 -0
- package/dist/trigger/validation.js +22 -0
- package/dist/utils/avatar-upload.js +59 -18
- package/dist/utils/error-dict.json +6 -3
- package/dist/utils/error-utils.js +27 -0
- package/dist/utils/logger.js +32 -10
- package/dist/utils/media-cache.js +84 -0
- package/dist/utils/msg-diag.js +97 -0
- package/dist/utils/welcome.js +37 -8
- package/kits/docs/AUTHORING.md +314 -0
- package/kits/docs/GUIDE.md +2 -0
- package/kits/docs/INDEX.md +2 -0
- package/kits/docs/aun/CHEATSHEET.md +4 -0
- package/kits/docs/aun/SDK.md +60 -0
- package/kits/docs/context-assembly.md +10 -6
- package/kits/docs/evolcore/INDEX.md +17 -3
- package/kits/docs/evolcore/agent-create.md +24 -0
- package/kits/docs/evolcore/agent.md +5 -2
- package/kits/docs/evolcore/config.md +1 -1
- package/kits/docs/evolcore/ec-capabilities.md +364 -0
- package/kits/docs/evolcore/group.md +34 -18
- package/kits/docs/evolcore/memory-architecture.md +78 -0
- package/kits/docs/evolcore/memory-consolidation-batches.md +133 -0
- package/kits/docs/evolcore/memory-consolidation.md +263 -0
- package/kits/docs/evolcore/memory-tags.md +63 -0
- package/kits/docs/evolcore/memory.md +110 -0
- package/kits/docs/evolcore/menu-client.md +27 -11
- package/kits/{schemas → docs/evolcore}/menu-exec-schema-commands.md +12 -6
- package/kits/docs/evolcore/menu-protocol.md +213 -0
- package/kits/docs/evolcore/msg.md +2 -0
- package/kits/docs/evolcore/trigger.md +7 -5
- package/kits/docs/path-registry.md +6 -8
- package/kits/docs/prompt-loading-architecture.md +5 -3
- package/kits/docs/venues/group.md +5 -4
- package/kits/eck_manifest.json +294 -88
- package/kits/eck_manifest.memory.json +257 -0
- package/kits/rules/03-identity.md +8 -8
- package/kits/rules/05-venue.md +4 -2
- package/kits/rules/06-channel.md +1 -1
- package/kits/schemas/_meta.json +276 -47
- package/kits/schemas/agent-config.schema.12.json +38 -2
- package/kits/schemas/agent-config.schema.13.json +591 -0
- package/kits/schemas/daemon.schema.6.json +9 -9
- package/kits/schemas/defaults.schema.5.json +29 -0
- package/kits/schemas/relation-config.schema.8.json +14 -1
- package/kits/schemas/relation-config.schema.9.json +129 -0
- package/kits/schemas/role-config.schema.1.json +3 -3
- package/kits/skills/.switches.json +3 -0
- package/kits/skills/ec-skill/SKILL.md +217 -0
- package/kits/skills/ec-skill/scripts/cli.mjs +128 -0
- package/kits/skills/ec-skill/scripts/deps.mjs +107 -0
- package/kits/skills/ec-skill/scripts/frontmatter.mjs +104 -0
- package/kits/skills/ec-skill/scripts/skill.mjs +629 -0
- package/kits/skills/ec-skill/scripts/template/SKILL.md.tmpl +30 -0
- package/kits/templates/decision/auto-mention.md +25 -0
- package/kits/templates/decision/session-renew.md +22 -0
- package/kits/templates/memory-consolidation/forget.md +104 -0
- package/kits/templates/memory-consolidation/generalize.md +76 -0
- package/kits/templates/memory-consolidation/notes.md +133 -0
- package/kits/templates/memory-consolidation/profile.md +58 -0
- package/kits/templates/memory-consolidation/promote.md +72 -0
- package/kits/templates/memory-consolidation/workflow.md +75 -0
- package/kits/templates/system-fragments/bootstrap.md +14 -5
- package/kits/templates/system-fragments/commands.md +1 -0
- package/kits/templates/system-fragments/group-history.md +6 -0
- package/kits/templates/system-fragments/identity.md +0 -3
- package/kits/templates/system-fragments/memory.md +79 -0
- package/kits/templates/system-fragments/reply.md +1 -0
- package/kits/templates/system-fragments/venue.md +1 -1
- package/package.json +4 -6
- package/dist/trigger/legacy-session-history.js +0 -19
- 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;
|
package/dist/config-store.js
CHANGED
|
@@ -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
|
|
61
|
-
return config.
|
|
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.
|
|
95
|
-
if (!value.
|
|
96
|
-
throw new Error('daemon.json.
|
|
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
|
|
99
|
-
if (
|
|
100
|
-
throw new Error(
|
|
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.
|
|
103
|
-
throw new Error('daemon.json.
|
|
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
|
-
|
|
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
|
-
:
|
|
202
|
-
? '
|
|
203
|
-
: params.auditMetadata?.decisionSource === '
|
|
204
|
-
? '
|
|
205
|
-
: '
|
|
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
|
|
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:
|
|
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
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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
|
-
:
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
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)
|