@soederpop/luca 0.1.1 → 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 (369) hide show
  1. package/CLAUDE.md +2 -0
  2. package/assistants/codingAssistant/hooks.ts +3 -0
  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/scripts/test-assistant-hooks.ts +13 -0
  300. package/src/agi/feature.ts +13 -0
  301. package/src/agi/features/assistant.ts +36 -25
  302. package/src/agi/features/assistants-manager.ts +70 -5
  303. package/src/agi/features/autonomous-assistant.ts +1 -5
  304. package/src/agi/features/browser-use.ts +2 -2
  305. package/src/agi/features/claude-code.ts +165 -1
  306. package/src/agi/features/conversation-history.ts +2 -6
  307. package/src/agi/features/conversation.ts +95 -3
  308. package/src/agi/features/docs-reader.ts +2 -1
  309. package/src/agi/features/file-tools.ts +2 -2
  310. package/src/agi/features/luca-coder.ts +1 -5
  311. package/src/agi/features/openai-codex.ts +1 -1
  312. package/src/agi/features/openapi.ts +3 -3
  313. package/src/agi/features/skills-library.ts +90 -2
  314. package/src/agi/lib/interceptor-chain.ts +10 -0
  315. package/src/agi/lib/token-counter.ts +1 -1
  316. package/src/bootstrap/generated.ts +126 -1
  317. package/src/bus.ts +27 -5
  318. package/src/cli/build-info.ts +2 -2
  319. package/src/client.ts +2 -2
  320. package/src/clients/elevenlabs/index.ts +5 -0
  321. package/src/commands/bootstrap.ts +2 -1
  322. package/src/commands/chat.ts +1 -0
  323. package/src/commands/code.ts +4 -2
  324. package/src/commands/prompt.ts +34 -34
  325. package/src/commands/sandbox-mcp.ts +69 -163
  326. package/src/commands/save-api-docs.ts +10 -8
  327. package/src/commands/select.ts +8 -3
  328. package/src/container-describer.ts +70 -84
  329. package/src/container.ts +93 -3
  330. package/src/endpoint.ts +1 -1
  331. package/src/entity.ts +173 -0
  332. package/src/feature.ts +3 -3
  333. package/src/helper.ts +8 -4
  334. package/src/introspection/generated.agi.ts +1403 -929
  335. package/src/introspection/generated.node.ts +127 -33
  336. package/src/introspection/generated.web.ts +95 -3
  337. package/src/introspection/scan.ts +1 -1
  338. package/src/node/container.ts +1 -1
  339. package/src/node/features/content-db.ts +3 -3
  340. package/src/node/features/file-manager.ts +10 -9
  341. package/src/node/features/git.ts +5 -5
  342. package/src/node/features/helpers.ts +1 -1
  343. package/src/node/features/json-tree.ts +1 -1
  344. package/src/node/features/os.ts +3 -3
  345. package/src/node/features/package-finder.ts +1 -1
  346. package/src/node/features/process-manager.ts +1 -1
  347. package/src/node/features/python.ts +3 -3
  348. package/src/node/features/redis.ts +1 -1
  349. package/src/node/features/repl.ts +2 -2
  350. package/src/node/features/transpiler.ts +34 -9
  351. package/src/node/features/ui.ts +1 -1
  352. package/src/node/features/vm.ts +6 -5
  353. package/src/node/features/yaml-tree.ts +1 -1
  354. package/src/node.ts +1 -0
  355. package/src/python/generated.ts +1 -1
  356. package/src/scaffolds/generated.ts +1 -1
  357. package/src/selector.ts +74 -4
  358. package/src/server.ts +2 -2
  359. package/src/servers/mcp.ts +6 -6
  360. package/src/web/features/helpers.ts +1 -1
  361. package/src/web/features/network.ts +1 -0
  362. package/test/assistant.test.ts +72 -0
  363. package/test/conversation.test.ts +220 -0
  364. package/test/vm-loadmodule.test.ts +213 -0
  365. package/tsconfig.build.json +12 -0
  366. package/tsconfig.json +1 -1
  367. package/scripts/examples/telegram-ink-ui.ts +0 -302
  368. package/scripts/examples/using-openai-codex.ts +0 -23
  369. package/scripts/examples/vm-loading-esm-modules.ts +0 -16
