@di-code/coding-agent 0.1.6 → 0.1.8

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 (338) hide show
  1. package/README.md +112 -68
  2. package/compositions/base.yml +54 -0
  3. package/compositions/interactive.yml +14 -0
  4. package/compositions/json.yml +6 -0
  5. package/compositions/print.yml +5 -0
  6. package/compositions/rpc.yml +12 -0
  7. package/compositions/webui.yml +9 -0
  8. package/dist/cli.d.ts +22 -1
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cli.js +97 -3
  11. package/dist/cli.js.map +1 -1
  12. package/dist/composition-loader-entry.d.ts +11 -0
  13. package/dist/composition-loader-entry.d.ts.map +1 -0
  14. package/dist/composition-loader-entry.js +12 -0
  15. package/dist/composition-loader-entry.js.map +1 -0
  16. package/dist/compositions.d.ts +32 -0
  17. package/dist/compositions.d.ts.map +1 -0
  18. package/dist/compositions.js +173 -0
  19. package/dist/compositions.js.map +1 -0
  20. package/dist/core/session/session-manager.d.ts +9 -2
  21. package/dist/core/session/session-manager.d.ts.map +1 -1
  22. package/dist/core/session/session-manager.js +28 -0
  23. package/dist/core/session/session-manager.js.map +1 -1
  24. package/dist/core/session/session-storage.d.ts.map +1 -1
  25. package/dist/core/session/session-storage.js +20 -3
  26. package/dist/core/session/session-storage.js.map +1 -1
  27. package/dist/core/session/types.d.ts +11 -2
  28. package/dist/core/session/types.d.ts.map +1 -1
  29. package/dist/core/session/types.js.map +1 -1
  30. package/dist/core/session-tools.d.ts +5 -0
  31. package/dist/core/session-tools.d.ts.map +1 -0
  32. package/dist/core/session-tools.js +17 -0
  33. package/dist/core/session-tools.js.map +1 -0
  34. package/dist/core/session.d.ts +21 -7
  35. package/dist/core/session.d.ts.map +1 -1
  36. package/dist/core/session.js +77 -41
  37. package/dist/core/session.js.map +1 -1
  38. package/dist/core/tools/bash.d.ts +1 -38
  39. package/dist/core/tools/bash.d.ts.map +1 -1
  40. package/dist/core/tools/bash.js +1 -207
  41. package/dist/core/tools/bash.js.map +1 -1
  42. package/dist/core/tools/edit-diff.d.ts +1 -24
  43. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  44. package/dist/core/tools/edit-diff.js +1 -135
  45. package/dist/core/tools/edit-diff.js.map +1 -1
  46. package/dist/core/tools/edit.d.ts +1 -26
  47. package/dist/core/tools/edit.d.ts.map +1 -1
  48. package/dist/core/tools/edit.js +1 -140
  49. package/dist/core/tools/edit.js.map +1 -1
  50. package/dist/core/tools/file-mutation-queue.d.ts +1 -1
  51. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  52. package/dist/core/tools/file-mutation-queue.js +1 -23
  53. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  54. package/dist/core/tools/file-search.d.ts +1 -13
  55. package/dist/core/tools/file-search.d.ts.map +1 -1
  56. package/dist/core/tools/file-search.js +1 -98
  57. package/dist/core/tools/file-search.js.map +1 -1
  58. package/dist/core/tools/glob.d.ts +1 -10
  59. package/dist/core/tools/glob.d.ts.map +1 -1
  60. package/dist/core/tools/glob.js +1 -43
  61. package/dist/core/tools/glob.js.map +1 -1
  62. package/dist/core/tools/grep.d.ts +1 -12
  63. package/dist/core/tools/grep.d.ts.map +1 -1
  64. package/dist/core/tools/grep.js +1 -66
  65. package/dist/core/tools/grep.js.map +1 -1
  66. package/dist/core/tools/load-skill.d.ts +1 -9
  67. package/dist/core/tools/load-skill.d.ts.map +1 -1
  68. package/dist/core/tools/load-skill.js +1 -21
  69. package/dist/core/tools/load-skill.js.map +1 -1
  70. package/dist/core/tools/path-boundary.d.ts +1 -4
  71. package/dist/core/tools/path-boundary.d.ts.map +1 -1
  72. package/dist/core/tools/path-boundary.js +1 -65
  73. package/dist/core/tools/path-boundary.js.map +1 -1
  74. package/dist/core/tools/read.d.ts +1 -16
  75. package/dist/core/tools/read.d.ts.map +1 -1
  76. package/dist/core/tools/read.js +1 -131
  77. package/dist/core/tools/read.js.map +1 -1
  78. package/dist/core/tools/write.d.ts +1 -16
  79. package/dist/core/tools/write.d.ts.map +1 -1
  80. package/dist/core/tools/write.js +1 -44
  81. package/dist/core/tools/write.js.map +1 -1
  82. package/dist/entry.js +30 -28
  83. package/dist/entry.js.map +1 -1
  84. package/dist/i18n.d.ts.map +1 -1
  85. package/dist/i18n.js +2 -0
  86. package/dist/i18n.js.map +1 -1
  87. package/dist/index.d.ts +6 -3
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js +6 -3
  90. package/dist/index.js.map +1 -1
  91. package/dist/interactive/builtin.d.ts +17 -0
  92. package/dist/interactive/builtin.d.ts.map +1 -0
  93. package/dist/interactive/builtin.js +47 -0
  94. package/dist/interactive/builtin.js.map +1 -0
  95. package/dist/interactive/controller.d.ts +85 -0
  96. package/dist/interactive/controller.d.ts.map +1 -0
  97. package/dist/interactive/controller.js +211 -0
  98. package/dist/interactive/controller.js.map +1 -0
  99. package/dist/interactive/index.d.ts +3 -0
  100. package/dist/interactive/index.d.ts.map +1 -0
  101. package/dist/interactive/index.js +3 -0
  102. package/dist/interactive/index.js.map +1 -0
  103. package/dist/interactive-host-entry.d.ts +7 -0
  104. package/dist/interactive-host-entry.d.ts.map +1 -0
  105. package/dist/interactive-host-entry.js +123 -0
  106. package/dist/interactive-host-entry.js.map +1 -0
  107. package/dist/interactive-profile.d.ts +18 -0
  108. package/dist/interactive-profile.d.ts.map +1 -0
  109. package/dist/interactive-profile.js +290 -0
  110. package/dist/interactive-profile.js.map +1 -0
  111. package/dist/interactive-resources-entry.d.ts +7 -0
  112. package/dist/interactive-resources-entry.d.ts.map +1 -0
  113. package/dist/interactive-resources-entry.js +42 -0
  114. package/dist/interactive-resources-entry.js.map +1 -0
  115. package/dist/legacy-main.d.ts +39 -0
  116. package/dist/legacy-main.d.ts.map +1 -0
  117. package/dist/legacy-main.js +611 -0
  118. package/dist/legacy-main.js.map +1 -0
  119. package/dist/main.d.ts +15 -34
  120. package/dist/main.d.ts.map +1 -1
  121. package/dist/main.js +130 -513
  122. package/dist/main.js.map +1 -1
  123. package/dist/mcp/entries.d.ts +42 -0
  124. package/dist/mcp/entries.d.ts.map +1 -0
  125. package/dist/mcp/entries.js +98 -0
  126. package/dist/mcp/entries.js.map +1 -0
  127. package/dist/mcp-client-entry.d.ts +5 -0
  128. package/dist/mcp-client-entry.d.ts.map +1 -0
  129. package/dist/mcp-client-entry.js +6 -0
  130. package/dist/mcp-client-entry.js.map +1 -0
  131. package/dist/mcp-config-entry.d.ts +5 -0
  132. package/dist/mcp-config-entry.d.ts.map +1 -0
  133. package/dist/mcp-config-entry.js +6 -0
  134. package/dist/mcp-config-entry.js.map +1 -0
  135. package/dist/mcp-tools-entry.d.ts +5 -0
  136. package/dist/mcp-tools-entry.d.ts.map +1 -0
  137. package/dist/mcp-tools-entry.js +6 -0
  138. package/dist/mcp-tools-entry.js.map +1 -0
  139. package/dist/mcp-transport-entry.d.ts +5 -0
  140. package/dist/mcp-transport-entry.d.ts.map +1 -0
  141. package/dist/mcp-transport-entry.js +6 -0
  142. package/dist/mcp-transport-entry.js.map +1 -0
  143. package/dist/modes/interactive-entry.d.ts +18 -0
  144. package/dist/modes/interactive-entry.d.ts.map +1 -0
  145. package/dist/modes/interactive-entry.js +21 -0
  146. package/dist/modes/interactive-entry.js.map +1 -0
  147. package/dist/modes/interactive-state.js +1 -1
  148. package/dist/modes/interactive-state.js.map +1 -1
  149. package/dist/modes/interactive.d.ts +16 -6
  150. package/dist/modes/interactive.d.ts.map +1 -1
  151. package/dist/modes/interactive.js +99 -33
  152. package/dist/modes/interactive.js.map +1 -1
  153. package/dist/modes/json-entry.d.ts +13 -0
  154. package/dist/modes/json-entry.d.ts.map +1 -0
  155. package/dist/modes/json-entry.js +14 -0
  156. package/dist/modes/json-entry.js.map +1 -0
  157. package/dist/modes/json.d.ts +4 -1
  158. package/dist/modes/json.d.ts.map +1 -1
  159. package/dist/modes/json.js +3 -3
  160. package/dist/modes/json.js.map +1 -1
  161. package/dist/modes/print-entry.d.ts +10 -0
  162. package/dist/modes/print-entry.d.ts.map +1 -0
  163. package/dist/modes/print-entry.js +11 -0
  164. package/dist/modes/print-entry.js.map +1 -0
  165. package/dist/modes/tree-selector.d.ts.map +1 -1
  166. package/dist/modes/tree-selector.js +4 -0
  167. package/dist/modes/tree-selector.js.map +1 -1
  168. package/dist/plugin-dump-composition.d.ts +6 -0
  169. package/dist/plugin-dump-composition.d.ts.map +1 -0
  170. package/dist/plugin-dump-composition.js +7 -0
  171. package/dist/plugin-dump-composition.js.map +1 -0
  172. package/dist/plugin-inventory.d.ts +7 -0
  173. package/dist/plugin-inventory.d.ts.map +1 -0
  174. package/dist/plugin-inventory.js +8 -0
  175. package/dist/plugin-inventory.js.map +1 -0
  176. package/dist/plugin-manager.d.ts +6 -0
  177. package/dist/plugin-manager.d.ts.map +1 -0
  178. package/dist/plugin-manager.js +7 -0
  179. package/dist/plugin-manager.js.map +1 -0
  180. package/dist/plugin-trace.d.ts +6 -0
  181. package/dist/plugin-trace.d.ts.map +1 -0
  182. package/dist/plugin-trace.js +7 -0
  183. package/dist/plugin-trace.js.map +1 -0
  184. package/dist/plugins/dynamic-broker.d.ts +76 -0
  185. package/dist/plugins/dynamic-broker.d.ts.map +1 -0
  186. package/dist/plugins/dynamic-broker.js +852 -0
  187. package/dist/plugins/dynamic-broker.js.map +1 -0
  188. package/dist/plugins/profile.d.ts +15 -0
  189. package/dist/plugins/profile.d.ts.map +1 -0
  190. package/dist/plugins/profile.js +34 -0
  191. package/dist/plugins/profile.js.map +1 -0
  192. package/dist/plugins/runtime-host.d.ts +37 -0
  193. package/dist/plugins/runtime-host.d.ts.map +1 -0
  194. package/dist/plugins/runtime-host.js +95 -0
  195. package/dist/plugins/runtime-host.js.map +1 -0
  196. package/dist/plugins/trust.d.ts +8 -0
  197. package/dist/plugins/trust.d.ts.map +1 -0
  198. package/dist/plugins/trust.js +59 -0
  199. package/dist/plugins/trust.js.map +1 -0
  200. package/dist/project-trust-entry.d.ts +15 -0
  201. package/dist/project-trust-entry.d.ts.map +1 -0
  202. package/dist/project-trust-entry.js +15 -0
  203. package/dist/project-trust-entry.js.map +1 -0
  204. package/dist/rpc/client.d.ts +78 -6
  205. package/dist/rpc/client.d.ts.map +1 -1
  206. package/dist/rpc/client.js +172 -4
  207. package/dist/rpc/client.js.map +1 -1
  208. package/dist/rpc/dispatcher.d.ts +121 -0
  209. package/dist/rpc/dispatcher.d.ts.map +1 -0
  210. package/dist/rpc/dispatcher.js +994 -0
  211. package/dist/rpc/dispatcher.js.map +1 -0
  212. package/dist/rpc/index.d.ts +2 -0
  213. package/dist/rpc/index.d.ts.map +1 -1
  214. package/dist/rpc/index.js +2 -0
  215. package/dist/rpc/index.js.map +1 -1
  216. package/dist/rpc/lifecycle.d.ts +6 -0
  217. package/dist/rpc/lifecycle.d.ts.map +1 -0
  218. package/dist/rpc/lifecycle.js +24 -0
  219. package/dist/rpc/lifecycle.js.map +1 -0
  220. package/dist/rpc/protocol.d.ts +112 -31
  221. package/dist/rpc/protocol.d.ts.map +1 -1
  222. package/dist/rpc/protocol.js +569 -117
  223. package/dist/rpc/protocol.js.map +1 -1
  224. package/dist/rpc/server-service.d.ts +7 -0
  225. package/dist/rpc/server-service.d.ts.map +1 -0
  226. package/dist/rpc/server-service.js +3 -0
  227. package/dist/rpc/server-service.js.map +1 -0
  228. package/dist/rpc/server.d.ts +16 -19
  229. package/dist/rpc/server.d.ts.map +1 -1
  230. package/dist/rpc/server.js +59 -115
  231. package/dist/rpc/server.js.map +1 -1
  232. package/dist/rpc-client-sdk-entry.d.ts +12 -0
  233. package/dist/rpc-client-sdk-entry.d.ts.map +1 -0
  234. package/dist/rpc-client-sdk-entry.js +11 -0
  235. package/dist/rpc-client-sdk-entry.js.map +1 -0
  236. package/dist/rpc-entry.js +50 -18
  237. package/dist/rpc-entry.js.map +1 -1
  238. package/dist/rpc-server-entry.d.ts +8 -0
  239. package/dist/rpc-server-entry.d.ts.map +1 -0
  240. package/dist/rpc-server-entry.js +67 -0
  241. package/dist/rpc-server-entry.js.map +1 -0
  242. package/dist/runtime/attachment-store.d.ts +13 -0
  243. package/dist/runtime/attachment-store.d.ts.map +1 -0
  244. package/dist/runtime/attachment-store.js +93 -0
  245. package/dist/runtime/attachment-store.js.map +1 -0
  246. package/dist/runtime/bootstrap.d.ts +14 -0
  247. package/dist/runtime/bootstrap.d.ts.map +1 -0
  248. package/dist/runtime/bootstrap.js +29 -0
  249. package/dist/runtime/bootstrap.js.map +1 -0
  250. package/dist/runtime/interactive-host-service.d.ts +22 -0
  251. package/dist/runtime/interactive-host-service.d.ts.map +1 -0
  252. package/dist/runtime/interactive-host-service.js +2 -0
  253. package/dist/runtime/interactive-host-service.js.map +1 -0
  254. package/dist/runtime/interactive-resource-service.d.ts +11 -0
  255. package/dist/runtime/interactive-resource-service.d.ts.map +1 -0
  256. package/dist/runtime/interactive-resource-service.js +3 -0
  257. package/dist/runtime/interactive-resource-service.js.map +1 -0
  258. package/dist/runtime/plugin-inventory-entry.d.ts +5 -0
  259. package/dist/runtime/plugin-inventory-entry.d.ts.map +1 -0
  260. package/dist/runtime/plugin-inventory-entry.js +18 -0
  261. package/dist/runtime/plugin-inventory-entry.js.map +1 -0
  262. package/dist/runtime/plugin-inventory-service.d.ts +8 -0
  263. package/dist/runtime/plugin-inventory-service.d.ts.map +1 -0
  264. package/dist/runtime/plugin-inventory-service.js +3 -0
  265. package/dist/runtime/plugin-inventory-service.js.map +1 -0
  266. package/dist/runtime/plugin-manager-entry.d.ts +20 -0
  267. package/dist/runtime/plugin-manager-entry.d.ts.map +1 -0
  268. package/dist/runtime/plugin-manager-entry.js +96 -0
  269. package/dist/runtime/plugin-manager-entry.js.map +1 -0
  270. package/dist/runtime/plugin-observability-entry.d.ts +12 -0
  271. package/dist/runtime/plugin-observability-entry.d.ts.map +1 -0
  272. package/dist/runtime/plugin-observability-entry.js +73 -0
  273. package/dist/runtime/plugin-observability-entry.js.map +1 -0
  274. package/dist/runtime/product-host.d.ts +91 -0
  275. package/dist/runtime/product-host.d.ts.map +1 -0
  276. package/dist/runtime/product-host.js +529 -0
  277. package/dist/runtime/product-host.js.map +1 -0
  278. package/dist/runtime/session-factory.d.ts +17 -0
  279. package/dist/runtime/session-factory.d.ts.map +1 -0
  280. package/dist/runtime/session-factory.js +75 -0
  281. package/dist/runtime/session-factory.js.map +1 -0
  282. package/dist/runtime/session-host.d.ts +152 -0
  283. package/dist/runtime/session-host.d.ts.map +1 -0
  284. package/dist/runtime/session-host.js +895 -0
  285. package/dist/runtime/session-host.js.map +1 -0
  286. package/dist/runtime-core-entry.d.ts +11 -0
  287. package/dist/runtime-core-entry.d.ts.map +1 -0
  288. package/dist/runtime-core-entry.js +9 -0
  289. package/dist/runtime-core-entry.js.map +1 -0
  290. package/dist/session-factory-entry.d.ts +7 -0
  291. package/dist/session-factory-entry.d.ts.map +1 -0
  292. package/dist/session-factory-entry.js +9 -0
  293. package/dist/session-factory-entry.js.map +1 -0
  294. package/dist/session-store-jsonl-entry.d.ts +7 -0
  295. package/dist/session-store-jsonl-entry.d.ts.map +1 -0
  296. package/dist/session-store-jsonl-entry.js +33 -0
  297. package/dist/session-store-jsonl-entry.js.map +1 -0
  298. package/dist/startup.d.ts +21 -1
  299. package/dist/startup.d.ts.map +1 -1
  300. package/dist/startup.js +179 -10
  301. package/dist/startup.js.map +1 -1
  302. package/dist/subagents/index.d.ts +2 -0
  303. package/dist/subagents/index.d.ts.map +1 -0
  304. package/dist/subagents/index.js +2 -0
  305. package/dist/subagents/index.js.map +1 -0
  306. package/dist/subagents/service.d.ts +50 -0
  307. package/dist/subagents/service.d.ts.map +1 -0
  308. package/dist/subagents/service.js +521 -0
  309. package/dist/subagents/service.js.map +1 -0
  310. package/dist/ui-host.d.ts +125 -0
  311. package/dist/ui-host.d.ts.map +1 -0
  312. package/dist/ui-host.js +275 -0
  313. package/dist/ui-host.js.map +1 -0
  314. package/dist/web/assets/index-BwfuGMvv.css +1 -0
  315. package/dist/web/assets/index-DPUaJqFJ.js +41 -0
  316. package/dist/web/assets/index-DPUaJqFJ.js.map +1 -0
  317. package/dist/web/index.html +14 -0
  318. package/dist/web-command.d.ts +11 -0
  319. package/dist/web-command.d.ts.map +1 -0
  320. package/dist/web-command.js +114 -0
  321. package/dist/web-command.js.map +1 -0
  322. package/dist/web-frontend.d.ts +73 -0
  323. package/dist/web-frontend.d.ts.map +1 -0
  324. package/dist/web-frontend.js +249 -0
  325. package/dist/web-frontend.js.map +1 -0
  326. package/dist/web.d.ts +254 -0
  327. package/dist/web.d.ts.map +1 -0
  328. package/dist/web.js +740 -0
  329. package/dist/web.js.map +1 -0
  330. package/dist/webui-entry.d.ts +3 -0
  331. package/dist/webui-entry.d.ts.map +1 -0
  332. package/dist/webui-entry.js +73 -0
  333. package/dist/webui-entry.js.map +1 -0
  334. package/dist/webui.d.ts +91 -0
  335. package/dist/webui.d.ts.map +1 -0
  336. package/dist/webui.js +882 -0
  337. package/dist/webui.js.map +1 -0
  338. package/package.json +89 -8
