@travisennis/acai 0.0.1 → 0.0.2

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 (323) hide show
  1. package/README.md +3 -4
  2. package/dist/commands/health-command.d.ts +2 -0
  3. package/dist/commands/health-command.js +59 -0
  4. package/dist/commands/manager.js +2 -0
  5. package/dist/commands/paste-command.d.ts +1 -1
  6. package/dist/commands/paste-command.js +155 -11
  7. package/dist/commands/reset-command.js +1 -0
  8. package/dist/index.js +1 -1
  9. package/dist/models/openrouter-provider.d.ts +4 -1
  10. package/dist/models/openrouter-provider.js +46 -4
  11. package/dist/models/providers.d.ts +1 -1
  12. package/dist/prompts/manager.d.ts +1 -0
  13. package/dist/prompts/manager.js +10 -0
  14. package/dist/prompts.js +8 -6
  15. package/dist/repl.js +49 -26
  16. package/dist/terminal/formatting.d.ts +16 -5
  17. package/dist/terminal/formatting.js +40 -6
  18. package/dist/terminal/index.d.ts +1 -1
  19. package/dist/terminal/index.js +54 -14
  20. package/dist/terminal/markdown.js +0 -1
  21. package/dist/terminal/supports-color.d.ts +16 -0
  22. package/dist/terminal/supports-color.js +121 -0
  23. package/dist/terminal/supports-hyperlinks.d.ts +7 -0
  24. package/dist/terminal/supports-hyperlinks.js +98 -0
  25. package/dist/tools/bash.js +95 -117
  26. package/dist/tools/code-interpreter.js +11 -1
  27. package/dist/tools/command-validation.d.ts +7 -3
  28. package/dist/tools/command-validation.js +67 -23
  29. package/dist/tools/delete-file.d.ts +4 -1
  30. package/dist/tools/delete-file.js +47 -3
  31. package/dist/tools/git-utils.d.ts +6 -0
  32. package/dist/tools/git-utils.js +89 -12
  33. package/dist/tools/grep.d.ts +20 -0
  34. package/dist/tools/grep.js +128 -40
  35. package/dist/tools/index.d.ts +2 -18
  36. package/dist/tools/index.js +4 -18
  37. package/package.json +30 -20
  38. package/.acai/acai.json +0 -9
  39. package/.acai/prompts/add-openrouter-model.md +0 -13
  40. package/.acai/prompts/project-status.md +0 -4
  41. package/.acai/prompts/update-architecture-document.md +0 -9
  42. package/.acai/rules/learned-rules.md +0 -9
  43. package/.ai/docs/available-tools.txt +0 -3
  44. package/.ai/docs/cognitive_complexity_refactoring_progress.md +0 -65
  45. package/.ai/docs/deleted_tools.md +0 -168
  46. package/.ai/docs/deleted_tools_88ced9ef.md +0 -56
  47. package/.ai/docs/image-pasting.md +0 -46
  48. package/.ai/docs/initialize-app.md +0 -117
  49. package/.ai/docs/issue-4-plan.md +0 -44
  50. package/.ai/docs/marked-renderer-debug.md +0 -15
  51. package/.ai/docs/marked-renderer-refactor-plan.md +0 -64
  52. package/.ai/docs/memory-use-cases.md +0 -55
  53. package/.ai/docs/prompt-consistency.md +0 -31
  54. package/.ai/docs/refactoring-tools.md +0 -98
  55. package/.ai/docs/system-prompt-update.md +0 -174
  56. package/.ai/docs/system_prompt.txt +0 -210
  57. package/.ai/docs/tasks.md +0 -49
  58. package/.ai/plan.md +0 -131
  59. package/.ai/prompt.md +0 -1
  60. package/.ai/scripts/fetch_models.js +0 -27
  61. package/.ai/scripts/generateSystemPrompt.ts +0 -15
  62. package/.ai/scripts/list-tools.mjs +0 -4
  63. package/.ai/scripts/p5_geometric_shapes.js +0 -149
  64. package/.husky/commit-msg +0 -1
  65. package/.husky/pre-commit +0 -3
  66. package/.husky/pre-push +0 -1
  67. package/.ignore +0 -4
  68. package/AGENTS.md +0 -25
  69. package/ARCHITECTURE.md +0 -304
  70. package/TODO.md +0 -2
  71. package/biome.json +0 -61
  72. package/commitlint.config.js +0 -3
  73. package/dist/source/cli.d.ts +0 -19
  74. package/dist/source/cli.js +0 -116
  75. package/dist/source/commands/application-log-command.d.ts +0 -2
  76. package/dist/source/commands/application-log-command.js +0 -43
  77. package/dist/source/commands/clear-command.d.ts +0 -2
  78. package/dist/source/commands/clear-command.js +0 -12
  79. package/dist/source/commands/compact-command.d.ts +0 -2
  80. package/dist/source/commands/compact-command.js +0 -51
  81. package/dist/source/commands/copy-command.d.ts +0 -2
  82. package/dist/source/commands/copy-command.js +0 -51
  83. package/dist/source/commands/edit-command.d.ts +0 -2
  84. package/dist/source/commands/edit-command.js +0 -53
  85. package/dist/source/commands/edit-prompt-command.d.ts +0 -2
  86. package/dist/source/commands/edit-prompt-command.js +0 -25
  87. package/dist/source/commands/exit-command.d.ts +0 -2
  88. package/dist/source/commands/exit-command.js +0 -14
  89. package/dist/source/commands/files-command.d.ts +0 -2
  90. package/dist/source/commands/files-command.js +0 -63
  91. package/dist/source/commands/generate-rules-command.d.ts +0 -2
  92. package/dist/source/commands/generate-rules-command.js +0 -61
  93. package/dist/source/commands/help-command.d.ts +0 -2
  94. package/dist/source/commands/help-command.js +0 -19
  95. package/dist/source/commands/init-command.d.ts +0 -2
  96. package/dist/source/commands/init-command.js +0 -40
  97. package/dist/source/commands/last-log-command.d.ts +0 -2
  98. package/dist/source/commands/last-log-command.js +0 -76
  99. package/dist/source/commands/manager.d.ts +0 -22
  100. package/dist/source/commands/manager.js +0 -123
  101. package/dist/source/commands/model-command.d.ts +0 -2
  102. package/dist/source/commands/model-command.js +0 -84
  103. package/dist/source/commands/paste-command.d.ts +0 -2
  104. package/dist/source/commands/paste-command.js +0 -40
  105. package/dist/source/commands/prompt-command.d.ts +0 -2
  106. package/dist/source/commands/prompt-command.js +0 -111
  107. package/dist/source/commands/reset-command.d.ts +0 -2
  108. package/dist/source/commands/reset-command.js +0 -16
  109. package/dist/source/commands/rules-command.d.ts +0 -2
  110. package/dist/source/commands/rules-command.js +0 -68
  111. package/dist/source/commands/save-command.d.ts +0 -2
  112. package/dist/source/commands/save-command.js +0 -14
  113. package/dist/source/commands/types.d.ts +0 -26
  114. package/dist/source/commands/types.js +0 -1
  115. package/dist/source/commands/usage-command.d.ts +0 -2
  116. package/dist/source/commands/usage-command.js +0 -21
  117. package/dist/source/config.d.ts +0 -60
  118. package/dist/source/config.js +0 -193
  119. package/dist/source/conversation-analyzer.d.ts +0 -10
  120. package/dist/source/conversation-analyzer.js +0 -88
  121. package/dist/source/dedent.d.ts +0 -3
  122. package/dist/source/dedent.js +0 -38
  123. package/dist/source/formatting.d.ts +0 -17
  124. package/dist/source/formatting.js +0 -103
  125. package/dist/source/index.d.ts +0 -18
  126. package/dist/source/index.js +0 -213
  127. package/dist/source/logger.d.ts +0 -2
  128. package/dist/source/logger.js +0 -24
  129. package/dist/source/mentions.d.ts +0 -9
  130. package/dist/source/mentions.js +0 -182
  131. package/dist/source/messages.d.ts +0 -69
  132. package/dist/source/messages.js +0 -261
  133. package/dist/source/middleware/audit-message.d.ts +0 -5
  134. package/dist/source/middleware/audit-message.js +0 -95
  135. package/dist/source/middleware/index.d.ts +0 -2
  136. package/dist/source/middleware/index.js +0 -2
  137. package/dist/source/middleware/rate-limit.d.ts +0 -4
  138. package/dist/source/middleware/rate-limit.js +0 -17
  139. package/dist/source/models/ai-config.d.ts +0 -12
  140. package/dist/source/models/ai-config.js +0 -87
  141. package/dist/source/models/anthropic-provider.d.ts +0 -25
  142. package/dist/source/models/anthropic-provider.js +0 -184
  143. package/dist/source/models/deepseek-provider.d.ts +0 -20
  144. package/dist/source/models/deepseek-provider.js +0 -42
  145. package/dist/source/models/google-provider.d.ts +0 -19
  146. package/dist/source/models/google-provider.js +0 -56
  147. package/dist/source/models/manager.d.ts +0 -15
  148. package/dist/source/models/manager.js +0 -48
  149. package/dist/source/models/openai-provider.d.ts +0 -22
  150. package/dist/source/models/openai-provider.js +0 -70
  151. package/dist/source/models/openrouter-provider.d.ts +0 -36
  152. package/dist/source/models/openrouter-provider.js +0 -276
  153. package/dist/source/models/providers.d.ts +0 -33
  154. package/dist/source/models/providers.js +0 -116
  155. package/dist/source/models/xai-provider.d.ts +0 -20
  156. package/dist/source/models/xai-provider.js +0 -47
  157. package/dist/source/parsing.d.ts +0 -2
  158. package/dist/source/parsing.js +0 -18
  159. package/dist/source/prompts/manager.d.ts +0 -19
  160. package/dist/source/prompts/manager.js +0 -71
  161. package/dist/source/prompts.d.ts +0 -4
  162. package/dist/source/prompts.js +0 -158
  163. package/dist/source/repl-prompt.d.ts +0 -14
  164. package/dist/source/repl-prompt.js +0 -147
  165. package/dist/source/repl.d.ts +0 -27
  166. package/dist/source/repl.js +0 -431
  167. package/dist/source/terminal/formatting.d.ts +0 -37
  168. package/dist/source/terminal/formatting.js +0 -106
  169. package/dist/source/terminal/index.d.ts +0 -94
  170. package/dist/source/terminal/index.js +0 -420
  171. package/dist/source/terminal/markdown-utils.d.ts +0 -2
  172. package/dist/source/terminal/markdown-utils.js +0 -81
  173. package/dist/source/terminal/markdown.d.ts +0 -1
  174. package/dist/source/terminal/markdown.js +0 -111
  175. package/dist/source/terminal/types.d.ts +0 -71
  176. package/dist/source/terminal/types.js +0 -1
  177. package/dist/source/terminal-output.d.ts +0 -8
  178. package/dist/source/terminal-output.js +0 -213
  179. package/dist/source/terminal-output.test.d.ts +0 -8
  180. package/dist/source/terminal-output.test.js +0 -213
  181. package/dist/source/token-tracker.d.ts +0 -14
  182. package/dist/source/token-tracker.js +0 -53
  183. package/dist/source/token-utils.d.ts +0 -7
  184. package/dist/source/token-utils.js +0 -13
  185. package/dist/source/tools/agent.d.ts +0 -17
  186. package/dist/source/tools/agent.js +0 -87
  187. package/dist/source/tools/bash.d.ts +0 -19
  188. package/dist/source/tools/bash.js +0 -294
  189. package/dist/source/tools/code-interpreter.d.ts +0 -12
  190. package/dist/source/tools/code-interpreter.js +0 -131
  191. package/dist/source/tools/command-validation.d.ts +0 -8
  192. package/dist/source/tools/command-validation.js +0 -69
  193. package/dist/source/tools/delete-file.d.ts +0 -12
  194. package/dist/source/tools/delete-file.js +0 -56
  195. package/dist/source/tools/directory-tree.d.ts +0 -12
  196. package/dist/source/tools/directory-tree.js +0 -38
  197. package/dist/source/tools/edit-file.d.ts +0 -19
  198. package/dist/source/tools/edit-file.js +0 -107
  199. package/dist/source/tools/filesystem-utils.d.ts +0 -22
  200. package/dist/source/tools/filesystem-utils.js +0 -191
  201. package/dist/source/tools/git-utils.d.ts +0 -14
  202. package/dist/source/tools/git-utils.js +0 -64
  203. package/dist/source/tools/grep.d.ts +0 -17
  204. package/dist/source/tools/grep.js +0 -138
  205. package/dist/source/tools/index.d.ts +0 -161
  206. package/dist/source/tools/index.js +0 -209
  207. package/dist/source/tools/memory-read.d.ts +0 -13
  208. package/dist/source/tools/memory-read.js +0 -135
  209. package/dist/source/tools/memory-write.d.ts +0 -12
  210. package/dist/source/tools/memory-write.js +0 -83
  211. package/dist/source/tools/move-file.d.ts +0 -13
  212. package/dist/source/tools/move-file.js +0 -44
  213. package/dist/source/tools/read-file.d.ts +0 -17
  214. package/dist/source/tools/read-file.js +0 -86
  215. package/dist/source/tools/read-multiple-files.d.ts +0 -14
  216. package/dist/source/tools/read-multiple-files.js +0 -55
  217. package/dist/source/tools/save-file.d.ts +0 -17
  218. package/dist/source/tools/save-file.js +0 -98
  219. package/dist/source/tools/think.d.ts +0 -11
  220. package/dist/source/tools/think.js +0 -45
  221. package/dist/source/tools/types.d.ts +0 -29
  222. package/dist/source/tools/types.js +0 -14
  223. package/dist/source/tools/web-fetch.d.ts +0 -47
  224. package/dist/source/tools/web-fetch.js +0 -246
  225. package/dist/source/tools/web-search.d.ts +0 -13
  226. package/dist/source/tools/web-search.js +0 -80
  227. package/dist/source/utils/process.d.ts +0 -36
  228. package/dist/source/utils/process.js +0 -75
  229. package/dist/source/version.d.ts +0 -1
  230. package/dist/source/version.js +0 -21
  231. package/dist/terminal-output.d.ts +0 -8
  232. package/dist/terminal-output.js +0 -213
  233. package/dist/tools/memory-read.d.ts +0 -13
  234. package/dist/tools/memory-read.js +0 -135
  235. package/dist/tools/memory-write.d.ts +0 -12
  236. package/dist/tools/memory-write.js +0 -83
  237. package/knip.json +0 -5
  238. package/source/cli.ts +0 -172
  239. package/source/commands/application-log-command.ts +0 -53
  240. package/source/commands/clear-command.ts +0 -14
  241. package/source/commands/compact-command.ts +0 -64
  242. package/source/commands/copy-command.ts +0 -55
  243. package/source/commands/edit-command.ts +0 -63
  244. package/source/commands/edit-prompt-command.ts +0 -31
  245. package/source/commands/exit-command.ts +0 -18
  246. package/source/commands/files-command.ts +0 -85
  247. package/source/commands/generate-rules-command.ts +0 -82
  248. package/source/commands/help-command.ts +0 -27
  249. package/source/commands/init-command.ts +0 -48
  250. package/source/commands/last-log-command.ts +0 -88
  251. package/source/commands/manager.ts +0 -151
  252. package/source/commands/model-command.ts +0 -123
  253. package/source/commands/paste-command.ts +0 -62
  254. package/source/commands/prompt-command.ts +0 -150
  255. package/source/commands/reset-command.ts +0 -22
  256. package/source/commands/rules-command.ts +0 -76
  257. package/source/commands/save-command.ts +0 -20
  258. package/source/commands/types.ts +0 -28
  259. package/source/commands/usage-command.ts +0 -26
  260. package/source/config.ts +0 -223
  261. package/source/conversation-analyzer.ts +0 -115
  262. package/source/dedent.ts +0 -53
  263. package/source/formatting.ts +0 -132
  264. package/source/index.ts +0 -240
  265. package/source/logger.ts +0 -29
  266. package/source/mentions.ts +0 -227
  267. package/source/messages.ts +0 -360
  268. package/source/middleware/audit-message.ts +0 -133
  269. package/source/middleware/index.ts +0 -2
  270. package/source/middleware/rate-limit.ts +0 -24
  271. package/source/models/ai-config.ts +0 -109
  272. package/source/models/anthropic-provider.ts +0 -199
  273. package/source/models/deepseek-provider.ts +0 -53
  274. package/source/models/google-provider.ts +0 -68
  275. package/source/models/manager.ts +0 -84
  276. package/source/models/openai-provider.ts +0 -81
  277. package/source/models/openrouter-provider.ts +0 -288
  278. package/source/models/providers.ts +0 -197
  279. package/source/models/xai-provider.ts +0 -59
  280. package/source/parsing.ts +0 -20
  281. package/source/prompts/manager.ts +0 -90
  282. package/source/prompts.ts +0 -172
  283. package/source/repl-prompt.ts +0 -196
  284. package/source/repl.ts +0 -572
  285. package/source/terminal/formatting.ts +0 -121
  286. package/source/terminal/index.ts +0 -518
  287. package/source/terminal/markdown-utils.ts +0 -89
  288. package/source/terminal/markdown.ts +0 -155
  289. package/source/terminal/types.ts +0 -84
  290. package/source/terminal-output.test.ts +0 -266
  291. package/source/token-tracker.ts +0 -78
  292. package/source/token-utils.ts +0 -17
  293. package/source/tools/agent.ts +0 -107
  294. package/source/tools/bash.ts +0 -367
  295. package/source/tools/code-interpreter.ts +0 -172
  296. package/source/tools/command-validation.ts +0 -81
  297. package/source/tools/delete-file.ts +0 -71
  298. package/source/tools/directory-tree.ts +0 -54
  299. package/source/tools/edit-file.ts +0 -155
  300. package/source/tools/filesystem-utils.ts +0 -265
  301. package/source/tools/git-utils.ts +0 -70
  302. package/source/tools/grep.ts +0 -184
  303. package/source/tools/index.ts +0 -278
  304. package/source/tools/memory-read.ts +0 -174
  305. package/source/tools/memory-write.ts +0 -105
  306. package/source/tools/move-file.ts +0 -59
  307. package/source/tools/read-file.ts +0 -129
  308. package/source/tools/read-multiple-files.ts +0 -80
  309. package/source/tools/save-file.ts +0 -147
  310. package/source/tools/think.ts +0 -51
  311. package/source/tools/types.ts +0 -58
  312. package/source/tools/web-fetch.ts +0 -327
  313. package/source/tools/web-search.ts +0 -101
  314. package/source/utils/process.ts +0 -121
  315. package/source/version.ts +0 -21
  316. package/test/commands/copy-command.test.ts +0 -69
  317. package/test/config.test.ts +0 -200
  318. package/test/terminal/markdown-utils.test.ts +0 -124
  319. package/test/tools/bash-tool.test.ts +0 -58
  320. package/test/tools/code-interpreter.test.ts +0 -91
  321. package/test/tools/command-validation.test.ts +0 -48
  322. package/tsconfig.build.json +0 -9
  323. package/tsconfig.json +0 -30
