evolcore 0.0.35 → 0.0.37
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 +42 -0
- package/README.md +6 -0
- package/bin/install-codex-managed-hooks.mjs +20 -3
- package/dist/agents/baseagent-availability.js +1 -1
- package/dist/agents/baseagent.js +88 -0
- package/dist/agents/claude-runner.js +59 -12
- package/dist/agents/codex/app-server-client.js +18 -1
- package/dist/agents/codex-runner.js +262 -21
- package/dist/agents/ecagent-runner.js +47 -11
- package/dist/agents/gemini-runner.js +36 -12
- package/dist/aun/aid/client.js +4 -3
- package/dist/aun/aid/control-aid.js +270 -8
- package/dist/aun/aid/identity.js +6 -3
- package/dist/aun/aid/ownership-operation.js +58 -0
- package/dist/aun/aid/store.js +1 -0
- package/dist/aun/group/fs-download.js +67 -2
- package/dist/aun/group/history.js +20 -5
- package/dist/aun/group/operations.js +8 -2
- package/dist/aun/menu/client.js +39 -2
- package/dist/aun/menu/transport.js +1 -1
- package/dist/aun/msg/history.js +43 -4
- package/dist/aun/msg/managed-operation.js +30 -53
- package/dist/aun/msg/p2p.js +51 -15
- package/dist/aun/outbox.js +4 -0
- package/dist/channels/aun.js +260 -73
- package/dist/channels/daemon.js +6 -4
- package/dist/channels/feishu.js +3 -0
- package/dist/cli/agent-command.js +161 -223
- package/dist/cli/agent-create.js +190 -223
- package/dist/cli/agent.js +3 -1
- package/dist/cli/aid-bind-command.js +53 -0
- package/dist/cli/aun-commands.js +384 -91
- package/dist/cli/bench.js +1 -1
- package/dist/cli/cli-argv.js +74 -0
- package/dist/cli/daemon-commands.js +105 -74
- package/dist/cli/diag.js +1 -1
- package/dist/cli/fs-command.js +18 -19
- package/dist/cli/getmeta-command.js +159 -6
- package/dist/cli/index.js +6 -12
- package/dist/cli/init-channel.js +8 -55
- package/dist/cli/init.js +125 -218
- package/dist/cli/queue-command.js +11 -8
- package/dist/cli/restart-monitor.js +177 -85
- package/dist/cli/session-command.js +1 -1
- package/dist/cli/stats.js +7 -5
- package/dist/cli/thread-command.js +103 -30
- package/dist/cli/trigger-command.js +1 -1
- package/dist/cli/watch-msg-diag.js +1 -0
- package/dist/config/access-policy.js +2 -2
- package/dist/config/boot-log.js +14 -11
- package/dist/config/config-field-policy.js +9 -1
- package/dist/config/config-manager.js +176 -90
- package/dist/config/config-operation-service.js +7 -14
- package/dist/config/contact-book-store.js +142 -26
- package/dist/config/contact-book.js +4 -2
- package/dist/config/lifecycle.js +1 -1
- package/dist/config/owner-config.js +96 -0
- package/dist/config/peer-role-resolver.js +12 -18
- package/dist/config/providers-config.js +382 -0
- package/dist/config/providers-import.js +74 -0
- package/dist/config/providers-management.js +328 -0
- package/dist/config/providers.js +22 -0
- package/dist/config/resolved-config-op.js +17 -14
- package/dist/config/snapshot.js +28 -16
- package/dist/config-store.js +18 -14
- package/dist/core/agent-ipc-registration.js +21 -63
- package/dist/core/agent-runtime.js +16 -5
- package/dist/core/auth/agent-delegation.js +199 -25
- package/dist/core/auth/auth-gateway.js +44 -2
- package/dist/core/auth/authorization-denial.js +1 -1
- package/dist/core/auth/debug-context.js +2 -1
- package/dist/core/auth/delegation-authorization.js +2 -2
- package/dist/core/auth/delegation-diagnostics.js +4 -1
- package/dist/core/auth/operation-authorizer.js +10 -1
- package/dist/core/auth/operation-catalog.js +19 -1
- package/dist/core/bootstrap-service.js +43 -20
- package/dist/core/channel-loader.js +0 -1
- package/dist/core/channel-runtime.js +30 -4
- package/dist/core/command/agent-control.js +196 -37
- package/dist/core/command/cli-intent-parser.js +60 -4
- package/dist/core/command/command-handler.js +212 -60
- package/dist/core/command/connect-menu.js +1 -0
- package/dist/core/command/diag-service.js +5 -4
- package/dist/core/command/file-search.js +368 -0
- package/dist/core/command/menu-catalog.js +2 -1
- package/dist/core/command/menu-handler.js +292 -107
- package/dist/core/command/menu-protocol.js +23 -0
- package/dist/core/command/role-menu.js +7 -19
- package/dist/core/command/skill-slash.js +130 -25
- package/dist/core/command/slash-handler.js +101 -53
- package/dist/core/command/topic-slash.js +16 -5
- package/dist/core/daemon-bootstrap.js +7 -1
- package/dist/core/daemon-control.js +19 -1
- package/dist/core/daemon-lifecycle.js +2 -1
- package/dist/core/event-catalog.js +2 -0
- package/dist/core/evolagent-registry.js +13 -2
- package/dist/core/evolagent.js +3 -6
- package/dist/core/handoff/ipc-registration.js +2 -2
- package/dist/core/handoff/runtime.js +40 -6
- package/dist/core/handoff/store.js +1 -0
- package/dist/core/inference/text-inference.js +95 -6
- package/dist/core/ipc-registration.js +43 -24
- package/dist/core/memory/consolidation-context.js +4 -1
- package/dist/core/memory/consolidation.js +25 -14
- package/dist/core/message/aun-ipc-sender.js +296 -88
- package/dist/core/message/aun-request-registry.js +226 -0
- package/dist/core/message/create-status.js +33 -4
- package/dist/core/message/message-bridge.js +62 -13
- package/dist/core/message/message-log.js +0 -1
- package/dist/core/message/message-queue.js +4 -1
- package/dist/core/message/message-utils.js +8 -1
- package/dist/core/message/peer-mode.js +17 -9
- package/dist/core/message/response-engine.js +266 -124
- package/dist/core/message/retry-scheduler.js +11 -8
- package/dist/core/model/catalog-service.js +60 -18
- package/dist/core/model/model-catalog.js +112 -9
- package/dist/core/model/model-fallback.js +94 -26
- package/dist/core/ownership/owner-resolver.js +687 -0
- package/dist/core/permission/approval-gateway.js +196 -62
- package/dist/core/permission/ec-command-parser.js +138 -25
- package/dist/core/permission/filesystem-approval-scope.js +141 -0
- package/dist/core/permission/protected-paths.js +14 -1
- package/dist/core/permission/sandbox-runtime.js +171 -18
- package/dist/core/permission/tool-policy.js +13 -2
- package/dist/core/role/runtime-policy.js +3 -4
- package/dist/core/session/session-idle-monitor.js +10 -8
- package/dist/core/session/session-manager.js +72 -45
- package/dist/core/session/session-mapper.js +18 -7
- package/dist/core/session/session-renew.js +5 -4
- package/dist/core/session/session-title.js +2 -1
- package/dist/core/session/session-turn-coordinator.js +4 -2
- package/dist/core/skill/index-generator.js +2 -16
- package/dist/core/task/task-recovery-service.js +15 -2
- package/dist/core/thread/command-parser.js +28 -13
- package/dist/core/thread/index.js +1 -0
- package/dist/core/thread/ipc-operation.js +11 -8
- package/dist/core/thread/thread-operation.js +79 -44
- package/dist/core/thread/thread-route.js +54 -6
- package/dist/core/thread/thread-service.js +236 -45
- package/dist/core/thread/topic-id.js +248 -0
- package/dist/eck/kit-renderer.js +1 -1
- package/dist/eck/message-renderer.js +12 -2
- package/dist/index.js +50 -27
- package/dist/ipc.js +59 -6
- package/dist/paths.js +1 -0
- package/dist/product.js +1 -0
- package/dist/response-system/engines/v1/proactive-flow.js +10 -3
- package/dist/stats/db.js +8 -0
- package/dist/stats/writer.js +26 -4
- package/dist/trigger/authorization.js +48 -3
- package/dist/trigger/feedback.js +28 -10
- package/dist/trigger/ipc-service.js +86 -10
- package/dist/trigger/local-files.js +55 -23
- package/dist/trigger/scheduler.js +57 -11
- package/dist/trigger/system-definitions.js +1 -0
- package/dist/trigger/validation.js +44 -15
- package/dist/utils/aid-bind.js +17 -297
- package/dist/utils/avatar-upload.js +16 -4
- package/dist/utils/cross-platform.js +115 -36
- package/dist/utils/ecweb-supervisor.js +1 -1
- package/dist/utils/ecweb-utils.js +8 -1
- package/dist/utils/error-utils.js +44 -6
- package/dist/utils/instance-registry.js +12 -2
- package/dist/utils/npm-ops.js +102 -27
- package/dist/utils/package-version-info.js +72 -27
- package/dist/utils/restart-safety.js +50 -0
- package/dist/utils/upgrade-jobs.js +304 -0
- package/dist/utils/windows-autostart.js +17 -10
- package/kits/docs/INDEX.md +5 -0
- package/kits/docs/aun/ABILITY_CALLING.md +239 -0
- package/kits/docs/evolcore/INDEX.md +14 -3
- package/kits/docs/evolcore/agent-create.md +55 -141
- package/kits/docs/evolcore/agent.md +38 -16
- package/kits/docs/evolcore/aid.md +34 -0
- package/kits/docs/evolcore/config.md +20 -1
- package/kits/docs/evolcore/event.md +2 -2
- package/kits/docs/evolcore/fs.md +18 -2
- package/kits/docs/evolcore/group-collaboration.md +13 -246
- package/kits/docs/evolcore/group.md +2 -1
- package/kits/docs/evolcore/memory-consolidation-batches.md +1 -1
- package/kits/docs/evolcore/menu-protocol.md +65 -1
- package/kits/docs/evolcore/msg.md +64 -2
- package/kits/docs/evolcore/skill.md +86 -0
- package/kits/docs/evolcore/stats.md +6 -0
- package/kits/docs/evolcore/task.md +2 -0
- package/kits/docs/evolcore/thread-chain.md +145 -51
- package/kits/docs/evolcore/trigger.md +48 -3
- package/kits/eck_manifest.json +21 -0
- package/kits/eck_message_manifest.json +4 -4
- package/kits/migrations/migrate-role-config-v5.mjs +20 -7
- package/kits/rules/01-overview.md +2 -1
- package/kits/rules/03-identity.md +3 -1
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/07-memory-and-thread.md +1 -1
- package/kits/schemas/_meta.json +16 -1
- package/kits/schemas/agent-config.schema.13.json +44 -2
- package/kits/schemas/contact-book.schema.3.json +5 -0
- package/kits/schemas/daemon.schema.6.json +3 -1
- package/kits/schemas/daemon.schema.8.json +329 -0
- package/kits/schemas/defaults.schema.5.json +39 -3
- package/kits/schemas/providers.schema.1.json +76 -0
- package/kits/schemas/relation-config.schema.9.json +5 -1
- package/kits/skills/.switches.json +28 -0
- package/kits/skills/aid-ability/SKILL.md +214 -0
- package/kits/skills/ec-agent-create/SKILL.md +162 -0
- package/kits/skills/ec-group/SKILL.md +2 -1
- package/kits/skills/ec-group-collaboration/SKILL.md +228 -0
- package/kits/skills/ec-identity/SKILL.md +143 -0
- package/kits/skills/ec-skill/scripts/cli.mjs +36 -3
- package/kits/skills/ec-skill/scripts/skill.mjs +202 -15
- package/kits/skills/ec-trigger/SKILL.md +167 -0
- package/kits/skills/ec-trigger/references/examples.md +153 -0
- package/kits/skills/thread-ops/SKILL.md +108 -420
- package/kits/templates/message-fragments/thread-context.md +1 -2
- package/kits/templates/message-fragments/trigger-thread-check.md +1 -1
- package/kits/templates/roles/member.json +1 -1
- package/kits/templates/roles/visitor.json +1 -0
- package/kits/templates/system-fragments/bootstrap.md +1 -0
- package/kits/templates/system-fragments/channel.md +2 -1
- package/kits/templates/system-fragments/commands.md +5 -4
- package/kits/templates/system-fragments/identity.md +2 -0
- package/kits/templates/system-fragments/session.md +1 -1
- package/kits/templates/system-fragments/venue.md +1 -1
- package/package.json +4 -3
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { isTrustedWindowsShellExecutable } from '../../utils/windows-shell-trust.js';
|
|
4
|
+
export function parseJsonObjectArgument(raw, optionName = '--payload') {
|
|
5
|
+
let parsed;
|
|
6
|
+
try {
|
|
7
|
+
parsed = JSON.parse(raw);
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
const detail = error instanceof Error ? `: ${error.message}` : '';
|
|
11
|
+
throw new Error(`${optionName} must be valid JSON${detail}`);
|
|
12
|
+
}
|
|
13
|
+
if (typeof parsed === 'string') {
|
|
14
|
+
try {
|
|
15
|
+
parsed = JSON.parse(parsed);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
const detail = error instanceof Error ? `: ${error.message}` : '';
|
|
19
|
+
throw new Error(`${optionName} JSON string must contain valid JSON${detail}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
23
|
+
throw new Error(`${optionName} must be a JSON object`);
|
|
24
|
+
}
|
|
25
|
+
return parsed;
|
|
26
|
+
}
|
|
4
27
|
function parseApplyPatchCommand(command, options = {}) {
|
|
5
28
|
const input = command.trim();
|
|
6
29
|
let commandText = input;
|
|
@@ -114,7 +137,8 @@ export function parseShellApplyPatchInvocation(input, options = {}, parsedArgv)
|
|
|
114
137
|
else {
|
|
115
138
|
addCandidate(rawCommand);
|
|
116
139
|
}
|
|
117
|
-
|
|
140
|
+
if (!carrier?.parseFailure)
|
|
141
|
+
addCandidate(carrier?.command);
|
|
118
142
|
if (parsedArgv && argvUsesShellApplyPatch(parsedArgv)) {
|
|
119
143
|
return parseApplyPatchArgv(parsedArgv);
|
|
120
144
|
}
|
|
@@ -641,15 +665,68 @@ function hasCarrierEcCommandPrefix(command, dialect) {
|
|
|
641
665
|
export function normalizeEvolcoreCommandArgv(argv) {
|
|
642
666
|
if (argv.length === 0)
|
|
643
667
|
return [];
|
|
644
|
-
const
|
|
668
|
+
const materialized = materializeEvolcoreCmdShimArgv(argv);
|
|
669
|
+
const executable = materialized[0];
|
|
645
670
|
if (typeof executable === 'string' && EVOLCORE_EXECUTABLE_ALIASES.has(executable.toLowerCase())) {
|
|
646
|
-
return ['ec', ...
|
|
671
|
+
return ['ec', ...materialized.slice(1)];
|
|
647
672
|
}
|
|
648
|
-
if (typeof executable === 'string' && isNodeExecutable(executable) &&
|
|
649
|
-
&& typeof
|
|
650
|
-
return ['ec', ...
|
|
673
|
+
if (typeof executable === 'string' && isNodeExecutable(executable) && materialized.length >= 2
|
|
674
|
+
&& typeof materialized[1] === 'string' && normalizeNodeWrapperPath(materialized[1])) {
|
|
675
|
+
return ['ec', ...materialized.slice(2)];
|
|
676
|
+
}
|
|
677
|
+
return materialized;
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Materialize the argv that an npm `ec.cmd` shim gives to Node.
|
|
681
|
+
*
|
|
682
|
+
* PowerShell first produces an argv for the batch file and the shim then
|
|
683
|
+
* expands `%*` into a command line before starting Node. That second Windows
|
|
684
|
+
* command-line parse consumes an even half of each backslash run before a
|
|
685
|
+
* quote; an odd run leaves a literal quote. JSON commonly arrives as
|
|
686
|
+
* `{\"key\":\"value\"}` at this boundary, while Node actually receives
|
|
687
|
+
* `{\"key\":\"value\"}` with the escaping backslashes removed. The
|
|
688
|
+
* approval hash must describe the latter, because that is what the CLI uses
|
|
689
|
+
* when it authenticates the IPC request.
|
|
690
|
+
*
|
|
691
|
+
* This is deliberately limited to the trusted EC batch entrypoint. It is
|
|
692
|
+
* not a general cmd.exe parser and does not authorize shell composition.
|
|
693
|
+
*/
|
|
694
|
+
function materializeEvolcoreCmdShimArg(value) {
|
|
695
|
+
let result = '';
|
|
696
|
+
let backslashes = 0;
|
|
697
|
+
const flushBackslashes = (count = backslashes) => {
|
|
698
|
+
if (count > 0)
|
|
699
|
+
result += '\\'.repeat(count);
|
|
700
|
+
backslashes = 0;
|
|
701
|
+
};
|
|
702
|
+
for (const char of value) {
|
|
703
|
+
if (char === '\\') {
|
|
704
|
+
backslashes++;
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
if (char === '"') {
|
|
708
|
+
result += '\\'.repeat(Math.floor(backslashes / 2));
|
|
709
|
+
if (backslashes % 2 === 1)
|
|
710
|
+
result += '"';
|
|
711
|
+
backslashes = 0;
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
flushBackslashes();
|
|
715
|
+
result += char;
|
|
651
716
|
}
|
|
652
|
-
|
|
717
|
+
flushBackslashes();
|
|
718
|
+
return result;
|
|
719
|
+
}
|
|
720
|
+
function isEvolcoreCmdShimExecutable(value) {
|
|
721
|
+
if (!value)
|
|
722
|
+
return false;
|
|
723
|
+
const normalized = value.replaceAll('\\', '/').toLowerCase();
|
|
724
|
+
return normalized === 'ec.cmd' || normalized.endsWith('/ec.cmd');
|
|
725
|
+
}
|
|
726
|
+
function materializeEvolcoreCmdShimArgv(argv) {
|
|
727
|
+
if (!isEvolcoreCmdShimExecutable(argv[0]))
|
|
728
|
+
return [...argv];
|
|
729
|
+
return [argv[0], ...argv.slice(1).map(materializeEvolcoreCmdShimArg)];
|
|
653
730
|
}
|
|
654
731
|
const INLINE_TEXT_FLAGS = {
|
|
655
732
|
agentNew: new Set(['--description', '--persona-text', '--handoff']),
|
|
@@ -669,6 +746,7 @@ const MESSAGE_POSITIONAL_FLAGS = new Set([
|
|
|
669
746
|
'--file', '--payload', '--as', '--content-type', '--text', '--transcript',
|
|
670
747
|
'--thread', '--title', '--description', '--text-from-file',
|
|
671
748
|
'--handoff-context', '--aun-path', '--app', '--format', '--encrypt', '--no-encrypt',
|
|
749
|
+
'--return', '--timeout',
|
|
672
750
|
]);
|
|
673
751
|
/** Decode the EC inline text contract exactly once, after shell parsing. */
|
|
674
752
|
export function decodeEvolcoreInlineText(value) {
|
|
@@ -980,6 +1058,8 @@ export function parseCodexCarrierCommand(carrier, options = {}) {
|
|
|
980
1058
|
return parseFailure('untrusted-shell', carrier.command, 0, 0, carrier.dialect);
|
|
981
1059
|
}
|
|
982
1060
|
}
|
|
1061
|
+
if (carrier.parseFailure)
|
|
1062
|
+
return carrier.parseFailure;
|
|
983
1063
|
const unwrapped = carrier.dialect === 'powershell'
|
|
984
1064
|
? unwrapPowerShellCredentialPrefix(carrier.command)
|
|
985
1065
|
: { kind: 'none', command: carrier.command };
|
|
@@ -1012,9 +1092,10 @@ export function parseCodexCarrierCommand(carrier, options = {}) {
|
|
|
1012
1092
|
return parseFailure('shell-composition', unwrapped.command, 0, 0, carrier.dialect);
|
|
1013
1093
|
}
|
|
1014
1094
|
const bodyArgv = materialized.argv.slice(executeIndex + 1);
|
|
1015
|
-
const
|
|
1095
|
+
const parsedBody = bodyArgv.length === 1
|
|
1016
1096
|
? parseLiteralShellCommand(bodyArgv[0], { ...options, dialect: 'cmd' })
|
|
1017
1097
|
: { ok: true, argv: normalizeEvolcoreCommandArgv(bodyArgv) };
|
|
1098
|
+
const materializedBody = parsedBody;
|
|
1018
1099
|
if (!materializedBody.ok
|
|
1019
1100
|
|| materializedBody.argv.length !== nested.argv.length
|
|
1020
1101
|
|| materializedBody.argv.some((value, index) => value !== nested.argv[index])) {
|
|
@@ -1452,21 +1533,27 @@ function resolveCodexPowerShellCarrierString(command) {
|
|
|
1452
1533
|
/** Resolve a POSIX shell carrier when app-server provides only a string. */
|
|
1453
1534
|
function resolveCodexPosixShellCarrierString(command) {
|
|
1454
1535
|
const input = trimShellSpacing(command);
|
|
1536
|
+
const parsed = parseLiteralShellCommand(input);
|
|
1537
|
+
if (parsed.ok) {
|
|
1538
|
+
const carrier = resolveCodexShellCarrierArgv(parsed.argv);
|
|
1539
|
+
if (carrier?.dialect === 'posix')
|
|
1540
|
+
return carrier;
|
|
1541
|
+
}
|
|
1455
1542
|
const match = input.match(/^(?:"([^"]+)"|(\S+))\s+(-lc|-c)\s+(.+)$/s);
|
|
1456
1543
|
if (!match)
|
|
1457
1544
|
return undefined;
|
|
1458
1545
|
const executable = match[1] ?? match[2];
|
|
1459
1546
|
if (!isCodexPosixShellExecutable(executable))
|
|
1460
1547
|
return undefined;
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1548
|
+
const nested = match[4].trim().replace(/^['"]/, '');
|
|
1549
|
+
return {
|
|
1550
|
+
command: nested,
|
|
1551
|
+
dialect: 'posix',
|
|
1552
|
+
executable,
|
|
1553
|
+
parseFailure: parsed.ok
|
|
1554
|
+
? parseFailure('shell-composition', input, 0, 0, 'posix')
|
|
1555
|
+
: parsed,
|
|
1556
|
+
};
|
|
1470
1557
|
}
|
|
1471
1558
|
export function resolveCodexShellCarrierString(command) {
|
|
1472
1559
|
return resolveCodexCmdCarrierString(command)
|
|
@@ -2424,16 +2511,40 @@ export function parseCodexExecToolCommand(input) {
|
|
|
2424
2511
|
}
|
|
2425
2512
|
return decoded;
|
|
2426
2513
|
}
|
|
2514
|
+
/**
|
|
2515
|
+
* parseCodexToolCommand 只接受单条字面量调用;`cd <dir> && ec …` 和 `ec … | <filter>`
|
|
2516
|
+
* 包裹的发送命令会被判为 shell-composition。classifyEvolcoreShellCommand 已对这两种
|
|
2517
|
+
* 复合形态做了安全边界校验,复用它提取的 ec argv 让下游发送校验看到和裸调用一致的字面量。
|
|
2518
|
+
*/
|
|
2519
|
+
function classifyCompositeProactiveSend(commandText, options) {
|
|
2520
|
+
const classified = classifyEvolcoreShellCommand(commandText, options);
|
|
2521
|
+
if (classified.kind === 'cd-prefixed' || classified.kind === 'bounded-output') {
|
|
2522
|
+
const argv = normalizeEvolcoreCommandArgv(classified.command.argv);
|
|
2523
|
+
if (argv[0] === 'ec')
|
|
2524
|
+
return { ok: true, argv };
|
|
2525
|
+
}
|
|
2526
|
+
return null;
|
|
2527
|
+
}
|
|
2427
2528
|
function parseProactiveToolCommand(toolName, input) {
|
|
2428
2529
|
const options = {
|
|
2429
2530
|
verifyShellExecutable: process.platform === 'win32',
|
|
2430
2531
|
};
|
|
2431
|
-
if (toolName === 'Bash' || toolName === 'Shell')
|
|
2432
|
-
|
|
2532
|
+
if (toolName === 'Bash' || toolName === 'Shell') {
|
|
2533
|
+
const literal = parseCodexToolCommand(input, options);
|
|
2534
|
+
if (literal.ok)
|
|
2535
|
+
return literal;
|
|
2536
|
+
const commandText = typeof input.command === 'string' ? input.command : '';
|
|
2537
|
+
return commandText ? (classifyCompositeProactiveSend(commandText, options) ?? literal) : literal;
|
|
2538
|
+
}
|
|
2433
2539
|
if (toolName !== 'exec')
|
|
2434
2540
|
return null;
|
|
2435
2541
|
const command = parseCodexExecToolCommand(input);
|
|
2436
|
-
|
|
2542
|
+
if (!command)
|
|
2543
|
+
return null;
|
|
2544
|
+
const literal = parseCodexToolCommand({ command }, options);
|
|
2545
|
+
if (literal.ok)
|
|
2546
|
+
return literal;
|
|
2547
|
+
return classifyCompositeProactiveSend(command, options) ?? literal;
|
|
2437
2548
|
}
|
|
2438
2549
|
function scanBoundedOutputOperators(command) {
|
|
2439
2550
|
const operators = [];
|
|
@@ -2888,7 +2999,7 @@ const PROACTIVE_SEND_VALUE_FLAGS = new Set([
|
|
|
2888
2999
|
'--format', '--app', '--file', '--text-from-file', '--link', '--payload',
|
|
2889
3000
|
'--title', '--description', '--text', '--transcript', '--as',
|
|
2890
3001
|
'--content-type', '--mention', '--aun-path', '--thread', '--handoff-context',
|
|
2891
|
-
'--check-every', '--trigger-prompt',
|
|
3002
|
+
'--check-every', '--trigger-prompt', '--return', '--timeout',
|
|
2892
3003
|
]);
|
|
2893
3004
|
const PROACTIVE_SEND_BOOLEAN_FLAGS = new Set([
|
|
2894
3005
|
'--encrypt', '--no-encrypt', '--mention-all',
|
|
@@ -3019,8 +3130,8 @@ function hasExecutableProactiveSendPayload(argv, scope) {
|
|
|
3019
3130
|
}
|
|
3020
3131
|
if (modes.has('payload')) {
|
|
3021
3132
|
try {
|
|
3022
|
-
|
|
3023
|
-
return
|
|
3133
|
+
parseJsonObjectArgument(modes.get('payload'));
|
|
3134
|
+
return true;
|
|
3024
3135
|
}
|
|
3025
3136
|
catch {
|
|
3026
3137
|
return false;
|
|
@@ -3134,8 +3245,10 @@ export function describeProactiveSendCommandFailure(toolName, input, session) {
|
|
|
3134
3245
|
+ ' "<正文>"';
|
|
3135
3246
|
// 带 --thread 的发送走话题路由校验,允许省略 from/to;走到这里说明话题本身没解析通过。
|
|
3136
3247
|
if (sendOptionValue(argv, '--thread')) {
|
|
3137
|
-
return `${prefix}带 --thread
|
|
3138
|
-
+
|
|
3248
|
+
return `${prefix}带 --thread 的跨话题发送未通过校验——话题必须已存在于本端:话题写裸 id,`
|
|
3249
|
+
+ `主话题写 main:<aidA>:<aidB>(两端之一必须是本端)。from(若给出)必须是本人,`
|
|
3250
|
+
+ `to(若给出)必须是该话题对端,且需携带 --check-every。`
|
|
3251
|
+
+ `主话题 id 只在本端之间有效,不要转发给别人使用。可用 ec thread list <对方AID> 取得可用值。`;
|
|
3139
3252
|
}
|
|
3140
3253
|
// 私聊/群聊用错了 scope。
|
|
3141
3254
|
if (session?.chatType && (session.chatType === 'group') !== (scope === 'group')) {
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { isSameOrDescendant, resolveProtectedCandidate } from './protected-paths.js';
|
|
4
|
+
function literalPath(value, cwd) {
|
|
5
|
+
if (typeof value !== 'string' || !value.trim() || /[\0\r\n*?\[\]{}$`]/.test(value))
|
|
6
|
+
return undefined;
|
|
7
|
+
const canonical = resolveProtectedCandidate(value, cwd);
|
|
8
|
+
// Broad convenience grants never represent a filesystem/drive root.
|
|
9
|
+
if (path.dirname(canonical) === canonical)
|
|
10
|
+
return undefined;
|
|
11
|
+
return process.platform === 'win32' ? canonical.toLowerCase() : canonical;
|
|
12
|
+
}
|
|
13
|
+
export function fileChangeApprovalScope(rawPaths, cwd, grantRoot) {
|
|
14
|
+
// A grantRoot does not replace concrete targets (notably move destinations).
|
|
15
|
+
const candidates = grantRoot ? [grantRoot, ...rawPaths] : rawPaths;
|
|
16
|
+
if (candidates.length === 0)
|
|
17
|
+
return undefined;
|
|
18
|
+
const paths = candidates.map(candidate => literalPath(candidate, cwd));
|
|
19
|
+
if (paths.some(candidate => !candidate))
|
|
20
|
+
return undefined;
|
|
21
|
+
const canonical = [...new Set(paths)].sort();
|
|
22
|
+
const root = grantRoot ? literalPath(grantRoot, cwd) : undefined;
|
|
23
|
+
const directories = root
|
|
24
|
+
? canonical.every(candidate => isSameOrDescendant(candidate, root)) ? [root] : []
|
|
25
|
+
: [...new Set(canonical.map(candidate => path.dirname(candidate)))];
|
|
26
|
+
return {
|
|
27
|
+
targets: canonical.map(candidate => ({ path: candidate, access: 'write' })),
|
|
28
|
+
...(directories.length === 1 && path.dirname(directories[0]) !== directories[0]
|
|
29
|
+
? { directory: directories[0] } : {}),
|
|
30
|
+
access: 'write',
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Parse only proven, filesystem-only capabilities. Commands, network, globs,
|
|
35
|
+
* root aliases, unknown fields and future schemas retain exact-input approval.
|
|
36
|
+
*/
|
|
37
|
+
export function permissionApprovalScope(input, cwd, workspace) {
|
|
38
|
+
if (Object.keys(input).some(key => !['permissions', 'cwd', 'reason'].includes(key)))
|
|
39
|
+
return undefined;
|
|
40
|
+
const profile = input.permissions;
|
|
41
|
+
if (!profile || typeof profile !== 'object' || Array.isArray(profile))
|
|
42
|
+
return undefined;
|
|
43
|
+
const record = profile;
|
|
44
|
+
if (Object.keys(record).length !== 1)
|
|
45
|
+
return undefined;
|
|
46
|
+
const filesystem = record.fileSystem ?? record.filesystem;
|
|
47
|
+
if (!filesystem || typeof filesystem !== 'object' || Array.isArray(filesystem))
|
|
48
|
+
return undefined;
|
|
49
|
+
const targets = [];
|
|
50
|
+
const add = (value, access) => {
|
|
51
|
+
let candidate = value;
|
|
52
|
+
let base = cwd;
|
|
53
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
54
|
+
const spec = value;
|
|
55
|
+
if (spec.type === 'special' && spec.value && typeof spec.value === 'object') {
|
|
56
|
+
const special = spec.value;
|
|
57
|
+
if (special.kind !== 'project_roots'
|
|
58
|
+
|| Object.keys(spec).some(key => key !== 'type' && key !== 'value')
|
|
59
|
+
|| Object.keys(special).some(key => key !== 'kind' && key !== 'subpath'))
|
|
60
|
+
return false;
|
|
61
|
+
candidate = special.subpath ?? '.';
|
|
62
|
+
base = workspace;
|
|
63
|
+
}
|
|
64
|
+
else if (Object.keys(spec).length === 1 && (typeof spec.path === 'string' || typeof spec.pattern === 'string')) {
|
|
65
|
+
candidate = spec.path ?? spec.pattern;
|
|
66
|
+
}
|
|
67
|
+
else
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
if (typeof candidate === 'string' && candidate.startsWith(':'))
|
|
71
|
+
return false;
|
|
72
|
+
const canonical = literalPath(candidate, base);
|
|
73
|
+
if (!canonical)
|
|
74
|
+
return false;
|
|
75
|
+
targets.push({ path: canonical, access });
|
|
76
|
+
return true;
|
|
77
|
+
};
|
|
78
|
+
for (const [key, value] of Object.entries(filesystem)) {
|
|
79
|
+
if (key === 'read' || key === 'write') {
|
|
80
|
+
if (!Array.isArray(value) || !value.every(candidate => add(candidate, key)))
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
else if (key === 'entries') {
|
|
84
|
+
if (!Array.isArray(value))
|
|
85
|
+
return undefined;
|
|
86
|
+
for (const entry of value) {
|
|
87
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry))
|
|
88
|
+
return undefined;
|
|
89
|
+
const rule = entry;
|
|
90
|
+
if (Object.keys(rule).some(field => field !== 'access' && field !== 'path'))
|
|
91
|
+
return undefined;
|
|
92
|
+
if (rule.access === 'deny')
|
|
93
|
+
continue;
|
|
94
|
+
if ((rule.access !== 'read' && rule.access !== 'write') || !add(rule.path, rule.access))
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else if (value === 'read' || value === 'write') {
|
|
99
|
+
if (!add(key, value))
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
else if (value !== 'deny')
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
if (targets.length === 0)
|
|
106
|
+
return undefined;
|
|
107
|
+
const directories = [...new Set(targets.map(target => {
|
|
108
|
+
try {
|
|
109
|
+
if (fs.statSync(target.path).isDirectory())
|
|
110
|
+
return target.path;
|
|
111
|
+
}
|
|
112
|
+
catch { }
|
|
113
|
+
return path.dirname(target.path);
|
|
114
|
+
}))];
|
|
115
|
+
return {
|
|
116
|
+
targets,
|
|
117
|
+
...(directories.length === 1 && path.dirname(directories[0]) !== directories[0]
|
|
118
|
+
? { directory: directories[0] } : {}),
|
|
119
|
+
access: targets.some(target => target.access === 'write') ? 'write' : 'read',
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export function filesystemScopeContains(scope, requested) {
|
|
123
|
+
// Re-resolve on every use, including missing/new-file ancestors. A directory
|
|
124
|
+
// replaced with a symlink/junction cannot redirect an existing grant.
|
|
125
|
+
if (scope.directory && literalPath(scope.directory, scope.directory) !== scope.directory)
|
|
126
|
+
return false;
|
|
127
|
+
return requested.targets.every(target => {
|
|
128
|
+
if (scope.access !== 'write' && target.access === 'write')
|
|
129
|
+
return false;
|
|
130
|
+
if (scope.directory && !isSameOrDescendant(target.path, scope.directory))
|
|
131
|
+
return false;
|
|
132
|
+
// Do not silently reuse a broad grant for hard-linked file aliases.
|
|
133
|
+
try {
|
|
134
|
+
const stat = fs.statSync(target.path);
|
|
135
|
+
if (stat.isFile() && stat.nlink > 1)
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
catch { }
|
|
139
|
+
return true;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { resolveRoot } from '../../paths.js';
|
|
3
|
+
import { resolveInstanceSocketPath, resolveRoot } from '../../paths.js';
|
|
4
4
|
import { isValidAid } from '../../aun/aid/identity.js';
|
|
5
5
|
const H_CLASS_RELATIVE_PATTERNS = [
|
|
6
6
|
// These are true secret or daemon-owned roots. Agent-owned content is not
|
|
@@ -11,6 +11,7 @@ const H_CLASS_RELATIVE_PATTERNS = [
|
|
|
11
11
|
/^CA(?:\/|$)/,
|
|
12
12
|
/^\.device_id$/,
|
|
13
13
|
/^\.env$/,
|
|
14
|
+
/^providers\.json(?:_|__|\.lock(?:\..*)?)?$/,
|
|
14
15
|
/^\.seed(?:\.|$)/,
|
|
15
16
|
/^\.migrated-/,
|
|
16
17
|
/^\.lock$/,
|
|
@@ -19,6 +20,7 @@ const H_CLASS_RELATIVE_PATTERNS = [
|
|
|
19
20
|
/^agents\/[^/]+\/data\/(?:.*\/)?(?:context-tokens|menu-tokens)\.json(?:_|__|\.tmp-.*)?$/,
|
|
20
21
|
];
|
|
21
22
|
const H_CLASS_REFERENCE_PATTERNS = [
|
|
23
|
+
/(?:^|[^A-Za-z0-9_.-])providers\.json(?:_|__|\.lock(?:\.[^/\\\s"']*)?)?(?=$|[\s"';&|<>)},])/,
|
|
22
24
|
/(?:^|[/\\\s"'=<>])data[\\/]/,
|
|
23
25
|
/(?:^|[/\\\s"'=<>])(?:AIDs|aids)[\\/]/,
|
|
24
26
|
/(?:^|[^A-Za-z0-9_.-])(?:\.device_id|\.env|\.seed(?:\.[^/\\\s"']*)?|\.migrated-[^/\\\s"']*|\.lock|daemon\.pid)(?=$|[\s"';&|<>)},])/,
|
|
@@ -146,6 +148,7 @@ function matchesHClassRelative(relative) {
|
|
|
146
148
|
return false;
|
|
147
149
|
}
|
|
148
150
|
const SELF_AWARE_H_CLASS_RELATIVE_PATTERNS = [
|
|
151
|
+
/^providers\.json(?:_|__|\.lock(?:\..*)?)?$/,
|
|
149
152
|
/^data(?:\/|$)/,
|
|
150
153
|
/^(?:AIDs|aids)(?:\/|$)/,
|
|
151
154
|
/^CA(?:\/|$)/,
|
|
@@ -873,6 +876,16 @@ export function buildCodexProtectedFilesystemRules(root = resolveRoot(), options
|
|
|
873
876
|
...buildCodexHClassFilesystemRules(root, hClassOptions),
|
|
874
877
|
...lClassRules,
|
|
875
878
|
}, platform);
|
|
879
|
+
if (platform === 'linux' && options.outerHClassGuard) {
|
|
880
|
+
const dataDirectory = path.join(resolveThroughExistingAncestor(root), 'data');
|
|
881
|
+
const socketPath = resolveThroughExistingAncestor(resolveInstanceSocketPath(root));
|
|
882
|
+
if (filesystem[dataDirectory] === 'deny'
|
|
883
|
+
&& isSameOrDescendant(socketPath, dataDirectory)
|
|
884
|
+
&& getExistingHClassMaskTargets(root, options).some(target => target.path === dataDirectory && target.kind === 'directory')) {
|
|
885
|
+
filesystem[dataDirectory] = 'read';
|
|
886
|
+
delete filesystem[dataDirectory + '/**'];
|
|
887
|
+
}
|
|
888
|
+
}
|
|
876
889
|
assertWindowsCodexFilesystemRuleBudget(filesystem, platform);
|
|
877
890
|
return filesystem;
|
|
878
891
|
}
|