evolcore 0.0.33 → 0.0.35-test.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/bin/ec.js +3 -2
  3. package/bin/install-codex-managed-hooks.mjs +38 -4
  4. package/dist/agents/baseagent-availability.js +20 -36
  5. package/dist/agents/claude-runner.js +93 -14
  6. package/dist/agents/codex/diagnostics.js +79 -22
  7. package/dist/agents/codex-runner.js +245 -47
  8. package/dist/agents/ecagent-runner.js +90 -27
  9. package/dist/agents/runner-types.js +5 -6
  10. package/dist/aun/aid/agentmd.js +13 -2
  11. package/dist/aun/aid/client.js +1 -1
  12. package/dist/aun/aid/identity.js +1 -1
  13. package/dist/aun/msg/managed-operation.js +16 -0
  14. package/dist/aun/msg/p2p.js +18 -2
  15. package/dist/channels/aun.js +123 -7
  16. package/dist/channels/daemon.js +6 -3
  17. package/dist/cli/aun-commands.js +39 -14
  18. package/dist/cli/ctl-command.js +3 -1
  19. package/dist/cli/daemon-commands.js +130 -23
  20. package/dist/cli/fs-command.js +2 -1
  21. package/dist/cli/getmeta-command.js +211 -0
  22. package/dist/cli/index.js +26 -6
  23. package/dist/cli/integrity-command.js +196 -0
  24. package/dist/cli/managed-command-guard.js +3 -0
  25. package/dist/cli/memory-command.js +200 -156
  26. package/dist/cli/thread-command.js +17 -7
  27. package/dist/cli/trigger-command.js +6 -1
  28. package/dist/config/config-field-policy.js +4 -0
  29. package/dist/config/config-manager.js +19 -0
  30. package/dist/config/contact-book-store.js +5 -0
  31. package/dist/config/resolved-config-op.js +25 -8
  32. package/dist/config/role-service.js +13 -0
  33. package/dist/config/runtime-env.js +16 -0
  34. package/dist/config-store.js +30 -0
  35. package/dist/core/audit/log-integrity.js +3 -1
  36. package/dist/core/auth/agent-delegation.js +108 -35
  37. package/dist/core/auth/delegation-diagnostics.js +414 -0
  38. package/dist/core/auth/operation-authorizer.js +22 -3
  39. package/dist/core/auth/operation-catalog.js +16 -0
  40. package/dist/core/channel-runtime.js +1 -1
  41. package/dist/core/command/agent-control.js +18 -26
  42. package/dist/core/command/command-handler.js +145 -5
  43. package/dist/core/command/daemon-diagnostics.js +1 -0
  44. package/dist/core/command/menu-catalog.js +3 -0
  45. package/dist/core/command/menu-handler.js +24 -7
  46. package/dist/core/command/menu-protocol.js +1 -0
  47. package/dist/core/command/slash-handler.js +2 -0
  48. package/dist/core/command/topic-slash.js +4 -7
  49. package/dist/core/daemon-bootstrap.js +4 -7
  50. package/dist/core/event-catalog.js +14 -0
  51. package/dist/core/file-integrity/read.js +61 -0
  52. package/dist/core/file-integrity/store.js +262 -0
  53. package/dist/core/ipc-registration.js +47 -2
  54. package/dist/core/memory/batch-meta.js +133 -0
  55. package/dist/core/memory/consolidation-context.js +132 -28
  56. package/dist/core/memory/consolidation-ops-records.js +14 -16
  57. package/dist/core/memory/consolidation-trigger.js +15 -7
  58. package/dist/core/memory/consolidation.js +146 -26
  59. package/dist/core/memory/index-manager.js +19 -28
  60. package/dist/core/memory/memory-admin.js +19 -14
  61. package/dist/core/memory/memory-document.js +53 -9
  62. package/dist/core/memory/memory-writer.js +50 -19
  63. package/dist/core/message/aun-ipc-sender.js +19 -1
  64. package/dist/core/message/im-renderer.js +20 -4
  65. package/dist/core/message/message-bridge.js +5 -4
  66. package/dist/core/message/message-log.js +1 -0
  67. package/dist/core/message/peer-mode.js +17 -0
  68. package/dist/core/message/response-engine.js +598 -50
  69. package/dist/core/permission/ec-command-parser.js +169 -44
  70. package/dist/core/permission/protected-paths.js +5 -1
  71. package/dist/core/permission/readonly-shell-query.js +43 -5
  72. package/dist/core/permission/session-path-grants.js +334 -0
  73. package/dist/core/permission/tool-canonical.js +226 -0
  74. package/dist/core/permission/tool-policy.js +60 -62
  75. package/dist/core/relation/peer-identity.js +87 -12
  76. package/dist/core/role/runtime-policy.js +21 -10
  77. package/dist/core/session/session-fs-store.js +123 -8
  78. package/dist/core/session/session-manager.js +131 -6
  79. package/dist/core/session/session-title.js +26 -1
  80. package/dist/core/skill/index-generator.js +17 -3
  81. package/dist/core/system-session/grants.js +40 -0
  82. package/dist/core/system-session/memory.js +289 -0
  83. package/dist/core/system-session/registry.js +74 -0
  84. package/dist/core/system-session/types.js +14 -0
  85. package/dist/core/thread/command-parser.js +6 -1
  86. package/dist/core/thread/ipc-operation.js +33 -16
  87. package/dist/core/thread/thread-operation.js +62 -24
  88. package/dist/core/thread/thread-service.js +258 -51
  89. package/dist/eck/kit-renderer.js +7 -2
  90. package/dist/eck/manifest-engine.js +24 -4
  91. package/dist/index.js +11 -2
  92. package/dist/ipc.js +51 -40
  93. package/dist/paths.js +70 -0
  94. package/dist/stats/billing.js +20 -8
  95. package/dist/trigger/feedback.js +6 -1
  96. package/dist/trigger/parser.js +8 -1
  97. package/dist/trigger/system-definitions.js +3 -3
  98. package/dist/utils/cross-platform.js +17 -5
  99. package/dist/utils/error-dict.json +18 -4
  100. package/dist/utils/error-utils.js +121 -0
  101. package/dist/utils/instance-registry.js +13 -8
  102. package/dist/utils/log-writer.js +12 -0
  103. package/dist/utils/process-introspect.js +11 -0
  104. package/kits/docs/context-assembly.md +0 -1
  105. package/kits/docs/evolcore/agent-create.md +5 -4
  106. package/kits/docs/evolcore/agent.md +2 -2
  107. package/kits/docs/evolcore/config.md +2 -2
  108. package/kits/docs/evolcore/ctl.md +2 -2
  109. package/kits/docs/evolcore/ec-capabilities.md +1 -1
  110. package/kits/docs/evolcore/fs-architecture.md +9 -10
  111. package/kits/docs/evolcore/fs.md +2 -2
  112. package/kits/docs/evolcore/group-collaboration.md +3 -3
  113. package/kits/docs/evolcore/group.md +14 -12
  114. package/kits/docs/evolcore/memory-architecture.md +4 -5
  115. package/kits/docs/evolcore/memory-consolidation.md +19 -16
  116. package/kits/docs/evolcore/memory-tags.md +3 -1
  117. package/kits/docs/evolcore/memory.md +24 -12
  118. package/kits/docs/evolcore/model.md +16 -0
  119. package/kits/docs/evolcore/msg.md +7 -7
  120. package/kits/docs/evolcore/stats.md +1 -1
  121. package/kits/docs/evolcore/thread-chain.md +30 -14
  122. package/kits/docs/prompt-loading-architecture.md +0 -1
  123. package/kits/eck_manifest.auxiliary.json +9 -0
  124. package/kits/eck_manifest.json +20 -20
  125. package/kits/eck_manifest.memory.json +20 -12
  126. package/kits/rules/01-overview.md +11 -0
  127. package/kits/rules/03-identity.md +6 -3
  128. package/kits/rules/06-channel.md +1 -1
  129. package/kits/rules/07-memory-and-thread.md +13 -16
  130. package/kits/schemas/_meta.json +7 -1
  131. package/kits/schemas/agent-config.schema.13.json +1 -1
  132. package/kits/schemas/daemon.schema.7.json +140 -0
  133. package/kits/skills/ec-group/SKILL.md +8 -5
  134. package/kits/skills/ec-skill/scripts/cli.mjs +1 -1
  135. package/kits/skills/ec-skill/scripts/skill.mjs +1 -1
  136. package/kits/skills/thread-ops/SKILL.md +18 -18
  137. package/kits/templates/memory-consolidation/forget.md +2 -2
  138. package/kits/templates/memory-consolidation/generalize.md +4 -4
  139. package/kits/templates/memory-consolidation/notes.md +12 -12
  140. package/kits/templates/memory-consolidation/profile.md +3 -3
  141. package/kits/templates/memory-consolidation/promote.md +3 -3
  142. package/kits/templates/memory-consolidation/workflow.md +42 -7
  143. package/kits/templates/message-fragments/handoff-request-to-target.md +1 -1
  144. package/kits/templates/system-fragments/bash-description.md +9 -0
  145. package/kits/templates/system-fragments/bootstrap.md +4 -3
  146. package/kits/templates/system-fragments/channel.md +2 -2
  147. package/kits/templates/system-fragments/memory.md +16 -9
  148. package/kits/templates/system-fragments/reply.md +6 -6
  149. package/kits/templates/system-fragments/session.md +1 -1
  150. package/package.json +4 -3
  151. package/dist/core/memory/memory-session-policy.js +0 -41
  152. package/dist/core/memory/memory-signature.js +0 -257
