evolcore 0.0.35 → 0.0.37

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 (224) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +6 -0
  3. package/bin/install-codex-managed-hooks.mjs +20 -3
  4. package/dist/agents/baseagent-availability.js +1 -1
  5. package/dist/agents/baseagent.js +88 -0
  6. package/dist/agents/claude-runner.js +59 -12
  7. package/dist/agents/codex/app-server-client.js +18 -1
  8. package/dist/agents/codex-runner.js +262 -21
  9. package/dist/agents/ecagent-runner.js +47 -11
  10. package/dist/agents/gemini-runner.js +36 -12
  11. package/dist/aun/aid/client.js +4 -3
  12. package/dist/aun/aid/control-aid.js +270 -8
  13. package/dist/aun/aid/identity.js +6 -3
  14. package/dist/aun/aid/ownership-operation.js +58 -0
  15. package/dist/aun/aid/store.js +1 -0
  16. package/dist/aun/group/fs-download.js +67 -2
  17. package/dist/aun/group/history.js +20 -5
  18. package/dist/aun/group/operations.js +8 -2
  19. package/dist/aun/menu/client.js +39 -2
  20. package/dist/aun/menu/transport.js +1 -1
  21. package/dist/aun/msg/history.js +43 -4
  22. package/dist/aun/msg/managed-operation.js +30 -53
  23. package/dist/aun/msg/p2p.js +51 -15
  24. package/dist/aun/outbox.js +4 -0
  25. package/dist/channels/aun.js +260 -73
  26. package/dist/channels/daemon.js +6 -4
  27. package/dist/channels/feishu.js +3 -0
  28. package/dist/cli/agent-command.js +161 -223
  29. package/dist/cli/agent-create.js +190 -223
  30. package/dist/cli/agent.js +3 -1
  31. package/dist/cli/aid-bind-command.js +53 -0
  32. package/dist/cli/aun-commands.js +384 -91
  33. package/dist/cli/bench.js +1 -1
  34. package/dist/cli/cli-argv.js +74 -0
  35. package/dist/cli/daemon-commands.js +105 -74
  36. package/dist/cli/diag.js +1 -1
  37. package/dist/cli/fs-command.js +18 -19
  38. package/dist/cli/getmeta-command.js +159 -6
  39. package/dist/cli/index.js +6 -12
  40. package/dist/cli/init-channel.js +8 -55
  41. package/dist/cli/init.js +125 -218
  42. package/dist/cli/queue-command.js +11 -8
  43. package/dist/cli/restart-monitor.js +177 -85
  44. package/dist/cli/session-command.js +1 -1
  45. package/dist/cli/stats.js +7 -5
  46. package/dist/cli/thread-command.js +103 -30
  47. package/dist/cli/trigger-command.js +1 -1
  48. package/dist/cli/watch-msg-diag.js +1 -0
  49. package/dist/config/access-policy.js +2 -2
  50. package/dist/config/boot-log.js +14 -11
  51. package/dist/config/config-field-policy.js +9 -1
  52. package/dist/config/config-manager.js +176 -90
  53. package/dist/config/config-operation-service.js +7 -14
  54. package/dist/config/contact-book-store.js +142 -26
  55. package/dist/config/contact-book.js +4 -2
  56. package/dist/config/lifecycle.js +1 -1
  57. package/dist/config/owner-config.js +96 -0
  58. package/dist/config/peer-role-resolver.js +12 -18
  59. package/dist/config/providers-config.js +382 -0
  60. package/dist/config/providers-import.js +74 -0
  61. package/dist/config/providers-management.js +328 -0
  62. package/dist/config/providers.js +22 -0
  63. package/dist/config/resolved-config-op.js +17 -14
  64. package/dist/config/snapshot.js +28 -16
  65. package/dist/config-store.js +18 -14
  66. package/dist/core/agent-ipc-registration.js +21 -63
  67. package/dist/core/agent-runtime.js +16 -5
  68. package/dist/core/auth/agent-delegation.js +199 -25
  69. package/dist/core/auth/auth-gateway.js +44 -2
  70. package/dist/core/auth/authorization-denial.js +1 -1
  71. package/dist/core/auth/debug-context.js +2 -1
  72. package/dist/core/auth/delegation-authorization.js +2 -2
  73. package/dist/core/auth/delegation-diagnostics.js +4 -1
  74. package/dist/core/auth/operation-authorizer.js +10 -1
  75. package/dist/core/auth/operation-catalog.js +19 -1
  76. package/dist/core/bootstrap-service.js +43 -20
  77. package/dist/core/channel-loader.js +0 -1
  78. package/dist/core/channel-runtime.js +30 -4
  79. package/dist/core/command/agent-control.js +196 -37
  80. package/dist/core/command/cli-intent-parser.js +60 -4
  81. package/dist/core/command/command-handler.js +212 -60
  82. package/dist/core/command/connect-menu.js +1 -0
  83. package/dist/core/command/diag-service.js +5 -4
  84. package/dist/core/command/file-search.js +368 -0
  85. package/dist/core/command/menu-catalog.js +2 -1
  86. package/dist/core/command/menu-handler.js +292 -107
  87. package/dist/core/command/menu-protocol.js +23 -0
  88. package/dist/core/command/role-menu.js +7 -19
  89. package/dist/core/command/skill-slash.js +130 -25
  90. package/dist/core/command/slash-handler.js +101 -53
  91. package/dist/core/command/topic-slash.js +16 -5
  92. package/dist/core/daemon-bootstrap.js +7 -1
  93. package/dist/core/daemon-control.js +19 -1
  94. package/dist/core/daemon-lifecycle.js +2 -1
  95. package/dist/core/event-catalog.js +2 -0
  96. package/dist/core/evolagent-registry.js +13 -2
  97. package/dist/core/evolagent.js +3 -6
  98. package/dist/core/handoff/ipc-registration.js +2 -2
  99. package/dist/core/handoff/runtime.js +40 -6
  100. package/dist/core/handoff/store.js +1 -0
  101. package/dist/core/inference/text-inference.js +95 -6
  102. package/dist/core/ipc-registration.js +43 -24
  103. package/dist/core/memory/consolidation-context.js +4 -1
  104. package/dist/core/memory/consolidation.js +25 -14
  105. package/dist/core/message/aun-ipc-sender.js +296 -88
  106. package/dist/core/message/aun-request-registry.js +226 -0
  107. package/dist/core/message/create-status.js +33 -4
  108. package/dist/core/message/message-bridge.js +62 -13
  109. package/dist/core/message/message-log.js +0 -1
  110. package/dist/core/message/message-queue.js +4 -1
  111. package/dist/core/message/message-utils.js +8 -1
  112. package/dist/core/message/peer-mode.js +17 -9
  113. package/dist/core/message/response-engine.js +266 -124
  114. package/dist/core/message/retry-scheduler.js +11 -8
  115. package/dist/core/model/catalog-service.js +60 -18
  116. package/dist/core/model/model-catalog.js +112 -9
  117. package/dist/core/model/model-fallback.js +94 -26
  118. package/dist/core/ownership/owner-resolver.js +687 -0
  119. package/dist/core/permission/approval-gateway.js +196 -62
  120. package/dist/core/permission/ec-command-parser.js +138 -25
  121. package/dist/core/permission/filesystem-approval-scope.js +141 -0
  122. package/dist/core/permission/protected-paths.js +14 -1
  123. package/dist/core/permission/sandbox-runtime.js +171 -18
  124. package/dist/core/permission/tool-policy.js +13 -2
  125. package/dist/core/role/runtime-policy.js +3 -4
  126. package/dist/core/session/session-idle-monitor.js +10 -8
  127. package/dist/core/session/session-manager.js +72 -45
  128. package/dist/core/session/session-mapper.js +18 -7
  129. package/dist/core/session/session-renew.js +5 -4
  130. package/dist/core/session/session-title.js +2 -1
  131. package/dist/core/session/session-turn-coordinator.js +4 -2
  132. package/dist/core/skill/index-generator.js +2 -16
  133. package/dist/core/task/task-recovery-service.js +15 -2
  134. package/dist/core/thread/command-parser.js +28 -13
  135. package/dist/core/thread/index.js +1 -0
  136. package/dist/core/thread/ipc-operation.js +11 -8
  137. package/dist/core/thread/thread-operation.js +79 -44
  138. package/dist/core/thread/thread-route.js +54 -6
  139. package/dist/core/thread/thread-service.js +236 -45
  140. package/dist/core/thread/topic-id.js +248 -0
  141. package/dist/eck/kit-renderer.js +1 -1
  142. package/dist/eck/message-renderer.js +12 -2
  143. package/dist/index.js +50 -27
  144. package/dist/ipc.js +59 -6
  145. package/dist/paths.js +1 -0
  146. package/dist/product.js +1 -0
  147. package/dist/response-system/engines/v1/proactive-flow.js +10 -3
  148. package/dist/stats/db.js +8 -0
  149. package/dist/stats/writer.js +26 -4
  150. package/dist/trigger/authorization.js +48 -3
  151. package/dist/trigger/feedback.js +28 -10
  152. package/dist/trigger/ipc-service.js +86 -10
  153. package/dist/trigger/local-files.js +55 -23
  154. package/dist/trigger/scheduler.js +57 -11
  155. package/dist/trigger/system-definitions.js +1 -0
  156. package/dist/trigger/validation.js +44 -15
  157. package/dist/utils/aid-bind.js +17 -297
  158. package/dist/utils/avatar-upload.js +16 -4
  159. package/dist/utils/cross-platform.js +115 -36
  160. package/dist/utils/ecweb-supervisor.js +1 -1
  161. package/dist/utils/ecweb-utils.js +8 -1
  162. package/dist/utils/error-utils.js +44 -6
  163. package/dist/utils/instance-registry.js +12 -2
  164. package/dist/utils/npm-ops.js +102 -27
  165. package/dist/utils/package-version-info.js +72 -27
  166. package/dist/utils/restart-safety.js +50 -0
  167. package/dist/utils/upgrade-jobs.js +304 -0
  168. package/dist/utils/windows-autostart.js +17 -10
  169. package/kits/docs/INDEX.md +5 -0
  170. package/kits/docs/aun/ABILITY_CALLING.md +239 -0
  171. package/kits/docs/evolcore/INDEX.md +14 -3
  172. package/kits/docs/evolcore/agent-create.md +55 -141
  173. package/kits/docs/evolcore/agent.md +38 -16
  174. package/kits/docs/evolcore/aid.md +34 -0
  175. package/kits/docs/evolcore/config.md +20 -1
  176. package/kits/docs/evolcore/event.md +2 -2
  177. package/kits/docs/evolcore/fs.md +18 -2
  178. package/kits/docs/evolcore/group-collaboration.md +13 -246
  179. package/kits/docs/evolcore/group.md +2 -1
  180. package/kits/docs/evolcore/memory-consolidation-batches.md +1 -1
  181. package/kits/docs/evolcore/menu-protocol.md +65 -1
  182. package/kits/docs/evolcore/msg.md +64 -2
  183. package/kits/docs/evolcore/skill.md +86 -0
  184. package/kits/docs/evolcore/stats.md +6 -0
  185. package/kits/docs/evolcore/task.md +2 -0
  186. package/kits/docs/evolcore/thread-chain.md +145 -51
  187. package/kits/docs/evolcore/trigger.md +48 -3
  188. package/kits/eck_manifest.json +21 -0
  189. package/kits/eck_message_manifest.json +4 -4
  190. package/kits/migrations/migrate-role-config-v5.mjs +20 -7
  191. package/kits/rules/01-overview.md +2 -1
  192. package/kits/rules/03-identity.md +3 -1
  193. package/kits/rules/04-relation.md +1 -1
  194. package/kits/rules/07-memory-and-thread.md +1 -1
  195. package/kits/schemas/_meta.json +16 -1
  196. package/kits/schemas/agent-config.schema.13.json +44 -2
  197. package/kits/schemas/contact-book.schema.3.json +5 -0
  198. package/kits/schemas/daemon.schema.6.json +3 -1
  199. package/kits/schemas/daemon.schema.8.json +329 -0
  200. package/kits/schemas/defaults.schema.5.json +39 -3
  201. package/kits/schemas/providers.schema.1.json +76 -0
  202. package/kits/schemas/relation-config.schema.9.json +5 -1
  203. package/kits/skills/.switches.json +28 -0
  204. package/kits/skills/aid-ability/SKILL.md +214 -0
  205. package/kits/skills/ec-agent-create/SKILL.md +162 -0
  206. package/kits/skills/ec-group/SKILL.md +2 -1
  207. package/kits/skills/ec-group-collaboration/SKILL.md +228 -0
  208. package/kits/skills/ec-identity/SKILL.md +143 -0
  209. package/kits/skills/ec-skill/scripts/cli.mjs +36 -3
  210. package/kits/skills/ec-skill/scripts/skill.mjs +202 -15
  211. package/kits/skills/ec-trigger/SKILL.md +167 -0
  212. package/kits/skills/ec-trigger/references/examples.md +153 -0
  213. package/kits/skills/thread-ops/SKILL.md +108 -420
  214. package/kits/templates/message-fragments/thread-context.md +1 -2
  215. package/kits/templates/message-fragments/trigger-thread-check.md +1 -1
  216. package/kits/templates/roles/member.json +1 -1
  217. package/kits/templates/roles/visitor.json +1 -0
  218. package/kits/templates/system-fragments/bootstrap.md +1 -0
  219. package/kits/templates/system-fragments/channel.md +2 -1
  220. package/kits/templates/system-fragments/commands.md +5 -4
  221. package/kits/templates/system-fragments/identity.md +2 -0
  222. package/kits/templates/system-fragments/session.md +1 -1
  223. package/kits/templates/system-fragments/venue.md +1 -1
  224. package/package.json +4 -3
