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
|
@@ -4,8 +4,8 @@ import os from 'os';
|
|
|
4
4
|
import { randomUUID } from 'crypto';
|
|
5
5
|
import { logger } from '../../utils/logger.js';
|
|
6
6
|
import { resolveRoot } from '../../paths.js';
|
|
7
|
-
import { containsHClassReference, containsLClassReference, checkProtectedPathAccess, getExistingHClassMaskTargets, hClassGrantIncludesProtectedPath, isHClassPath, isSameOrDescendant, isLClassPath, lClassGrantIncludesProtectedPath, resolveProtectedCandidate, resolveProtectedCandidateWithoutFinalSymlink, } from './protected-paths.js';
|
|
8
|
-
import { classifyEvolcoreShellCommand, hasCodexCmdCarrierEcIntent, hasPowerShellCredentialAssignment, parseCodexToolCommand, parseBoundedOutputShellCommand, parseLiteralShellArgv, resolveCodexShellCarrierArgv, resolveCodexShellCarrierString, } from './ec-command-parser.js';
|
|
7
|
+
import { containsHClassReference, containsAgentScopeReference, containsLClassReference, checkProtectedPathAccess, getExistingHClassMaskTargets, hClassGrantIncludesProtectedPath, isHClassPath, isSameOrDescendant, isLClassPath, lClassGrantIncludesProtectedPath, resolveProtectedCandidate, resolveProtectedCandidateWithoutFinalSymlink, } from './protected-paths.js';
|
|
8
|
+
import { classifyEvolcoreShellCommand, hasCodexCmdCarrierEcIntent, hasPowerShellCredentialAssignment, parseCodexToolCommand, parseBoundedOutputShellCommand, parseShellApplyPatchInvocation, parseLiteralShellArgv, resolveCodexShellCarrierArgv, resolveCodexShellCarrierString, } from './ec-command-parser.js';
|
|
9
9
|
import { analyzeReadonlyShellQuery, } from './readonly-shell-query.js';
|
|
10
10
|
/** Resolve the session-owned temporary root supplied by the execution host. */
|
|
11
11
|
export function resolveManagedTempDir(env = process.env) {
|
|
@@ -88,6 +88,99 @@ export function checkManagedTempShellCommand(command, options = {}) {
|
|
|
88
88
|
}
|
|
89
89
|
return { kind: 'allow', paths: uniqueCandidates };
|
|
90
90
|
}
|
|
91
|
+
function sharedTemporaryRoots(options) {
|
|
92
|
+
if (process.platform === 'win32')
|
|
93
|
+
return windowsTempRoots(options);
|
|
94
|
+
return ['/tmp', '/var/tmp', '/dev/shm']
|
|
95
|
+
.map(value => resolveProtectedCandidate(value))
|
|
96
|
+
.filter((value, index, values) => values.indexOf(value) === index);
|
|
97
|
+
}
|
|
98
|
+
function collectManagedTempToolPaths(toolName, input) {
|
|
99
|
+
const paths = [];
|
|
100
|
+
if (toolName === 'FileChange') {
|
|
101
|
+
if (typeof input.grantRoot === 'string' && input.grantRoot)
|
|
102
|
+
paths.push(input.grantRoot);
|
|
103
|
+
collectFileChangePaths(input.fileChanges, paths);
|
|
104
|
+
}
|
|
105
|
+
else if (['Write', 'Edit', 'NotebookEdit'].includes(toolName)) {
|
|
106
|
+
for (const key of ['file_path', 'notebook_path', 'path']) {
|
|
107
|
+
if (typeof input[key] === 'string' && input[key])
|
|
108
|
+
paths.push(input[key]);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else if (toolName === 'PermissionGrant') {
|
|
112
|
+
if (typeof input.grantRoot === 'string' && input.grantRoot)
|
|
113
|
+
paths.push(input.grantRoot);
|
|
114
|
+
collectFilesystemWriteGrantPaths(input.permissions, paths);
|
|
115
|
+
collectFilesystemWriteGrantPaths(input.additionalPermissions, paths);
|
|
116
|
+
}
|
|
117
|
+
return [...new Set(paths)];
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Enforce the session temporary boundary for path-oriented tools before a
|
|
121
|
+
* runner-specific approval flow. Project paths remain governed by the normal
|
|
122
|
+
* H/L-class and workspace policies; only temporary-looking operands are
|
|
123
|
+
* restricted to the current managed TMPDIR.
|
|
124
|
+
*/
|
|
125
|
+
export function checkManagedTempToolPaths(toolName, input, options = {}) {
|
|
126
|
+
const candidates = collectManagedTempToolPaths(toolName, input);
|
|
127
|
+
if (candidates.length === 0)
|
|
128
|
+
return { kind: 'none' };
|
|
129
|
+
const root = resolveManagedTempDir(options.env);
|
|
130
|
+
const workspace = options.workspacePath
|
|
131
|
+
? resolveProtectedCandidate(options.workspacePath, options.cwd)
|
|
132
|
+
: undefined;
|
|
133
|
+
const temporaryRoots = sharedTemporaryRoots(options);
|
|
134
|
+
const outside = [];
|
|
135
|
+
let hardLink;
|
|
136
|
+
for (const rawCandidate of candidates) {
|
|
137
|
+
const explicitManagedTemp = /^\$(?:\{TMPDIR\}|TMPDIR)(?:[\\/]|$)/.test(rawCandidate.trim());
|
|
138
|
+
const expanded = substituteTmpDir(rawCandidate, root);
|
|
139
|
+
const lexical = path.isAbsolute(expanded)
|
|
140
|
+
? path.resolve(expanded)
|
|
141
|
+
: path.resolve(options.cwd ?? process.cwd(), expanded);
|
|
142
|
+
const canonical = resolveProtectedCandidate(lexical, options.cwd);
|
|
143
|
+
const canonicalInWorkspace = !!workspace && isSameOrDescendant(canonical, workspace);
|
|
144
|
+
const lexicalInManagedTemp = !!root && isSameOrDescendant(lexical, root);
|
|
145
|
+
const canonicalInManagedTemp = !!root && isSameOrDescendant(canonical, root);
|
|
146
|
+
const temporaryLooking = explicitManagedTemp
|
|
147
|
+
|| lexicalInManagedTemp
|
|
148
|
+
|| temporaryRoots.some(tempRoot => isSameOrDescendant(lexical, tempRoot))
|
|
149
|
+
|| temporaryRoots.some(tempRoot => isSameOrDescendant(canonical, tempRoot));
|
|
150
|
+
if (canonicalInWorkspace && !explicitManagedTemp)
|
|
151
|
+
continue;
|
|
152
|
+
if (!temporaryLooking)
|
|
153
|
+
continue;
|
|
154
|
+
if (!root || !canonicalInManagedTemp) {
|
|
155
|
+
outside.push(rawCandidate);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
const stat = fs.statSync(canonical);
|
|
160
|
+
if (stat.isFile() && stat.nlink > 1)
|
|
161
|
+
hardLink = canonical;
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
// A new target is valid when its canonical existing ancestor is inside
|
|
165
|
+
// the managed root; the runner will create it after this preflight.
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (hardLink) {
|
|
169
|
+
return {
|
|
170
|
+
kind: 'deny',
|
|
171
|
+
paths: [hardLink],
|
|
172
|
+
reason: 'managed TMPDIR target is a hard link and cannot be safely modified',
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (outside.length > 0) {
|
|
176
|
+
return {
|
|
177
|
+
kind: 'deny',
|
|
178
|
+
paths: outside,
|
|
179
|
+
reason: 'temporary path is outside the session-managed TMPDIR',
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return { kind: 'none' };
|
|
183
|
+
}
|
|
91
184
|
/**
|
|
92
185
|
* Replace only already-validated temporary path operands before a second
|
|
93
186
|
* policy pass. This prevents a harmless `$TMPDIR/CA/root` name from looking
|
|
@@ -689,7 +782,7 @@ function protectedClassPath(value, className, options) {
|
|
|
689
782
|
if (!looksLikeLiteralShellPath(value))
|
|
690
783
|
return undefined;
|
|
691
784
|
const matched = className === 'h'
|
|
692
|
-
? (containsHClassReference(value) || isHClassPath(value, options))
|
|
785
|
+
? (containsHClassReference(value, options) || containsAgentScopeReference(value, options) || isHClassPath(value, options))
|
|
693
786
|
: (containsLClassReference(value) || isLClassPath(value, options));
|
|
694
787
|
return matched ? resolveProtectedCandidate(value, options.cwd) : undefined;
|
|
695
788
|
}
|
|
@@ -942,6 +1035,35 @@ function literalGitOperands(argv) {
|
|
|
942
1035
|
function literalReadonlyOperands(argv) {
|
|
943
1036
|
const executable = commandName(argv[0]);
|
|
944
1037
|
const args = argv.slice(1);
|
|
1038
|
+
if (['get-content', 'gc', 'type'].includes(executable)) {
|
|
1039
|
+
const values = [];
|
|
1040
|
+
const pathOptions = new Set(['-path', '-literalpath', '-pspath']);
|
|
1041
|
+
const optionsWithValues = new Set([
|
|
1042
|
+
...pathOptions,
|
|
1043
|
+
'-filter', '-include', '-exclude', '-encoding', '-readcount', '-totalcount',
|
|
1044
|
+
'-delimiter', '-raw', '-wait', '-tail', '-stream', '-force', '-credential',
|
|
1045
|
+
]);
|
|
1046
|
+
for (let index = 0; index < args.length; index++) {
|
|
1047
|
+
const argument = args[index];
|
|
1048
|
+
const option = argument.split('=', 1)[0].toLowerCase();
|
|
1049
|
+
if (argument.startsWith('-')) {
|
|
1050
|
+
if (pathOptions.has(option)) {
|
|
1051
|
+
const value = argument.includes('=') ? argument.slice(argument.indexOf('=') + 1) : args[index + 1];
|
|
1052
|
+
if (value) {
|
|
1053
|
+
values.push(value);
|
|
1054
|
+
if (!argument.includes('='))
|
|
1055
|
+
index++;
|
|
1056
|
+
}
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
if (optionsWithValues.has(option) && !argument.includes('=') && index + 1 < args.length)
|
|
1060
|
+
index++;
|
|
1061
|
+
continue;
|
|
1062
|
+
}
|
|
1063
|
+
values.push(argument);
|
|
1064
|
+
}
|
|
1065
|
+
return values;
|
|
1066
|
+
}
|
|
945
1067
|
if (['cat', 'ls', 'head', 'sort', 'wc', 'stat', 'lstat'].includes(executable)) {
|
|
946
1068
|
const valueOptions = new Set(['-c', '--format', '--printf', '-I', '-T', '-w', '--block-size', '--color', '--format', '--hide', '--ignore', '--key', '--parallel', '--sort']);
|
|
947
1069
|
const values = [];
|
|
@@ -1164,7 +1286,9 @@ function shellOperandMatchesClass(operand, className, options, accessKind = oper
|
|
|
1164
1286
|
return undefined;
|
|
1165
1287
|
}
|
|
1166
1288
|
}
|
|
1167
|
-
const reference = className === 'h'
|
|
1289
|
+
const reference = className === 'h'
|
|
1290
|
+
? (containsHClassReference(operand.value, options) || containsAgentScopeReference(operand.value, options))
|
|
1291
|
+
: containsLClassReference(operand.value);
|
|
1168
1292
|
if (reference)
|
|
1169
1293
|
return resolveProtectedCandidate(operand.value, options.cwd);
|
|
1170
1294
|
if (operand.access === 'recursive') {
|
|
@@ -1192,12 +1316,12 @@ function hClassReadScopeIncludesProtectedPath(value, options) {
|
|
|
1192
1316
|
return true;
|
|
1193
1317
|
if (!isSameOrDescendant(scope, root))
|
|
1194
1318
|
return false;
|
|
1195
|
-
return getExistingHClassMaskTargets(root).some(target => isSameOrDescendant(target.path, scope));
|
|
1319
|
+
return getExistingHClassMaskTargets(root, { selfAid: options.selfAid }).some(target => isSameOrDescendant(target.path, scope));
|
|
1196
1320
|
}
|
|
1197
1321
|
function analyzeShellProtectedOperands(command, options) {
|
|
1198
1322
|
const analysis = analyzeReadonlyShellQuery(command, { managedTempDir: options.managedTempDir });
|
|
1199
1323
|
if (analysis.kind === 'unproven') {
|
|
1200
|
-
const pathOptions = { cwd: options.cwd, root: options.root };
|
|
1324
|
+
const pathOptions = { cwd: options.cwd, root: options.root, selfAid: options.selfAid };
|
|
1201
1325
|
const hClassPath = literalProtectedShellOperand(command, 'h', pathOptions);
|
|
1202
1326
|
const lClassPath = literalProtectedShellOperand(command, 'l', pathOptions);
|
|
1203
1327
|
return {
|
|
@@ -1272,7 +1396,7 @@ function protectedReadToolMatchesLClass(toolName, input, options) {
|
|
|
1272
1396
|
}
|
|
1273
1397
|
if (toolName === 'Glob') {
|
|
1274
1398
|
const scope = globReadScope(input, options);
|
|
1275
|
-
return lClassGrantIncludesProtectedPath(scope, { root: options.root })
|
|
1399
|
+
return lClassGrantIncludesProtectedPath(scope, { root: options.root, selfAid: options.selfAid })
|
|
1276
1400
|
? resolveProtectedCandidate(scope, options.cwd)
|
|
1277
1401
|
: undefined;
|
|
1278
1402
|
}
|
|
@@ -1280,7 +1404,9 @@ function protectedReadToolMatchesLClass(toolName, input, options) {
|
|
|
1280
1404
|
}
|
|
1281
1405
|
function protectedReadToolMatchesHClass(toolName, input, options) {
|
|
1282
1406
|
const paths = protectedReadToolPaths(toolName, input);
|
|
1283
|
-
const direct = paths.find(filePath => containsHClassReference(filePath
|
|
1407
|
+
const direct = paths.find(filePath => containsHClassReference(filePath, options)
|
|
1408
|
+
|| containsAgentScopeReference(filePath, options)
|
|
1409
|
+
|| isHClassPath(filePath, options));
|
|
1284
1410
|
if (direct) {
|
|
1285
1411
|
return resolveProtectedCandidate(direct, options.cwd);
|
|
1286
1412
|
}
|
|
@@ -1447,11 +1573,13 @@ export function checkReadonly(toolName, input, projectPath, context) {
|
|
|
1447
1573
|
]);
|
|
1448
1574
|
if (readOnlyTools.has(toolName)) {
|
|
1449
1575
|
const paths = protectedReadToolPaths(toolName, input);
|
|
1450
|
-
const pathOptions = { cwd: projectPath, root: context?.root };
|
|
1576
|
+
const pathOptions = { cwd: projectPath, root: context?.root, selfAid: context?.selfAid };
|
|
1451
1577
|
if (!protectedReadToolStaysInWorkspace(toolName, input, projectPath)) {
|
|
1452
1578
|
return { behavior: 'deny', message: '🔒 只读模式:文件读取范围不能越出当前项目目录', policyCode: 'readonly_workspace_escape' };
|
|
1453
1579
|
}
|
|
1454
|
-
const directHPath = paths.find(filePath => containsHClassReference(filePath
|
|
1580
|
+
const directHPath = paths.find(filePath => containsHClassReference(filePath, pathOptions)
|
|
1581
|
+
|| containsAgentScopeReference(filePath, pathOptions)
|
|
1582
|
+
|| isHClassPath(filePath, pathOptions));
|
|
1455
1583
|
if (directHPath) {
|
|
1456
1584
|
return { behavior: 'deny', message: '🔒 只读模式:不允许读取 H 类受保护路径', policyCode: 'readonly_h_class_read', matchedPath: resolveProtectedCandidate(directHPath, projectPath) };
|
|
1457
1585
|
}
|
|
@@ -1480,6 +1608,7 @@ export function checkReadonly(toolName, input, projectPath, context) {
|
|
|
1480
1608
|
: context?.managedTempDir ?? resolveManagedTempDir();
|
|
1481
1609
|
const protectedOperands = analyzeShellProtectedOperands(cmd, {
|
|
1482
1610
|
cwd: projectPath,
|
|
1611
|
+
selfAid: context?.selfAid,
|
|
1483
1612
|
root: context?.root,
|
|
1484
1613
|
managedTempDir,
|
|
1485
1614
|
allowProtectedMetadata: context?.allowProtectedMetadata,
|
|
@@ -1725,6 +1854,32 @@ function collectFileChangePaths(value, output) {
|
|
|
1725
1854
|
}
|
|
1726
1855
|
}
|
|
1727
1856
|
}
|
|
1857
|
+
function applyPatchChangePaths(changes) {
|
|
1858
|
+
const paths = [];
|
|
1859
|
+
for (const change of changes) {
|
|
1860
|
+
paths.push(change.path);
|
|
1861
|
+
if (change.movePath)
|
|
1862
|
+
paths.push(change.movePath);
|
|
1863
|
+
}
|
|
1864
|
+
return paths;
|
|
1865
|
+
}
|
|
1866
|
+
function applyPatchWorkspaceEscape(changes, context) {
|
|
1867
|
+
const workspace = resolveProtectedCandidate(context.workspacePath ?? context.projectPath);
|
|
1868
|
+
const managedTempDir = context.managedTempDir
|
|
1869
|
+
? resolveProtectedCandidate(context.managedTempDir)
|
|
1870
|
+
: undefined;
|
|
1871
|
+
for (const candidate of applyPatchChangePaths(changes)) {
|
|
1872
|
+
const resolved = path.isAbsolute(candidate)
|
|
1873
|
+
? resolveProtectedCandidate(candidate)
|
|
1874
|
+
: resolveProtectedCandidate(candidate, workspace);
|
|
1875
|
+
if (isSameOrDescendant(resolved, workspace))
|
|
1876
|
+
continue;
|
|
1877
|
+
if (managedTempDir && isSameOrDescendant(resolved, managedTempDir))
|
|
1878
|
+
continue;
|
|
1879
|
+
return resolved;
|
|
1880
|
+
}
|
|
1881
|
+
return undefined;
|
|
1882
|
+
}
|
|
1728
1883
|
function requestsFilesystemRoot(value) {
|
|
1729
1884
|
if (Array.isArray(value))
|
|
1730
1885
|
return value.some(requestsFilesystemRoot);
|
|
@@ -1793,6 +1948,7 @@ export function checkHClassWrite(toolName, input, context) {
|
|
|
1793
1948
|
: command;
|
|
1794
1949
|
const protectedOperands = analyzeShellProtectedOperands(policyCommand, {
|
|
1795
1950
|
cwd: context?.projectPath,
|
|
1951
|
+
selfAid: context?.selfAid,
|
|
1796
1952
|
root: context?.root,
|
|
1797
1953
|
managedTempDir: context?.managedTempDir,
|
|
1798
1954
|
allowProtectedMetadata: context?.allowProtectedMetadata,
|
|
@@ -1830,7 +1986,7 @@ export function checkHClassWrite(toolName, input, context) {
|
|
|
1830
1986
|
// *.json_ backup indistinguishable from an actual file access. Any nested
|
|
1831
1987
|
// filesystem operation is checked again when it reaches Read/Write/Bash,
|
|
1832
1988
|
// FileChange, or PermissionGrant above.
|
|
1833
|
-
const pathOptions = { cwd: context?.projectPath, root: context?.root };
|
|
1989
|
+
const pathOptions = { cwd: context?.projectPath, root: context?.root, selfAid: context?.selfAid };
|
|
1834
1990
|
if (['Read', 'Glob', 'Grep'].includes(toolName)) {
|
|
1835
1991
|
const matchedReadPath = protectedReadToolMatchesHClass(toolName, input, pathOptions);
|
|
1836
1992
|
if (matchedReadPath) {
|
|
@@ -1854,7 +2010,8 @@ export function checkHClassWrite(toolName, input, context) {
|
|
|
1854
2010
|
for (const grantPath of grantPaths) {
|
|
1855
2011
|
if (!grantPath)
|
|
1856
2012
|
continue;
|
|
1857
|
-
if (containsHClassReference(grantPath) ||
|
|
2013
|
+
if (containsHClassReference(grantPath, pathOptions) || containsAgentScopeReference(grantPath, pathOptions)
|
|
2014
|
+
|| hClassGrantIncludesProtectedPath(grantPath, pathOptions)) {
|
|
1858
2015
|
logger.warn(`[H-Class Protection] 🔒 Permission grant covers protected paths: tool=${toolName} path=${grantPath} ` +
|
|
1859
2016
|
`session=${context?.sessionId} channel=${context?.channel} peer=${context?.peerId} role=${context?.role}`);
|
|
1860
2017
|
return {
|
|
@@ -1877,7 +2034,8 @@ export function checkHClassWrite(toolName, input, context) {
|
|
|
1877
2034
|
const candidate = path.isAbsolute(subpath)
|
|
1878
2035
|
? subpath
|
|
1879
2036
|
: path.resolve(permissionWorkspace, subpath);
|
|
1880
|
-
if (containsHClassReference(subpath) ||
|
|
2037
|
+
if (containsHClassReference(subpath, pathOptions) || containsAgentScopeReference(subpath, pathOptions)
|
|
2038
|
+
|| hClassGrantIncludesProtectedPath(candidate, pathOptions)) {
|
|
1881
2039
|
logger.warn(`[H-Class Protection] 🔒 project_roots grant covers protected paths: tool=${toolName} path=${subpath} ` +
|
|
1882
2040
|
`workspace=${permissionWorkspace} session=${context?.sessionId} channel=${context?.channel} ` +
|
|
1883
2041
|
`peer=${context?.peerId} role=${context?.role}`);
|
|
@@ -1890,7 +2048,9 @@ export function checkHClassWrite(toolName, input, context) {
|
|
|
1890
2048
|
}
|
|
1891
2049
|
for (const filePath of paths) {
|
|
1892
2050
|
const normalized = filePath.replace(/\\/g, '/');
|
|
1893
|
-
const matched = containsHClassReference(normalized
|
|
2051
|
+
const matched = containsHClassReference(normalized, pathOptions)
|
|
2052
|
+
|| containsAgentScopeReference(normalized, pathOptions)
|
|
2053
|
+
|| isHClassPath(filePath, pathOptions);
|
|
1894
2054
|
if (matched) {
|
|
1895
2055
|
const fileBasename = path.basename(filePath);
|
|
1896
2056
|
logger.warn(`[H-Class Protection] 🔒 Protected file access blocked: tool=${toolName} path=${filePath} ` +
|
|
@@ -1932,7 +2092,7 @@ export function checkHClassWrite(toolName, input, context) {
|
|
|
1932
2092
|
*/
|
|
1933
2093
|
export function checkLClassWrite(toolName, input, context) {
|
|
1934
2094
|
if (['Read', 'Glob', 'Grep'].includes(toolName)) {
|
|
1935
|
-
const pathOptions = { cwd: context?.projectPath, root: context?.root };
|
|
2095
|
+
const pathOptions = { cwd: context?.projectPath, root: context?.root, selfAid: context?.selfAid };
|
|
1936
2096
|
const lClassRead = protectedReadToolMatchesLClass(toolName, input, pathOptions);
|
|
1937
2097
|
if (lClassRead && context?.permissionMode === 'readonly') {
|
|
1938
2098
|
return { behavior: 'deny', message: '🔒 只读模式:visitor 最低权限不允许读取 L-class 路径', matchedPath: lClassRead };
|
|
@@ -1961,7 +2121,7 @@ export function checkLClassWrite(toolName, input, context) {
|
|
|
1961
2121
|
}
|
|
1962
2122
|
else if (toolName === 'Bash') {
|
|
1963
2123
|
const command = typeof input.command === 'string' ? input.command : '';
|
|
1964
|
-
const pathOptions = { cwd: context?.projectPath, root: context?.root };
|
|
2124
|
+
const pathOptions = { cwd: context?.projectPath, root: context?.root, selfAid: context?.selfAid };
|
|
1965
2125
|
const protectedOperands = analyzeShellProtectedOperands(command, {
|
|
1966
2126
|
...pathOptions,
|
|
1967
2127
|
managedTempDir: context?.managedTempDir,
|
|
@@ -1991,7 +2151,7 @@ export function checkLClassWrite(toolName, input, context) {
|
|
|
1991
2151
|
// Do not infer mutation targets from arbitrary orchestration-tool strings.
|
|
1992
2152
|
// Skills commonly pass args: "" and delegation prompts may discuss
|
|
1993
2153
|
// protected paths; neither is itself a filesystem mutation.
|
|
1994
|
-
const pathOptions = { cwd: context?.projectPath, root: context?.root };
|
|
2154
|
+
const pathOptions = { cwd: context?.projectPath, root: context?.root, selfAid: context?.selfAid };
|
|
1995
2155
|
for (const grantPath of writeGrantPaths) {
|
|
1996
2156
|
if (grantPath && lClassGrantIncludesProtectedPath(grantPath, pathOptions)) {
|
|
1997
2157
|
logger.warn(`[L-Class Protection] Write grant covers protected paths: tool=${toolName} path=${grantPath}`);
|
|
@@ -2008,7 +2168,7 @@ export function checkLClassWrite(toolName, input, context) {
|
|
|
2008
2168
|
return { behavior: 'deny', message: '🔒 无法确定 L-class 写权限的 thread workspace,已拒绝授权' };
|
|
2009
2169
|
}
|
|
2010
2170
|
const candidate = path.isAbsolute(subpath) ? subpath : path.resolve(permissionWorkspace, subpath);
|
|
2011
|
-
if (lClassGrantIncludesProtectedPath(candidate, { root: context?.root })) {
|
|
2171
|
+
if (lClassGrantIncludesProtectedPath(candidate, { root: context?.root, selfAid: context?.selfAid })) {
|
|
2012
2172
|
return { behavior: 'deny', message: '🔒 请求的 project_roots 写权限包含 L-class 只读路径,已拒绝授权' };
|
|
2013
2173
|
}
|
|
2014
2174
|
}
|
|
@@ -2053,6 +2213,7 @@ export function checkBlacklist(toolName, input) {
|
|
|
2053
2213
|
function managedTempOptions(context) {
|
|
2054
2214
|
return {
|
|
2055
2215
|
cwd: context.projectPath,
|
|
2216
|
+
workspacePath: context.workspacePath ?? context.projectPath,
|
|
2056
2217
|
// A managed session must never inherit the daemon's unrelated TMPDIR.
|
|
2057
2218
|
env: context.sessionId
|
|
2058
2219
|
? { ...process.env, TMPDIR: context.managedTempDir ?? '' }
|
|
@@ -2061,6 +2222,28 @@ function managedTempOptions(context) {
|
|
|
2061
2222
|
: process.env,
|
|
2062
2223
|
};
|
|
2063
2224
|
}
|
|
2225
|
+
/**
|
|
2226
|
+
* 校验 `cd <dir> && ec ...` 白名单里的目录。
|
|
2227
|
+
*
|
|
2228
|
+
* 白名单放宽的是"前缀拼接"这一层,目录本身的可见范围不能跟着放大:它必须
|
|
2229
|
+
* 落在当前项目(或 workspace)之内。目录是字面量、已经过词法层过滤(不含
|
|
2230
|
+
* 变量展开/通配),这里只需做包含性判断,禁用相对路径逃逸与绝对路径出界。
|
|
2231
|
+
*/
|
|
2232
|
+
function validateCdPrefixedCwd(cwd, context) {
|
|
2233
|
+
// 零副作用前缀(pwd/echo)不带目录,无需校验。
|
|
2234
|
+
if (!cwd)
|
|
2235
|
+
return undefined;
|
|
2236
|
+
const projectRoot = resolveProtectedCandidate(context.projectPath);
|
|
2237
|
+
const workspaceRoot = resolveProtectedCandidate(context.workspacePath ?? context.projectPath);
|
|
2238
|
+
// 相对路径按项目根解析——与 shell 里 cd 的语义一致(runner 的起始目录)。
|
|
2239
|
+
const resolved = path.isAbsolute(cwd)
|
|
2240
|
+
? resolveProtectedCandidate(cwd)
|
|
2241
|
+
: resolveProtectedCandidate(path.resolve(projectRoot, cwd));
|
|
2242
|
+
if (!isSameOrDescendant(resolved, projectRoot) && !isSameOrDescendant(resolved, workspaceRoot)) {
|
|
2243
|
+
return '🔒 `cd <目录> && ec ...` 的目录必须位于当前项目范围内;如需其它目录请直接调用 `ec ...`,不要拼接 `cd`。';
|
|
2244
|
+
}
|
|
2245
|
+
return undefined;
|
|
2246
|
+
}
|
|
2064
2247
|
function validateBoundedOutputPath(command, context) {
|
|
2065
2248
|
const outputPath = command.outputPath;
|
|
2066
2249
|
if (!outputPath)
|
|
@@ -2140,14 +2323,18 @@ function prepareBoundedOutputInput(input, command, kind, context) {
|
|
|
2140
2323
|
return { behavior: 'deny', input, message: '🔒 安全输出 helper 不可用,命令已拒绝', policyCode: 'bounded_output_helper_unavailable' };
|
|
2141
2324
|
}
|
|
2142
2325
|
const payload = Buffer.from(JSON.stringify({
|
|
2143
|
-
|
|
2326
|
+
// v3 carries a chain of stdin-only filters; v1 named only the filter's
|
|
2327
|
+
// arguments and v2 carried a single filter argv, so both are rejected
|
|
2328
|
+
// rather than misread.
|
|
2329
|
+
v: 3,
|
|
2144
2330
|
nonce: randomUUID(),
|
|
2145
2331
|
kind,
|
|
2146
2332
|
workspace: path.resolve(context.workspacePath ?? context.projectPath),
|
|
2147
|
-
cwd: path.resolve(context.projectPath),
|
|
2333
|
+
cwd: command.cwd ? path.resolve(command.cwd) : path.resolve(context.projectPath),
|
|
2148
2334
|
argv: command.argv,
|
|
2149
2335
|
mergeStderr: command.mergeStderr,
|
|
2150
|
-
...(command.
|
|
2336
|
+
...(command.filterChain?.length ? { filterChain: command.filterChain } : {}),
|
|
2337
|
+
...(command.suffixArgv?.length ? { suffixArgv: command.suffixArgv } : {}),
|
|
2151
2338
|
...(command.outputPath
|
|
2152
2339
|
? { outputPath: expandManagedTempPath(command.outputPath, managedTempOptions(context)) }
|
|
2153
2340
|
: {}),
|
|
@@ -2161,6 +2348,18 @@ function prepareBoundedOutputInput(input, command, kind, context) {
|
|
|
2161
2348
|
}
|
|
2162
2349
|
/** Shared fail-closed preflight used before runner-specific mode and approval handling. */
|
|
2163
2350
|
export function evaluateToolPreflight(toolName, input, context) {
|
|
2351
|
+
let applyPatchFileChanges;
|
|
2352
|
+
const managedPathCheck = checkManagedTempToolPaths(toolName, input, managedTempOptions(context));
|
|
2353
|
+
if (managedPathCheck.kind === 'deny') {
|
|
2354
|
+
const currentTmpDir = resolveManagedTempDir(managedTempOptions(context).env);
|
|
2355
|
+
return {
|
|
2356
|
+
behavior: 'deny',
|
|
2357
|
+
input,
|
|
2358
|
+
message: `🔒 临时文件必须位于当前会话的 $TMPDIR 内(当前值:${currentTmpDir ?? '<不可用>'}):${managedPathCheck.reason}`,
|
|
2359
|
+
policyCode: 'temporary_path_outside_tmpdir',
|
|
2360
|
+
matchedPath: managedPathCheck.paths[0],
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2164
2363
|
if (toolName === 'Bash') {
|
|
2165
2364
|
const command = typeof input.command === 'string' ? input.command : '';
|
|
2166
2365
|
const explicitCommandArgv = Array.isArray(input.commandArgv)
|
|
@@ -2215,6 +2414,41 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
2215
2414
|
: {}),
|
|
2216
2415
|
};
|
|
2217
2416
|
const parsedCodex = context.sessionId ? parseCodexToolCommand(input, parserOptions) : null;
|
|
2417
|
+
const applyPatch = parseShellApplyPatchInvocation(input, parserOptions, parsedCodex?.ok ? parsedCodex.argv : undefined);
|
|
2418
|
+
if (applyPatch.kind === 'invalid') {
|
|
2419
|
+
return {
|
|
2420
|
+
behavior: 'deny',
|
|
2421
|
+
input,
|
|
2422
|
+
message: '文件修改 patch 无法安全解析:' + applyPatch.reason,
|
|
2423
|
+
policyCode: 'apply_patch_unparseable',
|
|
2424
|
+
errorDetailCode: 'APPLY_PATCH_UNPARSEABLE',
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
if (applyPatch.kind === 'file-change') {
|
|
2428
|
+
applyPatchFileChanges = applyPatch.fileChanges;
|
|
2429
|
+
if (context.permissionMode === 'readonly') {
|
|
2430
|
+
return {
|
|
2431
|
+
behavior: 'deny',
|
|
2432
|
+
input,
|
|
2433
|
+
message: '🔒 只读模式:禁止通过 apply_patch 修改文件',
|
|
2434
|
+
policyCode: 'readonly_file_write',
|
|
2435
|
+
errorDetailCode: 'READONLY_FILE_WRITE',
|
|
2436
|
+
};
|
|
2437
|
+
}
|
|
2438
|
+
const escapedPath = applyPatchWorkspaceEscape(applyPatch.fileChanges, context);
|
|
2439
|
+
if (escapedPath && context.sessionId
|
|
2440
|
+
&& context.permissionMode !== 'request'
|
|
2441
|
+
&& context.permissionMode !== 'fullaccess') {
|
|
2442
|
+
return {
|
|
2443
|
+
behavior: 'deny',
|
|
2444
|
+
input,
|
|
2445
|
+
message: '🔒 apply_patch 目标必须位于当前 workspace 或会话 $TMPDIR 内',
|
|
2446
|
+
policyCode: 'apply_patch_workspace_escape',
|
|
2447
|
+
errorDetailCode: 'APPLY_PATCH_WORKSPACE_ESCAPE',
|
|
2448
|
+
matchedPath: escapedPath,
|
|
2449
|
+
};
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2218
2452
|
if (context.sessionId && parsedCodex && !parsedCodex.ok && parsedCodex.issue === 'untrusted-shell') {
|
|
2219
2453
|
return {
|
|
2220
2454
|
behavior: 'deny',
|
|
@@ -2310,6 +2544,13 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
2310
2544
|
if (ecCommand.kind === 'bounded-output') {
|
|
2311
2545
|
return prepareBoundedOutputInput(input, ecCommand.command, 'ec', context);
|
|
2312
2546
|
}
|
|
2547
|
+
if (ecCommand.kind === 'cd-prefixed') {
|
|
2548
|
+
const cwdError = validateCdPrefixedCwd(ecCommand.cwd, context);
|
|
2549
|
+
if (cwdError) {
|
|
2550
|
+
return { behavior: 'deny', input, message: cwdError, policyCode: 'ec_shell_composite_command' };
|
|
2551
|
+
}
|
|
2552
|
+
return prepareBoundedOutputInput(input, ecCommand.command, 'ec', context);
|
|
2553
|
+
}
|
|
2313
2554
|
if (ecCommand.kind === 'composite') {
|
|
2314
2555
|
if (ecCommand.issue === 'untrusted-shell') {
|
|
2315
2556
|
return {
|
|
@@ -2334,7 +2575,7 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
2334
2575
|
}
|
|
2335
2576
|
if (ecCommand.issue === 'invalid-quote') {
|
|
2336
2577
|
const message = shellDialect === 'powershell'
|
|
2337
|
-
? '🔒 EC 命令引号未闭合或存在错误嵌套;PowerShell
|
|
2578
|
+
? '🔒 EC 命令引号未闭合或存在错误嵌套;PowerShell 也会解释中文弯引号。请按 PowerShell 规则转义,或把消息正文写入当前 $TMPDIR 后使用 --text-from-file'
|
|
2338
2579
|
: shellDialect === 'cmd'
|
|
2339
2580
|
? '🔒 EC 命令引号未闭合或存在错误嵌套;cmd.exe carrier 的命令正文请保持一个完整参数,不要用未配对的双引号'
|
|
2340
2581
|
: '🔒 EC 命令引号未闭合或存在错误嵌套;正文中的双引号请写成 \\"';
|
|
@@ -2345,6 +2586,12 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
2345
2586
|
policyCode: 'ec_shell_invalid_quote',
|
|
2346
2587
|
};
|
|
2347
2588
|
}
|
|
2589
|
+
if (ecCommand.issue === 'unsupported-escape') {
|
|
2590
|
+
return { behavior: 'deny', input, message: '🔒 EC 内联文本不支持 PowerShell `n;请使用字面量 \\n。', policyCode: 'ec_shell_unsupported_escape' };
|
|
2591
|
+
}
|
|
2592
|
+
if (ecCommand.issue === 'lossy-cmd-shim') {
|
|
2593
|
+
return { behavior: 'deny', input, message: '🔒 PowerShell 调用 ec.cmd 会截断物理多行参数;请保持原有 ec ... 调用,由 PowerShell 原生 shim 无损传递。', policyCode: 'ec_shell_lossy_cmd_shim' };
|
|
2594
|
+
}
|
|
2348
2595
|
const message = shellDialect === 'powershell'
|
|
2349
2596
|
? '🔒 EC 命令调用被拒绝:一次 PowerShell/exec_command 只能执行一条完整的 `ec` 命令。请直接调用 `ec ...`,不要拼接探测命令,也不要使用 `&&`、`||`、`;`、`|`、重定向、变量展开或子表达式追加其它命令。'
|
|
2350
2597
|
: shellDialect === 'cmd'
|
|
@@ -2375,11 +2622,16 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
2375
2622
|
return { behavior: 'deny', input, message: blacklist.message, policyCode: 'tool_blacklist' };
|
|
2376
2623
|
}
|
|
2377
2624
|
const checkedInput = blacklist.updatedInput;
|
|
2378
|
-
const
|
|
2625
|
+
const policyInput = applyPatchFileChanges
|
|
2626
|
+
? { ...checkedInput, fileChanges: applyPatchFileChanges }
|
|
2627
|
+
: checkedInput;
|
|
2628
|
+
const policyToolName = applyPatchFileChanges ? 'FileChange' : toolName;
|
|
2629
|
+
const hClass = checkHClassWrite(policyToolName, policyInput, {
|
|
2379
2630
|
sessionId: context.sessionId,
|
|
2380
2631
|
channel: context.channel,
|
|
2381
2632
|
peerId: context.userId,
|
|
2382
2633
|
role: context.role,
|
|
2634
|
+
selfAid: context.selfAid,
|
|
2383
2635
|
projectPath: context.projectPath,
|
|
2384
2636
|
workspacePath: context.workspacePath,
|
|
2385
2637
|
root: context.root,
|
|
@@ -2391,11 +2643,12 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
2391
2643
|
if (hClass.behavior === 'deny') {
|
|
2392
2644
|
return { behavior: 'deny', input: checkedInput, message: hClass.message, policyCode: 'h_class_protection', matchedPath: hClass.matchedPath };
|
|
2393
2645
|
}
|
|
2394
|
-
const lClass = checkLClassWrite(
|
|
2646
|
+
const lClass = checkLClassWrite(policyToolName, policyInput, {
|
|
2395
2647
|
sessionId: context.sessionId,
|
|
2396
2648
|
channel: context.channel,
|
|
2397
2649
|
peerId: context.userId,
|
|
2398
2650
|
role: context.role,
|
|
2651
|
+
selfAid: context.selfAid,
|
|
2399
2652
|
permissionMode: context.permissionMode,
|
|
2400
2653
|
projectPath: context.projectPath,
|
|
2401
2654
|
workspacePath: context.workspacePath,
|
|
@@ -2,7 +2,7 @@ import { getRoleDefinition } from '../../config/roles.js';
|
|
|
2
2
|
import { PUBLIC_PERMISSION_MODES } from '../permission/mode.js';
|
|
3
3
|
import { ConfigTarget, read } from '../../config/config-manager.js';
|
|
4
4
|
import { resolvePrincipal } from '../../config/contact-book.js';
|
|
5
|
-
import {
|
|
5
|
+
import { loadDaemonConfig } from '../../config-store.js';
|
|
6
6
|
import { hasTrustedPrincipal } from '../auth/authenticated-actor.js';
|
|
7
7
|
const PERMISSION_MODE_AUTHORITY = {
|
|
8
8
|
readonly: 0,
|
|
@@ -145,17 +145,16 @@ export function validateRuntimeStringFieldOverride(input) {
|
|
|
145
145
|
}
|
|
146
146
|
/**
|
|
147
147
|
* Resolve the Agent-level host execution policy for one authenticated principal.
|
|
148
|
-
* The Agent flag is
|
|
148
|
+
* The Agent flag is independent of role permissionMode values.
|
|
149
149
|
*/
|
|
150
150
|
export function agentFullAccessStatus(agentAid, principalId) {
|
|
151
151
|
const config = read(ConfigTarget.Agent, { self: agentAid }, { cache: false });
|
|
152
152
|
const daemon = loadDaemonConfig();
|
|
153
153
|
const enabled = config?.fullaccess?.enabled === true;
|
|
154
|
-
const hostEnabled = isFullAccessEnabled(daemon);
|
|
155
154
|
const eligible = !!principalId
|
|
156
155
|
&& !!config?.owners?.includes(principalId)
|
|
157
156
|
&& !!daemon.owners?.includes(principalId);
|
|
158
|
-
return { enabled,
|
|
157
|
+
return { enabled, eligible, effective: enabled && eligible };
|
|
159
158
|
}
|
|
160
159
|
/** Create a trusted per-call claim for a direct private message. */
|
|
161
160
|
export function createAgentPolicyAuthorization(input) {
|
|
@@ -198,8 +197,5 @@ export function agentFullAccessWriteError(input) {
|
|
|
198
197
|
if (!input.daemonOwner || input.delegated) {
|
|
199
198
|
return 'Only authenticated ECWeb management may configure Agent fullaccess';
|
|
200
199
|
}
|
|
201
|
-
if (input.enabled && !isFullAccessEnabled()) {
|
|
202
|
-
return 'Host fullaccess is disabled';
|
|
203
|
-
}
|
|
204
200
|
return undefined;
|
|
205
201
|
}
|
|
@@ -86,7 +86,7 @@ function eventTaskId(event, aliases) {
|
|
|
86
86
|
const useId = typeof event.toolUseId === 'string' ? event.toolUseId.trim() : '';
|
|
87
87
|
return useId ? aliases.get(useId) ?? `tool:${useId}` : null;
|
|
88
88
|
}
|
|
89
|
-
export function
|
|
89
|
+
export function readRunnerEventSummary(logsDir, evolSessionId) {
|
|
90
90
|
let files;
|
|
91
91
|
try {
|
|
92
92
|
files = fs.readdirSync(logsDir)
|
|
@@ -545,82 +545,6 @@ export function scanNestedAgentChatDirs(sessionsRoot) {
|
|
|
545
545
|
}
|
|
546
546
|
return results.sort((left, right) => left.dirPath < right.dirPath ? -1 : left.dirPath > right.dirPath ? 1 : 0);
|
|
547
547
|
}
|
|
548
|
-
/** Scan the pre-migration data/sessions layout without changing it. */
|
|
549
|
-
export function scanLegacyChatDirs(sessionsDir) {
|
|
550
|
-
const results = [];
|
|
551
|
-
let typeEntries;
|
|
552
|
-
try {
|
|
553
|
-
typeEntries = fs.readdirSync(sessionsDir, { withFileTypes: true });
|
|
554
|
-
}
|
|
555
|
-
catch {
|
|
556
|
-
return results;
|
|
557
|
-
}
|
|
558
|
-
for (const typeEntry of typeEntries) {
|
|
559
|
-
if (!typeEntry.isDirectory())
|
|
560
|
-
continue;
|
|
561
|
-
const channelType = typeEntry.name;
|
|
562
|
-
const typeDir = path.join(sessionsDir, channelType);
|
|
563
|
-
let level2Entries;
|
|
564
|
-
try {
|
|
565
|
-
level2Entries = fs.readdirSync(typeDir, { withFileTypes: true });
|
|
566
|
-
}
|
|
567
|
-
catch {
|
|
568
|
-
continue;
|
|
569
|
-
}
|
|
570
|
-
if (channelType === 'aun') {
|
|
571
|
-
for (const selfEntry of level2Entries) {
|
|
572
|
-
if (!selfEntry.isDirectory())
|
|
573
|
-
continue;
|
|
574
|
-
const selfDir = path.join(typeDir, selfEntry.name);
|
|
575
|
-
let chatEntries;
|
|
576
|
-
try {
|
|
577
|
-
chatEntries = fs.readdirSync(selfDir, { withFileTypes: true });
|
|
578
|
-
}
|
|
579
|
-
catch {
|
|
580
|
-
continue;
|
|
581
|
-
}
|
|
582
|
-
for (const chatEntry of chatEntries)
|
|
583
|
-
if (chatEntry.isDirectory())
|
|
584
|
-
results.push({
|
|
585
|
-
channelType, selfAID: decodeSegment(selfEntry.name), channelId: decodeSegment(chatEntry.name), dirPath: path.join(selfDir, chatEntry.name),
|
|
586
|
-
});
|
|
587
|
-
}
|
|
588
|
-
continue;
|
|
589
|
-
}
|
|
590
|
-
if (channelType === 'wecom') {
|
|
591
|
-
for (const entry of level2Entries) {
|
|
592
|
-
if (!entry.isDirectory())
|
|
593
|
-
continue;
|
|
594
|
-
const decoded = decodeSegment(entry.name);
|
|
595
|
-
const entryDir = path.join(typeDir, entry.name);
|
|
596
|
-
if (decoded.startsWith('wecom#') && decoded.split('#').length === 3) {
|
|
597
|
-
let chatEntries;
|
|
598
|
-
try {
|
|
599
|
-
chatEntries = fs.readdirSync(entryDir, { withFileTypes: true });
|
|
600
|
-
}
|
|
601
|
-
catch {
|
|
602
|
-
continue;
|
|
603
|
-
}
|
|
604
|
-
for (const chatEntry of chatEntries)
|
|
605
|
-
if (chatEntry.isDirectory())
|
|
606
|
-
results.push({
|
|
607
|
-
channelType, selfAID: '', channelKey: decoded, channelId: decodeSegment(chatEntry.name), dirPath: path.join(entryDir, chatEntry.name),
|
|
608
|
-
});
|
|
609
|
-
}
|
|
610
|
-
else {
|
|
611
|
-
results.push({ channelType, selfAID: '', channelId: decoded, dirPath: entryDir });
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
continue;
|
|
615
|
-
}
|
|
616
|
-
for (const chatEntry of level2Entries)
|
|
617
|
-
if (chatEntry.isDirectory())
|
|
618
|
-
results.push({
|
|
619
|
-
channelType, selfAID: '', channelId: decodeSegment(chatEntry.name), dirPath: path.join(typeDir, chatEntry.name),
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
return results.sort((left, right) => left.dirPath < right.dirPath ? -1 : left.dirPath > right.dirPath ? 1 : 0);
|
|
623
|
-
}
|
|
624
548
|
export function scanMetaFiles(chatDir) {
|
|
625
549
|
try {
|
|
626
550
|
const entries = fs.readdirSync(chatDir);
|