evolcore 0.0.10 → 0.0.12
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/README.md +3 -3
- package/bin/codex-managed-hook.mjs +80 -0
- package/dist/agents/baseagent.js +4 -0
- package/dist/agents/claude-runner.js +158 -44
- package/dist/agents/codex-app-server-client.js +153 -23
- package/dist/agents/codex-runner.js +331 -43
- package/dist/agents/ecagent-runner.js +53 -6
- package/dist/agents/gemini-runner.js +1 -0
- package/dist/agents/request-identity.js +55 -0
- package/dist/aun/aid/client.js +11 -21
- package/dist/aun/aid/managed-operation.js +107 -0
- package/dist/aun/msg/group.js +81 -12
- package/dist/aun/msg/managed-operation.js +804 -0
- package/dist/aun/msg/mention-schema.js +20 -0
- package/dist/aun/msg/p2p.js +7 -0
- package/dist/aun/outbox.js +28 -31
- package/dist/channels/aun.js +418 -235
- package/dist/channels/daemon.js +13 -0
- package/dist/cli/agent-command.js +72 -17
- package/dist/cli/agent.js +249 -22
- package/dist/cli/aun-commands.js +270 -69
- package/dist/cli/bench.js +12 -3
- package/dist/cli/daemon-commands.js +52 -33
- package/dist/cli/fs-command.js +60 -0
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +106 -63
- package/dist/cli/init-cancel.js +208 -0
- package/dist/cli/init-channel.js +343 -195
- package/dist/cli/init.js +21 -9
- package/dist/cli/managed-command-guard.js +41 -0
- package/dist/cli/model.js +11 -4
- package/dist/cli/queue-command.js +11 -1
- package/dist/cli/response.js +71 -0
- package/dist/cli/restart-monitor.js +4 -18
- package/dist/cli/trigger-command.js +4 -0
- package/dist/config/builtin-roles.js +5 -0
- package/dist/config/contact-book-store.js +11 -3
- package/dist/config/contact-request-service.js +161 -15
- package/dist/config/gateway-config.js +26 -10
- package/dist/config/mention-mode.js +8 -24
- package/dist/core/agent-reload-coordinator.js +53 -0
- package/dist/core/auth/agent-delegation.js +0 -1
- package/dist/core/auth/operation-authorizer.js +62 -154
- package/dist/core/auth/operation-catalog.js +264 -22
- package/dist/core/bootstrap-messages.js +68 -0
- package/dist/core/bootstrap-service.js +144 -10
- 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 +24 -7
- package/dist/core/command/agent-control.js +14 -11
- package/dist/core/command/cli-intent-parser.js +9 -0
- package/dist/core/command/command-handler.js +467 -6
- 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 +366 -0
- package/dist/core/command/menu-handler.js +213 -110
- package/dist/core/command/menu-protocol.js +71 -0
- package/dist/core/command/slash-gate.js +5 -2
- package/dist/core/command/slash-handler.js +45 -25
- package/dist/core/daemon-file-cache.js +2 -1
- package/dist/core/data-migration.js +9 -2
- package/dist/core/evolagent-registry.js +125 -35
- package/dist/core/evolagent.js +12 -5
- package/dist/core/handoff/dispatcher.js +5 -2
- package/dist/core/handoff/runtime.js +145 -21
- package/dist/core/handoff/store.js +24 -0
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/inference/text-inference.js +38 -4
- 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 +237 -79
- package/dist/core/message/message-log.js +23 -0
- package/dist/core/message/message-queue.js +20 -4
- package/dist/core/message/response-engine.js +54 -36
- package/dist/core/model/model-catalog.js +143 -24
- package/dist/core/model/model-diagnostics.js +28 -10
- package/dist/core/permission/ec-command-parser.js +156 -10
- package/dist/core/permission/index.js +1 -0
- package/dist/core/permission/readonly-shell-query.js +532 -0
- package/dist/core/permission/sandbox-runtime.js +22 -1
- package/dist/core/permission/shell-environment.js +46 -0
- package/dist/core/permission/tool-policy.js +292 -111
- package/dist/core/protected-paths.js +17 -9
- package/dist/core/runner-reload-transaction.js +57 -0
- package/dist/core/session/session-manager.js +21 -2
- package/dist/eck/manifest-engine.js +39 -13
- package/dist/index.js +644 -180
- package/dist/ipc.js +246 -19
- 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/script-executor.js +1 -0
- package/dist/utils/aid-bind.js +3 -8
- 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/log-writer.js +6 -10
- package/dist/utils/logger.js +5 -5
- 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/msg.md +13 -0
- 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/rules/01-overview.md +9 -0
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/05-venue.md +2 -2
- package/kits/schemas/agent-config.schema.3.json +1 -1
- package/kits/schemas/agent-config.schema.4.json +1 -1
- package/kits/schemas/relation-config.schema.2.json +1 -1
- package/kits/schemas/role-config.schema.1.json +1 -1
- 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 +44 -0
- package/kits/templates/roles/visitor.json +40 -2
- package/kits/templates/system-fragments/bootstrap.md +12 -6
- package/kits/templates/system-fragments/channel.md +6 -0
- package/kits/templates/system-fragments/session.md +2 -0
- package/package.json +3 -2
- package/skills/eclink/SKILL.md +15 -3
- package/skills/eclink/agents/openai.yaml +3 -3
- package/dist/core/command/evol-menu-version-gate.js +0 -39
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.12 (2026-08-19)
|
|
7
|
+
|
|
8
|
+
### AUN 通信
|
|
9
|
+
|
|
10
|
+
- 规范结构化提及在消息收发、队列和响应链路中的传递,完善 @all、目标校验和非法载荷处理。
|
|
11
|
+
- 增强入站去重、断线重投和错误回传,避免消息重复处理与错误循环。
|
|
12
|
+
|
|
13
|
+
### 会话与 Agent
|
|
14
|
+
|
|
15
|
+
- 串行化 Codex 会话的继续、压缩、分叉和回滚操作,完善 app-server 进程管理和安全清理。
|
|
16
|
+
- 优化 proactive 会话、Handoff 和 Bootstrap 的生命周期处理,隔离并发会话上下文。
|
|
17
|
+
|
|
18
|
+
### 权限与菜单
|
|
19
|
+
|
|
20
|
+
- 收敛菜单执行上下文和托管命令授权,强化 EC shell 与 Codex 命令解析边界。
|
|
21
|
+
- 完善菜单批量查询、关系上下文、联系人状态和工具拦截审计。
|
|
22
|
+
|
|
23
|
+
### 运行兼容性
|
|
24
|
+
|
|
25
|
+
- 统一跨平台子进程启动和 Windows 窗口处理,改进升级监控与内置 AUN SDK 管理。
|
|
26
|
+
- 统一 Claude、Codex 的技能发现和运行时上下文注入。
|
|
27
|
+
|
|
28
|
+
## 0.0.11 (2026-08-11)
|
|
29
|
+
|
|
30
|
+
### Agent 初始化与引导
|
|
31
|
+
|
|
32
|
+
- `ec init` 支持在任意交互步骤连续按两次 Esc 或使用 Ctrl+C 安全退出,并完善二维码超时后的重新生成、手动绑定与取消清理流程。
|
|
33
|
+
- 重新设计 Agent bootstrap 引导消息,在 Agent 完成初始化后可靠投递,并通过持久化和去重避免断线重试造成消息丢失或重复。
|
|
34
|
+
|
|
35
|
+
### 权限与消息可靠性
|
|
36
|
+
|
|
37
|
+
- 扩展安全只读 Shell 查询和环境变量解析能力,统一工具授权、危险命令识别与审批边界。
|
|
38
|
+
- 完善 AUN 关键消息的持久化、去重和日志落盘,提升断线恢复与异常重试时的一致性。
|
|
39
|
+
|
|
40
|
+
### 请求身份与模型目录
|
|
41
|
+
|
|
42
|
+
- 模型请求统一携带 EvolCore 版本、Agent AID 和 BaseAgent 类型,便于网关进行来源识别、审计和问题定位。
|
|
43
|
+
- Claude 元数据请求复用 Agent 自定义请求头,同时隔离公共模型目录请求中的 API Key、租户头和 Agent 身份信息。
|
|
44
|
+
|
|
45
|
+
### 热重载
|
|
46
|
+
|
|
47
|
+
- 同一 Agent 的重载操作改为串行执行;任务繁忙时默认拒绝重载,也可通过 `--force` 清理队列并等待运行中任务中断。
|
|
48
|
+
- 为 runner 与渠道重载建立明确的提交边界和失败回滚,完善 resync 下线清理,避免重载失败后残留旧实例或半更新状态。
|
|
49
|
+
|
|
6
50
|
## 0.0.10 (2026-08-06)
|
|
7
51
|
|
|
8
52
|
### 数据迁移
|
package/README.md
CHANGED
|
@@ -86,13 +86,13 @@ ResponseEngine.processMessage()
|
|
|
86
86
|
**一行安装(macOS / Linux)**:
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
|
-
curl -fsSL https://download.evolai.cn/
|
|
89
|
+
curl -fsSL https://download.evolai.cn/install.sh | bash
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
**Windows PowerShell**:
|
|
93
93
|
|
|
94
94
|
```powershell
|
|
95
|
-
& ([scriptblock]::Create((irm https://download.evolai.cn/
|
|
95
|
+
& ([scriptblock]::Create((irm https://download.evolai.cn/install.ps1))) -AutoStart
|
|
96
96
|
```
|
|
97
97
|
|
|
98
98
|
安装器会自动进入 `ec init` 交互向导;只有已有配置同时包含控制 AID 和 Owner 时才跳过。上面的 Windows 命令会在绑定完成后自动运行 `ec start`;macOS/Linux 需手动运行 `ec start`。安装器不会停止或重启已有服务,详细流程见 [一键安装脚本说明](docs/evolcore-install-script.md)。
|
|
@@ -145,7 +145,7 @@ ec init aun
|
|
|
145
145
|
- 渠道选择(飞书 / 微信 / 钉钉 / QQ 频道 / 企业微信 / AUN)并扫码登录
|
|
146
146
|
- 默认项目路径
|
|
147
147
|
- 模型选择(sonnet/opus/haiku)
|
|
148
|
-
- owner 配置:默认走 **Evol App 扫码绑定**,若无 Evol App 可手工录入 AID
|
|
148
|
+
- owner 配置:默认走 **Evol App 扫码绑定**,若无 Evol App 可手工录入 AID 兜底。扫码流程只追加 owner;移除 owner 请直接编辑对应 JSON 配置。
|
|
149
149
|
- 自动写入 `EVOLCORE_HOME` 到 shell profile(Unix)或用户环境变量(Windows)
|
|
150
150
|
|
|
151
151
|
配置文件生成在 `{EVOLCORE_HOME}/data/daemon.json`(默认 `~/.evolcore/data/daemon.json`)。
|
|
@@ -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/dist/agents/baseagent.js
CHANGED
|
@@ -279,6 +279,8 @@ export function resolveOpenaiConfig(config, override) {
|
|
|
279
279
|
effort,
|
|
280
280
|
enableRequestUserInput,
|
|
281
281
|
approvalsReviewer,
|
|
282
|
+
evolcoreAgentAid: override?.evolcoreAgentAid ?? config.agents?.codex?.evolcoreAgentAid,
|
|
283
|
+
evolcoreAgentConfig: override?.evolcoreAgentConfig ?? config.agents?.codex?.evolcoreAgentConfig,
|
|
282
284
|
};
|
|
283
285
|
}
|
|
284
286
|
export function resolveGoogleConfig(config, override) {
|
|
@@ -325,11 +327,13 @@ export function resolveEcagentConfig(config, override) {
|
|
|
325
327
|
|| process.env.ECAGENT_BASE_URL
|
|
326
328
|
|| DEFAULT_ECAGENT_BASE_URL;
|
|
327
329
|
const model = override?.model || configured?.model || DEFAULT_ECAGENT_MODEL;
|
|
330
|
+
const headers = normalizeStringMap(override?.headers ?? configured?.headers, 'headers');
|
|
328
331
|
const effort = override?.effort ?? configured?.effort;
|
|
329
332
|
const numberOr = (value, fallback) => (typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback);
|
|
330
333
|
return {
|
|
331
334
|
apiKey,
|
|
332
335
|
baseUrl,
|
|
336
|
+
headers,
|
|
333
337
|
model,
|
|
334
338
|
effort,
|
|
335
339
|
timeoutMs: numberOr(override?.timeoutMs ?? configured?.timeoutMs, DEFAULT_ECAGENT_TIMEOUT_MS),
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { query, forkSession as sdkForkSession, getSessionMessages as sdkGetSessionMessages, resolveSettings as sdkResolveSettings } from '@anthropic-ai/claude-agent-sdk';
|
|
1
|
+
import { query, forkSession as sdkForkSession, getSessionMessages as sdkGetSessionMessages, resolveSettings as sdkResolveSettings, } from '@anthropic-ai/claude-agent-sdk';
|
|
2
|
+
import { spawn } from 'child_process';
|
|
2
3
|
import { atomicReadJson, atomicWriteJson, ensureDir } from '../utils/atomic-write.js';
|
|
3
4
|
import { resolveAnthropicConfig } from './baseagent.js';
|
|
5
|
+
import { buildModelRequestHeaders } from './request-identity.js';
|
|
4
6
|
import { DEFAULT_PERMISSION_MODE } from '../types.js';
|
|
5
7
|
import { renderActionAsText } from '../core/interaction-router.js';
|
|
6
8
|
import { buildEnvelope, sendInteractionPayload } from '../core/message/message-utils.js';
|
|
@@ -11,7 +13,8 @@ import crypto from 'crypto';
|
|
|
11
13
|
import { logger } from '../utils/logger.js';
|
|
12
14
|
import { requestDangerousCommandPermission } from '../core/permission/approval-gateway.js';
|
|
13
15
|
import { checkDangerousCommand, checkReadonly, evaluateToolPreflight } from '../core/permission/tool-policy.js';
|
|
14
|
-
import {
|
|
16
|
+
import { sanitizeShellExecutionEnvironment } from '../core/permission/shell-environment.js';
|
|
17
|
+
import { summarizeToolInput, summarizeToolInputForAudit } from '../utils/tool-summary.js';
|
|
15
18
|
import { encodePath } from '../utils/cross-platform.js';
|
|
16
19
|
import { getPackageRoot, resolvePaths } from '../paths.js';
|
|
17
20
|
import { resolveEffective } from '../config/config-manager.js';
|
|
@@ -20,7 +23,7 @@ import { resolveClaudeCapabilityRunOptionsForProject } from '../core/capability/
|
|
|
20
23
|
import { normalizePermissionMode } from '../core/permission/mode.js';
|
|
21
24
|
import { resolvePhaseOneExecutionSandbox } from '../core/permission/execution-sandbox.js';
|
|
22
25
|
import { buildClaudeProtectedFilesystem, containsHClassReference, containsLClassReference, isHClassPath, isLClassPath, resolveProtectedCandidate, } from '../core/protected-paths.js';
|
|
23
|
-
import { ensureClaudeGitWorktreeConfig, prependExecutableDirectory, resolveBubblewrapPath, shouldFailIfClaudeSandboxUnavailable } from '../core/permission/sandbox-runtime.js';
|
|
26
|
+
import { buildHClassGuardCommand, ensureClaudeGitWorktreeConfig, prependExecutableDirectory, resolveBubblewrapPath, shouldFailIfClaudeSandboxUnavailable, } from '../core/permission/sandbox-runtime.js';
|
|
24
27
|
import { buildClaudeUnixSocketAllowlist } from '../core/permission/unix-socket-policy.js';
|
|
25
28
|
import { contextTokensForUsage, usageForContext, isClaudeContextUsageModel, isOneMillionContextModel, realContextWindowForModel, autoCompactWindowForModel } from './runner-types.js';
|
|
26
29
|
export { hasCompact, hasModelSwitcher, hasPermissionController } from './runner-types.js';
|
|
@@ -59,6 +62,22 @@ const CLAUDE_BUILTIN_TOOLS = new Set([
|
|
|
59
62
|
'WebSearch',
|
|
60
63
|
'Write',
|
|
61
64
|
]);
|
|
65
|
+
function spawnClaudeWithHClassGuard(options, root, onStderr) {
|
|
66
|
+
const guarded = buildHClassGuardCommand(options.command, options.args, root);
|
|
67
|
+
if (!guarded) {
|
|
68
|
+
throw new Error('Claude owner bypass 缺少 EvolCore H 类路径隔离运行时,已拒绝启动');
|
|
69
|
+
}
|
|
70
|
+
const child = spawn(guarded.command, guarded.args, {
|
|
71
|
+
cwd: options.cwd,
|
|
72
|
+
env: options.env,
|
|
73
|
+
signal: options.signal,
|
|
74
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
75
|
+
windowsHide: true,
|
|
76
|
+
});
|
|
77
|
+
child.stderr.on('data', chunk => onStderr(String(chunk)));
|
|
78
|
+
child.stderr.on('error', error => onStderr(`[ClaudeGuard] stderr read failed: ${error.message}`));
|
|
79
|
+
return child;
|
|
80
|
+
}
|
|
62
81
|
function isClaudeMcpTool(toolName) {
|
|
63
82
|
return toolName.startsWith('mcp__');
|
|
64
83
|
}
|
|
@@ -337,7 +356,7 @@ function apiPricingToQuad(p) {
|
|
|
337
356
|
}
|
|
338
357
|
/** 拉取网关 /v1/models 的官方价(pricing) + 网关价(effective_pricing),写入 gatewayPricingCache。
|
|
339
358
|
* 失败静默——保持回退到本地价表 / official。 */
|
|
340
|
-
async function refreshGatewayPricing(baseUrl, apiKey) {
|
|
359
|
+
async function refreshGatewayPricing(baseUrl, apiKey, requestHeaders) {
|
|
341
360
|
if (gatewayPricingInFlight.has(baseUrl))
|
|
342
361
|
return;
|
|
343
362
|
gatewayPricingInFlight.add(baseUrl);
|
|
@@ -347,7 +366,10 @@ async function refreshGatewayPricing(baseUrl, apiKey) {
|
|
|
347
366
|
const timer = setTimeout(() => controller.abort(), 5000);
|
|
348
367
|
const resp = await fetch(url, {
|
|
349
368
|
signal: controller.signal,
|
|
350
|
-
headers:
|
|
369
|
+
headers: {
|
|
370
|
+
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
371
|
+
...requestHeaders,
|
|
372
|
+
},
|
|
351
373
|
});
|
|
352
374
|
clearTimeout(timer);
|
|
353
375
|
if (!resp.ok)
|
|
@@ -408,7 +430,7 @@ function deriveAliasesFromModelIds(ids) {
|
|
|
408
430
|
return aliases;
|
|
409
431
|
}
|
|
410
432
|
/** 异步刷新某 baseUrl 的别名缓存(失败静默,不抛出) */
|
|
411
|
-
async function refreshModelAliases(baseUrl, apiKey) {
|
|
433
|
+
async function refreshModelAliases(baseUrl, apiKey, requestHeaders) {
|
|
412
434
|
const cacheKey = normalizeModelGatewayUrl(baseUrl);
|
|
413
435
|
if (modelAliasInFlight.has(cacheKey))
|
|
414
436
|
return;
|
|
@@ -420,7 +442,10 @@ async function refreshModelAliases(baseUrl, apiKey) {
|
|
|
420
442
|
const timer = setTimeout(() => controller.abort(), 5000);
|
|
421
443
|
const resp = await fetch(url, {
|
|
422
444
|
signal: controller.signal,
|
|
423
|
-
headers:
|
|
445
|
+
headers: {
|
|
446
|
+
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
447
|
+
...requestHeaders,
|
|
448
|
+
},
|
|
424
449
|
});
|
|
425
450
|
clearTimeout(timer);
|
|
426
451
|
if (!resp.ok)
|
|
@@ -571,13 +596,18 @@ export class AgentRunner {
|
|
|
571
596
|
if (config) {
|
|
572
597
|
const anthropic = resolveAnthropicConfig(config);
|
|
573
598
|
this.claudeExecutablePath = anthropic.pathToClaudeCodeExecutable;
|
|
574
|
-
this.customHeaders =
|
|
599
|
+
this.customHeaders = buildModelRequestHeaders({
|
|
600
|
+
baseagent: 'claude',
|
|
601
|
+
baseUrl: anthropic.baseUrl,
|
|
602
|
+
agentAid: config.agents?.claude?.evolcoreAgentAid,
|
|
603
|
+
configuredHeaders: anthropic.headers,
|
|
604
|
+
});
|
|
575
605
|
this.customQueryParams = anthropic.queryParams;
|
|
576
606
|
}
|
|
577
607
|
}
|
|
578
|
-
getAgentEnv(runtimeEnv, evolcoreSessionId) {
|
|
608
|
+
getAgentEnv(runtimeEnv, evolcoreSessionId, permissionMode) {
|
|
579
609
|
const anthropicBaseUrl = appendQueryParams(this.baseUrl, this.customQueryParams);
|
|
580
|
-
const env = {
|
|
610
|
+
const env = sanitizeShellExecutionEnvironment({
|
|
581
611
|
...process.env,
|
|
582
612
|
ANTHROPIC_AUTH_TOKEN: this.apiKey,
|
|
583
613
|
ANTHROPIC_API_KEY: this.apiKey,
|
|
@@ -589,7 +619,7 @@ export class AgentRunner {
|
|
|
589
619
|
: {}),
|
|
590
620
|
...(evolcoreSessionId ? { EVOLCORE_SESSION_ID: evolcoreSessionId } : {}),
|
|
591
621
|
...(runtimeEnv ?? {}),
|
|
592
|
-
};
|
|
622
|
+
}, { lockExecutableResolution: permissionMode === 'readonly' });
|
|
593
623
|
const bubblewrapPath = resolveBubblewrapPath();
|
|
594
624
|
return bubblewrapPath
|
|
595
625
|
? prependExecutableDirectory(env, bubblewrapPath)
|
|
@@ -608,7 +638,7 @@ export class AgentRunner {
|
|
|
608
638
|
return undefined;
|
|
609
639
|
const entry = gatewayPricingCache.get(this.baseUrl);
|
|
610
640
|
if (!entry || Date.now() - entry.fetchedAt > GATEWAY_PRICING_TTL_MS) {
|
|
611
|
-
refreshGatewayPricing(this.baseUrl, this.apiKey); // 不 await,stale-while-revalidate
|
|
641
|
+
refreshGatewayPricing(this.baseUrl, this.apiKey, this.customHeaders); // 不 await,stale-while-revalidate
|
|
612
642
|
}
|
|
613
643
|
return entry?.cache;
|
|
614
644
|
}
|
|
@@ -617,11 +647,26 @@ export class AgentRunner {
|
|
|
617
647
|
let agentConfig = claudeConfig?.evolcoreAgentConfig;
|
|
618
648
|
if (claudeConfig?.evolcoreAgentAid) {
|
|
619
649
|
try {
|
|
620
|
-
agentConfig = resolveEffective({ self: claudeConfig.evolcoreAgentAid }, { cache: true });
|
|
650
|
+
agentConfig = resolveEffective({ self: claudeConfig.evolcoreAgentAid }, { cache: true, expand: true });
|
|
621
651
|
}
|
|
622
652
|
catch { }
|
|
623
653
|
}
|
|
624
|
-
|
|
654
|
+
const resolved = await resolveClaudeCapabilityRunOptionsForProject(agentConfig, projectPath, 'claude');
|
|
655
|
+
const settings = resolved.settings && typeof resolved.settings === 'object' && !Array.isArray(resolved.settings)
|
|
656
|
+
? resolved.settings
|
|
657
|
+
: {};
|
|
658
|
+
const skillOverrides = settings.skillOverrides
|
|
659
|
+
&& typeof settings.skillOverrides === 'object'
|
|
660
|
+
&& !Array.isArray(settings.skillOverrides)
|
|
661
|
+
? settings.skillOverrides
|
|
662
|
+
: {};
|
|
663
|
+
return {
|
|
664
|
+
...resolved,
|
|
665
|
+
settings: {
|
|
666
|
+
...settings,
|
|
667
|
+
skillOverrides: { ...skillOverrides, eclink: 'off' },
|
|
668
|
+
},
|
|
669
|
+
};
|
|
625
670
|
}
|
|
626
671
|
async listModels() {
|
|
627
672
|
if (this.baseUrl) {
|
|
@@ -631,11 +676,11 @@ export class AgentRunner {
|
|
|
631
676
|
const stale = !cached || (Date.now() - cached.fetchedAt > MODEL_ALIAS_TTL_MS);
|
|
632
677
|
// 缓存为空(首次打开)→ 等待刷新;缓存仅过期 → 后台刷新不阻塞
|
|
633
678
|
if (!cached) {
|
|
634
|
-
await refreshModelAliases(this.baseUrl, this.apiKey);
|
|
679
|
+
await refreshModelAliases(this.baseUrl, this.apiKey, this.customHeaders);
|
|
635
680
|
cached = modelAliasCache.get(cacheKey);
|
|
636
681
|
}
|
|
637
682
|
else if (stale) {
|
|
638
|
-
refreshModelAliases(this.baseUrl, this.apiKey);
|
|
683
|
+
refreshModelAliases(this.baseUrl, this.apiKey, this.customHeaders);
|
|
639
684
|
}
|
|
640
685
|
// 有缓存时返回网关 /models 的全量原始 ID
|
|
641
686
|
if (cached && cached.ids.length > 0)
|
|
@@ -1729,12 +1774,12 @@ export class AgentRunner {
|
|
|
1729
1774
|
const cacheKey = normalizeModelGatewayUrl(this.baseUrl);
|
|
1730
1775
|
const cached = modelAliasCache.get(cacheKey);
|
|
1731
1776
|
if (!cached || (Date.now() - cached.fetchedAt > MODEL_ALIAS_TTL_MS)) {
|
|
1732
|
-
refreshModelAliases(this.baseUrl, this.apiKey);
|
|
1777
|
+
refreshModelAliases(this.baseUrl, this.apiKey, this.customHeaders);
|
|
1733
1778
|
}
|
|
1734
1779
|
// 顺带预热网关价格缓存(1h TTL),让本轮结束写库时已就绪
|
|
1735
1780
|
const pricing = gatewayPricingCache.get(this.baseUrl);
|
|
1736
1781
|
if (!pricing || (Date.now() - pricing.fetchedAt > GATEWAY_PRICING_TTL_MS)) {
|
|
1737
|
-
refreshGatewayPricing(this.baseUrl, this.apiKey);
|
|
1782
|
+
refreshGatewayPricing(this.baseUrl, this.apiKey, this.customHeaders);
|
|
1738
1783
|
}
|
|
1739
1784
|
}
|
|
1740
1785
|
ensureDir(projectPath);
|
|
@@ -1807,6 +1852,27 @@ export class AgentRunner {
|
|
|
1807
1852
|
...(updatedInput ? { updatedInput } : {}),
|
|
1808
1853
|
},
|
|
1809
1854
|
});
|
|
1855
|
+
const recordPreflightDenial = async (toolName, toolInput, preflight, requestId, policyCode) => {
|
|
1856
|
+
const code = policyCode ?? (preflight?.behavior === 'deny' ? preflight.policyCode : undefined);
|
|
1857
|
+
if (!code)
|
|
1858
|
+
return;
|
|
1859
|
+
try {
|
|
1860
|
+
await this.permissionContexts.get(sessionId)?.recordExecutionAnomaly?.({
|
|
1861
|
+
code: 'operation_blocked',
|
|
1862
|
+
severity: 'warning',
|
|
1863
|
+
phase: 'execution',
|
|
1864
|
+
occurredAt: Date.now(),
|
|
1865
|
+
toolName,
|
|
1866
|
+
...(typeof requestId === 'string' && requestId ? { requestId } : {}),
|
|
1867
|
+
policyCode: code,
|
|
1868
|
+
summary: summarizeToolInputForAudit(toolName, toolInput).slice(0, 512),
|
|
1869
|
+
effect: 'operation_skipped',
|
|
1870
|
+
});
|
|
1871
|
+
}
|
|
1872
|
+
catch (error) {
|
|
1873
|
+
logger.warn(`[AgentRunner] failed to record preflight denial: session=${sessionId} tool=${toolName} code=${code} error=${error instanceof Error ? error.message : String(error)}`);
|
|
1874
|
+
}
|
|
1875
|
+
};
|
|
1810
1876
|
// A rewritten helper command is trusted only after this hook produced its
|
|
1811
1877
|
// random one-shot payload from a validated output wrapper.
|
|
1812
1878
|
const trustedSafeOutputCommands = new Set();
|
|
@@ -1851,6 +1917,7 @@ export class AgentRunner {
|
|
|
1851
1917
|
// proactive 模式行为策略(首次工具调用必须是 ec msg send)
|
|
1852
1918
|
const policyResult = this.permissionContexts.get(sessionId)?.policyHook?.(toolName, toolInput);
|
|
1853
1919
|
if (policyResult?.block) {
|
|
1920
|
+
await recordPreflightDenial(toolName, toolInput, undefined, input.tool_use_id ?? input.toolUseID, policyResult.policyCode ?? 'session_policy_hook');
|
|
1854
1921
|
return hookDecision('deny', policyResult.reason || '当前会话策略拒绝此工具调用', updatedInput);
|
|
1855
1922
|
}
|
|
1856
1923
|
const permCtx = this.permissionContexts.get(sessionId);
|
|
@@ -1859,6 +1926,7 @@ export class AgentRunner {
|
|
|
1859
1926
|
: undefined;
|
|
1860
1927
|
const preflight = evaluateToolPreflight(toolName, toolInput, {
|
|
1861
1928
|
sessionId,
|
|
1929
|
+
selfAid: permCtx?.selfAid,
|
|
1862
1930
|
channel: permCtx?.channel,
|
|
1863
1931
|
userId: permCtx?.userId,
|
|
1864
1932
|
role: permCtx?.role,
|
|
@@ -1870,8 +1938,10 @@ export class AgentRunner {
|
|
|
1870
1938
|
toolInput = preflight.input;
|
|
1871
1939
|
if (toolInput !== originalInput)
|
|
1872
1940
|
updatedInput = toolInput;
|
|
1873
|
-
if (preflight.behavior === 'deny')
|
|
1941
|
+
if (preflight.behavior === 'deny') {
|
|
1942
|
+
await recordPreflightDenial(toolName, toolInput, preflight, input.tool_use_id ?? input.toolUseID);
|
|
1874
1943
|
return hookDecision('deny', preflight.message, updatedInput);
|
|
1944
|
+
}
|
|
1875
1945
|
if (preflight.behavior === 'allow') {
|
|
1876
1946
|
const commandAfterPreflight = typeof toolInput.command === 'string' ? toolInput.command : undefined;
|
|
1877
1947
|
if (commandAfterPreflight && commandAfterPreflight !== commandBeforePreflight) {
|
|
@@ -1893,10 +1963,10 @@ export class AgentRunner {
|
|
|
1893
1963
|
channel: permCtx?.channel,
|
|
1894
1964
|
peerId: permCtx?.userId,
|
|
1895
1965
|
role: permCtx?.role,
|
|
1896
|
-
allowLiteralReadShell: true,
|
|
1897
1966
|
};
|
|
1898
1967
|
const roResult = checkReadonly(toolName, toolInput, projectPath, readonlyContext);
|
|
1899
1968
|
if (roResult.behavior === 'deny') {
|
|
1969
|
+
await recordPreflightDenial(toolName, toolInput, undefined, input.tool_use_id ?? input.toolUseID, 'readonly_mode');
|
|
1900
1970
|
return hookDecision('deny', roResult.message, updatedInput);
|
|
1901
1971
|
}
|
|
1902
1972
|
return hookDecision('allow', undefined, updatedInput);
|
|
@@ -1906,6 +1976,7 @@ export class AgentRunner {
|
|
|
1906
1976
|
}
|
|
1907
1977
|
if (isUnknownClaudeTool(toolName)) {
|
|
1908
1978
|
if (callPermissionMode === 'auto') {
|
|
1979
|
+
await recordPreflightDenial(toolName, toolInput, undefined, input.tool_use_id ?? input.toolUseID, 'auto_unknown_tool');
|
|
1909
1980
|
return hookDecision('deny', `auto 模式拒绝未知工具 ${toolName}:该工具不受 Claude 本地 sandbox 的完整约束`, updatedInput);
|
|
1910
1981
|
}
|
|
1911
1982
|
// request/bypass only receive external tools admitted by the session's
|
|
@@ -1915,6 +1986,7 @@ export class AgentRunner {
|
|
|
1915
1986
|
const danger = checkDangerousCommand(toolName, toolInput);
|
|
1916
1987
|
if (danger.isDangerous) {
|
|
1917
1988
|
if (callPermissionMode === 'auto') {
|
|
1989
|
+
await recordPreflightDenial(toolName, toolInput, undefined, input.tool_use_id ?? input.toolUseID, 'auto_dangerous_command');
|
|
1918
1990
|
return hookDecision('deny', `危险操作已由 auto 模式拒绝:${danger.reason}`, updatedInput);
|
|
1919
1991
|
}
|
|
1920
1992
|
if (callPermissionMode === 'request' || callPermissionMode === 'bypass') {
|
|
@@ -1974,6 +2046,7 @@ export class AgentRunner {
|
|
|
1974
2046
|
const permCtx = this.permissionContexts.get(sessionId);
|
|
1975
2047
|
const preflight = evaluateToolPreflight(toolName, input, {
|
|
1976
2048
|
sessionId,
|
|
2049
|
+
selfAid: permCtx?.selfAid,
|
|
1977
2050
|
channel: permCtx?.channel,
|
|
1978
2051
|
userId: permCtx?.userId,
|
|
1979
2052
|
role: permCtx?.role,
|
|
@@ -1984,6 +2057,7 @@ export class AgentRunner {
|
|
|
1984
2057
|
});
|
|
1985
2058
|
input = preflight.input;
|
|
1986
2059
|
if (preflight.behavior === 'deny') {
|
|
2060
|
+
await recordPreflightDenial(toolName, input, preflight, options.toolUseID);
|
|
1987
2061
|
return { behavior: 'deny', message: preflight.message, decisionClassification: 'user_reject' };
|
|
1988
2062
|
}
|
|
1989
2063
|
if (preflight.behavior === 'allow') {
|
|
@@ -1995,6 +2069,7 @@ export class AgentRunner {
|
|
|
1995
2069
|
}
|
|
1996
2070
|
const permissionExpansion = inspectClaudePermissionExpansion(input, options, projectPath);
|
|
1997
2071
|
if (permissionExpansion.protectedReason) {
|
|
2072
|
+
await recordPreflightDenial(toolName, input, undefined, options.toolUseID, 'protected_permission_expansion');
|
|
1998
2073
|
return {
|
|
1999
2074
|
behavior: 'deny',
|
|
2000
2075
|
message: permissionExpansion.protectedReason,
|
|
@@ -2003,6 +2078,7 @@ export class AgentRunner {
|
|
|
2003
2078
|
}
|
|
2004
2079
|
if (permissionExpansion.requiresApproval) {
|
|
2005
2080
|
if (callPermissionMode === 'readonly' || callPermissionMode === 'auto') {
|
|
2081
|
+
await recordPreflightDenial(toolName, input, undefined, options.toolUseID, callPermissionMode === 'readonly' ? 'readonly_permission_expansion' : 'auto_permission_expansion');
|
|
2006
2082
|
return {
|
|
2007
2083
|
behavior: 'deny',
|
|
2008
2084
|
message: `${callPermissionMode} 模式拒绝 Claude SDK 文件系统或未知权限扩权请求`,
|
|
@@ -2043,10 +2119,10 @@ export class AgentRunner {
|
|
|
2043
2119
|
channel: permCtx?.channel,
|
|
2044
2120
|
peerId: permCtx?.userId,
|
|
2045
2121
|
role: permCtx?.role,
|
|
2046
|
-
allowLiteralReadShell: true,
|
|
2047
2122
|
};
|
|
2048
2123
|
const roResult = checkReadonly(toolName, input, projectPath, readonlyContext);
|
|
2049
2124
|
if (roResult.behavior === 'deny') {
|
|
2125
|
+
await recordPreflightDenial(toolName, input, undefined, options.toolUseID, 'readonly_mode');
|
|
2050
2126
|
return { behavior: 'deny', message: roResult.message, decisionClassification: 'user_reject' };
|
|
2051
2127
|
}
|
|
2052
2128
|
return { behavior: 'allow', updatedInput: input, decisionClassification: 'user_permanent' };
|
|
@@ -2056,6 +2132,7 @@ export class AgentRunner {
|
|
|
2056
2132
|
}
|
|
2057
2133
|
if (isUnknownClaudeTool(toolName)) {
|
|
2058
2134
|
if (callPermissionMode === 'auto') {
|
|
2135
|
+
await recordPreflightDenial(toolName, input, undefined, options.toolUseID, 'auto_unknown_tool');
|
|
2059
2136
|
return {
|
|
2060
2137
|
behavior: 'deny',
|
|
2061
2138
|
message: `auto 模式拒绝未知工具 ${toolName}:该工具不受 Claude 本地 sandbox 的完整约束`,
|
|
@@ -2067,6 +2144,7 @@ export class AgentRunner {
|
|
|
2067
2144
|
const danger = checkDangerousCommand(toolName, input);
|
|
2068
2145
|
if (danger.isDangerous) {
|
|
2069
2146
|
if (callPermissionMode === 'auto') {
|
|
2147
|
+
await recordPreflightDenial(toolName, input, undefined, options.toolUseID, 'auto_dangerous_command');
|
|
2070
2148
|
return { behavior: 'deny', message: `auto 模式拒绝危险操作:${danger.reason}`, decisionClassification: 'user_reject' };
|
|
2071
2149
|
}
|
|
2072
2150
|
if (callPermissionMode === 'request' || callPermissionMode === 'bypass') {
|
|
@@ -2129,7 +2207,9 @@ export class AgentRunner {
|
|
|
2129
2207
|
: await (async () => {
|
|
2130
2208
|
const sandboxProjectionStartedAt = Date.now();
|
|
2131
2209
|
ensureClaudeGitWorktreeConfig(projectPath);
|
|
2132
|
-
const sandboxFilesystem = buildClaudeProtectedFilesystem(resolvePaths().root
|
|
2210
|
+
const sandboxFilesystem = buildClaudeProtectedFilesystem(resolvePaths().root, {
|
|
2211
|
+
denyLClassRead: callPermissionMode === 'readonly',
|
|
2212
|
+
});
|
|
2133
2213
|
const sandboxUnixSockets = buildClaudeUnixSocketAllowlist(projectPath, {
|
|
2134
2214
|
role: runPermissionContext?.role,
|
|
2135
2215
|
});
|
|
@@ -2153,8 +2233,29 @@ export class AgentRunner {
|
|
|
2153
2233
|
};
|
|
2154
2234
|
})();
|
|
2155
2235
|
if (executionSandbox.state === 'off') {
|
|
2156
|
-
logger.info(
|
|
2236
|
+
logger.info(process.platform === 'linux'
|
|
2237
|
+
? '[ClaudeSandbox] task sandbox disabled for authenticated owner bypass; outer H-class guard remains active'
|
|
2238
|
+
: '[ClaudeSandbox] disabled for authenticated owner bypass task');
|
|
2157
2239
|
}
|
|
2240
|
+
const handleClaudeStderr = (msg) => {
|
|
2241
|
+
const trimmed = msg.trim();
|
|
2242
|
+
if (trimmed) {
|
|
2243
|
+
let buf = this.recentStderr.get(sessionId);
|
|
2244
|
+
if (!buf) {
|
|
2245
|
+
buf = [];
|
|
2246
|
+
this.recentStderr.set(sessionId, buf);
|
|
2247
|
+
}
|
|
2248
|
+
buf.push(trimmed);
|
|
2249
|
+
if (buf.length > AgentRunner.STDERR_BUFFER_MAX)
|
|
2250
|
+
buf.shift();
|
|
2251
|
+
}
|
|
2252
|
+
if (msg.includes('[ERROR]') || msg.includes('[WARN]') || msg.includes('Stream started')) {
|
|
2253
|
+
logger.info(`[Claude-stderr] ${trimmed}`);
|
|
2254
|
+
}
|
|
2255
|
+
else {
|
|
2256
|
+
logger.debug(`[Claude-stderr] ${trimmed}`);
|
|
2257
|
+
}
|
|
2258
|
+
};
|
|
2158
2259
|
const commonOptions = {
|
|
2159
2260
|
cwd: projectPath,
|
|
2160
2261
|
model: sdkModel,
|
|
@@ -2163,6 +2264,9 @@ export class AgentRunner {
|
|
|
2163
2264
|
managedSettings,
|
|
2164
2265
|
...(callEffort ? { effort: callEffort } : {}),
|
|
2165
2266
|
...(this.claudeExecutablePath ? { pathToClaudeCodeExecutable: this.claudeExecutablePath } : {}),
|
|
2267
|
+
...(executionSandbox.state === 'off' && process.platform === 'linux' ? {
|
|
2268
|
+
spawnClaudeCodeProcess: (options) => spawnClaudeWithHClassGuard(options, resolvePaths().root, handleClaudeStderr),
|
|
2269
|
+
} : {}),
|
|
2166
2270
|
autoCompactWindow: autoCompactWindowForModel(sdkModel),
|
|
2167
2271
|
advisorModel: 'haiku',
|
|
2168
2272
|
canUseTool: canUseToolCallback,
|
|
@@ -2177,27 +2281,8 @@ export class AgentRunner {
|
|
|
2177
2281
|
PermissionDenied: [{ matcher: '.*', hooks: [permissionDeniedHook] }]
|
|
2178
2282
|
},
|
|
2179
2283
|
...(enableSummaries ? { agentProgressSummaries: true } : {}),
|
|
2180
|
-
stderr:
|
|
2181
|
-
|
|
2182
|
-
if (trimmed) {
|
|
2183
|
-
// 环形缓冲:保留最近 N 行,供子进程崩溃时还原真实原因
|
|
2184
|
-
let buf = this.recentStderr.get(sessionId);
|
|
2185
|
-
if (!buf) {
|
|
2186
|
-
buf = [];
|
|
2187
|
-
this.recentStderr.set(sessionId, buf);
|
|
2188
|
-
}
|
|
2189
|
-
buf.push(trimmed);
|
|
2190
|
-
if (buf.length > AgentRunner.STDERR_BUFFER_MAX)
|
|
2191
|
-
buf.shift();
|
|
2192
|
-
}
|
|
2193
|
-
if (msg.includes('[ERROR]') || msg.includes('[WARN]') || msg.includes('Stream started')) {
|
|
2194
|
-
logger.info(`[Claude-stderr] ${trimmed}`);
|
|
2195
|
-
}
|
|
2196
|
-
else {
|
|
2197
|
-
logger.debug(`[Claude-stderr] ${trimmed}`);
|
|
2198
|
-
}
|
|
2199
|
-
},
|
|
2200
|
-
env: this.getAgentEnv(runtimeEnv, sessionId)
|
|
2284
|
+
stderr: handleClaudeStderr,
|
|
2285
|
+
env: this.getAgentEnv(runtimeEnv, sessionId, callPermissionMode)
|
|
2201
2286
|
};
|
|
2202
2287
|
const createQuery = (promptInput, resumeSessionId, resumeAt) => {
|
|
2203
2288
|
if (useSettingSources) {
|
|
@@ -2479,6 +2564,35 @@ export class AgentRunner {
|
|
|
2479
2564
|
this.activeQueries.delete(sessionId);
|
|
2480
2565
|
this.permissionContexts.delete(sessionId);
|
|
2481
2566
|
}
|
|
2567
|
+
async dispose() {
|
|
2568
|
+
const interrupts = [...this.interruptFns.values()].map(async (interrupt) => {
|
|
2569
|
+
try {
|
|
2570
|
+
await interrupt();
|
|
2571
|
+
}
|
|
2572
|
+
catch { }
|
|
2573
|
+
});
|
|
2574
|
+
await Promise.allSettled(interrupts);
|
|
2575
|
+
for (const stream of this.activeMessageStreams.values())
|
|
2576
|
+
stream.end();
|
|
2577
|
+
for (const query of this.activeQueries.values()) {
|
|
2578
|
+
try {
|
|
2579
|
+
if (typeof query?.close === 'function')
|
|
2580
|
+
await query.close();
|
|
2581
|
+
}
|
|
2582
|
+
catch { }
|
|
2583
|
+
}
|
|
2584
|
+
for (const resolve of this.streamDoneResolvers.values())
|
|
2585
|
+
resolve();
|
|
2586
|
+
this.activeSessions.clear();
|
|
2587
|
+
this.activeStreams.clear();
|
|
2588
|
+
this.activeMessageStreams.clear();
|
|
2589
|
+
this.interruptFns.clear();
|
|
2590
|
+
this.activeQueries.clear();
|
|
2591
|
+
this.streamDone.clear();
|
|
2592
|
+
this.streamDoneResolvers.clear();
|
|
2593
|
+
this.permissionContexts.clear();
|
|
2594
|
+
this.recentStderr.clear();
|
|
2595
|
+
}
|
|
2482
2596
|
resolveSessionFile(agentSessionId, projectPath) {
|
|
2483
2597
|
const encodedProjectPath = encodePath(projectPath);
|
|
2484
2598
|
const sessionFile = path.join(os.homedir(), '.claude', 'projects', encodedProjectPath, `${agentSessionId}.jsonl`);
|