@codeany/open-agent-sdk 0.2.2 → 0.2.4

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 (280) hide show
  1. package/dist/agent.d.ts +113 -0
  2. package/dist/agent.d.ts.map +1 -0
  3. package/dist/agent.js +453 -0
  4. package/dist/agent.js.map +1 -0
  5. package/dist/engine.d.ts +62 -0
  6. package/dist/engine.d.ts.map +1 -0
  7. package/dist/engine.js +496 -0
  8. package/dist/engine.js.map +1 -0
  9. package/dist/hooks.d.ts +111 -0
  10. package/dist/hooks.d.ts.map +1 -0
  11. package/dist/hooks.js +179 -0
  12. package/dist/hooks.js.map +1 -0
  13. package/dist/index.d.ts +66 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +133 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/mcp/client.d.ts +19 -0
  18. package/dist/mcp/client.d.ts.map +1 -0
  19. package/dist/mcp/client.js +126 -0
  20. package/dist/mcp/client.js.map +1 -0
  21. package/dist/providers/anthropic.d.ts +17 -0
  22. package/dist/providers/anthropic.d.ts.map +1 -0
  23. package/dist/providers/anthropic.js +47 -0
  24. package/dist/providers/anthropic.js.map +1 -0
  25. package/dist/providers/index.d.ts +20 -0
  26. package/dist/providers/index.d.ts.map +1 -0
  27. package/dist/providers/index.js +26 -0
  28. package/dist/providers/index.js.map +1 -0
  29. package/dist/providers/openai.d.ts +26 -0
  30. package/dist/providers/openai.d.ts.map +1 -0
  31. package/dist/providers/openai.js +212 -0
  32. package/dist/providers/openai.js.map +1 -0
  33. package/dist/providers/types.d.ts +84 -0
  34. package/dist/providers/types.d.ts.map +1 -0
  35. package/dist/providers/types.js +11 -0
  36. package/dist/providers/types.js.map +1 -0
  37. package/dist/sdk-mcp-server.d.ts +52 -0
  38. package/dist/sdk-mcp-server.d.ts.map +1 -0
  39. package/dist/sdk-mcp-server.js +57 -0
  40. package/dist/sdk-mcp-server.js.map +1 -0
  41. package/dist/session.d.ts +73 -0
  42. package/dist/session.d.ts.map +1 -0
  43. package/dist/session.js +159 -0
  44. package/dist/session.js.map +1 -0
  45. package/dist/skills/bundled/commit.d.ts +7 -0
  46. package/dist/skills/bundled/commit.d.ts.map +1 -0
  47. package/dist/skills/bundled/commit.js +35 -0
  48. package/dist/skills/bundled/commit.js.map +1 -0
  49. package/dist/skills/bundled/debug.d.ts +7 -0
  50. package/dist/skills/bundled/debug.d.ts.map +1 -0
  51. package/{src/skills/bundled/debug.ts → dist/skills/bundled/debug.js} +20 -22
  52. package/dist/skills/bundled/debug.js.map +1 -0
  53. package/dist/skills/bundled/index.d.ts +11 -0
  54. package/dist/skills/bundled/index.d.ts.map +1 -0
  55. package/dist/skills/bundled/index.js +26 -0
  56. package/dist/skills/bundled/index.js.map +1 -0
  57. package/dist/skills/bundled/review.d.ts +7 -0
  58. package/dist/skills/bundled/review.d.ts.map +1 -0
  59. package/{src/skills/bundled/review.ts → dist/skills/bundled/review.js} +18 -21
  60. package/dist/skills/bundled/review.js.map +1 -0
  61. package/dist/skills/bundled/simplify.d.ts +8 -0
  62. package/dist/skills/bundled/simplify.d.ts.map +1 -0
  63. package/{src/skills/bundled/simplify.ts → dist/skills/bundled/simplify.js} +16 -19
  64. package/dist/skills/bundled/simplify.js.map +1 -0
  65. package/dist/skills/bundled/test.d.ts +7 -0
  66. package/dist/skills/bundled/test.d.ts.map +1 -0
  67. package/{src/skills/bundled/test.ts → dist/skills/bundled/test.js} +18 -21
  68. package/dist/skills/bundled/test.js.map +1 -0
  69. package/dist/skills/index.d.ts +7 -0
  70. package/dist/skills/index.d.ts.map +1 -0
  71. package/dist/skills/index.js +8 -0
  72. package/dist/skills/index.js.map +1 -0
  73. package/dist/skills/registry.d.ts +43 -0
  74. package/dist/skills/registry.d.ts.map +1 -0
  75. package/dist/skills/registry.js +111 -0
  76. package/dist/skills/registry.js.map +1 -0
  77. package/dist/skills/types.d.ts +83 -0
  78. package/dist/skills/types.d.ts.map +1 -0
  79. package/dist/skills/types.js +8 -0
  80. package/dist/skills/types.js.map +1 -0
  81. package/dist/tool-helper.d.ts +73 -0
  82. package/dist/tool-helper.d.ts.map +1 -0
  83. package/dist/tool-helper.js +86 -0
  84. package/dist/tool-helper.js.map +1 -0
  85. package/dist/tools/agent-tool.d.ts +17 -0
  86. package/dist/tools/agent-tool.d.ts.map +1 -0
  87. package/dist/tools/agent-tool.js +146 -0
  88. package/dist/tools/agent-tool.js.map +1 -0
  89. package/dist/tools/ask-user.d.ts +18 -0
  90. package/dist/tools/ask-user.d.ts.map +1 -0
  91. package/dist/tools/ask-user.js +72 -0
  92. package/dist/tools/ask-user.js.map +1 -0
  93. package/dist/tools/bash.d.ts +5 -0
  94. package/dist/tools/bash.d.ts.map +1 -0
  95. package/dist/tools/bash.js +67 -0
  96. package/dist/tools/bash.js.map +1 -0
  97. package/dist/tools/config-tool.d.ts +20 -0
  98. package/dist/tools/config-tool.d.ts.map +1 -0
  99. package/dist/tools/config-tool.js +83 -0
  100. package/dist/tools/config-tool.js.map +1 -0
  101. package/dist/tools/cron-tools.d.ts +33 -0
  102. package/dist/tools/cron-tools.d.ts.map +1 -0
  103. package/dist/tools/cron-tools.js +128 -0
  104. package/dist/tools/cron-tools.js.map +1 -0
  105. package/dist/tools/edit.d.ts +5 -0
  106. package/dist/tools/edit.d.ts.map +1 -0
  107. package/dist/tools/edit.js +70 -0
  108. package/dist/tools/edit.js.map +1 -0
  109. package/dist/tools/glob.d.ts +5 -0
  110. package/dist/tools/glob.d.ts.map +1 -0
  111. package/dist/tools/glob.js +75 -0
  112. package/dist/tools/glob.js.map +1 -0
  113. package/dist/tools/grep.d.ts +5 -0
  114. package/dist/tools/grep.d.ts.map +1 -0
  115. package/dist/tools/grep.js +168 -0
  116. package/dist/tools/grep.js.map +1 -0
  117. package/dist/tools/index.d.ts +45 -0
  118. package/dist/tools/index.d.ts.map +1 -0
  119. package/dist/tools/index.js +162 -0
  120. package/dist/tools/index.js.map +1 -0
  121. package/dist/tools/lsp-tool.d.ts +9 -0
  122. package/dist/tools/lsp-tool.d.ts.map +1 -0
  123. package/dist/tools/lsp-tool.js +137 -0
  124. package/dist/tools/lsp-tool.js.map +1 -0
  125. package/dist/tools/mcp-resource-tools.d.ts +14 -0
  126. package/dist/tools/mcp-resource-tools.d.ts.map +1 -0
  127. package/dist/tools/mcp-resource-tools.js +117 -0
  128. package/dist/tools/mcp-resource-tools.js.map +1 -0
  129. package/dist/tools/notebook-edit.d.ts +5 -0
  130. package/dist/tools/notebook-edit.d.ts.map +1 -0
  131. package/dist/tools/notebook-edit.js +85 -0
  132. package/dist/tools/notebook-edit.js.map +1 -0
  133. package/dist/tools/plan-tools.d.ts +12 -0
  134. package/dist/tools/plan-tools.d.ts.map +1 -0
  135. package/dist/tools/plan-tools.js +77 -0
  136. package/dist/tools/plan-tools.js.map +1 -0
  137. package/dist/tools/read.d.ts +5 -0
  138. package/dist/tools/read.d.ts.map +1 -0
  139. package/dist/tools/read.js +66 -0
  140. package/dist/tools/read.js.map +1 -0
  141. package/dist/tools/send-message.d.ts +31 -0
  142. package/dist/tools/send-message.d.ts.map +1 -0
  143. package/dist/tools/send-message.js +77 -0
  144. package/dist/tools/send-message.js.map +1 -0
  145. package/dist/tools/skill-tool.d.ts +9 -0
  146. package/dist/tools/skill-tool.d.ts.map +1 -0
  147. package/dist/tools/skill-tool.js +115 -0
  148. package/dist/tools/skill-tool.js.map +1 -0
  149. package/dist/tools/task-tools.d.ts +48 -0
  150. package/dist/tools/task-tools.d.ts.map +1 -0
  151. package/dist/tools/task-tools.js +242 -0
  152. package/dist/tools/task-tools.js.map +1 -0
  153. package/dist/tools/team-tools.d.ts +34 -0
  154. package/dist/tools/team-tools.d.ts.map +1 -0
  155. package/dist/tools/team-tools.js +103 -0
  156. package/dist/tools/team-tools.js.map +1 -0
  157. package/dist/tools/todo-tool.d.ts +22 -0
  158. package/dist/tools/todo-tool.d.ts.map +1 -0
  159. package/dist/tools/todo-tool.js +93 -0
  160. package/dist/tools/todo-tool.js.map +1 -0
  161. package/dist/tools/tool-search.d.ts +13 -0
  162. package/dist/tools/tool-search.d.ts.map +1 -0
  163. package/dist/tools/tool-search.js +76 -0
  164. package/dist/tools/tool-search.js.map +1 -0
  165. package/dist/tools/types.d.ts +29 -0
  166. package/dist/tools/types.d.ts.map +1 -0
  167. package/dist/tools/types.js +52 -0
  168. package/dist/tools/types.js.map +1 -0
  169. package/dist/tools/web-fetch.d.ts +5 -0
  170. package/dist/tools/web-fetch.d.ts.map +1 -0
  171. package/dist/tools/web-fetch.js +60 -0
  172. package/dist/tools/web-fetch.js.map +1 -0
  173. package/dist/tools/web-search.d.ts +5 -0
  174. package/dist/tools/web-search.d.ts.map +1 -0
  175. package/dist/tools/web-search.js +77 -0
  176. package/dist/tools/web-search.js.map +1 -0
  177. package/dist/tools/worktree-tools.d.ts +10 -0
  178. package/dist/tools/worktree-tools.d.ts.map +1 -0
  179. package/dist/tools/worktree-tools.js +130 -0
  180. package/dist/tools/worktree-tools.js.map +1 -0
  181. package/dist/tools/write.d.ts +5 -0
  182. package/dist/tools/write.d.ts.map +1 -0
  183. package/dist/tools/write.js +40 -0
  184. package/dist/tools/write.js.map +1 -0
  185. package/dist/types.d.ts +432 -0
  186. package/dist/types.d.ts.map +1 -0
  187. package/dist/types.js +5 -0
  188. package/dist/types.js.map +1 -0
  189. package/dist/utils/compact.d.ts +44 -0
  190. package/dist/utils/compact.d.ts.map +1 -0
  191. package/dist/utils/compact.js +162 -0
  192. package/dist/utils/compact.js.map +1 -0
  193. package/dist/utils/context.d.ts +35 -0
  194. package/dist/utils/context.d.ts.map +1 -0
  195. package/dist/utils/context.js +174 -0
  196. package/dist/utils/context.js.map +1 -0
  197. package/dist/utils/fileCache.d.ts +64 -0
  198. package/dist/utils/fileCache.d.ts.map +1 -0
  199. package/dist/utils/fileCache.js +117 -0
  200. package/dist/utils/fileCache.js.map +1 -0
  201. package/dist/utils/messages.d.ts +57 -0
  202. package/dist/utils/messages.d.ts.map +1 -0
  203. package/dist/utils/messages.js +152 -0
  204. package/dist/utils/messages.js.map +1 -0
  205. package/dist/utils/retry.d.ts +48 -0
  206. package/dist/utils/retry.d.ts.map +1 -0
  207. package/dist/utils/retry.js +111 -0
  208. package/dist/utils/retry.js.map +1 -0
  209. package/dist/utils/tokens.d.ts +57 -0
  210. package/dist/utils/tokens.d.ts.map +1 -0
  211. package/dist/utils/tokens.js +134 -0
  212. package/dist/utils/tokens.js.map +1 -0
  213. package/package.json +6 -1
  214. package/.env.example +0 -8
  215. package/examples/01-simple-query.ts +0 -43
  216. package/examples/02-multi-tool.ts +0 -44
  217. package/examples/03-multi-turn.ts +0 -39
  218. package/examples/04-prompt-api.ts +0 -29
  219. package/examples/05-custom-system-prompt.ts +0 -26
  220. package/examples/06-mcp-server.ts +0 -49
  221. package/examples/07-custom-tools.ts +0 -87
  222. package/examples/08-official-api-compat.ts +0 -38
  223. package/examples/09-subagents.ts +0 -48
  224. package/examples/10-permissions.ts +0 -40
  225. package/examples/11-custom-mcp-tools.ts +0 -101
  226. package/examples/12-skills.ts +0 -88
  227. package/examples/13-hooks.ts +0 -88
  228. package/examples/14-openai-compat.ts +0 -71
  229. package/examples/web/index.html +0 -365
  230. package/examples/web/server.ts +0 -157
  231. package/src/agent.ts +0 -516
  232. package/src/engine.ts +0 -633
  233. package/src/hooks.ts +0 -261
  234. package/src/index.ts +0 -426
  235. package/src/mcp/client.ts +0 -150
  236. package/src/providers/anthropic.ts +0 -60
  237. package/src/providers/index.ts +0 -34
  238. package/src/providers/openai.ts +0 -315
  239. package/src/providers/types.ts +0 -85
  240. package/src/sdk-mcp-server.ts +0 -78
  241. package/src/session.ts +0 -227
  242. package/src/skills/bundled/commit.ts +0 -38
  243. package/src/skills/bundled/index.ts +0 -28
  244. package/src/skills/index.ts +0 -25
  245. package/src/skills/registry.ts +0 -133
  246. package/src/skills/types.ts +0 -99
  247. package/src/tool-helper.ts +0 -127
  248. package/src/tools/agent-tool.ts +0 -164
  249. package/src/tools/ask-user.ts +0 -79
  250. package/src/tools/bash.ts +0 -75
  251. package/src/tools/config-tool.ts +0 -89
  252. package/src/tools/cron-tools.ts +0 -153
  253. package/src/tools/edit.ts +0 -74
  254. package/src/tools/glob.ts +0 -77
  255. package/src/tools/grep.ts +0 -168
  256. package/src/tools/index.ts +0 -240
  257. package/src/tools/lsp-tool.ts +0 -163
  258. package/src/tools/mcp-resource-tools.ts +0 -125
  259. package/src/tools/notebook-edit.ts +0 -93
  260. package/src/tools/plan-tools.ts +0 -88
  261. package/src/tools/read.ts +0 -73
  262. package/src/tools/send-message.ts +0 -96
  263. package/src/tools/skill-tool.ts +0 -133
  264. package/src/tools/task-tools.ts +0 -290
  265. package/src/tools/team-tools.ts +0 -128
  266. package/src/tools/todo-tool.ts +0 -112
  267. package/src/tools/tool-search.ts +0 -87
  268. package/src/tools/types.ts +0 -66
  269. package/src/tools/web-fetch.ts +0 -66
  270. package/src/tools/web-search.ts +0 -86
  271. package/src/tools/worktree-tools.ts +0 -140
  272. package/src/tools/write.ts +0 -42
  273. package/src/types.ts +0 -486
  274. package/src/utils/compact.ts +0 -207
  275. package/src/utils/context.ts +0 -191
  276. package/src/utils/fileCache.ts +0 -148
  277. package/src/utils/messages.ts +0 -195
  278. package/src/utils/retry.ts +0 -140
  279. package/src/utils/tokens.ts +0 -145
  280. package/tsconfig.json +0 -19