@@ -0,0 +1,124 @@
1
+ ---
2
+ title: "Entity"
3
+ tags: [entity, state, events, tools, core]
4
+ lastTested: null
5
+ lastTestPassed: null
6
+ ---
7
+
8
+ # entity
9
+
10
+ Lightweight, composable objects with observable state, a typed event bus, and an optional tool interface.
11
+
12
+ ## Overview
13
+
14
+ An entity is a plain object — not a class — created via `container.entity(id, options?)`. Same id + options always returns the same underlying state and bus instance. Entities are designed to be extended with methods and getters via `.extend()`, and can expose those methods as AI tools via `.expose()`.
15
+
16
+ ## Basic Entity with Observable State
17
+
18
+ Create an entity and read/write state through the observable `state` property.
19
+
20
+ ```ts
21
+ const counter = container.entity<{ count: number }>('counter')
22
+ counter.setState({ count: 0 })
23
+
24
+ counter.state.observe((next) => {
25
+ console.log('count changed to', next.count)
26
+ })
27
+
28
+ counter.setState(s => ({ count: s.count + 1 }))
29
+ counter.setState(s => ({ count: s.count + 1 }))
30
+ console.log('final count:', counter.state.get('count'))
31
+ ```
32
+
33
+ `setState` accepts either a partial object or a function that receives the current state. Observers fire synchronously after each change.
34
+
35
+ ## Typed Event Bus
36
+
37
+ Every entity has a built-in event bus. Declare the event map as the third type parameter.
38
+
39
+ ```ts
40
+ type TimerEvents = {
41
+ tick: [elapsed: number]
42
+ done: []
43
+ }
44
+
45
+ const timer = container.entity<{}, {}, TimerEvents>('timer')
46
+
47
+ timer.on('tick', (elapsed) => {
48
+ console.log('tick at', elapsed, 'ms')
49
+ })
50
+
51
+ timer.once('done', () => {
52
+ console.log('timer finished')
53
+ })
54
+
55
+ timer.emit('tick', 100)
56
+ timer.emit('tick', 200)
57
+ timer.emit('done')
58
+ ```
59
+
60
+ `once` auto-detaches after the first fire. `waitFor` returns a promise that resolves on the next emit of that event.
61
+
62
+ ## Extending with Methods
63
+
64
+ Use `.extend()` to graft methods and getters onto an entity. All base properties — `state`, `options`, `container`, and the event methods — are available via `this`.
65
+
66
+ ```ts
67
+ const session = container.entity('session', { userId: '42' })
68
+ .extend({
69
+ greet() {
70
+ return `Hello user ${this.options.userId}`
71
+ },
72
+ get label() {
73
+ return `Session ${this.id} (user ${this.options.userId})`
74
+ },
75
+ bump() {
76
+ const visits = (this.state.get('visits') ?? 0) + 1
77
+ this.setState({ visits })
78
+ this.emit('visited', visits)
79
+ return visits
80
+ },
81
+ })
82
+
83
+ console.log(session.greet())
84
+ console.log(session.label)
85
+ console.log('visits:', session.bump())
86
+ console.log('visits:', session.bump())
87
+ ```
88
+
89
+ Extensions are chained via prototype delegation — each layer can see everything below it.
90
+
91
+ ## Exposing Methods as AI Tools
92
+
93
+ Use `.expose(methodName, zodSchema)` to register methods as tools. `.toTools()` returns `{ schemas, handlers }` compatible with `assistant.addTools()`.
94
+
95
+ ```ts
96
+ const search = container.entity('search', {})
97
+ .extend({
98
+ async lookup({ query }: { query: string }) {
99
+ return `Results for: ${query}`
100
+ },
101
+ async summarize({ text, maxWords }: { text: string; maxWords: number }) {
102
+ return text.split(' ').slice(0, maxWords).join(' ')
103
+ },
104
+ })
105
+ .expose('lookup', z.object({
106
+ query: z.string().describe('The search query'),
107
+ }))
108
+ .expose('summarize', z.object({
109
+ text: z.string().describe('Text to summarize'),
110
+ maxWords: z.number().describe('Maximum words in summary'),
111
+ }))
112
+
113
+ const { schemas, handlers } = search.toTools()
114
+ console.log('registered tools:', Object.keys(schemas))
115
+
116
+ // Pass directly to an assistant
117
+ // assistant.addTools(search)
118
+ ```
119
+
120
+ `.expose()` is chainable and returns `this`, so you can stack as many as you need.
121
+
122
+ ## Summary
123
+
124
+ Entities give you observable state, a typed event bus, and prototype-safe method extension — all as a plain object with no class overhead. The `.expose()` / `.toTools()` interface makes it straightforward to surface entity methods as AI tools.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soederpop/luca",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "website": "https://luca.soederpop.com",
5
5
  "description": "lightweight universal conversational architecture AKA Le Ultimate Component Architecture AKA Last Universal Common Ancestor, part AI part Human",
