evolcore 0.0.8 → 0.0.9

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 (213) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +37 -3
  3. package/dist/agents/baseagent.js +111 -19
  4. package/dist/agents/claude-runner.js +30 -2
  5. package/dist/agents/codex-app-server-client.js +31 -0
  6. package/dist/agents/codex-runner.js +3 -0
  7. package/dist/aun/msg/group.js +9 -9
  8. package/dist/aun/msg/p2p.js +11 -6
  9. package/dist/aun/msg/upload.js +43 -29
  10. package/dist/aun/outbox.js +48 -5
  11. package/dist/channels/aun.js +199 -50
  12. package/dist/cli/aun-commands.js +10 -7
  13. package/dist/cli/daemon-commands.js +19 -4
  14. package/dist/cli/index.js +11 -1
  15. package/dist/cli/init.js +103 -4
  16. package/dist/cli/model.js +5 -5
  17. package/dist/config/builtin-role-templates.js +4 -3
  18. package/dist/config/config-field-policy.js +6 -2
  19. package/dist/config/config-manager.js +48 -11
  20. package/dist/config/role-schema.js +18 -3
  21. package/dist/config/role-service.js +3 -3
  22. package/dist/config/role-store.js +4 -3
  23. package/dist/config-store.js +4 -0
  24. package/dist/core/auth/operation-authorizer.js +3 -1
  25. package/dist/core/bootstrap-service.js +0 -12
  26. package/dist/core/channel-loader.js +5 -1
  27. package/dist/core/command/command-handler.js +1 -1
  28. package/dist/core/command/evol-menu-version-gate.js +3 -2
  29. package/dist/core/command/menu-handler.js +33 -22
  30. package/dist/core/command/menu-protocol.js +7 -4
  31. package/dist/core/command/slash-handler.js +4 -4
  32. package/dist/core/inference/text-inference.js +18 -5
  33. package/dist/core/message/message-bridge.js +17 -6
  34. package/dist/core/message/response-engine.js +22 -3
  35. package/dist/core/model/config-scope.js +3 -3
  36. package/dist/core/permission/tool-policy.js +18 -0
  37. package/dist/index.js +43 -26
  38. package/dist/ipc.js +1 -1
  39. package/dist/product.js +1 -0
  40. package/dist/utils/autostart.js +27 -0
  41. package/dist/utils/ecweb-utils.js +15 -2
  42. package/dist/utils/macos-autostart.js +179 -0
  43. package/dist/utils/windows-autostart.js +130 -0
  44. package/ecagent/dist/agent-loop.d.ts +1 -1
  45. package/ecagent/dist/agent-loop.js +1 -1
  46. package/ecagent/dist/agent.d.ts +1 -1
  47. package/ecagent/dist/agent.js +1 -1
  48. package/ecagent/dist/harness/agent-harness.d.ts +1 -1
  49. package/ecagent/dist/harness/compaction/branch-summarization.d.ts +1 -1
  50. package/ecagent/dist/harness/compaction/compaction.d.ts +1 -1
  51. package/ecagent/dist/harness/compaction/utils.d.ts +1 -1
  52. package/ecagent/dist/harness/messages.d.ts +1 -1
  53. package/ecagent/dist/harness/session/session.d.ts +1 -1
  54. package/ecagent/dist/harness/types.d.ts +1 -1
  55. package/ecagent/dist/proxy.d.ts +1 -1
  56. package/ecagent/dist/proxy.js +1 -1
  57. package/ecagent/dist/types.d.ts +1 -1
  58. package/kits/migrations/migrate-role-config-v5.mjs +27 -2
  59. package/kits/schemas/agent-config.schema.10.json +19 -1
  60. package/kits/schemas/daemon.schema.4.json +4 -0
  61. package/kits/schemas/defaults.schema.4.json +15 -1
  62. package/kits/schemas/relation-config.schema.8.json +22 -1
  63. package/kits/schemas/role-config.schema.1.json +2 -2
  64. package/package.json +2 -4
  65. package/ecagent/dist/agent-loop.d.ts.map +0 -1
  66. package/ecagent/dist/agent-loop.js.map +0 -1
  67. package/ecagent/dist/agent.d.ts.map +0 -1
  68. package/ecagent/dist/agent.js.map +0 -1
  69. package/ecagent/dist/harness/agent-harness.d.ts.map +0 -1
  70. package/ecagent/dist/harness/agent-harness.js.map +0 -1
  71. package/ecagent/dist/harness/compaction/branch-summarization.d.ts.map +0 -1
  72. package/ecagent/dist/harness/compaction/branch-summarization.js.map +0 -1
  73. package/ecagent/dist/harness/compaction/compaction.d.ts.map +0 -1
  74. package/ecagent/dist/harness/compaction/compaction.js.map +0 -1
  75. package/ecagent/dist/harness/compaction/utils.d.ts.map +0 -1
  76. package/ecagent/dist/harness/compaction/utils.js.map +0 -1
  77. package/ecagent/dist/harness/env/nodejs.d.ts.map +0 -1
  78. package/ecagent/dist/harness/env/nodejs.js.map +0 -1
  79. package/ecagent/dist/harness/messages.d.ts.map +0 -1
  80. package/ecagent/dist/harness/messages.js.map +0 -1
  81. package/ecagent/dist/harness/prompt-templates.d.ts.map +0 -1
  82. package/ecagent/dist/harness/prompt-templates.js.map +0 -1
  83. package/ecagent/dist/harness/session/jsonl-repo.d.ts.map +0 -1
  84. package/ecagent/dist/harness/session/jsonl-repo.js.map +0 -1
  85. package/ecagent/dist/harness/session/jsonl-storage.d.ts.map +0 -1
  86. package/ecagent/dist/harness/session/jsonl-storage.js.map +0 -1
  87. package/ecagent/dist/harness/session/memory-repo.d.ts.map +0 -1
  88. package/ecagent/dist/harness/session/memory-repo.js.map +0 -1
  89. package/ecagent/dist/harness/session/memory-storage.d.ts.map +0 -1
  90. package/ecagent/dist/harness/session/memory-storage.js.map +0 -1
  91. package/ecagent/dist/harness/session/repo-utils.d.ts.map +0 -1
  92. package/ecagent/dist/harness/session/repo-utils.js.map +0 -1
  93. package/ecagent/dist/harness/session/session.d.ts.map +0 -1
  94. package/ecagent/dist/harness/session/session.js.map +0 -1
  95. package/ecagent/dist/harness/session/uuid.d.ts.map +0 -1
  96. package/ecagent/dist/harness/session/uuid.js.map +0 -1
  97. package/ecagent/dist/harness/skills.d.ts.map +0 -1
  98. package/ecagent/dist/harness/skills.js.map +0 -1
  99. package/ecagent/dist/harness/system-prompt.d.ts.map +0 -1
  100. package/ecagent/dist/harness/system-prompt.js.map +0 -1
  101. package/ecagent/dist/harness/types.d.ts.map +0 -1
  102. package/ecagent/dist/harness/types.js.map +0 -1
  103. package/ecagent/dist/harness/utils/shell-output.d.ts.map +0 -1
  104. package/ecagent/dist/harness/utils/shell-output.js.map +0 -1
  105. package/ecagent/dist/harness/utils/truncate.d.ts.map +0 -1
  106. package/ecagent/dist/harness/utils/truncate.js.map +0 -1
  107. package/ecagent/dist/index.d.ts.map +0 -1
  108. package/ecagent/dist/index.js.map +0 -1
  109. package/ecagent/dist/node.d.ts.map +0 -1
  110. package/ecagent/dist/node.js.map +0 -1
  111. package/ecagent/dist/proxy.d.ts.map +0 -1
  112. package/ecagent/dist/proxy.js.map +0 -1
  113. package/ecagent/dist/runtime/api/lazy.d.ts.map +0 -1
  114. package/ecagent/dist/runtime/api/lazy.js.map +0 -1
  115. package/ecagent/dist/runtime/api/pi-messages.d.ts.map +0 -1
  116. package/ecagent/dist/runtime/api/pi-messages.js.map +0 -1
  117. package/ecagent/dist/runtime/api/pi-messages.lazy.d.ts.map +0 -1
  118. package/ecagent/dist/runtime/api/pi-messages.lazy.js.map +0 -1
  119. package/ecagent/dist/runtime/auth/context.d.ts.map +0 -1
  120. package/ecagent/dist/runtime/auth/context.js.map +0 -1
  121. package/ecagent/dist/runtime/auth/credential-store.d.ts.map +0 -1
  122. package/ecagent/dist/runtime/auth/credential-store.js.map +0 -1
  123. package/ecagent/dist/runtime/auth/helpers.d.ts.map +0 -1
  124. package/ecagent/dist/runtime/auth/helpers.js.map +0 -1
  125. package/ecagent/dist/runtime/auth/resolve.d.ts.map +0 -1
  126. package/ecagent/dist/runtime/auth/resolve.js.map +0 -1
  127. package/ecagent/dist/runtime/auth/types.d.ts.map +0 -1
  128. package/ecagent/dist/runtime/auth/types.js.map +0 -1
  129. package/ecagent/dist/runtime/compat.d.ts.map +0 -1
  130. package/ecagent/dist/runtime/compat.js.map +0 -1
  131. package/ecagent/dist/runtime/index.d.ts.map +0 -1
  132. package/ecagent/dist/runtime/index.js.map +0 -1
  133. package/ecagent/dist/runtime/models.d.ts.map +0 -1
  134. package/ecagent/dist/runtime/models.js.map +0 -1
  135. package/ecagent/dist/runtime/oauth.d.ts.map +0 -1
  136. package/ecagent/dist/runtime/oauth.js.map +0 -1
  137. package/ecagent/dist/runtime/providers/faux.d.ts.map +0 -1
  138. package/ecagent/dist/runtime/providers/faux.js.map +0 -1
  139. package/ecagent/dist/runtime/types.d.ts.map +0 -1
  140. package/ecagent/dist/runtime/types.js.map +0 -1
  141. package/ecagent/dist/runtime/utils/diagnostics.d.ts.map +0 -1
  142. package/ecagent/dist/runtime/utils/diagnostics.js.map +0 -1
  143. package/ecagent/dist/runtime/utils/event-stream.d.ts.map +0 -1
  144. package/ecagent/dist/runtime/utils/event-stream.js.map +0 -1
  145. package/ecagent/dist/runtime/utils/headers.d.ts.map +0 -1
  146. package/ecagent/dist/runtime/utils/headers.js.map +0 -1
  147. package/ecagent/dist/runtime/utils/json-parse.d.ts.map +0 -1
  148. package/ecagent/dist/runtime/utils/json-parse.js.map +0 -1
  149. package/ecagent/dist/runtime/utils/oauth/types.d.ts.map +0 -1
  150. package/ecagent/dist/runtime/utils/oauth/types.js.map +0 -1
  151. package/ecagent/dist/runtime/utils/overflow.d.ts.map +0 -1
  152. package/ecagent/dist/runtime/utils/overflow.js.map +0 -1
  153. package/ecagent/dist/runtime/utils/provider-env.d.ts.map +0 -1
  154. package/ecagent/dist/runtime/utils/provider-env.js.map +0 -1
  155. package/ecagent/dist/runtime/utils/retry.d.ts.map +0 -1
  156. package/ecagent/dist/runtime/utils/retry.js.map +0 -1
  157. package/ecagent/dist/runtime/utils/typebox-helpers.d.ts.map +0 -1
  158. package/ecagent/dist/runtime/utils/typebox-helpers.js.map +0 -1
  159. package/ecagent/dist/runtime/utils/validation.d.ts.map +0 -1
  160. package/ecagent/dist/runtime/utils/validation.js.map +0 -1
  161. package/ecagent/dist/types.d.ts.map +0 -1
  162. package/ecagent/dist/types.js.map +0 -1
  163. package/ecagent/package.json +0 -93
  164. package/ecagent/src/agent-loop.ts +0 -792
  165. package/ecagent/src/agent.ts +0 -575
  166. package/ecagent/src/harness/agent-harness.ts +0 -1029
  167. package/ecagent/src/harness/compaction/branch-summarization.ts +0 -261
  168. package/ecagent/src/harness/compaction/compaction.ts +0 -753
  169. package/ecagent/src/harness/compaction/utils.ts +0 -144
  170. package/ecagent/src/harness/env/nodejs.ts +0 -569
  171. package/ecagent/src/harness/messages.ts +0 -164
  172. package/ecagent/src/harness/prompt-templates.ts +0 -267
  173. package/ecagent/src/harness/session/jsonl-repo.ts +0 -179
  174. package/ecagent/src/harness/session/jsonl-storage.ts +0 -314
  175. package/ecagent/src/harness/session/memory-repo.ts +0 -50
  176. package/ecagent/src/harness/session/memory-storage.ts +0 -133
  177. package/ecagent/src/harness/session/repo-utils.ts +0 -51
  178. package/ecagent/src/harness/session/session.ts +0 -338
  179. package/ecagent/src/harness/session/uuid.ts +0 -54
  180. package/ecagent/src/harness/skills.ts +0 -375
  181. package/ecagent/src/harness/system-prompt.ts +0 -34
  182. package/ecagent/src/harness/types.ts +0 -838
  183. package/ecagent/src/harness/utils/shell-output.ts +0 -135
  184. package/ecagent/src/harness/utils/truncate.ts +0 -344
  185. package/ecagent/src/index.ts +0 -46
  186. package/ecagent/src/node.ts +0 -2
  187. package/ecagent/src/proxy.ts +0 -367
  188. package/ecagent/src/runtime/api/lazy.ts +0 -70
  189. package/ecagent/src/runtime/api/pi-messages.lazy.ts +0 -4
  190. package/ecagent/src/runtime/api/pi-messages.ts +0 -436
  191. package/ecagent/src/runtime/auth/context.ts +0 -45
  192. package/ecagent/src/runtime/auth/credential-store.ts +0 -47
  193. package/ecagent/src/runtime/auth/helpers.ts +0 -46
  194. package/ecagent/src/runtime/auth/resolve.ts +0 -141
  195. package/ecagent/src/runtime/auth/types.ts +0 -182
  196. package/ecagent/src/runtime/compat.ts +0 -158
  197. package/ecagent/src/runtime/index.ts +0 -31
  198. package/ecagent/src/runtime/models.ts +0 -452
  199. package/ecagent/src/runtime/oauth.ts +0 -1
  200. package/ecagent/src/runtime/providers/faux.ts +0 -538
  201. package/ecagent/src/runtime/types.ts +0 -718
  202. package/ecagent/src/runtime/utils/diagnostics.ts +0 -45
  203. package/ecagent/src/runtime/utils/event-stream.ts +0 -88
  204. package/ecagent/src/runtime/utils/headers.ts +0 -18
  205. package/ecagent/src/runtime/utils/json-parse.ts +0 -124
  206. package/ecagent/src/runtime/utils/oauth/types.ts +0 -79
  207. package/ecagent/src/runtime/utils/overflow.ts +0 -165
  208. package/ecagent/src/runtime/utils/provider-env.ts +0 -52
  209. package/ecagent/src/runtime/utils/retry.ts +0 -101
  210. package/ecagent/src/runtime/utils/typebox-helpers.ts +0 -24
  211. package/ecagent/src/runtime/utils/validation.ts +0 -310
  212. package/ecagent/src/types.ts +0 -430
  213. package/ecagent/tsconfig.build.json +0 -32