@@ -1,191 +0,0 @@
1
- /**
2
- * System & User Context
3
- *
4
- * Builds context for the system prompt:
5
- * - Git status injection (branch, commits, status)
6
- * - AGENT.md / project context discovery and injection
7
- * - Working directory info
8
- * - Date injection
9
- */
10
-
11
- import { execSync } from 'child_process'
12
- import { readFile, stat } from 'fs/promises'
13
- import { join, resolve } from 'path'
14
-
15
- // Memoization cache
16
- let cachedGitStatus: string | null = null
17
- let cachedGitStatusCwd: string | null = null
18
-
19
- /**
20
- * Get git status info for system prompt.
21
- * Memoized per cwd (cleared on new session).
22
- */
23
- export async function getGitStatus(cwd: string): Promise<string> {
24
- if (cachedGitStatus && cachedGitStatusCwd === cwd) {
25
- return cachedGitStatus
26
- }
27
-
28
- try {
29
- const parts: string[] = []
30
-
31
- const gitExec = (cmd: string, timeoutMs = 5000): string | null => {
32
- try {
33
- return execSync(cmd, {
34
- cwd, timeout: timeoutMs, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
35
- }).trim()
36
- } catch {
37
- return null
38
- }
39
- }
40
-
41
- // Check if this is a git repo at all
42
- if (!gitExec('git rev-parse --git-dir')) return ''
43
-
44
- // Current branch
45
- const branch = gitExec('git rev-parse --abbrev-ref HEAD')
46
- if (branch) parts.push(`Current branch: ${branch}`)
47
-
48
- // Main branch detection
49
- const mainBranch = detectMainBranch(cwd)
50
- if (mainBranch) parts.push(`Main branch: ${mainBranch}`)
51
-
52
- // Git user
53
- const user = gitExec('git config user.name', 3000)
54
- if (user) parts.push(`Git user: ${user}`)
55
-
56
- // Status (staged + unstaged)
57
- const status = gitExec('git status --short')
58
- if (status) {
59
- const truncated = status.length > 2000
60
- ? status.slice(0, 2000) + '\n...(truncated)'
61
- : status
62
- parts.push(`Status:\n${truncated}`)
63
- }
64
-
65
- // Recent commits (only if HEAD exists)
66
- const hasHead = gitExec('git rev-parse HEAD')
67
- if (hasHead) {
68
- const log = gitExec('git log --oneline -5 --no-decorate')
69
- if (log) parts.push(`Recent commits:\n${log}`)
70
- }
71
-
72
- cachedGitStatus = parts.join('\n\n')
73
- cachedGitStatusCwd = cwd
74
-
75
- return cachedGitStatus
76
- } catch {
77
- return ''
78
- }
79
- }
80
-
81
- /**
82
- * Detect the main branch name (main or master).
83
- */
84
- function detectMainBranch(cwd: string): string | null {
85
- try {
86
- const branches = execSync('git branch -l main master', {
87
- cwd, timeout: 3000, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
88
- }).trim()
89
- if (branches.includes('main')) return 'main'
90
- if (branches.includes('master')) return 'master'
91
- return null
92
- } catch {
93
- return null
94
- }
95
- }
96
-
97
- /**
98
- * Discover project context files (AGENT.md, CLAUDE.md) in the project.
99
- */
100
- export async function discoverProjectContextFiles(cwd: string): Promise<string[]> {
101
- const candidates = [
102
- join(cwd, 'AGENT.md'),
103
- join(cwd, 'CLAUDE.md'),
104
- join(cwd, '.claude', 'CLAUDE.md'),
105
- join(cwd, 'claude.md'),
106
- ]
107
-
108
- // Also check home directory
109
- const home = process.env.HOME || process.env.USERPROFILE || ''
110
- if (home) {
111
- candidates.push(
112
- join(home, '.claude', 'CLAUDE.md'),
113
- )
114
- }
115
-
116
- const found: string[] = []
117
- for (const path of candidates) {
118
- try {
119
- const s = await stat(path)
120
- if (s.isFile()) {
121
- found.push(path)
122
- }
123
- } catch {
124
- // File doesn't exist
125
- }
126
- }
127
-
128
- return found
129
- }
130
-
131
- /**
132
- * Read project context file content from discovered files.
133
- */
134
- export async function readProjectContextContent(cwd: string): Promise<string> {
135
- const files = await discoverProjectContextFiles(cwd)
136
- if (files.length === 0) return ''
137
-
138
- const parts: string[] = []
139
- for (const file of files) {
140
- try {
141
- const content = await readFile(file, 'utf-8')
142
- if (content.trim()) {
143
- parts.push(`# From ${file}:\n${content.trim()}`)
144
- }
145
- } catch {
146
- // Skip unreadable files
147
- }
148
- }
149
-
150
- return parts.join('\n\n')
151
- }
152
-
153
- /**
154
- * Get system context for the system prompt.
155
- */
156
- export async function getSystemContext(cwd: string): Promise<string> {
157
- const parts: string[] = []
158
-
159
- const gitStatus = await getGitStatus(cwd)
160
- if (gitStatus) {
161
- parts.push(`gitStatus: ${gitStatus}`)
162
- }
163
-
164
- return parts.join('\n\n')
165
- }
166
-
167
- /**
168
- * Get user context (AGENT.md, date, etc).
169
- */
170
- export async function getUserContext(cwd: string): Promise<string> {
171
- const parts: string[] = []
172
-
173
- // Current date
174
- parts.push(`# currentDate\nToday's date is ${new Date().toISOString().split('T')[0]}.`)
175
-
176
- // Project context files
177
- const projectCtx = await readProjectContextContent(cwd)
178
- if (projectCtx) {
179
- parts.push(projectCtx)
180
- }
181
-
182
- return parts.join('\n\n')
183
- }
184
-
185
- /**
186
- * Clear memoized context (call between sessions).
187
- */
188
- export function clearContextCache(): void {
189
- cachedGitStatus = null
190
- cachedGitStatusCwd = null
191
- }
@@ -1,148 +0,0 @@
1
- /**
2
- * File State LRU Cache
3
- *
4
- * Bounded cache for file contents with path normalization.
5
- * Used to track file states for compaction diffs and
6
- * avoiding redundant reads.
7
- */
8
-
9
- import { normalize, resolve } from 'path'
10
-
11
- /**
12
- * Cached file state.
13
- */
14
- export interface FileState {
15
- content: string
16
- timestamp: number
17
- offset?: number
18
- limit?: number
19
- isPartialView?: boolean
20
- }
21
-
22
- /**
23
- * LRU file state cache with size limits.
24
- */
25
- export class FileStateCache {
26
- private cache = new Map<string, FileState>()
27
- private maxEntries: number
28
- private maxSizeBytes: number
29
- private currentSizeBytes = 0
30
-
31
- constructor(maxEntries: number = 100, maxSizeBytes: number = 25 * 1024 * 1024) {
32
- this.maxEntries = maxEntries
33
- this.maxSizeBytes = maxSizeBytes
34
- }
35
-
36
- /**
37
- * Normalize a file path for cache lookup.
38
- */
39
- private normalizePath(filePath: string): string {
40
- return normalize(resolve(filePath))
41
- }
42
-
43
- /**
44
- * Get a cached file state.
45
- */
46
- get(filePath: string): FileState | undefined {
47
- const key = this.normalizePath(filePath)
48
- const entry = this.cache.get(key)
49
- if (entry) {
50
- // Move to end (most recently used)
51
- this.cache.delete(key)
52
- this.cache.set(key, entry)
53
- }
54
- return entry
55
- }
56
-
57
- /**
58
- * Set a cached file state.
59
- */
60
- set(filePath: string, state: FileState): void {
61
- const key = this.normalizePath(filePath)
62
-
63
- // Remove old entry if exists
64
- const old = this.cache.get(key)
65
- if (old) {
66
- this.currentSizeBytes -= Buffer.byteLength(old.content, 'utf-8')
67
- this.cache.delete(key)
68
- }
69
-
70
- const newSize = Buffer.byteLength(state.content, 'utf-8')
71
-
72
- // Evict entries if necessary
73
- while (
74
- (this.cache.size >= this.maxEntries || this.currentSizeBytes + newSize > this.maxSizeBytes) &&
75
- this.cache.size > 0
76
- ) {
77
- const firstKey = this.cache.keys().next().value
78
- if (firstKey) {
79
- const entry = this.cache.get(firstKey)
80
- if (entry) {
81
- this.currentSizeBytes -= Buffer.byteLength(entry.content, 'utf-8')
82
- }
83
- this.cache.delete(firstKey)
84
- }
85
- }
86
-
87
- this.cache.set(key, state)
88
- this.currentSizeBytes += newSize
89
- }
90
-
91
- /**
92
- * Delete a cached entry.
93
- */
94
- delete(filePath: string): boolean {
95
- const key = this.normalizePath(filePath)
96
- const entry = this.cache.get(key)
97
- if (entry) {
98
- this.currentSizeBytes -= Buffer.byteLength(entry.content, 'utf-8')
99
- this.cache.delete(key)
100
- return true
101
- }
102
- return false
103
- }
104
-
105
- /**
106
- * Clear all cached entries.
107
- */
108
- clear(): void {
109
- this.cache.clear()
110
- this.currentSizeBytes = 0
111
- }
112
-
113
- /**
114
- * Get the number of cached entries.
115
- */
116
- get size(): number {
117
- return this.cache.size
118
- }
119
-
120
- /**
121
- * Get all cached file paths.
122
- */
123
- keys(): string[] {
124
- return Array.from(this.cache.keys())
125
- }
126
-
127
- /**
128
- * Clone the cache.
129
- */
130
- clone(): FileStateCache {
131
- const clone = new FileStateCache(this.maxEntries, this.maxSizeBytes)
132
- for (const [key, value] of this.cache) {
133
- clone.cache.set(key, { ...value })
134
- }
135
- clone.currentSizeBytes = this.currentSizeBytes
136
- return clone
137
- }
138
- }
139
-
140
- /**
141
- * Create a file state cache with default limits.
142
- */
143
- export function createFileStateCache(
144
- maxEntries: number = 100,
145
- maxSizeBytes: number = 25 * 1024 * 1024,
146
- ): FileStateCache {
147
- return new FileStateCache(maxEntries, maxSizeBytes)
148
- }
@@ -1,195 +0,0 @@
1
- /**
2
- * Message Utilities
3
- *
4
- * Message creation factories, normalization for API,
5
- * synthetic placeholders, and content processing.
6
- */
7
-
8
- import type { Message, UserMessage, AssistantMessage, TokenUsage } from '../types.js'
9
-
10
- /**
11
- * Create a user message.
12
- */
13
- export function createUserMessage(
14
- content: string | any[],
15
- options?: {
16
- uuid?: string
17
- isMeta?: boolean
18
- toolUseResult?: unknown
19
- },
20
- ): UserMessage {
21
- return {
22
- type: 'user',
23
- message: {
24
- role: 'user',
25
- content,
26
- },
27
- uuid: options?.uuid || crypto.randomUUID(),
28
- timestamp: new Date().toISOString(),
29
- }
30
- }
31
-
32
- /**
33
- * Create an assistant message.
34
- */
35
- export function createAssistantMessage(
36
- content: any[],
37
- usage?: TokenUsage,
38
- ): AssistantMessage {
39
- return {
40
- type: 'assistant',
41
- message: {
42
- role: 'assistant',
43
- content,
44
- },
45
- uuid: crypto.randomUUID(),
46
- timestamp: new Date().toISOString(),
47
- usage,
48
- }
49
- }
50
-
51
- /**
52
- * Normalize messages for the LLM API.
53
- * Ensures proper message format, strips internal metadata,
54
- * and fixes tool result pairing.
55
- */
56
- export function normalizeMessagesForAPI(
57
- messages: Array<{ role: string; content: any }>,
58
- ): Array<{ role: string; content: any }> {
59
- const normalized: Array<{ role: string; content: any }> = []
60
-
61
- for (let i = 0; i < messages.length; i++) {
62
- const msg = messages[i]
63
-
64
- // Ensure alternating user/assistant messages
65
- if (normalized.length > 0) {
66
- const last = normalized[normalized.length - 1]
67
- if (last.role === msg.role) {
68
- // Merge same-role messages
69
- if (msg.role === 'user') {
70
- // Combine content
71
- const lastContent = typeof last.content === 'string'
72
- ? [{ type: 'text' as const, text: last.content }]
73
- : last.content as any[]
74
- const newContent = typeof msg.content === 'string'
75
- ? [{ type: 'text' as const, text: msg.content }]
76
- : msg.content as any[]
77
- normalized[normalized.length - 1] = {
78
- role: 'user',
79
- content: [...lastContent, ...newContent],
80
- }
81
- continue
82
- }
83
- }
84
- }
85
-
86
- normalized.push({ ...msg })
87
- }
88
-
89
- // Ensure tool results are properly paired with tool_use
90
- return fixToolResultPairing(normalized)
91
- }
92
-
93
- /**
94
- * Fix tool result pairing: ensure every tool_result has a
95
- * matching tool_use in the previous assistant message.
96
- */
97
- function fixToolResultPairing(
98
- messages: Array<{ role: string; content: any }>,
99
- ): Array<{ role: string; content: any }> {
100
- const result: Array<{ role: string; content: any }> = []
101
-
102
- for (let i = 0; i < messages.length; i++) {
103
- const msg = messages[i]
104
-
105
- if (msg.role === 'user' && Array.isArray(msg.content)) {
106
- // Check for tool_result blocks
107
- const toolResults = (msg.content as any[]).filter(
108
- (block: any) => block.type === 'tool_result',
109
- )
110
-
111
- if (toolResults.length > 0 && result.length > 0) {
112
- // Find the previous assistant message
113
- const prevAssistant = result[result.length - 1]
114
- if (prevAssistant.role === 'assistant' && Array.isArray(prevAssistant.content)) {
115
- const toolUseIds = new Set(
116
- (prevAssistant.content as any[])
117
- .filter((b: any) => b.type === 'tool_use')
118
- .map((b: any) => b.id),
119
- )
120
-
121
- // Filter out orphaned tool results
122
- const validContent = (msg.content as any[]).filter((block: any) => {
123
- if (block.type === 'tool_result') {
124
- return toolUseIds.has(block.tool_use_id)
125
- }
126
- return true
127
- })
128
-
129
- if (validContent.length > 0) {
130
- result.push({ ...msg, content: validContent })
131
- }
132
- continue
133
- }
134
- }
135
- }
136
-
137
- result.push(msg)
138
- }
139
-
140
- return result
141
- }
142
-
143
- /**
144
- * Strip images from messages (for compaction).
145
- */
146
- export function stripImagesFromMessages(
147
- messages: Array<{ role: string; content: any }>,
148
- ): Array<{ role: string; content: any }> {
149
- return messages.map((msg) => {
150
- if (typeof msg.content === 'string') return msg
151
- if (!Array.isArray(msg.content)) return msg
152
-
153
- const filtered = (msg.content as any[]).filter(
154
- (block: any) => block.type !== 'image',
155
- )
156
-
157
- return {
158
- ...msg,
159
- content: filtered.length > 0 ? filtered : '[content removed]',
160
- }
161
- })
162
- }
163
-
164
- /**
165
- * Extract text from message content blocks.
166
- */
167
- export function extractTextFromContent(
168
- content: any[] | string,
169
- ): string {
170
- if (typeof content === 'string') return content
171
-
172
- return content
173
- .filter((b: any) => b.type === 'text')
174
- .map((b: any) => b.text)
175
- .join('')
176
- }
177
-
178
- /**
179
- * Create a system message for compact boundary.
180
- */
181
- export function createCompactBoundaryMessage(): { role: string; content: string } {
182
- return {
183
- role: 'user',
184
- content: '[Previous context has been summarized above. Continuing conversation.]',
185
- }
186
- }
187
-
188
- /**
189
- * Truncate text to max length with ellipsis.
190
- */
191
- export function truncateText(text: string, maxLength: number): string {
192
- if (text.length <= maxLength) return text
193
- const half = Math.floor(maxLength / 2)
194
- return text.slice(0, half) + '\n...(truncated)...\n' + text.slice(-half)
195
- }
@@ -1,140 +0,0 @@
1
- /**
2
- * Retry Logic with Exponential Backoff
3
- *
4
- * Handles API retries for rate limits, overloaded servers,
5
- * and transient failures.
6
- */
7
-
8
- /**
9
- * Retry configuration.
10
- */
11
- export interface RetryConfig {
12
- maxRetries: number
13
- baseDelayMs: number
14
- maxDelayMs: number
15
- retryableStatusCodes: number[]
16
- }
17
-
18
- /**
19
- * Default retry configuration.
20
- */
21
- export const DEFAULT_RETRY_CONFIG: RetryConfig = {
22
- maxRetries: 3,
23
- baseDelayMs: 2000,
24
- maxDelayMs: 30000,
25
- retryableStatusCodes: [429, 500, 502, 503, 529],
26
- }
27
-
28
- /**
29
- * Check if an error is retryable.
30
- */
31
- export function isRetryableError(err: any, config: RetryConfig = DEFAULT_RETRY_CONFIG): boolean {
32
- if (err?.status && config.retryableStatusCodes.includes(err.status)) {
33
- return true
34
- }
35
-
36
- // Network errors
37
- if (err?.code === 'ECONNRESET' || err?.code === 'ETIMEDOUT' || err?.code === 'ECONNREFUSED') {
38
- return true
39
- }
40
-
41
- // API overloaded
42
- if (err?.error?.type === 'overloaded_error') {
43
- return true
44
- }
45
-
46
- return false
47
- }
48
-
49
- /**
50
- * Calculate delay for exponential backoff.
51
- */
52
- export function getRetryDelay(attempt: number, config: RetryConfig = DEFAULT_RETRY_CONFIG): number {
53
- const delay = config.baseDelayMs * Math.pow(2, attempt)
54
- // Add jitter (±25%)
55
- const jitter = delay * 0.25 * (Math.random() * 2 - 1)
56
- return Math.min(delay + jitter, config.maxDelayMs)
57
- }
58
-
59
- /**
60
- * Execute a function with retries.
61
- */
62
- export async function withRetry<T>(
63
- fn: () => Promise<T>,
64
- config: RetryConfig = DEFAULT_RETRY_CONFIG,
65
- abortSignal?: AbortSignal,
66
- ): Promise<T> {
67
- let lastError: any
68
-
69
- for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
70
- if (abortSignal?.aborted) {
71
- throw new Error('Aborted')
72
- }
73
-
74
- try {
75
- return await fn()
76
- } catch (err: any) {
77
- lastError = err
78
-
79
- if (!isRetryableError(err, config)) {
80
- throw err
81
- }
82
-
83
- if (attempt === config.maxRetries) {
84
- throw err
85
- }
86
-
87
- // Wait before retry
88
- const delay = getRetryDelay(attempt, config)
89
- await new Promise((resolve) => setTimeout(resolve, delay))
90
- }
91
- }
92
-
93
- throw lastError
94
- }
95
-
96
- /**
97
- * Check if an error is a "prompt too long" error.
98
- */
99
- export function isPromptTooLongError(err: any): boolean {
100
- if (err?.status === 400) {
101
- const message = err?.error?.error?.message || err?.message || ''
102
- return message.includes('prompt is too long') ||
103
- message.includes('max_tokens') ||
104
- message.includes('context length')
105
- }
106
- return false
107
- }
108
-
109
- /**
110
- * Check if error is an auth error.
111
- */
112
- export function isAuthError(err: any): boolean {
113
- return err?.status === 401 || err?.status === 403
114
- }
115
-
116
- /**
117
- * Check if error is a rate limit error.
118
- */
119
- export function isRateLimitError(err: any): boolean {
120
- return err?.status === 429
121
- }
122
-
123
- /**
124
- * Format an API error for display.
125
- */
126
- export function formatApiError(err: any): string {
127
- if (isAuthError(err)) {
128
- return 'Authentication failed. Check your CODEANY_API_KEY.'
129
- }
130
- if (isRateLimitError(err)) {
131
- return 'Rate limit exceeded. Please retry after a short wait.'
132
- }
133
- if (err?.status === 529) {
134
- return 'API overloaded. Please retry later.'
135
- }
136
- if (isPromptTooLongError(err)) {
137
- return 'Prompt too long. Auto-compacting conversation...'
138
- }
139
- return `API error: ${err.message || err}`
140
- }