6
6
  "author": "jon soeder aka the people's champ <jon@soederpop.com>",
@@ -19,26 +19,77 @@
19
19
  "coding assistant"
20
20
  ],
21
21
  "main": "./src/node.ts",
22
- "types": "./src/node.ts",
22
+ "types": "./dist/node.d.ts",
23
23
  "browser": "./src/browser.ts",
24
24
  "exports": {
25
- ".": "./src/node.ts",
26
- "./node": "./src/node.ts",
27
- "./node/*": "./src/node/*",
28
- "./web": "./src/browser.ts",
29
- "./web/*": "./src/web/*",
30
- "./agi": "./src/agi/index.ts",
31
- "./agi/*": "./src/agi/*",
32
- "./schemas": "./src/schemas/base.ts",
33
- "./schemas/*": "./src/schemas/*",
34
- "./container": "./src/container.ts",
35
- "./client": "./src/client.ts",
36
- "./clients/*": "./src/clients/*",
37
- "./server": "./src/server.ts",
38
- "./servers/*": "./src/servers/*",
39
- "./feature": "./src/feature.ts",
40
- "./react": "./src/react/index.ts",
41
- "./introspection": "./src/introspection/index.ts"
25
+ ".": {
26
+ "types": "./dist/node.d.ts",
27
+ "default": "./src/node.ts"
28
+ },
29
+ "./node": {
30
+ "types": "./dist/node.d.ts",
31
+ "default": "./src/node.ts"
32
+ },
33
+ "./node/*": {
34
+ "types": "./dist/node/*.d.ts",
35
+ "default": "./src/node/*"
36
+ },
37
+ "./web": {
38
+ "types": "./dist/browser.d.ts",
39
+ "default": "./src/browser.ts"
40
+ },
41
+ "./web/*": {
42
+ "types": "./dist/web/*.d.ts",
43
+ "default": "./src/web/*"
44
+ },
45
+ "./agi": {
46
+ "types": "./dist/agi/index.d.ts",
47
+ "default": "./src/agi/index.ts"
48
+ },
49
+ "./agi/*": {
50
+ "types": "./dist/agi/*.d.ts",
51
+ "default": "./src/agi/*"
52
+ },
53
+ "./schemas": {
54
+ "types": "./dist/schemas/base.d.ts",
55
+ "default": "./src/schemas/base.ts"
56
+ },
57
+ "./schemas/*": {
58
+ "types": "./dist/schemas/*.d.ts",
59
+ "default": "./src/schemas/*"
60
+ },
61
+ "./container": {
62
+ "types": "./dist/container.d.ts",
63
+ "default": "./src/container.ts"
64
+ },
65
+ "./client": {
66
+ "types": "./dist/client.d.ts",
67
+ "default": "./src/client.ts"
68
+ },
69
+ "./clients/*": {
70
+ "types": "./dist/clients/*.d.ts",
71
+ "default": "./src/clients/*"
72
+ },
73
+ "./server": {
74
+ "types": "./dist/server.d.ts",
75
+ "default": "./src/server.ts"
76
+ },
77
+ "./servers/*": {
78
+ "types": "./dist/servers/*.d.ts",
79
+ "default": "./src/servers/*"
80
+ },
81
+ "./feature": {
82
+ "types": "./dist/feature.d.ts",
83
+ "default": "./src/feature.ts"
84
+ },
85
+ "./react": {
86
+ "types": "./dist/react/index.d.ts",
87
+ "default": "./src/react/index.ts"
88
+ },
89
+ "./introspection": {
90
+ "types": "./dist/introspection/index.d.ts",
91
+ "default": "./src/introspection/index.ts"
92
+ }
42
93
  },