package/CHANGELOG.md CHANGED
@@ -5,6 +5,65 @@
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 0.0.35 (2026-10-19)
9
+
10
+ ### 权限与记忆
11
+
12
+ - 规范 Claude Code 与 ecagent 的 Bash 调用说明,要求明确命令目标并标注低、中、高风险等级;缺失或格式错误时提供具体提示,避免权限模式不同导致调用说明不一致。
13
+ - agent 级记忆整理会扫描所有关系记忆并按关系归类,避免遗漏其他对端的过期记忆。
14
+ - 首次设定尚未完成时阻止记忆整理会话启动,避免记忆被提前认领后无法正常处理。
15
+ - 细化只读 Shell 操作的拒绝原因,提升受限命令的诊断可读性。
16
+
17
+ ### Codex 与运行稳定性
18
+
19
+ - 保留 Codex 模型服务重连进度,并在最终失败时展示实际重试次数,避免只显示笼统的请求失败。
20
+ - 统一 Codex managed hook 的包根目录与安装器解析,升级监视器暂存运行文件时同步携带 `bin` 目录,提升升级和重启过程的一致性。
21
+ - 补充生命周期启动失败时的 stderr 诊断输出,便于定位启动异常。
22
+ - 修复 `agent.md` 显示名解析,改用 YAML 结构化解析以正确处理引号和特殊字符。
23
+ - 修复话题摘要写入时的游标竞态,确保 `coveredThroughTs` 在追加摘要前读取。
24
+
25
+ ### 消息与依赖兼容
26
+
27
+ - 规范 AUN 原始错误展示,截断错误响应中附带的 HTML 页面,保留有用的错误信息并减少无关输出。
28
+ - 将 AUN SDK 最低运行版本提升至 `0.5.20`,使用经过当前版本验证的 SDK 依赖。
29
+
30
+ ## 0.0.34 (2026-10-18)
31
+
32
+ ### 模型与上下文
33
+
34
+ - 统一 Claude Code、Codex 和 ecagent 的模型参数解析与上下文窗口统计,减少执行窗口、压缩阈值和用量展示之间的不一致。
35
+ - Claude Code 和 ecagent 按模型参数表应用上下文窗口与输出上限;Codex 优先使用 app-server 提供的实时窗口,缺失时仅补充统计信息,不虚构执行窗口。
36
+ - 统一模型窗口后缀、模型参数覆盖和查询/压缩路径的处理,支持新模型参数在不修改 runner 代码的情况下生效。
37
+
38
+ ### 会话与工具
39
+
40
+ - 完善 Codex 排队任务取消及中断后的状态核验,避免停止操作影响错误轮次。
41
+ - 改进 Shell 工具结果处理,分别保留实际退出码、原始输出和错误摘要,避免将普通输出误当作失败原因。
42
+ - 新增任务授权与会话凭据生命周期诊断,提供可追踪的失效原因和事件信息。
43
+ - 闲置巡检覆盖私聊与群聊,开启前校验必要配置,仅针对未完成且停滞的事项推进或提醒。
44
+ - 补充任务运行结果的结构化错误类型、终止原因和原始诊断信息,区分超时、输出上限、协议不完整及目标会话丢失等终态。
45
+
46
+ ### 错误反馈与运行稳定性
47
+
48
+ - 细化 AUN 错误反馈展示,保留原始错误详情并避免将未知 403 错误套用无差别的重试或“API 访问受限”提示。
49
+ - 对客户端过旧、积分不足、团队上下文超限等已知 403 原因提供针对性提示;未知原因保留诊断信息并停止自动猜测。
50
+ - 支持目标会话丢失、触发器超时和输出上限等错误在消息协议中携带稳定的 `subtype` 与 `reason`。
51
+ - 抑制 CLI 子进程的 Node.js 实验性 SQLite 警告,减少正常命令输出噪声。
52
+ - 支持创建 AID 时明确登记主体类型,避免身份类型缺失。
53
+
54
+ ### 管理与发布
55
+
56
+ - 支持同时具备目标 Agent Owner 与 Daemon Owner 身份的用户,通过菜单配置 Agent Fullaccess。
57
+ - Trigger 创建支持填写说明,并在列表和详情中展示。
58
+ - 优化 Windows 进程探测与启动、停止速度,修正 Node 路径识别,并统一 Claude 可用性检测与实际执行程序。
59
+ - 统一列表类命令参数的解析,减少 PowerShell 下逗号分隔参数与审批校验不一致的问题。
60
+ - 升级 AUN SDK 至 `0.5.20`,并收紧 npm 包发布内容,避免将内部文件完整性写入模块打入发行包。
61
+
62
+ ### 兼容性调整
63
+
64
+ - 首次设定的协作交接统一写入 `persona.md`;`personal.md` 不再自动注入上下文。
65
+ - 移除 `ec memory feedback` 命令,整理反馈改由专用会话按任务要求写入反馈文件。
66
+
8
67
  ## 0.0.33 (2026-10-15)