package/CHANGELOG.md CHANGED
@@ -3,6 +3,30 @@
3
3
  本文件记录 EvolCore 的重要变更。EvolClaw 版本线的历史记录已归档至
4
4
  [`docs/_archive/CHANGELOG-evolclaw.md`](docs/_archive/CHANGELOG-evolclaw.md)。
5
5
 
6
+ ## 0.0.9 (2026-08-06)
7
+
8
+ ### 安装与自启
9
+
10
+ - 支持在安装或初始化时配置登录自启,Windows 与 macOS 开机登录后自动拉起 EvolCore。
11
+ - 已完成初始化的安装可单独开关登录自启,无需重跑初始化流程。
12
+
13
+ ### AUN 通信
14
+
15
+ - 出站消息默认加密,可通过配置调整;观察者转发与回执一并遵循该设置。
16
+ - 未连接时的图片与消息发送进入待发队列,连接恢复后按序重投,不再直接丢弃。
17
+ - 完善图片上传的类型识别与结构化内容处理。
18
+
19
+ ### BaseAgent 配置
20
+
21
+ - 支持为 BaseAgent 自定义请求头和查询参数,便于对接需要额外标识的网关。
22
+ - 统一各 BaseAgent 的推理强度配置项,旧字段仍可识别。
23
+
24
+ ### 运行可靠性
25
+
26
+ - 修复 Agent 在启动阶段被中断时的状态残留。
27
+ - EC Web 版本过低时拒绝启动并提示升级命令,避免新旧组件搭配下的异常行为。
28
+ - 完善菜单协议的权限校验,并修正系统信息查询。
29
+
6
30
  ## 0.0.8 (2026-08-04)