package/dist/startup.js CHANGED
@@ -1,4 +1,5 @@
1
- import { mkdir, readFile, writeFile } from "node:fs/promises";
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
2
3
  import { homedir } from "node:os";
3
4
  import { join } from "node:path";
4
5
  import { createAnthropicProvider, createDeepSeekProvider, createFauxProvider, createKimiProvider, createOpenAIChatCompletionsProvider, createOpenAIProvider, createZhipuProvider, findBuiltinModel, } from "@di-code/ai";
@@ -136,12 +137,23 @@ function parseModels(value, path, providerId, providerApi, providerBaseUrl, sett
136
137
  : configuredReasoningEfforts === undefined
137
138
  ? undefined
138
139
  : [...configuredReasoningEfforts];
140
+ const defaultReasoningEffort = model.defaultReasoningEffort;
141
+ if (defaultReasoningEffort !== undefined &&
142
+ (typeof defaultReasoningEffort !== "string" ||
143
+ !reasoningEfforts?.includes(defaultReasoningEffort))) {
144
+ throw new Error(`${settingsPath}: ${modelPath}.defaultReasoningEffort must be included in reasoningEfforts`);
145
+ }
139
146
  const baseUrl = optionalString(model.baseUrl, `${modelPath}.baseUrl`, settingsPath) ?? providerBaseUrl;
140
147
  const id = requiredString(model.id, `${modelPath}.id`, settingsPath);
141
148
  const chatCompletionsCompat = parseChatCompletionsCompat(model.chatCompletionsCompat, `${modelPath}.chatCompletionsCompat`, settingsPath);
142
149
  if (chatCompletionsCompat !== undefined && typedApi !== "openai-chat-completions") {
143
150
  throw new Error(`${settingsPath}: ${modelPath}.chatCompletionsCompat requires api "openai-chat-completions"`);
144
151
  }
152
+ const contextWindow = positiveInteger(model.contextWindow, `${modelPath}.contextWindow`, 128_000, settingsPath);
153
+ const maxOutputTokens = positiveInteger(model.maxTokens ?? model.maxOutputTokens, `${modelPath}.maxTokens`, 16_384, settingsPath);
154
+ if (maxOutputTokens >= contextWindow) {
155
+ throw new Error(`${settingsPath}: ${modelPath}.maxTokens must be smaller than contextWindow`);
156
+ }
145
157
  return {
146
158
  id,
147
159
  name: requiredString(model.name ?? id, `${modelPath}.name`, settingsPath),
@@ -151,11 +163,14 @@ function parseModels(value, path, providerId, providerApi, providerBaseUrl, sett
151
163
  input: [...input],
152
164
  reasoning: model.reasoning ?? false,
153
165
  ...(reasoningEfforts ? { reasoningEfforts } : {}),
166
+ ...(defaultReasoningEffort
167
+ ? { defaultReasoningEffort: defaultReasoningEffort }
168
+ : {}),
154
169
  ...(chatCompletionsCompat ? { chatCompletionsCompat } : {}),
155
170
  ...(model.cacheRetention === "long" ? { cacheRetention: "long" } : {}),
156
171
  ...(model.sessionAffinity === "codex" ? { sessionAffinity: "codex" } : {}),
157
- contextWindow: positiveInteger(model.contextWindow, `${modelPath}.contextWindow`, 128_000, settingsPath),
158
- maxOutputTokens: positiveInteger(model.maxTokens ?? model.maxOutputTokens, `${modelPath}.maxTokens`, 16_384, settingsPath),
172
+ contextWindow,
173
+ maxOutputTokens,
159
174
  cost: {
160
175
  input: nonNegativeNumber(prices.input, `${modelPath}.cost.input`, 0, settingsPath) / 1_000_000,
161
176
  output: nonNegativeNumber(prices.output, `${modelPath}.cost.output`, 0, settingsPath) / 1_000_000,
@@ -227,6 +242,7 @@ function serializeModel(model) {
227
242
  input: model.input,
228
243
  reasoning: model.reasoning,
229
244
  ...(model.reasoningEfforts ? { reasoningEfforts: model.reasoningEfforts } : {}),
245
+ ...(model.defaultReasoningEffort ? { defaultReasoningEffort: model.defaultReasoningEffort } : {}),
230
246
  ...(model.chatCompletionsCompat ? { chatCompletionsCompat: model.chatCompletionsCompat } : {}),
231
247
  ...(model.cacheRetention ? { cacheRetention: model.cacheRetention } : {}),
232
248
  ...(model.sessionAffinity ? { sessionAffinity: model.sessionAffinity } : {}),
@@ -262,16 +278,49 @@ function parseSettings(value, settingsPath) {
262
278
  ? undefined
263
279
  : requiredString(record.defaultProvider, "defaultProvider", settingsPath);
264
280
  const defaultModel = record.defaultModel === undefined ? undefined : requiredString(record.defaultModel, "defaultModel", settingsPath);
281
+ const thinkingLevels = parseThinkingLevels(record.thinkingLevels, settingsPath);
265
282
  if (record.locale !== undefined && !isLocale(record.locale)) {
266
283
  throw new Error(`${settingsPath}: locale must be "en" or "zh-CN"`);
267
284
  }
285
+ if (record.permissionMode !== undefined &&
286
+ record.permissionMode !== "ask" &&
287
+ record.permissionMode !== "allow" &&
288
+ record.permissionMode !== "deny") {
289
+ throw new Error(`${settingsPath}: permissionMode must be "ask", "allow", or "deny"`);
290
+ }
268
291
  return {
269
292
  providers,
270
293
  ...(defaultProvider === undefined ? {} : { defaultProvider }),
271
294
  ...(defaultModel === undefined ? {} : { defaultModel }),
295
+ ...(thinkingLevels === undefined ? {} : { thinkingLevels }),
272
296
  ...(record.locale === undefined ? {} : { locale: record.locale }),
297
+ ...(record.permissionMode === undefined ? {} : { permissionMode: record.permissionMode }),
273
298
  };
274
299
  }
300
+ function parseThinkingLevels(value, settingsPath) {
301
+ if (value === undefined)
302
+ return undefined;
303
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
304
+ throw new Error(`${settingsPath}: thinkingLevels must be an object`);
305
+ }
306
+ const preferences = {};
307
+ for (const [providerId, models] of Object.entries(value)) {
308
+ requiredString(providerId, "thinkingLevels provider ID", settingsPath);
309
+ if (typeof models !== "object" || models === null || Array.isArray(models)) {
310
+ throw new Error(`${settingsPath}: thinkingLevels.${providerId} must be an object`);
311
+ }
312
+ const modelPreferences = {};
313
+ for (const [modelId, level] of Object.entries(models)) {
314
+ requiredString(modelId, `thinkingLevels.${providerId} model ID`, settingsPath);
315
+ if (level !== "low" && level !== "medium" && level !== "high" && level !== "max") {
316
+ throw new Error(`${settingsPath}: thinkingLevels.${providerId}.${modelId} must be a valid thinking level`);
317
+ }
318
+ modelPreferences[modelId] = level;
319
+ }
320
+ preferences[providerId] = modelPreferences;
321
+ }
322
+ return preferences;
323
+ }
275
324
  async function readSettingsFile(path, displayPath) {
276
325
  try {
277
326
  const source = await readFile(path, "utf8");
@@ -304,14 +353,45 @@ function mergeSettings(globalSettings, projectSettings) {
304
353
  const defaultProvider = projectSettings?.defaultProvider ?? globalSettings?.defaultProvider;
305
354
  const defaultModel = projectSettings?.defaultModel ??
306
355
  (projectSettings?.defaultProvider === undefined ? globalSettings?.defaultModel : undefined);
356
+ const thinkingLevels = mergeThinkingLevels(globalSettings?.thinkingLevels, projectSettings?.thinkingLevels);
307
357
  const locale = globalSettings?.locale;
308
358
  return {
309
359
  providers,
310
360
  ...(defaultProvider === undefined ? {} : { defaultProvider }),
311
361
  ...(defaultModel === undefined ? {} : { defaultModel }),
362
+ ...(thinkingLevels === undefined ? {} : { thinkingLevels }),
312
363
  ...(locale === undefined ? {} : { locale }),
364
+ ...(globalSettings?.permissionMode === undefined ? {} : { permissionMode: globalSettings.permissionMode }),
313
365
  };
314
366
  }
367
+ function mergeThinkingLevels(globalPreferences, workspacePreferences) {
368
+ if (globalPreferences === undefined && workspacePreferences === undefined)
369
+ return undefined;
370
+ const result = {};
371
+ for (const [providerId, models] of Object.entries(globalPreferences ?? {}))
372
+ result[providerId] = { ...models };
373
+ for (const [providerId, models] of Object.entries(workspacePreferences ?? {})) {
374
+ result[providerId] = { ...result[providerId], ...models };
375
+ }
376
+ return result;
377
+ }
378
+ async function writeSettingsFile(agentDir, settings) {
379
+ const settingsFilePath = join(agentDir, SETTINGS_FILE_NAME);
380
+ await mkdir(agentDir, { recursive: true, mode: 0o700 });
381
+ const temporaryPath = `${settingsFilePath}.tmp-${randomUUID()}`;
382
+ try {
383
+ await writeFile(temporaryPath, `${JSON.stringify(settings, null, "\t")}\n`, { encoding: "utf8", mode: 0o600 });
384
+ await rename(temporaryPath, settingsFilePath);
385
+ }
386
+ finally {
387
+ try {
388
+ await unlink(temporaryPath);
389
+ }
390
+ catch {
391
+ // The temporary file was renamed or was never created.
392
+ }
393
+ }
394
+ }
315
395
  /** Persists a user-level terminal language preference without changing project settings. */
316
396
  export async function saveGlobalLocale(agentDir, locale) {
317
397
  const settingsFilePath = join(agentDir, SETTINGS_FILE_NAME);
@@ -320,10 +400,19 @@ export async function saveGlobalLocale(agentDir, locale) {
320
400
  providers: existingSettings?.providers ?? {},
321
401
  ...(existingSettings?.defaultProvider === undefined ? {} : { defaultProvider: existingSettings.defaultProvider }),
322
402
  ...(existingSettings?.defaultModel === undefined ? {} : { defaultModel: existingSettings.defaultModel }),
403
+ ...(existingSettings?.thinkingLevels === undefined ? {} : { thinkingLevels: existingSettings.thinkingLevels }),
323
404
  locale,
405
+ ...(existingSettings?.permissionMode === undefined ? {} : { permissionMode: existingSettings.permissionMode }),
324
406
  };
325
- await mkdir(agentDir, { recursive: true, mode: 0o700 });
326
- await writeFile(settingsFilePath, `${JSON.stringify(settings, null, "\t")}\n`, { encoding: "utf8", mode: 0o600 });
407
+ await writeSettingsFile(agentDir, settings);
408
+ }
409
+ /** Persists the default permission mode; the active Session keeps its own runtime until next turn. */
410
+ export async function saveGlobalPermissionMode(agentDir, permissionMode) {
411
+ const settingsFilePath = join(agentDir, SETTINGS_FILE_NAME);
412
+ const existingSettings = await readSettingsFile(settingsFilePath, settingsFilePath);
413
+ if (permissionMode !== "ask" && permissionMode !== "allow" && permissionMode !== "deny")
414
+ throw new Error(`${settingsFilePath}: permissionMode must be "ask", "allow", or "deny"`);
415
+ await writeSettingsFile(agentDir, { ...mergeSettings(existingSettings, undefined), permissionMode });
327
416
  }
328
417
  export async function saveGlobalProviderApiKey(agentDir, providerId, api, apiKey, modelId) {
329
418
  const settingsFilePath = join(agentDir, SETTINGS_FILE_NAME);
@@ -336,8 +425,80 @@ export async function saveGlobalProviderApiKey(agentDir, providerId, api, apiKey
336
425
  defaultProvider: providerId,
337
426
  ...(modelId === undefined ? {} : { defaultModel: modelId }),
338
427
  });
339
- await mkdir(agentDir, { recursive: true, mode: 0o700 });
340
- await writeFile(settingsFilePath, `${JSON.stringify(settings, null, "\t")}\n`, { encoding: "utf8", mode: 0o600 });
428
+ await writeSettingsFile(agentDir, settings);
429
+ }
430
+ /** Persists the interactive model selection without changing credentials or Provider configuration. */
431
+ export async function saveGlobalModelSelection(agentDir, providerId, modelId) {
432
+ const settingsFilePath = join(agentDir, SETTINGS_FILE_NAME);
433
+ const normalizedProviderId = requiredString(providerId, "providerId", settingsFilePath);
434
+ const normalizedModelId = requiredString(modelId, "modelId", settingsFilePath);
435
+ const existingSettings = await readSettingsFile(settingsFilePath, settingsFilePath);
436
+ const settings = mergeSettings(existingSettings, {
437
+ providers: {},
438
+ defaultProvider: normalizedProviderId,
439
+ defaultModel: normalizedModelId,
440
+ });
441
+ await writeSettingsFile(agentDir, settings);
442
+ }
443
+ /**
444
+ * Persists a runtime preference in an existing workspace settings file, falling back to user settings when the
445
+ * workspace has no settings file. This keeps an explicit WebUI selection aligned with startup resolution.
446
+ */
447
+ export async function saveScopedModelSelection(cwd, agentDir, providerId, modelId) {
448
+ const workspaceDir = join(cwd, ".di-code");
449
+ const workspacePath = join(cwd, SETTINGS_PATH);
450
+ const workspaceSettings = await readSettingsFile(workspacePath, SETTINGS_PATH);
451
+ if (workspaceSettings === undefined) {
452
+ await saveGlobalModelSelection(agentDir, providerId, modelId);
453
+ return;
454
+ }
455
+ const normalizedProviderId = requiredString(providerId, "providerId", SETTINGS_PATH);
456
+ const normalizedModelId = requiredString(modelId, "modelId", SETTINGS_PATH);
457
+ await writeSettingsFile(workspaceDir, {
458
+ ...workspaceSettings,
459
+ defaultProvider: normalizedProviderId,
460
+ defaultModel: normalizedModelId,
461
+ });
462
+ }
463
+ /** Persists a per-provider/model thinking preference in user settings. */
464
+ export async function saveGlobalThinkingLevel(agentDir, providerId, modelId, level) {
465
+ const settingsFilePath = join(agentDir, SETTINGS_FILE_NAME);
466
+ const normalizedProviderId = requiredString(providerId, "providerId", settingsFilePath);
467
+ const normalizedModelId = requiredString(modelId, "modelId", settingsFilePath);
468
+ const existingSettings = await readSettingsFile(settingsFilePath, settingsFilePath);
469
+ const settings = {
470
+ ...mergeSettings(existingSettings, undefined),
471
+ thinkingLevels: {
472
+ ...(existingSettings?.thinkingLevels ?? {}),
473
+ [normalizedProviderId]: {
474
+ ...(existingSettings?.thinkingLevels?.[normalizedProviderId] ?? {}),
475
+ [normalizedModelId]: level,
476
+ },
477
+ },
478
+ };
479
+ await writeSettingsFile(agentDir, settings);
480
+ }
481
+ /** Persists a thinking preference beside an existing workspace model selection, or in user settings as a fallback. */
482
+ export async function saveScopedThinkingLevel(cwd, agentDir, providerId, modelId, level) {
483
+ const workspaceDir = join(cwd, ".di-code");
484
+ const workspacePath = join(cwd, SETTINGS_PATH);
485
+ const workspaceSettings = await readSettingsFile(workspacePath, SETTINGS_PATH);
486
+ if (workspaceSettings === undefined) {
487
+ await saveGlobalThinkingLevel(agentDir, providerId, modelId, level);
488
+ return;
489
+ }
490
+ const normalizedProviderId = requiredString(providerId, "providerId", SETTINGS_PATH);
491
+ const normalizedModelId = requiredString(modelId, "modelId", SETTINGS_PATH);
492
+ await writeSettingsFile(workspaceDir, {
493
+ ...workspaceSettings,
494
+ thinkingLevels: {
495
+ ...(workspaceSettings.thinkingLevels ?? {}),
496
+ [normalizedProviderId]: {
497
+ ...(workspaceSettings.thinkingLevels?.[normalizedProviderId] ?? {}),
498
+ [normalizedModelId]: level,
499
+ },
500
+ },
501
+ });
341
502
  }
342
503
  /**
343
504
  * Replaces the user-managed `custom` provider while retaining other global
@@ -361,8 +522,7 @@ export async function saveGlobalCustomProvider(agentDir, input) {
361
522
  defaultProvider: "custom",
362
523
  defaultModel: configuration.models?.[0]?.id,
363
524
  });
364
- await mkdir(agentDir, { recursive: true, mode: 0o700 });
365
- await writeFile(settingsFilePath, `${JSON.stringify(settings, null, "\t")}\n`, { encoding: "utf8", mode: 0o600 });
525
+ await writeSettingsFile(agentDir, settings);
366
526
  return configuration;
367
527
  }
368
528
  /**
@@ -388,9 +548,11 @@ export async function removeGlobalProviderApiKey(agentDir, providerId) {
388
548
  ...(clearsDefault || existingSettings.defaultModel === undefined
389
549
  ? {}
390
550
  : { defaultModel: existingSettings.defaultModel }),
551
+ ...(existingSettings.thinkingLevels === undefined ? {} : { thinkingLevels: existingSettings.thinkingLevels }),
391
552
  ...(existingSettings.locale === undefined ? {} : { locale: existingSettings.locale }),
553
+ ...(existingSettings.permissionMode === undefined ? {} : { permissionMode: existingSettings.permissionMode }),
392
554
  };
393
- await writeFile(settingsFilePath, `${JSON.stringify(settings, null, "\t")}\n`, { encoding: "utf8", mode: 0o600 });
555
+ await writeSettingsFile(agentDir, settings);
394
556
  return true;
395
557
  }
396
558
  function resolveConfigValue(value, env) {
@@ -561,6 +723,11 @@ export function resolveStartupRuntime(env, providers, defaults = {}) {
561
723
  return builtInRuntime;
562
724
  throw new Error(`Unknown configured provider "${selectedProviderId}". Available providers: ${providers.map((provider) => provider.id).join(", ")}.`);
563
725
  }
726
+ /** Returns a stored preference only when the selected model still supports it. */
727
+ export function resolveThinkingLevelPreference(configuration, runtime) {
728
+ const preference = configuration?.thinkingLevels?.[runtime.provider.id]?.[runtime.model.id];
729
+ return preference !== undefined && runtime.model.reasoningEfforts?.includes(preference) ? preference : undefined;
730
+ }
564
731
  export async function loadStartupConfiguration(cwd, environment = process.env, agentDir = join(homedir(), ".di-code")) {
565
732
  const [globalSettings, projectSettings] = await Promise.all([
566
733
  readSettingsFile(join(agentDir, SETTINGS_FILE_NAME), join(agentDir, SETTINGS_FILE_NAME)),
@@ -586,7 +753,9 @@ export async function loadStartupConfiguration(cwd, environment = process.env, a
586
753
  return {
587
754
  environment,
588
755
  providers,
756
+ ...(settings.thinkingLevels === undefined ? {} : { thinkingLevels: settings.thinkingLevels }),
589
757
  ...(locale === undefined ? {} : { locale }),
758
+ ...(settings.permissionMode === undefined ? {} : { permissionMode: settings.permissionMode }),
590
759
  ...(defaults === undefined ? {} : { defaults }),
591
760
  };
592
761
  }