@@ -1,360 +0,0 @@
1
- import EventEmitter from "node:events";
2
- import { readdir, readFile, writeFile } from "node:fs/promises";
3
- import { join } from "node:path";
4
- import { isString } from "@travisennis/stdlib/typeguards";
5
- import {
6
- type AssistantModelMessage,
7
- generateText,
8
- type ImagePart, // Added ImagePart
9
- type ModelMessage,
10
- type TextPart,
11
- type ToolModelMessage,
12
- type UserModelMessage,
13
- } from "ai";
14
- import type { ModelManager } from "./models/manager.ts";
15
- import type { TokenTracker } from "./token-tracker.ts";
16
-
17
- // Define a type for the items that can be passed in the first argument
18
- export type UserMessageContentItem = string | ImagePart;
19
-
20
- export function createUserMessage(
21
- contentItems: UserMessageContentItem[],
22
- prompt?: string,
23
- ): UserModelMessage {
24
- const messageParts: (TextPart | ImagePart)[] = [];
25
-
26
- // Process content items (images and pre-defined texts)
27
- for (const item of contentItems) {
28
- if (typeof item === "string") {
29
- if (item.trim().length > 0) {
30
- messageParts.push({ type: "text", text: item });
31
- }
32
- } else if (item.type === "image") {
33
- messageParts.push(item);
34
- }
35
- }
36
-
37
- // Add the main prompt text if provided
38
- if (prompt && prompt.trim().length > 0) {
39
- messageParts.push({ type: "text", text: prompt });
40
- }
41
-
42
- return {
43
- role: "user",
44
- content: messageParts,
45
- };
46
- }
47
-
48
- export function createAssistantMessage(content: string): AssistantModelMessage {
49
- return {
50
- role: "assistant",
51
- content: [
52
- {
53
- type: "text",
54
- text: content,
55
- },
56
- ],
57
- };
58
- }
59
-
60
- /**
61
- A message that was generated during the generation process.
62
- It can be either an assistant message or a tool message.
63
- */
64
- type ResponseMessage = AssistantModelMessage | ToolModelMessage;
65
-
66
- export type SavedMessageHistory = {
67
- title: string;
68
- createdAt: Date;
69
- updatedAt: Date;
70
- messages: ModelMessage[];
71
- };
72
-
73
- type RawMessageHistory = Omit<
74
- SavedMessageHistory,
75
- "createdAt" | "updatedAt"
76
- > & {
77
- createdAt?: string;
78
- updatedAt?: string;
79
- };
80
-
81
- interface MessageHistoryEvents {
82
- "update-title": [string];
83
- "clear-history": [];
84
- }
85
-
86
- export class MessageHistory extends EventEmitter<MessageHistoryEvents> {
87
- private history: ModelMessage[];
88
- private title: string;
89
- private createdAt: Date;
90
- private updatedAt: Date;
91
- private stateDir: string;
92
- private modelManager: ModelManager;
93
- private tokenTracker: TokenTracker;
94
-
95
- constructor({
96
- stateDir,
97
- modelManager,
98
- tokenTracker,
99
- }: {
100
- stateDir: string;
101
- modelManager: ModelManager;
102
- tokenTracker: TokenTracker;
103
- }) {
104
- super();
105
- this.history = [];
106
- this.title = "";
107
- this.createdAt = new Date();
108
- this.updatedAt = new Date();
109
- this.stateDir = stateDir;
110
- this.modelManager = modelManager;
111
- this.tokenTracker = tokenTracker;
112
- }
113
-
114
- private validMessage(msg: ModelMessage) {
115
- // Filter out messages with empty content arrays
116
- if (Array.isArray(msg.content) && msg.content.length === 0) {
117
- return false;
118
- }
119
-
120
- // Filter out assistant messages with empty text fields
121
- if (
122
- msg.role === "assistant" &&
123
- Array.isArray(msg.content) &&
124
- msg.content.length === 1 &&
125
- msg.content[0]?.type === "text" &&
126
- msg.content[0]?.text === ""
127
- ) {
128
- return false;
129
- }
130
-
131
- return true;
132
- }
133
-
134
- get() {
135
- return [...this.history].filter(this.validMessage);
136
- }
137
-
138
- clear() {
139
- this.history.length = 0;
140
- this.emit("clear-history");
141
- }
142
-
143
- appendUserMessage(msg: string): void;
144
- appendUserMessage(msg: UserModelMessage): void;
145
- appendUserMessage(msg: string | UserModelMessage) {
146
- const now = new Date();
147
- const msgObj = isString(msg) ? createUserMessage([], msg) : msg;
148
- if (
149
- this.history.length === 0 &&
150
- msgObj.content &&
151
- msgObj.content.length > 0
152
- ) {
153
- const textPart = msgObj.content.at(-1) as TextPart;
154
- if (textPart?.text && textPart.text.trim() !== "") {
155
- this.generateTitle(textPart.text);
156
- }
157
- this.createdAt = now;
158
- }
159
- this.updatedAt = now;
160
- this.history.push(msgObj);
161
- }
162
-
163
- appendAssistantMessage(msg: string): void;
164
- appendAssistantMessage(msg: AssistantModelMessage): void;
165
- appendAssistantMessage(msg: string | AssistantModelMessage) {
166
- this.updatedAt = new Date();
167
- const msgObj = isString(msg) ? createAssistantMessage(msg) : msg;
168
- this.history.push(msgObj);
169
- }
170
-
171
- appendResponseMessages(responseMessages: ResponseMessage[]) {
172
- this.updatedAt = new Date();
173
- // Filter out messages with empty content arrays
174
- const validMessages = responseMessages.filter(this.validMessage);
175
- this.history.push(...validMessages);
176
- }
177
-
178
- isEmpty() {
179
- return this.history.length === 0;
180
- }
181
-
182
- async save() {
183
- const msgHistoryDir = this.stateDir;
184
- const timestamp = new Date().toISOString().replace(/:/g, "-");
185
- const fileName = `message-history-${timestamp}.json`;
186
- const filePath = join(msgHistoryDir, fileName);
187
-
188
- const output: SavedMessageHistory = {
189
- title: this.title,
190
- createdAt: this.createdAt,
191
- updatedAt: this.updatedAt,
192
- messages: this.history,
193
- };
194
-
195
- await writeFile(filePath, JSON.stringify(output, null, 2));
196
- }
197
-
198
- private async generateTitle(message: string) {
199
- // Skip title generation if message is empty
200
- if (!message || message.trim() === "") {
201
- return;
202
- }
203
-
204
- const app = "title-conversation";
205
-
206
- const systemPrompt =
207
- "You are an assistant who task is to analyze messages to generate a conversation topic that can be used as a conversation title. For each message, generate a 4-7 word title that captures the topic. Return only the title with no other text.\n\nExamples:\nMessage:\nHow do I implement authentication in my Express app?\nTitle: Express Authentication Implementation\n\nMessage:\nCan you help me debug this React component that isn't rendering correctly?\nTitle:React Component Rendering Debug";
208
- try {
209
- const { text, usage } = await generateText({
210
- model: this.modelManager.getModel(app),
211
- system: systemPrompt,
212
- prompt: `Request:\n${message}\nTitle:`,
213
- });
214
-
215
- this.tokenTracker.trackUsage(app, usage);
216
-
217
- if (text && text.split(" ").length < 10) {
218
- this.title = text;
219
- this.emit("update-title", this.title);
220
- }
221
- } catch (error) {
222
- console.error(error);
223
- }
224
- }
225
-
226
- getFirstUserMessage(): UserModelMessage | undefined {
227
- const firstUser = this.get().find(
228
- (msg): msg is UserModelMessage => msg.role === "user",
229
- );
230
- return firstUser;
231
- }
232
-
233
- static async load(
234
- stateDir: string,
235
- count = 10, // Add count parameter with default
236
- ): Promise<SavedMessageHistory[]> {
237
- try {
238
- const files = await readdir(stateDir);
239
- const messageHistoryFiles = files
240
- .filter(
241
- (file) =>
242
- file.startsWith("message-history-") && file.endsWith(".json"),
243
- )
244
- .sort((a, b) => {
245
- // Extract timestamps and compare in reverse order (newest first)
246
- const timeA = a.replace("message-history-", "").replace(".json", "");
247
- const timeB = b.replace("message-history-", "").replace(".json", "");
248
- return timeB.localeCompare(timeA); // Newest first
249
- })
250
- .slice(0, count); // Use the count parameter here
251
-
252
- const fileReadPromises = messageHistoryFiles.map(async (fileName) => {
253
- const filePath = join(stateDir, fileName);
254
- try {
255
- const content = await readFile(filePath, "utf-8");
256
- const parsed = JSON.parse(content) as RawMessageHistory;
257
- const result: SavedMessageHistory =
258
- parsed as unknown as SavedMessageHistory;
259
- // Basic validation - ensure messages array exists
260
- if (parsed && Array.isArray(parsed.messages)) {
261
- // Convert date strings back to Date objects
262
- result.createdAt = new Date(parsed.createdAt ?? 0);
263
- result.updatedAt = new Date(parsed.updatedAt ?? 0);
264
- return result;
265
- }
266
- } catch (error) {
267
- console.error(`Error reading or parsing file ${filePath}:`, error);
268
- }
269
- return null; // Return null for failed reads/parses
270
- });
271
-
272
- const results = await Promise.all(fileReadPromises);
273
- // Filter out null results (failed reads/parses)
274
- //sort results by result.updatedAt which is a Date
275
- return results
276
- .filter((result): result is SavedMessageHistory => result !== null)
277
- .sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
278
- } catch (error) {
279
- // Handle cases where the directory might not exist or other readdir errors
280
- if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
281
- console.error(`Error loading message history from ${stateDir}:`, error);
282
- }
283
- // Return empty array if directory doesn't exist or other read errors occur
284
- return [];
285
- }
286
- }
287
-
288
- // Method to restore state from a SavedMessageHistory object
289
- restore(savedHistory: SavedMessageHistory): void {
290
- this.title = savedHistory.title;
291
- // Ensure dates are Date objects, though load should handle this
292
- this.createdAt =
293
- typeof savedHistory.createdAt === "string"
294
- ? new Date(savedHistory.createdAt)
295
- : savedHistory.createdAt;
296
- this.updatedAt =
297
- typeof savedHistory.updatedAt === "string"
298
- ? new Date(savedHistory.updatedAt)
299
- : savedHistory.updatedAt;
300
- this.history = [...savedHistory.messages]; // Use the correct internal property name and create a copy
301
- }
302
- }
303
-
304
- /**
305
- * Normalizes an array of messages for API consumption by:
306
- * 1. Filtering out progress-type messages
307
- * 2. Processing user and assistant messages
308
- * 3. Handling tool results by either:
309
- * - Adding them as new messages if they're the first tool result
310
- * - Adding them as new messages if the previous message wasn't a tool result
311
- * - Merging them with the previous message if it was also a tool result
312
- *
313
- * This consolidation of sequential tool results into a single message
314
- * ensures proper formatting for API consumption while maintaining the
315
- * logical flow of the conversation.
316
- *
317
- * @param messages - Array of messages to normalize
318
- * @returns Normalized array of user and assistant messages ready for API
319
- */
320
- export function normalizeMessagesForApi(
321
- messages: ModelMessage[],
322
- ): ModelMessage[] {
323
- const result: ModelMessage[] = [];
324
- for (const message of messages) {
325
- switch (message.role) {
326
- case "user": {
327
- result.push(message);
328
- continue;
329
- }
330
- case "tool": {
331
- // If the last message is not a tool result, add it to the result
332
- const lastMessage = result.at(-1);
333
- if (
334
- !lastMessage ||
335
- lastMessage.role === "assistant" ||
336
- !Array.isArray(lastMessage.content) ||
337
- lastMessage.content[0]?.type !== "tool-result"
338
- ) {
339
- result.push(message);
340
- continue;
341
- }
342
-
343
- // Otherwise, merge the current message with the last message
344
- result[result.indexOf(lastMessage)] = {
345
- ...lastMessage,
346
- // biome-ignore lint/suspicious/noExplicitAny: can't figure out type
347
- content: [...lastMessage.content, ...message.content] as any, // #FIXME figure out what type this should be
348
- };
349
- continue;
350
- }
351
- case "assistant": {
352
- result.push(message);
353
- continue;
354
- }
355
- default:
356
- continue;
357
- }
358
- }
359
- return result;
360
- }
@@ -1,133 +0,0 @@
1
- import { mkdir, writeFile } from "node:fs/promises";
2
- import { dirname, join } from "node:path";
3
- import type {
4
- LanguageModelV2Middleware,
5
- LanguageModelV2Prompt,
6
- LanguageModelV2StreamPart,
7
- LanguageModelV2TextPart,
8
- } from "@ai-sdk/provider";
9
- import type { LanguageModelUsage } from "ai";
10
-
11
- interface AuditRecord {
12
- model: string;
13
- app: string;
14
- messages: LanguageModelV2Prompt;
15
- usage: LanguageModelUsage;
16
- timestamp: number;
17
- }
18
-
19
- /**
20
- * Writes an audit record to the specified file, overwriting any existing content.
21
- * Ensures the directory exists before writing.
22
- *
23
- * @param filePath - The path to the file where the audit record will be saved.
24
- * @param content - The audit record object to write.
25
- */
26
- const writeAuditRecord = async (
27
- app: string,
28
- filePath: string,
29
- content: AuditRecord,
30
- ): Promise<void> => {
31
- try {
32
- const now = new Date();
33
- const path = join(filePath, `${now.toISOString()}-${app}-message.json`);
34
- // Ensure directory exists
35
- await mkdir(dirname(path), { recursive: true });
36
- await writeFile(path, `${JSON.stringify(content, null, 2)}`);
37
- } catch (error) {
38
- console.error("Error writing audit file:", error);
39
- throw error;
40
- }
41
- };
42
-
43
- export const auditMessage = ({
44
- filePath = "messages",
45
- app = "default",
46
- }: {
47
- filePath: string;
48
- app: string;
49
- }) => {
50
- const middleware: LanguageModelV2Middleware = {
51
- wrapGenerate: async ({ doGenerate, params, model }) => {
52
- const result = await doGenerate();
53
-
54
- const msg: AuditRecord = {
55
- model: model.modelId,
56
- app,
57
- messages: [...params.prompt].concat({
58
- role: "assistant",
59
- content: [
60
- {
61
- type: "text",
62
- // biome-ignore lint/suspicious/noExplicitAny: work-around on type issue
63
- text: (result as any).text,
64
- } as LanguageModelV2TextPart,
65
- ],
66
- }),
67
- usage: result.usage,
68
- timestamp: Date.now(),
69
- };
70
-
71
- await writeAuditRecord(app, filePath, msg);
72
-
73
- return result;
74
- },
75
-
76
- wrapStream: async ({ doStream, params, model }) => {
77
- const { stream, ...rest } = await doStream();
78
-
79
- let generatedText = "";
80
- let usage: LanguageModelUsage = {
81
- inputTokens: 0,
82
- outputTokens: 0,
83
- totalTokens: 0,
84
- };
85
-
86
- const transformStream = new TransformStream<
87
- LanguageModelV2StreamPart,
88
- LanguageModelV2StreamPart
89
- >({
90
- transform(chunk, controller) {
91
- if (chunk.type === "text-delta") {
92
- generatedText += chunk.delta;
93
- }
94
- if (chunk.type === "finish") {
95
- usage = chunk.usage;
96
- }
97
- controller.enqueue(chunk);
98
- },
99
-
100
- async flush() {
101
- const msg: AuditRecord = {
102
- model: model.modelId,
103
- app,
104
- messages: generatedText
105
- ? [
106
- ...params.prompt,
107
- {
108
- role: "assistant",
109
- content: [
110
- {
111
- type: "text",
112
- text: generatedText,
113
- },
114
- ],
115
- },
116
- ]
117
- : [...params.prompt],
118
- usage,
119
- timestamp: Date.now(),
120
- };
121
-
122
- await writeAuditRecord(app, filePath, msg);
123
- },
124
- });
125
-
126
- return {
127
- stream: stream.pipeThrough(transformStream),
128
- ...rest,
129
- };
130
- },
131
- };
132
- return middleware;
133
- };
@@ -1,2 +0,0 @@
1
- export { auditMessage } from "./audit-message.ts";
2
- export { createRateLimitMiddleware } from "./rate-limit.ts";
@@ -1,24 +0,0 @@
1
- import type { LanguageModelV2Middleware } from "@ai-sdk/provider";
2
- import pThrottle from "p-throttle";
3
-
4
- export const createRateLimitMiddleware = ({
5
- requestsPerMinute,
6
- }: {
7
- requestsPerMinute: number;
8
- }): LanguageModelV2Middleware => {
9
- const throttle = pThrottle({
10
- limit: requestsPerMinute,
11
- interval: 60 * 1000, // 1 minute
12
- });
13
-
14
- return {
15
- wrapGenerate: ({ doGenerate }) => {
16
- const throttledGenerate = throttle(doGenerate);
17
- return Promise.resolve(throttledGenerate());
18
- },
19
- wrapStream: ({ doStream }) => {
20
- const throttledStream = throttle(doStream);
21
- return throttledStream();
22
- },
23
- };
24
- };
@@ -1,109 +0,0 @@
1
- import type { SharedV2ProviderMetadata } from "@ai-sdk/provider";
2
- import type { ModelMetadata } from "./providers.ts";
3
-
4
- type Effort = "none" | "low" | "medium" | "high";
5
-
6
- const THINKING_TIERS: {
7
- pattern: RegExp;
8
- budget: number;
9
- effort: Effort;
10
- }[] = [
11
- {
12
- pattern:
13
- /\b(ultrathink|think super hard|think really hard|think intensely)\b/i,
14
- budget: 31999,
15
- effort: "high",
16
- },
17
- {
18
- pattern: /\b(megathink|think (very )?hard|think (a lot|more|about it))\b/i,
19
- budget: 10000,
20
- effort: "medium",
21
- },
22
- {
23
- pattern: /\bthink\b/i, // Catch-all for standalone "think"
24
- budget: 4000,
25
- effort: "low",
26
- },
27
- ];
28
-
29
- function calculateThinkingLevel(userInput: string): {
30
- tokenBudget: number;
31
- effort: Effort;
32
- } {
33
- let tokenBudget = 0; // Default
34
- let effort: Effort = "none";
35
- for (const tier of THINKING_TIERS) {
36
- if (tier.pattern.test(userInput)) {
37
- tokenBudget = tier.budget;
38
- effort = tier.effort;
39
- break; // Use highest priority match
40
- }
41
- }
42
- return { tokenBudget, effort };
43
- }
44
-
45
- export class AiConfig {
46
- private modelMetadata: ModelMetadata;
47
- private prompt: string;
48
- constructor({
49
- modelMetadata,
50
- prompt,
51
- }: { modelMetadata: ModelMetadata; prompt: string }) {
52
- this.modelMetadata = modelMetadata;
53
- this.prompt = prompt;
54
- }
55
-
56
- getMaxTokens() {
57
- const modelConfig = this.modelMetadata;
58
- const thinkingLevel = calculateThinkingLevel(this.prompt);
59
- const maxTokens =
60
- modelConfig.provider === "anthropic" && modelConfig.supportsReasoning
61
- ? modelConfig.maxOutputTokens - thinkingLevel.tokenBudget
62
- : modelConfig.maxOutputTokens;
63
- return maxTokens;
64
- }
65
-
66
- getProviderOptions(): SharedV2ProviderMetadata {
67
- const modelConfig = this.modelMetadata;
68
- const thinkingLevel = calculateThinkingLevel(this.prompt);
69
-
70
- if (modelConfig.supportsReasoning && thinkingLevel.effort !== "none") {
71
- switch (modelConfig.provider) {
72
- case "anthropic":
73
- return {
74
- anthropic: {
75
- thinking: {
76
- type: "enabled",
77
- budgetTokens: thinkingLevel.tokenBudget,
78
- },
79
- },
80
- };
81
- case "openai":
82
- return { openai: { reasoningEffort: thinkingLevel.effort } };
83
- case "google": {
84
- return {
85
- google: {
86
- thinkingConfig: {
87
- thinkingBudget: thinkingLevel.tokenBudget,
88
- },
89
- },
90
- };
91
- }
92
- case "openrouter": {
93
- return {
94
- openrouter: {
95
- reasoning: {
96
- enabled: true,
97
- effort: thinkingLevel.effort,
98
- },
99
- },
100
- };
101
- }
102
- default:
103
- return {};
104
- }
105
- }
106
- // If supportsReasoning is false, or no provider case matched
107
- return {};
108
- }
109
- }