@soederpop/luca 0.1.2 → 0.1.3

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 (366) hide show
  1. package/CLAUDE.md +2 -0
  2. package/assistants/codingAssistant/hooks.ts +1 -5
  3. package/assistants/inkbot/CORE.md +69 -0
  4. package/assistants/inkbot/hooks.ts +14 -0
  5. package/assistants/inkbot/tools.ts +47 -0
  6. package/commands/inkbot.ts +353 -0
  7. package/dist/agi/container.server.d.ts +63 -0
  8. package/dist/agi/container.server.d.ts.map +1 -0
  9. package/dist/agi/endpoints/ask.d.ts +20 -0
  10. package/dist/agi/endpoints/ask.d.ts.map +1 -0
  11. package/dist/agi/endpoints/conversations/[id].d.ts +27 -0
  12. package/dist/agi/endpoints/conversations/[id].d.ts.map +1 -0
  13. package/dist/agi/endpoints/conversations.d.ts +18 -0
  14. package/dist/agi/endpoints/conversations.d.ts.map +1 -0
  15. package/dist/agi/endpoints/experts.d.ts +8 -0
  16. package/dist/agi/endpoints/experts.d.ts.map +1 -0
  17. package/dist/agi/feature.d.ts +9 -0
  18. package/dist/agi/feature.d.ts.map +1 -0
  19. package/dist/agi/features/assistant.d.ts +509 -0
  20. package/dist/agi/features/assistant.d.ts.map +1 -0
  21. package/dist/agi/features/assistants-manager.d.ts +236 -0
  22. package/dist/agi/features/assistants-manager.d.ts.map +1 -0
  23. package/dist/agi/features/autonomous-assistant.d.ts +281 -0
  24. package/dist/agi/features/autonomous-assistant.d.ts.map +1 -0
  25. package/dist/agi/features/browser-use.d.ts +479 -0
  26. package/dist/agi/features/browser-use.d.ts.map +1 -0
  27. package/dist/agi/features/claude-code.d.ts +824 -0
  28. package/dist/agi/features/claude-code.d.ts.map +1 -0
  29. package/dist/agi/features/conversation-history.d.ts +245 -0
  30. package/dist/agi/features/conversation-history.d.ts.map +1 -0
  31. package/dist/agi/features/conversation.d.ts +464 -0
  32. package/dist/agi/features/conversation.d.ts.map +1 -0
  33. package/dist/agi/features/docs-reader.d.ts +72 -0
  34. package/dist/agi/features/docs-reader.d.ts.map +1 -0
  35. package/dist/agi/features/file-tools.d.ts +110 -0
  36. package/dist/agi/features/file-tools.d.ts.map +1 -0
  37. package/dist/agi/features/luca-coder.d.ts +323 -0
  38. package/dist/agi/features/luca-coder.d.ts.map +1 -0
  39. package/dist/agi/features/openai-codex.d.ts +381 -0
  40. package/dist/agi/features/openai-codex.d.ts.map +1 -0
  41. package/dist/agi/features/openapi.d.ts +200 -0
  42. package/dist/agi/features/openapi.d.ts.map +1 -0
  43. package/dist/agi/features/skills-library.d.ts +167 -0
  44. package/dist/agi/features/skills-library.d.ts.map +1 -0
  45. package/dist/agi/index.d.ts +5 -0
  46. package/dist/agi/index.d.ts.map +1 -0
  47. package/dist/agi/lib/interceptor-chain.d.ts +44 -0
  48. package/dist/agi/lib/interceptor-chain.d.ts.map +1 -0
  49. package/dist/agi/lib/token-counter.d.ts +13 -0
  50. package/dist/agi/lib/token-counter.d.ts.map +1 -0
  51. package/dist/bootstrap/generated.d.ts +5 -0
  52. package/dist/bootstrap/generated.d.ts.map +1 -0
  53. package/dist/browser.d.ts +12 -0
  54. package/dist/browser.d.ts.map +1 -0
  55. package/dist/bus.d.ts +29 -0
  56. package/dist/bus.d.ts.map +1 -0
  57. package/dist/cli/build-info.d.ts +4 -0
  58. package/dist/cli/build-info.d.ts.map +1 -0
  59. package/dist/cli/cli.d.ts +3 -0
  60. package/dist/cli/cli.d.ts.map +1 -0
  61. package/dist/client.d.ts +60 -0
  62. package/dist/client.d.ts.map +1 -0
  63. package/dist/clients/civitai/index.d.ts +472 -0
  64. package/dist/clients/civitai/index.d.ts.map +1 -0
  65. package/dist/clients/client-template.d.ts +30 -0
  66. package/dist/clients/client-template.d.ts.map +1 -0
  67. package/dist/clients/comfyui/index.d.ts +281 -0
  68. package/dist/clients/comfyui/index.d.ts.map +1 -0
  69. package/dist/clients/elevenlabs/index.d.ts +197 -0
  70. package/dist/clients/elevenlabs/index.d.ts.map +1 -0
  71. package/dist/clients/graph.d.ts +64 -0
  72. package/dist/clients/graph.d.ts.map +1 -0
  73. package/dist/clients/openai/index.d.ts +247 -0
  74. package/dist/clients/openai/index.d.ts.map +1 -0
  75. package/dist/clients/rest.d.ts +92 -0
  76. package/dist/clients/rest.d.ts.map +1 -0
  77. package/dist/clients/supabase/index.d.ts +176 -0
  78. package/dist/clients/supabase/index.d.ts.map +1 -0
  79. package/dist/clients/websocket.d.ts +127 -0
  80. package/dist/clients/websocket.d.ts.map +1 -0
  81. package/dist/command.d.ts +163 -0
  82. package/dist/command.d.ts.map +1 -0
  83. package/dist/commands/bootstrap.d.ts +20 -0
  84. package/dist/commands/bootstrap.d.ts.map +1 -0
  85. package/dist/commands/chat.d.ts +37 -0
  86. package/dist/commands/chat.d.ts.map +1 -0
  87. package/dist/commands/code.d.ts +28 -0
  88. package/dist/commands/code.d.ts.map +1 -0
  89. package/dist/commands/console.d.ts +22 -0
  90. package/dist/commands/console.d.ts.map +1 -0
  91. package/dist/commands/describe.d.ts +50 -0
  92. package/dist/commands/describe.d.ts.map +1 -0
  93. package/dist/commands/eval.d.ts +23 -0
  94. package/dist/commands/eval.d.ts.map +1 -0
  95. package/dist/commands/help.d.ts +25 -0
  96. package/dist/commands/help.d.ts.map +1 -0
  97. package/dist/commands/index.d.ts +18 -0
  98. package/dist/commands/index.d.ts.map +1 -0
  99. package/dist/commands/introspect.d.ts +24 -0
  100. package/dist/commands/introspect.d.ts.map +1 -0
  101. package/dist/commands/mcp.d.ts +35 -0
  102. package/dist/commands/mcp.d.ts.map +1 -0
  103. package/dist/commands/prompt.d.ts +38 -0
  104. package/dist/commands/prompt.d.ts.map +1 -0
  105. package/dist/commands/run.d.ts +24 -0
  106. package/dist/commands/run.d.ts.map +1 -0
  107. package/dist/commands/sandbox-mcp.d.ts +34 -0
  108. package/dist/commands/sandbox-mcp.d.ts.map +1 -0
  109. package/dist/commands/save-api-docs.d.ts +21 -0
  110. package/dist/commands/save-api-docs.d.ts.map +1 -0
  111. package/dist/commands/scaffold.d.ts +24 -0
  112. package/dist/commands/scaffold.d.ts.map +1 -0
  113. package/dist/commands/select.d.ts +22 -0
  114. package/dist/commands/select.d.ts.map +1 -0
  115. package/dist/commands/serve.d.ts +29 -0
  116. package/dist/commands/serve.d.ts.map +1 -0
  117. package/dist/container-describer.d.ts +144 -0
  118. package/dist/container-describer.d.ts.map +1 -0
  119. package/dist/container.d.ts +451 -0
  120. package/dist/container.d.ts.map +1 -0
  121. package/dist/endpoint.d.ts +113 -0
  122. package/dist/endpoint.d.ts.map +1 -0
  123. package/dist/feature.d.ts +47 -0
  124. package/dist/feature.d.ts.map +1 -0
  125. package/dist/graft.d.ts +29 -0
  126. package/dist/graft.d.ts.map +1 -0
  127. package/dist/hash-object.d.ts +8 -0
  128. package/dist/hash-object.d.ts.map +1 -0
  129. package/dist/helper.d.ts +209 -0
  130. package/dist/helper.d.ts.map +1 -0
  131. package/dist/introspection/generated.node.d.ts +44623 -0
  132. package/dist/introspection/generated.node.d.ts.map +1 -0
  133. package/dist/introspection/generated.web.d.ts +1412 -0
  134. package/dist/introspection/generated.web.d.ts.map +1 -0
  135. package/dist/introspection/index.d.ts +156 -0
  136. package/dist/introspection/index.d.ts.map +1 -0
  137. package/dist/introspection/scan.d.ts +147 -0
  138. package/dist/introspection/scan.d.ts.map +1 -0
  139. package/dist/node/container.d.ts +256 -0
  140. package/dist/node/container.d.ts.map +1 -0
  141. package/dist/node/feature.d.ts +9 -0
  142. package/dist/node/feature.d.ts.map +1 -0
  143. package/dist/node/features/container-link.d.ts +213 -0
  144. package/dist/node/features/container-link.d.ts.map +1 -0
  145. package/dist/node/features/content-db.d.ts +354 -0
  146. package/dist/node/features/content-db.d.ts.map +1 -0
  147. package/dist/node/features/disk-cache.d.ts +236 -0
  148. package/dist/node/features/disk-cache.d.ts.map +1 -0
  149. package/dist/node/features/dns.d.ts +511 -0
  150. package/dist/node/features/dns.d.ts.map +1 -0
  151. package/dist/node/features/docker.d.ts +485 -0
  152. package/dist/node/features/docker.d.ts.map +1 -0
  153. package/dist/node/features/downloader.d.ts +73 -0
  154. package/dist/node/features/downloader.d.ts.map +1 -0
  155. package/dist/node/features/figlet-fonts.d.ts +4 -0
  156. package/dist/node/features/figlet-fonts.d.ts.map +1 -0
  157. package/dist/node/features/file-manager.d.ts +177 -0
  158. package/dist/node/features/file-manager.d.ts.map +1 -0
  159. package/dist/node/features/fs.d.ts +635 -0
  160. package/dist/node/features/fs.d.ts.map +1 -0
  161. package/dist/node/features/git.d.ts +329 -0
  162. package/dist/node/features/git.d.ts.map +1 -0
  163. package/dist/node/features/google-auth.d.ts +200 -0
  164. package/dist/node/features/google-auth.d.ts.map +1 -0
  165. package/dist/node/features/google-calendar.d.ts +194 -0
  166. package/dist/node/features/google-calendar.d.ts.map +1 -0
  167. package/dist/node/features/google-docs.d.ts +138 -0
  168. package/dist/node/features/google-docs.d.ts.map +1 -0
  169. package/dist/node/features/google-drive.d.ts +202 -0
  170. package/dist/node/features/google-drive.d.ts.map +1 -0
  171. package/dist/node/features/google-mail.d.ts +221 -0
  172. package/dist/node/features/google-mail.d.ts.map +1 -0
  173. package/dist/node/features/google-sheets.d.ts +157 -0
  174. package/dist/node/features/google-sheets.d.ts.map +1 -0
  175. package/dist/node/features/grep.d.ts +207 -0
  176. package/dist/node/features/grep.d.ts.map +1 -0
  177. package/dist/node/features/helpers.d.ts +236 -0
  178. package/dist/node/features/helpers.d.ts.map +1 -0
  179. package/dist/node/features/ink.d.ts +332 -0
  180. package/dist/node/features/ink.d.ts.map +1 -0
  181. package/dist/node/features/ipc-socket.d.ts +298 -0
  182. package/dist/node/features/ipc-socket.d.ts.map +1 -0
  183. package/dist/node/features/json-tree.d.ts +140 -0
  184. package/dist/node/features/json-tree.d.ts.map +1 -0
  185. package/dist/node/features/networking.d.ts +373 -0
  186. package/dist/node/features/networking.d.ts.map +1 -0
  187. package/dist/node/features/nlp.d.ts +125 -0
  188. package/dist/node/features/nlp.d.ts.map +1 -0
  189. package/dist/node/features/opener.d.ts +93 -0
  190. package/dist/node/features/opener.d.ts.map +1 -0
  191. package/dist/node/features/os.d.ts +168 -0
  192. package/dist/node/features/os.d.ts.map +1 -0
  193. package/dist/node/features/package-finder.d.ts +419 -0
  194. package/dist/node/features/package-finder.d.ts.map +1 -0
  195. package/dist/node/features/postgres.d.ts +173 -0
  196. package/dist/node/features/postgres.d.ts.map +1 -0
  197. package/dist/node/features/proc.d.ts +285 -0
  198. package/dist/node/features/proc.d.ts.map +1 -0
  199. package/dist/node/features/process-manager.d.ts +427 -0
  200. package/dist/node/features/process-manager.d.ts.map +1 -0
  201. package/dist/node/features/python.d.ts +477 -0
  202. package/dist/node/features/python.d.ts.map +1 -0
  203. package/dist/node/features/redis.d.ts +247 -0
  204. package/dist/node/features/redis.d.ts.map +1 -0
  205. package/dist/node/features/repl.d.ts +84 -0
  206. package/dist/node/features/repl.d.ts.map +1 -0
  207. package/dist/node/features/runpod.d.ts +527 -0
  208. package/dist/node/features/runpod.d.ts.map +1 -0
  209. package/dist/node/features/secure-shell.d.ts +145 -0
  210. package/dist/node/features/secure-shell.d.ts.map +1 -0
  211. package/dist/node/features/semantic-search.d.ts +207 -0
  212. package/dist/node/features/semantic-search.d.ts.map +1 -0
  213. package/dist/node/features/sqlite.d.ts +180 -0
  214. package/dist/node/features/sqlite.d.ts.map +1 -0
  215. package/dist/node/features/telegram.d.ts +173 -0
  216. package/dist/node/features/telegram.d.ts.map +1 -0
  217. package/dist/node/features/transpiler.d.ts +51 -0
  218. package/dist/node/features/transpiler.d.ts.map +1 -0
  219. package/dist/node/features/tts.d.ts +108 -0
  220. package/dist/node/features/tts.d.ts.map +1 -0
  221. package/dist/node/features/ui.d.ts +562 -0
  222. package/dist/node/features/ui.d.ts.map +1 -0
  223. package/dist/node/features/vault.d.ts +90 -0
  224. package/dist/node/features/vault.d.ts.map +1 -0
  225. package/dist/node/features/vm.d.ts +285 -0
  226. package/dist/node/features/vm.d.ts.map +1 -0
  227. package/dist/node/features/yaml-tree.d.ts +118 -0
  228. package/dist/node/features/yaml-tree.d.ts.map +1 -0
  229. package/dist/node/features/yaml.d.ts +127 -0
  230. package/dist/node/features/yaml.d.ts.map +1 -0
  231. package/dist/node.d.ts +67 -0
  232. package/dist/node.d.ts.map +1 -0
  233. package/dist/python/generated.d.ts +2 -0
  234. package/dist/python/generated.d.ts.map +1 -0
  235. package/dist/react/index.d.ts +36 -0
  236. package/dist/react/index.d.ts.map +1 -0
  237. package/dist/registry.d.ts +97 -0
  238. package/dist/registry.d.ts.map +1 -0
  239. package/dist/scaffolds/generated.d.ts +13 -0
  240. package/dist/scaffolds/generated.d.ts.map +1 -0
  241. package/dist/scaffolds/template.d.ts +11 -0
  242. package/dist/scaffolds/template.d.ts.map +1 -0
  243. package/dist/schemas/base.d.ts +254 -0
  244. package/dist/schemas/base.d.ts.map +1 -0
  245. package/dist/selector.d.ts +130 -0
  246. package/dist/selector.d.ts.map +1 -0
  247. package/dist/server.d.ts +89 -0
  248. package/dist/server.d.ts.map +1 -0
  249. package/dist/servers/express.d.ts +104 -0
  250. package/dist/servers/express.d.ts.map +1 -0
  251. package/dist/servers/mcp.d.ts +201 -0
  252. package/dist/servers/mcp.d.ts.map +1 -0
  253. package/dist/servers/socket.d.ts +121 -0
  254. package/dist/servers/socket.d.ts.map +1 -0
  255. package/dist/state.d.ts +24 -0
  256. package/dist/state.d.ts.map +1 -0
  257. package/dist/web/clients/socket.d.ts +37 -0
  258. package/dist/web/clients/socket.d.ts.map +1 -0
  259. package/dist/web/container.d.ts +55 -0
  260. package/dist/web/container.d.ts.map +1 -0
  261. package/dist/web/extension.d.ts +4 -0
  262. package/dist/web/extension.d.ts.map +1 -0
  263. package/dist/web/feature.d.ts +8 -0
  264. package/dist/web/feature.d.ts.map +1 -0
  265. package/dist/web/features/asset-loader.d.ts +35 -0
  266. package/dist/web/features/asset-loader.d.ts.map +1 -0
  267. package/dist/web/features/container-link.d.ts +167 -0
  268. package/dist/web/features/container-link.d.ts.map +1 -0
  269. package/dist/web/features/esbuild.d.ts +51 -0
  270. package/dist/web/features/esbuild.d.ts.map +1 -0
  271. package/dist/web/features/helpers.d.ts +140 -0
  272. package/dist/web/features/helpers.d.ts.map +1 -0
  273. package/dist/web/features/network.d.ts +69 -0
  274. package/dist/web/features/network.d.ts.map +1 -0
  275. package/dist/web/features/speech.d.ts +71 -0
  276. package/dist/web/features/speech.d.ts.map +1 -0
  277. package/dist/web/features/vault.d.ts +62 -0
  278. package/dist/web/features/vault.d.ts.map +1 -0
  279. package/dist/web/features/vm.d.ts +48 -0
  280. package/dist/web/features/vm.d.ts.map +1 -0
  281. package/dist/web/features/voice-recognition.d.ts +96 -0
  282. package/dist/web/features/voice-recognition.d.ts.map +1 -0
  283. package/dist/web/shims/isomorphic-vm.d.ts +22 -0
  284. package/dist/web/shims/isomorphic-vm.d.ts.map +1 -0
  285. package/docs/apis/features/agi/assistant.md +1 -0
  286. package/docs/apis/features/agi/assistants-manager.md +62 -2
  287. package/docs/apis/features/agi/auto-assistant.md +11 -109
  288. package/docs/apis/features/agi/claude-code.md +138 -0
  289. package/docs/apis/features/agi/conversation.md +60 -31
  290. package/docs/apis/features/agi/luca-coder.md +407 -0
  291. package/docs/apis/features/agi/openapi.md +2 -2
  292. package/docs/apis/features/agi/skills-library.md +12 -0
  293. package/docs/apis/features/node/python.md +81 -11
  294. package/docs/apis/features/node/transpiler.md +74 -0
  295. package/docs/apis/features/web/esbuild.md +0 -6
  296. package/docs/apis/servers/mcp.md +2 -2
  297. package/docs/examples/entity.md +124 -0
  298. package/package.json +73 -21
  299. package/src/agi/feature.ts +13 -0
  300. package/src/agi/features/assistant.ts +36 -25
  301. package/src/agi/features/assistants-manager.ts +70 -5
  302. package/src/agi/features/autonomous-assistant.ts +1 -5
  303. package/src/agi/features/browser-use.ts +2 -2
  304. package/src/agi/features/claude-code.ts +165 -1
  305. package/src/agi/features/conversation-history.ts +2 -6
  306. package/src/agi/features/conversation.ts +95 -3
  307. package/src/agi/features/docs-reader.ts +2 -1
  308. package/src/agi/features/file-tools.ts +2 -2
  309. package/src/agi/features/luca-coder.ts +1 -5
  310. package/src/agi/features/openai-codex.ts +1 -1
  311. package/src/agi/features/openapi.ts +3 -3
  312. package/src/agi/features/skills-library.ts +87 -6
  313. package/src/agi/lib/interceptor-chain.ts +10 -0
  314. package/src/agi/lib/token-counter.ts +1 -1
  315. package/src/bootstrap/generated.ts +126 -1
  316. package/src/bus.ts +27 -5
  317. package/src/cli/build-info.ts +2 -2
  318. package/src/client.ts +2 -2
  319. package/src/clients/elevenlabs/index.ts +5 -0
  320. package/src/commands/bootstrap.ts +2 -1
  321. package/src/commands/chat.ts +1 -0
  322. package/src/commands/code.ts +4 -2
  323. package/src/commands/prompt.ts +34 -34
  324. package/src/commands/sandbox-mcp.ts +69 -163
  325. package/src/commands/save-api-docs.ts +10 -8
  326. package/src/commands/select.ts +8 -3
  327. package/src/container-describer.ts +70 -84
  328. package/src/container.ts +93 -3
  329. package/src/endpoint.ts +1 -1
  330. package/src/entity.ts +173 -0
  331. package/src/feature.ts +3 -3
  332. package/src/helper.ts +8 -4
  333. package/src/introspection/generated.agi.ts +1246 -798
  334. package/src/introspection/generated.node.ts +892 -798
  335. package/src/introspection/generated.web.ts +95 -3
  336. package/src/introspection/scan.ts +1 -1
  337. package/src/node/container.ts +1 -1
  338. package/src/node/features/content-db.ts +3 -3
  339. package/src/node/features/file-manager.ts +10 -9
  340. package/src/node/features/git.ts +5 -5
  341. package/src/node/features/helpers.ts +1 -1
  342. package/src/node/features/json-tree.ts +1 -1
  343. package/src/node/features/os.ts +3 -3
  344. package/src/node/features/package-finder.ts +1 -1
  345. package/src/node/features/process-manager.ts +1 -1
  346. package/src/node/features/python.ts +3 -3
  347. package/src/node/features/redis.ts +1 -1
  348. package/src/node/features/repl.ts +2 -2
  349. package/src/node/features/transpiler.ts +2 -2
  350. package/src/node/features/ui.ts +1 -1
  351. package/src/node/features/vm.ts +3 -3
  352. package/src/node/features/yaml-tree.ts +1 -1
  353. package/src/node.ts +1 -0
  354. package/src/python/generated.ts +1 -1
  355. package/src/scaffolds/generated.ts +1 -1
  356. package/src/selector.ts +74 -4
  357. package/src/server.ts +2 -2
  358. package/src/servers/mcp.ts +6 -6
  359. package/src/web/features/helpers.ts +1 -1
  360. package/src/web/features/network.ts +1 -0
  361. package/test/conversation.test.ts +220 -0
  362. package/tsconfig.build.json +12 -0
  363. package/tsconfig.json +1 -1
  364. package/scripts/examples/telegram-ink-ui.ts +0 -302
  365. package/scripts/examples/using-openai-codex.ts +0 -23
  366. package/scripts/examples/vm-loading-esm-modules.ts +0 -16