package/CHANGELOG.md CHANGED
@@ -5,6 +5,48 @@
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 0.0.37 (2026-10-24)
9
+
10
+ ### 升级与重启稳定性
11
+
12
+ - 修复升级重启过程中运行时资源版本不同步的问题,暂存运行文件时一并保留 `kits` 目录。
13
+ - 修复 daemon Owner 通知配置异常或发送失败时可能影响重启恢复流程的问题;通知失败会保留诊断并继续执行重启。
14
+
15
+ ### 配置维护
16
+
17
+ - 改进本地 Provider 配置文件的忽略规则,避免运行时凭据文件被误加入 Git 工作区。
18
+
19
+ ## 0.0.36 (2026-10-24)
20
+
21
+ ### Agent 创建与所有权
22
+
23
+ - 默认使用一次性初始化创建 Agent,并提供独立的 Bootstrap 模式;初始化支持进度查询、超时识别和失败恢复。
24
+ - 强化 Owner 绑定与共识确认流程,避免未完成授权的 Agent 提前激活或处理普通消息。
25
+ - 支持创建 Agent 时上传头像,并完善创建状态、欢迎消息和多 Owner 交接。
26
+ - AUN 私聊支持首个配置 Owner 通过纯 `/bind` 发起 ownership 绑定;系统会持续刷新待确认状态,且不影响带渠道参数的联系人绑定。
27
+
28
+ ### Provider 与模型
29
+
30
+ - 支持按 Agent 基座类型选择 Provider 服务,并统一 Provider 配置、模型目录和备用模型处理。
31
+ - 支持从运行环境导入 Provider 连接信息,允许在运行环境尚未配置完整时保存模型选择。
32
+ - 改进模型不可用、连接配置和运行时切换的诊断。
33
+
34
+ ### 权限与运行稳定性
35
+
36
+ - 按操作敏感度复用 Owner 共识缓存,并在敏感操作、Owner 变更和授权材料变化时重新核验。
37
+ - 修复 Claude runner 未正确应用显式服务地址和 API 密钥的问题。
38
+ - 改进 Codex 会话恢复、AUN 断线期间的 Owner 状态保留、Trigger 重试和跨话题复查。
39
+ - 收紧受管临时路径识别和命令授权边界,提升权限拒绝原因的可读性。
40
+ - 提升 AUN SDK 兼容要求,使用经过当前版本验证的依赖版本。
41
+
42
+ ### 技能与运维
43
+
44
+ - 新增 `ec skill load`、`ec skill path` 和分页技能列表,支持按来源、描述和筛选条件查看技能。
45
+ - 新增 `ec-agent-create` 技能,集中处理 Agent 创建、初始化、Bootstrap、恢复和验收流程。
46
+ - 新增 `ec-group-collaboration` 技能,集中处理 AUN 协作群创建、邀请、规则、任务分派和验收。
47
+ - 新增可恢复的 npm 升级任务、项目文件搜索和更完整的 EC Web 运维状态展示。
48
+ - 改进 Windows 启动、自启动、联系人同步和基础配置诊断。
49
+
8
50
  ## 0.0.35 (2026-10-19)
