evolcore 0.0.27 → 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.
- package/CHANGELOG.md +38 -15
- package/bin/ec-safe-output.js +270 -54
- package/dist/agents/baseagent-availability.js +37 -1
- package/dist/agents/baseagent.js +29 -14
- package/dist/agents/claude-runner.js +171 -13
- package/dist/agents/codex/app-server-client.js +13 -2
- package/dist/agents/codex/catalog.js +7 -1
- package/dist/agents/codex/diagnostics.js +4 -3
- package/dist/agents/codex-runner.js +444 -106
- 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/client.js +1 -0
- 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 +262 -86
- 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 +116 -149
- package/dist/cli/data-command.js +4 -4
- package/dist/cli/diag.js +1 -1
- 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/menu-client.js +4 -0
- package/dist/cli/restart-monitor.js +127 -23
- 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 +80 -24
- package/dist/core/command/diag-service.js +292 -27
- 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 +331 -79
- package/dist/core/command/menu-protocol.js +45 -4
- package/dist/core/command/slash-gate.js +10 -10
- package/dist/core/command/slash-handler.js +230 -79
- 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 +90 -20
- package/dist/core/message/message-bridge.js +333 -23
- 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 +596 -90
- package/dist/core/permission/ec-command-parser.js +1044 -87
- 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 +277 -24
- 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 +482 -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 +722 -171
- package/dist/ipc.js +564 -2
- package/dist/paths.js +30 -9
- package/dist/product.js +1 -0
- 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/atomic-write.js +98 -0
- package/dist/utils/avatar-upload.js +59 -18
- package/dist/utils/cross-platform.js +87 -16
- 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/npm-ops.js +119 -4
- package/dist/utils/package-version-info.js +244 -0
- package/dist/utils/welcome.js +37 -8
- package/dist/utils/windows-shell-trust.js +37 -0
- 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/channels/aun.md +8 -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
|
@@ -6,6 +6,7 @@ import { promisify } from 'util';
|
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import { getProcessStartTime, parseCimDate } from './process-introspect.js';
|
|
8
8
|
import { decodeWindowsOutput } from './windows-output.js';
|
|
9
|
+
import { resolveTrustedWindowsCmdPath } from './windows-shell-trust.js';
|
|
9
10
|
const execFileAsync = promisify(execFile);
|
|
10
11
|
const CLI_PROBE_FAILURE_BACKOFF_MS = 5_000;
|
|
11
12
|
const CLI_PROBE_PACKAGES = {
|
|
@@ -17,9 +18,70 @@ const cliPathCache = new Map();
|
|
|
17
18
|
const cliInstallationCache = new Map();
|
|
18
19
|
const cliProbeCache = new Map();
|
|
19
20
|
export const isWindows = process.platform === 'win32';
|
|
21
|
+
// Keep this in sync with the Windows command-line metacharacters. The whole
|
|
22
|
+
// `/c` expression is passed as one verbatim argument so paths with spaces and
|
|
23
|
+
// shim-forwarded values such as `%`, `!`, `&`, and quotes stay data.
|
|
24
|
+
const WINDOWS_CMD_META_RE = /([()[\]%!^"`<>&|;, *?])/g;
|
|
25
|
+
function escapeWindowsCmdCommand(value) {
|
|
26
|
+
return value.replace(WINDOWS_CMD_META_RE, '^$1');
|
|
27
|
+
}
|
|
28
|
+
function escapeWindowsCmdArgument(value) {
|
|
29
|
+
let escaped = String(value);
|
|
30
|
+
escaped = escaped.replace(/(?=(\\+?)?)\1"/g, '$1$1\\"');
|
|
31
|
+
escaped = escaped.replace(/(?=(\\+?)?)\1$/g, '$1$1');
|
|
32
|
+
escaped = `"${escaped}"`;
|
|
33
|
+
return escaped.replace(WINDOWS_CMD_META_RE, '^$1');
|
|
34
|
+
}
|
|
35
|
+
/** Build one argv-based CLI launch without consulting the parent terminal. */
|
|
36
|
+
export function buildCliLaunchCommand(executable, args, options = {}) {
|
|
37
|
+
const windows = options.isWindows ?? isWindows;
|
|
38
|
+
const extension = windows ? path.win32.extname(executable).toLowerCase() : '';
|
|
39
|
+
const shim = extension === '.cmd' || extension === '.bat';
|
|
40
|
+
if (!shim) {
|
|
41
|
+
return {
|
|
42
|
+
command: executable,
|
|
43
|
+
args,
|
|
44
|
+
resolvedPath: executable,
|
|
45
|
+
launcherKind: 'native-exe',
|
|
46
|
+
launcherPath: executable,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const resolveWindowsCmd = options.resolveWindowsCmd ?? resolveTrustedWindowsCmdPath;
|
|
50
|
+
const launcherPath = resolveWindowsCmd(options.comSpec);
|
|
51
|
+
if (!launcherPath) {
|
|
52
|
+
throw Object.assign(new Error('未找到受信任的系统 cmd.exe,已拒绝启动 Windows CLI shim'), {
|
|
53
|
+
code: 'EACCES',
|
|
54
|
+
path: executable,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
const shellCommand = [
|
|
58
|
+
escapeWindowsCmdCommand(executable),
|
|
59
|
+
...args.map(escapeWindowsCmdArgument),
|
|
60
|
+
].join(' ');
|
|
61
|
+
return {
|
|
62
|
+
command: launcherPath,
|
|
63
|
+
args: ['/d', '/s', '/v:off', '/c', `"${shellCommand}"`],
|
|
64
|
+
resolvedPath: executable,
|
|
65
|
+
launcherKind: extension === '.cmd' ? 'cmd-shim' : 'bat-shim',
|
|
66
|
+
launcherPath,
|
|
67
|
+
windowsVerbatimArguments: true,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
20
70
|
/** Resolve a CLI executable without spawning where/which. */
|
|
21
71
|
export function resolveProbePath(command) {
|
|
22
|
-
const
|
|
72
|
+
const extensions = isWindows
|
|
73
|
+
? (process.env.PATHEXT ?? '.COM;.EXE;.BAT;.CMD').split(';').filter(ext => /\.(com|exe|bat|cmd)$/i.test(ext))
|
|
74
|
+
: [''];
|
|
75
|
+
return resolveProbePathWithExtensions(command, extensions, 'executable');
|
|
76
|
+
}
|
|
77
|
+
/** Diagnose an incomplete npm installation without ever executing its PS1 shim. */
|
|
78
|
+
export function resolvePowerShellShimPath(command) {
|
|
79
|
+
if (!isWindows)
|
|
80
|
+
return null;
|
|
81
|
+
return resolveProbePathWithExtensions(command, ['.ps1'], 'powershell-shim');
|
|
82
|
+
}
|
|
83
|
+
function resolveProbePathWithExtensions(command, extensions, cacheKind) {
|
|
84
|
+
const pathKey = `${cacheKind}\0${command}\0${process.cwd()}\0${process.env.PATH ?? ''}\0${extensions.join(';')}`;
|
|
23
85
|
const cachedPath = cliPathCache.get(pathKey);
|
|
24
86
|
if (cachedPath && fs.existsSync(cachedPath))
|
|
25
87
|
return cachedPath;
|
|
@@ -27,9 +89,6 @@ export function resolveProbePath(command) {
|
|
|
27
89
|
const directories = (process.env.PATH ?? '').split(path.delimiter);
|
|
28
90
|
if (isWindows)
|
|
29
91
|
directories.unshift(process.cwd());
|
|
30
|
-
const extensions = isWindows
|
|
31
|
-
? (process.env.PATHEXT ?? '.COM;.EXE;.BAT;.CMD').split(';').filter(ext => /\.(com|exe|bat|cmd)$/i.test(ext))
|
|
32
|
-
: [''];
|
|
33
92
|
for (const directory of directories) {
|
|
34
93
|
for (const extension of extensions) {
|
|
35
94
|
const candidate = path.resolve(directory.replace(/^"|"$/g, ''), command + extension);
|
|
@@ -98,20 +157,25 @@ export function invalidateCliProbes() {
|
|
|
98
157
|
function cliProbeTimeouts(command, options) {
|
|
99
158
|
return options.timeouts?.length ? options.timeouts : command === 'codex' ? [3_000, 15_000] : [3_000];
|
|
100
159
|
}
|
|
101
|
-
function prepareCliProbe(command, args) {
|
|
102
|
-
const executable = resolveProbePath(command);
|
|
160
|
+
function prepareCliProbe(command, args, options = {}) {
|
|
161
|
+
const executable = options.executable ?? resolveProbePath(command);
|
|
103
162
|
if (!executable)
|
|
104
163
|
throw Object.assign(new Error(`${command} CLI not found`), { code: 'ENOENT' });
|
|
105
|
-
const
|
|
164
|
+
const launchCommand = buildCliLaunchCommand(executable, args);
|
|
165
|
+
const launch = launchCommand;
|
|
166
|
+
const identity = `${probeInstallationIdentity(command, executable)}|${cliFileIdentity(launch.launcherPath)}`;
|
|
106
167
|
const key = JSON.stringify([command, args]);
|
|
107
168
|
let entry = cliProbeCache.get(key);
|
|
108
169
|
if (!entry || entry.identity !== identity || (entry.retryAt !== undefined && Date.now() >= entry.retryAt)) {
|
|
109
170
|
entry = { identity };
|
|
110
171
|
cliProbeCache.set(key, entry);
|
|
111
172
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
173
|
+
return {
|
|
174
|
+
entry,
|
|
175
|
+
command: launchCommand.command,
|
|
176
|
+
args: launchCommand.args,
|
|
177
|
+
launch,
|
|
178
|
+
};
|
|
115
179
|
}
|
|
116
180
|
function failCliProbe(entry, error) {
|
|
117
181
|
entry.error = error;
|
|
@@ -119,7 +183,7 @@ function failCliProbe(entry, error) {
|
|
|
119
183
|
throw error;
|
|
120
184
|
}
|
|
121
185
|
export function probeCliSync(command, args, options = {}) {
|
|
122
|
-
const request = prepareCliProbe(command, args);
|
|
186
|
+
const request = prepareCliProbe(command, args, options);
|
|
123
187
|
if (request.entry.output !== undefined)
|
|
124
188
|
return request.entry.output;
|
|
125
189
|
if (request.entry.error !== undefined)
|
|
@@ -127,10 +191,14 @@ export function probeCliSync(command, args, options = {}) {
|
|
|
127
191
|
for (const [index, timeout] of cliProbeTimeouts(command, options).entries()) {
|
|
128
192
|
const startedAt = Date.now();
|
|
129
193
|
try {
|
|
130
|
-
return request.entry.output = execFileSync(request.command, request.args, {
|
|
194
|
+
return request.entry.output = execFileSync(request.command, request.args, {
|
|
195
|
+
encoding: 'utf8', timeout, windowsHide: true,
|
|
196
|
+
windowsVerbatimArguments: request.launch.windowsVerbatimArguments,
|
|
197
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
198
|
+
});
|
|
131
199
|
}
|
|
132
200
|
catch (error) {
|
|
133
|
-
options.onFailure?.(args, startedAt, error, timeout);
|
|
201
|
+
options.onFailure?.(args, startedAt, error, timeout, request.launch);
|
|
134
202
|
if (error.code !== 'ETIMEDOUT' || index === cliProbeTimeouts(command, options).length - 1)
|
|
135
203
|
return failCliProbe(request.entry, error);
|
|
136
204
|
}
|
|
@@ -138,7 +206,7 @@ export function probeCliSync(command, args, options = {}) {
|
|
|
138
206
|
throw new Error('CLI probe exhausted');
|
|
139
207
|
}
|
|
140
208
|
export async function probeCli(command, args, options = {}) {
|
|
141
|
-
const request = prepareCliProbe(command, args);
|
|
209
|
+
const request = prepareCliProbe(command, args, options);
|
|
142
210
|
if (request.entry.output !== undefined)
|
|
143
211
|
return request.entry.output;
|
|
144
212
|
if (request.entry.error !== undefined)
|
|
@@ -151,7 +219,10 @@ export async function probeCli(command, args, options = {}) {
|
|
|
151
219
|
const startedAt = Date.now();
|
|
152
220
|
try {
|
|
153
221
|
const output = await new Promise((resolve, reject) => {
|
|
154
|
-
const child = execFile(request.command, request.args, {
|
|
222
|
+
const child = execFile(request.command, request.args, {
|
|
223
|
+
encoding: 'utf8', timeout, windowsHide: true,
|
|
224
|
+
windowsVerbatimArguments: request.launch.windowsVerbatimArguments,
|
|
225
|
+
}, (error, stdout, stderr) => {
|
|
155
226
|
if (error) {
|
|
156
227
|
if (error.killed && error.signal === 'SIGTERM' && error.code == null)
|
|
157
228
|
error.code = 'ETIMEDOUT';
|
|
@@ -166,7 +237,7 @@ export async function probeCli(command, args, options = {}) {
|
|
|
166
237
|
return request.entry.output = output;
|
|
167
238
|
}
|
|
168
239
|
catch (error) {
|
|
169
|
-
options.onFailure?.(args, startedAt, error, timeout);
|
|
240
|
+
options.onFailure?.(args, startedAt, error, timeout, request.launch);
|
|
170
241
|
if (error.code !== 'ETIMEDOUT' || index === timeouts.length - 1)
|
|
171
242
|
return failCliProbe(request.entry, error);
|
|
172
243
|
}
|
|
@@ -109,19 +109,22 @@
|
|
|
109
109
|
"id": "api-error-502",
|
|
110
110
|
"match": "api error: 502",
|
|
111
111
|
"action": "retry",
|
|
112
|
-
"type": "api_error"
|
|
112
|
+
"type": "api_error",
|
|
113
|
+
"message": "❌ API 网关异常,请稍后重试"
|
|
113
114
|
},
|
|
114
115
|
{
|
|
115
116
|
"id": "api-error-503",
|
|
116
117
|
"match": "api error: 503",
|
|
117
118
|
"action": "retry",
|
|
118
|
-
"type": "api_error"
|
|
119
|
+
"type": "api_error",
|
|
120
|
+
"message": "❌ API 服务暂时不可用,请稍后重试"
|
|
119
121
|
},
|
|
120
122
|
{
|
|
121
123
|
"id": "api-error-504",
|
|
122
124
|
"match": "api error: 504",
|
|
123
125
|
"action": "retry",
|
|
124
|
-
"type": "api_error"
|
|
126
|
+
"type": "api_error",
|
|
127
|
+
"message": "❌ API 请求超时,请稍后重试"
|
|
125
128
|
},
|
|
126
129
|
{
|
|
127
130
|
"id": "not-valid-json",
|
|
@@ -25,6 +25,7 @@ export function isExecutionFailure(error) {
|
|
|
25
25
|
return error instanceof ExecutionTimeoutError || error instanceof OrphanedTargetSessionError;
|
|
26
26
|
}
|
|
27
27
|
export const CLAUDE_STARTUP_ARGUMENTS_TOO_LONG = 'claude_startup_arguments_too_long';
|
|
28
|
+
export const CLAUDE_CODE_STARTUP_FAILED = 'claude_code_startup_failed';
|
|
28
29
|
/** A completed provider response exhausted its output budget, rather than a transport failure. */
|
|
29
30
|
export function isOutputTokenLimitError(error) {
|
|
30
31
|
const record = error && typeof error === 'object' ? error : undefined;
|
|
@@ -58,6 +59,22 @@ export function createClaudeStartupArgumentsTooLongError(error) {
|
|
|
58
59
|
wrapped.cause = error;
|
|
59
60
|
return wrapped;
|
|
60
61
|
}
|
|
62
|
+
export function isClaudeCodeStartupProcessError(error) {
|
|
63
|
+
const record = error && typeof error === 'object' ? error : undefined;
|
|
64
|
+
const code = String(record?.code ?? '').toLowerCase();
|
|
65
|
+
const message = String(record?.message ?? error ?? '').toLowerCase();
|
|
66
|
+
return code === 'eftype'
|
|
67
|
+
|| message.includes('spawn eftype')
|
|
68
|
+
|| message.includes('not a valid application for this os platform');
|
|
69
|
+
}
|
|
70
|
+
export function createClaudeCodeStartupError(error) {
|
|
71
|
+
const wrapped = new Error(`${CLAUDE_CODE_STARTUP_FAILED}: Claude Code SDK 本地运行时启动失败,任务未执行`);
|
|
72
|
+
wrapped.name = 'ClaudeCodeStartupError';
|
|
73
|
+
wrapped.code = CLAUDE_CODE_STARTUP_FAILED;
|
|
74
|
+
if (error !== undefined)
|
|
75
|
+
wrapped.cause = error;
|
|
76
|
+
return wrapped;
|
|
77
|
+
}
|
|
61
78
|
export var ErrorType;
|
|
62
79
|
(function (ErrorType) {
|
|
63
80
|
ErrorType["SDK_TIMEOUT"] = "sdk_timeout";
|
|
@@ -71,6 +88,7 @@ export var ErrorType;
|
|
|
71
88
|
ErrorType["SESSION_UNAVAILABLE"] = "session_unavailable";
|
|
72
89
|
ErrorType["SANDBOX_INITIALIZATION_FAILED"] = "sandbox_initialization_failed";
|
|
73
90
|
ErrorType["CLAUDE_STARTUP_ARGUMENTS_TOO_LONG"] = "claude_startup_arguments_too_long";
|
|
91
|
+
ErrorType["CLAUDE_CODE_STARTUP_FAILED"] = "claude_code_startup_failed";
|
|
74
92
|
ErrorType["UNKNOWN"] = "unknown";
|
|
75
93
|
})(ErrorType || (ErrorType = {}));
|
|
76
94
|
/**
|
|
@@ -329,6 +347,9 @@ export function classifyError(error) {
|
|
|
329
347
|
if (isClaudeStartupArgumentsTooLong(error)) {
|
|
330
348
|
return ErrorType.CLAUDE_STARTUP_ARGUMENTS_TOO_LONG;
|
|
331
349
|
}
|
|
350
|
+
if (error?.code === CLAUDE_CODE_STARTUP_FAILED) {
|
|
351
|
+
return ErrorType.CLAUDE_CODE_STARTUP_FAILED;
|
|
352
|
+
}
|
|
332
353
|
// 字典优先 — 命中则直接返回
|
|
333
354
|
const rule = matchErrorRule(msg);
|
|
334
355
|
if (rule) {
|
|
@@ -410,6 +431,10 @@ export function getErrorMessage(error, terminalReason, includeEmoji = true) {
|
|
|
410
431
|
const prefix = includeEmoji ? '⚠️ ' : '';
|
|
411
432
|
return `${prefix}当前 Claude 启动参数过长,任务未执行`;
|
|
412
433
|
}
|
|
434
|
+
if (error?.code === CLAUDE_CODE_STARTUP_FAILED) {
|
|
435
|
+
const prefix = includeEmoji ? '❌ ' : '';
|
|
436
|
+
return `${prefix}Claude Code SDK 本地运行时启动失败(${CLAUDE_CODE_STARTUP_FAILED}),当前任务未执行。请重新安装并对齐 Claude Code SDK 与 Windows 原生组件版本,然后重启 EvolCore 后重试`;
|
|
437
|
+
}
|
|
413
438
|
// terminalReason 提供更精确的错误提示(SDK 0.2.100+)
|
|
414
439
|
if (terminalReason) {
|
|
415
440
|
const prefix = includeEmoji ? '❌ ' : '';
|
|
@@ -433,6 +458,8 @@ export function getErrorMessage(error, terminalReason, includeEmoji = true) {
|
|
|
433
458
|
return `${warnPrefix}执行沙箱启动失败(sandbox_initialization_failed),当前操作未执行;请检查文件系统挂载规则及 user namespace/seccomp 配置`;
|
|
434
459
|
case 'claude_startup_arguments_too_long':
|
|
435
460
|
return `${warnPrefix}当前 Claude 启动参数过长,任务未执行`;
|
|
461
|
+
case 'claude_code_startup_failed':
|
|
462
|
+
return `${prefix}Claude Code SDK 本地运行时启动失败(${CLAUDE_CODE_STARTUP_FAILED}),当前任务未执行。请重新安装并对齐 Claude Code SDK 与 Windows 原生组件版本,然后重启 EvolCore 后重试`;
|
|
436
463
|
case 'aborted_streaming':
|
|
437
464
|
case 'aborted_tools':
|
|
438
465
|
return `${prefix}任务已中断`;
|
package/dist/utils/logger.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import { resolvePaths } from '../paths.js';
|
|
3
3
|
import { LogWriter } from './log-writer.js';
|
|
4
|
-
import {
|
|
4
|
+
import { normalizeToolErrorClassification } from '../core/permission/tool-error-code.js';
|
|
5
5
|
import { normalizePermissionMode } from '../core/permission/mode.js';
|
|
6
6
|
import { buildToolLifecycleEventKey, normalizeToolCorrelation } from '../core/audit/event-key.js';
|
|
7
7
|
let currentLevel = process.env.LOG_LEVEL || 'INFO';
|
|
@@ -114,21 +114,40 @@ export function normalizeStructuredLog(data) {
|
|
|
114
114
|
?? (isToolResult && isError === true ? 'error' : undefined)
|
|
115
115
|
?? (isToolResult && isError === false ? 'allow' : undefined)
|
|
116
116
|
?? (isToolUse ? 'pending' : undefined);
|
|
117
|
-
const
|
|
117
|
+
const explicitExecuted = data.executed ?? nested?.executed;
|
|
118
|
+
const explicitExecutionState = data.executionState ?? data.execution_state
|
|
119
|
+
?? nested?.executionState ?? nested?.execution_state;
|
|
120
|
+
const executed = explicitExecuted
|
|
118
121
|
?? (isToolResult ? true : isToolUse ? false : undefined);
|
|
119
|
-
const executionState =
|
|
120
|
-
?? nested?.executionState ?? nested?.execution_state
|
|
122
|
+
const executionState = explicitExecutionState
|
|
121
123
|
?? (isToolResult ? (isError === true ? 'failed' : 'completed') : isToolUse ? 'requested' : undefined);
|
|
124
|
+
const exitCode = data.exitCode ?? data.exit_code ?? nested?.exitCode ?? nested?.exit_code;
|
|
122
125
|
const explicitErrorCode = data.errorCode
|
|
123
126
|
?? data.error_code
|
|
124
127
|
?? nested?.errorCode
|
|
125
128
|
?? nested?.error_code;
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
const explicitErrorDetailCode = data.errorDetailCode
|
|
130
|
+
?? data.detailCode
|
|
131
|
+
?? data.detail_code
|
|
132
|
+
?? nested?.errorDetailCode
|
|
133
|
+
?? nested?.detailCode
|
|
134
|
+
?? nested?.detail_code;
|
|
135
|
+
const classification = isToolResult && isError === true
|
|
136
|
+
? normalizeToolErrorClassification({
|
|
137
|
+
errorCode: explicitErrorCode,
|
|
138
|
+
errorDetailCode: explicitErrorDetailCode,
|
|
139
|
+
decisionSource,
|
|
140
|
+
executed: explicitExecuted,
|
|
141
|
+
executionState: explicitExecutionState,
|
|
142
|
+
exitCode,
|
|
143
|
+
policyCode: data.policyCode ?? nested?.policyCode,
|
|
144
|
+
})
|
|
145
|
+
: undefined;
|
|
146
|
+
const errorCode = classification?.errorCode ?? explicitErrorCode;
|
|
147
|
+
const errorDetailCode = classification?.errorDetailCode
|
|
148
|
+
?? (typeof explicitErrorDetailCode === 'string' ? explicitErrorDetailCode : undefined);
|
|
149
|
+
const classificationMissing = classification?.classificationMissing || data.classificationMissing === true || nested?.classificationMissing === true;
|
|
150
|
+
const classificationConflict = classification?.classificationConflict || data.classificationConflict === true || nested?.classificationConflict === true;
|
|
132
151
|
const lifecycleEventKey = lifecycleRecord
|
|
133
152
|
? (eventKey ?? buildToolLifecycleEventKey({
|
|
134
153
|
sessionId,
|
|
@@ -155,7 +174,10 @@ export function normalizeStructuredLog(data) {
|
|
|
155
174
|
...(executed !== undefined ? { executed } : {}),
|
|
156
175
|
...(executionState ? { executionState } : {}),
|
|
157
176
|
...(errorCode ? { errorCode } : {}),
|
|
177
|
+
...(errorDetailCode ? { errorDetailCode } : {}),
|
|
178
|
+
...(exitCode !== undefined ? { exitCode } : {}),
|
|
158
179
|
...(classificationMissing ? { classificationMissing: true } : {}),
|
|
180
|
+
...(classificationConflict ? { classificationConflict: true } : {}),
|
|
159
181
|
...(missingContextFields.length > 0 ? { contextMissing: missingContextFields } : {}),
|
|
160
182
|
};
|
|
161
183
|
}
|
|
@@ -11,6 +11,9 @@ import path from 'path';
|
|
|
11
11
|
import fs from 'fs';
|
|
12
12
|
import crypto from 'crypto';
|
|
13
13
|
import { logger } from './logger.js';
|
|
14
|
+
import { agentRelationDir } from '../paths.js';
|
|
15
|
+
import { parsePeerKey } from '../core/relation/peer-identity.js';
|
|
16
|
+
import { isSameOrDescendant } from '../core/permission/protected-paths.js';
|
|
14
17
|
// ── 常量 ──────────────────────────────────────────────────────────────────────
|
|
15
18
|
const DEFAULT_MAX_IMAGE_SIZE = 10 * 1024 * 1024; // 10 MB
|
|
16
19
|
const DEFAULT_MAX_FILE_SIZE = 100 * 1024 * 1024; // 100 MB
|
|
@@ -18,6 +21,14 @@ const ALLOWED_IMAGE_MIMES = new Set([
|
|
|
18
21
|
'image/png', 'image/jpeg', 'image/gif', 'image/webp',
|
|
19
22
|
]);
|
|
20
23
|
const UPLOADS_SUBDIR = '.evolcore/uploads';
|
|
24
|
+
const RELATION_ATTACHMENT_ID_RE = /^[a-f0-9]{32}$/;
|
|
25
|
+
const RELATION_ATTACHMENT_MAX_BYTES = DEFAULT_MAX_FILE_SIZE;
|
|
26
|
+
const RELATION_ATTACHMENT_EXTENSIONS = {
|
|
27
|
+
'image/png': '.png',
|
|
28
|
+
'image/jpeg': '.jpg',
|
|
29
|
+
'image/gif': '.gif',
|
|
30
|
+
'image/webp': '.webp',
|
|
31
|
+
};
|
|
21
32
|
// ── SSRF 防护 ─────────────────────────────────────────────────────────────────
|
|
22
33
|
/** 已知安全的 CDN 域名白名单 */
|
|
23
34
|
const ALLOWED_CDN_HOSTS = new Set([
|
|
@@ -101,6 +112,79 @@ export async function validateImage(buffer, opts) {
|
|
|
101
112
|
}
|
|
102
113
|
return { mime: type.mime };
|
|
103
114
|
}
|
|
115
|
+
function relationAttachmentsDir(selfAid, peerKey) {
|
|
116
|
+
parsePeerKey(peerKey);
|
|
117
|
+
return path.join(agentRelationDir(selfAid, peerKey), 'attachments');
|
|
118
|
+
}
|
|
119
|
+
/** Save one inbound image as a stable, peer-scoped attachment owned by the daemon. */
|
|
120
|
+
export function saveRelationAttachment(buffer, selfAid, peerKey, mimeType) {
|
|
121
|
+
const extension = RELATION_ATTACHMENT_EXTENSIONS[mimeType];
|
|
122
|
+
if (!extension)
|
|
123
|
+
throw new Error(`Unsupported relation attachment MIME type: ${mimeType}`);
|
|
124
|
+
if (buffer.length < 1 || buffer.length > RELATION_ATTACHMENT_MAX_BYTES) {
|
|
125
|
+
throw new Error(`Relation attachment must be between 1 byte and ${RELATION_ATTACHMENT_MAX_BYTES} bytes`);
|
|
126
|
+
}
|
|
127
|
+
const directory = relationAttachmentsDir(selfAid, peerKey);
|
|
128
|
+
fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
129
|
+
if (process.platform !== 'win32')
|
|
130
|
+
fs.chmodSync(directory, 0o700);
|
|
131
|
+
const attachmentId = crypto.randomBytes(16).toString('hex');
|
|
132
|
+
const filename = `${attachmentId}${extension}`;
|
|
133
|
+
const filePath = path.join(directory, filename);
|
|
134
|
+
const metadataPath = path.join(directory, `${attachmentId}.json`);
|
|
135
|
+
const sha256 = crypto.createHash('sha256').update(buffer).digest('hex');
|
|
136
|
+
const metadata = {
|
|
137
|
+
version: 1, attachmentId, selfAid, peerKey, filename, mimeType,
|
|
138
|
+
sha256, size: buffer.length, createdAt: Date.now(),
|
|
139
|
+
};
|
|
140
|
+
fs.writeFileSync(filePath, buffer, { flag: 'wx', mode: 0o600 });
|
|
141
|
+
fs.writeFileSync(metadataPath, JSON.stringify(metadata), { flag: 'wx', mode: 0o600 });
|
|
142
|
+
return { ...metadata, filePath };
|
|
143
|
+
}
|
|
144
|
+
/** Resolve an attachment only inside the authenticated Agent/peer relation. */
|
|
145
|
+
export function readRelationAttachment(selfAid, peerKey, attachmentSource) {
|
|
146
|
+
const attachmentId = RELATION_ATTACHMENT_ID_RE.test(attachmentSource)
|
|
147
|
+
? attachmentSource
|
|
148
|
+
: path.basename(attachmentSource, path.extname(attachmentSource));
|
|
149
|
+
if (!RELATION_ATTACHMENT_ID_RE.test(attachmentId))
|
|
150
|
+
throw new Error('Invalid attachment ID');
|
|
151
|
+
const directory = relationAttachmentsDir(selfAid, peerKey);
|
|
152
|
+
const metadataPath = path.join(directory, `${attachmentId}.json`);
|
|
153
|
+
const metadataStat = fs.lstatSync(metadataPath);
|
|
154
|
+
if (!metadataStat.isFile() || metadataStat.isSymbolicLink())
|
|
155
|
+
throw new Error('Attachment metadata must be a regular file');
|
|
156
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
|
|
157
|
+
if (metadata.version !== 1 || metadata.attachmentId !== attachmentId
|
|
158
|
+
|| metadata.selfAid !== selfAid || metadata.peerKey !== peerKey
|
|
159
|
+
|| RELATION_ATTACHMENT_EXTENSIONS[metadata.mimeType] === undefined
|
|
160
|
+
|| metadata.filename !== `${attachmentId}${RELATION_ATTACHMENT_EXTENSIONS[metadata.mimeType]}`) {
|
|
161
|
+
throw new Error('Attachment ownership metadata mismatch');
|
|
162
|
+
}
|
|
163
|
+
const filePath = path.join(directory, metadata.filename);
|
|
164
|
+
const stat = fs.lstatSync(filePath);
|
|
165
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size !== metadata.size
|
|
166
|
+
|| stat.size < 1 || stat.size > RELATION_ATTACHMENT_MAX_BYTES) {
|
|
167
|
+
throw new Error('Attachment file is missing or invalid');
|
|
168
|
+
}
|
|
169
|
+
const realDirectory = fs.realpathSync(directory);
|
|
170
|
+
const realPath = fs.realpathSync(filePath);
|
|
171
|
+
if (!isSameOrDescendant(realPath, realDirectory))
|
|
172
|
+
throw new Error('Attachment path escaped its relation directory');
|
|
173
|
+
if (path.isAbsolute(attachmentSource) && fs.realpathSync(attachmentSource) !== realPath) {
|
|
174
|
+
throw new Error('Attachment path does not match the current relation');
|
|
175
|
+
}
|
|
176
|
+
const buffer = fs.readFileSync(filePath);
|
|
177
|
+
const sha256 = crypto.createHash('sha256').update(buffer).digest('hex');
|
|
178
|
+
if (sha256 !== metadata.sha256)
|
|
179
|
+
throw new Error('Attachment content hash mismatch');
|
|
180
|
+
return {
|
|
181
|
+
attachment: {
|
|
182
|
+
attachmentId, selfAid, peerKey, filePath: realPath,
|
|
183
|
+
mimeType: metadata.mimeType, sha256, size: buffer.length,
|
|
184
|
+
},
|
|
185
|
+
buffer,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
104
188
|
/**
|
|
105
189
|
* 把已下载的附件 Buffer 转成入站图片条目(供 baseagent 视觉通道)。
|
|
106
190
|
*
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* msg-diag: 消息生命周期诊断打点
|
|
3
|
+
* 各处理环节调用 diagLog(),写入专用 msg-diag.jsonl。
|
|
4
|
+
* ec watch diag 读取该文件,固定行实时展示各环节最新处理的消息。
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
function getLogPath() {
|
|
10
|
+
const home = process.env.EVOLCORE_HOME ?? path.join(os.homedir(), '.evolcore');
|
|
11
|
+
return path.join(home, 'logs', 'msg-diag.jsonl');
|
|
12
|
+
}
|
|
13
|
+
let _logPath = null;
|
|
14
|
+
let _fd = null;
|
|
15
|
+
const MAX_SIZE = 500 * 1024; // 500 KB
|
|
16
|
+
const TRIM_SIZE = 250 * 1024; // 超限后保留最后 250 KB
|
|
17
|
+
function trimIfNeeded(p) {
|
|
18
|
+
try {
|
|
19
|
+
const stat = fs.statSync(p);
|
|
20
|
+
if (stat.size <= MAX_SIZE)
|
|
21
|
+
return;
|
|
22
|
+
// 读最后 TRIM_SIZE 字节,从第一个完整行开始保留
|
|
23
|
+
const fd = fs.openSync(p, 'r');
|
|
24
|
+
const buf = Buffer.alloc(TRIM_SIZE);
|
|
25
|
+
fs.readSync(fd, buf, 0, TRIM_SIZE, stat.size - TRIM_SIZE);
|
|
26
|
+
fs.closeSync(fd);
|
|
27
|
+
const str = buf.toString('utf-8');
|
|
28
|
+
const firstNl = str.indexOf('\n');
|
|
29
|
+
const kept = firstNl >= 0 ? str.slice(firstNl + 1) : str;
|
|
30
|
+
fs.writeFileSync(p, kept, 'utf-8');
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// 修剪失败不阻塞主流程
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function getFd() {
|
|
37
|
+
try {
|
|
38
|
+
if (!_logPath)
|
|
39
|
+
_logPath = getLogPath();
|
|
40
|
+
const dir = path.dirname(_logPath);
|
|
41
|
+
if (!fs.existsSync(dir))
|
|
42
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
43
|
+
// 每次写前检查大小,超限则修剪并重新打开
|
|
44
|
+
if (_fd !== null) {
|
|
45
|
+
try {
|
|
46
|
+
const stat = fs.statSync(_logPath);
|
|
47
|
+
if (stat.size > MAX_SIZE) {
|
|
48
|
+
fs.closeSync(_fd);
|
|
49
|
+
_fd = null;
|
|
50
|
+
trimIfNeeded(_logPath);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
fs.closeSync(_fd);
|
|
55
|
+
_fd = null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (_fd === null) {
|
|
59
|
+
_fd = fs.openSync(_logPath, 'a');
|
|
60
|
+
}
|
|
61
|
+
return _fd;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function shortId(id) {
|
|
68
|
+
if (!id)
|
|
69
|
+
return '';
|
|
70
|
+
return id.split('.')[0];
|
|
71
|
+
}
|
|
72
|
+
function cut(text, n) {
|
|
73
|
+
if (!text)
|
|
74
|
+
return '';
|
|
75
|
+
const s = text.replace(/\r?\n/g, ' ').trim();
|
|
76
|
+
return s.length <= n ? s : s.slice(0, n - 1) + '…';
|
|
77
|
+
}
|
|
78
|
+
export function diagLog(stage, data) {
|
|
79
|
+
try {
|
|
80
|
+
const entry = {
|
|
81
|
+
stage,
|
|
82
|
+
ts: new Date().toISOString(),
|
|
83
|
+
aid: shortId(data.aid ?? ''),
|
|
84
|
+
from: shortId(data.from ?? ''),
|
|
85
|
+
to: shortId(data.to ?? ''),
|
|
86
|
+
msgId: data.msgId ?? '',
|
|
87
|
+
taskId: data.taskId,
|
|
88
|
+
preview: cut(data.text ?? '', 50),
|
|
89
|
+
};
|
|
90
|
+
const fd = getFd();
|
|
91
|
+
if (fd !== null)
|
|
92
|
+
fs.writeSync(fd, JSON.stringify(entry) + '\n');
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// 诊断打点失败不影响主流程
|
|
96
|
+
}
|
|
97
|
+
}
|