evolcore 0.0.24 → 0.0.26
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 +59 -0
- package/bin/ec.js +12 -1
- package/bin/install-codex-managed-hooks.mjs +28 -1
- package/dist/agents/baseagent.js +96 -46
- package/dist/agents/claude-runner.js +54 -13
- package/dist/agents/codex/app-server-client.js +13 -5
- package/dist/agents/codex/catalog.js +1 -1
- package/dist/agents/codex/diagnostics.js +34 -1
- package/dist/agents/codex-runner.js +128 -42
- package/dist/agents/ecagent-runner.js +14 -18
- package/dist/agents/gemini-runner.js +24 -9
- package/dist/agents/runner-types.js +25 -17
- package/dist/aun/aid/agentmd.js +135 -10
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/identity.js +1 -1
- package/dist/aun/aid/index.js +2 -1
- package/dist/aun/aid/managed-operation.js +53 -1
- package/dist/aun/aid/store.js +17 -13
- package/dist/aun/menu-client.js +74 -0
- package/dist/aun/msg/group.js +1 -1
- package/dist/aun/msg/managed-operation.js +1 -1
- package/dist/aun/msg/p2p.js +6 -1
- package/dist/aun/msg/upload.js +30 -22
- package/dist/aun/storage/client.js +4 -1
- package/dist/aun/storage/index.js +1 -1
- package/dist/channels/aun.js +329 -94
- package/dist/cli/agent-command.js +125 -5
- package/dist/cli/agent-create.js +151 -11
- package/dist/cli/agent.js +119 -6
- package/dist/cli/aun-commands.js +74 -7
- package/dist/cli/config.js +27 -8
- package/dist/cli/contact.js +63 -6
- package/dist/cli/daemon-commands.js +90 -25
- package/dist/cli/diag.js +31 -0
- package/dist/cli/handoff-command.js +35 -3
- package/dist/cli/index.js +7 -1
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/menu-client.js +38 -23
- package/dist/cli/restart-monitor.js +57 -10
- package/dist/config/builtin-roles.js +2 -0
- package/dist/config/config-manager.js +15 -0
- package/dist/config/config-operation-service.js +165 -3
- package/dist/config/contact-operation-service.js +96 -7
- package/dist/config/resolved-config-op.js +66 -8
- package/dist/core/agent-application-service.js +2 -1
- package/dist/core/auth/authorization-audit.js +20 -12
- package/dist/core/auth/operation-authorizer.js +87 -8
- package/dist/core/auth/operation-catalog.js +51 -4
- package/dist/core/auth/remote-access-store.js +40 -1
- package/dist/core/bootstrap-service.js +1 -1
- package/dist/core/command/agent-control.js +54 -25
- package/dist/core/command/cli-intent-parser.js +18 -0
- package/dist/core/command/command-handler.js +152 -20
- package/dist/core/command/diag-service.js +118 -0
- package/dist/core/command/menu-catalog.js +13 -3
- package/dist/core/command/menu-handler.js +88 -31
- package/dist/core/command/menu-protocol.js +6 -0
- package/dist/core/command/menu-token-store.js +41 -8
- package/dist/core/command/slash-gate.js +34 -4
- package/dist/core/command/slash-handler.js +77 -12
- package/dist/core/event-catalog.js +5 -0
- package/dist/core/evolagent.js +3 -3
- package/dist/core/handoff/runtime.js +205 -27
- package/dist/core/handoff/store.js +193 -4
- package/dist/core/message/im-renderer.js +72 -21
- package/dist/core/message/message-bridge.js +69 -9
- package/dist/core/message/message-queue.js +26 -0
- package/dist/core/message/message-utils.js +1 -0
- package/dist/core/message/response-engine.js +48 -23
- package/dist/core/model/catalog-service.js +79 -49
- package/dist/core/model/context-window-suffix.js +33 -0
- package/dist/core/model/model-catalog.js +1 -1
- package/dist/core/model/model-permission.js +12 -3
- package/dist/core/permission/ec-command-parser.js +51 -12
- package/dist/core/permission/sandbox-runtime.js +36 -14
- package/dist/core/permission/tool-policy.js +47 -7
- package/dist/index.js +195 -31
- package/dist/ipc.js +54 -2
- package/dist/response-system/engines/v1/proactive-flow.js +59 -2
- package/dist/stats/billing.js +41 -15
- package/dist/stats/price-resolver.js +28 -10
- package/dist/utils/avatar-upload.js +282 -23
- package/dist/utils/cross-platform.js +30 -1
- package/dist/utils/error-utils.js +21 -0
- package/dist/utils/evolcore-version.js +129 -0
- package/dist/utils/logger.js +3 -1
- package/dist/utils/model-prices.jsonl +3 -3
- package/dist/utils/npm-ops.js +176 -35
- package/kits/docs/context-assembly.md +1 -0
- package/kits/docs/evolcore/INDEX.md +3 -1
- package/kits/docs/evolcore/agent-create.md +30 -11
- package/kits/docs/evolcore/agent.md +18 -8
- package/kits/docs/evolcore/aid.md +6 -4
- package/kits/docs/evolcore/codex-windows-sandbox.md +146 -0
- package/kits/docs/evolcore/config.md +7 -0
- package/kits/docs/evolcore/contact.md +12 -5
- package/kits/docs/evolcore/menu-client.md +11 -3
- package/kits/docs/evolcore/msg.md +1 -0
- package/kits/docs/prompt-loading-architecture.md +1 -0
- package/kits/docs/venues/group.md +12 -0
- package/kits/eck_manifest.json +12 -0
- package/kits/rules/03-identity.md +3 -1
- package/kits/templates/message-fragments/handoff-request-to-target.md +3 -3
- package/kits/templates/roles/admin.json +14 -2
- package/kits/templates/roles/member.json +3 -2
- package/kits/templates/roles/visitor.json +4 -3
- package/kits/templates/system-fragments/bootstrap.md +16 -5
- package/kits/templates/system-fragments/channel.md +1 -1
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +6 -4
- package/skills/eclink/SKILL.md +0 -84
- package/skills/eclink/agents/openai.yaml +0 -4
|
@@ -183,6 +183,10 @@ function substituteManagedTmpDirToken(value) {
|
|
|
183
183
|
}
|
|
184
184
|
return result;
|
|
185
185
|
}
|
|
186
|
+
function containsManagedTmpDirReference(value) {
|
|
187
|
+
return /\$TMPDIR|\$\{TMPDIR\}/.test(value)
|
|
188
|
+
|| /\$env:TMPDIR|\$\{env:TMPDIR\}/i.test(value);
|
|
189
|
+
}
|
|
186
190
|
function restoreManagedTmpDirToken(value, managedTempDir) {
|
|
187
191
|
return value.replaceAll(MANAGED_TMPDIR_TOKEN, managedTempDir ?? '$TMPDIR');
|
|
188
192
|
}
|
|
@@ -362,7 +366,7 @@ function parseFailure(issue, input, offset, tokenIndex, dialect) {
|
|
|
362
366
|
inputLength: input.length,
|
|
363
367
|
};
|
|
364
368
|
}
|
|
365
|
-
function parseLiteralPowerShellCommand(command) {
|
|
369
|
+
function parseLiteralPowerShellCommand(command, options = {}) {
|
|
366
370
|
const input = trimShellSpacing(command);
|
|
367
371
|
if (!input)
|
|
368
372
|
return parseFailure('empty-command', input, 0, 0, 'powershell');
|
|
@@ -372,6 +376,9 @@ function parseLiteralPowerShellCommand(command) {
|
|
|
372
376
|
let token = '';
|
|
373
377
|
let tokenStarted = false;
|
|
374
378
|
let quote = null;
|
|
379
|
+
// Environment variable names are case-insensitive on Windows. Require a
|
|
380
|
+
// complete variable name, including PowerShell's Unicode/name characters.
|
|
381
|
+
const managedTmpDirReference = new RegExp('^\\$(?:env:TMPDIR(?![\\p{L}\\p{Nd}_?:])|\\{env:TMPDIR\\})', process.platform === 'win32' ? 'iu' : 'u');
|
|
375
382
|
const finishToken = () => {
|
|
376
383
|
if (!tokenStarted)
|
|
377
384
|
return;
|
|
@@ -396,6 +403,23 @@ function parseLiteralPowerShellCommand(command) {
|
|
|
396
403
|
}
|
|
397
404
|
continue;
|
|
398
405
|
}
|
|
406
|
+
if (char === '$' && isShellParameterExpansionStart(input[index + 1])) {
|
|
407
|
+
const reference = managedTmpDirReference.exec(input.slice(index))?.[0];
|
|
408
|
+
const next = reference ? input[index + reference.length] : undefined;
|
|
409
|
+
if (!reference) {
|
|
410
|
+
return parseFailure('unsafe-expansion', input, index, argv.length, 'powershell');
|
|
411
|
+
}
|
|
412
|
+
if (!options.allowManagedTmpDir || !options.managedTempDir
|
|
413
|
+
|| (next !== undefined && !/[\\/\s"']/u.test(next))) {
|
|
414
|
+
return parseFailure('unsafe-expansion', input, index, argv.length, 'powershell');
|
|
415
|
+
}
|
|
416
|
+
// Append the registered value directly: spaces remain in this argument,
|
|
417
|
+
// and characters in the directory are never interpreted as shell syntax.
|
|
418
|
+
token += options.managedTempDir;
|
|
419
|
+
tokenStarted = true;
|
|
420
|
+
index += reference.length - 1;
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
399
423
|
if (quote === 'double') {
|
|
400
424
|
if (char === '"') {
|
|
401
425
|
if (!isLiteralArgBoundary(input[index + 1]))
|
|
@@ -412,9 +436,6 @@ function parseLiteralPowerShellCommand(command) {
|
|
|
412
436
|
token += next;
|
|
413
437
|
continue;
|
|
414
438
|
}
|
|
415
|
-
if (char === '$' && isShellParameterExpansionStart(input[index + 1])) {
|
|
416
|
-
return parseFailure('unsafe-expansion', input, index, argv.length, 'powershell');
|
|
417
|
-
}
|
|
418
439
|
token += char;
|
|
419
440
|
continue;
|
|
420
441
|
}
|
|
@@ -729,7 +750,7 @@ function doubleQuotedIssueAt(input, index) {
|
|
|
729
750
|
*/
|
|
730
751
|
export function parseLiteralShellCommand(command, options = {}) {
|
|
731
752
|
if (options.dialect === 'powershell') {
|
|
732
|
-
return parseLiteralPowerShellCommand(command);
|
|
753
|
+
return parseLiteralPowerShellCommand(command, options);
|
|
733
754
|
}
|
|
734
755
|
if (options.dialect === 'cmd') {
|
|
735
756
|
return parseLiteralCmdCommand(command, options);
|
|
@@ -1416,16 +1437,16 @@ export function parseCodexExecToolCommand(input) {
|
|
|
1416
1437
|
}
|
|
1417
1438
|
return decoded;
|
|
1418
1439
|
}
|
|
1419
|
-
function
|
|
1440
|
+
function parseProactiveToolCommand(toolName, input) {
|
|
1420
1441
|
const options = {
|
|
1421
1442
|
verifyShellExecutable: process.platform === 'win32',
|
|
1422
1443
|
};
|
|
1423
1444
|
if (toolName === 'Bash' || toolName === 'Shell')
|
|
1424
|
-
return
|
|
1445
|
+
return parseCodexToolCommand(input, options);
|
|
1425
1446
|
if (toolName !== 'exec')
|
|
1426
1447
|
return null;
|
|
1427
1448
|
const command = parseCodexExecToolCommand(input);
|
|
1428
|
-
return command ?
|
|
1449
|
+
return command ? parseCodexToolCommand({ command }, options) : null;
|
|
1429
1450
|
}
|
|
1430
1451
|
function scanBoundedOutputOperators(command) {
|
|
1431
1452
|
const operators = [];
|
|
@@ -1711,13 +1732,13 @@ function findSingleFileOption(argv, startIndex) {
|
|
|
1711
1732
|
const PROACTIVE_SEND_VALUE_FLAGS = new Set([
|
|
1712
1733
|
'--format', '--app', '--file', '--text-from-file', '--link', '--payload',
|
|
1713
1734
|
'--title', '--description', '--text', '--transcript', '--as',
|
|
1714
|
-
'--content-type', '--mention', '--aun-path', '--thread', '--return',
|
|
1735
|
+
'--content-type', '--mention', '--aun-path', '--thread', '--return', '--handoff-context',
|
|
1715
1736
|
]);
|
|
1716
1737
|
const PROACTIVE_SEND_BOOLEAN_FLAGS = new Set([
|
|
1717
1738
|
'--encrypt', '--no-encrypt', '--mention-all',
|
|
1718
1739
|
]);
|
|
1719
1740
|
const SEND_CONTENT_VALUE_FLAGS = new Set([
|
|
1720
|
-
'--payload', '--title', '--description', '--text', '--transcript',
|
|
1741
|
+
'--payload', '--title', '--description', '--text', '--transcript', '--handoff-context',
|
|
1721
1742
|
]);
|
|
1722
1743
|
const TRIGGER_CONTENT_VALUE_FLAGS = new Set(['--prompt']);
|
|
1723
1744
|
/**
|
|
@@ -1728,7 +1749,7 @@ const TRIGGER_CONTENT_VALUE_FLAGS = new Set(['--prompt']);
|
|
|
1728
1749
|
*/
|
|
1729
1750
|
export function containsLiteralManagedTmpDirOutsideSendContent(argv) {
|
|
1730
1751
|
const literalIndexes = argv
|
|
1731
|
-
.map((value, index) => value
|
|
1752
|
+
.map((value, index) => containsManagedTmpDirReference(value) ? index : -1)
|
|
1732
1753
|
.filter(index => index >= 0);
|
|
1733
1754
|
if (literalIndexes.length === 0)
|
|
1734
1755
|
return false;
|
|
@@ -1903,7 +1924,8 @@ export function isExactEvolcoreSendCommandForSession(toolName, input, channelId,
|
|
|
1903
1924
|
// is represented as `exec` and is handled only for one literal inner call.
|
|
1904
1925
|
if (!selfAid)
|
|
1905
1926
|
return false;
|
|
1906
|
-
const
|
|
1927
|
+
const result = parseProactiveToolCommand(toolName, input);
|
|
1928
|
+
const argv = result?.ok ? result.argv : null;
|
|
1907
1929
|
const parsed = argv ? parseEvolcoreSendArgv(argv) : null;
|
|
1908
1930
|
if (!parsed || parsed.action !== 'send')
|
|
1909
1931
|
return false;
|
|
@@ -1914,3 +1936,20 @@ export function isExactEvolcoreSendCommandForSession(toolName, input, channelId,
|
|
|
1914
1936
|
&& !!argv
|
|
1915
1937
|
&& hasExecutableProactiveSendPayload(argv, expectedScope);
|
|
1916
1938
|
}
|
|
1939
|
+
/** Diagnostic only: this never broadens the exact-send authorization check. */
|
|
1940
|
+
export function describeProactiveSendCommandFailure(toolName, input) {
|
|
1941
|
+
if (!['Bash', 'Shell', 'exec'].includes(toolName) || !input || typeof input !== 'object')
|
|
1942
|
+
return undefined;
|
|
1943
|
+
const commandText = [input.command, input.input, ...(Array.isArray(input.commandArgv) ? input.commandArgv : [])]
|
|
1944
|
+
.filter((value) => typeof value === 'string').join(' ');
|
|
1945
|
+
if (!/\bec(?:\.cmd|\.exe)?\s+(?:group|msg)\s+send\b/i.test(commandText))
|
|
1946
|
+
return undefined;
|
|
1947
|
+
const result = parseProactiveToolCommand(toolName, input);
|
|
1948
|
+
if (!result)
|
|
1949
|
+
return '当前汇报命令未通过规范解析:只支持单次、字面量的发送调用,请将发送作为独立工具调用。';
|
|
1950
|
+
if (!result.ok) {
|
|
1951
|
+
return `当前汇报命令未通过规范解析(${result.issue},${result.dialect},位置 ${result.offset});`
|
|
1952
|
+
+ '请检查引号和转义,并将发送作为一条独立命令。';
|
|
1953
|
+
}
|
|
1954
|
+
return '当前汇报命令不符合当前会话的发送要求;请核对发送者、私聊或群目标,以及正文和参数。';
|
|
1955
|
+
}
|
|
@@ -5,34 +5,55 @@ import { getExistingHClassMaskTargets, getExistingLClassReadOnlyTargets, isSameO
|
|
|
5
5
|
import { resolveRoot } from '../../paths.js';
|
|
6
6
|
let cachedBubblewrapPath;
|
|
7
7
|
let sandboxInitializationCircuit;
|
|
8
|
+
const sessionSandboxInitializationCircuits = new Map();
|
|
8
9
|
let sandboxCircuitNoticeAt = 0;
|
|
9
10
|
export const SANDBOX_INITIALIZATION_FAILED = 'sandbox_initialization_failed';
|
|
10
11
|
export const SANDBOX_INITIALIZATION_COOLDOWN_MS = 5 * 60 * 1000;
|
|
11
12
|
export const SANDBOX_CIRCUIT_NOTICE_INTERVAL_MS = 60 * 1000;
|
|
12
13
|
const SANDBOX_DIAGNOSTIC_TARGET_LIMIT = 256;
|
|
13
|
-
/**
|
|
14
|
-
|
|
14
|
+
/** Host bootstrap failures open a process circuit; tool-result failures only
|
|
15
|
+
* affect their originating session because tool output is not host attestation. */
|
|
16
|
+
export function recordSandboxInitializationFailure(error, stderr = [], now = Date.now(), sessionId) {
|
|
15
17
|
const detail = createSandboxInitializationError(error, stderr).message.slice(0, 512);
|
|
16
|
-
|
|
18
|
+
getActiveSandboxInitializationFailure(now);
|
|
19
|
+
const previous = sessionId ? sessionSandboxInitializationCircuits.get(sessionId) : sandboxInitializationCircuit;
|
|
20
|
+
const circuit = previous
|
|
17
21
|
? {
|
|
18
|
-
...
|
|
22
|
+
...previous,
|
|
19
23
|
lastObservedAt: now,
|
|
20
|
-
occurrences:
|
|
24
|
+
occurrences: previous.occurrences + 1,
|
|
21
25
|
detail,
|
|
22
26
|
}
|
|
23
27
|
: { openedAt: now, lastObservedAt: now, occurrences: 1, detail };
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
if (sessionId)
|
|
29
|
+
sessionSandboxInitializationCircuits.set(sessionId, circuit);
|
|
30
|
+
else
|
|
31
|
+
sandboxInitializationCircuit = circuit;
|
|
32
|
+
return { ...circuit };
|
|
33
|
+
}
|
|
34
|
+
/** Expire old failures and return the process or requested session circuit. */
|
|
35
|
+
export function getActiveSandboxInitializationFailure(now = Date.now(), cooldownMs = SANDBOX_INITIALIZATION_COOLDOWN_MS, sessionId) {
|
|
36
|
+
for (const [id, circuit] of sessionSandboxInitializationCircuits) {
|
|
37
|
+
if (now - circuit.lastObservedAt >= cooldownMs)
|
|
38
|
+
sessionSandboxInitializationCircuits.delete(id);
|
|
39
|
+
}
|
|
40
|
+
if (sandboxInitializationCircuit && now - sandboxInitializationCircuit.lastObservedAt >= cooldownMs) {
|
|
31
41
|
sandboxInitializationCircuit = undefined;
|
|
32
42
|
sandboxCircuitNoticeAt = 0;
|
|
33
|
-
return undefined;
|
|
34
43
|
}
|
|
35
|
-
|
|
44
|
+
const circuit = sandboxInitializationCircuit ?? (sessionId ? sessionSandboxInitializationCircuits.get(sessionId) : undefined);
|
|
45
|
+
return circuit ? { ...circuit } : undefined;
|
|
46
|
+
}
|
|
47
|
+
/** Only recognize the complete native bootstrap diagnostic, never a quotation
|
|
48
|
+
* embedded in ordinary tool output. Tool-result evidence is session-scoped. */
|
|
49
|
+
export function isSandboxInitializationToolResult(content) {
|
|
50
|
+
const text = typeof content === 'string'
|
|
51
|
+
? content
|
|
52
|
+
: Array.isArray(content) && content.every(block => block?.type === 'text' && typeof block.text === 'string')
|
|
53
|
+
? content.map(block => block.text).join('\n')
|
|
54
|
+
: '';
|
|
55
|
+
return /^(?:Exit code [1-9]\d*\r?\n)?apply-seccomp: write \/proc\/self\/(?:uid|gid)_map: (?:Operation not permitted|Permission denied)\s*$/i
|
|
56
|
+
.test(text.trim());
|
|
36
57
|
}
|
|
37
58
|
/** Rate-limit human-readable warnings; structured audit still records every blocked task. */
|
|
38
59
|
export function shouldLogSandboxCircuitNotice(now = Date.now(), intervalMs = SANDBOX_CIRCUIT_NOTICE_INTERVAL_MS) {
|
|
@@ -561,6 +582,7 @@ export function buildBubblewrapCommand(executable, executableArgs, options) {
|
|
|
561
582
|
return { command: bubblewrapPath, args };
|
|
562
583
|
}
|
|
563
584
|
export function _resetSandboxRuntimeCache() {
|
|
585
|
+
sessionSandboxInitializationCircuits.clear();
|
|
564
586
|
cachedBubblewrapPath = undefined;
|
|
565
587
|
sandboxInitializationCircuit = undefined;
|
|
566
588
|
sandboxCircuitNoticeAt = 0;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
3
4
|
import { randomUUID } from 'crypto';
|
|
4
5
|
import { logger } from '../../utils/logger.js';
|
|
5
6
|
import { resolveRoot } from '../../paths.js';
|
|
@@ -41,6 +42,14 @@ export function expandManagedTempPath(value, options = {}) {
|
|
|
41
42
|
}
|
|
42
43
|
const WRITE_INTENT_RE = /(?:^|[\s;&|])(?:mkdir|mktemp|touch|tee|cp|mv|install|chmod|chown|ln|rm)(?:[\s;&|]|$)|(?:^|[\s])(?:\d>>?|>>?)\s*/i;
|
|
43
44
|
const TEMP_TOKEN_RE = /(?:\$TMPDIR|\$\{TMPDIR\}|\/tmp(?:\/|$)|\/var\/tmp(?:\/|$)|\/dev\/shm(?:\/|$))[^\s'";&|<>)]*/g;
|
|
45
|
+
function windowsTempRoots(options) {
|
|
46
|
+
if (process.platform !== 'win32')
|
|
47
|
+
return [];
|
|
48
|
+
return [os.tmpdir(), options.env?.TEMP, options.env?.TMP, process.env.TEMP, process.env.TMP]
|
|
49
|
+
.filter((value) => typeof value === 'string' && path.isAbsolute(value))
|
|
50
|
+
.map(value => resolveProtectedCandidate(value))
|
|
51
|
+
.filter((value, index, values) => values.indexOf(value) === index);
|
|
52
|
+
}
|
|
44
53
|
/**
|
|
45
54
|
* Inspect shell write targets that look like temporary paths. Ordinary
|
|
46
55
|
* project writes are left to the existing workspace/H-class policy; any
|
|
@@ -52,19 +61,32 @@ export function checkManagedTempShellCommand(command, options = {}) {
|
|
|
52
61
|
const root = resolveManagedTempDir(options.env);
|
|
53
62
|
const normalized = substituteTmpDir(command, root);
|
|
54
63
|
const candidates = [...normalized.matchAll(TEMP_TOKEN_RE)].map(match => match[0].replace(/[.,:]+$/, ''));
|
|
55
|
-
|
|
64
|
+
// Windows has no portable `/tmp` spelling. Treat absolute paths below the
|
|
65
|
+
// host temporary roots as temporary operands too, so a sibling session
|
|
66
|
+
// directory cannot bypass the managed TMPDIR check.
|
|
67
|
+
if (process.platform === 'win32') {
|
|
68
|
+
const roots = windowsTempRoots(options);
|
|
69
|
+
const windowsCandidates = normalized.match(/[A-Za-z]:[\\/][^\s'";&|<>)]*/g) ?? [];
|
|
70
|
+
for (const candidate of windowsCandidates) {
|
|
71
|
+
const canonical = resolveProtectedCandidate(candidate.replace(/[.,:]+$/, ''), options.cwd);
|
|
72
|
+
if (roots.some(root => isSameOrDescendant(canonical, root)))
|
|
73
|
+
candidates.push(candidate.replace(/[.,:]+$/, ''));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const uniqueCandidates = [...new Set(candidates)];
|
|
77
|
+
if (uniqueCandidates.length === 0)
|
|
56
78
|
return { kind: 'none' };
|
|
57
79
|
if (!root) {
|
|
58
|
-
return { kind: 'deny', paths:
|
|
80
|
+
return { kind: 'deny', paths: uniqueCandidates, reason: 'TMPDIR is unset or not an absolute managed path' };
|
|
59
81
|
}
|
|
60
|
-
const outside =
|
|
82
|
+
const outside = uniqueCandidates.filter(candidate => !isManagedTempPath(candidate, {
|
|
61
83
|
...options,
|
|
62
84
|
env: { ...(options.env ?? process.env), TMPDIR: root },
|
|
63
85
|
}));
|
|
64
86
|
if (outside.length > 0) {
|
|
65
87
|
return { kind: 'deny', paths: outside, reason: 'temporary write target is outside the session-managed TMPDIR' };
|
|
66
88
|
}
|
|
67
|
-
return { kind: 'allow', paths:
|
|
89
|
+
return { kind: 'allow', paths: uniqueCandidates };
|
|
68
90
|
}
|
|
69
91
|
/**
|
|
70
92
|
* Replace only already-validated temporary path operands before a second
|
|
@@ -2187,6 +2209,7 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
2187
2209
|
}
|
|
2188
2210
|
const parserOptions = {
|
|
2189
2211
|
verifyShellExecutable: process.platform === 'win32',
|
|
2212
|
+
managedTempDir: context.managedTempDir,
|
|
2190
2213
|
...(context.delegationCarrier
|
|
2191
2214
|
? { expectedDelegationToken: context.delegationCarrier }
|
|
2192
2215
|
: {}),
|
|
@@ -2237,16 +2260,33 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
2237
2260
|
&& managedAidArgv[2] === 'agentmd'
|
|
2238
2261
|
&& managedAidArgv[3] === 'put'
|
|
2239
2262
|
&& ownAid;
|
|
2263
|
+
const avatarFileIndex = managedAidArgv?.indexOf('--file') ?? -1;
|
|
2264
|
+
const avatarUrlIndex = managedAidArgv?.indexOf('--url') ?? -1;
|
|
2265
|
+
const avatarSourceIndex = avatarFileIndex >= 0 ? avatarFileIndex : avatarUrlIndex;
|
|
2266
|
+
const isSelfAvatarUpload = !!managedAidArgv
|
|
2267
|
+
&& (avatarSourceIndex === 4)
|
|
2268
|
+
&& managedAidArgv.length === 6
|
|
2269
|
+
&& managedAidArgv[0] === 'ec'
|
|
2270
|
+
&& managedAidArgv[1] === 'aid'
|
|
2271
|
+
&& managedAidArgv[2] === 'avatar'
|
|
2272
|
+
&& !!context.selfAid
|
|
2273
|
+
&& managedAidArgv[3]?.replace(/^@/, '') === context.selfAid.replace(/^@/, '')
|
|
2274
|
+
&& !!managedAidArgv[5]
|
|
2275
|
+
&& !managedAidArgv[5].startsWith('-');
|
|
2240
2276
|
const canPublishSelfAgentMd = isSelfAgentMdPublish
|
|
2241
2277
|
&& ['request', 'bypass'].includes(context.permissionMode ?? '');
|
|
2242
|
-
const
|
|
2278
|
+
const canUploadSelfAvatar = isSelfAvatarUpload
|
|
2279
|
+
&& ['request', 'bypass'].includes(context.permissionMode ?? '');
|
|
2280
|
+
const isAllowedManagedAidCommand = isManagedAidPublicRead || isSelfManagedAidShow || canPublishSelfAgentMd || canUploadSelfAvatar;
|
|
2243
2281
|
if (context.sessionId && /^\s*ec\s+aid(?:\s|$)/i.test(policyCommand) && !isAllowedManagedAidCommand) {
|
|
2244
2282
|
return {
|
|
2245
2283
|
behavior: 'deny',
|
|
2246
2284
|
input,
|
|
2247
2285
|
message: isSelfAgentMdPublish && (context.permissionMode === 'readonly' || context.permissionMode === 'auto')
|
|
2248
2286
|
? `🔒 ${context.permissionMode} 模式禁止发布 agent.md;请切换到 request 或 bypass 后再执行 \`ec aid agentmd put <当前 self AID>\`。`
|
|
2249
|
-
:
|
|
2287
|
+
: isSelfAvatarUpload && (context.permissionMode === 'readonly' || context.permissionMode === 'auto')
|
|
2288
|
+
? `🔒 ${context.permissionMode} 模式禁止上传头像;请切换到 request 或 bypass 后再执行 \`ec aid avatar <当前 self AID> (--file <path> 或 --url <https-url>)\`。`
|
|
2289
|
+
: '🔒 EvolCore 托管会话允许 `ec aid show <当前 self AID>`、`ec aid lookup <任意合法 AID>` 或 `ec aid agentmd get <任意合法 AID>`;`ec aid agentmd put` 及 `ec aid avatar` 还要求针对当前 self AID 且处于 request 或 bypass。可选后缀仅限 `--format json`。禁止列举本地身份、切换身份或对其它 AID 执行 show/put/avatar。',
|
|
2250
2290
|
policyCode: 'managed_ec_aid_scope_forbidden',
|
|
2251
2291
|
};
|
|
2252
2292
|
}
|
|
@@ -2281,7 +2321,7 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
2281
2321
|
}
|
|
2282
2322
|
if (ecCommand.issue === 'unsafe-expansion') {
|
|
2283
2323
|
const message = shellDialect === 'powershell'
|
|
2284
|
-
? '🔒 EC
|
|
2324
|
+
? '🔒 EC 命令包含无法安全解析的 PowerShell 展开;临时路径仅支持已登记的当前会话 $env:TMPDIR 或 ${env:TMPDIR},也可使用完整路径。纯文字请使用单引号,或用 PowerShell 反引号转义 $ 和正文内的双引号。'
|
|
2285
2325
|
: shellDialect === 'cmd'
|
|
2286
2326
|
? '🔒 EC 命令包含未转义的 cmd.exe 展开;请不要使用 %VAR%、!VAR! 或 ^ 转义。cmd.exe 传给 ec 时必须保持原文字面量'
|
|
2287
2327
|
: '🔒 EC 双引号正文包含未转义的 Shell 展开;请将反引号写成 \\`,将 $()、${}、$变量中的 $ 写成 \\$。Shell 传给 ec 时会还原为原文字面量';
|