luca 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (763) hide show
  1. package/.github/workflows/release.yaml +169 -0
  2. package/AGENTS.md +99 -0
  3. package/CLAUDE.md +115 -0
  4. package/CNAME +1 -0
  5. package/README.md +257 -9
  6. package/RUNME.md +56 -0
  7. package/assistants/codingAssistant/ABOUT.md +5 -0
  8. package/assistants/codingAssistant/CORE.md +28 -0
  9. package/assistants/codingAssistant/hooks.ts +21 -0
  10. package/assistants/codingAssistant/tools.ts +12 -0
  11. package/assistants/inkbot/ABOUT.md +16 -0
  12. package/assistants/inkbot/CORE.md +330 -0
  13. package/assistants/inkbot/hooks.ts +6 -0
  14. package/assistants/inkbot/tools.ts +53 -0
  15. package/assistants/researcher/ABOUT.md +5 -0
  16. package/assistants/researcher/CORE.md +46 -0
  17. package/assistants/researcher/hooks.ts +16 -0
  18. package/assistants/researcher/tools.ts +237 -0
  19. package/bun.lock +2769 -0
  20. package/bunfig.toml +3 -0
  21. package/commands/audit-docs.ts +740 -0
  22. package/commands/build-bootstrap.ts +118 -0
  23. package/commands/build-python-bridge.ts +43 -0
  24. package/commands/build-scaffolds.ts +176 -0
  25. package/commands/generate-api-docs.ts +114 -0
  26. package/commands/inkbot.ts +874 -0
  27. package/commands/release.ts +80 -0
  28. package/commands/try-all-challenges.ts +543 -0
  29. package/commands/try-challenge.ts +100 -0
  30. package/dist/agi/container.server.d.ts +63 -0
  31. package/dist/agi/container.server.d.ts.map +1 -0
  32. package/dist/agi/endpoints/ask.d.ts +20 -0
  33. package/dist/agi/endpoints/ask.d.ts.map +1 -0
  34. package/dist/agi/endpoints/conversations/[id].d.ts +27 -0
  35. package/dist/agi/endpoints/conversations/[id].d.ts.map +1 -0
  36. package/dist/agi/endpoints/conversations.d.ts +18 -0
  37. package/dist/agi/endpoints/conversations.d.ts.map +1 -0
  38. package/dist/agi/endpoints/experts.d.ts +8 -0
  39. package/dist/agi/endpoints/experts.d.ts.map +1 -0
  40. package/dist/agi/feature.d.ts +9 -0
  41. package/dist/agi/feature.d.ts.map +1 -0
  42. package/dist/agi/features/assistant.d.ts +509 -0
  43. package/dist/agi/features/assistant.d.ts.map +1 -0
  44. package/dist/agi/features/assistants-manager.d.ts +236 -0
  45. package/dist/agi/features/assistants-manager.d.ts.map +1 -0
  46. package/dist/agi/features/autonomous-assistant.d.ts +281 -0
  47. package/dist/agi/features/autonomous-assistant.d.ts.map +1 -0
  48. package/dist/agi/features/browser-use.d.ts +479 -0
  49. package/dist/agi/features/browser-use.d.ts.map +1 -0
  50. package/dist/agi/features/claude-code.d.ts +824 -0
  51. package/dist/agi/features/claude-code.d.ts.map +1 -0
  52. package/dist/agi/features/conversation-history.d.ts +245 -0
  53. package/dist/agi/features/conversation-history.d.ts.map +1 -0
  54. package/dist/agi/features/conversation.d.ts +464 -0
  55. package/dist/agi/features/conversation.d.ts.map +1 -0
  56. package/dist/agi/features/docs-reader.d.ts +72 -0
  57. package/dist/agi/features/docs-reader.d.ts.map +1 -0
  58. package/dist/agi/features/file-tools.d.ts +110 -0
  59. package/dist/agi/features/file-tools.d.ts.map +1 -0
  60. package/dist/agi/features/luca-coder.d.ts +323 -0
  61. package/dist/agi/features/luca-coder.d.ts.map +1 -0
  62. package/dist/agi/features/openai-codex.d.ts +381 -0
  63. package/dist/agi/features/openai-codex.d.ts.map +1 -0
  64. package/dist/agi/features/openapi.d.ts +200 -0
  65. package/dist/agi/features/openapi.d.ts.map +1 -0
  66. package/dist/agi/features/skills-library.d.ts +167 -0
  67. package/dist/agi/features/skills-library.d.ts.map +1 -0
  68. package/dist/agi/index.d.ts +5 -0
  69. package/dist/agi/index.d.ts.map +1 -0
  70. package/dist/agi/lib/interceptor-chain.d.ts +44 -0
  71. package/dist/agi/lib/interceptor-chain.d.ts.map +1 -0
  72. package/dist/agi/lib/token-counter.d.ts +13 -0
  73. package/dist/agi/lib/token-counter.d.ts.map +1 -0
  74. package/dist/bootstrap/generated.d.ts +5 -0
  75. package/dist/bootstrap/generated.d.ts.map +1 -0
  76. package/dist/browser.d.ts +12 -0
  77. package/dist/browser.d.ts.map +1 -0
  78. package/dist/bus.d.ts +29 -0
  79. package/dist/bus.d.ts.map +1 -0
  80. package/dist/cli/build-info.d.ts +4 -0
  81. package/dist/cli/build-info.d.ts.map +1 -0
  82. package/dist/cli/cli.d.ts +3 -12
  83. package/dist/cli/cli.d.ts.map +1 -0
  84. package/dist/client.d.ts +60 -0
  85. package/dist/client.d.ts.map +1 -0
  86. package/dist/clients/civitai/index.d.ts +472 -0
  87. package/dist/clients/civitai/index.d.ts.map +1 -0
  88. package/dist/clients/client-template.d.ts +30 -0
  89. package/dist/clients/client-template.d.ts.map +1 -0
  90. package/dist/clients/comfyui/index.d.ts +281 -0
  91. package/dist/clients/comfyui/index.d.ts.map +1 -0
  92. package/dist/clients/elevenlabs/index.d.ts +197 -0
  93. package/dist/clients/elevenlabs/index.d.ts.map +1 -0
  94. package/dist/clients/graph.d.ts +64 -0
  95. package/dist/clients/graph.d.ts.map +1 -0
  96. package/dist/clients/openai/index.d.ts +247 -0
  97. package/dist/clients/openai/index.d.ts.map +1 -0
  98. package/dist/clients/rest.d.ts +92 -0
  99. package/dist/clients/rest.d.ts.map +1 -0
  100. package/dist/clients/supabase/index.d.ts +176 -0
  101. package/dist/clients/supabase/index.d.ts.map +1 -0
  102. package/dist/clients/websocket.d.ts +127 -0
  103. package/dist/clients/websocket.d.ts.map +1 -0
  104. package/dist/command.d.ts +163 -0
  105. package/dist/command.d.ts.map +1 -0
  106. package/dist/commands/bootstrap.d.ts +20 -0
  107. package/dist/commands/bootstrap.d.ts.map +1 -0
  108. package/dist/commands/chat.d.ts +37 -0
  109. package/dist/commands/chat.d.ts.map +1 -0
  110. package/dist/commands/code.d.ts +28 -0
  111. package/dist/commands/code.d.ts.map +1 -0
  112. package/dist/commands/console.d.ts +22 -0
  113. package/dist/commands/console.d.ts.map +1 -0
  114. package/dist/commands/describe.d.ts +50 -0
  115. package/dist/commands/describe.d.ts.map +1 -0
  116. package/dist/commands/eval.d.ts +23 -0
  117. package/dist/commands/eval.d.ts.map +1 -0
  118. package/dist/commands/help.d.ts +25 -0
  119. package/dist/commands/help.d.ts.map +1 -0
  120. package/dist/commands/index.d.ts +18 -0
  121. package/dist/commands/index.d.ts.map +1 -0
  122. package/dist/commands/introspect.d.ts +24 -0
  123. package/dist/commands/introspect.d.ts.map +1 -0
  124. package/dist/commands/mcp.d.ts +35 -0
  125. package/dist/commands/mcp.d.ts.map +1 -0
  126. package/dist/commands/prompt.d.ts +38 -0
  127. package/dist/commands/prompt.d.ts.map +1 -0
  128. package/dist/commands/run.d.ts +24 -0
  129. package/dist/commands/run.d.ts.map +1 -0
  130. package/dist/commands/sandbox-mcp.d.ts +34 -0
  131. package/dist/commands/sandbox-mcp.d.ts.map +1 -0
  132. package/dist/commands/save-api-docs.d.ts +21 -0
  133. package/dist/commands/save-api-docs.d.ts.map +1 -0
  134. package/dist/commands/scaffold.d.ts +24 -0
  135. package/dist/commands/scaffold.d.ts.map +1 -0
  136. package/dist/commands/select.d.ts +22 -0
  137. package/dist/commands/select.d.ts.map +1 -0
  138. package/dist/commands/serve.d.ts +29 -0
  139. package/dist/commands/serve.d.ts.map +1 -0
  140. package/dist/container-describer.d.ts +144 -0
  141. package/dist/container-describer.d.ts.map +1 -0
  142. package/dist/container.d.ts +451 -0
  143. package/dist/container.d.ts.map +1 -0
  144. package/dist/endpoint.d.ts +113 -0
  145. package/dist/endpoint.d.ts.map +1 -0
  146. package/dist/feature.d.ts +47 -0
  147. package/dist/feature.d.ts.map +1 -0
  148. package/dist/graft.d.ts +29 -0
  149. package/dist/graft.d.ts.map +1 -0
  150. package/dist/hash-object.d.ts +8 -0
  151. package/dist/hash-object.d.ts.map +1 -0
  152. package/dist/helper.d.ts +209 -0
  153. package/dist/helper.d.ts.map +1 -0
  154. package/dist/introspection/generated.node.d.ts +44623 -0
  155. package/dist/introspection/generated.node.d.ts.map +1 -0
  156. package/dist/introspection/generated.web.d.ts +1412 -0
  157. package/dist/introspection/generated.web.d.ts.map +1 -0
  158. package/dist/introspection/index.d.ts +156 -0
  159. package/dist/introspection/index.d.ts.map +1 -0
  160. package/dist/introspection/scan.d.ts +147 -0
  161. package/dist/introspection/scan.d.ts.map +1 -0
  162. package/dist/node/container.d.ts +256 -0
  163. package/dist/node/container.d.ts.map +1 -0
  164. package/dist/node/feature.d.ts +9 -0
  165. package/dist/node/feature.d.ts.map +1 -0
  166. package/dist/node/features/container-link.d.ts +213 -0
  167. package/dist/node/features/container-link.d.ts.map +1 -0
  168. package/dist/node/features/content-db.d.ts +354 -0
  169. package/dist/node/features/content-db.d.ts.map +1 -0
  170. package/dist/node/features/disk-cache.d.ts +236 -0
  171. package/dist/node/features/disk-cache.d.ts.map +1 -0
  172. package/dist/node/features/dns.d.ts +511 -0
  173. package/dist/node/features/dns.d.ts.map +1 -0
  174. package/dist/node/features/docker.d.ts +485 -0
  175. package/dist/node/features/docker.d.ts.map +1 -0
  176. package/dist/node/features/downloader.d.ts +73 -0
  177. package/dist/node/features/downloader.d.ts.map +1 -0
  178. package/dist/node/features/figlet-fonts.d.ts +4 -0
  179. package/dist/node/features/figlet-fonts.d.ts.map +1 -0
  180. package/dist/node/features/file-manager.d.ts +177 -0
  181. package/dist/node/features/file-manager.d.ts.map +1 -0
  182. package/dist/node/features/fs.d.ts +635 -0
  183. package/dist/node/features/fs.d.ts.map +1 -0
  184. package/dist/node/features/git.d.ts +329 -0
  185. package/dist/node/features/git.d.ts.map +1 -0
  186. package/dist/node/features/google-auth.d.ts +200 -0
  187. package/dist/node/features/google-auth.d.ts.map +1 -0
  188. package/dist/node/features/google-calendar.d.ts +194 -0
  189. package/dist/node/features/google-calendar.d.ts.map +1 -0
  190. package/dist/node/features/google-docs.d.ts +138 -0
  191. package/dist/node/features/google-docs.d.ts.map +1 -0
  192. package/dist/node/features/google-drive.d.ts +202 -0
  193. package/dist/node/features/google-drive.d.ts.map +1 -0
  194. package/dist/node/features/google-mail.d.ts +221 -0
  195. package/dist/node/features/google-mail.d.ts.map +1 -0
  196. package/dist/node/features/google-sheets.d.ts +157 -0
  197. package/dist/node/features/google-sheets.d.ts.map +1 -0
  198. package/dist/node/features/grep.d.ts +207 -0
  199. package/dist/node/features/grep.d.ts.map +1 -0
  200. package/dist/node/features/helpers.d.ts +236 -0
  201. package/dist/node/features/helpers.d.ts.map +1 -0
  202. package/dist/node/features/ink.d.ts +332 -0
  203. package/dist/node/features/ink.d.ts.map +1 -0
  204. package/dist/node/features/ipc-socket.d.ts +298 -0
  205. package/dist/node/features/ipc-socket.d.ts.map +1 -0
  206. package/dist/node/features/json-tree.d.ts +140 -0
  207. package/dist/node/features/json-tree.d.ts.map +1 -0
  208. package/dist/node/features/networking.d.ts +373 -0
  209. package/dist/node/features/networking.d.ts.map +1 -0
  210. package/dist/node/features/nlp.d.ts +125 -0
  211. package/dist/node/features/nlp.d.ts.map +1 -0
  212. package/dist/node/features/opener.d.ts +93 -0
  213. package/dist/node/features/opener.d.ts.map +1 -0
  214. package/dist/node/features/os.d.ts +168 -0
  215. package/dist/node/features/os.d.ts.map +1 -0
  216. package/dist/node/features/package-finder.d.ts +419 -0
  217. package/dist/node/features/package-finder.d.ts.map +1 -0
  218. package/dist/node/features/postgres.d.ts +173 -0
  219. package/dist/node/features/postgres.d.ts.map +1 -0
  220. package/dist/node/features/proc.d.ts +285 -0
  221. package/dist/node/features/proc.d.ts.map +1 -0
  222. package/dist/node/features/process-manager.d.ts +427 -0
  223. package/dist/node/features/process-manager.d.ts.map +1 -0
  224. package/dist/node/features/python.d.ts +477 -0
  225. package/dist/node/features/python.d.ts.map +1 -0
  226. package/dist/node/features/redis.d.ts +247 -0
  227. package/dist/node/features/redis.d.ts.map +1 -0
  228. package/dist/node/features/repl.d.ts +84 -0
  229. package/dist/node/features/repl.d.ts.map +1 -0
  230. package/dist/node/features/runpod.d.ts +527 -0
  231. package/dist/node/features/runpod.d.ts.map +1 -0
  232. package/dist/node/features/secure-shell.d.ts +145 -0
  233. package/dist/node/features/secure-shell.d.ts.map +1 -0
  234. package/dist/node/features/semantic-search.d.ts +207 -0
  235. package/dist/node/features/semantic-search.d.ts.map +1 -0
  236. package/dist/node/features/sqlite.d.ts +180 -0
  237. package/dist/node/features/sqlite.d.ts.map +1 -0
  238. package/dist/node/features/telegram.d.ts +173 -0
  239. package/dist/node/features/telegram.d.ts.map +1 -0
  240. package/dist/node/features/transpiler.d.ts +51 -0
  241. package/dist/node/features/transpiler.d.ts.map +1 -0
  242. package/dist/node/features/tts.d.ts +108 -0
  243. package/dist/node/features/tts.d.ts.map +1 -0
  244. package/dist/node/features/ui.d.ts +562 -0
  245. package/dist/node/features/ui.d.ts.map +1 -0
  246. package/dist/node/features/vault.d.ts +90 -0
  247. package/dist/node/features/vault.d.ts.map +1 -0
  248. package/dist/node/features/vm.d.ts +285 -0
  249. package/dist/node/features/vm.d.ts.map +1 -0
  250. package/dist/node/features/yaml-tree.d.ts +118 -0
  251. package/dist/node/features/yaml-tree.d.ts.map +1 -0
  252. package/dist/node/features/yaml.d.ts +127 -0
  253. package/dist/node/features/yaml.d.ts.map +1 -0
  254. package/dist/node.d.ts +67 -0
  255. package/dist/node.d.ts.map +1 -0
  256. package/dist/python/generated.d.ts +2 -0
  257. package/dist/python/generated.d.ts.map +1 -0
  258. package/dist/react/index.d.ts +36 -0
  259. package/dist/react/index.d.ts.map +1 -0
  260. package/dist/registry.d.ts +97 -0
  261. package/dist/registry.d.ts.map +1 -0
  262. package/dist/scaffolds/generated.d.ts +13 -0
  263. package/dist/scaffolds/generated.d.ts.map +1 -0
  264. package/dist/scaffolds/template.d.ts +11 -0
  265. package/dist/scaffolds/template.d.ts.map +1 -0
  266. package/dist/schemas/base.d.ts +254 -0
  267. package/dist/schemas/base.d.ts.map +1 -0
  268. package/dist/selector.d.ts +130 -0
  269. package/dist/selector.d.ts.map +1 -0
  270. package/dist/server.d.ts +89 -0
  271. package/dist/server.d.ts.map +1 -0
  272. package/dist/servers/express.d.ts +104 -0
  273. package/dist/servers/express.d.ts.map +1 -0
  274. package/dist/servers/mcp.d.ts +201 -0
  275. package/dist/servers/mcp.d.ts.map +1 -0
  276. package/dist/servers/socket.d.ts +121 -0
  277. package/dist/servers/socket.d.ts.map +1 -0
  278. package/dist/state.d.ts +24 -0
  279. package/dist/state.d.ts.map +1 -0
  280. package/dist/web/clients/socket.d.ts +37 -0
  281. package/dist/web/clients/socket.d.ts.map +1 -0
  282. package/dist/web/container.d.ts +55 -0
  283. package/dist/web/container.d.ts.map +1 -0
  284. package/dist/web/extension.d.ts +4 -0
  285. package/dist/web/extension.d.ts.map +1 -0
  286. package/dist/web/feature.d.ts +8 -0
  287. package/dist/web/feature.d.ts.map +1 -0
  288. package/dist/web/features/asset-loader.d.ts +35 -0
  289. package/dist/web/features/asset-loader.d.ts.map +1 -0
  290. package/dist/web/features/container-link.d.ts +167 -0
  291. package/dist/web/features/container-link.d.ts.map +1 -0
  292. package/dist/web/features/esbuild.d.ts +51 -0
  293. package/dist/web/features/esbuild.d.ts.map +1 -0
  294. package/dist/web/features/helpers.d.ts +140 -0
  295. package/dist/web/features/helpers.d.ts.map +1 -0
  296. package/dist/web/features/network.d.ts +69 -0
  297. package/dist/web/features/network.d.ts.map +1 -0
  298. package/dist/web/features/speech.d.ts +71 -0
  299. package/dist/web/features/speech.d.ts.map +1 -0
  300. package/dist/web/features/vault.d.ts +62 -0
  301. package/dist/web/features/vault.d.ts.map +1 -0
  302. package/dist/web/features/vm.d.ts +48 -0
  303. package/dist/web/features/vm.d.ts.map +1 -0
  304. package/dist/web/features/voice-recognition.d.ts +96 -0
  305. package/dist/web/features/voice-recognition.d.ts.map +1 -0
  306. package/dist/web/shims/isomorphic-vm.d.ts +22 -0
  307. package/dist/web/shims/isomorphic-vm.d.ts.map +1 -0
  308. package/docs/CLI.md +335 -0
  309. package/docs/CNAME +1 -0
  310. package/docs/README.md +60 -0
  311. package/docs/TABLE-OF-CONTENTS.md +183 -0
  312. package/docs/apis/clients/elevenlabs.md +308 -0
  313. package/docs/apis/clients/graph.md +107 -0
  314. package/docs/apis/clients/openai.md +429 -0
  315. package/docs/apis/clients/rest.md +161 -0
  316. package/docs/apis/clients/websocket.md +174 -0
  317. package/docs/apis/features/agi/assistant.md +625 -0
  318. package/docs/apis/features/agi/assistants-manager.md +282 -0
  319. package/docs/apis/features/agi/auto-assistant.md +279 -0
  320. package/docs/apis/features/agi/browser-use.md +802 -0
  321. package/docs/apis/features/agi/claude-code.md +884 -0
  322. package/docs/apis/features/agi/conversation-history.md +364 -0
  323. package/docs/apis/features/agi/conversation.md +548 -0
  324. package/docs/apis/features/agi/docs-reader.md +99 -0
  325. package/docs/apis/features/agi/file-tools.md +163 -0
  326. package/docs/apis/features/agi/luca-coder.md +407 -0
  327. package/docs/apis/features/agi/openai-codex.md +396 -0
  328. package/docs/apis/features/agi/openapi.md +138 -0
  329. package/docs/apis/features/agi/semantic-search.md +387 -0
  330. package/docs/apis/features/agi/skills-library.md +239 -0
  331. package/docs/apis/features/node/container-link.md +192 -0
  332. package/docs/apis/features/node/content-db.md +450 -0
  333. package/docs/apis/features/node/disk-cache.md +379 -0
  334. package/docs/apis/features/node/dns.md +652 -0
  335. package/docs/apis/features/node/docker.md +706 -0
  336. package/docs/apis/features/node/downloader.md +81 -0
  337. package/docs/apis/features/node/esbuild.md +60 -0
  338. package/docs/apis/features/node/file-manager.md +191 -0
  339. package/docs/apis/features/node/fs.md +1217 -0
  340. package/docs/apis/features/node/git.md +371 -0
  341. package/docs/apis/features/node/google-auth.md +193 -0
  342. package/docs/apis/features/node/google-calendar.md +202 -0
  343. package/docs/apis/features/node/google-docs.md +173 -0
  344. package/docs/apis/features/node/google-drive.md +246 -0
  345. package/docs/apis/features/node/google-mail.md +214 -0
  346. package/docs/apis/features/node/google-sheets.md +194 -0
  347. package/docs/apis/features/node/grep.md +292 -0
  348. package/docs/apis/features/node/helpers.md +164 -0
  349. package/docs/apis/features/node/ink.md +334 -0
  350. package/docs/apis/features/node/ipc-socket.md +249 -0
  351. package/docs/apis/features/node/json-tree.md +86 -0
  352. package/docs/apis/features/node/networking.md +316 -0
  353. package/docs/apis/features/node/nlp.md +133 -0
  354. package/docs/apis/features/node/opener.md +97 -0
  355. package/docs/apis/features/node/os.md +146 -0
  356. package/docs/apis/features/node/package-finder.md +392 -0
  357. package/docs/apis/features/node/postgres.md +234 -0
  358. package/docs/apis/features/node/proc.md +399 -0
  359. package/docs/apis/features/node/process-manager.md +305 -0
  360. package/docs/apis/features/node/python.md +604 -0
  361. package/docs/apis/features/node/redis.md +380 -0
  362. package/docs/apis/features/node/repl.md +88 -0
  363. package/docs/apis/features/node/runpod.md +674 -0
  364. package/docs/apis/features/node/secure-shell.md +176 -0
  365. package/docs/apis/features/node/semantic-search.md +408 -0
  366. package/docs/apis/features/node/sqlite.md +233 -0
  367. package/docs/apis/features/node/telegram.md +279 -0
  368. package/docs/apis/features/node/transpiler.md +74 -0
  369. package/docs/apis/features/node/tts.md +133 -0
  370. package/docs/apis/features/node/ui.md +701 -0
  371. package/docs/apis/features/node/vault.md +59 -0
  372. package/docs/apis/features/node/vm.md +75 -0
  373. package/docs/apis/features/node/yaml-tree.md +85 -0
  374. package/docs/apis/features/node/yaml.md +176 -0
  375. package/docs/apis/features/web/asset-loader.md +59 -0
  376. package/docs/apis/features/web/container-link.md +192 -0
  377. package/docs/apis/features/web/esbuild.md +54 -0
  378. package/docs/apis/features/web/helpers.md +164 -0
  379. package/docs/apis/features/web/network.md +44 -0
  380. package/docs/apis/features/web/speech.md +69 -0
  381. package/docs/apis/features/web/vault.md +59 -0
  382. package/docs/apis/features/web/vm.md +75 -0
  383. package/docs/apis/features/web/voice.md +84 -0
  384. package/docs/apis/servers/express.md +171 -0
  385. package/docs/apis/servers/mcp.md +238 -0
  386. package/docs/apis/servers/websocket.md +170 -0
  387. package/docs/bootstrap/CLAUDE.md +101 -0
  388. package/docs/bootstrap/SKILL.md +341 -0
  389. package/docs/bootstrap/templates/about-command.ts +41 -0
  390. package/docs/bootstrap/templates/docs-models.ts +22 -0
  391. package/docs/bootstrap/templates/docs-readme.md +43 -0
  392. package/docs/bootstrap/templates/example-feature.ts +53 -0
  393. package/docs/bootstrap/templates/health-endpoint.ts +15 -0
  394. package/docs/bootstrap/templates/luca-cli.ts +30 -0
  395. package/docs/bootstrap/templates/runme.md +54 -0
  396. package/docs/challenges/caching-proxy.md +16 -0
  397. package/docs/challenges/content-db-round-trip.md +14 -0
  398. package/docs/challenges/custom-command.md +9 -0
  399. package/docs/challenges/file-watcher-pipeline.md +11 -0
  400. package/docs/challenges/grep-audit-report.md +15 -0
  401. package/docs/challenges/multi-feature-dashboard.md +14 -0
  402. package/docs/challenges/process-orchestrator.md +17 -0
  403. package/docs/challenges/rest-api-server-with-client.md +12 -0
  404. package/docs/challenges/script-runner-with-vm.md +11 -0
  405. package/docs/challenges/simple-rest-api.md +15 -0
  406. package/docs/challenges/websocket-serve-and-client.md +11 -0
  407. package/docs/challenges/yaml-config-system.md +14 -0
  408. package/docs/command-system-overhaul.md +94 -0
  409. package/docs/documentation-audit.md +134 -0
  410. package/docs/examples/assistant/CORE.md +18 -0
  411. package/docs/examples/assistant/hooks.ts +3 -0
  412. package/docs/examples/assistant/tools.ts +10 -0
  413. package/docs/examples/assistant-hooks-reference.ts +171 -0
  414. package/docs/examples/assistant-with-process-manager.md +84 -0
  415. package/docs/examples/content-db.md +77 -0
  416. package/docs/examples/disk-cache.md +83 -0
  417. package/docs/examples/docker.md +101 -0
  418. package/docs/examples/downloader.md +70 -0
  419. package/docs/examples/entity.md +124 -0
  420. package/docs/examples/esbuild.md +80 -0
  421. package/docs/examples/feature-as-tool-provider.md +143 -0
  422. package/docs/examples/file-manager.md +82 -0
  423. package/docs/examples/fs.md +83 -0
  424. package/docs/examples/git.md +85 -0
  425. package/docs/examples/google-auth.md +88 -0
  426. package/docs/examples/google-calendar.md +94 -0
  427. package/docs/examples/google-docs.md +82 -0
  428. package/docs/examples/google-drive.md +96 -0
  429. package/docs/examples/google-sheets.md +95 -0
  430. package/docs/examples/grep.md +85 -0
  431. package/docs/examples/ink-blocks.md +75 -0
  432. package/docs/examples/ink-renderer.md +41 -0
  433. package/docs/examples/ink.md +103 -0
  434. package/docs/examples/ipc-socket.md +103 -0
  435. package/docs/examples/json-tree.md +91 -0
  436. package/docs/examples/networking.md +58 -0
  437. package/docs/examples/nlp.md +91 -0
  438. package/docs/examples/opener.md +78 -0
  439. package/docs/examples/os.md +72 -0
  440. package/docs/examples/package-finder.md +89 -0
  441. package/docs/examples/postgres.md +91 -0
  442. package/docs/examples/proc.md +81 -0
  443. package/docs/examples/process-manager.md +79 -0
  444. package/docs/examples/python.md +132 -0
  445. package/docs/examples/repl.md +93 -0
  446. package/docs/examples/runpod.md +119 -0
  447. package/docs/examples/secure-shell.md +92 -0
  448. package/docs/examples/sqlite.md +86 -0
  449. package/docs/examples/structured-output-with-assistants.md +144 -0
  450. package/docs/examples/telegram.md +77 -0
  451. package/docs/examples/tts.md +86 -0
  452. package/docs/examples/ui.md +80 -0
  453. package/docs/examples/vault.md +70 -0
  454. package/docs/examples/vm.md +86 -0
  455. package/docs/examples/websocket-ask-and-reply-example.md +128 -0
  456. package/docs/examples/yaml-tree.md +93 -0
  457. package/docs/examples/yaml.md +104 -0
  458. package/docs/ideas/assistant-factory-pattern.md +142 -0
  459. package/docs/in-memory-fs.md +4 -0
  460. package/docs/introspection-audit.md +49 -0
  461. package/docs/introspection.md +164 -0
  462. package/docs/mcp/readme.md +162 -0
  463. package/docs/models.ts +41 -0
  464. package/docs/philosophy.md +86 -0
  465. package/docs/principles.md +7 -0
  466. package/docs/prompts/audit-codebase-for-failures-to-use-the-container.md +34 -0
  467. package/docs/prompts/check-for-undocumented-features.md +27 -0
  468. package/docs/prompts/mcp-test-easy-command.md +27 -0
  469. package/docs/scaffolds/client.md +149 -0
  470. package/docs/scaffolds/command.md +120 -0
  471. package/docs/scaffolds/endpoint.md +171 -0
  472. package/docs/scaffolds/feature.md +158 -0
  473. package/docs/scaffolds/selector.md +91 -0
  474. package/docs/scaffolds/server.md +196 -0
  475. package/docs/selectors.md +115 -0
  476. package/docs/sessions/custom-command/attempt-log-2.md +195 -0
  477. package/docs/sessions/file-watcher-pipeline/attempt-log-1.md +728 -0
  478. package/docs/sessions/file-watcher-pipeline/attempt-log-2.md +555 -0
  479. package/docs/sessions/grep-audit-report/attempt-log-1.md +289 -0
  480. package/docs/sessions/multi-feature-dashboard/attempt-log-2.md +679 -0
  481. package/docs/sessions/rest-api-server-with-client/attempt-log-1.md +1 -0
  482. package/docs/sessions/rest-api-server-with-client/attempt-log-3.md +920 -0
  483. package/docs/sessions/simple-rest-api/attempt-log-1.md +593 -0
  484. package/docs/sessions/websocket-serve-and-client/attempt-log-2.md +995 -0
  485. package/docs/tutorials/00-bootstrap.md +166 -0
  486. package/docs/tutorials/01-getting-started.md +106 -0
  487. package/docs/tutorials/02-container.md +210 -0
  488. package/docs/tutorials/03-scripts.md +194 -0
  489. package/docs/tutorials/04-features-overview.md +196 -0
  490. package/docs/tutorials/05-state-and-events.md +171 -0
  491. package/docs/tutorials/06-servers.md +157 -0
  492. package/docs/tutorials/07-endpoints.md +198 -0
  493. package/docs/tutorials/08-commands.md +252 -0
  494. package/docs/tutorials/09-clients.md +162 -0
  495. package/docs/tutorials/10-creating-features.md +203 -0
  496. package/docs/tutorials/11-contentbase.md +191 -0
  497. package/docs/tutorials/12-assistants.md +215 -0
  498. package/docs/tutorials/13-introspection.md +157 -0
  499. package/docs/tutorials/14-type-system.md +174 -0
  500. package/docs/tutorials/15-project-patterns.md +222 -0
  501. package/docs/tutorials/16-google-features.md +534 -0
  502. package/docs/tutorials/17-tui-blocks.md +530 -0
  503. package/docs/tutorials/18-semantic-search.md +334 -0
  504. package/docs/tutorials/19-python-sessions.md +401 -0
  505. package/docs/tutorials/20-browser-esm.md +234 -0
  506. package/index.html +1430 -0
  507. package/index.ts +1 -0
  508. package/install.sh +84 -0
  509. package/luca.cli.ts +16 -0
  510. package/luca.console.ts +9 -0
  511. package/main.py +6 -0
  512. package/package.json +219 -58
  513. package/public/index.html +1430 -0
  514. package/public/slides-ai-native.html +902 -0
  515. package/public/slides-intro.html +974 -0
  516. package/pyproject.toml +7 -0
  517. package/scripts/build-web.ts +28 -0
  518. package/scripts/examples/ask-luca-expert.ts +42 -0
  519. package/scripts/examples/assistant-questions.ts +12 -0
  520. package/scripts/examples/excalidraw-expert.ts +75 -0
  521. package/scripts/examples/expert-chat.ts +0 -0
  522. package/scripts/examples/file-manager.ts +14 -0
  523. package/scripts/examples/ideas.ts +12 -0
  524. package/scripts/examples/interactive-chat.ts +20 -0
  525. package/scripts/examples/openai-tool-calls.ts +113 -0
  526. package/scripts/examples/opening-a-web-browser.ts +5 -0
  527. package/scripts/examples/telegram-bot.ts +79 -0
  528. package/scripts/examples/using-assistant-with-mcp.ts +555 -0
  529. package/scripts/examples/using-claude-code.ts +10 -0
  530. package/scripts/examples/using-contentdb.ts +35 -0
  531. package/scripts/examples/using-conversations.ts +35 -0
  532. package/scripts/examples/using-disk-cache.ts +10 -0
  533. package/scripts/examples/using-docker-shell.ts +75 -0
  534. package/scripts/examples/using-elevenlabs.ts +25 -0
  535. package/scripts/examples/using-google-calendar.ts +57 -0
  536. package/scripts/examples/using-google-docs.ts +74 -0
  537. package/scripts/examples/using-google-drive.ts +74 -0
  538. package/scripts/examples/using-google-sheets.ts +89 -0
  539. package/scripts/examples/using-nlp.ts +55 -0
  540. package/scripts/examples/using-ollama.ts +11 -0
  541. package/scripts/examples/using-postgres.ts +55 -0
  542. package/scripts/examples/using-runpod.ts +32 -0
  543. package/scripts/examples/using-tts.ts +40 -0
  544. package/scripts/scaffold.ts +391 -0
  545. package/scripts/scratch.ts +15 -0
  546. package/scripts/stamp-build.sh +12 -0
  547. package/scripts/test-assistant-hooks.ts +13 -0
  548. package/scripts/test-docs-reader.ts +10 -0
  549. package/scripts/test-linux-binary.sh +80 -0
  550. package/scripts/update-introspection-data.ts +58 -0
  551. package/src/agi/README.md +14 -0
  552. package/src/agi/container.server.ts +152 -0
  553. package/src/agi/endpoints/ask.ts +60 -0
  554. package/src/agi/endpoints/conversations/[id].ts +45 -0
  555. package/src/agi/endpoints/conversations.ts +31 -0
  556. package/src/agi/endpoints/experts.ts +37 -0
  557. package/src/agi/feature.ts +13 -0
  558. package/src/agi/features/agent-memory.ts +694 -0
  559. package/src/agi/features/assistant.ts +1624 -0
  560. package/src/agi/features/assistants-manager.ts +418 -0
  561. package/src/agi/features/autonomous-assistant.ts +431 -0
  562. package/src/agi/features/browser-use.ts +653 -0
  563. package/src/agi/features/claude-code.ts +1538 -0
  564. package/src/agi/features/coding-tools.ts +175 -0
  565. package/src/agi/features/conversation-history.ts +495 -0
  566. package/src/agi/features/conversation.ts +1323 -0
  567. package/src/agi/features/docs-reader.ts +167 -0
  568. package/src/agi/features/file-tools.ts +293 -0
  569. package/src/agi/features/luca-coder.ts +639 -0
  570. package/src/agi/features/openai-codex.ts +651 -0
  571. package/src/agi/features/openapi.ts +445 -0
  572. package/src/agi/features/skills-library.ts +478 -0
  573. package/src/agi/index.ts +6 -0
  574. package/src/agi/lib/interceptor-chain.ts +89 -0
  575. package/src/agi/lib/token-counter.ts +122 -0
  576. package/src/bootstrap/generated.ts +9792 -0
  577. package/src/browser.ts +25 -0
  578. package/src/bus.ts +122 -0
  579. package/src/cli/build-info.ts +4 -0
  580. package/src/cli/cli.ts +355 -0
  581. package/src/client.ts +170 -0
  582. package/src/clients/civitai/index.ts +537 -0
  583. package/src/clients/client-template.ts +41 -0
  584. package/src/clients/comfyui/index.ts +604 -0
  585. package/src/clients/elevenlabs/index.ts +317 -0
  586. package/src/clients/graph.ts +87 -0
  587. package/src/clients/openai/index.ts +456 -0
  588. package/src/clients/rest.ts +207 -0
  589. package/src/clients/supabase/index.ts +357 -0
  590. package/src/clients/voicebox/index.ts +300 -0
  591. package/src/clients/websocket.ts +251 -0
  592. package/src/command.ts +505 -0
  593. package/src/commands/bootstrap.ts +244 -0
  594. package/src/commands/chat.ts +308 -0
  595. package/src/commands/code.ts +371 -0
  596. package/src/commands/console.ts +189 -0
  597. package/src/commands/describe.ts +243 -0
  598. package/src/commands/eval.ts +121 -0
  599. package/src/commands/help.ts +240 -0
  600. package/src/commands/index.ts +19 -0
  601. package/src/commands/introspect.ts +218 -0
  602. package/src/commands/mcp.ts +64 -0
  603. package/src/commands/prompt.ts +982 -0
  604. package/src/commands/run.ts +278 -0
  605. package/src/commands/sandbox-mcp.ts +343 -0
  606. package/src/commands/save-api-docs.ts +51 -0
  607. package/src/commands/scaffold.ts +225 -0
  608. package/src/commands/select.ts +99 -0
  609. package/src/commands/serve.ts +208 -0
  610. package/src/container-describer.ts +1084 -0
  611. package/src/container.ts +1186 -0
  612. package/src/endpoint.ts +365 -0
  613. package/src/entity.ts +173 -0
  614. package/src/feature.ts +118 -0
  615. package/src/graft.ts +181 -0
  616. package/src/hash-object.ts +97 -0
  617. package/src/helper.ts +849 -0
  618. package/src/introspection/generated.agi.ts +40208 -0
  619. package/src/introspection/generated.node.ts +28686 -0
  620. package/src/introspection/generated.web.ts +2251 -0
  621. package/src/introspection/index.ts +296 -0
  622. package/src/introspection/scan.ts +1131 -0
  623. package/src/node/container.ts +409 -0
  624. package/src/node/feature.ts +13 -0
  625. package/src/node/features/container-link.ts +559 -0
  626. package/src/node/features/content-db.ts +812 -0
  627. package/src/node/features/disk-cache.ts +388 -0
  628. package/src/node/features/dns.ts +669 -0
  629. package/src/node/features/docker.ts +921 -0
  630. package/src/node/features/downloader.ts +79 -0
  631. package/src/node/features/figlet-fonts.ts +600 -0
  632. package/src/node/features/file-manager.ts +535 -0
  633. package/src/node/features/fs.ts +1050 -0
  634. package/src/node/features/git.ts +592 -0
  635. package/src/node/features/google-auth.ts +504 -0
  636. package/src/node/features/google-calendar.ts +306 -0
  637. package/src/node/features/google-docs.ts +412 -0
  638. package/src/node/features/google-drive.ts +346 -0
  639. package/src/node/features/google-mail.ts +540 -0
  640. package/src/node/features/google-sheets.ts +286 -0
  641. package/src/node/features/grep.ts +427 -0
  642. package/src/node/features/helpers.ts +735 -0
  643. package/src/node/features/ink.ts +490 -0
  644. package/src/node/features/ipc-socket.ts +649 -0
  645. package/src/node/features/json-tree.ts +170 -0
  646. package/src/node/features/networking.ts +961 -0
  647. package/src/node/features/nlp.ts +212 -0
  648. package/src/node/features/opener.ts +180 -0
  649. package/src/node/features/os.ts +403 -0
  650. package/src/node/features/package-finder.ts +540 -0
  651. package/src/node/features/postgres.ts +289 -0
  652. package/src/node/features/proc.ts +503 -0
  653. package/src/node/features/process-manager.ts +844 -0
  654. package/src/node/features/python.ts +906 -0
  655. package/src/node/features/redis.ts +446 -0
  656. package/src/node/features/repl.ts +212 -0
  657. package/src/node/features/runpod.ts +811 -0
  658. package/src/node/features/secure-shell.ts +267 -0
  659. package/src/node/features/semantic-search.ts +935 -0
  660. package/src/node/features/sqlite.ts +289 -0
  661. package/src/node/features/telegram.ts +343 -0
  662. package/src/node/features/transpiler.ts +161 -0
  663. package/src/node/features/tts.ts +185 -0
  664. package/src/node/features/ui.ts +786 -0
  665. package/src/node/features/vault.ts +153 -0
  666. package/src/node/features/vm.ts +462 -0
  667. package/src/node/features/yaml-tree.ts +148 -0
  668. package/src/node/features/yaml.ts +133 -0
  669. package/src/node.ts +76 -0
  670. package/src/python/bridge.py +220 -0
  671. package/src/python/generated.ts +227 -0
  672. package/src/react/index.ts +175 -0
  673. package/src/registry.ts +210 -0
  674. package/src/scaffolds/generated.ts +1815 -0
  675. package/src/scaffolds/template.ts +46 -0
  676. package/src/schemas/base.ts +296 -0
  677. package/src/selector.ts +352 -0
  678. package/src/server.ts +229 -0
  679. package/src/servers/express.ts +283 -0
  680. package/src/servers/mcp.ts +802 -0
  681. package/src/servers/socket.ts +258 -0
  682. package/src/state.ts +101 -0
  683. package/src/web/clients/socket.ts +99 -0
  684. package/src/web/container.ts +75 -0
  685. package/src/web/extension.ts +30 -0
  686. package/src/web/feature.ts +12 -0
  687. package/src/web/features/asset-loader.ts +72 -0
  688. package/src/web/features/container-link.ts +382 -0
  689. package/src/web/features/esbuild.ts +93 -0
  690. package/src/web/features/helpers.ts +269 -0
  691. package/src/web/features/network.ts +85 -0
  692. package/src/web/features/speech.ts +104 -0
  693. package/src/web/features/vault.ts +207 -0
  694. package/src/web/features/vm.ts +85 -0
  695. package/src/web/features/voice-recognition.ts +161 -0
  696. package/src/web/shims/isomorphic-vm.ts +149 -0
  697. package/test/assistant-hooks.test.ts +306 -0
  698. package/test/assistant.test.ts +81 -0
  699. package/test/bus.test.ts +134 -0
  700. package/test/clients-servers.test.ts +217 -0
  701. package/test/command.test.ts +267 -0
  702. package/test/container-link.test.ts +274 -0
  703. package/test/conversation.test.ts +220 -0
  704. package/test/features.test.ts +160 -0
  705. package/test/fork-and-research.test.ts +450 -0
  706. package/test/integration.test.ts +787 -0
  707. package/test/interceptor-chain.test.ts +61 -0
  708. package/test/node-container.test.ts +121 -0
  709. package/test/python-session.test.ts +105 -0
  710. package/test/rate-limit.test.ts +272 -0
  711. package/test/semantic-search.test.ts +550 -0
  712. package/test/state.test.ts +121 -0
  713. package/test/vm-context.test.ts +146 -0
  714. package/test/vm-loadmodule.test.ts +213 -0
  715. package/test/websocket-ask.test.ts +101 -0
  716. package/test-integration/assistant.test.ts +138 -0
  717. package/test-integration/assistants-manager.test.ts +113 -0
  718. package/test-integration/claude-code.test.ts +98 -0
  719. package/test-integration/conversation-history.test.ts +205 -0
  720. package/test-integration/conversation.test.ts +137 -0
  721. package/test-integration/elevenlabs.test.ts +55 -0
  722. package/test-integration/google-services.test.ts +80 -0
  723. package/test-integration/helpers.ts +89 -0
  724. package/test-integration/memory.test.ts +204 -0
  725. package/test-integration/openai-codex.test.ts +93 -0
  726. package/test-integration/runpod.test.ts +58 -0
  727. package/test-integration/server-endpoints.test.ts +97 -0
  728. package/test-integration/telegram.test.ts +46 -0
  729. package/tsconfig.build.json +12 -0
  730. package/tsconfig.json +58 -0
  731. package/uv.lock +8 -0
  732. package/LICENSE +0 -21
  733. package/dist/cli/cli.js +0 -48
  734. package/dist/cli/common.d.ts +0 -2
  735. package/dist/cli/common.js +0 -6
  736. package/dist/cli/index.d.ts +0 -2
  737. package/dist/cli/index.js +0 -5
  738. package/dist/cli/run.d.ts +0 -1
  739. package/dist/cli/run.js +0 -38
  740. package/dist/core/index.d.ts +0 -4
  741. package/dist/core/index.js +0 -32
  742. package/dist/core/read.d.ts +0 -2
  743. package/dist/core/read.js +0 -29
  744. package/dist/core/request.d.ts +0 -1
  745. package/dist/core/request.js +0 -2
  746. package/dist/core/write.d.ts +0 -2
  747. package/dist/core/write.js +0 -21
  748. package/dist/index.d.ts +0 -1
  749. package/dist/index.js +0 -5
  750. package/dist/utils/common.d.ts +0 -9
  751. package/dist/utils/common.js +0 -57
  752. package/dist/utils/consts.d.ts +0 -3
  753. package/dist/utils/consts.js +0 -11
  754. package/dist/utils/dict.d.ts +0 -1
  755. package/dist/utils/dict.js +0 -7
  756. package/dist/utils/index.d.ts +0 -5
  757. package/dist/utils/index.js +0 -21
  758. package/dist/utils/log.d.ts +0 -1
  759. package/dist/utils/log.js +0 -5
  760. package/dist/utils/types.d.ts +0 -1
  761. package/dist/utils/types.js +0 -2
  762. package/dist/utils/utils.test.d.ts +0 -1
  763. package/dist/utils/utils.test.js +0 -7