@@ -136,7 +136,7 @@ export class Helpers extends Feature<HelpersState, HelpersOptions> {
136
136
  return {}
137
137
  }
138
138
 
139
- const manifest: Manifest = await response.json()
139
+ const manifest = await response.json() as Manifest
140
140
  this._manifest = manifest
141
141
  this.state.set('manifestLoaded', true)
142
142
  this.emit('manifestLoaded' as any, manifest)
@@ -1,3 +1,4 @@
1
+ /// <reference lib="dom" />
1
2
  import { z } from 'zod'
2
3
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
4
  import { Feature } from "../feature.js";
@@ -0,0 +1,220 @@
1
+ import { describe, it, expect, beforeEach } from 'bun:test'
2
+ import { AGIContainer } from '../src/agi/container.server'
3
+ import type { Conversation } from '../src/agi/features/conversation'
4
+
5
+ function makeConversation(opts: Record<string, any> = {}): Conversation {
6
+ const container = new AGIContainer()
7
+ return container.feature('conversation', { model: 'gpt-5', ...opts }) as Conversation
8
+ }
9
+
10
+ describe('Conversation', () => {
11
+ describe('state', () => {
12
+ it('initializes with empty messages when no history provided', () => {
13
+ const conv = makeConversation()
14
+ expect(conv.messages).toEqual([])
15
+ })
16
+
17
+ it('seeds message history from options', () => {
18
+ const history = [{ role: 'system', content: 'You are helpful.' }]
19
+ const conv = makeConversation({ history })
20
+ expect(conv.messages).toHaveLength(1)
21
+ expect(conv.messages[0]).toEqual(history[0])
22
+ })
23
+
24
+ it('uses provided model', () => {
25
+ const conv = makeConversation({ model: 'gpt-4.1' })
26
+ expect(conv.model).toBe('gpt-4.1')
27
+ })
28
+
29
+ it('isStreaming is false initially', () => {
30
+ const conv = makeConversation()
31
+ expect(conv.isStreaming).toBe(false)
32
+ })
33
+ })
34
+
35
+ describe('pushMessage', () => {
36
+ it('appends to messages', () => {
37
+ const conv = makeConversation()
38
+ conv.pushMessage({ role: 'user', content: 'hello' })
39
+ expect(conv.messages).toHaveLength(1)
40
+ expect(conv.messages[0]!.role).toBe('user')
41
+ })
42
+
43
+ it('does not mutate prior messages array', () => {
44
+ const conv = makeConversation()
45
+ const before = conv.messages
46
+ conv.pushMessage({ role: 'user', content: 'hi' })
47
+ expect(conv.messages).not.toBe(before)
48
+ })
49
+ })
50
+
51
+ describe('tools', () => {
52
+ it('starts with no tools', () => {
53
+ const conv = makeConversation()
54
+ expect(conv.availableTools).toHaveLength(0)
55
+ })
56
+
57
+ it('addTool registers a tool', () => {
58
+ const conv = makeConversation()
59
+ conv.addTool('greet', {
60
+ description: 'Says hello',
61
+ parameters: { type: 'object', properties: {} },
62
+ handler: async () => 'hello',
63
+ })
64
+ expect(conv.availableTools).toContain('greet')
65
+ })
66
+
67
+ it('removeTool deregisters a tool', () => {
68
+ const conv = makeConversation()
69
+ conv.addTool('greet', {
70
+ description: 'Says hello',
71
+ parameters: { type: 'object', properties: {} },
72
+ handler: async () => 'hello',
73
+ })
74
+ conv.removeTool('greet')
75
+ expect(conv.availableTools).not.toContain('greet')
76
+ })
77
+
78
+ it('updateTools merges without replacing unrelated tools', () => {
79
+ const conv = makeConversation()
80
+ conv.addTool('a', { description: 'A', parameters: { type: 'object', properties: {} }, handler: async () => 'a' })
81
+ conv.updateTools({ b: { description: 'B', parameters: { type: 'object', properties: {} }, handler: async () => 'b' } })
82
+ expect(conv.availableTools).toContain('a')
83
+ expect(conv.availableTools).toContain('b')
84
+ })
85
+
86
+ it('construction-time tools are available immediately', () => {
87
+ const conv = makeConversation({
88
+ tools: {
89
+ ping: { description: 'Ping', parameters: { type: 'object', properties: {} }, handler: async () => 'pong' }
90
+ }
91
+ })
92
+ expect(conv.availableTools).toContain('ping')
93
+ })
94
+ })
95
+
96
+ describe('estimateTokens', () => {
97
+ it('returns a near-zero baseline for empty messages', () => {
98
+ const conv = makeConversation()
99
+ expect(conv.estimateTokens()).toBeLessThan(10)
100
+ })
101
+
102
+ it('returns a positive number when messages exist', () => {
103
+ const conv = makeConversation({
104
+ history: [{ role: 'user', content: 'What is the capital of France?' }]
105
+ })
106
+ expect(conv.estimateTokens()).toBeGreaterThan(0)
107
+ })
108
+
109
+ it('increases as more messages are added', () => {
110
+ const conv = makeConversation()
111
+ conv.pushMessage({ role: 'user', content: 'Hello' })
112
+ const first = conv.estimateTokens()
113
+ conv.pushMessage({ role: 'assistant', content: 'Hi there, how can I help you today?' })
114
+ const second = conv.estimateTokens()
115
+ expect(second).toBeGreaterThan(first)
116
+ })
117
+ })
118
+
119
+ describe('stub()', () => {
120
+ it('exact string match returns the stub response', async () => {
121
+ const conv = makeConversation()
122
+ conv.stub('ping', 'pong')
123
+ const result = await conv.ask('ping')
124
+ expect(result).toBe('pong')
125
+ })
126
+
127
+ it('substring match triggers the stub', async () => {
128
+ const conv = makeConversation()
129
+ conv.stub('weather', 'Sunny and 72°F.')
130
+ const result = await conv.ask('what is the weather today?')
131
+ expect(result).toBe('Sunny and 72°F.')
132
+ })
133
+
134
+ it('regex match triggers the stub', async () => {
135
+ const conv = makeConversation()
136
+ conv.stub(/hello/i, 'Hey there!')
137
+ const result = await conv.ask('HELLO')
138
+ expect(result).toBe('Hey there!')
139
+ })
140
+
141
+ it('function response is called on each match', async () => {
142
+ const conv = makeConversation()
143
+ let callCount = 0
144
+ conv.stub('count', () => `call ${++callCount}`)
145
+ expect(await conv.ask('count')).toBe('call 1')
146
+ expect(await conv.ask('count')).toBe('call 2')
147
+ })
148
+
149
+ it('first matching stub wins', async () => {
150
+ const conv = makeConversation()
151
+ conv.stub('hello', 'first')
152
+ conv.stub('hello', 'second')
153
+ expect(await conv.ask('hello')).toBe('first')
154
+ })
155
+
156
+ it('appends user and assistant messages to history', async () => {
157
+ const conv = makeConversation()
158
+ conv.stub('hi', 'hello back')
159
+ await conv.ask('hi')
160
+ expect(conv.messages).toHaveLength(2)
161
+ expect(conv.messages[0]).toMatchObject({ role: 'user', content: 'hi' })
162
+ expect(conv.messages[1]).toMatchObject({ role: 'assistant', content: 'hello back' })
163
+ })
164
+
165
+ it('isStreaming is false after the call resolves', async () => {
166
+ const conv = makeConversation()
167
+ conv.stub('test', 'response')
168
+ await conv.ask('test')
169
+ expect(conv.isStreaming).toBe(false)
170
+ })
171
+
172
+ it('emits chunk events for each word', async () => {
173
+ const conv = makeConversation()
174
+ conv.stub('go', 'one two three')
175
+ const chunks: string[] = []
176
+ conv.on('chunk', (delta: string) => chunks.push(delta))
177
+ await conv.ask('go')
178
+ expect(chunks.join('')).toBe('one two three')
179
+ })
180
+
181
+ it('emits preview events with accumulating text', async () => {
182
+ const conv = makeConversation()
183
+ conv.stub('go', 'alpha beta')
184
+ const previews: string[] = []
185
+ conv.on('preview', (text: string) => previews.push(text))
186
+ await conv.ask('go')
187
+ expect(previews.at(-1)).toBe('alpha beta')
188
+ // Each preview should be longer than or equal to the previous
189
+ for (let i = 1; i < previews.length; i++) {
190
+ expect(previews[i]!.length).toBeGreaterThanOrEqual(previews[i - 1]!.length)
191
+ }
192
+ })
193
+
194
+ it('emits turnStart and turnEnd events', async () => {
195
+ const conv = makeConversation()
196
+ conv.stub('x', 'y')
197
+ const events: string[] = []
198
+ conv.on('turnStart', () => events.push('turnStart'))
199
+ conv.on('turnEnd', () => events.push('turnEnd'))
200
+ await conv.ask('x')
201
+ expect(events).toEqual(['turnStart', 'turnEnd'])
202
+ })
203
+
204
+ it('emits response event with the full text', async () => {
205
+ const conv = makeConversation()
206
+ conv.stub('question', 'the answer')
207
+ let emitted = ''
208
+ conv.on('response', (text: string) => { emitted = text })
209
+ await conv.ask('question')
210
+ expect(emitted).toBe('the answer')
211
+ })
212
+
213
+ it('stub is chainable', () => {
214
+ const conv = makeConversation()
215
+ const result = conv.stub('a', 'A').stub('b', 'B')
216
+ expect(result).toBe(conv)
217
+ expect(conv.availableTools).toHaveLength(0) // stubs don't affect tools
218
+ })
219
+ })
220
+ })
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": false,
5
+ "emitDeclarationOnly": true,
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "outDir": "./dist",
9
+ "rootDir": "./src"
10
+ },
11
+ "include": ["src/**/*.ts", "src/**/*.tsx"]
12
+ }
package/tsconfig.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  // Environment setup & latest features
4
- "lib": ["ESNext"],
4
+ "lib": ["ESNext", "dom", "dom.iterable"],
5
5
  "target": "ESNext",
