longer-agent 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +227 -0
  3. package/README.zh-CN.md +227 -0
  4. package/agent_templates/executor/agent.yaml +22 -0
  5. package/agent_templates/executor/system_prompt.md +17 -0
  6. package/agent_templates/explorer/agent.yaml +13 -0
  7. package/agent_templates/explorer/system_prompt.md +19 -0
  8. package/agent_templates/main/agent.yaml +7 -0
  9. package/agent_templates/main/system_prompt.md +45 -0
  10. package/configExample.yaml +83 -0
  11. package/dist/agents/agent.d.ts +79 -0
  12. package/dist/agents/agent.d.ts.map +1 -0
  13. package/dist/agents/agent.js +156 -0
  14. package/dist/agents/agent.js.map +1 -0
  15. package/dist/agents/tool-loop.d.ts +140 -0
  16. package/dist/agents/tool-loop.d.ts.map +1 -0
  17. package/dist/agents/tool-loop.js +465 -0
  18. package/dist/agents/tool-loop.js.map +1 -0
  19. package/dist/ask.d.ts +81 -0
  20. package/dist/ask.d.ts.map +1 -0
  21. package/dist/ask.js +34 -0
  22. package/dist/ask.js.map +1 -0
  23. package/dist/auth/openai-oauth.d.ts +66 -0
  24. package/dist/auth/openai-oauth.d.ts.map +1 -0
  25. package/dist/auth/openai-oauth.js +640 -0
  26. package/dist/auth/openai-oauth.js.map +1 -0
  27. package/dist/cli.d.ts +14 -0
  28. package/dist/cli.d.ts.map +1 -0
  29. package/dist/cli.js +254 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/commands.d.ts +118 -0
  32. package/dist/commands.d.ts.map +1 -0
  33. package/dist/commands.js +862 -0
  34. package/dist/commands.js.map +1 -0
  35. package/dist/config.d.ts +130 -0
  36. package/dist/config.d.ts.map +1 -0
  37. package/dist/config.js +648 -0
  38. package/dist/config.js.map +1 -0
  39. package/dist/context-rendering.d.ts +69 -0
  40. package/dist/context-rendering.d.ts.map +1 -0
  41. package/dist/context-rendering.js +250 -0
  42. package/dist/context-rendering.js.map +1 -0
  43. package/dist/document-projection.d.ts +12 -0
  44. package/dist/document-projection.d.ts.map +1 -0
  45. package/dist/document-projection.js +75 -0
  46. package/dist/document-projection.js.map +1 -0
  47. package/dist/ephemeral-log.d.ts +15 -0
  48. package/dist/ephemeral-log.d.ts.map +1 -0
  49. package/dist/ephemeral-log.js +173 -0
  50. package/dist/ephemeral-log.js.map +1 -0
  51. package/dist/file-attach.d.ts +89 -0
  52. package/dist/file-attach.d.ts.map +1 -0
  53. package/dist/file-attach.js +571 -0
  54. package/dist/file-attach.js.map +1 -0
  55. package/dist/index.d.ts +29 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +43 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/init-wizard.d.ts +13 -0
  60. package/dist/init-wizard.d.ts.map +1 -0
  61. package/dist/init-wizard.js +328 -0
  62. package/dist/init-wizard.js.map +1 -0
  63. package/dist/log-entry.d.ts +104 -0
  64. package/dist/log-entry.d.ts.map +1 -0
  65. package/dist/log-entry.js +292 -0
  66. package/dist/log-entry.js.map +1 -0
  67. package/dist/log-projection.d.ts +73 -0
  68. package/dist/log-projection.d.ts.map +1 -0
  69. package/dist/log-projection.js +651 -0
  70. package/dist/log-projection.js.map +1 -0
  71. package/dist/mcp-client.d.ts +55 -0
  72. package/dist/mcp-client.d.ts.map +1 -0
  73. package/dist/mcp-client.js +402 -0
  74. package/dist/mcp-client.js.map +1 -0
  75. package/dist/model-selection.d.ts +16 -0
  76. package/dist/model-selection.d.ts.map +1 -0
  77. package/dist/model-selection.js +181 -0
  78. package/dist/model-selection.js.map +1 -0
  79. package/dist/network-retry.d.ts +38 -0
  80. package/dist/network-retry.d.ts.map +1 -0
  81. package/dist/network-retry.js +140 -0
  82. package/dist/network-retry.js.map +1 -0
  83. package/dist/persistence.d.ts +104 -0
  84. package/dist/persistence.d.ts.map +1 -0
  85. package/dist/persistence.js +644 -0
  86. package/dist/persistence.js.map +1 -0
  87. package/dist/primitives/context.d.ts +29 -0
  88. package/dist/primitives/context.d.ts.map +1 -0
  89. package/dist/primitives/context.js +85 -0
  90. package/dist/primitives/context.js.map +1 -0
  91. package/dist/progress.d.ts +51 -0
  92. package/dist/progress.d.ts.map +1 -0
  93. package/dist/progress.js +229 -0
  94. package/dist/progress.js.map +1 -0
  95. package/dist/provider-presets.d.ts +34 -0
  96. package/dist/provider-presets.d.ts.map +1 -0
  97. package/dist/provider-presets.js +181 -0
  98. package/dist/provider-presets.js.map +1 -0
  99. package/dist/providers/anthropic.d.ts +32 -0
  100. package/dist/providers/anthropic.d.ts.map +1 -0
  101. package/dist/providers/anthropic.js +450 -0
  102. package/dist/providers/anthropic.js.map +1 -0
  103. package/dist/providers/base.d.ts +135 -0
  104. package/dist/providers/base.d.ts.map +1 -0
  105. package/dist/providers/base.js +104 -0
  106. package/dist/providers/base.js.map +1 -0
  107. package/dist/providers/glm.d.ts +18 -0
  108. package/dist/providers/glm.d.ts.map +1 -0
  109. package/dist/providers/glm.js +59 -0
  110. package/dist/providers/glm.js.map +1 -0
  111. package/dist/providers/kimi.d.ts +23 -0
  112. package/dist/providers/kimi.d.ts.map +1 -0
  113. package/dist/providers/kimi.js +89 -0
  114. package/dist/providers/kimi.js.map +1 -0
  115. package/dist/providers/minimax.d.ts +20 -0
  116. package/dist/providers/minimax.d.ts.map +1 -0
  117. package/dist/providers/minimax.js +192 -0
  118. package/dist/providers/minimax.js.map +1 -0
  119. package/dist/providers/openai-chat.d.ts +33 -0
  120. package/dist/providers/openai-chat.d.ts.map +1 -0
  121. package/dist/providers/openai-chat.js +543 -0
  122. package/dist/providers/openai-chat.js.map +1 -0
  123. package/dist/providers/openai-responses.d.ts +26 -0
  124. package/dist/providers/openai-responses.d.ts.map +1 -0
  125. package/dist/providers/openai-responses.js +443 -0
  126. package/dist/providers/openai-responses.js.map +1 -0
  127. package/dist/providers/openrouter.d.ts +24 -0
  128. package/dist/providers/openrouter.d.ts.map +1 -0
  129. package/dist/providers/openrouter.js +177 -0
  130. package/dist/providers/openrouter.js.map +1 -0
  131. package/dist/providers/registry.d.ts +7 -0
  132. package/dist/providers/registry.d.ts.map +1 -0
  133. package/dist/providers/registry.js +38 -0
  134. package/dist/providers/registry.js.map +1 -0
  135. package/dist/security/path.d.ts +51 -0
  136. package/dist/security/path.d.ts.map +1 -0
  137. package/dist/security/path.js +187 -0
  138. package/dist/security/path.js.map +1 -0
  139. package/dist/security/sensitive-files.d.ts +3 -0
  140. package/dist/security/sensitive-files.d.ts.map +1 -0
  141. package/dist/security/sensitive-files.js +41 -0
  142. package/dist/security/sensitive-files.js.map +1 -0
  143. package/dist/session.d.ts +446 -0
  144. package/dist/session.d.ts.map +1 -0
  145. package/dist/session.js +4595 -0
  146. package/dist/session.js.map +1 -0
  147. package/dist/settings.d.ts +46 -0
  148. package/dist/settings.d.ts.map +1 -0
  149. package/dist/settings.js +134 -0
  150. package/dist/settings.js.map +1 -0
  151. package/dist/show-context.d.ts +35 -0
  152. package/dist/show-context.d.ts.map +1 -0
  153. package/dist/show-context.js +320 -0
  154. package/dist/show-context.js.map +1 -0
  155. package/dist/skills/loader.d.ts +49 -0
  156. package/dist/skills/loader.d.ts.map +1 -0
  157. package/dist/skills/loader.js +166 -0
  158. package/dist/skills/loader.js.map +1 -0
  159. package/dist/summarize-context.d.ts +29 -0
  160. package/dist/summarize-context.d.ts.map +1 -0
  161. package/dist/summarize-context.js +247 -0
  162. package/dist/summarize-context.js.map +1 -0
  163. package/dist/templates/loader.d.ts +104 -0
  164. package/dist/templates/loader.d.ts.map +1 -0
  165. package/dist/templates/loader.js +514 -0
  166. package/dist/templates/loader.js.map +1 -0
  167. package/dist/tools/basic.d.ts +29 -0
  168. package/dist/tools/basic.d.ts.map +1 -0
  169. package/dist/tools/basic.js +2079 -0
  170. package/dist/tools/basic.js.map +1 -0
  171. package/dist/tools/comm.d.ts +17 -0
  172. package/dist/tools/comm.d.ts.map +1 -0
  173. package/dist/tools/comm.js +192 -0
  174. package/dist/tools/comm.js.map +1 -0
  175. package/dist/tools/web-fetch.d.ts +11 -0
  176. package/dist/tools/web-fetch.d.ts.map +1 -0
  177. package/dist/tools/web-fetch.js +237 -0
  178. package/dist/tools/web-fetch.js.map +1 -0
  179. package/dist/tools/web-search.d.ts +24 -0
  180. package/dist/tools/web-search.d.ts.map +1 -0
  181. package/dist/tools/web-search.js +51 -0
  182. package/dist/tools/web-search.js.map +1 -0
  183. package/dist/tui/app.d.ts +35 -0
  184. package/dist/tui/app.d.ts.map +1 -0
  185. package/dist/tui/app.js +1042 -0
  186. package/dist/tui/app.js.map +1 -0
  187. package/dist/tui/checkbox-picker.d.ts +35 -0
  188. package/dist/tui/checkbox-picker.d.ts.map +1 -0
  189. package/dist/tui/checkbox-picker.js +85 -0
  190. package/dist/tui/checkbox-picker.js.map +1 -0
  191. package/dist/tui/command-picker.d.ts +31 -0
  192. package/dist/tui/command-picker.d.ts.map +1 -0
  193. package/dist/tui/command-picker.js +113 -0
  194. package/dist/tui/command-picker.js.map +1 -0
  195. package/dist/tui/components/ask-panel.d.ts +21 -0
  196. package/dist/tui/components/ask-panel.d.ts.map +1 -0
  197. package/dist/tui/components/ask-panel.js +81 -0
  198. package/dist/tui/components/ask-panel.js.map +1 -0
  199. package/dist/tui/components/conversation-panel.d.ts +68 -0
  200. package/dist/tui/components/conversation-panel.d.ts.map +1 -0
  201. package/dist/tui/components/conversation-panel.js +611 -0
  202. package/dist/tui/components/conversation-panel.js.map +1 -0
  203. package/dist/tui/components/input-panel.d.ts +27 -0
  204. package/dist/tui/components/input-panel.d.ts.map +1 -0
  205. package/dist/tui/components/input-panel.js +725 -0
  206. package/dist/tui/components/input-panel.js.map +1 -0
  207. package/dist/tui/components/logo-panel.d.ts +14 -0
  208. package/dist/tui/components/logo-panel.d.ts.map +1 -0
  209. package/dist/tui/components/logo-panel.js +37 -0
  210. package/dist/tui/components/logo-panel.js.map +1 -0
  211. package/dist/tui/components/plan-panel.d.ts +10 -0
  212. package/dist/tui/components/plan-panel.d.ts.map +1 -0
  213. package/dist/tui/components/plan-panel.js +8 -0
  214. package/dist/tui/components/plan-panel.js.map +1 -0
  215. package/dist/tui/components/status-bar.d.ts +24 -0
  216. package/dist/tui/components/status-bar.d.ts.map +1 -0
  217. package/dist/tui/components/status-bar.js +80 -0
  218. package/dist/tui/components/status-bar.js.map +1 -0
  219. package/dist/tui/input/editor-state.d.ts +22 -0
  220. package/dist/tui/input/editor-state.d.ts.map +1 -0
  221. package/dist/tui/input/editor-state.js +157 -0
  222. package/dist/tui/input/editor-state.js.map +1 -0
  223. package/dist/tui/input/keymap.d.ts +3 -0
  224. package/dist/tui/input/keymap.d.ts.map +1 -0
  225. package/dist/tui/input/keymap.js +72 -0
  226. package/dist/tui/input/keymap.js.map +1 -0
  227. package/dist/tui/input/paste-slots.d.ts +17 -0
  228. package/dist/tui/input/paste-slots.d.ts.map +1 -0
  229. package/dist/tui/input/paste-slots.js +46 -0
  230. package/dist/tui/input/paste-slots.js.map +1 -0
  231. package/dist/tui/input/paste.d.ts +15 -0
  232. package/dist/tui/input/paste.d.ts.map +1 -0
  233. package/dist/tui/input/paste.js +35 -0
  234. package/dist/tui/input/paste.js.map +1 -0
  235. package/dist/tui/input/protocol.d.ts +9 -0
  236. package/dist/tui/input/protocol.d.ts.map +1 -0
  237. package/dist/tui/input/protocol.js +387 -0
  238. package/dist/tui/input/protocol.js.map +1 -0
  239. package/dist/tui/input/sanitize.d.ts +6 -0
  240. package/dist/tui/input/sanitize.d.ts.map +1 -0
  241. package/dist/tui/input/sanitize.js +20 -0
  242. package/dist/tui/input/sanitize.js.map +1 -0
  243. package/dist/tui/input/types.d.ts +18 -0
  244. package/dist/tui/input/types.d.ts.map +1 -0
  245. package/dist/tui/input/types.js +2 -0
  246. package/dist/tui/input/types.js.map +1 -0
  247. package/dist/tui/launch.d.ts +23 -0
  248. package/dist/tui/launch.d.ts.map +1 -0
  249. package/dist/tui/launch.js +104 -0
  250. package/dist/tui/launch.js.map +1 -0
  251. package/dist/tui/theme.d.ts +20 -0
  252. package/dist/tui/theme.d.ts.map +1 -0
  253. package/dist/tui/theme.js +29 -0
  254. package/dist/tui/theme.js.map +1 -0
  255. package/dist/tui/types.d.ts +136 -0
  256. package/dist/tui/types.d.ts.map +1 -0
  257. package/dist/tui/types.js +9 -0
  258. package/dist/tui/types.js.map +1 -0
  259. package/package.json +76 -0
  260. package/prompts/sections/agents_md.md +23 -0
  261. package/prompts/sections/important_log.md +16 -0
  262. package/prompts/sections/system_mechanisms.md +18 -0
  263. package/prompts/tools/apply_patch.md +31 -0
  264. package/prompts/tools/ask.md +18 -0
  265. package/prompts/tools/bash.md +13 -0
  266. package/prompts/tools/bash_background.md +9 -0
  267. package/prompts/tools/bash_output.md +9 -0
  268. package/prompts/tools/check_status.md +3 -0
  269. package/prompts/tools/diff.md +5 -0
  270. package/prompts/tools/edit_file.md +11 -0
  271. package/prompts/tools/glob.md +7 -0
  272. package/prompts/tools/grep.md +20 -0
  273. package/prompts/tools/kill_agent.md +3 -0
  274. package/prompts/tools/kill_shell.md +5 -0
  275. package/prompts/tools/list_dir.md +5 -0
  276. package/prompts/tools/plan.md +252 -0
  277. package/prompts/tools/read_file.md +9 -0
  278. package/prompts/tools/show_context.md +12 -0
  279. package/prompts/tools/skill.md +7 -0
  280. package/prompts/tools/spawn_agent.md +195 -0
  281. package/prompts/tools/summarize_context.md +122 -0
  282. package/prompts/tools/test.md +5 -0
  283. package/prompts/tools/wait.md +17 -0
  284. package/prompts/tools/web_fetch.md +9 -0
  285. package/prompts/tools/web_search.md +5 -0
  286. package/prompts/tools/write_file.md +11 -0
  287. package/skills/.staging/.gitkeep +0 -0
  288. package/skills/explain-code/SKILL.md +15 -0
  289. package/skills/skill-manager/SKILL.md +83 -0