@@ -0,0 +1,844 @@
1
+ import { z } from 'zod'
2
+ import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
+ import { Feature } from '../feature.js'
4
+ import { State } from '../../state.js'
5
+ import { Bus, type EventMap } from '../../bus.js'
6
+ import type { ChildProcess } from './proc.js'
7
+ import type { Helper } from '../../helper.js'
8
+
9
+ // ─── Output Buffer ─────────────────────────────────────────────────────────
10
+
11
+ const HEAD_LINES = 20
12
+ const TAIL_LINES = 50
13
+
14
+ /**
15
+ * A memory-efficient output buffer that keeps the first N lines (head)
16
+ * and the last M lines (tail), discarding everything in between.
17
+ */
18
+ class OutputBuffer {
19
+ private _head: string[] = []
20
+ private _tail: string[] = []
21
+ private _totalLines = 0
22
+ private _partial = ''
23
+ private _headFull = false
24
+
25
+ constructor(
26
+ private _headLimit = HEAD_LINES,
27
+ private _tailLimit = TAIL_LINES
28
+ ) {}
29
+
30
+ /** Append a chunk of output (may contain partial lines) */
31
+ append(chunk: string): void {
32
+ const text = this._partial + chunk
33
+ const lines = text.split('\n')
34
+
35
+ // Last element is a partial line (no trailing newline) — hold it
36
+ this._partial = lines.pop()!
37
+
38
+ for (const line of lines) {
39
+ this._pushLine(line)
40
+ }
41
+ }
42
+
43
+ /** Flush any remaining partial line */
44
+ flush(): void {
45
+ if (this._partial) {
46
+ this._pushLine(this._partial)
47
+ this._partial = ''
48
+ }
49
+ }
50
+
51
+ get totalLines() { return this._totalLines }
52
+
53
+ /** Get the head lines (first N lines of output) */
54
+ get head(): string[] { return this._head }
55
+
56
+ /** Get the tail lines (last M lines of output) */
57
+ get tail(): string[] { return this._tail }
58
+
59
+ /** Number of lines dropped between head and tail */
60
+ get droppedLines(): number {
61
+ return Math.max(0, this._totalLines - this._head.length - this._tail.length)
62
+ }
63
+
64
+ /** Format the buffer for display */
65
+ toString(): string {
66
+ const parts: string[] = []
67
+ if (this._head.length) parts.push(this._head.join('\n'))
68
+ if (this.droppedLines > 0) parts.push(`\n... (${this.droppedLines} lines omitted) ...\n`)
69
+ if (this._tail.length && this._totalLines > this._headLimit) parts.push(this._tail.join('\n'))
70
+ return parts.join('\n')
71
+ }
72
+
73
+ private _pushLine(line: string): void {
74
+ this._totalLines++
75
+
76
+ if (!this._headFull) {
77
+ this._head.push(line)
78
+ if (this._head.length >= this._headLimit) this._headFull = true
79
+ return
80
+ }
81
+
82
+ this._tail.push(line)
83
+ if (this._tail.length > this._tailLimit) {
84
+ this._tail.shift()
85
+ }
86
+ }
87
+ }
88
+
89
+ // ─── Schemas ────────────────────────────────────────────────────────────────
90
+
91
+ const ProcessMetadataSchema = z.object({
92
+ id: z.string().describe('Unique process identifier'),
93
+ tag: z.string().optional().describe('User-defined tag for lookups'),
94
+ command: z.string().describe('The command that was spawned'),
95
+ args: z.array(z.string()).describe('Arguments passed to the command'),
96
+ pid: z.number().optional().describe('OS process ID'),
97
+ status: z.enum(['running', 'exited', 'crashed', 'killed']).describe('Current process lifecycle status'),
98
+ exitCode: z.number().optional().describe('Exit code after process ends'),
99
+ startedAt: z.number().describe('Timestamp when the process was spawned'),
100
+ endedAt: z.number().optional().describe('Timestamp when the process ended'),
101
+ })
102
+
103
+ export const ProcessManagerStateSchema = FeatureStateSchema.extend({
104
+ processes: z.record(z.string(), ProcessMetadataSchema)
105
+ .describe('Map of process ID to metadata'),
106
+ totalSpawned: z.number().default(0)
107
+ .describe('Total number of processes spawned since feature creation'),
108
+ })
109
+ export type ProcessManagerState = z.infer<typeof ProcessManagerStateSchema>
110
+
111
+ export const ProcessManagerOptionsSchema = FeatureOptionsSchema.extend({
112
+ autoCleanup: z.boolean().default(true)
113
+ .describe('Register process.on exit/SIGINT/SIGTERM handlers to kill all tracked processes'),
114
+ })
115
+ export type ProcessManagerOptions = z.infer<typeof ProcessManagerOptionsSchema>
116
+
117
+ export const ProcessManagerEventsSchema = FeatureEventsSchema.extend({
118
+ spawned: z.tuple([z.string().describe('process ID'), z.string().describe('process metadata')])
119
+ .describe('Emitted when a new process is spawned'),
120
+ exited: z.tuple([z.string().describe('process ID'), z.number().describe('exit code')])
121
+ .describe('Emitted when a process exits normally'),
122
+ crashed: z.tuple([z.string().describe('process ID'), z.number().describe('exit code'), z.string().describe('error info')])
123
+ .describe('Emitted when a process exits with non-zero code'),
124
+ killed: z.tuple([z.string().describe('process ID')])
125
+ .describe('Emitted when a process is killed'),
126
+ allStopped: z.tuple([])
127
+ .describe('Emitted when all tracked processes have stopped'),
128
+ })
129
+
130
+ // ─── SpawnHandler ───────────────────────────────────────────────────────────
131
+
132
+ interface SpawnHandlerState {
133
+ id: string
134
+ tag?: string
135
+ command: string
136
+ args: string[]
137
+ pid?: number
138
+ status: 'running' | 'exited' | 'crashed' | 'killed'
139
+ exitCode?: number
140
+ startedAt: number
141
+ endedAt?: number
142
+ }
143
+
144
+ interface SpawnHandlerEvents extends EventMap {
145
+ stdout: [data: string]
146
+ stderr: [data: string]
147
+ exit: [code: number]
148
+ crash: [code: number]
149
+ killed: []
150
+ }
151
+
152
+ export interface SpawnOptions {
153
+ /** User-defined tag for later lookups via getByTag() */
154
+ tag?: string
155
+ /** Working directory for the spawned process (defaults to container cwd) */
156
+ cwd?: string
157
+ /** Additional environment variables merged with process.env */
158
+ env?: Record<string, string>
159
+ /** stdin mode: 'pipe' to write to the process, 'inherit', or 'ignore' (default: 'ignore') */
160
+ stdin?: 'pipe' | 'inherit' | 'ignore' | null
161
+ /** stdout mode: 'pipe' to capture output, 'inherit', or 'ignore' (default: 'pipe') */
162
+ stdout?: 'pipe' | 'inherit' | 'ignore' | null
163
+ /** stderr mode: 'pipe' to capture errors, 'inherit', or 'ignore' (default: 'pipe') */
164
+ stderr?: 'pipe' | 'inherit' | 'ignore' | null
165
+ }
166
+
167
+ /**
168
+ * A handle to a spawned long-running process.
169
+ *
170
+ * Provides observable state, events, and methods to interact with
171
+ * the running process. Returned immediately from `ProcessManager.spawn()`
172
+ * without blocking. Maintains a memory-efficient output buffer that keeps
173
+ * the first 20 lines and last 50 lines of stdout/stderr.
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * const handler = pm.spawn('node', ['server.js'], { tag: 'api' })
178
+ * handler.on('stdout', (data) => console.log(data))
179
+ * handler.on('crash', (code) => console.error('crashed:', code))
180
+ * const exitCode = await handler.await()
181
+ * ```
182
+ */
183
+ export class SpawnHandler {
184
+ readonly state: State<SpawnHandlerState>
185
+ readonly events = new Bus<SpawnHandlerEvents>()
186
+ readonly stdout = new OutputBuffer(HEAD_LINES, TAIL_LINES)
187
+ readonly stderr = new OutputBuffer(HEAD_LINES, TAIL_LINES)
188
+
189
+ private _childProcess: any = null
190
+ private _manager: ProcessManager
191
+ private _exitPromise: Promise<number> | null = null
192
+ private _exitResolve: ((code: number) => void) | null = null
193
+
194
+ constructor(
195
+ id: string,
196
+ command: string,
197
+ args: string[],
198
+ manager: ProcessManager,
199
+ options: SpawnOptions = {}
200
+ ) {
201
+ this._manager = manager
202
+ this.state = new State<SpawnHandlerState>({
203
+ initialState: {
204
+ id,
205
+ command,
206
+ args,
207
+ tag: options.tag,
208
+ status: 'running',
209
+ startedAt: Date.now(),
210
+ }
211
+ })
212
+
213
+ this._exitPromise = new Promise<number>((resolve) => {
214
+ this._exitResolve = resolve
215
+ })
216
+ }
217
+
218
+ /** The unique process identifier */
219
+ get id() { return this.state.get('id')! }
220
+
221
+ /** The user-defined tag, if any */
222
+ get tag() { return this.state.get('tag') }
223
+
224
+ /** The OS process ID */
225
+ get pid() { return this.state.get('pid') }
226
+
227
+ /** Whether the process is still running */
228
+ get isRunning() { return this.state.get('status') === 'running' }
229
+
230
+ /** Whether the process has finished (exited, crashed, or killed) */
231
+ get isDone() {
232
+ const s = this.state.get('status')
233
+ return s === 'exited' || s === 'crashed' || s === 'killed'
234
+ }
235
+
236
+ /** Current process lifecycle status */
237
+ get status() { return this.state.get('status')! }
238
+
239
+ /** Exit code after process ends */
240
+ get exitCode() { return this.state.get('exitCode') }
241
+
242
+ /**
243
+ * Start the process using proc.spawnAndCapture. Called internally by `ProcessManager.spawn()`.
244
+ */
245
+ _start(spawnOptions: SpawnOptions = {}): void {
246
+ const command = this.state.get('command')!
247
+ const args = this.state.get('args')!
248
+ const proc = this._manager.container.feature('proc') as ChildProcess
249
+
250
+ const cwd = spawnOptions.cwd ?? this._manager.container.cwd
251
+
252
+ // Use proc.spawnAndCapture with hooks for real-time streaming
253
+ proc.spawnAndCapture(command, args, {
254
+ cwd,
255
+ onStart: (childProcess: any) => {
256
+ this._childProcess = childProcess
257
+ if (childProcess.pid) {
258
+ this.state.set('pid', childProcess.pid)
259
+ }
260
+ },
261
+ onOutput: (data: string) => {
262
+ this.stdout.append(data)
263
+ this.events.emit('stdout', data)
264
+ },
265
+ onError: (data: string) => {
266
+ this.stderr.append(data)
267
+ this.events.emit('stderr', data)
268
+ },
269
+ onExit: (code: number) => {
270
+ this.stdout.flush()
271
+ this.stderr.flush()
272
+ this._onExit(code)
273
+ },
274
+ }).catch((err: any) => {
275
+ // spawnAndCapture rejected — treat as crash
276
+ this.stdout.flush()
277
+ this.stderr.flush()
278
+ if (!this.isDone) {
279
+ this._onExit(err?.code ?? 1)
280
+ }
281
+ })
282
+ }
283
+
284
+ /**
285
+ * Kill the process.
286
+ *
287
+ * @param signal - Signal to send (default: SIGTERM)
288
+ */
289
+ kill(signal: NodeJS.Signals | number = 'SIGTERM'): void {
290
+ if (this.isDone) return
291
+
292
+ try {
293
+ const pid = this.state.get('pid')
294
+ if (pid) {
295
+ process.kill(pid, signal)
296
+ }
297
+ } catch (err: any) {
298
+ if (err.code !== 'ESRCH') throw err
299
+ }
300
+
301
+ this.stdout.flush()
302
+ this.stderr.flush()
303
+ this.state.set('status', 'killed')
304
+ this.state.set('endedAt', Date.now())
305
+ this.events.emit('killed')
306
+ this._manager._onHandlerDone(this, 'killed')
307
+
308
+ if (this._exitResolve) {
309
+ this._exitResolve(-1)
310
+ this._exitResolve = null
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Returns a promise that resolves with the exit code when the process finishes.
316
+ */
317
+ async await(): Promise<number> {
318
+ if (this.isDone) {
319
+ return this.state.get('exitCode') ?? -1
320
+ }
321
+ return this._exitPromise!
322
+ }
323
+
324
+ /**
325
+ * Write data to the process's stdin (requires `stdin: 'pipe'` in spawn options).
326
+ *
327
+ * @param data - String or Uint8Array to write
328
+ */
329
+ write(data: string | Uint8Array): void {
330
+ const stdin = this._childProcess?.stdin
331
+ if (!stdin) {
332
+ throw new Error('stdin is not piped — pass { stdin: "pipe" } in spawn options')
333
+ }
334
+ stdin.write(data)
335
+ }
336
+
337
+ /**
338
+ * Peek at the buffered output. Returns head (first 20 lines), tail (last 50 lines),
339
+ * and metadata about how much was dropped.
340
+ */
341
+ peek(stream: 'stdout' | 'stderr' = 'stdout'): { head: string[]; tail: string[]; totalLines: number; droppedLines: number } {
342
+ const buf = stream === 'stderr' ? this.stderr : this.stdout
343
+ return {
344
+ head: buf.head,
345
+ tail: buf.tail,
346
+ totalLines: buf.totalLines,
347
+ droppedLines: buf.droppedLines,
348
+ }
349
+ }
350
+
351
+ /** Subscribe to handler events */
352
+ on<E extends string & keyof SpawnHandlerEvents>(event: E, listener: (...args: SpawnHandlerEvents[E]) => void) {
353
+ this.events.on(event, listener)
354
+ return this
355
+ }
356
+
357
+ /** Subscribe to a handler event once */
358
+ once<E extends string & keyof SpawnHandlerEvents>(event: E, listener: (...args: SpawnHandlerEvents[E]) => void) {
359
+ this.events.once(event, listener)
360
+ return this
361
+ }
362
+
363
+ /** Unsubscribe from a handler event */
364
+ off<E extends string & keyof SpawnHandlerEvents>(event: E, listener: (...args: SpawnHandlerEvents[E]) => void) {
365
+ this.events.off(event, listener)
366
+ return this
367
+ }
368
+
369
+ // ─── Internal ─────────────────────────────────────────────────────────────
370
+
371
+ private _onExit(code: number): void {
372
+ if (this.isDone) return
373
+
374
+ const isCrash = code !== 0
375
+ this.state.set('exitCode', code)
376
+ this.state.set('endedAt', Date.now())
377
+
378
+ if (isCrash) {
379
+ this.state.set('status', 'crashed')
380
+ this.events.emit('crash', code)
381
+ this._manager._onHandlerDone(this, 'crashed', code)
382
+ } else {
383
+ this.state.set('status', 'exited')
384
+ this.events.emit('exit', code)
385
+ this._manager._onHandlerDone(this, 'exited', code)
386
+ }
387
+
388
+ if (this._exitResolve) {
389
+ this._exitResolve(code)
390
+ this._exitResolve = null
391
+ }
392
+ }
393
+ }
394
+
395
+ // ─── ProcessManager Feature ─────────────────────────────────────────────────
396
+
397
+ /**
398
+ * Manages long-running child processes with tracking, events, and automatic cleanup.
399
+ *
400
+ * Unlike the `proc` feature whose spawn methods block until the child exits,
401
+ * ProcessManager returns a SpawnHandler immediately — a handle object with its own
402
+ * state, events, and lifecycle methods. The feature tracks all spawned processes,
403
+ * maintains observable state, and can automatically kill them on parent exit.
404
+ *
405
+ * Each handler maintains a memory-efficient output buffer: the first 20 lines (head)
406
+ * and last 50 lines (tail) of stdout/stderr are kept, everything in between is discarded.
407
+ *
408
+ * @example
409
+ * ```typescript
410
+ * const pm = container.feature('processManager', { enable: true })
411
+ *
412
+ * const server = pm.spawn('node', ['server.js'], { tag: 'api', cwd: '/app' })
413
+ * server.on('stdout', (data) => console.log('[api]', data))
414
+ * server.on('crash', (code) => console.error('API crashed:', code))
415
+ *
416
+ * // Peek at buffered output
417
+ * const { head, tail } = server.peek()
418
+ *
419
+ * // Kill one
420
+ * server.kill()
421
+ *
422
+ * // Kill all tracked processes
423
+ * pm.killAll()
424
+ *
425
+ * // List and lookup
426
+ * pm.list() // SpawnHandler[]
427
+ * pm.getByTag('api') // SpawnHandler | undefined
428
+ * ```
429
+ *
430
+ * @extends Feature
431
+ */
432
+ export class ProcessManager extends Feature {
433
+ static override shortcut = 'features.processManager' as const
434
+ static override stateSchema = ProcessManagerStateSchema
435
+ static override optionsSchema = ProcessManagerOptionsSchema
436
+ static override eventsSchema = ProcessManagerEventsSchema
437
+ static { Feature.register(this, 'processManager') }
438
+
439
+ /** Tools that an assistant can use to spawn and manage processes. */
440
+ static override tools: Record<string, { schema: z.ZodType; handler?: Function }> = {
441
+ spawnProcess: {
442
+ schema: z.object({
443
+ command: z.string().describe('The executable to run (e.g. "node", "bun", "python"). NOT a shell command — use runCommand for shell syntax like pipes or &&.'),
444
+ args: z.string().optional().describe('Arguments as a single space-separated string. WARNING: spaces are used to split args, so paths with spaces will break. For complex argument quoting, prefer runCommand instead.'),
445
+ tag: z.string().optional().describe('A short, descriptive label for this process (e.g. "api-server", "file-watcher"). Always set a tag — it makes the process easy to find later with getProcessOutput and killProcess.'),
446
+ cwd: z.string().optional().describe('Working directory for the process. Defaults to the project root.'),
447
+ }).describe(
448
+ 'Start a long-running background process (server, watcher, daemon). Returns immediately with a process ID — the process keeps running. Use this for anything that runs indefinitely. After spawning, call getProcessOutput to check if it started successfully. Always set a tag.'
449
+ ),
450
+ },
451
+ runCommand: {
452
+ schema: z.object({
453
+ command: z.string().describe('A full shell command string (executed via sh -c). Supports pipes, &&, redirects, env vars, globs — anything you can type in a terminal. Examples: "bun test", "npm install && npm run build", "cat logs/*.txt | grep ERROR"'),
454
+ cwd: z.string().optional().describe('Working directory for the command. Defaults to the project root.'),
455
+ }).describe(
456
+ 'Run a shell command and wait for it to complete. Returns stdout, stderr, and exit code. Use this for commands that finish on their own — builds, installs, tests, one-off scripts. For anything that runs forever (servers, watchers), use spawnProcess instead.'
457
+ ),
458
+ },
459
+ listProcesses: {
460
+ schema: z.object({}).describe(
461
+ 'List all tracked background processes with their status, PID, command, uptime, and the last few lines of output. Call this to get an overview before deciding which process to inspect or kill.'
462
+ ),
463
+ },
464
+ getProcessOutput: {
465
+ schema: z.object({
466
+ id: z.string().optional().describe('The process ID (returned by spawnProcess). Provide either id or tag, not both.'),
467
+ tag: z.string().optional().describe('The tag you assigned when spawning the process. Provide either id or tag, not both.'),
468
+ stream: z.string().optional().describe('"stdout" (default) or "stderr". Check stderr when a process crashes or behaves unexpectedly.'),
469
+ }).describe(
470
+ 'Read a background process\'s buffered output — the first 20 lines (startup) and last 50 lines (recent activity). Call this after spawning to verify the process started correctly, and periodically to monitor its health.'
471
+ ),
472
+ },
473
+ killProcess: {
474
+ schema: z.object({
475
+ id: z.string().optional().describe('The process ID to kill. Provide either id or tag, not both.'),
476
+ tag: z.string().optional().describe('The tag of the process to kill. Provide either id or tag, not both.'),
477
+ signal: z.string().optional().describe('"SIGTERM" (default) for graceful shutdown, "SIGKILL" to force-kill a stuck process. Try SIGTERM first.'),
478
+ }).describe(
479
+ 'Stop a running background process. Use SIGTERM (default) for graceful shutdown. If a process doesn\'t respond, follow up with SIGKILL. Always clean up processes you spawned when they\'re no longer needed.'
480
+ ),
481
+ },
482
+ }
483
+
484
+ private _handlers = new Map<string, SpawnHandler>()
485
+ private _cleanupRegistered = false
486
+ private _cleanupHandlers: Array<() => void> = []
487
+
488
+ // ─── Tool Handlers ──────────────────────────────────────────────────────
489
+
490
+ /**
491
+ * Tool handler: spawn a long-running background process.
492
+ */
493
+ async spawnProcess(args: { command: string; args?: string; tag?: string; cwd?: string }) {
494
+ const cmdArgs = args.args ? args.args.split(/\s+/) : []
495
+ const handler = this.spawn(args.command, cmdArgs, {
496
+ tag: args.tag,
497
+ cwd: args.cwd,
498
+ })
499
+
500
+ return {
501
+ id: handler.id,
502
+ pid: handler.pid,
503
+ tag: handler.tag,
504
+ command: `${args.command} ${args.args ?? ''}`.trim(),
505
+ status: 'running',
506
+ }
507
+ }
508
+
509
+ /**
510
+ * Tool handler: run a command to completion and return its output.
511
+ */
512
+ async runCommand(args: { command: string; cwd?: string }) {
513
+ const proc = this.container.feature('proc') as ChildProcess
514
+ const osFeature = this.container.feature('os')
515
+ const result = await proc.spawnAndCapture(osFeature.shell, [osFeature.shellFlag, args.command], {
516
+ cwd: args.cwd ?? this.container.cwd,
517
+ })
518
+
519
+ return {
520
+ exitCode: result.exitCode,
521
+ stdout: result.stdout,
522
+ stderr: result.stderr,
523
+ success: result.exitCode === 0,
524
+ }
525
+ }
526
+
527
+ /**
528
+ * Tool handler: list all tracked processes.
529
+ */
530
+ async listProcesses() {
531
+ const handlers = this.list()
532
+ if (handlers.length === 0) return { processes: [], message: 'No tracked processes.' }
533
+
534
+ return {
535
+ processes: handlers.map(h => {
536
+ const now = Date.now()
537
+ const startedAt = h.state.get('startedAt')!
538
+ const endedAt = h.state.get('endedAt')
539
+ const duration = (endedAt ?? now) - startedAt
540
+ const lastStdout = h.stdout.tail.length ? h.stdout.tail.slice(-3) : h.stdout.head.slice(-3)
541
+
542
+ return {
543
+ id: h.id,
544
+ tag: h.tag,
545
+ pid: h.pid,
546
+ command: `${h.state.get('command')} ${(h.state.get('args') ?? []).join(' ')}`.trim(),
547
+ status: h.status,
548
+ exitCode: h.exitCode,
549
+ uptimeMs: duration,
550
+ uptime: formatDuration(duration),
551
+ outputLines: h.stdout.totalLines,
552
+ errorLines: h.stderr.totalLines,
553
+ recentOutput: lastStdout,
554
+ }
555
+ }),
556
+ }
557
+ }
558
+
559
+ /**
560
+ * Tool handler: peek at a process's buffered output.
561
+ */
562
+ async getProcessOutput(args: { id?: string; tag?: string; stream?: string }) {
563
+ const handler = args.id ? this.get(args.id) : args.tag ? this.getByTag(args.tag) : undefined
564
+ if (!handler) return { error: `Process not found. Provide a valid id or tag.` }
565
+
566
+ const streamName = (args.stream === 'stderr' ? 'stderr' : 'stdout') as 'stdout' | 'stderr'
567
+ const peek = handler.peek(streamName)
568
+
569
+ return {
570
+ id: handler.id,
571
+ tag: handler.tag,
572
+ status: handler.status,
573
+ stream: streamName,
574
+ totalLines: peek.totalLines,
575
+ droppedLines: peek.droppedLines,
576
+ head: peek.head,
577
+ tail: peek.tail,
578
+ }
579
+ }
580
+
581
+ /**
582
+ * Tool handler: kill a process by ID or tag.
583
+ */
584
+ async killProcess(args: { id?: string; tag?: string; signal?: string }) {
585
+ const handler = args.id ? this.get(args.id) : args.tag ? this.getByTag(args.tag) : undefined
586
+ if (!handler) return { error: `Process not found. Provide a valid id or tag.` }
587
+
588
+ if (handler.isDone) {
589
+ return { id: handler.id, status: handler.status, message: 'Process already finished.' }
590
+ }
591
+
592
+ const signal = (args.signal ?? 'SIGTERM') as NodeJS.Signals
593
+ handler.kill(signal)
594
+
595
+ return { id: handler.id, status: handler.status, signal, message: 'Process killed.' }
596
+ }
597
+
598
+ /**
599
+ * When an assistant uses processManager, inject system prompt guidance
600
+ * about how to manage processes safely and effectively.
601
+ */
602
+ override setupToolsConsumer(consumer: Helper) {
603
+ if (typeof (consumer as any).addSystemPromptExtension === 'function') {
604
+ (consumer as any).addSystemPromptExtension('processManager', [
605
+ '## Process Management',
606
+ '',
607
+ '**Choosing the right tool:**',
608
+ '- `runCommand` — for anything that finishes on its own (builds, tests, installs, queries). Blocks until done.',
609
+ '- `spawnProcess` — for anything that runs indefinitely (servers, watchers, tails). Returns immediately.',
610
+ '- When in doubt: if you\'d press Ctrl-C to stop it, use `spawnProcess`. If you\'d wait for it, use `runCommand`.',
611
+ '',
612
+ '**After spawning a process:**',
613
+ '1. Always assign a descriptive `tag` so you can reference it later',
614
+ '2. Call `getProcessOutput` within a few seconds to verify it started correctly',
615
+ '3. Check `stderr` if the process crashes or output looks wrong',
616
+ '',
617
+ '**Monitoring:**',
618
+ '- Call `listProcesses` to see all running and finished processes at a glance',
619
+ '- Call `getProcessOutput` to read recent output — it keeps the first 20 and last 50 lines',
620
+ '- A process with status "crashed" exited with a non-zero code — check its stderr for the error',
621
+ '',
622
+ '**Cleanup:**',
623
+ '- Always `killProcess` background processes when they\'re no longer needed',
624
+ '- Use SIGTERM (default) first for graceful shutdown. Only use SIGKILL if SIGTERM doesn\'t work.',
625
+ '- If you spawned it, you\'re responsible for killing it',
626
+ ].join('\n'))
627
+ }
628
+ }
629
+
630
+ // ─── Core API ───────────────────────────────────────────────────────────
631
+
632
+ /**
633
+ * Spawn a long-running process and return a handle immediately.
634
+ *
635
+ * The returned SpawnHandler provides events for stdout/stderr streaming,
636
+ * exit/crash notifications, and methods to kill or await the process.
637
+ *
638
+ * @param command - The command to execute (e.g. 'node', 'bun', 'python')
639
+ * @param args - Arguments to pass to the command
640
+ * @param options - Spawn configuration
641
+ * @param options.tag - User-defined tag for later lookups via getByTag()
642
+ * @param options.cwd - Working directory (defaults to container cwd)
643
+ * @param options.env - Additional environment variables
644
+ * @param options.stdin - stdin mode: 'pipe', 'inherit', 'ignore' (default: 'ignore')
645
+ * @param options.stdout - stdout mode: 'pipe', 'inherit', 'ignore' (default: 'pipe')
646
+ * @param options.stderr - stderr mode: 'pipe', 'inherit', 'ignore' (default: 'pipe')
647
+ * @returns SpawnHandler — a non-blocking handle to the process
648
+ */
649
+ spawn(command: string, args: string[] = [], options: SpawnOptions = {}): SpawnHandler {
650
+ const id = crypto.randomUUID()
651
+ const handler = new SpawnHandler(id, command, args, this, options)
652
+
653
+ this._handlers.set(id, handler)
654
+
655
+ // Register cleanup on first spawn
656
+ if (!this._cleanupRegistered && (this.options as ProcessManagerOptions).autoCleanup !== false) {
657
+ this._registerCleanup()
658
+ }
659
+
660
+ handler._start(options)
661
+
662
+ // Update feature-level state
663
+ const totalSpawned = ((this.state.get('totalSpawned' as any) as number) ?? 0) + 1
664
+ this.state.set('totalSpawned' as any, totalSpawned)
665
+
666
+ const processes = { ...(this.state.get('processes' as any) as Record<string, any> ?? {}) }
667
+ processes[id] = {
668
+ id,
669
+ tag: options.tag,
670
+ command,
671
+ args,
672
+ pid: handler.pid,
673
+ status: 'running',
674
+ startedAt: Date.now(),
675
+ }
676
+ this.state.set('processes' as any, processes)
677
+
678
+ this.emit('spawned', id, processes[id])
679
+
680
+ return handler
681
+ }
682
+
683
+ /**
684
+ * Get a SpawnHandler by its unique ID.
685
+ *
686
+ * @param id - The process ID returned by spawn
687
+ * @returns The SpawnHandler, or undefined if not found
688
+ */
689
+ get(id: string): SpawnHandler | undefined {
690
+ return this._handlers.get(id)
691
+ }
692
+
693
+ /**
694
+ * Find a SpawnHandler by its user-defined tag.
695
+ *
696
+ * @param tag - The tag passed to spawn()
697
+ * @returns The first matching SpawnHandler, or undefined
698
+ */
699
+ getByTag(tag: string): SpawnHandler | undefined {
700
+ for (const handler of this._handlers.values()) {
701
+ if (handler.tag === tag) return handler
702
+ }
703
+ return undefined
704
+ }
705
+
706
+ /**
707
+ * List all tracked SpawnHandlers (running and finished).
708
+ *
709
+ * @returns Array of all SpawnHandlers
710
+ */
711
+ list(): SpawnHandler[] {
712
+ return Array.from(this._handlers.values())
713
+ }
714
+
715
+ /**
716
+ * Kill all running processes.
717
+ *
718
+ * @param signal - Signal to send (default: SIGTERM)
719
+ */
720
+ killAll(signal?: NodeJS.Signals | number): void {
721
+ for (const handler of this._handlers.values()) {
722
+ if (handler.isRunning) {
723
+ handler.kill(signal)
724
+ }
725
+ }
726
+ }
727
+
728
+ /**
729
+ * Stop the process manager: kill all running processes and remove cleanup handlers.
730
+ */
731
+ async stop(): Promise<void> {
732
+ this.killAll()
733
+ this._removeCleanup()
734
+ }
735
+
736
+ /**
737
+ * Remove a finished handler from tracking.
738
+ *
739
+ * @param id - The process ID to remove
740
+ * @returns True if the handler was found and removed
741
+ */
742
+ remove(id: string): boolean {
743
+ const handler = this._handlers.get(id)
744
+ if (!handler) return false
745
+ if (handler.isRunning) {
746
+ throw new Error(`Cannot remove running process ${id} — kill it first`)
747
+ }
748
+
749
+ this._handlers.delete(id)
750
+
751
+ const processes = { ...(this.state.get('processes') as any ?? {}) }
752
+ delete processes[id]
753
+ this.state.set('processes' as any, processes)
754
+
755
+ return true
756
+ }
757
+
758
+ override async enable(options: any = {}): Promise<this> {
759
+ await super.enable(options)
760
+ return this
761
+ }
762
+
763
+ // ─── Internal ─────────────────────────────────────────────────────────────
764
+
765
+ /** Called by SpawnHandler when a process finishes. Updates feature-level state. */
766
+ _onHandlerDone(handler: SpawnHandler, status: 'exited' | 'crashed' | 'killed', exitCode?: number): void {
767
+ const id = handler.id
768
+
769
+ // Update feature-level process record
770
+ const processes = { ...(this.state.get('processes') as any ?? {}) }
771
+ if (processes[id]) {
772
+ processes[id] = {
773
+ ...processes[id],
774
+ status,
775
+ exitCode: exitCode ?? (status === 'killed' ? -1 : undefined),
776
+ endedAt: Date.now(),
777
+ }
778
+ this.state.set('processes' as any, processes)
779
+ }
780
+
781
+ // Emit feature-level events
782
+ if (status === 'exited') {
783
+ this.emit('exited', id, exitCode ?? 0)
784
+ } else if (status === 'crashed') {
785
+ this.emit('crashed', id, exitCode ?? 1, { command: handler.state.get('command'), args: handler.state.get('args') })
786
+ } else if (status === 'killed') {
787
+ this.emit('killed', id)
788
+ }
789
+
790
+ // Check if all processes are done
791
+ const allDone = Array.from(this._handlers.values()).every(h => h.isDone)
792
+ if (allDone && this._handlers.size > 0) {
793
+ this.emit('allStopped')
794
+ }
795
+ }
796
+
797
+ private _registerCleanup(): void {
798
+ if (this._cleanupRegistered) return
799
+ this._cleanupRegistered = true
800
+
801
+ const onExit = () => { this.killAll() }
802
+ const onSignal = (signal: NodeJS.Signals) => {
803
+ this.killAll()
804
+ process.removeListener(signal, onSignal as any)
805
+ process.kill(process.pid, signal)
806
+ }
807
+
808
+ const onSigInt = () => onSignal('SIGINT')
809
+ const onSigTerm = () => onSignal('SIGTERM')
810
+
811
+ process.on('exit', onExit)
812
+ process.on('SIGINT', onSigInt)
813
+ process.on('SIGTERM', onSigTerm)
814
+
815
+ this._cleanupHandlers = [
816
+ () => process.removeListener('exit', onExit),
817
+ () => process.removeListener('SIGINT', onSigInt),
818
+ () => process.removeListener('SIGTERM', onSigTerm),
819
+ ]
820
+ }
821
+
822
+ private _removeCleanup(): void {
823
+ for (const remove of this._cleanupHandlers) {
824
+ remove()
825
+ }
826
+ this._cleanupHandlers = []
827
+ this._cleanupRegistered = false
828
+ }
829
+ }
830
+
831
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
832
+
833
+ function formatDuration(ms: number): string {
834
+ const seconds = Math.floor(ms / 1000)
835
+ if (seconds < 60) return `${seconds}s`
836
+ const minutes = Math.floor(seconds / 60)
837
+ const secs = seconds % 60
838
+ if (minutes < 60) return `${minutes}m ${secs}s`
839
+ const hours = Math.floor(minutes / 60)
840
+ const mins = minutes % 60
841
+ return `${hours}h ${mins}m`
842
+ }
843
+
844
+ export default ProcessManager