7
31
 
8
32
  ### AID 与域名管理
package/README.md CHANGED
@@ -86,16 +86,16 @@ ResponseEngine.processMessage()
86
86
  **一行安装(macOS / Linux)**:
87
87
 
88
88
  ```bash
89
- curl -fsSL https://download.evolai.cn/install.sh | bash
89
+ curl -fsSL https://download.evolai.cn/releases/evolcore/2026-08-05.2/install.sh | bash
90
90
  ```
91
91
 
92
92
  **Windows PowerShell**:
93
93
 
94
94
  ```powershell
95
- irm https://download.evolai.cn/install.ps1 | iex
95
+ & ([scriptblock]::Create((irm https://download.evolai.cn/releases/evolcore/2026-08-05.2/install.ps1))) -AutoStart
96
96
  ```
97
97
 
98
- 安装器会自动进入 `ec init` 交互向导;只有已有配置同时包含控制 AID 和 Owner 时才跳过。绑定完成后运行 `ec start` `ec status`。安装器不会停止或重启服务,详细流程见 [一键安装脚本说明](docs/evolcore-install-script.md)。
98
+ 安装器会自动进入 `ec init` 交互向导;只有已有配置同时包含控制 AID 和 Owner 时才跳过。上面的 Windows 命令会在绑定完成后自动运行 `ec start`;macOS/Linux 需手动运行 `ec start`。安装器不会停止或重启已有服务,详细流程见 [一键安装脚本说明](docs/evolcore-install-script.md)。
99
99
 