43
94
  "bin": {
44
95
  "luca": "./src/cli/cli.ts"
@@ -51,13 +102,14 @@
51
102
  "build:web": "bun run scripts/build-web.ts",
52
103
  "build:introspection": "bun run src/cli/cli.ts introspect",
53
104
  "compile": "bun run build:introspection && bun run build:scaffolds && bun run build:bootstrap && bun run build:python-bridge && bash scripts/stamp-build.sh && bun build ./src/cli/cli.ts --compile --outfile dist/luca --external node-llama-cpp",
105
+ "build:types": "tsc -p tsconfig.build.json",
54
106
  "typecheck": "tsc -p tsconfig.json --noEmit",
55
107
  "build:scaffolds": "bun run src/cli/cli.ts build-scaffolds",
56
108
  "build:bootstrap": "bun run src/cli/cli.ts build-bootstrap",
57
109
  "build:python-bridge": "bun run src/cli/cli.ts build-python-bridge",
58
110
  "test": "bun test test/*.test.ts",
59
111
  "update-all-docs": "bun run test && bun run build:introspection && bun run src/cli/cli.ts generate-api-docs && bun run build:scaffolds && bun run build:bootstrap && bun run build:python-bridge",
60
- "precommit": "bun run update-all-docs && git add docs/apis/ src/introspection/generated.*.ts src/scaffolds/generated.ts src/bootstrap/generated.ts src/python/generated.ts && bun compile",
112
+ "precommit": "bun run typecheck && bun run update-all-docs && git add docs/apis/ src/introspection/generated.*.ts src/scaffolds/generated.ts src/bootstrap/generated.ts src/python/generated.ts && bun compile",
61
113
  "test:integration": "bun test ./test-integration/"
62
114
  },