9
51
 
10
52
  ### 权限与记忆
package/README.md CHANGED
@@ -105,6 +105,12 @@ npm install -g evolcore
105
105
 
106
106
  Linux 全局安装会在 `postinstall` 阶段准备 Codex 受管运行所需的真实目录 `/etc/codex`。如果当前 npm 全局目录属于普通用户且该系统目录尚不存在,请先执行 `sudo install -d -m 0755 /etc/codex`,再重新安装。
107
107
 
108
+ Linux 托管 Codex 在外层沙箱已遮蔽真实 `data/` 并仅重新暴露 daemon IPC socket 时,内层权限配置对这棵已遮蔽的数据树使用只读投影,避免重复 `deny` 阻断 socket 挂载或连接;这不开放真实 `data/instance/`,其中的 `control.token` 等数据仍不可见,也不需要关闭沙箱或提高关系权限。
109
+
110
+ Ubuntu 24.04+ 若启用 `apparmor_restrict_unprivileged_userns=1`,Codex 受限会话启动前会运行实际 bwrap/user namespace 探针;探针失败时按 `codex_sandbox_userns_disabled_by_apparmor` fail closed,不会自动切换到全权限。请在服务实际运行环境执行 `ec diagnose --codex-sandbox`,由宿主为 EvolCore/Codex 配置最小范围的 AppArmor 允许规则,或提供等价隔离后端;不要全局关闭 AppArmor user namespace 限制。
111
+
112
+ Linux 上 Claude 的沙箱执行需要可执行的 `bubblewrap`(`bwrap`)和 `socat`。安装器、npm postinstall 和 daemon 启动会检查并提醒缺失项,不会自动安装系统包或关闭沙箱;Debian/Ubuntu 可由管理员运行 `apt install bubblewrap socat`。`bwrap` 可复用 `EVOLCORE_BWRAP_PATH` 或 Codex 自带的可执行文件。安装后在服务相同的环境中运行 `ec diagnose --claude-sandbox`,同时验证依赖路径和基础命名空间启动;工具存在不代表容器的 user namespace/seccomp 配置一定可用。缺少依赖的任务不会自动恢复重试,修复后可人工 `/resume`。
113
+
108
114
  Windows 的 npm 安装和升级不会直接写入机器级 Codex 配置。配置使用 Codex 后,执行 `ec start` 或 `ec restart` 时会自动检测托管 Hook;缺失时自动请求 Windows 管理员授权(UAC),安装并复检成功后继续启动。已安装时直接继续,无需单独执行安装命令,详见 [Windows Codex 托管 Hook](docs/codex-windows-managed-hook.md)。