100
100
  **npm 全局安装**:
101
101
 
@@ -174,6 +174,40 @@ ec init aun
174
174
  - `model`:配置文件 → `~/.claude/settings.json` → 默认 `sonnet`
175
175
  - `effort`:配置文件 → `~/.claude/settings.json` → SDK 默认值(`auto`)
176
176
 
177
+ Claude/Codex 兼容网关需要额外请求头或查询参数时,可以直接在 EvolCore 的
178
+ `baseagents` 配置中设置,不需要修改 Claude/Codex 的全局配置文件:
179
+
180
+ ```json
181
+ {
182
+ "baseagents": {
183
+ "claude": {
184
+ "baseUrl": "https://example.com/claude",
185
+ "headers": {
186
+ "X-Tenant-Id": "${TENANT_ID}"
187
+ },
188
+ "queryParams": {
189
+ "source": "evolcore"
190
+ }
191
+ },
192
+ "codex": {
193
+ "baseUrl": "https://example.com/codex",
194
+ "headers": {
195
+ "X-Tenant-Id": "${TENANT_ID}"
196
+ },
197
+ "queryParams": {
198
+ "source": "evolcore"
199
+ }
200
+ }
201
+ }
202
+ }
203
+ ```
204
+
205
+ `${VAR}` 会按 EvolCore 的环境变量规则展开。Claude 通过 `ANTHROPIC_CUSTOM_HEADERS`
206
+ 传递 Header;Codex app-server 启动时由 EvolCore 注入 `env_http_headers` 和
207
+ `query_params`,所以不需要手改 `~/.codex/config.toml`。`queryParams` 不应放置密钥等敏感信息。
208
+
209
+ 完整设计、运行链路和排障说明见:[Claude/Codex 自定义请求元数据技术文档](docs/baseagent-request-metadata.md)。
210
+
177
211
  ### 3. 运行
178
212
 