63
115
  "devDependencies": {
@@ -97,7 +149,7 @@
97
149
  "@supabase/supabase-js": "^2.95.3",
98
150
  "@types/marked": "^6.0.0",
99
151
  "@types/marked-terminal": "^6.1.1",
100
- "axios": "^1.3.5",
152
+ "axios": "1.13.6",
101
153
  "cacache": "^17.0.7",
102
154
  "chalk": "^5.2.0",
103
155
  "child-process-promise": "^2.2.1",
@@ -0,0 +1,13 @@
1
+ import container from '@soederpop/luca/agi'
2
+
3
+ const coder = container.feature('assistant', { folder: 'assistants/codingAssistant' })
4
+
5
+ await coder.start()
6
+ console.log("Available Tools", coder.availableTools)
7
+
8
+ if (coder.state.get('startedHookRan')) {
9
+ console.log('Started Hook Ran')
10
+ }
11
+
12
+
13
+
@@ -0,0 +1,13 @@
1
+ import type { AGIFeatures, AGIContainer } from './container.server.js'
2
+ import type { FeatureOptions, FeatureState } from '../feature.ts'
3
+ import { features, Feature as NodeFeature } from '../node/feature.ts'
4
+
5
+ export { features }
6
+
7
+ export type { FeatureState, FeatureOptions }
8
+
9
+ export class Feature<T extends FeatureState = FeatureState, K extends FeatureOptions = FeatureOptions> extends NodeFeature<T, K> {
10
+ override get container(): AGIContainer<AGIFeatures> {
11
+ return super.container as AGIContainer<AGIFeatures>
12
+ }
13
+ }
@@ -1,9 +1,8 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
3
  import { type AvailableFeatures } from '@soederpop/luca/feature'
4
- import { Feature } from '@soederpop/luca/feature'
4
+ import { Feature } from '../feature.js'
5
5
  import type { Conversation, ConversationTool, ContentPart, AskOptions, Message } from './conversation'
6
- import type { AGIContainer } from '../container.server.js'
7
6
  import type { ContentDb } from '@soederpop/luca/node'
8
7
  import type { ConversationHistory, ConversationMeta } from './conversation-history'
9
8
  import hashObject from '../../hash-object.js'
@@ -152,6 +151,10 @@ export class Assistant extends Feature<AssistantState, AssistantOptions> {
152
151
  * @returns this, for chaining
153
152
  */
154
153
  intercept<K extends InterceptorPoint>(point: K, fn: InterceptorFn<InterceptorPoints[K]>): this {
154
+ if (!(point in this.interceptors)) {
155
+ const available = Object.keys(this.interceptors).join(', ')
156
+ throw new Error(`Unknown intercept point "${point}". Available points: ${available}`)
157
+ }
155
158
  this.interceptors[point].add(fn as any)
156
159
  return this
157
160
  }
@@ -176,12 +179,9 @@ export class Assistant extends Feature<AssistantState, AssistantOptions> {
176
179
  }
177
180
  }
178
181
 
179
- override get container(): AGIContainer {
180
- return super.container as AGIContainer
181
- }
182
182
 
183
183
  get name() {
184
- return this.resolvedFolder.split('/').pop()
184
+ return this.options.name || this.resolvedFolder.split('/').pop()
185
185
  }
186
186
 
187
187
  /** The absolute resolved path to the assistant folder. */
@@ -213,11 +213,11 @@ export class Assistant extends Feature<AssistantState, AssistantOptions> {
213
213
  get voiceConfig(): Record<string, any> | undefined {
214
214
  if (!this.hasVoice) return undefined
215
215
  const yaml = this.container.feature('yaml')
216
- return yaml.parse(this.container.fs.readFile(this.paths.resolve('voice.yaml')))
216
+ return yaml.parse(String(this.container.fs.readFile(this.paths.resolve('voice.yaml'))))
217
217
  }
218
218
 
219
219
  get resolvedDocsFolder() {
220
- const { docsFolder = this.options.docsFolder || 'docs' } = this.state.current
220
+ const { docsFolder = this.effectiveOptions.docsFolder || 'docs' } = this.state.current
221
221
 
222
222
  if (this.container.fs.exists(docsFolder)) {
223
223
  return this.container.paths.resolve(docsFolder)
@@ -268,17 +268,17 @@ export class Assistant extends Feature<AssistantState, AssistantOptions> {
268
268
  let conv = this.state.get('conversation') as Conversation | null
269
269
  if (!conv) {
270
270
  conv = this.container.feature('conversation', {
271
- model: this.options.model || 'gpt-5.4',
272
- local: !!this.options.local,
271
+ model: this.effectiveOptions.model || 'gpt-5.4',
272
+ local: !!this.effectiveOptions.local,
273
273
  tools: this.tools,
274
274
  api: 'chat',
275
- ...(this.options.maxTokens ? { maxTokens: this.options.maxTokens } : {}),
276
- ...(this.options.temperature != null ? { temperature: this.options.temperature } : {}),
277
- ...(this.options.topP != null ? { topP: this.options.topP } : {}),
278
- ...(this.options.topK != null ? { topK: this.options.topK } : {}),
279
- ...(this.options.frequencyPenalty != null ? { frequencyPenalty: this.options.frequencyPenalty } : {}),
280
- ...(this.options.presencePenalty != null ? { presencePenalty: this.options.presencePenalty } : {}),
281
- ...(this.options.stop ? { stop: this.options.stop } : {}),
275
+ ...(this.effectiveOptions.maxTokens ? { maxTokens: this.effectiveOptions.maxTokens } : {}),
276
+ ...(this.effectiveOptions.temperature != null ? { temperature: this.effectiveOptions.temperature } : {}),
277
+ ...(this.effectiveOptions.topP != null ? { topP: this.effectiveOptions.topP } : {}),
278
+ ...(this.effectiveOptions.topK != null ? { topK: this.effectiveOptions.topK } : {}),
279
+ ...(this.effectiveOptions.frequencyPenalty != null ? { frequencyPenalty: this.effectiveOptions.frequencyPenalty } : {}),
280
+ ...(this.effectiveOptions.presencePenalty != null ? { presencePenalty: this.effectiveOptions.presencePenalty } : {}),
281
+ ...(this.effectiveOptions.stop ? { stop: this.effectiveOptions.stop } : {}),
282
282
  history: [
283
283
  { role: 'system', content: this.effectiveSystemPrompt },
284
284
  ],
@@ -372,7 +372,7 @@ export class Assistant extends Feature<AssistantState, AssistantOptions> {
372
372
  * allowTools is applied first (strict allowlist), then forbidTools removes from whatever remains.
373
373
  */
374
374
  private applyToolFilters(tools: Record<string, ConversationTool>): Record<string, ConversationTool> {
375
- const { allowTools, forbidTools, toolNames } = this.options
375
+ const { allowTools, forbidTools, toolNames } = this.effectiveOptions
376
376
  if (!allowTools && !forbidTools && !toolNames) return tools
377
377
 
378
378
  let names = Object.keys(tools)
@@ -395,7 +395,8 @@ export class Assistant extends Feature<AssistantState, AssistantOptions> {
395
395
 
396
396
  const result: Record<string, ConversationTool> = {}
397
397
  for (const n of names) {
398
- result[n] = tools[n]
398
+ const tool = tools[n]
399
+ if (tool) result[n] = tool
399
400
  }
400
401
  return result
401
402
  }
@@ -607,6 +608,15 @@ export class Assistant extends Feature<AssistantState, AssistantOptions> {
607
608
  return (this.state.get('meta') || {}) as Record<string, any>
608
609
  }
609
610
 
611
+ /**
612
+ * Merged options where CORE.md frontmatter provides defaults and
613
+ * constructor options take precedence. Prefer this over `this.options`
614
+ * anywhere model parameters or runtime config is consumed.
615
+ */
616
+ get effectiveOptions(): AssistantOptions & Record<string, any> {
617
+ return { ...this.meta, ...this.options }
618
+ }
619
+
610
620
  /**
611
621
  * Load the system prompt from CORE.md, applying any prepend/append options.
612
622
  * YAML frontmatter (between --- fences) is stripped from the prompt and
@@ -831,8 +841,9 @@ export class Assistant extends Feature<AssistantState, AssistantOptions> {
831
841
  return `${stamp} ${content}`
832
842
  }
833
843
 
834
- if (content.length > 0 && content[0].type === 'text') {
835
- return [{ type: 'text' as const, text: `${stamp} ${content[0].text}` }, ...content.slice(1)]
844
+ const firstPart = content[0]
845
+ if (firstPart && firstPart.type === 'text') {
846
+ return [{ type: 'text' as const, text: `${stamp} ${firstPart.text}` }, ...content.slice(1)]
836
847
  }
837
848
 
838
849
  return [{ type: 'text' as const, text: stamp }, ...content]
@@ -919,7 +930,7 @@ export class Assistant extends Feature<AssistantState, AssistantOptions> {
919
930
  * Called from start() for non-lifecycle modes.
920
931
  */
921
932
  private async loadConversationHistory(): Promise<void> {
922
- const mode = this.options.historyMode || 'lifecycle'
933
+ const mode = this.effectiveOptions.historyMode || 'lifecycle'
923
934
  if (mode === 'lifecycle') return
924
935
 
925
936
  const threadId = (this.state.get('resumeThreadId') as string | undefined) || this.buildThreadId(mode)
@@ -1104,7 +1115,7 @@ export class Assistant extends Feature<AssistantState, AssistantOptions> {
1104
1115
  await this.loadConversationHistory()
1105
1116
 
1106
1117
  // Enable autoCompact for modes that accumulate history
1107
- const mode = this.options.historyMode || 'lifecycle'
1118
+ const mode = this.effectiveOptions.historyMode || 'lifecycle'
1108
1119
  if (mode === 'daily' || mode === 'persistent') {
1109
1120
  (this.conversation.options as any).autoCompact = true
1110
1121
  }
@@ -1146,7 +1157,7 @@ export class Assistant extends Feature<AssistantState, AssistantOptions> {
1146
1157
  const count = (this.state.get('conversationCount') || 0) + 1
1147
1158
  this.state.set('conversationCount', count)
1148
1159
 
1149
- if (this.options.injectTimestamps) {
1160
+ if (this.effectiveOptions.injectTimestamps) {
1150
1161
  question = this.prependTimestamp(question)
1151
1162
  }
1152
1163
 
@@ -1169,7 +1180,7 @@ export class Assistant extends Feature<AssistantState, AssistantOptions> {
1169
1180
  }
1170
1181
 
1171
1182
  // Auto-save for non-lifecycle modes
1172
- if (this.options.historyMode !== 'lifecycle' && this.state.get('threadId')) {
1183
+ if (this.effectiveOptions.historyMode !== 'lifecycle' && this.state.get('threadId')) {
1173
1184
  await this.conversation.save({ thread: this.state.get('threadId') })
1174
1185
  }
1175
1186
 
@@ -1,9 +1,9 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
3
  import { type AvailableFeatures } from '@soederpop/luca/feature'
4
- import { Feature } from '@soederpop/luca/feature'
5
- import type { AGIContainer } from '../container.server.js'
4
+ import { Feature } from '../feature.js'
6
5
  import type { Assistant } from './assistant.js'
6
+ import type { InterceptorFn, InterceptorPoint, InterceptorPoints } from '../lib/interceptor-chain.js'
7
7
 
8
8
  declare module '@soederpop/luca/feature' {
9
9
  interface AvailableFeatures {
@@ -47,6 +47,7 @@ export const AssistantsManagerStateSchema = FeatureStateSchema.extend({
47
47
  entries: z.record(z.string(), z.any()).describe('Discovered assistant entries keyed by name'),
48
48
  instances: z.record(z.string(), z.any()).describe('Active assistant instances keyed by name'),
49
49
  factories: z.record(z.string(), z.any()).describe('Registered factory functions keyed by name'),
50
+ extraFolders: z.array(z.string()).describe('Additional folders to scan during discovery'),
50
51
  })
51
52
 
52
53
  export const AssistantsManagerOptionsSchema = FeatureOptionsSchema.extend({})
@@ -91,12 +92,10 @@ export class AssistantsManager extends Feature<AssistantsManagerState, Assistant
91
92
  entries: {},
92
93
  instances: {},
93
94
  factories: {},
95
+ extraFolders: [],
94
96
  }
95
97
  }
96
98
 
97
- override get container(): AGIContainer {
98
- return super.container as AGIContainer
99
- }
100
99
 
101
100
  /** Discovered assistant entries keyed by name. */
102
101
  get entries(): Record<string, AssistantEntry> {
@@ -113,12 +112,60 @@ export class AssistantsManager extends Feature<AssistantsManagerState, Assistant
113
112
  return (this.state.get('factories') || {}) as Record<string, (options: Record<string, any>) => Assistant>
114
113
  }
115
114
 
115
+ /** Interceptor registrations to be applied to every assistant this manager creates. */
116
+ private _interceptors: Array<{ point: InterceptorPoint; fn: InterceptorFn<any> }> = []
117
+
118
+ /**
119
+ * Registers a pipeline interceptor that is applied to every assistant created by this manager.
120
+ * Interceptors are applied at the given interception point on each assistant at creation time.
121
+ * This mirrors the per-assistant `assistant.intercept(point, fn)` API, but scopes it globally
122
+ * across all assistants managed here — useful for cross-cutting concerns like logging, tracing,
123
+ * or policy enforcement.
124
+ *
125
+ * @param {InterceptorPoint} point - The interception point (beforeAsk, beforeTurn, beforeToolCall, afterToolCall, beforeResponse)
126
+ * @param {InterceptorFn<InterceptorPoints[K]>} fn - Middleware function receiving (ctx, next)
127
+ * @returns {this} This instance, for chaining
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * manager.intercept('beforeAsk', async (ctx, next) => {
132
+ * console.log(`[${ctx.assistant.name}] asking: ${ctx.message}`)
133
+ * await next()
134
+ * })
135
+ * ```
136
+ */
137
+ intercept<K extends InterceptorPoint>(point: K, fn: InterceptorFn<InterceptorPoints[K]>): this {
138
+ this._interceptors.push({ point, fn })
139
+ return this
140
+ }
141
+
116
142
  /**
117
143
  * Discovers assistants by listing subdirectories in ~/.luca/assistants/
118
144
  * and cwd/assistants/. Each subdirectory containing a CORE.md is an assistant.
119
145
  *
120
146
  * @returns {Promise<this>} This instance, for chaining
121
147
  */
148
+ /**
149
+ * Registers an additional folder to scan during assistant discovery and
150
+ * immediately triggers a new discovery pass.
151
+ *
152
+ * @param {string} folderPath - Absolute path to a folder containing assistant subdirectories
153
+ * @returns {Promise<this>} This instance, for chaining
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * await manager.addDiscoveryFolder('/path/to/more/assistants')
158
+ * console.log(manager.available) // includes assistants from the new folder
159
+ * ```
160
+ */
161
+ async addDiscoveryFolder(folderPath: string): Promise<this> {
162
+ const current = this.state.get('extraFolders') as string[]
163
+ if (!current.includes(folderPath)) {
164
+ this.state.set('extraFolders', [...current, folderPath])
165
+ }
166
+ return this.discover()
167
+ }
168
+
122
169
  async discover(): Promise<this> {
123
170
  const { fs, paths, os } = this.container
124
171
 
@@ -127,6 +174,7 @@ export class AssistantsManager extends Feature<AssistantsManagerState, Assistant
127
174
  const locations = [
128
175
  `${os.homedir}/.luca/assistants`,
129
176
  paths.resolve('assistants'),
177
+ ...(this.state.get('extraFolders') as string[]),
130
178
  ]
131
179
 
132
180
  for (const location of locations) {
@@ -262,6 +310,7 @@ export class AssistantsManager extends Feature<AssistantsManagerState, Assistant
262
310
  const factory = this.factories[name]
263
311
  if (factory) {
264
312
  const instance = factory(options)
313
+ this._bindAssistant(instance)
265
314
  const updated = { ...this.instances, [name]: instance }
266
315
  this.state.setState({ instances: updated, activeCount: Object.keys(updated).length })
267
316
  this.emit('assistantCreated', name, instance)
@@ -281,6 +330,7 @@ export class AssistantsManager extends Feature<AssistantsManagerState, Assistant
281
330
  ...options,
282
331
  })
283
332
 
333
+ this._bindAssistant(instance)
284
334
  const updated = { ...this.instances, [name]: instance }
285
335
  this.state.setState({ instances: updated, activeCount: Object.keys(updated).length })
286
336
  this.emit('assistantCreated', name, instance)
@@ -288,6 +338,21 @@ export class AssistantsManager extends Feature<AssistantsManagerState, Assistant
288
338
  return instance
289
339
  }
290
340
 
341
+ /**
342
+ * Wires an assistant into the manager: bridges all assistant events up to the manager
343
+ * as `assistantEvent:<eventName>` with (assistant, ...originalArgs), and applies any
344
+ * globally registered interceptors.
345
+ */
346
+ private _bindAssistant(instance: Assistant): void {
347
+ instance.on('*', (event: string, ...args: any[]) => {
348
+ this.emit(`assistantEvent:${event}` as any, instance, ...args)
349
+ })
350
+
351
+ for (const { point, fn } of this._interceptors) {
352
+ instance.intercept(point, fn)
353
+ }
354
+ }
355
+
291
356
  /**
292
357
  * Returns a previously created assistant instance by name.
293
358
  *
@@ -1,7 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { Feature } from '@soederpop/luca/feature'
4
- import type { AGIContainer } from '../container.server.js'
3
+ import { Feature } from '../feature.js'
5
4
  import type { Assistant } from './assistant.js'
6
5
  import type { ToolCallCtx } from '../lib/interceptor-chain.js'
7
6
 
@@ -158,9 +157,6 @@ export class AutonomousAssistant extends Feature<AutonomousAssistantState, Auton
158
157
  }
159
158
  }
160
159
 
161
- override get container(): AGIContainer {
162
- return super.container as AGIContainer
163
- }
164
160
 
165
161
  /** The inner assistant. Throws if not started. */
166
162
  get assistant(): Assistant {
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { Feature } from '@soederpop/luca/feature'
3
+ import { Feature } from '../feature.js'
4
4
 
5
5
  declare module '@soederpop/luca/feature' {
6
6
  interface AvailableFeatures {
@@ -61,7 +61,7 @@ export class BrowserUse extends Feature<BrowserUseState, BrowserUseOptions> {
61
61
  static override optionsSchema = BrowserUseOptionsSchema
62
62
  static override eventsSchema = BrowserUseEventsSchema
63
63
 
64
- static tools = {
64
+ static override tools = {
65
65
  browserOpen: {
66
66
  description: 'Navigate the browser to a URL. Call this first to open a page before any interaction.',
67
67
  schema: z.object({