evolcore 0.0.10 → 0.0.11
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 +22 -0
- package/README.md +3 -3
- package/dist/agents/baseagent.js +4 -0
- package/dist/agents/claude-runner.js +123 -42
- package/dist/agents/codex-app-server-client.js +33 -9
- package/dist/agents/codex-runner.js +58 -8
- package/dist/agents/ecagent-runner.js +17 -2
- package/dist/agents/request-identity.js +55 -0
- package/dist/aun/outbox.js +28 -31
- package/dist/channels/aun.js +131 -128
- package/dist/cli/agent-command.js +16 -9
- package/dist/cli/agent.js +82 -19
- package/dist/cli/daemon-commands.js +21 -2
- package/dist/cli/index.js +76 -61
- 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/config/builtin-roles.js +1 -0
- package/dist/config/gateway-config.js +26 -10
- package/dist/core/agent-reload-coordinator.js +53 -0
- package/dist/core/auth/operation-authorizer.js +32 -147
- package/dist/core/auth/operation-catalog.js +80 -0
- package/dist/core/bootstrap-messages.js +50 -0
- package/dist/core/bootstrap-service.js +85 -10
- package/dist/core/channel-loader.js +23 -6
- package/dist/core/command/agent-control.js +14 -11
- package/dist/core/command/menu-handler.js +67 -76
- package/dist/core/command/slash-handler.js +4 -4
- package/dist/core/evolagent-registry.js +125 -35
- package/dist/core/evolagent.js +8 -3
- package/dist/core/inference/text-inference.js +38 -4
- package/dist/core/message/message-bridge.js +1 -1
- package/dist/core/message/message-log.js +22 -0
- package/dist/core/message/message-queue.js +19 -4
- package/dist/core/model/model-catalog.js +143 -24
- package/dist/core/model/model-diagnostics.js +28 -10
- package/dist/core/permission/index.js +1 -0
- package/dist/core/permission/readonly-shell-query.js +532 -0
- package/dist/core/permission/shell-environment.js +46 -0
- package/dist/core/permission/tool-policy.js +231 -93
- package/dist/core/protected-paths.js +10 -7
- package/dist/core/runner-reload-transaction.js +57 -0
- package/dist/index.js +262 -84
- package/dist/ipc.js +29 -11
- package/dist/utils/aid-bind.js +3 -8
- package/dist/utils/log-writer.js +6 -10
- package/dist/utils/logger.js +5 -5
- package/kits/docs/evolcore/msg.md +13 -0
- package/kits/rules/01-overview.md +9 -0
- 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/templates/roles/admin.json +5 -0
- package/kits/templates/roles/member.json +17 -0
- package/kits/templates/roles/visitor.json +8 -0
- 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 +2 -1
- package/skills/eclink/SKILL.md +15 -3
- package/skills/eclink/agents/openai.yaml +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
3
3
|
本文件记录 EvolCore 的重要变更。EvolClaw 版本线的历史记录已归档至
|
|
4
4
|
[`docs/_archive/CHANGELOG-evolclaw.md`](docs/_archive/CHANGELOG-evolclaw.md)。
|
|
5
5
|
|
|
6
|
+
## 0.0.11 (2026-08-11)
|
|
7
|
+
|
|
8
|
+
### Agent 初始化与引导
|
|
9
|
+
|
|
10
|
+
- `ec init` 支持在任意交互步骤连续按两次 Esc 或使用 Ctrl+C 安全退出,并完善二维码超时后的重新生成、手动绑定与取消清理流程。
|
|
11
|
+
- 重新设计 Agent bootstrap 引导消息,在 Agent 完成初始化后可靠投递,并通过持久化和去重避免断线重试造成消息丢失或重复。
|
|
12
|
+
|
|
13
|
+
### 权限与消息可靠性
|
|
14
|
+
|
|
15
|
+
- 扩展安全只读 Shell 查询和环境变量解析能力,统一工具授权、危险命令识别与审批边界。
|
|
16
|
+
- 完善 AUN 关键消息的持久化、去重和日志落盘,提升断线恢复与异常重试时的一致性。
|
|
17
|
+
|
|
18
|
+
### 请求身份与模型目录
|
|
19
|
+
|
|
20
|
+
- 模型请求统一携带 EvolCore 版本、Agent AID 和 BaseAgent 类型,便于网关进行来源识别、审计和问题定位。
|
|
21
|
+
- Claude 元数据请求复用 Agent 自定义请求头,同时隔离公共模型目录请求中的 API Key、租户头和 Agent 身份信息。
|
|
22
|
+
|
|
23
|
+
### 热重载
|
|
24
|
+
|
|
25
|
+
- 同一 Agent 的重载操作改为串行执行;任务繁忙时默认拒绝重载,也可通过 `--force` 清理队列并等待运行中任务中断。
|
|
26
|
+
- 为 runner 与渠道重载建立明确的提交边界和失败回滚,完善 resync 下线清理,避免重载失败后残留旧实例或半更新状态。
|
|
27
|
+
|
|
6
28
|
## 0.0.10 (2026-08-06)
|
|
7
29
|
|
|
8
30
|
### 数据迁移
|
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`)。
|
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,6 +13,7 @@ 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';
|
|
16
|
+
import { sanitizeShellExecutionEnvironment } from '../core/permission/shell-environment.js';
|
|
14
17
|
import { summarizeToolInput } from '../utils/tool-summary.js';
|
|
15
18
|
import { encodePath } from '../utils/cross-platform.js';
|
|
16
19
|
import { getPackageRoot, resolvePaths } from '../paths.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);
|
|
@@ -1859,6 +1904,7 @@ export class AgentRunner {
|
|
|
1859
1904
|
: undefined;
|
|
1860
1905
|
const preflight = evaluateToolPreflight(toolName, toolInput, {
|
|
1861
1906
|
sessionId,
|
|
1907
|
+
selfAid: permCtx?.selfAid,
|
|
1862
1908
|
channel: permCtx?.channel,
|
|
1863
1909
|
userId: permCtx?.userId,
|
|
1864
1910
|
role: permCtx?.role,
|
|
@@ -1893,7 +1939,6 @@ export class AgentRunner {
|
|
|
1893
1939
|
channel: permCtx?.channel,
|
|
1894
1940
|
peerId: permCtx?.userId,
|
|
1895
1941
|
role: permCtx?.role,
|
|
1896
|
-
allowLiteralReadShell: true,
|
|
1897
1942
|
};
|
|
1898
1943
|
const roResult = checkReadonly(toolName, toolInput, projectPath, readonlyContext);
|
|
1899
1944
|
if (roResult.behavior === 'deny') {
|
|
@@ -1974,6 +2019,7 @@ export class AgentRunner {
|
|
|
1974
2019
|
const permCtx = this.permissionContexts.get(sessionId);
|
|
1975
2020
|
const preflight = evaluateToolPreflight(toolName, input, {
|
|
1976
2021
|
sessionId,
|
|
2022
|
+
selfAid: permCtx?.selfAid,
|
|
1977
2023
|
channel: permCtx?.channel,
|
|
1978
2024
|
userId: permCtx?.userId,
|
|
1979
2025
|
role: permCtx?.role,
|
|
@@ -2043,7 +2089,6 @@ export class AgentRunner {
|
|
|
2043
2089
|
channel: permCtx?.channel,
|
|
2044
2090
|
peerId: permCtx?.userId,
|
|
2045
2091
|
role: permCtx?.role,
|
|
2046
|
-
allowLiteralReadShell: true,
|
|
2047
2092
|
};
|
|
2048
2093
|
const roResult = checkReadonly(toolName, input, projectPath, readonlyContext);
|
|
2049
2094
|
if (roResult.behavior === 'deny') {
|
|
@@ -2129,7 +2174,9 @@ export class AgentRunner {
|
|
|
2129
2174
|
: await (async () => {
|
|
2130
2175
|
const sandboxProjectionStartedAt = Date.now();
|
|
2131
2176
|
ensureClaudeGitWorktreeConfig(projectPath);
|
|
2132
|
-
const sandboxFilesystem = buildClaudeProtectedFilesystem(resolvePaths().root
|
|
2177
|
+
const sandboxFilesystem = buildClaudeProtectedFilesystem(resolvePaths().root, {
|
|
2178
|
+
denyLClassRead: callPermissionMode === 'readonly',
|
|
2179
|
+
});
|
|
2133
2180
|
const sandboxUnixSockets = buildClaudeUnixSocketAllowlist(projectPath, {
|
|
2134
2181
|
role: runPermissionContext?.role,
|
|
2135
2182
|
});
|
|
@@ -2153,8 +2200,29 @@ export class AgentRunner {
|
|
|
2153
2200
|
};
|
|
2154
2201
|
})();
|
|
2155
2202
|
if (executionSandbox.state === 'off') {
|
|
2156
|
-
logger.info(
|
|
2203
|
+
logger.info(process.platform === 'linux'
|
|
2204
|
+
? '[ClaudeSandbox] task sandbox disabled for authenticated owner bypass; outer H-class guard remains active'
|
|
2205
|
+
: '[ClaudeSandbox] disabled for authenticated owner bypass task');
|
|
2157
2206
|
}
|
|
2207
|
+
const handleClaudeStderr = (msg) => {
|
|
2208
|
+
const trimmed = msg.trim();
|
|
2209
|
+
if (trimmed) {
|
|
2210
|
+
let buf = this.recentStderr.get(sessionId);
|
|
2211
|
+
if (!buf) {
|
|
2212
|
+
buf = [];
|
|
2213
|
+
this.recentStderr.set(sessionId, buf);
|
|
2214
|
+
}
|
|
2215
|
+
buf.push(trimmed);
|
|
2216
|
+
if (buf.length > AgentRunner.STDERR_BUFFER_MAX)
|
|
2217
|
+
buf.shift();
|
|
2218
|
+
}
|
|
2219
|
+
if (msg.includes('[ERROR]') || msg.includes('[WARN]') || msg.includes('Stream started')) {
|
|
2220
|
+
logger.info(`[Claude-stderr] ${trimmed}`);
|
|
2221
|
+
}
|
|
2222
|
+
else {
|
|
2223
|
+
logger.debug(`[Claude-stderr] ${trimmed}`);
|
|
2224
|
+
}
|
|
2225
|
+
};
|
|
2158
2226
|
const commonOptions = {
|
|
2159
2227
|
cwd: projectPath,
|
|
2160
2228
|
model: sdkModel,
|
|
@@ -2163,6 +2231,9 @@ export class AgentRunner {
|
|
|
2163
2231
|
managedSettings,
|
|
2164
2232
|
...(callEffort ? { effort: callEffort } : {}),
|
|
2165
2233
|
...(this.claudeExecutablePath ? { pathToClaudeCodeExecutable: this.claudeExecutablePath } : {}),
|
|
2234
|
+
...(executionSandbox.state === 'off' && process.platform === 'linux' ? {
|
|
2235
|
+
spawnClaudeCodeProcess: (options) => spawnClaudeWithHClassGuard(options, resolvePaths().root, handleClaudeStderr),
|
|
2236
|
+
} : {}),
|
|
2166
2237
|
autoCompactWindow: autoCompactWindowForModel(sdkModel),
|
|
2167
2238
|
advisorModel: 'haiku',
|
|
2168
2239
|
canUseTool: canUseToolCallback,
|
|
@@ -2177,27 +2248,8 @@ export class AgentRunner {
|
|
|
2177
2248
|
PermissionDenied: [{ matcher: '.*', hooks: [permissionDeniedHook] }]
|
|
2178
2249
|
},
|
|
2179
2250
|
...(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)
|
|
2251
|
+
stderr: handleClaudeStderr,
|
|
2252
|
+
env: this.getAgentEnv(runtimeEnv, sessionId, callPermissionMode)
|
|
2201
2253
|
};
|
|
2202
2254
|
const createQuery = (promptInput, resumeSessionId, resumeAt) => {
|
|
2203
2255
|
if (useSettingSources) {
|
|
@@ -2479,6 +2531,35 @@ export class AgentRunner {
|
|
|
2479
2531
|
this.activeQueries.delete(sessionId);
|
|
2480
2532
|
this.permissionContexts.delete(sessionId);
|
|
2481
2533
|
}
|
|
2534
|
+
async dispose() {
|
|
2535
|
+
const interrupts = [...this.interruptFns.values()].map(async (interrupt) => {
|
|
2536
|
+
try {
|
|
2537
|
+
await interrupt();
|
|
2538
|
+
}
|
|
2539
|
+
catch { }
|
|
2540
|
+
});
|
|
2541
|
+
await Promise.allSettled(interrupts);
|
|
2542
|
+
for (const stream of this.activeMessageStreams.values())
|
|
2543
|
+
stream.end();
|
|
2544
|
+
for (const query of this.activeQueries.values()) {
|
|
2545
|
+
try {
|
|
2546
|
+
if (typeof query?.close === 'function')
|
|
2547
|
+
await query.close();
|
|
2548
|
+
}
|
|
2549
|
+
catch { }
|
|
2550
|
+
}
|
|
2551
|
+
for (const resolve of this.streamDoneResolvers.values())
|
|
2552
|
+
resolve();
|
|
2553
|
+
this.activeSessions.clear();
|
|
2554
|
+
this.activeStreams.clear();
|
|
2555
|
+
this.activeMessageStreams.clear();
|
|
2556
|
+
this.interruptFns.clear();
|
|
2557
|
+
this.activeQueries.clear();
|
|
2558
|
+
this.streamDone.clear();
|
|
2559
|
+
this.streamDoneResolvers.clear();
|
|
2560
|
+
this.permissionContexts.clear();
|
|
2561
|
+
this.recentStderr.clear();
|
|
2562
|
+
}
|
|
2482
2563
|
resolveSessionFile(agentSessionId, projectPath) {
|
|
2483
2564
|
const encodedProjectPath = encodePath(projectPath);
|
|
2484
2565
|
const sessionFile = path.join(os.homedir(), '.claude', 'projects', encodedProjectPath, `${agentSessionId}.jsonl`);
|
|
@@ -4,6 +4,8 @@ import { logger } from '../utils/logger.js';
|
|
|
4
4
|
import { buildHClassGuardCommand } from '../core/permission/sandbox-runtime.js';
|
|
5
5
|
import { resolveCodexLaunchCommand } from '../utils/codex-cli.js';
|
|
6
6
|
import { createHash } from 'crypto';
|
|
7
|
+
import { sanitizeShellExecutionEnvironment } from '../core/permission/shell-environment.js';
|
|
8
|
+
const BUILT_IN_CODEX_PROVIDER_IDS = new Set(['openai']);
|
|
7
9
|
function tomlProviderKey(value) {
|
|
8
10
|
if (!value || /[\0\r\n]/.test(value)) {
|
|
9
11
|
throw new Error(`Invalid Codex provider name: ${value}`);
|
|
@@ -19,6 +21,10 @@ function codexHeaderEnvName(headerName) {
|
|
|
19
21
|
const digest = createHash('sha256').update(headerName).digest('hex').slice(0, 16).toUpperCase();
|
|
20
22
|
return `EVOLCORE_CODEX_HEADER_${digest}`;
|
|
21
23
|
}
|
|
24
|
+
function runtimeProviderId(baseUrl) {
|
|
25
|
+
const digest = createHash('sha256').update(baseUrl || 'default').digest('hex').slice(0, 12);
|
|
26
|
+
return `evolcore_runtime_${digest}`;
|
|
27
|
+
}
|
|
22
28
|
function hasPermissionProfile(config) {
|
|
23
29
|
if (!config || typeof config.default_permissions !== 'string' || !config.default_permissions)
|
|
24
30
|
return false;
|
|
@@ -341,14 +347,7 @@ export class CodexAppServerClient {
|
|
|
341
347
|
}
|
|
342
348
|
}
|
|
343
349
|
startProcess() {
|
|
344
|
-
const env =
|
|
345
|
-
env.CODEX_API_KEY = this.options.apiKey;
|
|
346
|
-
for (const [name, value] of Object.entries(this.options.headers ?? {})) {
|
|
347
|
-
env[codexHeaderEnvName(name)] = value;
|
|
348
|
-
}
|
|
349
|
-
if (this.options.baseUrl) {
|
|
350
|
-
env.OPENAI_BASE_URL = this.options.baseUrl;
|
|
351
|
-
}
|
|
350
|
+
const env = this.buildProcessEnvironment();
|
|
352
351
|
const args = this.buildProcessArgs();
|
|
353
352
|
const launchCommand = resolveCodexLaunchCommand(args);
|
|
354
353
|
if (!launchCommand)
|
|
@@ -378,6 +377,20 @@ export class CodexAppServerClient {
|
|
|
378
377
|
logger.debug(`[CodexAppServer] ${text}`);
|
|
379
378
|
});
|
|
380
379
|
}
|
|
380
|
+
buildProcessEnvironment() {
|
|
381
|
+
const env = sanitizeShellExecutionEnvironment({
|
|
382
|
+
...process.env,
|
|
383
|
+
...this.options.env,
|
|
384
|
+
});
|
|
385
|
+
env.CODEX_API_KEY = this.options.apiKey;
|
|
386
|
+
for (const [name, value] of Object.entries(this.options.headers ?? {})) {
|
|
387
|
+
env[codexHeaderEnvName(name)] = value;
|
|
388
|
+
}
|
|
389
|
+
if (this.options.baseUrl) {
|
|
390
|
+
env.OPENAI_BASE_URL = this.options.baseUrl;
|
|
391
|
+
}
|
|
392
|
+
return env;
|
|
393
|
+
}
|
|
381
394
|
buildProcessArgs() {
|
|
382
395
|
const args = ['app-server', '--listen', 'stdio://'];
|
|
383
396
|
if (this.options.enableRequestUserInput !== false) {
|
|
@@ -390,7 +403,18 @@ export class CodexAppServerClient {
|
|
|
390
403
|
const hasHeaders = Object.keys(headerEnvNames).length > 0;
|
|
391
404
|
const hasQueryParams = Object.keys(this.options.queryParams ?? {}).length > 0;
|
|
392
405
|
if (hasHeaders || hasQueryParams) {
|
|
393
|
-
const
|
|
406
|
+
const configuredProvider = this.options.provider?.trim();
|
|
407
|
+
const useRuntimeProvider = !configuredProvider || BUILT_IN_CODEX_PROVIDER_IDS.has(configuredProvider.toLowerCase());
|
|
408
|
+
const providerName = !useRuntimeProvider
|
|
409
|
+
? configuredProvider
|
|
410
|
+
: runtimeProviderId(this.options.baseUrl);
|
|
411
|
+
const provider = tomlProviderKey(providerName);
|
|
412
|
+
args.push('--config', `model_provider=${JSON.stringify(providerName)}`);
|
|
413
|
+
args.push('--config', `model_providers.${provider}.name=${JSON.stringify('EvolCore runtime gateway')}`);
|
|
414
|
+
args.push('--config', `model_providers.${provider}.wire_api=${JSON.stringify('responses')}`);
|
|
415
|
+
if (useRuntimeProvider || this.options.baseUrl) {
|
|
416
|
+
args.push('--config', `model_providers.${provider}.base_url=${JSON.stringify(this.options.baseUrl || 'https://api.openai.com/v1')}`);
|
|
417
|
+
}
|
|
394
418
|
if (hasHeaders) {
|
|
395
419
|
args.push('--config', `model_providers.${provider}.env_http_headers=${tomlInlineStringTable(headerEnvNames)}`);
|
|
396
420
|
}
|
|
@@ -18,6 +18,7 @@ import { renderActionAsText } from '../core/interaction-router.js';
|
|
|
18
18
|
import { buildEnvelope, sendInteractionPayload } from '../core/message/message-utils.js';
|
|
19
19
|
import { resolveCodexCapabilityThreadConfigForProject } from '../core/capability/capability-manager.js';
|
|
20
20
|
import { AGENT_DELEGATION_TOKEN_ENV, hashDelegatedCommandArgv } from '../core/auth/agent-delegation.js';
|
|
21
|
+
import { sanitizeShellExecutionEnvironment } from '../core/permission/shell-environment.js';
|
|
21
22
|
import { classifyEvolcoreShellCommand, parseLiteralShellArgv } from '../core/permission/ec-command-parser.js';
|
|
22
23
|
import { compareVersions } from '../utils/npm-ops.js';
|
|
23
24
|
import { resolvePaths, resolveRoot } from '../paths.js';
|
|
@@ -25,6 +26,7 @@ import { buildSessionTurnList } from '../core/session/session-turns.js';
|
|
|
25
26
|
import { execFileSync } from 'child_process';
|
|
26
27
|
import { execCodexCliSync, resolveCodexCliPath } from '../utils/codex-cli.js';
|
|
27
28
|
import { createHash, randomBytes } from 'crypto';
|
|
29
|
+
import { buildModelRequestHeaders } from './request-identity.js';
|
|
28
30
|
import fs from 'fs';
|
|
29
31
|
import path from 'path';
|
|
30
32
|
import os from 'os';
|
|
@@ -231,6 +233,12 @@ export class CodexRunner {
|
|
|
231
233
|
pendingInterruptTtlMs = 30_000;
|
|
232
234
|
constructor(config, callbacks) {
|
|
233
235
|
this.resolvedConfig = resolveOpenaiConfig(config);
|
|
236
|
+
this.resolvedConfig.headers = buildModelRequestHeaders({
|
|
237
|
+
baseagent: 'codex',
|
|
238
|
+
baseUrl: this.resolvedConfig.baseUrl,
|
|
239
|
+
agentAid: config.agents?.codex?.evolcoreAgentAid,
|
|
240
|
+
configuredHeaders: this.resolvedConfig.headers,
|
|
241
|
+
});
|
|
234
242
|
this.resolvedConfig.evolcoreAgentAid = config.agents?.codex?.evolcoreAgentAid;
|
|
235
243
|
this.resolvedConfig.evolcoreAgentConfig = config.agents?.codex?.evolcoreAgentConfig;
|
|
236
244
|
this.capabilities = {
|
|
@@ -416,14 +424,35 @@ export class CodexRunner {
|
|
|
416
424
|
}
|
|
417
425
|
async resolveCapabilityThreadConfig(projectPath) {
|
|
418
426
|
const agentConfig = this.resolvedConfig.evolcoreAgentConfig;
|
|
419
|
-
if (!agentConfig)
|
|
420
|
-
return {
|
|
427
|
+
if (!agentConfig) {
|
|
428
|
+
return {
|
|
429
|
+
skills: { config: [{ name: 'eclink', enabled: false }] },
|
|
430
|
+
};
|
|
431
|
+
}
|
|
421
432
|
try {
|
|
422
|
-
|
|
433
|
+
const resolved = await resolveCodexCapabilityThreadConfigForProject(agentConfig, projectPath, 'codex');
|
|
434
|
+
const skills = this.configRecord(resolved.skills);
|
|
435
|
+
const configured = Array.isArray(skills?.config)
|
|
436
|
+
? skills.config.filter(entry => {
|
|
437
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry))
|
|
438
|
+
return true;
|
|
439
|
+
return entry.name !== 'eclink';
|
|
440
|
+
})
|
|
441
|
+
: [];
|
|
442
|
+
return this.mergeThreadConfig(resolved, {
|
|
443
|
+
skills: {
|
|
444
|
+
...(skills ?? {}),
|
|
445
|
+
config: [...configured, { name: 'eclink', enabled: false }],
|
|
446
|
+
},
|
|
447
|
+
}) ?? {
|
|
448
|
+
skills: { config: [{ name: 'eclink', enabled: false }] },
|
|
449
|
+
};
|
|
423
450
|
}
|
|
424
451
|
catch (error) {
|
|
425
452
|
logger.warn(`[CodexRunner] Failed to resolve Codex capability thread config: ${error}`);
|
|
426
|
-
return {
|
|
453
|
+
return {
|
|
454
|
+
skills: { config: [{ name: 'eclink', enabled: false }] },
|
|
455
|
+
};
|
|
427
456
|
}
|
|
428
457
|
}
|
|
429
458
|
configRecord(value) {
|
|
@@ -721,7 +750,9 @@ export class CodexRunner {
|
|
|
721
750
|
[profileName]: {
|
|
722
751
|
extends: mode === 'readonly' ? ':read-only' : ':workspace',
|
|
723
752
|
filesystem: {
|
|
724
|
-
...buildCodexProtectedFilesystemRules(resolveRoot()
|
|
753
|
+
...buildCodexProtectedFilesystemRules(resolveRoot(), {
|
|
754
|
+
denyLClassRead: mode === 'readonly',
|
|
755
|
+
}),
|
|
725
756
|
...this.resolveGitMetadataWriteRules(projectPath, mode),
|
|
726
757
|
...(mode !== 'readonly' && managedTempDir ? {
|
|
727
758
|
[managedTempDir]: 'write',
|
|
@@ -819,7 +850,7 @@ export class CodexRunner {
|
|
|
819
850
|
approvalPolicy: effectiveApprovalPolicy,
|
|
820
851
|
approvalsReviewer: 'user',
|
|
821
852
|
...(executionSandbox.sandbox ? { sandbox: executionSandbox.sandbox } : {}),
|
|
822
|
-
config: this.mergeThreadConfig(executionSandbox.permissionProfileConfig, runtimeEnv ? { shell_environment_policy: { set: runtimeEnv } } : undefined, capabilityConfig, externalToolConfig, this.buildEvolcoreShellEnvironmentConfig(sessionId, delegationCarrierCandidate), this.buildLifecycleLockdownConfig()),
|
|
853
|
+
config: this.sanitizeThreadEnvironmentConfig(this.mergeThreadConfig(executionSandbox.permissionProfileConfig, runtimeEnv ? { shell_environment_policy: { set: runtimeEnv } } : undefined, capabilityConfig, externalToolConfig, this.buildEvolcoreShellEnvironmentConfig(sessionId, delegationCarrierCandidate), this.buildLifecycleLockdownConfig()), callMode),
|
|
823
854
|
...(developerInstructions ? { developerInstructions } : {}),
|
|
824
855
|
};
|
|
825
856
|
logger.info(`[CodexRunner] runQuery permMode=${requestedPermissionMode}->${callMode} ` +
|
|
@@ -1086,7 +1117,7 @@ export class CodexRunner {
|
|
|
1086
1117
|
approvalPolicy: compactPolicy,
|
|
1087
1118
|
approvalsReviewer: 'user',
|
|
1088
1119
|
...(executionSandbox.sandbox ? { sandbox: executionSandbox.sandbox } : {}),
|
|
1089
|
-
config: this.mergeThreadConfig(executionSandbox.permissionProfileConfig, capabilityConfig, externalToolConfig, this.buildEvolcoreShellEnvironmentConfig(_sessionId, compactDelegationCarrier), this.buildLifecycleLockdownConfig()),
|
|
1120
|
+
config: this.sanitizeThreadEnvironmentConfig(this.mergeThreadConfig(executionSandbox.permissionProfileConfig, capabilityConfig, externalToolConfig, this.buildEvolcoreShellEnvironmentConfig(_sessionId, compactDelegationCarrier), this.buildLifecycleLockdownConfig()), compactMode),
|
|
1090
1121
|
});
|
|
1091
1122
|
this.threadProjectPaths.set(agentSessionId, path.resolve(_projectPath));
|
|
1092
1123
|
this.threadDelegationCarriers.set(agentSessionId, compactDelegationCarrier);
|
|
@@ -1236,7 +1267,7 @@ export class CodexRunner {
|
|
|
1236
1267
|
approvalPolicy: this.toApprovalPolicy(mode),
|
|
1237
1268
|
approvalsReviewer: 'user',
|
|
1238
1269
|
...(executionSandbox.sandbox ? { sandbox: executionSandbox.sandbox } : {}),
|
|
1239
|
-
config: this.mergeThreadConfig(executionSandbox.permissionProfileConfig, capabilityConfig, externalToolConfig, this.buildEvolcoreShellEnvironmentConfig(sessionKey, delegationCarrier), this.buildLifecycleLockdownConfig()),
|
|
1270
|
+
config: this.sanitizeThreadEnvironmentConfig(this.mergeThreadConfig(executionSandbox.permissionProfileConfig, capabilityConfig, externalToolConfig, this.buildEvolcoreShellEnvironmentConfig(sessionKey, delegationCarrier), this.buildLifecycleLockdownConfig()), mode),
|
|
1240
1271
|
});
|
|
1241
1272
|
const forkedThreadId = response.thread?.id;
|
|
1242
1273
|
if (!forkedThreadId)
|
|
@@ -1951,6 +1982,7 @@ export class CodexRunner {
|
|
|
1951
1982
|
const mode = normalizePermissionMode(rawMode).mode;
|
|
1952
1983
|
const preflight = evaluateToolPreflight(toolName, toolInput, {
|
|
1953
1984
|
sessionId: sessionKey,
|
|
1985
|
+
selfAid: permissionContext?.selfAid,
|
|
1954
1986
|
channel: permissionContext?.channel,
|
|
1955
1987
|
userId: permissionContext?.userId,
|
|
1956
1988
|
role: permissionContext?.role,
|
|
@@ -2810,6 +2842,24 @@ export class CodexRunner {
|
|
|
2810
2842
|
},
|
|
2811
2843
|
};
|
|
2812
2844
|
}
|
|
2845
|
+
sanitizeThreadEnvironmentConfig(config, permissionMode) {
|
|
2846
|
+
if (!config)
|
|
2847
|
+
return null;
|
|
2848
|
+
const policy = this.configRecord(config.shell_environment_policy);
|
|
2849
|
+
const set = this.configRecord(policy?.set);
|
|
2850
|
+
if (!policy || !set)
|
|
2851
|
+
return config;
|
|
2852
|
+
const safeSet = sanitizeShellExecutionEnvironment(set, {
|
|
2853
|
+
lockExecutableResolution: permissionMode === 'readonly',
|
|
2854
|
+
});
|
|
2855
|
+
return {
|
|
2856
|
+
...config,
|
|
2857
|
+
shell_environment_policy: {
|
|
2858
|
+
...policy,
|
|
2859
|
+
set: safeSet,
|
|
2860
|
+
},
|
|
2861
|
+
};
|
|
2862
|
+
}
|
|
2813
2863
|
async dispose() {
|
|
2814
2864
|
// Abort all active streams
|
|
2815
2865
|
for (const [key, controller] of this.activeAbortControllers) {
|