9
68
 
10
69
  ### 话题与会话
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
 
@@ -22,7 +22,25 @@ function hasArgument(name) {
22
22
  return process.argv.includes(name);
23
23
  }
24
24
 
25
- function fail(message) {
25
+ /** Path where the elevated installer writes a structured failure record for the non-elevated parent. */
26
+ function installerFailurePath(requirements) {
27
+ const configDir = requirements
28
+ ? path.dirname(path.resolve(requirements))
29
+ : path.join(process.env.ProgramData || path.join(os.homedir(), 'ProgramData'), 'OpenAI', 'Codex');
30
+ return path.join(configDir, '.evolcore-install-failure.json');
31
+ }
32
+
33
+ function writeFailureRecord(requirements, message) {
34
+ try {
35
+ const record = { time: new Date().toISOString(), message };
36
+ fs.writeFileSync(installerFailurePath(requirements), JSON.stringify(record) + '\n', { mode: 0o644 });
37
+ } catch {
38
+ // Best-effort; parent falls back to "installer exited with code 1" if missing.
39
+ }
40
+ }
41
+
42
+ function fail(message, requirements) {
43
+ writeFailureRecord(requirements, message);
26
44
  process.stderr.write(`[evolcore] ${message}\n`);
27
45
  process.exit(1);
28
46
  }
@@ -285,6 +303,17 @@ export function samePackageRoot(embedded, expected) {
285
303
  return normalize(embedded) === normalize(expected) || resolved(embedded) === resolved(expected);
286
304
  }
287
305
 
306
+ // Windows accepts an executable path without its `.exe` suffix. Keep an older
307
+ // `...\\node` binding from being treated as a different runtime than
308
+ // `...\\node.exe` during installer verification.
309
+ function sameRuntimePath(embedded, expected) {
310
+ if (samePackageRoot(embedded, expected)) return true;
311
+ if (process.platform !== 'win32' || !embedded) return false;
312
+ const appendExe = value => path.extname(value) ? value : `${value}.exe`;
313
+ return samePackageRoot(appendExe(embedded), expected)
314
+ || samePackageRoot(embedded, appendExe(expected));
315
+ }
316
+
288
317
  function normalizedPath(value) {
289
318
  return path.normalize(String(value || '')).replace(/[\\/]+$/, '').toLowerCase();
290
319
  }
@@ -391,7 +420,7 @@ export function inspectManagedHookInstallation({ requirements, managedDir, packa
391
420
  }
392
421
  const { embeddedPackageRoot, managedNodePath } = binding;
393
422
  const packageMatches = samePackageRoot(embeddedPackageRoot, path.resolve(packageRoot || process.cwd()));
394
- const nodeMatches = samePackageRoot(managedNodePath, nodePath);
423
+ const nodeMatches = sameRuntimePath(managedNodePath, nodePath);
395
424
  const state = {
396
425
  requirements: canonicalRequirements, managedDir: canonicalManagedDir, hookPath,
397
426
  managedPackageRoot: embeddedPackageRoot, managedNodePath,
@@ -642,7 +671,7 @@ function main() {
642
671
  const hookPath = path.join(targetDirectory, 'pre-tool-use.mjs');
643
672
  const sourceHook = path.join(packageRoot, 'bin', 'codex-managed-hook.mjs');
644
673
  for (const file of [sourceHook, nodePath]) {
645
- if (!fs.statSync(file).isFile()) fail(`Required Hook runtime file was not found: ${file}`);
674
+ if (!fs.statSync(file).isFile()) fail(`Required Hook runtime file was not found: ${file}`, requirements);
646
675
  }
647
676
  if (hasArgument('--repair')) {
648
677
  const result = repairManagedHookInstallation({
@@ -663,7 +692,12 @@ function main() {
663
692
  }
664
693
 
665
694
  if (process.argv[1] && pathToFileURL(fs.realpathSync(process.argv[1])).href === import.meta.url) {
695
+ // Resolve requirements now so writeFailureRecord has a path even if main()
696
+ // throws before the local `requirements` variable is assigned.
697
+ const earlyRequirements = argument('--requirements')
698
+ ? path.resolve(argument('--requirements'))
699
+ : path.join(process.env.ProgramData || path.join(os.homedir(), 'ProgramData'), 'OpenAI', 'Codex', 'requirements.toml');
666
700
  try { main(); } catch (error) {
667
- fail(error instanceof Error ? error.message : String(error));
701
+ fail(error instanceof Error ? error.message : String(error), earlyRequirements);
668
702
  }
669
703
  }
@@ -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 { createRequire } from 'module';
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
- * Check whether the bundled Claude SDK native binary is present on disk.
10
- * This covers Windows installations where the npm global shim may be absent
11
- * but the SDK ships claude.exe alongside evolcore's own node_modules.
6
+ * Claude 的检测判据就是执行判据:会话只会在 Windows 上跑内置的、与 SDK 锁定版本
7
+ * 配套的 claude.exe。PATH 里的全局 claude 执行层从不使用,因此不能作为可用判据——
8
+ * 那个假阳性正是「检测说可用、会话全失败」持续 21 小时没被发现的成因。
12
9
  */
13
- function isBundledClaudeAvailable() {
14
- if (process.platform !== 'win32')
15
- return false;
16
- const nativePackage = process.arch === 'arm64'
17
- ? '@anthropic-ai/claude-agent-sdk-win32-arm64'
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 commandExists(baseagent) || isBundledClaudeAvailable();
30
+ return claudeExecutable() !== undefined;
53
31
  return commandExists(baseagent);
54
32
  });
55
33
  }
56
34
  export function detectBaseagentDetails(codexAvailability = getCodexAppServerAvailability()) {
57
- const claude = commandExists('claude') || isBundledClaudeAvailable();
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 ? undefined : '未检测到 Claude CLI。',
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';
@@ -15,7 +15,7 @@ import crypto from 'crypto';
15
15
  import { logger } from '../utils/logger.js';
16
16
  import { createClaudeStartupArgumentsTooLongError, isClaudeStartupArgumentsTooLong, createClaudeCodeStartupError, isClaudeCodeStartupProcessError, } from '../utils/error-utils.js';
17
17
  import { requestDangerousCommandPermission } from '../core/permission/approval-gateway.js';
18
- import { checkDangerousCommand, checkReadonly, evaluateToolPreflight } from '../core/permission/tool-policy.js';
18
+ import { checkBashDescription, checkDangerousCommand, checkReadonly, evaluateToolPreflight } from '../core/permission/tool-policy.js';
19
19
  import { sanitizeShellExecutionEnvironment } from '../core/permission/shell-environment.js';
20
20
  import { summarizeToolInput, summarizeToolInputForAudit } from '../utils/tool-summary.js';
21
21
  import { encodePath } from '../utils/cross-platform.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 { contextTokensForUsage, usageForContext, isClaudeContextUsageModel, isOneMillionContextModel, realContextWindowForModel, autoCompactWindowForModel, sdkAutoCompactWindowForModel } from './runner-types.js';
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
- * 按本地名单补 `[1m]` 的旧行为。
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 (isOneMillionContextModel(modelId))
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
- const executionSandbox = resolvePhaseOneExecutionSandbox(callPermissionMode, runPermissionContext?.role);
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,29 @@ export class AgentRunner {
2355
2391
  delete toolInput.pages;
2356
2392
  }
2357
2393
  let updatedInput = toolInput === originalInput ? undefined : toolInput;
2394
+ // Bash description 格式约定先于一切放行分支(含系统会话与 fullaccess)。
2395
+ // TODO(bash-description): 该约束缺少验证——实测模型被拒后不会补 description 重试,
2396
+ // 话题链等链路会被整条堵死。在补齐测试与重试引导前先短路放行,恢复时删除本开关。
2397
+ if (process.env.EVOLCORE_BASH_DESCRIPTION_ENFORCE === '1') {
2398
+ const descriptionCheck = checkBashDescription(toolName, toolInput);
2399
+ if (descriptionCheck) {
2400
+ await recordPreflightDenial(toolName, toolInput, descriptionCheck, input.tool_use_id ?? input.toolUseID);
2401
+ return hookDecision('deny', descriptionCheck.message, updatedInput);
2402
+ }
2403
+ }
2404
+ // 系统会话:模块判定即最终结论,不进入下面任何按角色 / permissionMode 的分支
2405
+ const systemSessionCheck = this.permissionContexts.get(sessionId)?.systemSessionCheck;
2406
+ if (systemSessionCheck) {
2407
+ const decision = systemSessionCheck(toolName, toolInput);
2408
+ if (!decision.allow) {
2409
+ await recordPreflightDenial(toolName, toolInput, undefined, input.tool_use_id ?? input.toolUseID, decision.code);
2410
+ return hookDecision('deny', decision.reason, updatedInput);
2411
+ }
2412
+ // ec 命令与普通托管 ec 命令一样在沙箱外执行(需要连 daemon IPC);只读查询留在沙箱内
2413
+ return hookDecision('allow', undefined, decision.ecCommand
2414
+ ? { ...toolInput, dangerouslyDisableSandbox: true }
2415
+ : updatedInput);
2416
+ }
2358
2417
  if (callPermissionMode === 'fullaccess') {
2359
2418
  if (!hasTrustedFullAccessContext(this.permissionContexts.get(sessionId))) {
2360
2419
  return hookDecision('deny', 'fullaccess execution authorization is no longer active', updatedInput);
@@ -2435,6 +2494,7 @@ export class AgentRunner {
2435
2494
  const readonlyContext = {
2436
2495
  sessionId,
2437
2496
  managedTempDir: permCtx?.managedTempDir,
2497
+ grantedReadRoots: permCtx?.grantedReadRoots,
2438
2498
  channel: permCtx?.channel,
2439
2499
  peerId: permCtx?.userId,
2440
2500
  role: permCtx?.role,
@@ -2498,6 +2558,20 @@ export class AgentRunner {
2498
2558
  // SDK-level canUseTool 回调:接入 PermissionGateway 的用户审批入口
2499
2559
  // 黑名单已在 PreToolUse hook 拦截,危险命令在此处进入审批流程
2500
2560
  const canUseToolCallback = async (toolName, input, options) => {
2561
+ // 系统会话:与 PreToolUse 同一判定,SDK 若仍走到这里也不进入交互 / 角色分支
2562
+ const systemSessionCheck = this.permissionContexts.get(sessionId)?.systemSessionCheck;
2563
+ if (systemSessionCheck) {
2564
+ const decision = systemSessionCheck(toolName, input);
2565
+ if (!decision.allow) {
2566
+ await recordPreflightDenial(toolName, input, undefined, options.toolUseID, decision.code);
2567
+ return { behavior: 'deny', message: decision.reason, decisionClassification: 'user_reject' };
2568
+ }
2569
+ return {
2570
+ behavior: 'allow',
2571
+ updatedInput: decision.ecCommand ? { ...input, dangerouslyDisableSandbox: true } : input,
2572
+ decisionClassification: 'user_permanent',
2573
+ };
2574
+ }
2501
2575
  // 特殊处理:AskUserQuestion 工具(SDK 内置的用户交互工具)
2502
2576
  // 这不是权限审批,而是收集用户答案,需要构造表单卡片
2503
2577
  if (toolName === 'AskUserQuestion') {
@@ -2612,6 +2686,7 @@ export class AgentRunner {
2612
2686
  const readonlyContext = {
2613
2687
  sessionId,
2614
2688
  managedTempDir: permCtx?.managedTempDir,
2689
+ grantedReadRoots: permCtx?.grantedReadRoots,
2615
2690
  channel: permCtx?.channel,
2616
2691
  peerId: permCtx?.userId,
2617
2692
  role: permCtx?.role,
@@ -2700,7 +2775,8 @@ export class AgentRunner {
2700
2775
  else {
2701
2776
  logger.info(`[AgentRunner] systemPromptAppend: none`);
2702
2777
  }
2703
- const sdkModel = applyContextWindow(resolveCatalogModel(runCatalog, callModel), runCatalog);
2778
+ const catalogModel = resolveCatalogModel(runCatalog, callModel);
2779
+ const { sdkModel, contextWindow } = resolveClaudeModelContext(catalogModel, runCatalog);
2704
2780
  const capabilityOptions = await this.resolveCapabilityRunOptions(projectPath);
2705
2781
  const managedSettings = callPermissionMode === 'fullaccess'
2706
2782
  ? undefined
@@ -2787,7 +2863,7 @@ export class AgentRunner {
2787
2863
  ...(executionSandbox.state === 'off' && callPermissionMode !== 'fullaccess' && process.platform === 'linux' ? {
2788
2864
  spawnClaudeCodeProcess: (options) => spawnClaudeWithHClassGuard(options, resolvePaths().root, handleClaudeStderr, runPermissionContext?.selfAid ?? this.agentAid),
2789
2865
  } : {}),
2790
- autoCompactWindow: sdkAutoCompactWindowForModel(sdkModel),
2866
+ autoCompactWindow: sdkAutoCompactWindowForModel(sdkModel, contextWindow),
2791
2867
  advisorModel: 'haiku',
2792
2868
  canUseTool: canUseToolCallback,
2793
2869
  permissionMode: sdkPermissionMode,
@@ -3013,7 +3089,7 @@ export class AgentRunner {
3013
3089
  this.interruptFns.set(sessionId, () => sdkStream.interrupt());
3014
3090
  }
3015
3091
  // 返回标准 AgentEvent 流(重试由 MessageProcessor 层负责)
3016
- const transformed = this.transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, !!agentSessionId, sandboxSettingsFile, callPermissionMode, modelOverride?.turn, projectPath, topicBinding, recoverySessionId);
3092
+ const transformed = this.transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, !!agentSessionId, sandboxSettingsFile, callPermissionMode, modelOverride?.turn, projectPath, topicBinding, recoverySessionId, contextWindow);
3017
3093
  const self = this;
3018
3094
  return (async function* () {
3019
3095
  try {
@@ -3211,11 +3287,14 @@ export class AgentRunner {
3211
3287
  const catalog = useModel ? await this.getModelCatalog() : undefined;
3212
3288
  if (catalog)
3213
3289
  assertCatalogSelectable(catalog);
3290
+ const resolvedModel = catalog && callModel
3291
+ ? resolveClaudeModelContext(resolveCatalogModel(catalog, callModel), catalog)
3292
+ : undefined;
3214
3293
  return query({
3215
3294
  prompt,
3216
3295
  options: {
3217
3296
  cwd: projectPath,
3218
- ...(catalog && callModel ? { model: applyContextWindow(resolveCatalogModel(catalog, callModel), catalog) } : {}),
3297
+ ...(resolvedModel ? { model: resolvedModel.sdkModel } : {}),
3219
3298
  resume: agentSessionId,
3220
3299
  maxTurns: 1,
3221
3300
  tools: [],