6
6
  "module": "Preserve",
7
7
  "moduleDetection": "force",
@@ -1,302 +0,0 @@
1
- import { spawn, type ChildProcessByStdio } from 'node:child_process'
2
- import type { Readable } from 'node:stream'
3
- import path from 'node:path'
4
- import { existsSync } from 'node:fs'
5
- import container from '@soederpop/luca/node'
6
- import { resolveAnimation } from './animations'
7
-
8
- type ProcessStatus = 'idle' | 'running' | 'exited' | 'error'
9
-
10
- const MAX_OUTPUT_LINES = 220
11
- const DEFAULT_ANIMATION = process.env.LUCA_BOT_ANIMATION || 'neonPulse'
12
-
13
- function splitLines(input: string): string[] {
14
- return input.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n')
15
- }
16
-
17
- function pushChunk(target: string[], chunk: string, label?: string) {
18
- const lines = splitLines(chunk).filter((line) => line.length > 0)
19
- for (const line of lines) {
20
- target.push(label ? `[${label}] ${line}` : line)
21
- }
22
- if (target.length > MAX_OUTPUT_LINES) {
23
- target.splice(0, target.length - MAX_OUTPUT_LINES)
24
- }
25
- }
26
-
27
- function resolveTelegramScriptPath(): string | null {
28
- const fromEnv = process.env.LUCA_TELEGRAM_SCRIPT?.trim()
29
- const candidates = [
30
- fromEnv,
31
- 'scripts/examples/telegram.ts',
32
- 'scripts/examples/telegram-bot.ts',
33
- 'scripts/telegram.ts',
34
- 'scripts/telegram-bot.ts',
35
- ].filter(Boolean) as string[]
36
-
37
- for (const candidate of candidates) {
38
- const resolved = path.isAbsolute(candidate) ? candidate : path.resolve(process.cwd(), candidate)
39
- if (existsSync(resolved)) return resolved
40
- }
41
-
42
- return null
43
- }
44
-
45
- function createBotCommand(): { command: string; args: string[]; source: string } {
46
- const explicitCommand = process.env.LUCA_TELEGRAM_COMMAND?.trim()
47
- if (explicitCommand) {
48
- return {
49
- command: '/bin/sh',
50
- args: ['-lc', explicitCommand],
51
- source: explicitCommand,
52
- }
53
- }
54
-
55
- const scriptPath = resolveTelegramScriptPath()
56
- if (!scriptPath) {
57
- const helpLines = [
58
- 'No telegram script found.',
59
- 'Set LUCA_TELEGRAM_SCRIPT to your script path, e.g.:',
60
- ' LUCA_TELEGRAM_SCRIPT=scripts/my-telegram.ts',
61
- 'Or set LUCA_TELEGRAM_COMMAND for a custom run command.',
62
- ]
63
- const helpCommand = `${helpLines.map((line) => `echo "${line.replace(/"/g, '\\"')}"`).join('; ')}; sleep 9999`
64
-
65
- return {
66
- command: '/bin/sh',
67
- args: ['-lc', helpCommand],
68
- source: 'waiting-for-config',
69
- }
70
- }
71
-
72
- return {
73
- command: 'bun',
74
- args: ['run', scriptPath],
75
- source: scriptPath,
76
- }
77
- }
78
-
79
- async function main() {
80
- const ink = container.feature('ink', { enable: true, patchConsole: true })
81
- await ink.loadModules()
82
-
83
- const React = ink.React
84
- const h = React.createElement
85
- const { useEffect, useMemo, useRef, useState } = React
86
-
87
- const { Box, Text } = ink.components
88
- const { useApp, useInput, useStdout, useStdin } = ink.hooks
89
-
90
- const animation = resolveAnimation(DEFAULT_ANIMATION)
91
- const botProcess = createBotCommand()
92
-
93
- function useAnimationFrame(frames: string[], fps: number): string {
94
- const [index, setIndex] = useState(0)
95
-
96
- useEffect(() => {
97
- const safeFps = Math.max(1, fps)
98
- const interval = setInterval(() => {
99
- setIndex((prev: number) => (prev + 1) % frames.length)
100
- }, Math.floor(1000 / safeFps))
101
- return () => clearInterval(interval)
102
- }, [fps, frames.length])
103
-
104
- return frames[index] || ''
105
- }
106
-
107
- function frameLines(frame: string, maxWidth: number): string[] {
108
- return splitLines(frame).map((line) => (line.length > maxWidth ? line.slice(0, maxWidth - 1) + '.' : line))
109
- }
110
-
111
- function App() {
112
- const { stdout } = useStdout()
113
- const { isRawModeSupported } = useStdin()
114
- const { exit } = useApp()
115
-
116
- const [status, setStatus] = useState<ProcessStatus>('idle')
117
- const [exitCode, setExitCode] = useState<number | null>(null)
118
- const [startedAt, setStartedAt] = useState<number | null>(null)
119
- const [outputLines, setOutputLines] = useState<string[]>([])
120
-
121
- const processRef = useRef<ChildProcessByStdio<null, Readable, Readable> | null>(null)
122
- const bufferRef = useRef({ stdout: '', stderr: '' })
123
-
124
- const cols = stdout.columns || 120
125
- const leftWidth = Math.max(38, Math.floor(cols * 0.42))
126
- const rightWidth = Math.max(42, cols - leftWidth - 5)
127
-
128
- const currentFrame = useAnimationFrame(animation.frames, animation.fps)
129
-
130
- const uptime = useMemo(() => {
131
- if (!startedAt || status === 'idle') return '00:00'
132
- const secs = Math.floor((Date.now() - startedAt) / 1000)
133
- const mm = Math.floor(secs / 60)
134
- const ss = secs % 60
135
- return `${String(mm).padStart(2, '0')}:${String(ss).padStart(2, '0')}`
136
- }, [startedAt, status, outputLines.length])
137
-
138
- useEffect(() => {
139
- const run = () => {
140
- setStatus('running')
141
- setStartedAt(Date.now())
142
-
143
- const child = spawn(botProcess.command, botProcess.args, {
144
- cwd: process.cwd(),
145
- env: process.env,
146
- stdio: ['ignore', 'pipe', 'pipe'],
147
- })
148
-
149
- processRef.current = child
150
-
151
- const flushOutput = (kind: 'stdout' | 'stderr', chunk: Buffer) => {
152
- const key = kind
153
- const prior = bufferRef.current[key]
154
- const merged = prior + chunk.toString('utf8')
155
- const lines = merged.split(/\r?\n/)
156
- bufferRef.current[key] = lines.pop() || ''
157
-
158
- if (lines.length) {
159
- setOutputLines((prev) => {
160
- const next = [...prev]
161
- for (const line of lines) {
162
- if (!line) continue
163
- next.push(kind === 'stderr' ? `[err] ${line}` : line)
164
- }
165
- return next.slice(-MAX_OUTPUT_LINES)
166
- })
167
- }
168
- }
169
-
170
- child.stdout.on('data', (buf) => flushOutput('stdout', buf))
171
- child.stderr.on('data', (buf) => flushOutput('stderr', buf))
172
-
173
- child.on('error', (err) => {
174
- setStatus('error')
175
- setOutputLines((prev) => [...prev.slice(-MAX_OUTPUT_LINES + 1), `[launcher-error] ${err.message}`])
176
- })
177
-
178
- child.on('close', (code) => {
179
- const lastOut = bufferRef.current.stdout
180
- const lastErr = bufferRef.current.stderr
181
-
182
- setOutputLines((prev) => {
183
- const next = [...prev]
184
- if (lastOut) pushChunk(next, lastOut)
185
- if (lastErr) pushChunk(next, lastErr, 'err')
186
- next.push(`[system] telegram process exited${typeof code === 'number' ? ` with code ${code}` : ''}`)
187
- return next.slice(-MAX_OUTPUT_LINES)
188
- })
189
-
190
- setExitCode(typeof code === 'number' ? code : null)
191
- setStatus(code === 0 ? 'exited' : 'error')
192
- processRef.current = null
193
- bufferRef.current = { stdout: '', stderr: '' }
194
- })
195
-
196
- setOutputLines((prev) => [...prev.slice(-MAX_OUTPUT_LINES + 1), `[system] spawned: ${botProcess.command} ${botProcess.args.join(' ')}`])
197
- }
198
-
199
- run()
200
-
201
- return () => {
202
- const proc = processRef.current
203
- if (proc && !proc.killed) {
204
- try {
205
- proc.kill('SIGTERM')
206
- } catch {
207
- // no-op: process already gone
208
- }
209
- }
210
- }
211
- }, [])
212
-
213
- useInput(
214
- (input: string, key: any) => {
215
- if (input === 'q' || (key.ctrl && input === 'c')) {
216
- const proc = processRef.current
217
- if (proc && !proc.killed) {
218
- try {
219
- proc.kill('SIGTERM')
220
- } catch {
221
- // no-op
222
- }
223
- }
224
- exit()
225
- }
226
- },
227
- { isActive: !!isRawModeSupported },
228
- )
229
-
230
- const statusColor = status === 'running' ? 'green' : status === 'exited' ? 'cyan' : status === 'error' ? 'red' : 'yellow'
231
- const statusLabel = status === 'running' ? 'LIVE' : status === 'exited' ? `EXIT ${exitCode ?? 0}` : status === 'error' ? `ERR ${exitCode ?? '?'}` : 'BOOT'
232
-
233
- const visibleOutputLines = outputLines.slice(-Math.max(14, (stdout.rows || 32) - 10))
234
- const artLines = frameLines(currentFrame, leftWidth - 4)
235
-
236
- return h(
237
- Box,
238
- { flexDirection: 'column', width: cols, paddingX: 1 },
239
- h(
240
- Box,
241
- { justifyContent: 'space-between', marginBottom: 1 },
242
- h(Text, { bold: true, color: '#61dafb' }, 'LUCA TELEGRAM // ART CONSOLE'),
243
- h(Text, { color: statusColor }, `[${statusLabel}] uptime ${uptime}`),
244
- ),
245
- h(
246
- Box,
247
- { flexDirection: 'row' },
248
- h(
249
- Box,
250
- {
251
- width: leftWidth,
252
- flexDirection: 'column',
253
- borderStyle: 'round',
254
- borderColor: '#e879f9',
255
- paddingX: 1,
256
- minHeight: Math.max(16, (stdout.rows || 30) - 6),
257
- },
258
- h(Text, { color: '#f472b6', bold: true }, `${animation.name} (${animation.id})`),
259
- h(Text, { dimColor: true }, 'bot avatar loop // modular animation script'),
260
- h(Text, { dimColor: true }, '-'.repeat(Math.max(10, leftWidth - 4))),
261
- ...artLines.map((line, idx) => h(Text, { key: `frame-${idx}`, color: '#fbcfe8' }, line)),
262
- ),
263
- h(Box, { width: 1 }, h(Text, { dimColor: true }, '|')),
264
- h(
265
- Box,
266
- {
267
- width: rightWidth,
268
- flexDirection: 'column',
269
- borderStyle: 'round',
270
- borderColor: '#22d3ee',
271
- paddingX: 1,
272
- minHeight: Math.max(16, (stdout.rows || 30) - 6),
273
- },
274
- h(Text, { color: '#67e8f9', bold: true }, 'Telegram Bot Stream'),
275
- h(Text, { dimColor: true, wrap: 'truncate' }, `source: ${botProcess.source}`),
276
- h(Text, { dimColor: true }, '-'.repeat(Math.max(10, rightWidth - 4))),
277
- ...visibleOutputLines.map((line, idx) =>
278
- h(
279
- Text,
280
- {
281
- key: `line-${idx}`,
282
- wrap: 'truncate-middle',
283
- color: line.startsWith('[err]') || line.startsWith('[launcher-error]') ? '#f87171' : undefined,
284
- },
285
- line,
286
- ),
287
- ),
288
- ),
289
- ),
290
- h(Text, null, ''),
291
- h(Text, { dimColor: true }, 'q: quit ctrl+c: quit env: LUCA_TELEGRAM_SCRIPT | LUCA_TELEGRAM_COMMAND | LUCA_BOT_ANIMATION'),
292
- !isRawModeSupported
293
- ? h(Text, { dimColor: true }, 'note: raw stdin unavailable; keyboard shortcuts disabled in this terminal')
294
- : null,
295
- )
296
- }
297
-
298
- await ink.render(h(App))
299
- await ink.waitUntilExit()
300
- }
301
-
302
- await main()
@@ -1,23 +0,0 @@
1
- import container from '@soederpop/luca/agi'
2
-
3
- const codex = container.feature('openaiCodex')
4
-
5
- // Stream deltas as they come in
6
- codex.on('session:delta', ({ text }: any) => process.stdout.write(text))
7
- codex.on('session:exec', ({ exec }: any) => console.log(`\n[exec] ${exec.command?.join(' ')}`))
8
- codex.on('session:patch', ({ patch }: any) => console.log(`\n[patch] ${patch.path}`))
9
-
10
- async function main() {
11
- const session = await codex.run('Explain the purpose of this project in a few sentences.', {
12
- fullAuto: true,
13
- })
14
-
15
- console.log('\n---')
16
- console.log('Status:', session.status)
17
- console.log('Result:', session.result)
18
- console.log('Turns:', session.turns)
19
- console.log('Patches:', session.patches.length)
20
- console.log('Execs:', session.executions.length)
21
- }
22
-
23
- main()
@@ -1,16 +0,0 @@
1
- import container from '@soederpop/luca/agi'
2
-
3
- async function main() {
4
- const vm = container.feature('vm')
5
- const skillsFile = container.paths.resolve('experts/luca-core-framework/skills.ts')
6
- const skills = await container.fs.readFile(skillsFile)
7
- const transformedSkills = await container.feature('esbuild').transform(skills.toString(), { format: 'cjs' })
8
-
9
- const mod = { exports: {} as Record<string, any> }
10
- await vm.run(transformedSkills.code, { container, module: mod, exports: mod.exports })
11
-
12
- console.log('Loaded skills:', Object.keys(mod.exports))
13
- console.log(mod.exports)
14
- }
15
-
16
- main()