179
213
  ```bash
@@ -50,6 +50,35 @@ function directInferenceValue(...values) {
50
50
  }
51
51
  return undefined;
52
52
  }
53
+ const HTTP_HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
54
+ function normalizeStringMap(value, field) {
55
+ if (value === undefined || value === null)
56
+ return undefined;
57
+ if (typeof value !== 'object' || Array.isArray(value)) {
58
+ throw new Error(`${field} must be an object with string values`);
59
+ }
60
+ const entries = [];
61
+ for (const [key, item] of Object.entries(value)) {
62
+ if (!key)
63
+ throw new Error(`${field} names cannot be empty`);
64
+ if (typeof item !== 'string')
65
+ throw new Error(`${field}.${key} must be a string`);
66
+ if (field === 'headers') {
67
+ if (!HTTP_HEADER_NAME.test(key))
68
+ throw new Error(`Invalid HTTP header name: ${key}`);
69
+ if (/[\0\r\n]/.test(item))
70
+ throw new Error(`HTTP header ${key} cannot contain NUL or line breaks`);
71
+ }
72
+ entries.push([key, item]);
73
+ }
74
+ return entries.length > 0 ? Object.fromEntries(entries) : undefined;
75
+ }
76
+ function normalizeRequestMetadata(value) {
77
+ return {
78
+ headers: normalizeStringMap(value?.headers, 'headers'),
79
+ queryParams: normalizeStringMap(value?.queryParams, 'queryParams'),
80
+ };
81
+ }
53
82
  /** Claude Code accepts both names; AUTH_TOKEN is kept first for compatibility. */
54
83
  function resolveAnthropicApiKey(...values) {
55
84
  return directInferenceValue(...values);
@@ -71,13 +100,27 @@ function loadClaudeSettings() {
71
100
  */
72
101
  export function resolveAnthropicDirectConfig(override) {
73
102
  const explicit = directInferencePair(override?.apiKey, override?.baseUrl);
74
- if (explicit)
75
- return explicit;
103
+ if (explicit) {
104
+ return {
105
+ ...explicit,
106
+ ...normalizeRequestMetadata(override),
107
+ };
108
+ }
76
109
  const environment = directInferencePair(directInferenceValue(process.env.ANTHROPIC_AUTH_TOKEN, process.env.ANTHROPIC_API_KEY), process.env.ANTHROPIC_BASE_URL);
77
- if (environment)
78
- return environment;
110
+ if (environment) {
111
+ return {
112
+ ...environment,
113
+ ...normalizeRequestMetadata(override),
114
+ };
115
+ }
79
116
  const settings = loadClaudeSettings();
80
- return directInferencePair(directInferenceValue(settings.env?.ANTHROPIC_AUTH_TOKEN, settings.env?.ANTHROPIC_API_KEY), settings.env?.ANTHROPIC_BASE_URL);
117
+ const settingsResolved = directInferencePair(directInferenceValue(settings.env?.ANTHROPIC_AUTH_TOKEN, settings.env?.ANTHROPIC_API_KEY), settings.env?.ANTHROPIC_BASE_URL);
118
+ return settingsResolved
119
+ ? {
120
+ ...settingsResolved,
121
+ ...normalizeRequestMetadata(override),
122
+ }
123
+ : undefined;
81
124
  }
82
125
  export function resolveAnthropicConfig(config, override) {
83
126
  const settings = loadClaudeSettings();
@@ -95,6 +138,8 @@ export function resolveAnthropicConfig(config, override) {
95
138
  || globalBaseUrl
96
139
  || process.env.ANTHROPIC_BASE_URL
97
140
  || settings.env?.ANTHROPIC_BASE_URL;
141
+ const headers = normalizeStringMap(override?.headers ?? config.agents?.claude?.headers, 'headers');
142
+ const queryParams = normalizeStringMap(override?.queryParams ?? config.agents?.claude?.queryParams, 'queryParams');
98
143
  const model = override?.model
99
144
  || config.agents?.claude?.model
100
145
  || settings.model
@@ -106,19 +151,22 @@ export function resolveAnthropicConfig(config, override) {
106
151
  const pickExec = (v) => (!v || v.includes('your-') || v.includes('placeholder')) ? undefined : v;
107
152
  const pathToClaudeCodeExecutable = pickExec(override?.pathToClaudeCodeExecutable)
108
153
  || pickExec(config.agents?.claude?.pathToClaudeCodeExecutable);
109
- return { apiKey, baseUrl, model, effort, pathToClaudeCodeExecutable };
154
+ return { apiKey, baseUrl, headers, queryParams, model, effort, pathToClaudeCodeExecutable };
110
155
  }
111
156
  function loadCodexSettings() {
112
157
  try {
113
- const authPath = path.join(os.homedir(), '.codex', 'auth.json');
158
+ const configuredHome = process.env.CODEX_HOME?.trim();
159
+ const codexHome = configuredHome ? path.resolve(configuredHome) : path.join(os.homedir(), '.codex');
160
+ const authPath = path.join(codexHome, 'auth.json');
114
161
  let apiKey;
115
162
  if (fs.existsSync(authPath)) {
116
163
  const auth = JSON.parse(fs.readFileSync(authPath, 'utf-8'));
117
164
  apiKey = auth.OPENAI_API_KEY;
118
165
  }
119
- const configPath = path.join(os.homedir(), '.codex', 'config.toml');
166
+ const configPath = path.join(codexHome, 'config.toml');
120
167
  let model;
121
168
  let baseUrl;
169
+ let provider;
122
170
  if (fs.existsSync(configPath)) {
123
171
  const content = fs.readFileSync(configPath, 'utf-8');
124
172
  const modelMatch = content.match(/^model\s*=\s*"([^"]+)"/m);
@@ -126,17 +174,35 @@ function loadCodexSettings() {
126
174
  model = modelMatch[1];
127
175
  const providerMatch = content.match(/^model_provider\s*=\s*"([^"]+)"/m);
128
176
  if (providerMatch) {
129
- const provider = providerMatch[1];
130
- const baseUrlMatch = content.match(new RegExp(`\\[model_providers\\.${provider}\\][\\s\\S]*?base_url\\s*=\\s*"([^"]+)"`, 'm'));
131
- if (baseUrlMatch)
132
- baseUrl = baseUrlMatch[1];
177
+ provider = providerMatch[1];
178
+ baseUrl = readCodexProviderBaseUrl(content, provider);
133
179
  }
134
180
  }
135
- return { apiKey, baseUrl, model };
181
+ return { apiKey, baseUrl, model, provider };
136
182
  }
137
183
  catch { }
138
184
  return {};
139
185
  }
186
+ function escapeRegExp(value) {
187
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
188
+ }
189
+ function readCodexProviderBaseUrl(content, provider) {
190
+ const segments = /^[A-Za-z0-9_-]+$/.test(provider)
191
+ ? [provider, JSON.stringify(provider)]
192
+ : [JSON.stringify(provider)];
193
+ for (const segment of segments) {
194
+ const section = new RegExp(`^\\s*\\[\\s*model_providers\\s*\\.\\s*${escapeRegExp(segment)}\\s*\\]\\s*(?:#.*)?$`, 'm').exec(content);
195
+ if (!section || section.index === undefined)
196
+ continue;
197
+ const tail = content.slice(section.index + section[0].length);
198
+ const nextSection = tail.search(/^\s*\[/m);
199
+ const block = nextSection >= 0 ? tail.slice(0, nextSection) : tail;
200
+ const baseUrl = block.match(/^\s*base_url\s*=\s*"([^"]+)"/m)?.[1];
201
+ if (baseUrl)
202
+ return baseUrl;
203
+ }
204
+ return undefined;
205
+ }
140
206
  /**
141
207
  * Resolve a complete credential/endpoint pair for direct OpenAI HTTP calls.
142
208
  * auth.json and config.toml together form the Codex SDK source; fields are
@@ -144,13 +210,27 @@ function loadCodexSettings() {
144
210
  */
145
211
  export function resolveOpenaiDirectConfig(override) {
146
212
  const explicit = directInferencePair(override?.apiKey, override?.baseUrl);
147
- if (explicit)
148
- return explicit;
213
+ if (explicit) {
214
+ return {
215
+ ...explicit,
216
+ ...normalizeRequestMetadata(override),
217
+ };
218
+ }
149
219
  const environment = directInferencePair(process.env.OPENAI_API_KEY, process.env.OPENAI_BASE_URL);
150
- if (environment)
151
- return environment;
220
+ if (environment) {
221
+ return {
222
+ ...environment,
223
+ ...normalizeRequestMetadata(override),
224
+ };
225
+ }
152
226
  const settings = loadCodexSettings();
153
- return directInferencePair(settings.apiKey, settings.baseUrl);
227
+ const settingsResolved = directInferencePair(settings.apiKey, settings.baseUrl);
228
+ return settingsResolved
229
+ ? {
230
+ ...settingsResolved,
231
+ ...normalizeRequestMetadata(override),
232
+ }
233
+ : undefined;
154
234
  }