package/dist/config.js ADDED
@@ -0,0 +1,648 @@
1
+ /**
2
+ * Configuration management — load YAML config, resolve env vars,
3
+ * auto-detect model capabilities from known lookup tables.
4
+ */
5
+ import { existsSync, readFileSync, statSync } from "node:fs";
6
+ import { homedir } from "node:os";
7
+ import { dirname, join } from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+ import * as yaml from "js-yaml";
10
+ import { readOAuthAccessToken, hasOAuthTokens } from "./auth/openai-oauth.js";
11
+ // ------------------------------------------------------------------
12
+ // Known model lookup tables
13
+ // ------------------------------------------------------------------
14
+ export const KNOWN_CONTEXT_LENGTHS = {
15
+ // OpenAI - GPT-5 family
16
+ "gpt-5.2": 400_000,
17
+ "gpt-5.2-codex": 400_000,
18
+ "gpt-5.3-codex": 400_000,
19
+ "gpt-5.4": 1_050_000,
20
+ // Anthropic
21
+ "claude-opus-4-1-20250805": 200_000,
22
+ "claude-sonnet-4-5-20250929": 200_000,
23
+ "claude-haiku-4-5-20251001": 200_000,
24
+ "claude-haiku-4-5": 200_000,
25
+ "claude-opus-4-5-20251101": 200_000,
26
+ "claude-opus-4-6": 200_000,
27
+ "claude-sonnet-4-6": 200_000,
28
+ // OpenRouter Anthropic aliases
29
+ "claude-haiku-4.5": 200_000,
30
+ "claude-opus-4.6": 200_000,
31
+ "claude-sonnet-4.6": 200_000,
32
+ // Kimi
33
+ "kimi-k2.5": 256_000,
34
+ "kimi-k2-thinking": 256_000,
35
+ "kimi-k2-instruct": 128_000,
36
+ // GLM (Zhipu AI)
37
+ "glm-5": 200_000,
38
+ "glm-4.7": 200_000,
39
+ "glm-4.7-flash": 200_000,
40
+ // MiniMax
41
+ "MiniMax-M2.1": 200_000,
42
+ "MiniMax-M2.5": 204_800,
43
+ "MiniMax-M2.5-highspeed": 204_800,
44
+ "MiniMax-M1-40k": 1_000_000,
45
+ "MiniMax-M1-80k": 1_000_000,
46
+ // MiniMax — lowercase aliases for OpenRouter (minimax/minimax-m2.5 → minimax-m2.5)
47
+ "minimax-m2.1": 200_000,
48
+ "minimax-m2.5": 204_800,
49
+ "minimax-m1": 1_000_000,
50
+ };
51
+ export const KNOWN_MULTIMODAL_MODELS = new Set([
52
+ // OpenAI
53
+ "gpt-5.2", "gpt-5.2-codex", "gpt-5.3-codex", "gpt-5.4",
54
+ // Anthropic
55
+ "claude-opus-4-1-20250805", "claude-sonnet-4-5-20250929", "claude-haiku-4-5-20251001",
56
+ "claude-haiku-4-5",
57
+ "claude-opus-4-5-20251101", "claude-opus-4-6", "claude-sonnet-4-6",
58
+ "claude-haiku-4.5", "claude-opus-4.6", "claude-sonnet-4.6",
59
+ // Kimi
60
+ "kimi-k2.5",
61
+ ]);
62
+ export const KNOWN_THINKING_MODELS = new Set([
63
+ // OpenAI
64
+ "gpt-5.2", "gpt-5.2-codex", "gpt-5.3-codex", "gpt-5.4",
65
+ // Anthropic
66
+ "claude-opus-4-1-20250805", "claude-sonnet-4-5-20250929", "claude-haiku-4-5-20251001",
67
+ "claude-haiku-4-5",
68
+ "claude-opus-4-5-20251101", "claude-opus-4-6", "claude-sonnet-4-6",
69
+ "claude-haiku-4.5", "claude-opus-4.6", "claude-sonnet-4.6",
70
+ // Kimi
71
+ "kimi-k2.5", "kimi-k2-thinking",
72
+ // GLM
73
+ "glm-5", "glm-4.7", "glm-4.7-flash",
74
+ // MiniMax
75
+ "MiniMax-M2.5", "MiniMax-M2.5-highspeed", "MiniMax-M2.1",
76
+ "MiniMax-M1-40k", "MiniMax-M1-80k",
77
+ // MiniMax — lowercase aliases for OpenRouter
78
+ "minimax-m2.5", "minimax-m2.1", "minimax-m1",
79
+ ]);
80
+ export const KNOWN_NO_WEB_SEARCH_MODELS = new Set([
81
+ // MiniMax — M1 series lacks native web search
82
+ "MiniMax-M1-40k", "MiniMax-M1-80k",
83
+ ]);
84
+ // ------------------------------------------------------------------
85
+ // Max output tokens per model
86
+ // ------------------------------------------------------------------
87
+ export const KNOWN_MAX_OUTPUT_TOKENS = {
88
+ // OpenAI — GPT-5 family
89
+ "gpt-5.2": 128_000,
90
+ "gpt-5.2-codex": 128_000,
91
+ "gpt-5.3-codex": 128_000,
92
+ "gpt-5.4": 128_000,
93
+ // Anthropic — Claude 4.6
94
+ "claude-opus-4-6": 128_000,
95
+ "claude-sonnet-4-6": 64_000,
96
+ // Anthropic — Claude 4.5
97
+ "claude-opus-4-5-20251101": 64_000,
98
+ "claude-sonnet-4-5-20250929": 64_000,
99
+ "claude-haiku-4-5-20251001": 64_000,
100
+ "claude-haiku-4-5": 64_000,
101
+ // Anthropic — Claude 4.1
102
+ "claude-opus-4-1-20250805": 32_000,
103
+ // OpenRouter — Anthropic aliases
104
+ "claude-haiku-4.5": 64_000,
105
+ "claude-opus-4.6": 128_000,
106
+ "claude-sonnet-4.6": 64_000,
107
+ // Kimi
108
+ "kimi-k2.5": 65_536,
109
+ "kimi-k2-thinking": 65_536,
110
+ "kimi-k2-instruct": 65_536,
111
+ // GLM (Zhipu AI)
112
+ "glm-5": 128_000,
113
+ "glm-4.7": 128_000,
114
+ "glm-4.7-flash": 128_000,
115
+ // MiniMax
116
+ "MiniMax-M2.1": 8_192,
117
+ "MiniMax-M2.5": 196_608,
118
+ "MiniMax-M2.5-highspeed": 196_608,
119
+ "MiniMax-M1-40k": 40_000,
120
+ "MiniMax-M1-80k": 80_000,
121
+ // MiniMax — lowercase aliases for OpenRouter
122
+ "minimax-m2.1": 8_192,
123
+ "minimax-m2.5": 196_608,
124
+ "minimax-m1": 80_000,
125
+ };
126
+ /** Resolve max output tokens for a model. Priority: known lookup (exact then normalized) > undefined. */
127
+ export function getModelMaxOutputTokens(model) {
128
+ return KNOWN_MAX_OUTPUT_TOKENS[model]
129
+ ?? KNOWN_MAX_OUTPUT_TOKENS[normalizeModelId(model)];
130
+ }
131
+ // ------------------------------------------------------------------
132
+ // Thinking levels per model
133
+ // ------------------------------------------------------------------
134
+ export const KNOWN_THINKING_LEVELS = {
135
+ // OpenAI
136
+ "gpt-5.2": ["none", "low", "medium", "high", "xhigh"],
137
+ "gpt-5.2-codex": ["low", "medium", "high", "xhigh"],
138
+ "gpt-5.3-codex": ["low", "medium", "high", "xhigh"],
139
+ "gpt-5.4": ["none", "low", "medium", "high", "xhigh"],
140
+ // Anthropic — adaptive + effort (4.6)
141
+ "claude-opus-4-6": ["off", "low", "medium", "high", "max"],
142
+ "claude-sonnet-4-6": ["off", "low", "medium", "high", "max"],
143
+ "claude-opus-4.6": ["off", "low", "medium", "high", "max"],
144
+ "claude-sonnet-4.6": ["off", "low", "medium", "high", "max"],
145
+ // Anthropic — manual extended thinking (4.5 and earlier)
146
+ "claude-opus-4-1-20250805": ["off", "low", "medium", "high"],
147
+ "claude-sonnet-4-5-20250929": ["off", "low", "medium", "high"],
148
+ "claude-haiku-4-5-20251001": ["off", "low", "medium", "high"],
149
+ "claude-haiku-4-5": ["off", "low", "medium", "high"],
150
+ "claude-haiku-4.5": ["off", "low", "medium", "high"],
151
+ "claude-opus-4-5-20251101": ["off", "low", "medium", "high"],
152
+ // GLM
153
+ "glm-5": ["off", "on"], "glm-4.7": ["off", "on"], "glm-4.7-flash": ["off", "on"],
154
+ // Kimi
155
+ "kimi-k2.5": ["off", "on"], "kimi-k2-thinking": ["off", "on"],
156
+ // MiniMax (not configurable)
157
+ "MiniMax-M2.5": ["on"], "MiniMax-M2.5-highspeed": ["on"],
158
+ "MiniMax-M2.1": ["on"], "MiniMax-M1-40k": ["on"], "MiniMax-M1-80k": ["on"],
159
+ // MiniMax — lowercase aliases for OpenRouter
160
+ "minimax-m2.5": ["on"], "minimax-m2.1": ["on"], "minimax-m1": ["on"],
161
+ };
162
+ /** Return available thinking levels for a model, or empty array if not a thinking model. */
163
+ export function getThinkingLevels(model) {
164
+ return KNOWN_THINKING_LEVELS[model]
165
+ ?? KNOWN_THINKING_LEVELS[normalizeModelId(model)]
166
+ ?? [];
167
+ }
168
+ // ------------------------------------------------------------------
169
+ // Helper functions
170
+ // ------------------------------------------------------------------
171
+ /**
172
+ * Strip the vendor prefix from an OpenRouter-style model ID.
173
+ * e.g. "anthropic/claude-sonnet-4-6" → "claude-sonnet-4-6"
174
+ * If the model ID contains no "/", it is returned unchanged.
175
+ */
176
+ export function normalizeModelId(model) {
177
+ const idx = model.lastIndexOf("/");
178
+ return idx >= 0 ? model.slice(idx + 1) : model;
179
+ }
180
+ /** Format a short user-facing model label for UI surfaces such as the status bar. */
181
+ export function formatDisplayModelName(provider, model) {
182
+ const safeProvider = String(provider ?? "").trim();
183
+ const safeModel = String(model ?? "").trim();
184
+ if (!safeModel)
185
+ return safeProvider;
186
+ if (safeProvider === "openrouter") {
187
+ return `openrouter/${normalizeModelId(safeModel)}`;
188
+ }
189
+ return safeModel;
190
+ }
191
+ /** Format a provider-scoped user-facing model label for status messages. */
192
+ export function formatScopedModelName(provider, model) {
193
+ const safeProvider = String(provider ?? "").trim();
194
+ const safeModel = String(model ?? "").trim();
195
+ if (!safeProvider)
196
+ return formatDisplayModelName(undefined, safeModel);
197
+ if (!safeModel)
198
+ return safeProvider;
199
+ if (safeProvider === "openrouter") {
200
+ return `openrouter/${normalizeModelId(safeModel)}`;
201
+ }
202
+ return `${safeProvider}/${safeModel}`;
203
+ }
204
+ /** Resolve effective context length. Priority: explicit > known lookup (exact then normalized) > 0. */
205
+ export function getContextLength(model, contextLength = 0) {
206
+ if (contextLength > 0)
207
+ return contextLength;
208
+ return KNOWN_CONTEXT_LENGTHS[model]
209
+ ?? KNOWN_CONTEXT_LENGTHS[normalizeModelId(model)]
210
+ ?? 0;
211
+ }
212
+ /** Resolve multimodal support. Priority: explicit > known lookup (exact then normalized) > false. */
213
+ export function getMultimodalSupport(model, explicit) {
214
+ if (explicit !== undefined)
215
+ return explicit;
216
+ return KNOWN_MULTIMODAL_MODELS.has(model)
217
+ || KNOWN_MULTIMODAL_MODELS.has(normalizeModelId(model));
218
+ }
219
+ /** Resolve thinking/reasoning support. Priority: explicit > known lookup (exact then normalized) > false. */
220
+ export function getThinkingSupport(model, explicit) {
221
+ if (explicit !== undefined)
222
+ return explicit;
223
+ return KNOWN_THINKING_MODELS.has(model)
224
+ || KNOWN_THINKING_MODELS.has(normalizeModelId(model));
225
+ }
226
+ /** Resolve native web search support. Priority: explicit > provider default > blacklist > true. */
227
+ export function getWebSearchSupport(model, explicit, provider) {
228
+ if (explicit !== undefined)
229
+ return explicit;
230
+ // OpenRouter: web search is a paid add-on, default to false.
231
+ // Users can explicitly enable via supports_web_search: true in config.
232
+ if (provider === "openrouter")
233
+ return false;
234
+ if (KNOWN_NO_WEB_SEARCH_MODELS.has(model)
235
+ || KNOWN_NO_WEB_SEARCH_MODELS.has(normalizeModelId(model)))
236
+ return false;
237
+ return true;
238
+ }
239
+ // ------------------------------------------------------------------
240
+ // Environment variable resolution
241
+ // ------------------------------------------------------------------
242
+ function resolveEnv(value) {
243
+ if (typeof value === "string" && value.startsWith("${") && value.endsWith("}")) {
244
+ const envName = value.slice(2, -1);
245
+ const resolved = process.env[envName];
246
+ if (resolved === undefined) {
247
+ throw new Error(`Environment variable '${envName}' is not set`);
248
+ }
249
+ return resolved;
250
+ }
251
+ return value;
252
+ }
253
+ function parseEnvRef(value) {
254
+ if (typeof value === "string" && value.startsWith("${") && value.endsWith("}")) {
255
+ return value.slice(2, -1);
256
+ }
257
+ return null;
258
+ }
259
+ function hasResolvableApiKey(value) {
260
+ if (typeof value !== "string" || value.trim() === "")
261
+ return false;
262
+ // OAuth token check
263
+ if (value === "oauth:openai-codex")
264
+ return hasOAuthTokens();
265
+ if (value.startsWith("${") && value.endsWith("}")) {
266
+ const envName = value.slice(2, -1);
267
+ const resolved = process.env[envName];
268
+ return typeof resolved === "string" && resolved.trim() !== "";
269
+ }
270
+ return true;
271
+ }
272
+ function requireConfigStringField(modelConfigName, cfg, field) {
273
+ const raw = cfg[field];
274
+ if (typeof raw !== "string" || raw.trim() === "") {
275
+ throw new Error(`Invalid model config '${modelConfigName}': missing required string field '${field}'`);
276
+ }
277
+ return raw;
278
+ }
279
+ function optionalConfigStringField(modelConfigName, cfg, field) {
280
+ const raw = cfg[field];
281
+ if (raw === undefined || raw === null)
282
+ return undefined;
283
+ if (typeof raw !== "string") {
284
+ throw new Error(`Invalid model config '${modelConfigName}': field '${field}' must be a string`);
285
+ }
286
+ return raw;
287
+ }
288
+ function optionalConfigNumberField(modelConfigName, cfg, field) {
289
+ const raw = cfg[field];
290
+ if (raw === undefined || raw === null)
291
+ return undefined;
292
+ if (typeof raw !== "number" || Number.isNaN(raw)) {
293
+ throw new Error(`Invalid model config '${modelConfigName}': field '${field}' must be a number`);
294
+ }
295
+ return raw;
296
+ }
297
+ function optionalConfigBooleanField(modelConfigName, cfg, field) {
298
+ const raw = cfg[field];
299
+ if (raw === undefined || raw === null)
300
+ return undefined;
301
+ if (typeof raw !== "boolean") {
302
+ throw new Error(`Invalid model config '${modelConfigName}': field '${field}' must be a boolean`);
303
+ }
304
+ return raw;
305
+ }
306
+ // ------------------------------------------------------------------
307
+ // Config path resolution
308
+ // ------------------------------------------------------------------
309
+ /** Default home directory for LongerAgent configuration. */
310
+ export const LONGERAGENT_HOME_DIR = ".longeragent";
311
+ /**
312
+ * Discover config, templates, and skills paths.
313
+ *
314
+ * Discovery chain (highest priority first):
315
+ * 1. CLI flags (--config, --templates)
316
+ * 2. ~/.longeragent/
317
+ * 3. Current working directory
318
+ */
319
+ export function resolveConfigPaths(opts) {
320
+ const home = join(homedir(), LONGERAGENT_HOME_DIR);
321
+ // --- Config ---
322
+ let configPath = null;
323
+ if (opts?.configFlag) {
324
+ configPath = existsSync(opts.configFlag) ? opts.configFlag : null;
325
+ }
326
+ else {
327
+ const homeConfig = join(home, "config.yaml");
328
+ const cwdConfig = join(process.cwd(), "config.yaml");
329
+ if (existsSync(homeConfig)) {
330
+ configPath = homeConfig;
331
+ }
332
+ else if (existsSync(cwdConfig)) {
333
+ configPath = cwdConfig;
334
+ }
335
+ }
336
+ // --- Templates ---
337
+ let templatesPath = null;
338
+ if (opts?.templatesFlag) {
339
+ templatesPath = isDir(opts.templatesFlag) ? opts.templatesFlag : null;
340
+ }
341
+ else {
342
+ const homeTemplates = join(home, "agent_templates");
343
+ const cwdTemplates = join(process.cwd(), "agent_templates");
344
+ if (isDir(homeTemplates)) {
345
+ templatesPath = homeTemplates;
346
+ }
347
+ else if (isDir(cwdTemplates)) {
348
+ templatesPath = cwdTemplates;
349
+ }
350
+ }
351
+ // --- Prompts ---
352
+ let promptsPath = null;
353
+ if (templatesPath) {
354
+ // Look for prompts/ sibling to templates directory
355
+ const siblingPrompts = join(join(templatesPath, ".."), "prompts");
356
+ if (isDir(siblingPrompts)) {
357
+ promptsPath = siblingPrompts;
358
+ }
359
+ }
360
+ if (!promptsPath) {
361
+ const homePrompts = join(home, "prompts");
362
+ const cwdPrompts = join(process.cwd(), "prompts");
363
+ if (isDir(homePrompts)) {
364
+ promptsPath = homePrompts;
365
+ }
366
+ else if (isDir(cwdPrompts)) {
367
+ promptsPath = cwdPrompts;
368
+ }
369
+ }
370
+ // --- Skills ---
371
+ let skillsPath = null;
372
+ if (templatesPath) {
373
+ // Look for skills/ sibling to templates directory
374
+ const siblingSkills = join(join(templatesPath, ".."), "skills");
375
+ if (isDir(siblingSkills)) {
376
+ skillsPath = siblingSkills;
377
+ }
378
+ }
379
+ if (!skillsPath) {
380
+ const homeSkills = join(home, "skills");
381
+ const cwdSkills = join(process.cwd(), "skills");
382
+ if (isDir(homeSkills)) {
383
+ skillsPath = homeSkills;
384
+ }
385
+ else if (isDir(cwdSkills)) {
386
+ skillsPath = cwdSkills;
387
+ }
388
+ }
389
+ return { configPath, templatesPath, promptsPath, skillsPath, homeDir: home };
390
+ }
391
+ function isDir(p) {
392
+ try {
393
+ return existsSync(p) && statSync(p).isDirectory();
394
+ }
395
+ catch {
396
+ return false;
397
+ }
398
+ }
399
+ // ------------------------------------------------------------------
400
+ // Bundled assets path
401
+ // ------------------------------------------------------------------
402
+ /** Return the root directory of the installed package (parent of dist/). */
403
+ export function getBundledAssetsDir() {
404
+ // This file compiles to dist/config.js, so ".." reaches the package root.
405
+ const thisFile = fileURLToPath(import.meta.url);
406
+ return join(dirname(thisFile), "..");
407
+ }
408
+ // ------------------------------------------------------------------
409
+ // Provider default base URLs
410
+ // ------------------------------------------------------------------
411
+ const PROVIDER_URLS = {
412
+ "openai-codex": "https://chatgpt.com/backend-api/codex",
413
+ "kimi": "https://api.moonshot.ai/v1",
414
+ "kimi-cn": "https://api.moonshot.cn/v1",
415
+ "kimi-ai": "https://api.moonshot.ai/v1",
416
+ "kimi-code": "https://api.kimi.com/coding/v1",
417
+ "glm": "https://open.bigmodel.cn/api/paas/v4",
418
+ "glm-intl": "https://api.z.ai/api/paas/v4",
419
+ "glm-code": "https://open.bigmodel.cn/api/coding/paas/v4",
420
+ "glm-intl-code": "https://api.z.ai/api/coding/paas/v4",
421
+ "minimax": "https://api.minimax.io/v1",
422
+ "minimax-cn": "https://api.minimaxi.com/v1",
423
+ "openrouter": "https://openrouter.ai/api/v1",
424
+ };
425
+ // ------------------------------------------------------------------
426
+ // Config class
427
+ // ------------------------------------------------------------------
428
+ export class Config {
429
+ _data;
430
+ _rawModels;
431
+ _models = new Map();
432
+ _mcpServers;
433
+ constructor(opts) {
434
+ if (opts?.raw) {
435
+ this._data = opts.raw;
436
+ }
437
+ else if (opts?.path) {
438
+ this._data = this._loadYaml(opts.path);
439
+ }
440
+ else {
441
+ this._data = { models: {} };
442
+ }
443
+ this._rawModels = this._data["models"] ?? {};
444
+ this._mcpServers = this._parseMcpServers();
445
+ }
446
+ _loadYaml(path) {
447
+ const content = readFileSync(path, "utf-8");
448
+ return yaml.load(content) ?? {};
449
+ }
450
+ _buildModel(name, cfg) {
451
+ const provider = requireConfigStringField(name, cfg, "provider");
452
+ const modelName = requireConfigStringField(name, cfg, "model");
453
+ const apiKeyRaw = requireConfigStringField(name, cfg, "api_key");
454
+ const baseUrl = optionalConfigStringField(name, cfg, "base_url") || PROVIDER_URLS[provider];
455
+ const apiKeyEnv = parseEnvRef(apiKeyRaw);
456
+ const resolvedApiKey = (() => {
457
+ // OAuth token resolution
458
+ if (apiKeyRaw === "oauth:openai-codex") {
459
+ const token = readOAuthAccessToken();
460
+ if (!token) {
461
+ throw new Error(`Missing OAuth token for model config '${name}' (${provider}/${modelName}): ` +
462
+ "no OpenAI OAuth credentials stored.\n" +
463
+ "Run 'longeragent oauth' to log in with your ChatGPT account.");
464
+ }
465
+ return token;
466
+ }
467
+ if (!apiKeyEnv)
468
+ return apiKeyRaw;
469
+ const fromEnv = process.env[apiKeyEnv];
470
+ if (typeof fromEnv === "string" && fromEnv.trim() !== "") {
471
+ return fromEnv;
472
+ }
473
+ throw new Error(`Missing API key for model config '${name}' (${provider}/${modelName}): ` +
474
+ `environment variable '${apiKeyEnv}' is not set.\n` +
475
+ "Run 'longeragent init' to configure API keys, or export that variable and retry.");
476
+ })();
477
+ const knownKeys = new Set([
478
+ "provider", "model", "api_key", "base_url",
479
+ "temperature", "max_tokens", "context_length",
480
+ "supports_multimodal", "supports_thinking", "thinking_budget",
481
+ "supports_web_search",
482
+ ]);
483
+ const extra = {};
484
+ for (const [k, v] of Object.entries(cfg)) {
485
+ if (!knownKeys.has(k))
486
+ extra[k] = v;
487
+ }
488
+ const explicitCtxLen = optionalConfigNumberField(name, cfg, "context_length") ?? 0;
489
+ const temperature = optionalConfigNumberField(name, cfg, "temperature") ?? 0.7;
490
+ const maxTokens = optionalConfigNumberField(name, cfg, "max_tokens") ?? 32_000;
491
+ const thinkingBudget = optionalConfigNumberField(name, cfg, "thinking_budget") ?? 0;
492
+ const supportsMultimodalOverride = optionalConfigBooleanField(name, cfg, "supports_multimodal");
493
+ const supportsThinkingOverride = optionalConfigBooleanField(name, cfg, "supports_thinking");
494
+ const supportsWebSearchOverride = optionalConfigBooleanField(name, cfg, "supports_web_search");
495
+ return {
496
+ name,
497
+ provider,
498
+ model: modelName,
499
+ apiKey: resolvedApiKey,
500
+ baseUrl,
501
+ temperature,
502
+ maxTokens,
503
+ contextLength: getContextLength(modelName, explicitCtxLen),
504
+ supportsMultimodal: getMultimodalSupport(modelName, supportsMultimodalOverride),
505
+ supportsThinking: getThinkingSupport(modelName, supportsThinkingOverride),
506
+ thinkingBudget,
507
+ supportsWebSearch: getWebSearchSupport(modelName, supportsWebSearchOverride, provider),
508
+ extra,
509
+ };
510
+ }
511
+ getModel(name) {
512
+ const cached = this._models.get(name);
513
+ if (cached)
514
+ return cached;
515
+ const raw = this._rawModels[name];
516
+ if (!raw) {
517
+ const available = Object.keys(this._rawModels).join(", ") || "(none)";
518
+ throw new Error(`Model config '${name}' not found. Available: ${available}`);
519
+ }
520
+ const model = this._buildModel(name, raw);
521
+ this._models.set(name, model);
522
+ return model;
523
+ }
524
+ get modelNames() {
525
+ return Object.keys(this._rawModels);
526
+ }
527
+ /**
528
+ * Return raw model entries without resolving env vars.
529
+ * Useful for UI that needs to show missing API keys instead of throwing.
530
+ */
531
+ listModelEntries() {
532
+ const out = [];
533
+ for (const [name, cfg] of Object.entries(this._rawModels)) {
534
+ const provider = typeof cfg["provider"] === "string" ? cfg["provider"] : "";
535
+ const model = typeof cfg["model"] === "string" ? cfg["model"] : "";
536
+ const apiKeyRaw = typeof cfg["api_key"] === "string" ? cfg["api_key"] : "";
537
+ out.push({
538
+ name,
539
+ provider,
540
+ model,
541
+ apiKeyRaw,
542
+ hasResolvedApiKey: hasResolvableApiKey(apiKeyRaw),
543
+ });
544
+ }
545
+ return out;
546
+ }
547
+ /** Find the first model config name matching provider + model ID exactly. */
548
+ findModelConfigName(provider, model) {
549
+ for (const [name, cfg] of Object.entries(this._rawModels)) {
550
+ if (cfg["provider"] === provider && cfg["model"] === model) {
551
+ return name;
552
+ }
553
+ }
554
+ return undefined;
555
+ }
556
+ /**
557
+ * Insert or replace a raw model config at runtime.
558
+ * This mutates in-memory config only; it does not write config.yaml.
559
+ */
560
+ upsertModelRaw(name, cfg) {
561
+ this._rawModels[name] = { ...cfg };
562
+ if (!this._data["models"] || typeof this._data["models"] !== "object") {
563
+ this._data["models"] = {};
564
+ }
565
+ this._data["models"][name] = this._rawModels[name];
566
+ this._models.delete(name);
567
+ }
568
+ /**
569
+ * Return the best default model name.
570
+ * Priority: explicit default_model > first with resolvable API key > first model.
571
+ */
572
+ get defaultModel() {
573
+ // 1. Explicit config key
574
+ const explicit = this._data["default_model"];
575
+ if (explicit && explicit in this._rawModels)
576
+ return explicit;
577
+ // 2. First model with resolvable API key
578
+ for (const [name, cfg] of Object.entries(this._rawModels)) {
579
+ const apiKeyRaw = cfg["api_key"] ?? "";
580
+ if (typeof apiKeyRaw === "string" && apiKeyRaw.startsWith("${") && apiKeyRaw.endsWith("}")) {
581
+ const envName = apiKeyRaw.slice(2, -1);
582
+ if (process.env[envName])
583
+ return name;
584
+ }
585
+ else if (apiKeyRaw) {
586
+ return name; // literal key, always available
587
+ }
588
+ }
589
+ // 3. Fallback: first model
590
+ const names = Object.keys(this._rawModels);
591
+ return names.length > 0 ? names[0] : undefined;
592
+ }
593
+ /** Return the sub_agent_model config name, or undefined if not set. */
594
+ get subAgentModelName() {
595
+ return this._data["sub_agent_model"];
596
+ }
597
+ /** Return path overrides from config.yaml `paths:` section. */
598
+ get pathOverrides() {
599
+ const raw = this._data["paths"];
600
+ if (!raw)
601
+ return {};
602
+ const result = {};
603
+ if (raw["project_root"])
604
+ result.projectRoot = raw["project_root"];
605
+ if (raw["session_artifacts"])
606
+ result.sessionArtifacts = raw["session_artifacts"];
607
+ if (raw["system_data"])
608
+ result.systemData = raw["system_data"];
609
+ return result;
610
+ }
611
+ // ------------------------------------------------------------------
612
+ // MCP server configuration
613
+ // ------------------------------------------------------------------
614
+ _parseMcpServers() {
615
+ const raw = this._data["mcp_servers"];
616
+ if (!raw)
617
+ return [];
618
+ const servers = [];
619
+ for (const [name, cfg] of Object.entries(raw)) {
620
+ const env = {};
621
+ const rawEnv = cfg["env"];
622
+ if (rawEnv) {
623
+ for (const [k, v] of Object.entries(rawEnv)) {
624
+ env[k] = resolveEnv(String(v));
625
+ }
626
+ }
627
+ servers.push({
628
+ name,
629
+ transport: cfg["transport"] ?? "stdio",
630
+ command: cfg["command"] ?? "",
631
+ args: cfg["args"] ?? [],
632
+ url: cfg["url"] ?? "",
633
+ env,
634
+ envAllowlist: Array.isArray(cfg["env_allowlist"])
635
+ ? cfg["env_allowlist"].map((v) => String(v))
636
+ : undefined,
637
+ sensitiveTools: Array.isArray(cfg["sensitive_tools"])
638
+ ? cfg["sensitive_tools"].map((v) => String(v))
639
+ : undefined,
640
+ });
641
+ }
642
+ return servers;
643
+ }
644
+ get mcpServerConfigs() {
645
+ return this._mcpServers;
646
+ }
647
+ }
648
+ //# sourceMappingURL=config.js.map