evolcore 0.0.33 → 0.0.34
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 +37 -0
- package/bin/ec.js +3 -2
- package/bin/install-codex-managed-hooks.mjs +12 -1
- package/dist/agents/baseagent-availability.js +20 -36
- package/dist/agents/claude-runner.js +82 -13
- package/dist/agents/codex/diagnostics.js +37 -11
- package/dist/agents/codex-runner.js +218 -44
- package/dist/agents/ecagent-runner.js +73 -20
- package/dist/agents/runner-types.js +5 -6
- package/dist/aun/aid/identity.js +1 -1
- package/dist/channels/aun.js +16 -6
- package/dist/channels/daemon.js +6 -3
- package/dist/cli/aun-commands.js +3 -2
- package/dist/cli/ctl-command.js +3 -1
- package/dist/cli/daemon-commands.js +120 -19
- package/dist/cli/fs-command.js +2 -1
- package/dist/cli/index.js +20 -6
- package/dist/cli/integrity-command.js +196 -0
- package/dist/cli/memory-command.js +158 -145
- package/dist/cli/thread-command.js +5 -3
- package/dist/cli/trigger-command.js +6 -1
- package/dist/config/config-field-policy.js +4 -0
- package/dist/config/config-manager.js +19 -0
- package/dist/config/contact-book-store.js +5 -0
- package/dist/config/role-service.js +13 -0
- package/dist/config/runtime-env.js +16 -0
- package/dist/config-store.js +30 -0
- package/dist/core/audit/log-integrity.js +3 -1
- package/dist/core/auth/agent-delegation.js +108 -35
- package/dist/core/auth/delegation-diagnostics.js +414 -0
- package/dist/core/auth/operation-authorizer.js +1 -1
- package/dist/core/auth/operation-catalog.js +16 -0
- package/dist/core/command/agent-control.js +18 -26
- package/dist/core/command/command-handler.js +138 -4
- package/dist/core/command/daemon-diagnostics.js +1 -0
- package/dist/core/command/menu-catalog.js +3 -0
- package/dist/core/command/menu-handler.js +22 -7
- package/dist/core/command/menu-protocol.js +1 -0
- package/dist/core/command/slash-handler.js +2 -0
- package/dist/core/daemon-bootstrap.js +4 -7
- package/dist/core/file-integrity/read.js +61 -0
- package/dist/core/file-integrity/store.js +262 -0
- package/dist/core/ipc-registration.js +36 -1
- package/dist/core/memory/batch-meta.js +132 -0
- package/dist/core/memory/consolidation-context.js +35 -14
- package/dist/core/memory/consolidation-ops-records.js +14 -16
- package/dist/core/memory/consolidation.js +91 -26
- package/dist/core/memory/index-manager.js +19 -28
- package/dist/core/memory/memory-admin.js +19 -14
- package/dist/core/memory/memory-document.js +53 -9
- package/dist/core/memory/memory-writer.js +30 -15
- package/dist/core/message/im-renderer.js +20 -4
- package/dist/core/message/message-bridge.js +2 -1
- package/dist/core/message/response-engine.js +584 -47
- package/dist/core/permission/ec-command-parser.js +93 -4
- package/dist/core/permission/protected-paths.js +5 -1
- package/dist/core/permission/readonly-shell-query.js +43 -5
- package/dist/core/permission/session-path-grants.js +334 -0
- package/dist/core/permission/tool-canonical.js +226 -0
- package/dist/core/permission/tool-policy.js +30 -62
- package/dist/core/role/runtime-policy.js +21 -10
- package/dist/core/session/session-fs-store.js +123 -8
- package/dist/core/session/session-manager.js +75 -5
- package/dist/core/skill/index-generator.js +17 -3
- package/dist/core/system-session/grants.js +40 -0
- package/dist/core/system-session/memory.js +222 -0
- package/dist/core/system-session/registry.js +74 -0
- package/dist/core/system-session/types.js +14 -0
- package/dist/core/thread/thread-service.js +13 -20
- package/dist/eck/kit-renderer.js +7 -2
- package/dist/eck/manifest-engine.js +24 -4
- package/dist/index.js +11 -2
- package/dist/ipc.js +51 -40
- package/dist/paths.js +70 -0
- package/dist/stats/billing.js +20 -8
- package/dist/trigger/feedback.js +6 -1
- package/dist/trigger/parser.js +8 -1
- package/dist/trigger/system-definitions.js +3 -3
- package/dist/utils/cross-platform.js +11 -2
- package/dist/utils/error-dict.json +18 -4
- package/dist/utils/error-utils.js +121 -0
- package/dist/utils/instance-registry.js +13 -8
- package/dist/utils/log-writer.js +12 -0
- package/dist/utils/process-introspect.js +11 -0
- package/kits/docs/context-assembly.md +0 -1
- package/kits/docs/evolcore/agent-create.md +5 -4
- package/kits/docs/evolcore/agent.md +2 -2
- package/kits/docs/evolcore/config.md +2 -2
- package/kits/docs/evolcore/ctl.md +2 -2
- package/kits/docs/evolcore/ec-capabilities.md +1 -1
- package/kits/docs/evolcore/fs-architecture.md +4 -5
- package/kits/docs/evolcore/fs.md +1 -1
- package/kits/docs/evolcore/group-collaboration.md +2 -2
- package/kits/docs/evolcore/group.md +10 -10
- package/kits/docs/evolcore/memory-architecture.md +4 -5
- package/kits/docs/evolcore/memory-consolidation.md +18 -14
- package/kits/docs/evolcore/memory-tags.md +3 -1
- package/kits/docs/evolcore/memory.md +24 -12
- package/kits/docs/evolcore/model.md +16 -0
- package/kits/docs/evolcore/msg.md +7 -7
- package/kits/docs/evolcore/thread-chain.md +5 -5
- package/kits/docs/prompt-loading-architecture.md +0 -1
- package/kits/eck_manifest.json +0 -20
- package/kits/eck_manifest.memory.json +0 -12
- package/kits/rules/01-overview.md +11 -0
- package/kits/rules/03-identity.md +6 -3
- package/kits/rules/06-channel.md +1 -1
- package/kits/rules/07-memory-and-thread.md +12 -15
- package/kits/schemas/_meta.json +7 -1
- package/kits/schemas/agent-config.schema.13.json +1 -1
- package/kits/schemas/daemon.schema.7.json +140 -0
- package/kits/skills/ec-group/SKILL.md +4 -4
- package/kits/skills/ec-skill/scripts/cli.mjs +1 -1
- package/kits/skills/ec-skill/scripts/skill.mjs +1 -1
- package/kits/templates/memory-consolidation/forget.md +2 -2
- package/kits/templates/memory-consolidation/generalize.md +4 -4
- package/kits/templates/memory-consolidation/notes.md +11 -11
- package/kits/templates/memory-consolidation/profile.md +3 -3
- package/kits/templates/memory-consolidation/promote.md +3 -3
- package/kits/templates/memory-consolidation/workflow.md +6 -6
- package/kits/templates/message-fragments/handoff-request-to-target.md +1 -1
- package/kits/templates/system-fragments/bootstrap.md +4 -3
- package/kits/templates/system-fragments/channel.md +2 -2
- package/kits/templates/system-fragments/memory.md +16 -9
- package/kits/templates/system-fragments/reply.md +6 -6
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +4 -3
- package/dist/core/memory/memory-session-policy.js +0 -41
- package/dist/core/memory/memory-signature.js +0 -257
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,43 @@
|
|
|
5
5
|
|
|
6
6
|
## Unreleased
|
|
7
7
|
|
|
8
|
+
## 0.0.34 (2026-10-18)
|
|
9
|
+
|
|
10
|
+
### 模型与上下文
|
|
11
|
+
|
|
12
|
+
- 统一 Claude Code、Codex 和 ecagent 的模型参数解析与上下文窗口统计,减少执行窗口、压缩阈值和用量展示之间的不一致。
|
|
13
|
+
- Claude Code 和 ecagent 按模型参数表应用上下文窗口与输出上限;Codex 优先使用 app-server 提供的实时窗口,缺失时仅补充统计信息,不虚构执行窗口。
|
|
14
|
+
- 统一模型窗口后缀、模型参数覆盖和查询/压缩路径的处理,支持新模型参数在不修改 runner 代码的情况下生效。
|
|
15
|
+
|
|
16
|
+
### 会话与工具
|
|
17
|
+
|
|
18
|
+
- 完善 Codex 排队任务取消及中断后的状态核验,避免停止操作影响错误轮次。
|
|
19
|
+
- 改进 Shell 工具结果处理,分别保留实际退出码、原始输出和错误摘要,避免将普通输出误当作失败原因。
|
|
20
|
+
- 新增任务授权与会话凭据生命周期诊断,提供可追踪的失效原因和事件信息。
|
|
21
|
+
- 闲置巡检覆盖私聊与群聊,开启前校验必要配置,仅针对未完成且停滞的事项推进或提醒。
|
|
22
|
+
- 补充任务运行结果的结构化错误类型、终止原因和原始诊断信息,区分超时、输出上限、协议不完整及目标会话丢失等终态。
|
|
23
|
+
|
|
24
|
+
### 错误反馈与运行稳定性
|
|
25
|
+
|
|
26
|
+
- 细化 AUN 错误反馈展示,保留原始错误详情并避免将未知 403 错误套用无差别的重试或“API 访问受限”提示。
|
|
27
|
+
- 对客户端过旧、积分不足、团队上下文超限等已知 403 原因提供针对性提示;未知原因保留诊断信息并停止自动猜测。
|
|
28
|
+
- 支持目标会话丢失、触发器超时和输出上限等错误在消息协议中携带稳定的 `subtype` 与 `reason`。
|
|
29
|
+
- 抑制 CLI 子进程的 Node.js 实验性 SQLite 警告,减少正常命令输出噪声。
|
|
30
|
+
- 支持创建 AID 时明确登记主体类型,避免身份类型缺失。
|
|
31
|
+
|
|
32
|
+
### 管理与发布
|
|
33
|
+
|
|
34
|
+
- 支持同时具备目标 Agent Owner 与 Daemon Owner 身份的用户,通过菜单配置 Agent Fullaccess。
|
|
35
|
+
- Trigger 创建支持填写说明,并在列表和详情中展示。
|
|
36
|
+
- 优化 Windows 进程探测与启动、停止速度,修正 Node 路径识别,并统一 Claude 可用性检测与实际执行程序。
|
|
37
|
+
- 统一列表类命令参数的解析,减少 PowerShell 下逗号分隔参数与审批校验不一致的问题。
|
|
38
|
+
- 升级 AUN SDK 至 `0.5.20`,并收紧 npm 包发布内容,避免将内部文件完整性写入模块打入发行包。
|
|
39
|
+
|
|
40
|
+
### 兼容性调整
|
|
41
|
+
|
|
42
|
+
- 首次设定的协作交接统一写入 `persona.md`;`personal.md` 不再自动注入上下文。
|
|
43
|
+
- 移除 `ec memory feedback` 命令,整理反馈改由专用会话按任务要求写入反馈文件。
|
|
44
|
+
|
|
8
45
|
## 0.0.33 (2026-10-15)
|
|
9
46
|
|
|
10
47
|
### 话题与会话
|
package/bin/ec.js
CHANGED
|
@@ -11,6 +11,7 @@ const repoRoot = path.resolve(here, '..');
|
|
|
11
11
|
const srcEntry = path.join(repoRoot, 'src', 'cli', 'index.ts');
|
|
12
12
|
const distEntry = path.join(repoRoot, 'dist', 'cli', 'index.js');
|
|
13
13
|
const args = process.argv.slice(2);
|
|
14
|
+
const nodeOptions = ['--no-warnings=ExperimentalWarning'];
|
|
14
15
|
|
|
15
16
|
// tsx derives its cache directory from `process.geteuid()` on POSIX and
|
|
16
17
|
// `os.userInfo()` on Windows. In managed Windows sessions the latter can
|
|
@@ -26,13 +27,13 @@ if (fs.existsSync(srcEntry)) {
|
|
|
26
27
|
const imports = tsxWindowsCompatImport
|
|
27
28
|
? ['--import', tsxWindowsCompatImport, '--import', tsxImport]
|
|
28
29
|
: ['--import', tsxImport];
|
|
29
|
-
const result = spawnSync(process.execPath, [...imports, srcEntry, ...args], { stdio: 'inherit' });
|
|
30
|
+
const result = spawnSync(process.execPath, [...nodeOptions, ...imports, srcEntry, ...args], { stdio: 'inherit' });
|
|
30
31
|
process.exit(result.status ?? (result.error ? 1 : 0));
|
|
31
32
|
} catch {}
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
if (fs.existsSync(distEntry)) {
|
|
35
|
-
const result = spawnSync(process.execPath, [distEntry, ...args], { stdio: 'inherit' });
|
|
36
|
+
const result = spawnSync(process.execPath, [...nodeOptions, distEntry, ...args], { stdio: 'inherit' });
|
|
36
37
|
process.exit(result.status ?? (result.error ? 1 : 0));
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -285,6 +285,17 @@ export function samePackageRoot(embedded, expected) {
|
|
|
285
285
|
return normalize(embedded) === normalize(expected) || resolved(embedded) === resolved(expected);
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
// Windows accepts an executable path without its `.exe` suffix. Keep an older
|
|
289
|
+
// `...\\node` binding from being treated as a different runtime than
|
|
290
|
+
// `...\\node.exe` during installer verification.
|
|
291
|
+
function sameRuntimePath(embedded, expected) {
|
|
292
|
+
if (samePackageRoot(embedded, expected)) return true;
|
|
293
|
+
if (process.platform !== 'win32' || !embedded) return false;
|
|
294
|
+
const appendExe = value => path.extname(value) ? value : `${value}.exe`;
|
|
295
|
+
return samePackageRoot(appendExe(embedded), expected)
|
|
296
|
+
|| samePackageRoot(embedded, appendExe(expected));
|
|
297
|
+
}
|
|
298
|
+
|
|
288
299
|
function normalizedPath(value) {
|
|
289
300
|
return path.normalize(String(value || '')).replace(/[\\/]+$/, '').toLowerCase();
|
|
290
301
|
}
|
|
@@ -391,7 +402,7 @@ export function inspectManagedHookInstallation({ requirements, managedDir, packa
|
|
|
391
402
|
}
|
|
392
403
|
const { embeddedPackageRoot, managedNodePath } = binding;
|
|
393
404
|
const packageMatches = samePackageRoot(embeddedPackageRoot, path.resolve(packageRoot || process.cwd()));
|
|
394
|
-
const nodeMatches =
|
|
405
|
+
const nodeMatches = sameRuntimePath(managedNodePath, nodePath);
|
|
395
406
|
const state = {
|
|
396
407
|
requirements: canonicalRequirements, managedDir: canonicalManagedDir, hookPath,
|
|
397
408
|
managedPackageRoot: embeddedPackageRoot, managedNodePath,
|
|
@@ -1,39 +1,17 @@
|
|
|
1
1
|
import { normalizeBaseagent } from './baseagent.js';
|
|
2
2
|
import { commandExists } from '../utils/cross-platform.js';
|
|
3
3
|
import { getCodexAppServerAvailability } from './codex-runner.js';
|
|
4
|
-
import {
|
|
5
|
-
import fs from 'fs';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
const _resolveModule = createRequire(import.meta.url);
|
|
4
|
+
import { probeBundledClaudeVersion, resolveBundledClaudeExecutable } from './claude-runner.js';
|
|
8
5
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* Claude 的检测判据就是执行判据:会话只会在 Windows 上跑内置的、与 SDK 锁定版本
|
|
7
|
+
* 配套的 claude.exe。PATH 里的全局 claude 执行层从不使用,因此不能作为可用判据——
|
|
8
|
+
* 那个假阳性正是「检测说可用、会话全失败」持续 21 小时没被发现的成因。
|
|
12
9
|
*/
|
|
13
|
-
function
|
|
14
|
-
if (process.platform
|
|
15
|
-
return
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
: process.arch === 'x64'
|
|
19
|
-
? '@anthropic-ai/claude-agent-sdk-win32-x64'
|
|
20
|
-
: undefined;
|
|
21
|
-
if (!nativePackage)
|
|
22
|
-
return false;
|
|
23
|
-
try {
|
|
24
|
-
const exe = _resolveModule.resolve(`${nativePackage}/claude.exe`);
|
|
25
|
-
if (fs.existsSync(exe))
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
catch { /* fall through */ }
|
|
29
|
-
try {
|
|
30
|
-
const sdkEntry = _resolveModule.resolve('@anthropic-ai/claude-agent-sdk');
|
|
31
|
-
const exe = path.resolve(path.dirname(sdkEntry), '..', '@anthropic-ai', nativePackage.slice('@anthropic-ai/'.length), 'claude.exe');
|
|
32
|
-
return fs.existsSync(exe);
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
10
|
+
function claudeExecutable() {
|
|
11
|
+
if (process.platform === 'win32')
|
|
12
|
+
return resolveBundledClaudeExecutable();
|
|
13
|
+
// 非 Windows 平台由 SDK 自己解析平台包,此处沿用命令探测。
|
|
14
|
+
return commandExists('claude') ? 'claude' : undefined;
|
|
37
15
|
}
|
|
38
16
|
export const BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini', 'ecagent'];
|
|
39
17
|
export const AUTO_BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini'];
|
|
@@ -49,19 +27,25 @@ export function detectAvailableBaseagents(codexAvailability = getCodexAppServerA
|
|
|
49
27
|
if (baseagent === 'codex')
|
|
50
28
|
return codexAvailability.available;
|
|
51
29
|
if (baseagent === 'claude')
|
|
52
|
-
return
|
|
30
|
+
return claudeExecutable() !== undefined;
|
|
53
31
|
return commandExists(baseagent);
|
|
54
32
|
});
|
|
55
33
|
}
|
|
56
34
|
export function detectBaseagentDetails(codexAvailability = getCodexAppServerAvailability()) {
|
|
57
|
-
const claude =
|
|
35
|
+
const claude = claudeExecutable();
|
|
36
|
+
const claudeVersion = claude ? probeBundledClaudeVersion() : undefined;
|
|
58
37
|
const gemini = commandExists('gemini');
|
|
59
38
|
return [
|
|
60
39
|
{
|
|
61
40
|
id: 'claude',
|
|
62
|
-
detected: claude,
|
|
63
|
-
detectionSource: claude ? ['PATH'] : [],
|
|
64
|
-
reason: claude
|
|
41
|
+
detected: claude !== undefined,
|
|
42
|
+
detectionSource: claude ? [process.platform === 'win32' ? 'bundled SDK binary' : 'PATH'] : [],
|
|
43
|
+
reason: claude
|
|
44
|
+
? undefined
|
|
45
|
+
: process.platform === 'win32'
|
|
46
|
+
? '未找到内置 claude.exe(@anthropic-ai/claude-agent-sdk-win32-x64)。请重新安装 SDK 的平台包。'
|
|
47
|
+
: '未检测到 Claude CLI。',
|
|
48
|
+
version: claudeVersion,
|
|
65
49
|
},
|
|
66
50
|
{
|
|
67
51
|
id: 'codex',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getRunnerCatalog, peekRunnerCatalog, catalogEfforts, resolveCatalogModel, assertCatalogSelectable } from '../core/model/model-catalog.js';
|
|
2
2
|
import { query, forkSession as sdkForkSession, getSessionMessages as sdkGetSessionMessages, resolveSettings as sdkResolveSettings, } from '@anthropic-ai/claude-agent-sdk';
|
|
3
|
-
import { spawn } from 'child_process';
|
|
3
|
+
import { spawn, execFileSync } from 'child_process';
|
|
4
4
|
import { createRequire } from 'module';
|
|
5
5
|
import { ensureDir } from '../utils/atomic-write.js';
|
|
6
6
|
import { resolveAnthropicConfig } from './baseagent.js';
|
|
@@ -30,7 +30,8 @@ import { buildClaudeUnixSocketAllowlist } from '../core/permission/unix-socket-p
|
|
|
30
30
|
import { auditToolInfrastructureFailure, auditToolPreflightDenial } from '../core/auth/authorization-audit.js';
|
|
31
31
|
import { getManagedTaskTempDir, isManagedSessionRuntimeDir } from '../cli/task-context.js';
|
|
32
32
|
import { unsettledInterruptError, withInterruptDeadline, hasTrustedFullAccessContext } from './runner-types.js';
|
|
33
|
-
import {
|
|
33
|
+
import { resolveModelSpec } from '../stats/billing.js';
|
|
34
|
+
import { contextTokensForUsage, usageForContext, isClaudeContextUsageModel, realContextWindowForModel, autoCompactWindowForModel, sdkAutoCompactWindowForModel } from './runner-types.js';
|
|
34
35
|
import { hasContextWindowSuffix, stripContextWindowSuffix } from '../core/model/context-window-suffix.js';
|
|
35
36
|
const resolveModule = createRequire(import.meta.url);
|
|
36
37
|
const CLAUDE_INTERRUPT_TIMEOUT_MS = 5_000;
|
|
@@ -148,6 +149,22 @@ export function resolveBundledClaudeExecutable() {
|
|
|
148
149
|
return undefined;
|
|
149
150
|
}
|
|
150
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* 探测内置 claude.exe 的版本。检测层与执行层共用同一份二进制,所以这里探到的
|
|
154
|
+
* 版本就是会话真正会跑的那个版本,可与 SDK 锁定的 claudeCodeVersion 对照。
|
|
155
|
+
*/
|
|
156
|
+
export function probeBundledClaudeVersion() {
|
|
157
|
+
const executable = resolveBundledClaudeExecutable();
|
|
158
|
+
if (!executable)
|
|
159
|
+
return undefined;
|
|
160
|
+
try {
|
|
161
|
+
const raw = execFileSync(executable, ['--version'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
162
|
+
return raw.trim().split(/\s+/)[0] || undefined;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
151
168
|
export { hasCompact, hasModelSwitcher, hasPermissionController } from './runner-types.js';
|
|
152
169
|
// Built-in tools execute inside the Claude runtime and are covered by the
|
|
153
170
|
// runner's filesystem/shell policy. MCP and future unknown tools can perform
|
|
@@ -494,19 +511,28 @@ async function refreshGatewayPricing(baseUrl, apiKey, requestHeaders) {
|
|
|
494
511
|
*
|
|
495
512
|
* 若网关把窗口变体作为独立条目下发(目录里同时有 `claude-opus-5` 与
|
|
496
513
|
* `claude-opus-5[1m]`),裸 ID 就是用户明确选的非 1M 变体,不能擅自升级,
|
|
497
|
-
*
|
|
498
|
-
*
|
|
514
|
+
* 否则两条列表项的实际效果无法区分。只有目录未提供任何窗口变体时,才按已解析的
|
|
515
|
+
* 模型参数(缺失时才回退本地名单)补 `[1m]`。
|
|
499
516
|
*/
|
|
500
|
-
function applyContextWindow(modelId, catalog) {
|
|
517
|
+
function applyContextWindow(modelId, contextWindow, catalog) {
|
|
501
518
|
if (hasContextWindowSuffix(modelId))
|
|
502
519
|
return modelId; // 窗口已显式指定
|
|
503
520
|
const variantListed = catalog?.models.some(m => hasContextWindowSuffix(m.id) && stripContextWindowSuffix(m.id) === modelId);
|
|
504
521
|
if (variantListed)
|
|
505
522
|
return modelId;
|
|
506
|
-
if (
|
|
523
|
+
if (contextWindow === 1_000_000)
|
|
507
524
|
return `${modelId}[1m]`;
|
|
508
525
|
return modelId;
|
|
509
526
|
}
|
|
527
|
+
function resolveClaudeModelContext(modelId, catalog) {
|
|
528
|
+
const modelSpec = resolveModelSpec(resolvePaths().root, modelId, undefined, {
|
|
529
|
+
context_window: realContextWindowForModel(modelId),
|
|
530
|
+
});
|
|
531
|
+
return {
|
|
532
|
+
sdkModel: applyContextWindow(modelId, modelSpec.context_window, catalog),
|
|
533
|
+
contextWindow: modelSpec.context_window,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
510
536
|
class MessageStream {
|
|
511
537
|
queue = [];
|
|
512
538
|
waiting = null;
|
|
@@ -1455,7 +1481,7 @@ export class AgentRunner {
|
|
|
1455
1481
|
* SDK 原始事件 → 标准 AgentEvent 转换
|
|
1456
1482
|
* 所有 SDK 特有的事件类型引用封装在此方法内
|
|
1457
1483
|
*/
|
|
1458
|
-
async *transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, isResuming = false, sandboxSettingsFile, permissionMode, turn, projectPath = process.cwd(), topicBinding = false, recoverySessionId) {
|
|
1484
|
+
async *transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, isResuming = false, sandboxSettingsFile, permissionMode, turn, projectPath = process.cwd(), topicBinding = false, recoverySessionId, resolvedContextWindow) {
|
|
1459
1485
|
let lastSessionId;
|
|
1460
1486
|
let pendingSessionId;
|
|
1461
1487
|
let hasTurnActivity = false;
|
|
@@ -1890,7 +1916,7 @@ export class AgentRunner {
|
|
|
1890
1916
|
const effectiveModel = callModel ?? this.model;
|
|
1891
1917
|
const isClaudeModel = isClaudeContextUsageModel(effectiveModel);
|
|
1892
1918
|
const totalTokens = contextTokensForUsage(u, !!isClaudeModel);
|
|
1893
|
-
const contextWindowTokens = realContextWindowForModel(sdkModel);
|
|
1919
|
+
const contextWindowTokens = resolvedContextWindow ?? realContextWindowForModel(sdkModel);
|
|
1894
1920
|
const autoCompactTokens = autoCompactWindowForModel(sdkModel);
|
|
1895
1921
|
const aggregateContextUsage = totalTokens > 0 ? {
|
|
1896
1922
|
totalTokens,
|
|
@@ -2200,7 +2226,17 @@ export class AgentRunner {
|
|
|
2200
2226
|
if (callPermissionMode === 'fullaccess' && !hasTrustedFullAccessContext(runPermissionContext)) {
|
|
2201
2227
|
throw new Error('Claude fullaccess requires a trusted per-call execution authorization');
|
|
2202
2228
|
}
|
|
2203
|
-
|
|
2229
|
+
// 系统会话明确声明不要沙箱时,直接按「沙箱关闭」处理。SDK 沙箱以项目目录为中心,
|
|
2230
|
+
// 而系统会话的读写边界($EVOLCORE_HOME 下的批次目录与记忆目录)与 cwd 无关,
|
|
2231
|
+
// 启用之后只会把声明内的路径也挡在外面。边界由 systemSessionCheck 逐次判定。
|
|
2232
|
+
const executionSandbox = runPermissionContext?.systemSessionSandbox === 'off'
|
|
2233
|
+
? {
|
|
2234
|
+
permissionMode: normalizeExecutionPermissionMode(callPermissionMode),
|
|
2235
|
+
role: runPermissionContext?.role,
|
|
2236
|
+
state: 'off',
|
|
2237
|
+
reason: 'phase-one-required',
|
|
2238
|
+
}
|
|
2239
|
+
: resolvePhaseOneExecutionSandbox(callPermissionMode, runPermissionContext?.role);
|
|
2204
2240
|
const inputId = modelOverride?.turn?.inputId ?? crypto.randomUUID();
|
|
2205
2241
|
if (executionSandbox.state !== 'off') {
|
|
2206
2242
|
const activeSandboxFailure = getActiveSandboxInitializationFailure(undefined, undefined, sessionId);
|
|
@@ -2355,6 +2391,19 @@ export class AgentRunner {
|
|
|
2355
2391
|
delete toolInput.pages;
|
|
2356
2392
|
}
|
|
2357
2393
|
let updatedInput = toolInput === originalInput ? undefined : toolInput;
|
|
2394
|
+
// 系统会话:模块判定即最终结论,不进入下面任何按角色 / permissionMode 的分支
|
|
2395
|
+
const systemSessionCheck = this.permissionContexts.get(sessionId)?.systemSessionCheck;
|
|
2396
|
+
if (systemSessionCheck) {
|
|
2397
|
+
const decision = systemSessionCheck(toolName, toolInput);
|
|
2398
|
+
if (!decision.allow) {
|
|
2399
|
+
await recordPreflightDenial(toolName, toolInput, undefined, input.tool_use_id ?? input.toolUseID, decision.code);
|
|
2400
|
+
return hookDecision('deny', decision.reason, updatedInput);
|
|
2401
|
+
}
|
|
2402
|
+
// ec 命令与普通托管 ec 命令一样在沙箱外执行(需要连 daemon IPC);只读查询留在沙箱内
|
|
2403
|
+
return hookDecision('allow', undefined, decision.ecCommand
|
|
2404
|
+
? { ...toolInput, dangerouslyDisableSandbox: true }
|
|
2405
|
+
: updatedInput);
|
|
2406
|
+
}
|
|
2358
2407
|
if (callPermissionMode === 'fullaccess') {
|
|
2359
2408
|
if (!hasTrustedFullAccessContext(this.permissionContexts.get(sessionId))) {
|
|
2360
2409
|
return hookDecision('deny', 'fullaccess execution authorization is no longer active', updatedInput);
|
|
@@ -2435,6 +2484,7 @@ export class AgentRunner {
|
|
|
2435
2484
|
const readonlyContext = {
|
|
2436
2485
|
sessionId,
|
|
2437
2486
|
managedTempDir: permCtx?.managedTempDir,
|
|
2487
|
+
grantedReadRoots: permCtx?.grantedReadRoots,
|
|
2438
2488
|
channel: permCtx?.channel,
|
|
2439
2489
|
peerId: permCtx?.userId,
|
|
2440
2490
|
role: permCtx?.role,
|
|
@@ -2498,6 +2548,20 @@ export class AgentRunner {
|
|
|
2498
2548
|
// SDK-level canUseTool 回调:接入 PermissionGateway 的用户审批入口
|
|
2499
2549
|
// 黑名单已在 PreToolUse hook 拦截,危险命令在此处进入审批流程
|
|
2500
2550
|
const canUseToolCallback = async (toolName, input, options) => {
|
|
2551
|
+
// 系统会话:与 PreToolUse 同一判定,SDK 若仍走到这里也不进入交互 / 角色分支
|
|
2552
|
+
const systemSessionCheck = this.permissionContexts.get(sessionId)?.systemSessionCheck;
|
|
2553
|
+
if (systemSessionCheck) {
|
|
2554
|
+
const decision = systemSessionCheck(toolName, input);
|
|
2555
|
+
if (!decision.allow) {
|
|
2556
|
+
await recordPreflightDenial(toolName, input, undefined, options.toolUseID, decision.code);
|
|
2557
|
+
return { behavior: 'deny', message: decision.reason, decisionClassification: 'user_reject' };
|
|
2558
|
+
}
|
|
2559
|
+
return {
|
|
2560
|
+
behavior: 'allow',
|
|
2561
|
+
updatedInput: decision.ecCommand ? { ...input, dangerouslyDisableSandbox: true } : input,
|
|
2562
|
+
decisionClassification: 'user_permanent',
|
|
2563
|
+
};
|
|
2564
|
+
}
|
|
2501
2565
|
// 特殊处理:AskUserQuestion 工具(SDK 内置的用户交互工具)
|
|
2502
2566
|
// 这不是权限审批,而是收集用户答案,需要构造表单卡片
|
|
2503
2567
|
if (toolName === 'AskUserQuestion') {
|
|
@@ -2612,6 +2676,7 @@ export class AgentRunner {
|
|
|
2612
2676
|
const readonlyContext = {
|
|
2613
2677
|
sessionId,
|
|
2614
2678
|
managedTempDir: permCtx?.managedTempDir,
|
|
2679
|
+
grantedReadRoots: permCtx?.grantedReadRoots,
|
|
2615
2680
|
channel: permCtx?.channel,
|
|
2616
2681
|
peerId: permCtx?.userId,
|
|
2617
2682
|
role: permCtx?.role,
|
|
@@ -2700,7 +2765,8 @@ export class AgentRunner {
|
|
|
2700
2765
|
else {
|
|
2701
2766
|
logger.info(`[AgentRunner] systemPromptAppend: none`);
|
|
2702
2767
|
}
|
|
2703
|
-
const
|
|
2768
|
+
const catalogModel = resolveCatalogModel(runCatalog, callModel);
|
|
2769
|
+
const { sdkModel, contextWindow } = resolveClaudeModelContext(catalogModel, runCatalog);
|
|
2704
2770
|
const capabilityOptions = await this.resolveCapabilityRunOptions(projectPath);
|
|
2705
2771
|
const managedSettings = callPermissionMode === 'fullaccess'
|
|
2706
2772
|
? undefined
|
|
@@ -2787,7 +2853,7 @@ export class AgentRunner {
|
|
|
2787
2853
|
...(executionSandbox.state === 'off' && callPermissionMode !== 'fullaccess' && process.platform === 'linux' ? {
|
|
2788
2854
|
spawnClaudeCodeProcess: (options) => spawnClaudeWithHClassGuard(options, resolvePaths().root, handleClaudeStderr, runPermissionContext?.selfAid ?? this.agentAid),
|
|
2789
2855
|
} : {}),
|
|
2790
|
-
autoCompactWindow: sdkAutoCompactWindowForModel(sdkModel),
|
|
2856
|
+
autoCompactWindow: sdkAutoCompactWindowForModel(sdkModel, contextWindow),
|
|
2791
2857
|
advisorModel: 'haiku',
|
|
2792
2858
|
canUseTool: canUseToolCallback,
|
|
2793
2859
|
permissionMode: sdkPermissionMode,
|
|
@@ -3013,7 +3079,7 @@ export class AgentRunner {
|
|
|
3013
3079
|
this.interruptFns.set(sessionId, () => sdkStream.interrupt());
|
|
3014
3080
|
}
|
|
3015
3081
|
// 返回标准 AgentEvent 流(重试由 MessageProcessor 层负责)
|
|
3016
|
-
const transformed = this.transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, !!agentSessionId, sandboxSettingsFile, callPermissionMode, modelOverride?.turn, projectPath, topicBinding, recoverySessionId);
|
|
3082
|
+
const transformed = this.transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, !!agentSessionId, sandboxSettingsFile, callPermissionMode, modelOverride?.turn, projectPath, topicBinding, recoverySessionId, contextWindow);
|
|
3017
3083
|
const self = this;
|
|
3018
3084
|
return (async function* () {
|
|
3019
3085
|
try {
|
|
@@ -3211,11 +3277,14 @@ export class AgentRunner {
|
|
|
3211
3277
|
const catalog = useModel ? await this.getModelCatalog() : undefined;
|
|
3212
3278
|
if (catalog)
|
|
3213
3279
|
assertCatalogSelectable(catalog);
|
|
3280
|
+
const resolvedModel = catalog && callModel
|
|
3281
|
+
? resolveClaudeModelContext(resolveCatalogModel(catalog, callModel), catalog)
|
|
3282
|
+
: undefined;
|
|
3214
3283
|
return query({
|
|
3215
3284
|
prompt,
|
|
3216
3285
|
options: {
|
|
3217
3286
|
cwd: projectPath,
|
|
3218
|
-
...(
|
|
3287
|
+
...(resolvedModel ? { model: resolvedModel.sdkModel } : {}),
|
|
3219
3288
|
resume: agentSessionId,
|
|
3220
3289
|
maxTurns: 1,
|
|
3221
3290
|
tools: [],
|
|
@@ -65,12 +65,6 @@ function decodeProbeOutput(buffer) {
|
|
|
65
65
|
return new TextDecoder('gb18030').decode(buffer);
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
-
function windowsCommandArg(value) {
|
|
69
|
-
return '"' + value.replaceAll('"', '\\"') + '"';
|
|
70
|
-
}
|
|
71
|
-
function expectedWindowsHookCommand(nodePath, hookPath) {
|
|
72
|
-
return windowsCommandArg(nodePath) + ' ' + windowsCommandArg(hookPath) + ' %EVOLCORE_INSTANCE_SOCKET%';
|
|
73
|
-
}
|
|
74
68
|
/**
|
|
75
69
|
* Run synthetic Windows shell probes for the managed Codex hook.
|
|
76
70
|
*
|
|
@@ -104,13 +98,20 @@ export function runCodexHookLaunchDiagnostic(options = {}) {
|
|
|
104
98
|
throw new Error('No Windows managed hook directory configured.');
|
|
105
99
|
const managedDir = path.resolve(options.managedDir || configuredDir);
|
|
106
100
|
const hookPath = path.join(managedDir, 'pre-tool-use.mjs');
|
|
107
|
-
|
|
101
|
+
let expected;
|
|
108
102
|
const handlers = Array.isArray(config.hooks?.PreToolUse)
|
|
109
103
|
? config.hooks.PreToolUse.flatMap((group) => group?.hooks || [])
|
|
110
104
|
: [];
|
|
111
|
-
const handler = handlers.find((item) =>
|
|
112
|
-
|
|
113
|
-
|
|
105
|
+
const handler = handlers.find((item) => {
|
|
106
|
+
if (item?.type !== 'command' || item.async === true)
|
|
107
|
+
return false;
|
|
108
|
+
const command = parseManagedCommand(item.command_windows ?? item.commandWindows, hookPath);
|
|
109
|
+
if (!command || !sameRuntimePath(command.nodePath, nodePath, options.platform ?? process.platform))
|
|
110
|
+
return false;
|
|
111
|
+
expected = item.command_windows ?? item.commandWindows;
|
|
112
|
+
return true;
|
|
113
|
+
});
|
|
114
|
+
if (!handler || !expected || /[\r\n%;&|<>]/.test(nodePath + hookPath)) {
|
|
114
115
|
throw new Error('Managed command differs from the expected EvolCore command for this Node path; no hooks executed.');
|
|
115
116
|
}
|
|
116
117
|
fs.mkdirSync(path.dirname(output), { recursive: true, mode: 0o700 });
|
|
@@ -202,9 +203,34 @@ function samePackageRoot(embedded, expected) {
|
|
|
202
203
|
};
|
|
203
204
|
return normalize(embedded) === normalize(expected) || resolved(embedded) === resolved(expected);
|
|
204
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Windows accepts an executable path without its `.exe` suffix. Older
|
|
208
|
+
* installations may therefore bind `...\\node` while the current Node
|
|
209
|
+
* process reports `...\\node.exe`; these are the same runtime and must not
|
|
210
|
+
* trigger an elevated repair on every startup.
|
|
211
|
+
*/
|
|
212
|
+
function sameRuntimePath(embedded, expected, platform) {
|
|
213
|
+
if (samePackageRoot(embedded, expected))
|
|
214
|
+
return true;
|
|
215
|
+
if (platform !== 'win32' || !embedded)
|
|
216
|
+
return false;
|
|
217
|
+
const appendExe = (value) => path.extname(value) ? value : `${value}.exe`;
|
|
218
|
+
return samePackageRoot(appendExe(embedded), expected)
|
|
219
|
+
|| samePackageRoot(embedded, appendExe(expected));
|
|
220
|
+
}
|
|
205
221
|
function normalizedPath(value) {
|
|
206
222
|
return path.normalize(String(value || '')).replace(/[\\/]+$/, '').toLowerCase();
|
|
207
223
|
}
|
|
224
|
+
function parseManagedCommand(value, hookPath) {
|
|
225
|
+
if (typeof value !== 'string')
|
|
226
|
+
return null;
|
|
227
|
+
const match = /^"([^"\r\n]+)"\s+"([^"\r\n]+)"\s+%EVOLCORE_INSTANCE_SOCKET%$/i.exec(value.trim());
|
|
228
|
+
if (!match || !path.isAbsolute(match[1]) || /[%!]/.test(match[1] + match[2]))
|
|
229
|
+
return null;
|
|
230
|
+
if (normalizedPath(match[2]) !== normalizedPath(hookPath))
|
|
231
|
+
return null;
|
|
232
|
+
return { nodePath: match[1] };
|
|
233
|
+
}
|
|
208
234
|
function runtimeFileMayExist(file) {
|
|
209
235
|
try {
|
|
210
236
|
return fs.statSync(file).isFile();
|
|
@@ -297,7 +323,7 @@ export function inspectCodexManagedHook(options = {}) {
|
|
|
297
323
|
}
|
|
298
324
|
const { embeddedPackageRoot, managedNodePath } = binding;
|
|
299
325
|
const packageMatches = samePackageRoot(embeddedPackageRoot, expectedPackageRoot);
|
|
300
|
-
const nodeMatches =
|
|
326
|
+
const nodeMatches = sameRuntimePath(managedNodePath, options.nodePath || process.execPath, options.platform ?? process.platform);
|
|
301
327
|
const repairFingerprint = createHash('sha256').update(source).update('\0').update(launcher).digest('hex');
|
|
302
328
|
if (packageMatches && nodeMatches)
|
|
303
329
|
return { status: 'installed', requirements, managedDir, hookPath };
|