evolcore 0.0.11 → 0.0.13
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 +44 -0
- package/bin/codex-managed-hook.mjs +80 -0
- package/bin/ec-safe-output.js +26 -7
- package/dist/agents/claude-runner.js +86 -7
- package/dist/agents/codex-app-server-client.js +174 -20
- package/dist/agents/codex-runner.js +375 -92
- package/dist/agents/ecagent-runner.js +39 -4
- package/dist/agents/gemini-runner.js +1 -0
- package/dist/aun/aid/client.js +11 -21
- package/dist/aun/aid/control-aid.js +36 -13
- package/dist/aun/aid/managed-operation.js +107 -0
- package/dist/aun/aid/store.js +13 -7
- package/dist/aun/group-fs-download.js +56 -0
- package/dist/aun/group-identity.js +1 -2
- package/dist/aun/msg/group.js +91 -44
- package/dist/aun/msg/index.js +1 -2
- package/dist/aun/msg/managed-operation.js +865 -0
- package/dist/aun/msg/mention-schema.js +20 -0
- package/dist/aun/msg/p2p.js +7 -0
- package/dist/aun/msg/upload.js +16 -1
- package/dist/aun/outbox.js +31 -2
- package/dist/aun/rpc/client.js +66 -0
- package/dist/aun/rpc/index.js +1 -2
- package/dist/aun/storage/{upload.js → client.js} +11 -2
- package/dist/aun/storage/index.js +1 -2
- package/dist/channels/aun.js +655 -240
- package/dist/channels/daemon.js +13 -0
- package/dist/cli/agent-command.js +56 -8
- package/dist/cli/agent.js +108 -31
- package/dist/cli/aun-commands.js +270 -69
- package/dist/cli/bench.js +12 -3
- package/dist/cli/cli-argv.js +10 -0
- package/dist/cli/config.js +9 -17
- package/dist/cli/ctl-command.js +1 -1
- package/dist/cli/daemon-commands.js +70 -55
- package/dist/cli/fs-command.js +79 -10
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +30 -2
- package/dist/cli/init.js +77 -8
- package/dist/cli/managed-command-guard.js +41 -0
- package/dist/cli/model.js +11 -4
- package/dist/cli/queue-command.js +53 -1
- package/dist/cli/response.js +71 -0
- package/dist/cli/restart-monitor.js +7 -32
- package/dist/cli/trigger-command.js +11 -0
- package/dist/config/access-policy.js +1 -2
- package/dist/config/builtin-roles.js +9 -0
- package/dist/config/config-manager.js +21 -1
- package/dist/config/contact-book-store.js +11 -3
- package/dist/config/contact-request-service.js +161 -15
- package/dist/config/mention-mode.js +8 -24
- package/dist/config/peer-role-resolver.js +6 -2
- package/dist/config/role-migration-startup.js +53 -0
- package/dist/config/role-store.js +1 -1
- package/dist/core/auth/agent-delegation.js +1 -2
- package/dist/core/auth/auth-gateway.js +29 -1
- package/dist/core/auth/authorization-audit.js +15 -0
- package/dist/core/auth/operation-authorizer.js +31 -7
- package/dist/core/auth/operation-catalog.js +196 -25
- package/dist/core/bootstrap-messages.js +25 -0
- package/dist/core/bootstrap-service.js +64 -4
- package/dist/core/capability/providers/claude-capability-provider.js +9 -31
- package/dist/core/capability/providers/codex-capability-provider.js +4 -15
- package/dist/core/capability/skill-discovery.js +55 -0
- package/dist/core/channel-loader.js +1 -1
- package/dist/core/command/cli-intent-parser.js +9 -0
- package/dist/core/command/command-handler.js +650 -42
- package/dist/core/command/connect-menu.js +15 -0
- package/dist/core/command/group-menu.js +4 -4
- package/dist/core/command/menu-catalog.js +370 -0
- package/dist/core/command/menu-handler.js +230 -51
- package/dist/core/command/menu-protocol.js +94 -2
- package/dist/core/command/slash-gate.js +5 -2
- package/dist/core/command/slash-handler.js +99 -31
- package/dist/core/daemon-file-cache.js +2 -1
- package/dist/core/data-migration.js +9 -2
- package/dist/core/event-catalog.js +6 -0
- package/dist/core/evolagent.js +4 -2
- package/dist/core/handoff/dispatcher.js +5 -2
- package/dist/core/handoff/runtime.js +157 -22
- package/dist/core/handoff/store.js +29 -1
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/interaction-router.js +10 -0
- package/dist/core/message/im-renderer.js +19 -0
- package/dist/core/message/logical-queue-bridge.js +1 -0
- package/dist/core/message/mention-schema.js +126 -0
- package/dist/core/message/message-bridge.js +298 -88
- package/dist/core/message/message-log.js +3 -0
- package/dist/core/message/message-queue.js +268 -39
- package/dist/core/message/message-utils.js +81 -4
- package/dist/core/message/response-engine.js +161 -77
- package/dist/core/message/stream-debouncer.js +10 -2
- package/dist/core/permission/approval-gateway.js +1 -0
- package/dist/core/permission/ec-command-parser.js +243 -18
- package/dist/core/permission/index.js +1 -1
- package/dist/core/permission/mode.js +15 -0
- package/dist/core/permission/readonly-shell-query.js +148 -8
- package/dist/core/permission/sandbox-runtime.js +43 -1
- package/dist/core/permission/tool-policy.js +322 -57
- package/dist/core/protected-paths.js +114 -4
- package/dist/core/session/session-manager.js +42 -4
- package/dist/core/session/session-mapper.js +20 -1
- package/dist/eck/detect.js +18 -1
- package/dist/eck/group-rules-sync.js +15 -44
- package/dist/eck/manifest-engine.js +39 -13
- package/dist/index.js +541 -180
- package/dist/ipc.js +232 -10
- package/dist/paths.js +3 -0
- package/dist/response-system/coordinator.js +6 -4
- package/dist/response-system/engines/v1/proactive-flow.js +26 -8
- package/dist/response-system/modes/single-session/index.js +14 -1
- package/dist/response-system/selector.js +6 -5
- package/dist/trigger/anomaly-store.js +4 -0
- package/dist/trigger/feedback.js +64 -11
- package/dist/trigger/manager.js +21 -1
- package/dist/trigger/parser.js +14 -2
- package/dist/trigger/scheduler.js +1 -0
- package/dist/trigger/script-executor.js +1 -0
- package/dist/trigger/validation.js +67 -6
- package/dist/utils/codex-app-server-registry.js +90 -0
- package/dist/utils/codex-cli.js +5 -1
- package/dist/utils/cross-platform.js +15 -0
- package/dist/utils/ecweb-supervisor.js +332 -0
- package/dist/utils/error-utils.js +10 -0
- package/dist/utils/npm-ops.js +5 -12
- package/dist/utils/tool-summary.js +11 -0
- package/kits/docs/channels/aun.md +1 -1
- package/kits/docs/context-assembly.md +2 -2
- package/kits/docs/evolcore/agent.md +11 -6
- package/kits/docs/evolcore/aid.md +14 -0
- package/kits/docs/evolcore/trigger.md +17 -1
- package/kits/docs/prompt-loading-architecture.md +1 -2
- package/kits/docs/venues/group.md +1 -1
- package/kits/eck_manifest.json +0 -12
- package/kits/migrations/migrate-contact-book-v2.mjs +1 -2
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/05-venue.md +2 -2
- package/kits/schemas/role-config.schema.1.json +1 -0
- package/kits/schemas/single-session.schema.1.json +3 -3
- package/kits/schemas/single-session.schema.2.json +3 -3
- package/kits/templates/roles/admin.json +27 -0
- package/kits/templates/roles/member.json +34 -1
- package/kits/templates/roles/visitor.json +35 -3
- package/kits/templates/system-fragments/bootstrap.md +1 -1
- package/kits/templates/system-fragments/channel.md +1 -1
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +2 -2
- package/dist/aun/msg/payload-type.js +0 -27
- package/dist/aun/rpc/caller.js +0 -42
- package/dist/aun/rpc/connection.js +0 -25
- package/dist/aun/storage/manage.js +0 -10
- package/dist/config/access-policy-domain.js +0 -18
- package/dist/config/config-batch-get.js +0 -11
- package/dist/config/owner-policy.js +0 -4
- package/dist/config/role-config-v4-startup.js +0 -32
- package/dist/config/role-config-v5-startup.js +0 -27
- package/dist/core/auth/trigger-authorization.js +0 -15
- package/dist/core/command/evol-menu-version-gate.js +0 -39
- package/dist/core/interaction-registration.js +0 -10
- package/dist/core/permission/execution-sandbox.js +0 -16
- package/dist/core/relation/peer-key.js +0 -1
- package/dist/core/session/session-key.js +0 -24
- package/dist/eck/baseagent-caps.js +0 -18
- package/dist/eck/rules-loader.js +0 -28
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,50 @@
|
|
|
3
3
|
本文件记录 EvolCore 的重要变更。EvolClaw 版本线的历史记录已归档至
|
|
4
4
|
[`docs/_archive/CHANGELOG-evolclaw.md`](docs/_archive/CHANGELOG-evolclaw.md)。
|
|
5
5
|
|
|
6
|
+
## 0.0.13 (2026-08-20)
|
|
7
|
+
|
|
8
|
+
### AUN 消息与文件
|
|
9
|
+
|
|
10
|
+
- 显式区分 AUN 私聊与群聊投递路由,强化入站去重、顺序处理、消息队列持久化和失败恢复。
|
|
11
|
+
- 统一 AUN RPC 与存储客户端,扩展群文件写入与下载能力,收敛文件操作和载荷处理边界。
|
|
12
|
+
- 增加群文件下载票据校验和内容哈希验证,补齐短连接 RPC、存储上传及启动期角色配置迁移能力。
|
|
13
|
+
|
|
14
|
+
### 权限与关系
|
|
15
|
+
|
|
16
|
+
- 收敛角色迁移、权限作用域和最小能力模式,强化 Owner、关系范围与工具执行审计边界。
|
|
17
|
+
- 贯通会话投递元数据和跨会话授权校验,避免托管操作越过当前 Agent、会话或关系范围。
|
|
18
|
+
- 强化受管 `$TMPDIR`、群规则读取和群文件上传的路径隔离,拒绝符号链接、越界目录及不符合权限/所有者要求的路径。
|
|
19
|
+
- 完善菜单授权审计的请求与会话关联信息,扩展只读 Shell 查询、配置批量读取和委派有效期校验。
|
|
20
|
+
|
|
21
|
+
### 会话与运行时
|
|
22
|
+
|
|
23
|
+
- 完善消息撤回隔离、错误回传、菜单交互、重启命令和 Trigger 反馈的上下文校验。
|
|
24
|
+
- 简化 Agent 运行器与启动流程,修正系统重启路径并清理托管会话凭据。
|
|
25
|
+
- 为 Codex app-server 请求增加 request ID 与载荷指纹重放防护,使重复请求复用结果、冲突请求拒绝;会话重试按至多执行一次处理。
|
|
26
|
+
- 统一 EC Web 守护进程的发现、接管、健康检查和退出清理,修正 Linux 进程启动时间计算,并改进 Codex 初始化/升级探测与 Web 消息路由展示。
|
|
27
|
+
|
|
28
|
+
## 0.0.12 (2026-08-19)
|
|
29
|
+
|
|
30
|
+
### AUN 通信
|
|
31
|
+
|
|
32
|
+
- 规范结构化提及在消息收发、队列和响应链路中的传递,完善 @all、目标校验和非法载荷处理。
|
|
33
|
+
- 增强入站去重、断线重投和错误回传,避免消息重复处理与错误循环。
|
|
34
|
+
|
|
35
|
+
### 会话与 Agent
|
|
36
|
+
|
|
37
|
+
- 串行化 Codex 会话的继续、压缩、分叉和回滚操作,完善 app-server 进程管理和安全清理。
|
|
38
|
+
- 优化 proactive 会话、Handoff 和 Bootstrap 的生命周期处理,隔离并发会话上下文。
|
|
39
|
+
|
|
40
|
+
### 权限与菜单
|
|
41
|
+
|
|
42
|
+
- 收敛菜单执行上下文和托管命令授权,强化 EC shell 与 Codex 命令解析边界。
|
|
43
|
+
- 完善菜单批量查询、关系上下文、联系人状态和工具拦截审计。
|
|
44
|
+
|
|
45
|
+
### 运行兼容性
|
|
46
|
+
|
|
47
|
+
- 统一跨平台子进程启动和 Windows 窗口处理,改进升级监控与内置 AUN SDK 管理。
|
|
48
|
+
- 统一 Claude、Codex 的技能发现和运行时上下文注入。
|
|
49
|
+
|
|
6
50
|
## 0.0.11 (2026-08-11)
|
|
7
51
|
|
|
8
52
|
### Agent 初始化与引导
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import net from 'node:net';
|
|
3
|
+
|
|
4
|
+
const timeoutMs = 1500;
|
|
5
|
+
|
|
6
|
+
function deny(reason) {
|
|
7
|
+
process.stdout.write(JSON.stringify({
|
|
8
|
+
hookSpecificOutput: {
|
|
9
|
+
hookEventName: 'PreToolUse',
|
|
10
|
+
permissionDecision: 'deny',
|
|
11
|
+
permissionDecisionReason: reason || 'EvolCore policy denied this tool call.',
|
|
12
|
+
},
|
|
13
|
+
}));
|
|
14
|
+
// Codex 0.147 app-server runs this managed hook but does not reliably honor
|
|
15
|
+
// the JSON denial alone for thread/shellCommand. Exit 2 is the documented
|
|
16
|
+
// blocking transport; retain the JSON shape for compatible hook consumers.
|
|
17
|
+
process.stderr.write(`${reason || 'EvolCore policy denied this tool call.'}\n`);
|
|
18
|
+
process.exitCode = 2;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function queryDaemon(socketPath, payload) {
|
|
22
|
+
return new Promise(resolve => {
|
|
23
|
+
const socket = net.createConnection(socketPath);
|
|
24
|
+
let buffer = '';
|
|
25
|
+
let settled = false;
|
|
26
|
+
let timer;
|
|
27
|
+
const finish = value => {
|
|
28
|
+
if (settled) return;
|
|
29
|
+
settled = true;
|
|
30
|
+
clearTimeout(timer);
|
|
31
|
+
socket.destroy();
|
|
32
|
+
resolve(value);
|
|
33
|
+
};
|
|
34
|
+
timer = setTimeout(() => finish(null), timeoutMs);
|
|
35
|
+
timer.unref?.();
|
|
36
|
+
socket.once('error', () => finish(null));
|
|
37
|
+
socket.once('connect', () => socket.write(JSON.stringify(payload) + '\n'));
|
|
38
|
+
socket.on('data', chunk => {
|
|
39
|
+
buffer += String(chunk);
|
|
40
|
+
const index = buffer.indexOf('\n');
|
|
41
|
+
if (index < 0) return;
|
|
42
|
+
try { finish(JSON.parse(buffer.slice(0, index))); } catch { finish(null); }
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function main() {
|
|
48
|
+
const socketPath = process.argv[2];
|
|
49
|
+
let input;
|
|
50
|
+
try {
|
|
51
|
+
input = JSON.parse(fs.readFileSync(0, 'utf8'));
|
|
52
|
+
} catch {
|
|
53
|
+
deny('Invalid Codex PreToolUse payload.');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (!socketPath
|
|
57
|
+
|| input?.hook_event_name !== 'PreToolUse'
|
|
58
|
+
|| typeof input?.session_id !== 'string'
|
|
59
|
+
|| !input.session_id
|
|
60
|
+
|| typeof input?.tool_name !== 'string'
|
|
61
|
+
|| !input.tool_name) {
|
|
62
|
+
deny('Incomplete Codex PreToolUse payload.');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const result = await queryDaemon(socketPath, {
|
|
67
|
+
type: 'codex-pre-tool-use',
|
|
68
|
+
threadId: input.session_id,
|
|
69
|
+
toolName: typeof input.tool_name === 'string' ? input.tool_name : '',
|
|
70
|
+
toolInput: input.tool_input ?? null,
|
|
71
|
+
});
|
|
72
|
+
if (!result) {
|
|
73
|
+
deny('EvolCore policy service is unavailable.');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (result.ok === true && result.applicable === true && result.decision === 'allow') return;
|
|
77
|
+
deny(result.reason || 'EvolCore policy did not explicitly allow this tool call.');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
void main();
|
package/bin/ec-safe-output.js
CHANGED
|
@@ -82,12 +82,27 @@ function resolveExecutionPaths(payload) {
|
|
|
82
82
|
return { workspace, cwd };
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
function resolveManagedTempRoot() {
|
|
86
|
+
const raw = typeof process.env.TMPDIR === 'string' ? process.env.TMPDIR.trim() : '';
|
|
87
|
+
if (!raw || !path.isAbsolute(raw)) fail('managed TMPDIR is unavailable');
|
|
88
|
+
try {
|
|
89
|
+
const root = fs.realpathSync(raw);
|
|
90
|
+
if (!fs.statSync(root).isDirectory()) fail('managed TMPDIR is not a directory');
|
|
91
|
+
return root;
|
|
92
|
+
} catch {
|
|
93
|
+
fail('managed TMPDIR is unavailable');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
85
97
|
function openOutputFile(payload, paths) {
|
|
86
98
|
if (payload.outputPath === undefined) return undefined;
|
|
87
|
-
if (typeof payload.outputPath !== 'string' || !payload.outputPath
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
99
|
+
if (typeof payload.outputPath !== 'string' || !payload.outputPath) fail('output path is invalid');
|
|
100
|
+
|
|
101
|
+
const isManagedTempOutput = path.isAbsolute(payload.outputPath);
|
|
102
|
+
const outputRoot = isManagedTempOutput ? resolveManagedTempRoot() : paths.workspace;
|
|
103
|
+
const target = isManagedTempOutput
|
|
104
|
+
? path.resolve(payload.outputPath)
|
|
105
|
+
: path.resolve(paths.cwd, payload.outputPath);
|
|
91
106
|
const parent = path.dirname(target);
|
|
92
107
|
let realParent;
|
|
93
108
|
try {
|
|
@@ -97,9 +112,13 @@ function openOutputFile(payload, paths) {
|
|
|
97
112
|
fail('output parent does not exist');
|
|
98
113
|
}
|
|
99
114
|
const canonicalTarget = path.join(realParent, path.basename(target));
|
|
100
|
-
if (!sameOrDescendant(canonicalTarget,
|
|
101
|
-
|
|
102
|
-
|
|
115
|
+
if (!sameOrDescendant(canonicalTarget, outputRoot)) {
|
|
116
|
+
fail(isManagedTempOutput ? 'output path escapes managed TMPDIR' : 'output path escapes workspace');
|
|
117
|
+
}
|
|
118
|
+
if (!isManagedTempOutput) {
|
|
119
|
+
const relative = path.relative(paths.workspace, canonicalTarget);
|
|
120
|
+
if (relative === '.git' || relative.startsWith(`.git${path.sep}`)) fail('output path may not target Git metadata');
|
|
121
|
+
}
|
|
103
122
|
|
|
104
123
|
const noFollow = fs.constants.O_NOFOLLOW ?? 0;
|
|
105
124
|
try {
|
|
@@ -14,16 +14,15 @@ import { logger } from '../utils/logger.js';
|
|
|
14
14
|
import { requestDangerousCommandPermission } from '../core/permission/approval-gateway.js';
|
|
15
15
|
import { checkDangerousCommand, checkReadonly, evaluateToolPreflight } from '../core/permission/tool-policy.js';
|
|
16
16
|
import { sanitizeShellExecutionEnvironment } from '../core/permission/shell-environment.js';
|
|
17
|
-
import { summarizeToolInput } from '../utils/tool-summary.js';
|
|
17
|
+
import { summarizeToolInput, summarizeToolInputForAudit } from '../utils/tool-summary.js';
|
|
18
18
|
import { encodePath } from '../utils/cross-platform.js';
|
|
19
19
|
import { getPackageRoot, resolvePaths } from '../paths.js';
|
|
20
20
|
import { resolveEffective } from '../config/config-manager.js';
|
|
21
21
|
import { sanitizeSessionTitle } from '../core/session/session-title.js';
|
|
22
22
|
import { resolveClaudeCapabilityRunOptionsForProject } from '../core/capability/capability-manager.js';
|
|
23
|
-
import { normalizePermissionMode } from '../core/permission/mode.js';
|
|
24
|
-
import { resolvePhaseOneExecutionSandbox } from '../core/permission/execution-sandbox.js';
|
|
23
|
+
import { normalizePermissionMode, resolvePhaseOneExecutionSandbox } from '../core/permission/mode.js';
|
|
25
24
|
import { buildClaudeProtectedFilesystem, containsHClassReference, containsLClassReference, isHClassPath, isLClassPath, resolveProtectedCandidate, } from '../core/protected-paths.js';
|
|
26
|
-
import { buildHClassGuardCommand, ensureClaudeGitWorktreeConfig, prependExecutableDirectory, resolveBubblewrapPath, shouldFailIfClaudeSandboxUnavailable, } from '../core/permission/sandbox-runtime.js';
|
|
25
|
+
import { buildHClassGuardCommand, createSandboxInitializationError, isSandboxInitializationFailure, SANDBOX_INITIALIZATION_FAILED, ensureClaudeGitWorktreeConfig, prependExecutableDirectory, resolveBubblewrapPath, shouldFailIfClaudeSandboxUnavailable, } from '../core/permission/sandbox-runtime.js';
|
|
27
26
|
import { buildClaudeUnixSocketAllowlist } from '../core/permission/unix-socket-policy.js';
|
|
28
27
|
import { contextTokensForUsage, usageForContext, isClaudeContextUsageModel, isOneMillionContextModel, realContextWindowForModel, autoCompactWindowForModel } from './runner-types.js';
|
|
29
28
|
export { hasCompact, hasModelSwitcher, hasPermissionController } from './runner-types.js';
|
|
@@ -1759,6 +1758,30 @@ export class AgentRunner {
|
|
|
1759
1758
|
else {
|
|
1760
1759
|
logger.error(`[AgentRunner] Subprocess stream failed (session=${sessionId}) with no captured stderr.`);
|
|
1761
1760
|
}
|
|
1761
|
+
if (isSandboxInitializationFailure(err, buf ?? [])) {
|
|
1762
|
+
const detail = createSandboxInitializationError(err, buf ?? []);
|
|
1763
|
+
await this.permissionContexts.get(sessionId)?.recordExecutionAnomaly?.({
|
|
1764
|
+
code: 'capability_unavailable',
|
|
1765
|
+
severity: 'warning',
|
|
1766
|
+
phase: 'execution',
|
|
1767
|
+
occurredAt: Date.now(),
|
|
1768
|
+
toolName: 'Bash',
|
|
1769
|
+
policyCode: SANDBOX_INITIALIZATION_FAILED,
|
|
1770
|
+
decisionSource: 'infrastructure',
|
|
1771
|
+
agentAid: this.permissionContexts.get(sessionId)?.selfAid,
|
|
1772
|
+
summary: detail.message.slice(0, 512),
|
|
1773
|
+
effect: 'operation_skipped',
|
|
1774
|
+
});
|
|
1775
|
+
yield {
|
|
1776
|
+
type: 'complete',
|
|
1777
|
+
isError: true,
|
|
1778
|
+
subtype: 'infrastructure',
|
|
1779
|
+
terminalReason: SANDBOX_INITIALIZATION_FAILED,
|
|
1780
|
+
errors: [detail.message],
|
|
1781
|
+
queryFinal: true,
|
|
1782
|
+
};
|
|
1783
|
+
return;
|
|
1784
|
+
}
|
|
1762
1785
|
throw err;
|
|
1763
1786
|
}
|
|
1764
1787
|
finally {
|
|
@@ -1852,6 +1875,30 @@ export class AgentRunner {
|
|
|
1852
1875
|
...(updatedInput ? { updatedInput } : {}),
|
|
1853
1876
|
},
|
|
1854
1877
|
});
|
|
1878
|
+
const recordPreflightDenial = async (toolName, toolInput, preflight, requestId, policyCode) => {
|
|
1879
|
+
const code = policyCode ?? (preflight?.behavior === 'deny' ? preflight.policyCode : undefined);
|
|
1880
|
+
if (!code)
|
|
1881
|
+
return;
|
|
1882
|
+
try {
|
|
1883
|
+
await this.permissionContexts.get(sessionId)?.recordExecutionAnomaly?.({
|
|
1884
|
+
code: 'operation_blocked',
|
|
1885
|
+
severity: 'warning',
|
|
1886
|
+
phase: 'execution',
|
|
1887
|
+
occurredAt: Date.now(),
|
|
1888
|
+
toolName,
|
|
1889
|
+
...(typeof requestId === 'string' && requestId ? { requestId } : {}),
|
|
1890
|
+
policyCode: code,
|
|
1891
|
+
decisionSource: 'policy',
|
|
1892
|
+
agentAid: this.permissionContexts.get(sessionId)?.selfAid,
|
|
1893
|
+
permissionMode: callPermissionMode,
|
|
1894
|
+
summary: summarizeToolInputForAudit(toolName, toolInput).slice(0, 512),
|
|
1895
|
+
effect: 'operation_skipped',
|
|
1896
|
+
});
|
|
1897
|
+
}
|
|
1898
|
+
catch (error) {
|
|
1899
|
+
logger.warn(`[AgentRunner] failed to record preflight denial: session=${sessionId} tool=${toolName} code=${code} error=${error instanceof Error ? error.message : String(error)}`);
|
|
1900
|
+
}
|
|
1901
|
+
};
|
|
1855
1902
|
// A rewritten helper command is trusted only after this hook produced its
|
|
1856
1903
|
// random one-shot payload from a validated output wrapper.
|
|
1857
1904
|
const trustedSafeOutputCommands = new Set();
|
|
@@ -1896,6 +1943,7 @@ export class AgentRunner {
|
|
|
1896
1943
|
// proactive 模式行为策略(首次工具调用必须是 ec msg send)
|
|
1897
1944
|
const policyResult = this.permissionContexts.get(sessionId)?.policyHook?.(toolName, toolInput);
|
|
1898
1945
|
if (policyResult?.block) {
|
|
1946
|
+
await recordPreflightDenial(toolName, toolInput, undefined, input.tool_use_id ?? input.toolUseID, policyResult.policyCode ?? 'session_policy_hook');
|
|
1899
1947
|
return hookDecision('deny', policyResult.reason || '当前会话策略拒绝此工具调用', updatedInput);
|
|
1900
1948
|
}
|
|
1901
1949
|
const permCtx = this.permissionContexts.get(sessionId);
|
|
@@ -1910,14 +1958,17 @@ export class AgentRunner {
|
|
|
1910
1958
|
role: permCtx?.role,
|
|
1911
1959
|
permissionMode: callPermissionMode,
|
|
1912
1960
|
allowHostProcessCommands: true,
|
|
1961
|
+
allowProtectedMetadata: false,
|
|
1913
1962
|
safeOutputHelperPath: path.join(getPackageRoot(), 'bin', 'ec-safe-output.js'),
|
|
1914
1963
|
projectPath,
|
|
1915
1964
|
});
|
|
1916
1965
|
toolInput = preflight.input;
|
|
1917
1966
|
if (toolInput !== originalInput)
|
|
1918
1967
|
updatedInput = toolInput;
|
|
1919
|
-
if (preflight.behavior === 'deny')
|
|
1968
|
+
if (preflight.behavior === 'deny') {
|
|
1969
|
+
await recordPreflightDenial(toolName, toolInput, preflight, input.tool_use_id ?? input.toolUseID);
|
|
1920
1970
|
return hookDecision('deny', preflight.message, updatedInput);
|
|
1971
|
+
}
|
|
1921
1972
|
if (preflight.behavior === 'allow') {
|
|
1922
1973
|
const commandAfterPreflight = typeof toolInput.command === 'string' ? toolInput.command : undefined;
|
|
1923
1974
|
if (commandAfterPreflight && commandAfterPreflight !== commandBeforePreflight) {
|
|
@@ -1939,9 +1990,11 @@ export class AgentRunner {
|
|
|
1939
1990
|
channel: permCtx?.channel,
|
|
1940
1991
|
peerId: permCtx?.userId,
|
|
1941
1992
|
role: permCtx?.role,
|
|
1993
|
+
allowProtectedMetadata: false,
|
|
1942
1994
|
};
|
|
1943
1995
|
const roResult = checkReadonly(toolName, toolInput, projectPath, readonlyContext);
|
|
1944
1996
|
if (roResult.behavior === 'deny') {
|
|
1997
|
+
await recordPreflightDenial(toolName, toolInput, undefined, input.tool_use_id ?? input.toolUseID, roResult.policyCode ?? 'readonly_mode');
|
|
1945
1998
|
return hookDecision('deny', roResult.message, updatedInput);
|
|
1946
1999
|
}
|
|
1947
2000
|
return hookDecision('allow', undefined, updatedInput);
|
|
@@ -1951,6 +2004,7 @@ export class AgentRunner {
|
|
|
1951
2004
|
}
|
|
1952
2005
|
if (isUnknownClaudeTool(toolName)) {
|
|
1953
2006
|
if (callPermissionMode === 'auto') {
|
|
2007
|
+
await recordPreflightDenial(toolName, toolInput, undefined, input.tool_use_id ?? input.toolUseID, 'auto_unknown_tool');
|
|
1954
2008
|
return hookDecision('deny', `auto 模式拒绝未知工具 ${toolName}:该工具不受 Claude 本地 sandbox 的完整约束`, updatedInput);
|
|
1955
2009
|
}
|
|
1956
2010
|
// request/bypass only receive external tools admitted by the session's
|
|
@@ -1960,6 +2014,7 @@ export class AgentRunner {
|
|
|
1960
2014
|
const danger = checkDangerousCommand(toolName, toolInput);
|
|
1961
2015
|
if (danger.isDangerous) {
|
|
1962
2016
|
if (callPermissionMode === 'auto') {
|
|
2017
|
+
await recordPreflightDenial(toolName, toolInput, undefined, input.tool_use_id ?? input.toolUseID, 'auto_dangerous_command');
|
|
1963
2018
|
return hookDecision('deny', `危险操作已由 auto 模式拒绝:${danger.reason}`, updatedInput);
|
|
1964
2019
|
}
|
|
1965
2020
|
if (callPermissionMode === 'request' || callPermissionMode === 'bypass') {
|
|
@@ -2025,11 +2080,13 @@ export class AgentRunner {
|
|
|
2025
2080
|
role: permCtx?.role,
|
|
2026
2081
|
permissionMode: callPermissionMode,
|
|
2027
2082
|
allowHostProcessCommands: true,
|
|
2083
|
+
allowProtectedMetadata: false,
|
|
2028
2084
|
safeOutputHelperPath: path.join(getPackageRoot(), 'bin', 'ec-safe-output.js'),
|
|
2029
2085
|
projectPath,
|
|
2030
2086
|
});
|
|
2031
2087
|
input = preflight.input;
|
|
2032
2088
|
if (preflight.behavior === 'deny') {
|
|
2089
|
+
await recordPreflightDenial(toolName, input, preflight, options.toolUseID);
|
|
2033
2090
|
return { behavior: 'deny', message: preflight.message, decisionClassification: 'user_reject' };
|
|
2034
2091
|
}
|
|
2035
2092
|
if (preflight.behavior === 'allow') {
|
|
@@ -2041,6 +2098,7 @@ export class AgentRunner {
|
|
|
2041
2098
|
}
|
|
2042
2099
|
const permissionExpansion = inspectClaudePermissionExpansion(input, options, projectPath);
|
|
2043
2100
|
if (permissionExpansion.protectedReason) {
|
|
2101
|
+
await recordPreflightDenial(toolName, input, undefined, options.toolUseID, 'protected_permission_expansion');
|
|
2044
2102
|
return {
|
|
2045
2103
|
behavior: 'deny',
|
|
2046
2104
|
message: permissionExpansion.protectedReason,
|
|
@@ -2049,6 +2107,7 @@ export class AgentRunner {
|
|
|
2049
2107
|
}
|
|
2050
2108
|
if (permissionExpansion.requiresApproval) {
|
|
2051
2109
|
if (callPermissionMode === 'readonly' || callPermissionMode === 'auto') {
|
|
2110
|
+
await recordPreflightDenial(toolName, input, undefined, options.toolUseID, callPermissionMode === 'readonly' ? 'readonly_permission_expansion' : 'auto_permission_expansion');
|
|
2052
2111
|
return {
|
|
2053
2112
|
behavior: 'deny',
|
|
2054
2113
|
message: `${callPermissionMode} 模式拒绝 Claude SDK 文件系统或未知权限扩权请求`,
|
|
@@ -2089,9 +2148,11 @@ export class AgentRunner {
|
|
|
2089
2148
|
channel: permCtx?.channel,
|
|
2090
2149
|
peerId: permCtx?.userId,
|
|
2091
2150
|
role: permCtx?.role,
|
|
2151
|
+
allowProtectedMetadata: false,
|
|
2092
2152
|
};
|
|
2093
2153
|
const roResult = checkReadonly(toolName, input, projectPath, readonlyContext);
|
|
2094
2154
|
if (roResult.behavior === 'deny') {
|
|
2155
|
+
await recordPreflightDenial(toolName, input, undefined, options.toolUseID, roResult.policyCode ?? 'readonly_mode');
|
|
2095
2156
|
return { behavior: 'deny', message: roResult.message, decisionClassification: 'user_reject' };
|
|
2096
2157
|
}
|
|
2097
2158
|
return { behavior: 'allow', updatedInput: input, decisionClassification: 'user_permanent' };
|
|
@@ -2101,6 +2162,7 @@ export class AgentRunner {
|
|
|
2101
2162
|
}
|
|
2102
2163
|
if (isUnknownClaudeTool(toolName)) {
|
|
2103
2164
|
if (callPermissionMode === 'auto') {
|
|
2165
|
+
await recordPreflightDenial(toolName, input, undefined, options.toolUseID, 'auto_unknown_tool');
|
|
2104
2166
|
return {
|
|
2105
2167
|
behavior: 'deny',
|
|
2106
2168
|
message: `auto 模式拒绝未知工具 ${toolName}:该工具不受 Claude 本地 sandbox 的完整约束`,
|
|
@@ -2112,6 +2174,7 @@ export class AgentRunner {
|
|
|
2112
2174
|
const danger = checkDangerousCommand(toolName, input);
|
|
2113
2175
|
if (danger.isDangerous) {
|
|
2114
2176
|
if (callPermissionMode === 'auto') {
|
|
2177
|
+
await recordPreflightDenial(toolName, input, undefined, options.toolUseID, 'auto_dangerous_command');
|
|
2115
2178
|
return { behavior: 'deny', message: `auto 模式拒绝危险操作:${danger.reason}`, decisionClassification: 'user_reject' };
|
|
2116
2179
|
}
|
|
2117
2180
|
if (callPermissionMode === 'request' || callPermissionMode === 'bypass') {
|
|
@@ -2340,12 +2403,28 @@ export class AgentRunner {
|
|
|
2340
2403
|
logger.info('[AgentRunner] Creating query with images:', images.length, 'first image size:', images[0]?.data?.length ?? 0);
|
|
2341
2404
|
logger.debug('[AgentRunner] Skipping resume for image message to avoid history conflict');
|
|
2342
2405
|
msgStream.push(prompt, images, inputId);
|
|
2343
|
-
|
|
2406
|
+
try {
|
|
2407
|
+
sdkStream = createQuery(msgStream);
|
|
2408
|
+
}
|
|
2409
|
+
catch (error) {
|
|
2410
|
+
const stderr = this.recentStderr.get(sessionId) ?? [];
|
|
2411
|
+
if (isSandboxInitializationFailure(error, stderr))
|
|
2412
|
+
throw createSandboxInitializationError(error, stderr);
|
|
2413
|
+
throw error;
|
|
2414
|
+
}
|
|
2344
2415
|
}
|
|
2345
2416
|
else {
|
|
2346
2417
|
logger.debug('[AgentRunner] Creating query with text only, agentSessionId:', initialClaudeSessionId);
|
|
2347
2418
|
msgStream.push(prompt, undefined, inputId);
|
|
2348
|
-
|
|
2419
|
+
try {
|
|
2420
|
+
sdkStream = createQuery(msgStream, agentSessionId, resumeAt);
|
|
2421
|
+
}
|
|
2422
|
+
catch (error) {
|
|
2423
|
+
const stderr = this.recentStderr.get(sessionId) ?? [];
|
|
2424
|
+
if (isSandboxInitializationFailure(error, stderr))
|
|
2425
|
+
throw createSandboxInitializationError(error, stderr);
|
|
2426
|
+
throw error;
|
|
2427
|
+
}
|
|
2349
2428
|
}
|
|
2350
2429
|
this.activeMessageStreams.set(sessionId, msgStream);
|
|
2351
2430
|
this.activeQueries.set(sessionId, sdkStream);
|