109
115
 
110
116
  如果取消 UAC 或安装失败,本次启动或重启会停止;重启检查发生在停止旧服务之前。已有策略异常或安装位置变化需要管理员核对,自动安装不会覆盖这些状态。
@@ -649,10 +649,27 @@ export function provisionManagedFiles(options, acl = windowsAcl) {
649
649
  }
650
650
  }
651
651
 
652
- function main() {
652
+ async function checkClaudeSandboxDependencies() {
653
+ try {
654
+ const runtime = await import('../dist/core/permission/sandbox-runtime.js');
655
+ const check = runtime.inspectClaudeSandboxDependencies();
656
+ if (check.status === 'missing' || hasArgument('--check-claude-sandbox')) {
657
+ process.stderr.write(`[evolcore] ${runtime.formatClaudeSandboxDependencies(check)}\n`);
658
+ }
659
+ } catch (error) {
660
+ process.stderr.write(`[evolcore] Claude sandbox dependency check unavailable: ${error.message}. Run ec diagnose before using sandbox-required Claude sessions.\n`);
661
+ }
662
+ }
663
+
664
+ async function main() {
665
+ if (hasArgument('--check-claude-sandbox')) {
666
+ await checkClaudeSandboxDependencies();
667
+ return;
668
+ }
653
669
  // An explicit package root selects provisioning, even if the elevated
654
670
  // child inherited npm lifecycle variables. Ordinary postinstall skips it.
655
671
  if (!argument('--package-root') && (lifecycleEvent() === 'postinstall' || isGlobalInstall())) {
672
+ await checkClaudeSandboxDependencies();
656
673
  runLinuxPostinstall();
657
674
  windowsPostinstallNotice();
658
675
  return;
@@ -697,7 +714,7 @@ if (process.argv[1] && pathToFileURL(fs.realpathSync(process.argv[1])).href ===
697
714
  const earlyRequirements = argument('--requirements')
698
715
  ? path.resolve(argument('--requirements'))
699
716
  : path.join(process.env.ProgramData || path.join(os.homedir(), 'ProgramData'), 'OpenAI', 'Codex', 'requirements.toml');
700
- try { main(); } catch (error) {
717
+ void main().catch(error => {
701
718
  fail(error instanceof Error ? error.message : String(error), earlyRequirements);
702
- }
719
+ });
703
720
  }
@@ -131,6 +131,6 @@ export async function getBaseagentSnapshot(options = {}) {
131
131
  }
132
132
  const detections = options.probe === false
133
133
  ? null
134
- : (await import('./baseagent-availability.js')).detectBaseagentDetails();
134
+ : detectBaseagentDetails();
135
135
  return buildBaseagentSnapshot(config ?? {}, detections, options.loaded);
136
136
  }
@@ -16,6 +16,7 @@ import { buildCliLaunchCommand, commandExists, isWindows, resolvePowerShellShimP
16
16
  import { execFileSync } from 'child_process';
17
17
  import { parse as parseToml } from 'smol-toml';
18
18
  import { readRuntimeEnvironmentVariable } from '../config/runtime-env.js';
19
+ import { resolveProviderServiceForBaseagent, resolveProviderService } from '../config/providers.js';
19
20
  /** Resolve the installed Codex CLI entrypoint, including npm shims on Windows. */
20
21
  export function resolveCodexCliPath() {
21
22
  return resolveProbePath('codex');
@@ -138,6 +139,12 @@ function loadClaudeSettings() {
138
139
  * across Agent config, the process environment, and Claude Code settings.
139
140
  */
140
141
  export function resolveAnthropicDirectConfig(override, runtime) {
142
+ if (override?.provider !== undefined) {
143
+ const provider = resolveProviderService(override.provider, 'claude');
144
+ if (!provider.apiKey)
145
+ throw new Error(`Provider ${provider.providerId} has no API key`);
146
+ return { ...provider, apiKey: provider.apiKey };
147
+ }
141
148
  const explicit = directInferencePair(override?.apiKey, override?.baseUrl);
142
149
  if (explicit) {
143
150
  return {
@@ -163,6 +170,22 @@ export function resolveAnthropicDirectConfig(override, runtime) {
163
170
  }
164
171
  export function resolveAnthropicConfig(config, override, runtime) {
165
172
  const settings = loadClaudeSettings();
173
+ const providerId = override?.provider ?? config.agents?.claude?.provider;
174
+ if (providerId !== undefined) {
175
+ const provider = resolveProviderService(providerId, 'claude');
176
+ if (!provider.apiKey)
177
+ throw new Error(`Provider ${providerId} has no API key`);
178
+ return {
179
+ apiKey: provider.apiKey,
180
+ baseUrl: provider.baseUrl,
181
+ providerId,
182
+ headers: provider.headers,
183
+ queryParams: provider.queryParams,
184
+ model: override?.model || config.agents?.claude?.model || settings.model || 'sonnet',
185
+ effort: override?.effort || config.agents?.claude?.effort || settings.effortLevel || undefined,
186
+ pathToClaudeCodeExecutable: override?.pathToClaudeCodeExecutable || config.agents?.claude?.pathToClaudeCodeExecutable,
187
+ };
188
+ }
166
189
  const isPlaceholder = (v) => !v || v.includes('your-') || v.includes('placeholder');
167
190
  const resolveGatewayReference = (value) => {
168
191
  const name = value?.match(/^\$\{(ANTHROPIC_BASE_URL|ANTHROPIC_AUTH_TOKEN|ANTHROPIC_API_KEY)\}$/u)?.[1];
@@ -276,6 +299,12 @@ export function loadCodexSettings() {
276
299
  * never combined with a partial Agent or process-environment configuration.
277
300
  */
278
301
  export function resolveOpenaiDirectConfig(override) {
302
+ if (override?.provider !== undefined) {
303
+ const provider = resolveProviderService(override.provider, 'codex');
304
+ if (!provider.apiKey)
305
+ throw new Error(`Provider ${provider.providerId} has no API key`);
306
+ return { ...provider, apiKey: provider.apiKey };
307
+ }
279
308
  const explicit = directInferencePair(override?.apiKey, override?.baseUrl);
280
309
  if (explicit) {
281
310
  return {
@@ -310,6 +339,28 @@ export function resolveOpenaiDirectConfig(override) {
310
339
  }
311
340
  export function resolveOpenaiConfig(config, override, runtime) {
312
341
  const codexSettings = loadCodexSettings();
342
+ const providerId = override?.provider ?? config.agents?.codex?.provider;
343
+ if (providerId !== undefined) {
344
+ const provider = resolveProviderService(providerId, 'codex');
345
+ if (!provider.apiKey)
346
+ throw new Error(`Provider ${providerId} has no API key`);
347
+ return {
348
+ apiKey: provider.apiKey,
349
+ authType: 'apiKey',
350
+ authSource: 'agent-config',
351
+ baseUrl: provider.baseUrl,
352
+ provider: providerId,
353
+ providerId,
354
+ headers: provider.headers,
355
+ queryParams: provider.queryParams,
356
+ model: override?.model || config.agents?.codex?.model || codexSettings.model || 'gpt-5.2-codex',
357
+ effort: override?.effort || override?.reasoning || config.agents?.codex?.effort || undefined,
358
+ enableRequestUserInput: override?.enableRequestUserInput ?? config.agents?.codex?.enableRequestUserInput ?? true,
359
+ approvalsReviewer: override?.approvalsReviewer ?? config.agents?.codex?.approvalsReviewer,
360
+ agentAid: runtime?.agentAid,
361
+ agentConfig: runtime?.agentConfig,
362
+ };
363
+ }
313
364
  const isPlaceholder = (v) => !v || v.includes('your-') || v.includes('placeholder');
314
365
  const overrideApiKey = isPlaceholder(override?.apiKey) ? undefined : override?.apiKey;
315
366
  const globalApiKey = isPlaceholder(config.agents?.codex?.apiKey) ? undefined : config.agents?.codex?.apiKey;
@@ -388,6 +439,17 @@ export function resolveGoogleConfig(config, override) {
388
439
  cliPath = commandExists('gemini') ? 'gemini' : '';
389
440
  }
390
441
  const model = override?.model || googleCfg?.model || 'gemini-2.5-flash';
442
+ const providerId = override?.provider ?? googleCfg?.provider;
443
+ if (providerId !== undefined) {
444
+ const provider = resolveProviderService(providerId, 'gemini');
445
+ if (!provider.apiKey)
446
+ throw new Error(`Provider ${providerId} has no API key`);
447
+ if (provider.queryParams && Object.keys(provider.queryParams).length > 0) {
448
+ throw new Error(`Provider ${providerId} gemini service queryParams are unsupported by the Gemini CLI runtime`);
449
+ }
450
+ return { cliPath, model, apiKey: provider.apiKey, providerId, baseUrl: provider.baseUrl, headers: provider.headers,
451
+ queryParams: provider.queryParams, mode: override?.mode || googleCfg?.mode || 'cli', useVertex: false };
452
+ }
391
453
  const overrideApiKey = isPlaceholder(override?.apiKey) ? undefined : override?.apiKey;
392
454
  const globalApiKey = isPlaceholder(googleCfg?.apiKey) ? undefined : googleCfg?.apiKey;
393
455
  const apiKey = overrideApiKey
@@ -409,6 +471,32 @@ export const DEFAULT_ECAGENT_MAX_RETRIES = 2;
409
471
  export const DEFAULT_ECAGENT_MAX_RETRY_DELAY_MS = 4_000;
410
472
  export function resolveEcagentConfig(config, override) {
411
473
  const configured = config.agents?.ecagent;
474
+ const providerId = override?.provider ?? configured?.provider;
475
+ if (providerId !== undefined) {
476
+ const providerServiceType = override?.providerServiceType ?? configured?.providerServiceType;
477
+ const provider = resolveProviderServiceForBaseagent(providerId, 'ecagent', providerServiceType);
478
+ if (provider.style !== 'openai-compatible') {
479
+ throw new Error(`Provider ${providerId} uses unsupported ecagent style: ${provider.style}`);
480
+ }
481
+ if (!provider.apiKey)
482
+ throw new Error(`Provider ${providerId} has no API key`);
483
+ const numberOr = (value, fallback) => (typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback);
484
+ return {
485
+ apiKey: provider.apiKey,
486
+ baseUrl: provider.baseUrl,
487
+ headers: provider.headers,
488
+ queryParams: provider.queryParams,
489
+ model: override?.model || configured?.model || DEFAULT_ECAGENT_MODEL,
490
+ effort: override?.effort ?? configured?.effort,
491
+ timeoutMs: numberOr(override?.timeoutMs ?? configured?.timeoutMs, DEFAULT_ECAGENT_TIMEOUT_MS),
492
+ maxRetries: numberOr(override?.maxRetries ?? configured?.maxRetries, DEFAULT_ECAGENT_MAX_RETRIES),
493
+ maxRetryDelayMs: numberOr(override?.maxRetryDelayMs ?? configured?.maxRetryDelayMs, DEFAULT_ECAGENT_MAX_RETRY_DELAY_MS),
494
+ providerId,
495
+ providerServiceType: provider.serviceType,
496
+ providerStyle: provider.style,
497
+ modelOverrides: provider.modelOverrides,
498
+ };
499
+ }
412
500
  const isPlaceholder = (value) => !value
413
501
  || value.includes('your-')
414
502
  || value.includes('placeholder');
@@ -4,12 +4,15 @@ 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';
7
+ import { resolveProviderService } from '../config/providers.js';
7
8
  import { buildModelRequestHeaders } from './request-identity.js';
8
9
  import { DEFAULT_PERMISSION_MODE } from '../types.js';
9
10
  import { renderActionAsText } from '../core/interaction-router.js';
11
+ import { sessionTopicThreadId } from '../core/thread/topic-id.js';
10
12
  import { buildEnvelope, isInteractionSendAccepted, sendInteractionPayload } from '../core/message/message-utils.js';
11
13
  import path from 'path';
12
14
  import fs from 'fs';
15
+ import { assertOwnershipAuthorization } from '../core/ownership/owner-resolver.js';
13
16
  import os from 'os';
14
17
  import crypto from 'crypto';
15
18
  import { logger } from '../utils/logger.js';
@@ -25,7 +28,7 @@ import { sanitizeSessionTitle } from '../core/session/session-title.js';
25
28
  import { resolveClaudeCapabilityRunOptionsForProject } from '../core/capability/capability-manager.js';
26
29
  import { normalizeExecutionPermissionMode, normalizePermissionMode, resolvePhaseOneExecutionSandbox } from '../core/permission/mode.js';
27
30
  import { buildClaudeProtectedFilesystem, containsHClassReference, containsLClassReference, isHClassPath, isLClassPath, isSameOrDescendant, resolveProtectedCandidate, } from '../core/permission/protected-paths.js';
28
- import { buildHClassGuardCommand, collectSandboxGuardDiagnostics, createSandboxInitializationError, getActiveSandboxInitializationFailure, isSandboxInitializationFailure, isSandboxInitializationToolResult, recordSandboxInitializationFailure, getSandboxInitializationFailureCounters, SANDBOX_INITIALIZATION_COOLDOWN_MS, SANDBOX_INITIALIZATION_FAILED, ensureClaudeGitWorktreeConfig, prependExecutableDirectory, resolveBubblewrapPath, summarizeBubblewrapArgsForLog, summarizeSandboxGuardDiagnostics, shouldEnableClaudeWeakerNestedSandbox, shouldFailIfClaudeSandboxUnavailable, shouldLogSandboxCircuitNotice, } from '../core/permission/sandbox-runtime.js';
31
+ import { buildHClassGuardCommand, collectSandboxGuardDiagnostics, createSandboxInitializationError, createClaudeSandboxDependencyError, inspectClaudeSandboxDependencies, getActiveSandboxInitializationFailure, isSandboxInitializationFailure, isSandboxInitializationToolResult, recordSandboxInitializationFailure, getSandboxInitializationFailureCounters, SANDBOX_INITIALIZATION_COOLDOWN_MS, SANDBOX_INITIALIZATION_FAILED, ensureClaudeGitWorktreeConfig, prependExecutableDirectory, resolveBubblewrapPath, summarizeBubblewrapArgsForLog, summarizeSandboxGuardDiagnostics, shouldEnableClaudeWeakerNestedSandbox, shouldFailIfClaudeSandboxUnavailable, shouldLogSandboxCircuitNotice, } from '../core/permission/sandbox-runtime.js';
29
32
  import { buildClaudeUnixSocketAllowlist } from '../core/permission/unix-socket-policy.js';
30
33
  import { auditToolInfrastructureFailure, auditToolPreflightDenial } from '../core/auth/authorization-audit.js';
31
34
  import { getManagedTaskTempDir, isManagedSessionRuntimeDir } from '../cli/task-context.js';
@@ -158,7 +161,9 @@ export function probeBundledClaudeVersion() {
158
161
  if (!executable)
159
162
  return undefined;
160
163
  try {
161
- const raw = execFileSync(executable, ['--version'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
164
+ const raw = execFileSync(executable, ['--version'], {
165
+ encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true, timeout: 3000,
166
+ });
162
167
  return raw.trim().split(/\s+/)[0] || undefined;
163
168
  }
164
169
  catch {
@@ -353,7 +358,7 @@ function inspectClaudePermissionExpansion(input, options, projectPath) {
353
358
  : input,
354
359
  };
355
360
  }
356
- function buildClaudeManagedLockdownSettings(capabilityOptions) {
361
+ function buildClaudeManagedLockdownSettings(capabilityOptions, dependencies) {
357
362
  const settings = capabilityOptions?.settings;
358
363
  const rawAllowedMcpServers = settings && typeof settings === 'object' && !Array.isArray(settings)
359
364
  ? settings.allowedMcpServers
@@ -371,6 +376,7 @@ function buildClaudeManagedLockdownSettings(capabilityOptions) {
371
376
  disableSkillShellExecution: true,
372
377
  allowedHttpHookUrls: [],
373
378
  allowManagedPermissionRulesOnly: true,
379
+ ...(dependencies?.status === 'ready' ? { sandbox: { bwrapPath: dependencies.bwrapPath, socatPath: dependencies.socatPath } } : {}),
374
380
  ...(allowedMcpServers ? {
375
381
  allowManagedMcpServersOnly: true,
376
382
  allowedMcpServers,
@@ -700,7 +706,7 @@ export class AgentRunner {
700
706
  this.onSessionIdUpdate = onSessionIdUpdate;
701
707
  this.claudeExecutablePath = resolveBundledClaudeExecutable();
702
708
  if (config) {
703
- const anthropic = resolveAnthropicConfig(this.getRuntimeGatewayConfig(), undefined, this.getRuntimeGatewayScope());
709
+ const anthropic = resolveAnthropicConfig(this.getRuntimeGatewayConfig(), { apiKey, baseUrl }, this.getRuntimeGatewayScope());
704
710
  this.apiKey = apiKey || anthropic.apiKey;
705
711
  this.claudeExecutablePath = anthropic.pathToClaudeCodeExecutable || this.claudeExecutablePath;
706
712
  this.customHeaders = buildModelRequestHeaders({
@@ -734,8 +740,14 @@ export class AgentRunner {
734
740
  return this.config;
735
741
  }
736
742
  }
737
- getAgentEnv(runtimeEnv, evolcoreSessionId, permissionMode) {
738
- const anthropicBaseUrl = appendQueryParams(this.baseUrl, this.customQueryParams);
743
+ getAgentEnv(runtimeEnv, evolcoreSessionId, permissionMode, providerOverride) {
744
+ const current = this.getRuntimeGatewayConfig();
745
+ const providerConfigured = providerOverride !== undefined || current.agents?.claude?.provider !== undefined;
746
+ const connection = providerConfigured ? resolveAnthropicConfig(current, providerOverride ? { provider: providerOverride } : undefined, this.getRuntimeGatewayScope())
747
+ : { baseUrl: this.baseUrl, apiKey: this.apiKey, headers: this.customHeaders, queryParams: this.customQueryParams };
748
+ const anthropicBaseUrl = appendQueryParams(connection.baseUrl, connection.queryParams);
749
+ const headers = providerConfigured ? buildModelRequestHeaders({ baseagent: 'claude', baseUrl: connection.baseUrl,
750
+ agentAid: this.agentAid, configuredHeaders: connection.headers }) : connection.headers;
739
751
  const env = sanitizeShellExecutionEnvironment({
740
752
  ...process.env,
741
753
  ANTHROPIC_AUTH_TOKEN: this.apiKey,
@@ -752,6 +764,12 @@ export class AgentRunner {
752
764
  ...(evolcoreSessionId ? { EVOLCORE_SESSION_ID: evolcoreSessionId } : {}),
753
765
  ...(runtimeEnv ?? {}),
754
766
  }, { lockExecutableResolution: permissionMode === 'readonly' });
767
+ if (providerConfigured) {
768
+ env.ANTHROPIC_AUTH_TOKEN = connection.apiKey;
769
+ env.ANTHROPIC_API_KEY = connection.apiKey;
770
+ env.ANTHROPIC_BASE_URL = anthropicBaseUrl;
771
+ env.ANTHROPIC_CUSTOM_HEADERS = Object.entries(headers ?? {}).map(([name, value]) => `${name}: ${value}`).join('\n');
772
+ }
755
773
  const trustedRootFullAccess = permissionMode === 'fullaccess'
756
774
  && typeof process.getuid === 'function'
757
775
  && process.getuid() === 0
@@ -766,7 +784,7 @@ export class AgentRunner {
766
784
  env.IS_SANDBOX = '1';
767
785
  else
768
786
  delete env.IS_SANDBOX;
769
- const bubblewrapPath = resolveBubblewrapPath();
787
+ const bubblewrapPath = resolveBubblewrapPath(env);
770
788
  return bubblewrapPath
771
789
  ? prependExecutableDirectory(env, bubblewrapPath)
772
790
  : env;
@@ -817,7 +835,19 @@ export class AgentRunner {
817
835
  };
818
836
  }
819
837
  catalogConnection() {
820
- return { baseUrl: this.baseUrl, apiKey: this.apiKey, headers: this.customHeaders, queryParams: this.customQueryParams };
838
+ const current = this.getRuntimeGatewayConfig();
839
+ if (current.agents?.claude?.provider !== undefined) {
840
+ const provider = resolveProviderService(current.agents.claude.provider, 'claude');
841
+ return { ...provider, headers: buildModelRequestHeaders({ baseagent: 'claude', baseUrl: provider.baseUrl,
842
+ agentAid: this.agentAid, configuredHeaders: provider.headers }) };
843
+ }
844
+ return {
845
+ baseUrl: this.baseUrl,
846
+ apiKey: this.apiKey,
847
+ headers: this.customHeaders,
848
+ queryParams: this.customQueryParams,
849
+ providerId: this.config?.agents?.claude?.provider,
850
+ };
821
851
  }
822
852
  getModelCatalog() {
823
853
  return getRunnerCatalog('claude', this.agentAid, this.catalogConnection());
@@ -2357,6 +2387,12 @@ export class AgentRunner {
2357
2387
  const trustedSafeOutputCommands = new Set();
2358
2388
  // PreToolUse Hook - 黑名单检查 + input 修正(不可绕过,所有模式都走)
2359
2389
  const preToolUseHook = async (input, _toolUseId, hookOptions) => {
2390
+ try {
2391
+ await assertOwnershipAuthorization(this.permissionContexts.get(sessionId));
2392
+ }
2393
+ catch (error) {
2394
+ return hookDecision('deny', error instanceof Error ? error.message : String(error));
2395
+ }
2360
2396
  const pauseController = this.permissionContexts.get(sessionId)?.pauseController;
2361
2397
  if (pauseController) {
2362
2398
  const pauseResult = await pauseController.waitAtToolBoundary(hookOptions?.signal);
@@ -2573,6 +2609,12 @@ export class AgentRunner {
2573
2609
  };
2574
2610
  }
2575
2611
  // 特殊处理:AskUserQuestion 工具(SDK 内置的用户交互工具)
2612
+ try {
2613
+ await assertOwnershipAuthorization(this.permissionContexts.get(sessionId));
2614
+ }
2615
+ catch (error) {
2616
+ return { behavior: 'deny', message: error instanceof Error ? error.message : String(error) };
2617
+ }
2576
2618
  // 这不是权限审批,而是收集用户答案,需要构造表单卡片
2577
2619
  if (toolName === 'AskUserQuestion') {
2578
2620
  return await this.handleAskUserQuestion(sessionId, input, options);
@@ -2777,10 +2819,14 @@ export class AgentRunner {
2777
2819
  }
2778
2820
  const catalogModel = resolveCatalogModel(runCatalog, callModel);
2779
2821
  const { sdkModel, contextWindow } = resolveClaudeModelContext(catalogModel, runCatalog);
2822
+ const agentEnv = this.getAgentEnv(runtimeEnv, sessionId, callPermissionMode, modelOverride?.provider);
2823
+ const dependencies = executionSandbox.state !== 'off' ? inspectClaudeSandboxDependencies(agentEnv) : undefined;
2824
+ if (dependencies?.status === 'missing')
2825
+ throw createClaudeSandboxDependencyError(dependencies);
2780
2826
  const capabilityOptions = await this.resolveCapabilityRunOptions(projectPath);
2781
2827
  const managedSettings = callPermissionMode === 'fullaccess'
2782
2828
  ? undefined
2783
- : buildClaudeManagedLockdownSettings(capabilityOptions);
2829
+ : buildClaudeManagedLockdownSettings(capabilityOptions, dependencies);
2784
2830
  const sandboxOptions = executionSandbox.state === 'off'
2785
2831
  ? { enabled: false }
2786
2832
  : await (async () => {
@@ -2879,7 +2925,7 @@ export class AgentRunner {
2879
2925
  },
2880
2926
  ...(enableSummaries ? { agentProgressSummaries: true } : {}),
2881
2927
  stderr: handleClaudeStderr,
2882
- env: this.getAgentEnv(runtimeEnv, sessionId, callPermissionMode)
2928
+ env: agentEnv
2883
2929
  };
2884
2930
  // Native Windows has a small CreateProcess command-line limit. The SDK
2885
2931
  // normally serializes `sandbox` into inline `--settings` JSON, so preserve
@@ -3001,10 +3047,11 @@ export class AgentRunner {
3001
3047
  if (sessionManager && agentSessionId) {
3002
3048
  try {
3003
3049
  const currentSession = await sessionManager.getSessionById?.(sessionId);
3004
- if (currentSession?.threadId && topicBinding && typeof sessionManager.consumeTopicResumeAt === 'function') {
3050
+ const currentTopicThreadId = sessionTopicThreadId(currentSession);
3051
+ if (currentTopicThreadId && topicBinding && typeof sessionManager.consumeTopicResumeAt === 'function') {
3005
3052
  resumeAt = await sessionManager.consumeTopicResumeAt({
3006
3053
  sessionId,
3007
- expectedThreadId: currentSession.threadId,
3054
+ expectedThreadId: currentTopicThreadId,
3008
3055
  expectedAgentSessionId: agentSessionId,
3009
3056
  });
3010
3057
  }
@@ -1,7 +1,7 @@
1
1
  import { spawn } from 'child_process';
2
2
  import readline from 'readline';
3
3
  import { logger } from '../../utils/logger.js';
4
- import { buildHClassGuardCommand, collectSandboxGuardDiagnostics, summarizeBubblewrapArgsForLog, summarizeSandboxGuardDiagnostics, } from '../../core/permission/sandbox-runtime.js';
4
+ import { buildHClassGuardCommand, collectSandboxGuardDiagnostics, createCodexSandboxAvailabilityError, inspectCodexSandboxAvailability, probeCodexSandboxAvailability, summarizeBubblewrapArgsForLog, summarizeSandboxGuardDiagnostics, } from '../../core/permission/sandbox-runtime.js';
5
5
  import { resolveCodexLaunchCommand } from '../baseagent.js';
6
6
  import { createHash } from 'crypto';
7
7
  import { sanitizeShellExecutionEnvironment } from '../../core/permission/shell-environment.js';
@@ -71,6 +71,16 @@ export class CodexAppServerClient {
71
71
  constructor(options) {
72
72
  this.options = options;
73
73
  }
74
+ assertCodexSandboxAvailable(config, sandbox) {
75
+ if (process.platform !== 'linux')
76
+ return;
77
+ if (!hasPermissionProfile(config) && (!sandbox || sandbox === 'danger-full-access'))
78
+ return;
79
+ const availability = inspectCodexSandboxAvailability({ ...process.env, ...this.options.env });
80
+ const probe = probeCodexSandboxAvailability(availability);
81
+ if (!probe.ok)
82
+ throw createCodexSandboxAvailabilityError(probe);
83
+ }
74
84
  onNotification(handler) {
75
85
  this.notificationHandlers.add(handler);
76
86
  return () => this.notificationHandlers.delete(handler);
@@ -78,6 +88,7 @@ export class CodexAppServerClient {
78
88
  async threadStart(projectPath, options) {
79
89
  const effort = options?.effort ?? this.options.effort;
80
90
  const configUsesPermissionProfile = hasPermissionProfile(options?.config);
91
+ this.assertCodexSandboxAvailable(options?.config, options?.sandbox);
81
92
  return this.request('thread/start', {
82
93
  cwd: projectPath,
83
94
  model: options?.model ?? this.options.model ?? null,
@@ -100,6 +111,7 @@ export class CodexAppServerClient {
100
111
  async threadResume(threadId, projectPath, options) {
101
112
  const effort = options?.effort ?? this.options.effort;
102
113
  const configUsesPermissionProfile = hasPermissionProfile(options?.config);
114
+ this.assertCodexSandboxAvailable(options?.config, options?.sandbox);
103
115
  return this.request('thread/resume', {
104
116
  threadId,
105
117
  cwd: projectPath,
@@ -222,6 +234,7 @@ export class CodexAppServerClient {
222
234
  async threadFork(threadId, projectPath, title, options) {
223
235
  const effort = options?.effort ?? this.options.effort;
224
236
  const configUsesPermissionProfile = hasPermissionProfile(options?.config);
237
+ this.assertCodexSandboxAvailable(options?.config, options?.sandbox);
225
238
  const response = await this.request('thread/fork', {
226
239
  threadId,
227
240
  cwd: projectPath,
@@ -767,6 +780,10 @@ export class CodexAppServerClient {
767
780
  args.push('--config', `model_provider=${JSON.stringify(providerName)}`);
768
781
  args.push('--config', `model_providers.${provider}.name=${JSON.stringify('EvolCore runtime gateway')}`);
769
782
  args.push('--config', `model_providers.${provider}.wire_api=${JSON.stringify('responses')}`);
783
+ if (this.options.isolatedProvider) {
784
+ args.push('--config', `model_providers.${provider}.env_key=${JSON.stringify('CODEX_API_KEY')}`);
785
+ args.push('--config', `model_providers.${provider}.requires_openai_auth=false`);
786
+ }
770
787
  if (useRuntimeProvider || this.options.baseUrl) {
771
788
  args.push('--config', `model_providers.${provider}.base_url=${JSON.stringify(this.options.baseUrl || 'https://api.openai.com/v1')}`);
772
789
  }