155
235
  export function resolveOpenaiConfig(config, override) {
156
236
  const codexSettings = loadCodexSettings();
@@ -172,6 +252,8 @@ export function resolveOpenaiConfig(config, override) {
172
252
  || process.env.OPENAI_BASE_URL
173
253
  || codexSettings.baseUrl
174
254
  || undefined;
255
+ const headers = normalizeStringMap(override?.headers ?? config.agents?.codex?.headers, 'headers');
256
+ const queryParams = normalizeStringMap(override?.queryParams ?? config.agents?.codex?.queryParams, 'queryParams');
175
257
  const model = override?.model
176
258
  || config.agents?.codex?.model
177
259
  || codexSettings.model
@@ -187,7 +269,17 @@ export function resolveOpenaiConfig(config, override) {
187
269
  const approvalsReviewer = override?.approvalsReviewer
188
270
  ?? config.agents?.codex?.approvalsReviewer
189
271
  ?? undefined;
190
- return { apiKey, baseUrl, model, effort, enableRequestUserInput, approvalsReviewer };
272
+ return {
273
+ apiKey,
274
+ baseUrl,
275
+ provider: codexSettings.provider,
276
+ headers,
277
+ queryParams,
278
+ model,
279
+ effort,
280
+ enableRequestUserInput,
281
+ approvalsReviewer,
282
+ };
191
283
  }
192
284
  export function resolveGoogleConfig(config, override) {
193
285
  const googleCfg = config.agents?.gemini;
@@ -65,6 +65,19 @@ function isClaudeMcpTool(toolName) {
65
65
  function isUnknownClaudeTool(toolName) {
66
66
  return !isClaudeMcpTool(toolName) && !CLAUDE_BUILTIN_TOOLS.has(toolName);
67
67
  }
68
+ function appendQueryParams(baseUrl, queryParams) {
69
+ if (!baseUrl || !queryParams || Object.keys(queryParams).length === 0)
70
+ return baseUrl;
71
+ try {
72
+ const parsed = new URL(baseUrl);
73
+ for (const [name, value] of Object.entries(queryParams))
74
+ parsed.searchParams.set(name, value);
75
+ return parsed.toString();
76
+ }
77
+ catch {
78
+ return baseUrl;
79
+ }
80
+ }
68
81
  function stableClaudePermissionValue(value, depth = 0) {
69
82
  if (depth > 8)
70
83
  return '[truncated]';
@@ -529,6 +542,8 @@ export class AgentRunner {
529
542
  effort;
530
543
  permissionMode = DEFAULT_PERMISSION_MODE;
531
544
  baseUrl;
545
+ customHeaders;
546
+ customQueryParams;
532
547
  config;
533
548
  activeSessions = new Map();
534
549
  activeStreams = new Map();
@@ -556,16 +571,22 @@ export class AgentRunner {
556
571
  if (config) {
557
572
  const anthropic = resolveAnthropicConfig(config);
558
573
  this.claudeExecutablePath = anthropic.pathToClaudeCodeExecutable;
574
+ this.customHeaders = anthropic.headers;
575
+ this.customQueryParams = anthropic.queryParams;
559
576
  }
560
577
  }
561
578
  getAgentEnv(runtimeEnv, evolcoreSessionId) {
579
+ const anthropicBaseUrl = appendQueryParams(this.baseUrl, this.customQueryParams);
562
580
  const env = {
563
581
  ...process.env,
564
582
  ANTHROPIC_AUTH_TOKEN: this.apiKey,
565
583
  ANTHROPIC_API_KEY: this.apiKey,
566
584
  PATH: process.env.PATH,
567
585
  DISABLE_AUTOUPDATER: '1',
568
- ...(this.baseUrl ? { ANTHROPIC_BASE_URL: this.baseUrl } : {}),
586
+ ...(anthropicBaseUrl ? { ANTHROPIC_BASE_URL: anthropicBaseUrl } : {}),
587
+ ...(this.customHeaders
588
+ ? { ANTHROPIC_CUSTOM_HEADERS: Object.entries(this.customHeaders).map(([name, value]) => `${name}: ${value}`).join('\n') }
589
+ : {}),
569
590
  ...(evolcoreSessionId ? { EVOLCORE_SESSION_ID: evolcoreSessionId } : {}),
570
591
  ...(runtimeEnv ?? {}),
571
592
  };
@@ -2298,6 +2319,7 @@ export class AgentRunner {
2298
2319
  })();
2299
2320
  }
2300
2321
  async interrupt(sessionId) {
2322
+ const streamRegistered = this.activeStreams.has(sessionId);
2301
2323
  const fn = this.interruptFns.get(sessionId);
2302
2324
  const query = this.activeQueries.get(sessionId);
2303
2325
  const cancelledInputIds = [];
@@ -2327,7 +2349,10 @@ export class AgentRunner {
2327
2349
  if (query && typeof query.close === 'function')
2328
2350
  query.close();
2329
2351
  const done = this.streamDone.get(sessionId);
2330
- if (done) {
2352
+ // A stream interrupted after runQuery resolves but before registerStream has
2353
+ // no consumer that can enter the wrapper generator's finally block. Waiting
2354
+ // for streamDone here would always burn the full timeout.
2355
+ if (done && streamRegistered) {
2331
2356
  await Promise.race([done, new Promise(resolve => setTimeout(resolve, 5_000))]);
2332
2357
  }
2333
2358
  this.interruptFns.delete(sessionId);
@@ -2343,6 +2368,9 @@ export class AgentRunner {
2343
2368
  cleanupStream(sessionId) {
2344
2369
  this.activeMessageStreams.get(sessionId)?.end();
2345
2370
  this.activeMessageStreams.delete(sessionId);
2371
+ this.streamDoneResolvers.get(sessionId)?.();
2372
+ this.streamDoneResolvers.delete(sessionId);
2373
+ this.streamDone.delete(sessionId);
2346
2374
  this.activeStreams.delete(sessionId);
2347
2375
  this.interruptFns.delete(sessionId);
2348
2376
  this.activeQueries.delete(sessionId);
@@ -3,6 +3,22 @@ import readline from 'readline';
3
3
  import { logger } from '../utils/logger.js';
4
4
  import { buildHClassGuardCommand } from '../core/permission/sandbox-runtime.js';
5
5
  import { resolveCodexLaunchCommand } from '../utils/codex-cli.js';
6
+ import { createHash } from 'crypto';
7
+ function tomlProviderKey(value) {
8
+ if (!value || /[\0\r\n]/.test(value)) {
9
+ throw new Error(`Invalid Codex provider name: ${value}`);
10
+ }
11
+ return /^[A-Za-z0-9_-]+$/.test(value) ? value : JSON.stringify(value);
12
+ }
13
+ function tomlInlineStringTable(values) {
14
+ return `{${Object.entries(values)
15
+ .map(([name, value]) => `${JSON.stringify(name)}=${JSON.stringify(value)}`)
16
+ .join(',')}}`;
17
+ }
18
+ function codexHeaderEnvName(headerName) {
19
+ const digest = createHash('sha256').update(headerName).digest('hex').slice(0, 16).toUpperCase();
20
+ return `EVOLCORE_CODEX_HEADER_${digest}`;
21
+ }
6
22
  function hasPermissionProfile(config) {
7
23
  if (!config || typeof config.default_permissions !== 'string' || !config.default_permissions)
8
24
  return false;
@@ -327,6 +343,9 @@ export class CodexAppServerClient {
327
343
  startProcess() {
328
344
  const env = { ...process.env, ...this.options.env };
329
345
  env.CODEX_API_KEY = this.options.apiKey;
346
+ for (const [name, value] of Object.entries(this.options.headers ?? {})) {
347
+ env[codexHeaderEnvName(name)] = value;
348
+ }
330
349
  if (this.options.baseUrl) {
331
350
  env.OPENAI_BASE_URL = this.options.baseUrl;
332
351
  }
@@ -367,6 +386,18 @@ export class CodexAppServerClient {
367
386
  if (this.options.baseUrl) {
368
387
  args.push('--config', `openai_base_url=${JSON.stringify(this.options.baseUrl)}`);
369
388
  }
389
+ const headerEnvNames = Object.fromEntries(Object.keys(this.options.headers ?? {}).map(name => [name, codexHeaderEnvName(name)]));
390
+ const hasHeaders = Object.keys(headerEnvNames).length > 0;
391
+ const hasQueryParams = Object.keys(this.options.queryParams ?? {}).length > 0;
392
+ if (hasHeaders || hasQueryParams) {
393
+ const provider = tomlProviderKey(this.options.provider || 'openai');
394
+ if (hasHeaders) {
395
+ args.push('--config', `model_providers.${provider}.env_http_headers=${tomlInlineStringTable(headerEnvNames)}`);
396
+ }
397
+ if (hasQueryParams) {
398
+ args.push('--config', `model_providers.${provider}.query_params=${tomlInlineStringTable(this.options.queryParams ?? {})}`);
399
+ }
400
+ }
370
401
  if (this.options.effort) {
371
402
  args.push('--config', `model_reasoning_effort=${JSON.stringify(this.options.effort)}`);
372
403
  }
@@ -255,6 +255,9 @@ export class CodexRunner {
255
255
  const client = new CodexAppServerClient({
256
256
  apiKey: this.resolvedConfig.apiKey,
257
257
  baseUrl: this.resolvedConfig.baseUrl,
258
+ provider: this.resolvedConfig.provider,
259
+ headers: this.resolvedConfig.headers,
260
+ queryParams: this.resolvedConfig.queryParams,
258
261
  model: this.model,
259
262
  effort: this.effort,
260
263
  enableRequestUserInput: this.resolvedConfig.enableRequestUserInput,
@@ -5,7 +5,7 @@ import { TextDecoder } from 'util';
5
5
  import { createShortConnection } from '../rpc/index.js';
6
6
  import { getAidStore, loadClient, SLOT } from '../aid/store.js';
7
7
  import { uploadFileAndBuildPayload } from './upload.js';
8
- import { daemonTaskSendIpcTimeoutMs } from './p2p.js';
8
+ import { daemonTaskSendIpcTimeoutMs, resolveAunMessageEncrypt } from './p2p.js';
9
9
  import { checkGroupIndex, getGroupIndex } from './group-index.js';
10
10
  import { appendMessageLog, buildOutboundEntry, classifyAunPayloadForLog } from '../../core/message/message-log.js';
11
11
  import { chatDirPath } from '../../core/session/session-fs-store.js';
@@ -50,7 +50,7 @@ function groupSendLogMetadata(args, payload, runtimeContext) {
50
50
  source: (runtimeContext ? 'msg' : 'cli'),
51
51
  };
52
52
  }
53
- function appendGroupSendOutboundLog(args, messageId, payload, runtimeContext) {
53
+ function appendGroupSendOutboundLog(args, messageId, payload, runtimeContext, encrypt = resolveAunMessageEncrypt(args.encrypt)) {
54
54
  const log = groupSendLogMetadata(args, payload, runtimeContext);
55
55
  const chatDir = chatDirPath(resolvePaths().sessionsDir, 'aun', args.groupId, args.from);
56
56
  fs.mkdirSync(chatDir, { recursive: true });
@@ -63,7 +63,7 @@ function appendGroupSendOutboundLog(args, messageId, payload, runtimeContext) {
63
63
  groupId: args.groupId,
64
64
  msgId: messageId,
65
65
  content: log.content,
66
- encrypt: args.encrypt === true,
66
+ encrypt,
67
67
  msgType: log.msgType,
68
68
  payloadType: log.payloadType,
69
69
  payloadSummary: log.payloadSummary,
@@ -95,7 +95,7 @@ async function tryDaemonGroupSend(args, payload, runtimeContext, file) {
95
95
  scope: 'group',
96
96
  ...(payload ? { payload } : { file }),
97
97
  mentions: args.mentions,
98
- encrypt: args.encrypt === true,
98
+ encrypt: args.encrypt,
99
99
  thread,
100
100
  returnPolicy: args.returnPolicy,
101
101
  originSessionId: runtimeContext.sessionId,
@@ -135,7 +135,7 @@ async function daemonGroupSendResult(args, payload, runtimeContext, result) {
135
135
  const messageId = result.message_id
136
136
  ?? (typeof result.message.message_id === 'string' ? result.message.message_id : undefined);
137
137
  if (messageId && !result.log_written) {
138
- appendGroupSendOutboundLog(args, messageId, payload, runtimeContext);
138
+ appendGroupSendOutboundLog(args, messageId, payload, runtimeContext, result.encrypt ?? resolveAunMessageEncrypt(args.encrypt));
139
139
  }
140
140
  return {
141
141
  ok: true,
@@ -193,12 +193,12 @@ export async function groupSend(args) {
193
193
  payload.mentions = args.mentions;
194
194
  }
195
195
  applyGroupRoutingPayloadFields(payload, args, runtimeContext);
196
- const sendParams = { group_id: args.groupId, payload };
197
- sendParams.encrypt = args.encrypt === true;
196
+ const encrypt = resolveAunMessageEncrypt(args.encrypt);
197
+ const sendParams = { group_id: args.groupId, payload, encrypt };
198
198
  const result = await conn.call('group.send', sendParams);
199
199
  const messageId = typeof result?.message?.message_id === 'string' ? result.message.message_id : undefined;
200
200
  if (messageId)
201
- appendGroupSendOutboundLog(args, messageId, payload, runtimeContext);
201
+ appendGroupSendOutboundLog(args, messageId, payload, runtimeContext, encrypt);
202
202
  return {
203
203
  ok: true,
204
204
  group_id: result?.group_id ?? args.groupId,
@@ -954,7 +954,7 @@ async function sendGroupRulesUpdatedNotice(client, args) {
954
954
  const result = await client.group.send({
955
955
  group_id: args.groupId,
956
956
  payload,
957
- encrypt: false,
957
+ encrypt: resolveAunMessageEncrypt(),
958
958
  });
959
959
  const message = isRecord(result.message) ? result.message : {};
960
960
  const messageId = typeof result.message_id === 'string'
@@ -9,6 +9,7 @@ import { chatDirPath } from '../../core/session/session-fs-store.js';
9
9
  import { resolvePaths } from '../../paths.js';
10
10
  import { ipcQuery } from '../../ipc.js';
11
11
  import { AGENT_DELEGATION_TOKEN_ENV } from '../../core/auth/agent-delegation.js';
12
+ import { loadDaemonConfig } from '../../config-store.js';
12
13
  const DEFAULT_TASK_EXECUTION_MS = 60 * 60 * 1000;
13
14
  const DAEMON_SEND_TIMEOUT_GRACE_MS = 10_000;
14
15
  const MAX_TIMER_MS = 2_147_483_647;
@@ -24,6 +25,11 @@ export function daemonTaskSendIpcTimeoutMs(file = false) {
24
25
  catch { }
25
26
  return Math.min(MAX_TIMER_MS, Math.max(file ? 120_000 : 5_000, executionMs + DAEMON_SEND_TIMEOUT_GRACE_MS));
26
27
  }
28
+ export function resolveAunMessageEncrypt(explicit) {
29
+ if (typeof explicit === 'boolean')
30
+ return explicit;
31
+ return loadDaemonConfig().aun?.defaultEncrypt ?? true;
32
+ }
27
33
  function buildSimplePayload(body) {
28
34
  switch (body.mode) {
29
35
  case 'text':
@@ -113,7 +119,7 @@ async function tryDaemonMsgSend(args, payload, runtimeContext, file) {
113
119
  to: args.to,
114
120
  scope: 'msg',
115
121
  ...(payload ? { payload } : { file }),
116
- encrypt: args.encrypt === true,
122
+ encrypt: args.encrypt,
117
123
  thread,
118
124
  returnPolicy: args.returnPolicy,
119
125
  originSessionId: runtimeContext.sessionId,
@@ -155,7 +161,7 @@ async function daemonMsgSendResult(args, payload, runtimeContext, daemonResult)
155
161
  payload,
156
162
  runtimeContext,
157
163
  chatmode: daemonResult.chatmode,
158
- encrypt: daemonResult.encrypt ?? args.encrypt === true,
164
+ encrypt: daemonResult.encrypt ?? resolveAunMessageEncrypt(args.encrypt),
159
165
  });
160
166
  }
161
167
  return sent;
@@ -259,9 +265,8 @@ export async function msgSend(args) {
259
265
  // 5. 写入 payload.thread_id/ref_message_id(如果指定)
260
266
  applyRoutingPayloadFields(payload, args, runtimeContext, false);
261
267
  applyRoutingPayloadFields(payload, args, runtimeContext);
262
- const sendParams = { to: args.to, payload };
263
- // Default: plaintext. Set encrypt: true to enable E2EE.
264
- sendParams.encrypt = args.encrypt === true;
268
+ const encrypt = resolveAunMessageEncrypt(args.encrypt);
269
+ const sendParams = { to: args.to, payload, encrypt };
265
270
  const result = await conn.call('message.send', sendParams);
266
271
  // 5. 写出方向 jsonl(与 daemon 一致格式,标记 source)
267
272
  // source 标记:
@@ -273,7 +278,7 @@ export async function msgSend(args) {
273
278
  payload,
274
279
  runtimeContext,
275
280
  chatmode,
276
- encrypt: args.encrypt === true,
281
+ encrypt,
277
282
  });
278
283
  }
279
284
  catch { }