@edwardlee5423/magi 0.1.0-alpha.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 (531) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +184 -0
  3. package/dist/agent/headless-agent.d.ts +15 -0
  4. package/dist/agent/headless-agent.js +105 -0
  5. package/dist/agent/headless-agent.js.map +1 -0
  6. package/dist/agent/local-plan.d.ts +20 -0
  7. package/dist/agent/local-plan.js +30 -0
  8. package/dist/agent/local-plan.js.map +1 -0
  9. package/dist/agent/query-engine.d.ts +71 -0
  10. package/dist/agent/query-engine.js +938 -0
  11. package/dist/agent/query-engine.js.map +1 -0
  12. package/dist/agent/query.d.ts +128 -0
  13. package/dist/agent/query.js +674 -0
  14. package/dist/agent/query.js.map +1 -0
  15. package/dist/agent/system-prompt.d.ts +12 -0
  16. package/dist/agent/system-prompt.js +104 -0
  17. package/dist/agent/system-prompt.js.map +1 -0
  18. package/dist/agent/tools.d.ts +71 -0
  19. package/dist/agent/tools.js +37 -0
  20. package/dist/agent/tools.js.map +1 -0
  21. package/dist/agents/roles.d.ts +9 -0
  22. package/dist/agents/roles.js +18 -0
  23. package/dist/agents/roles.js.map +1 -0
  24. package/dist/agents/task-queue.d.ts +13 -0
  25. package/dist/agents/task-queue.js +50 -0
  26. package/dist/agents/task-queue.js.map +1 -0
  27. package/dist/cli.d.ts +7 -0
  28. package/dist/cli.js +1540 -0
  29. package/dist/cli.js.map +1 -0
  30. package/dist/colors.d.ts +20 -0
  31. package/dist/colors.js +92 -0
  32. package/dist/colors.js.map +1 -0
  33. package/dist/commands/agents.d.ts +9 -0
  34. package/dist/commands/agents.js +37 -0
  35. package/dist/commands/agents.js.map +1 -0
  36. package/dist/commands/clear.d.ts +9 -0
  37. package/dist/commands/clear.js +20 -0
  38. package/dist/commands/clear.js.map +1 -0
  39. package/dist/commands/commit.d.ts +8 -0
  40. package/dist/commands/commit.js +35 -0
  41. package/dist/commands/commit.js.map +1 -0
  42. package/dist/commands/compact.d.ts +8 -0
  43. package/dist/commands/compact.js +29 -0
  44. package/dist/commands/compact.js.map +1 -0
  45. package/dist/commands/copy.d.ts +8 -0
  46. package/dist/commands/copy.js +49 -0
  47. package/dist/commands/copy.js.map +1 -0
  48. package/dist/commands/cost.d.ts +8 -0
  49. package/dist/commands/cost.js +94 -0
  50. package/dist/commands/cost.js.map +1 -0
  51. package/dist/commands/diff.d.ts +8 -0
  52. package/dist/commands/diff.js +38 -0
  53. package/dist/commands/diff.js.map +1 -0
  54. package/dist/commands/doctor.d.ts +8 -0
  55. package/dist/commands/doctor.js +23 -0
  56. package/dist/commands/doctor.js.map +1 -0
  57. package/dist/commands/export.d.ts +8 -0
  58. package/dist/commands/export.js +42 -0
  59. package/dist/commands/export.js.map +1 -0
  60. package/dist/commands/fork.d.ts +8 -0
  61. package/dist/commands/fork.js +20 -0
  62. package/dist/commands/fork.js.map +1 -0
  63. package/dist/commands/goal.d.ts +9 -0
  64. package/dist/commands/goal.js +54 -0
  65. package/dist/commands/goal.js.map +1 -0
  66. package/dist/commands/help.d.ts +8 -0
  67. package/dist/commands/help.js +65 -0
  68. package/dist/commands/help.js.map +1 -0
  69. package/dist/commands/hooks.d.ts +8 -0
  70. package/dist/commands/hooks.js +56 -0
  71. package/dist/commands/hooks.js.map +1 -0
  72. package/dist/commands/image.d.ts +17 -0
  73. package/dist/commands/image.js +74 -0
  74. package/dist/commands/image.js.map +1 -0
  75. package/dist/commands/init.d.ts +26 -0
  76. package/dist/commands/init.js +214 -0
  77. package/dist/commands/init.js.map +1 -0
  78. package/dist/commands/mcp.d.ts +8 -0
  79. package/dist/commands/mcp.js +217 -0
  80. package/dist/commands/mcp.js.map +1 -0
  81. package/dist/commands/memory.d.ts +8 -0
  82. package/dist/commands/memory.js +79 -0
  83. package/dist/commands/memory.js.map +1 -0
  84. package/dist/commands/model.d.ts +8 -0
  85. package/dist/commands/model.js +21 -0
  86. package/dist/commands/model.js.map +1 -0
  87. package/dist/commands/permissions.d.ts +9 -0
  88. package/dist/commands/permissions.js +30 -0
  89. package/dist/commands/permissions.js.map +1 -0
  90. package/dist/commands/proactive.d.ts +9 -0
  91. package/dist/commands/proactive.js +24 -0
  92. package/dist/commands/proactive.js.map +1 -0
  93. package/dist/commands/register-all.d.ts +1 -0
  94. package/dist/commands/register-all.js +68 -0
  95. package/dist/commands/register-all.js.map +1 -0
  96. package/dist/commands/registry.d.ts +64 -0
  97. package/dist/commands/registry.js +235 -0
  98. package/dist/commands/registry.js.map +1 -0
  99. package/dist/commands/rename.d.ts +8 -0
  100. package/dist/commands/rename.js +16 -0
  101. package/dist/commands/rename.js.map +1 -0
  102. package/dist/commands/review.d.ts +8 -0
  103. package/dist/commands/review.js +13 -0
  104. package/dist/commands/review.js.map +1 -0
  105. package/dist/commands/rewind.d.ts +8 -0
  106. package/dist/commands/rewind.js +45 -0
  107. package/dist/commands/rewind.js.map +1 -0
  108. package/dist/commands/route.d.ts +9 -0
  109. package/dist/commands/route.js +83 -0
  110. package/dist/commands/route.js.map +1 -0
  111. package/dist/commands/sessions.d.ts +9 -0
  112. package/dist/commands/sessions.js +25 -0
  113. package/dist/commands/sessions.js.map +1 -0
  114. package/dist/commands/skill.d.ts +9 -0
  115. package/dist/commands/skill.js +46 -0
  116. package/dist/commands/skill.js.map +1 -0
  117. package/dist/commands/status.d.ts +8 -0
  118. package/dist/commands/status.js +41 -0
  119. package/dist/commands/status.js.map +1 -0
  120. package/dist/commands/summary.d.ts +8 -0
  121. package/dist/commands/summary.js +39 -0
  122. package/dist/commands/summary.js.map +1 -0
  123. package/dist/commands/tasks.d.ts +9 -0
  124. package/dist/commands/tasks.js +70 -0
  125. package/dist/commands/tasks.js.map +1 -0
  126. package/dist/commands/todos.d.ts +9 -0
  127. package/dist/commands/todos.js +27 -0
  128. package/dist/commands/todos.js.map +1 -0
  129. package/dist/commands/tutorial.d.ts +8 -0
  130. package/dist/commands/tutorial.js +159 -0
  131. package/dist/commands/tutorial.js.map +1 -0
  132. package/dist/commands/upgrade.d.ts +9 -0
  133. package/dist/commands/upgrade.js +73 -0
  134. package/dist/commands/upgrade.js.map +1 -0
  135. package/dist/commands/usage.d.ts +8 -0
  136. package/dist/commands/usage.js +54 -0
  137. package/dist/commands/usage.js.map +1 -0
  138. package/dist/commands/vim.d.ts +10 -0
  139. package/dist/commands/vim.js +34 -0
  140. package/dist/commands/vim.js.map +1 -0
  141. package/dist/config.d.ts +104 -0
  142. package/dist/config.js +533 -0
  143. package/dist/config.js.map +1 -0
  144. package/dist/context/compaction.d.ts +66 -0
  145. package/dist/context/compaction.js +353 -0
  146. package/dist/context/compaction.js.map +1 -0
  147. package/dist/context/layers.d.ts +32 -0
  148. package/dist/context/layers.js +114 -0
  149. package/dist/context/layers.js.map +1 -0
  150. package/dist/context/token-budget.d.ts +21 -0
  151. package/dist/context/token-budget.js +70 -0
  152. package/dist/context/token-budget.js.map +1 -0
  153. package/dist/control/auth.d.ts +17 -0
  154. package/dist/control/auth.js +31 -0
  155. package/dist/control/auth.js.map +1 -0
  156. package/dist/control/daemon.d.ts +38 -0
  157. package/dist/control/daemon.js +135 -0
  158. package/dist/control/daemon.js.map +1 -0
  159. package/dist/control/mdns.d.ts +51 -0
  160. package/dist/control/mdns.js +344 -0
  161. package/dist/control/mdns.js.map +1 -0
  162. package/dist/control/peer-client.d.ts +49 -0
  163. package/dist/control/peer-client.js +170 -0
  164. package/dist/control/peer-client.js.map +1 -0
  165. package/dist/control/server.d.ts +30 -0
  166. package/dist/control/server.js +887 -0
  167. package/dist/control/server.js.map +1 -0
  168. package/dist/cost.d.ts +23 -0
  169. package/dist/cost.js +80 -0
  170. package/dist/cost.js.map +1 -0
  171. package/dist/doctor.d.ts +8 -0
  172. package/dist/doctor.js +22 -0
  173. package/dist/doctor.js.map +1 -0
  174. package/dist/env.d.ts +7 -0
  175. package/dist/env.js +70 -0
  176. package/dist/env.js.map +1 -0
  177. package/dist/errors.d.ts +6 -0
  178. package/dist/errors.js +13 -0
  179. package/dist/errors.js.map +1 -0
  180. package/dist/events.d.ts +19 -0
  181. package/dist/events.js +236 -0
  182. package/dist/events.js.map +1 -0
  183. package/dist/fs-utils.d.ts +21 -0
  184. package/dist/fs-utils.js +48 -0
  185. package/dist/fs-utils.js.map +1 -0
  186. package/dist/goal.d.ts +32 -0
  187. package/dist/goal.js +172 -0
  188. package/dist/goal.js.map +1 -0
  189. package/dist/headless.d.ts +38 -0
  190. package/dist/headless.js +547 -0
  191. package/dist/headless.js.map +1 -0
  192. package/dist/history.d.ts +8 -0
  193. package/dist/history.js +77 -0
  194. package/dist/history.js.map +1 -0
  195. package/dist/hooks/events.d.ts +14 -0
  196. package/dist/hooks/events.js +37 -0
  197. package/dist/hooks/events.js.map +1 -0
  198. package/dist/hooks/runner.d.ts +73 -0
  199. package/dist/hooks/runner.js +256 -0
  200. package/dist/hooks/runner.js.map +1 -0
  201. package/dist/hooks/trigger.d.ts +9 -0
  202. package/dist/hooks/trigger.js +18 -0
  203. package/dist/hooks/trigger.js.map +1 -0
  204. package/dist/index.d.ts +61 -0
  205. package/dist/index.js +62 -0
  206. package/dist/index.js.map +1 -0
  207. package/dist/interactions.d.ts +102 -0
  208. package/dist/interactions.js +250 -0
  209. package/dist/interactions.js.map +1 -0
  210. package/dist/logger.d.ts +32 -0
  211. package/dist/logger.js +115 -0
  212. package/dist/logger.js.map +1 -0
  213. package/dist/markdown.d.ts +18 -0
  214. package/dist/markdown.js +293 -0
  215. package/dist/markdown.js.map +1 -0
  216. package/dist/mcp/approval.d.ts +9 -0
  217. package/dist/mcp/approval.js +27 -0
  218. package/dist/mcp/approval.js.map +1 -0
  219. package/dist/mcp/client.d.ts +61 -0
  220. package/dist/mcp/client.js +276 -0
  221. package/dist/mcp/client.js.map +1 -0
  222. package/dist/mcp/connection-manager.d.ts +30 -0
  223. package/dist/mcp/connection-manager.js +126 -0
  224. package/dist/mcp/connection-manager.js.map +1 -0
  225. package/dist/mcp/oauth-callback.d.ts +23 -0
  226. package/dist/mcp/oauth-callback.js +108 -0
  227. package/dist/mcp/oauth-callback.js.map +1 -0
  228. package/dist/mcp/oauth-flow.d.ts +45 -0
  229. package/dist/mcp/oauth-flow.js +145 -0
  230. package/dist/mcp/oauth-flow.js.map +1 -0
  231. package/dist/mcp/oauth.d.ts +82 -0
  232. package/dist/mcp/oauth.js +169 -0
  233. package/dist/mcp/oauth.js.map +1 -0
  234. package/dist/mcp/tool-registry.d.ts +29 -0
  235. package/dist/mcp/tool-registry.js +207 -0
  236. package/dist/mcp/tool-registry.js.map +1 -0
  237. package/dist/mcp/transport.d.ts +33 -0
  238. package/dist/mcp/transport.js +493 -0
  239. package/dist/mcp/transport.js.map +1 -0
  240. package/dist/mcp/types.d.ts +57 -0
  241. package/dist/mcp/types.js +2 -0
  242. package/dist/mcp/types.js.map +1 -0
  243. package/dist/memdir.d.ts +58 -0
  244. package/dist/memdir.js +219 -0
  245. package/dist/memdir.js.map +1 -0
  246. package/dist/memory-selection.d.ts +30 -0
  247. package/dist/memory-selection.js +102 -0
  248. package/dist/memory-selection.js.map +1 -0
  249. package/dist/memory.d.ts +85 -0
  250. package/dist/memory.js +259 -0
  251. package/dist/memory.js.map +1 -0
  252. package/dist/paths.d.ts +25 -0
  253. package/dist/paths.js +99 -0
  254. package/dist/paths.js.map +1 -0
  255. package/dist/permissions.d.ts +17 -0
  256. package/dist/permissions.js +72 -0
  257. package/dist/permissions.js.map +1 -0
  258. package/dist/plugins/manifest.d.ts +17 -0
  259. package/dist/plugins/manifest.js +98 -0
  260. package/dist/plugins/manifest.js.map +1 -0
  261. package/dist/plugins/marketplace.d.ts +22 -0
  262. package/dist/plugins/marketplace.js +98 -0
  263. package/dist/plugins/marketplace.js.map +1 -0
  264. package/dist/proactive.d.ts +15 -0
  265. package/dist/proactive.js +49 -0
  266. package/dist/proactive.js.map +1 -0
  267. package/dist/providers/errors.d.ts +14 -0
  268. package/dist/providers/errors.js +66 -0
  269. package/dist/providers/errors.js.map +1 -0
  270. package/dist/providers/format-proxy.d.ts +100 -0
  271. package/dist/providers/format-proxy.js +235 -0
  272. package/dist/providers/format-proxy.js.map +1 -0
  273. package/dist/providers/http.d.ts +4 -0
  274. package/dist/providers/http.js +21 -0
  275. package/dist/providers/http.js.map +1 -0
  276. package/dist/providers/ir.d.ts +84 -0
  277. package/dist/providers/ir.js +76 -0
  278. package/dist/providers/ir.js.map +1 -0
  279. package/dist/providers/messages-compatible.d.ts +19 -0
  280. package/dist/providers/messages-compatible.js +592 -0
  281. package/dist/providers/messages-compatible.js.map +1 -0
  282. package/dist/providers/openai.d.ts +18 -0
  283. package/dist/providers/openai.js +430 -0
  284. package/dist/providers/openai.js.map +1 -0
  285. package/dist/providers/registry.d.ts +10 -0
  286. package/dist/providers/registry.js +16 -0
  287. package/dist/providers/registry.js.map +1 -0
  288. package/dist/providers/sse.d.ts +6 -0
  289. package/dist/providers/sse.js +104 -0
  290. package/dist/providers/sse.js.map +1 -0
  291. package/dist/routing/model-alias.d.ts +8 -0
  292. package/dist/routing/model-alias.js +23 -0
  293. package/dist/routing/model-alias.js.map +1 -0
  294. package/dist/routing/model-router.d.ts +55 -0
  295. package/dist/routing/model-router.js +233 -0
  296. package/dist/routing/model-router.js.map +1 -0
  297. package/dist/routing/router.d.ts +26 -0
  298. package/dist/routing/router.js +63 -0
  299. package/dist/routing/router.js.map +1 -0
  300. package/dist/rules/agents-loader.d.ts +12 -0
  301. package/dist/rules/agents-loader.js +61 -0
  302. package/dist/rules/agents-loader.js.map +1 -0
  303. package/dist/runner/client.d.ts +67 -0
  304. package/dist/runner/client.js +175 -0
  305. package/dist/runner/client.js.map +1 -0
  306. package/dist/session-store.d.ts +250 -0
  307. package/dist/session-store.js +629 -0
  308. package/dist/session-store.js.map +1 -0
  309. package/dist/skills/bundled.d.ts +14 -0
  310. package/dist/skills/bundled.js +148 -0
  311. package/dist/skills/bundled.js.map +1 -0
  312. package/dist/skills/loader.d.ts +11 -0
  313. package/dist/skills/loader.js +73 -0
  314. package/dist/skills/loader.js.map +1 -0
  315. package/dist/slash-menu.d.ts +14 -0
  316. package/dist/slash-menu.js +166 -0
  317. package/dist/slash-menu.js.map +1 -0
  318. package/dist/slash.d.ts +75 -0
  319. package/dist/slash.js +126 -0
  320. package/dist/slash.js.map +1 -0
  321. package/dist/spinner.d.ts +27 -0
  322. package/dist/spinner.js +68 -0
  323. package/dist/spinner.js.map +1 -0
  324. package/dist/ssh/exec.d.ts +20 -0
  325. package/dist/ssh/exec.js +39 -0
  326. package/dist/ssh/exec.js.map +1 -0
  327. package/dist/ssh/file.d.ts +32 -0
  328. package/dist/ssh/file.js +60 -0
  329. package/dist/ssh/file.js.map +1 -0
  330. package/dist/syntax-highlight.d.ts +19 -0
  331. package/dist/syntax-highlight.js +181 -0
  332. package/dist/syntax-highlight.js.map +1 -0
  333. package/dist/tools/agent-tool.d.ts +53 -0
  334. package/dist/tools/agent-tool.js +62 -0
  335. package/dist/tools/agent-tool.js.map +1 -0
  336. package/dist/tools/archive-create.d.ts +35 -0
  337. package/dist/tools/archive-create.js +37 -0
  338. package/dist/tools/archive-create.js.map +1 -0
  339. package/dist/tools/archive-extract.d.ts +28 -0
  340. package/dist/tools/archive-extract.js +36 -0
  341. package/dist/tools/archive-extract.js.map +1 -0
  342. package/dist/tools/base64.d.ts +27 -0
  343. package/dist/tools/base64.js +19 -0
  344. package/dist/tools/base64.js.map +1 -0
  345. package/dist/tools/browser.d.ts +125 -0
  346. package/dist/tools/browser.js +241 -0
  347. package/dist/tools/browser.js.map +1 -0
  348. package/dist/tools/config-tool.d.ts +31 -0
  349. package/dist/tools/config-tool.js +167 -0
  350. package/dist/tools/config-tool.js.map +1 -0
  351. package/dist/tools/cron.d.ts +110 -0
  352. package/dist/tools/cron.js +299 -0
  353. package/dist/tools/cron.js.map +1 -0
  354. package/dist/tools/date.d.ts +24 -0
  355. package/dist/tools/date.js +26 -0
  356. package/dist/tools/date.js.map +1 -0
  357. package/dist/tools/dir-create.d.ts +21 -0
  358. package/dist/tools/dir-create.js +19 -0
  359. package/dist/tools/dir-create.js.map +1 -0
  360. package/dist/tools/dir-list.d.ts +36 -0
  361. package/dist/tools/dir-list.js +47 -0
  362. package/dist/tools/dir-list.js.map +1 -0
  363. package/dist/tools/disk-usage.d.ts +30 -0
  364. package/dist/tools/disk-usage.js +22 -0
  365. package/dist/tools/disk-usage.js.map +1 -0
  366. package/dist/tools/download-file.d.ts +28 -0
  367. package/dist/tools/download-file.js +42 -0
  368. package/dist/tools/download-file.js.map +1 -0
  369. package/dist/tools/environment.d.ts +31 -0
  370. package/dist/tools/environment.js +33 -0
  371. package/dist/tools/environment.js.map +1 -0
  372. package/dist/tools/errors.d.ts +5 -0
  373. package/dist/tools/errors.js +9 -0
  374. package/dist/tools/errors.js.map +1 -0
  375. package/dist/tools/file-copy.d.ts +34 -0
  376. package/dist/tools/file-copy.js +45 -0
  377. package/dist/tools/file-copy.js.map +1 -0
  378. package/dist/tools/file-delete.d.ts +34 -0
  379. package/dist/tools/file-delete.js +42 -0
  380. package/dist/tools/file-delete.js.map +1 -0
  381. package/dist/tools/file-find.d.ts +46 -0
  382. package/dist/tools/file-find.js +33 -0
  383. package/dist/tools/file-find.js.map +1 -0
  384. package/dist/tools/file-move.d.ts +33 -0
  385. package/dist/tools/file-move.js +32 -0
  386. package/dist/tools/file-move.js.map +1 -0
  387. package/dist/tools/files.d.ts +38 -0
  388. package/dist/tools/files.js +123 -0
  389. package/dist/tools/files.js.map +1 -0
  390. package/dist/tools/git-branch-delete.d.ts +27 -0
  391. package/dist/tools/git-branch-delete.js +25 -0
  392. package/dist/tools/git-branch-delete.js.map +1 -0
  393. package/dist/tools/git-reset.d.ts +30 -0
  394. package/dist/tools/git-reset.js +28 -0
  395. package/dist/tools/git-reset.js.map +1 -0
  396. package/dist/tools/git-stash.d.ts +28 -0
  397. package/dist/tools/git-stash.js +24 -0
  398. package/dist/tools/git-stash.js.map +1 -0
  399. package/dist/tools/git.d.ts +77 -0
  400. package/dist/tools/git.js +300 -0
  401. package/dist/tools/git.js.map +1 -0
  402. package/dist/tools/github.d.ts +68 -0
  403. package/dist/tools/github.js +77 -0
  404. package/dist/tools/github.js.map +1 -0
  405. package/dist/tools/head-tail.d.ts +34 -0
  406. package/dist/tools/head-tail.js +23 -0
  407. package/dist/tools/head-tail.js.map +1 -0
  408. package/dist/tools/http-request.d.ts +44 -0
  409. package/dist/tools/http-request.js +57 -0
  410. package/dist/tools/http-request.js.map +1 -0
  411. package/dist/tools/json-query.d.ts +27 -0
  412. package/dist/tools/json-query.js +90 -0
  413. package/dist/tools/json-query.js.map +1 -0
  414. package/dist/tools/kill-process.d.ts +32 -0
  415. package/dist/tools/kill-process.js +51 -0
  416. package/dist/tools/kill-process.js.map +1 -0
  417. package/dist/tools/lsp.d.ts +98 -0
  418. package/dist/tools/lsp.js +398 -0
  419. package/dist/tools/lsp.js.map +1 -0
  420. package/dist/tools/monitor.d.ts +29 -0
  421. package/dist/tools/monitor.js +78 -0
  422. package/dist/tools/monitor.js.map +1 -0
  423. package/dist/tools/network-check.d.ts +34 -0
  424. package/dist/tools/network-check.js +57 -0
  425. package/dist/tools/network-check.js.map +1 -0
  426. package/dist/tools/notebook.d.ts +63 -0
  427. package/dist/tools/notebook.js +158 -0
  428. package/dist/tools/notebook.js.map +1 -0
  429. package/dist/tools/plan-mode.d.ts +48 -0
  430. package/dist/tools/plan-mode.js +57 -0
  431. package/dist/tools/plan-mode.js.map +1 -0
  432. package/dist/tools/process-list.d.ts +35 -0
  433. package/dist/tools/process-list.js +42 -0
  434. package/dist/tools/process-list.js.map +1 -0
  435. package/dist/tools/registry.d.ts +132 -0
  436. package/dist/tools/registry.js +2262 -0
  437. package/dist/tools/registry.js.map +1 -0
  438. package/dist/tools/search.d.ts +38 -0
  439. package/dist/tools/search.js +327 -0
  440. package/dist/tools/search.js.map +1 -0
  441. package/dist/tools/shell.d.ts +18 -0
  442. package/dist/tools/shell.js +249 -0
  443. package/dist/tools/shell.js.map +1 -0
  444. package/dist/tools/skill-tool.d.ts +22 -0
  445. package/dist/tools/skill-tool.js +66 -0
  446. package/dist/tools/skill-tool.js.map +1 -0
  447. package/dist/tools/sleep.d.ts +16 -0
  448. package/dist/tools/sleep.js +24 -0
  449. package/dist/tools/sleep.js.map +1 -0
  450. package/dist/tools/snip.d.ts +26 -0
  451. package/dist/tools/snip.js +95 -0
  452. package/dist/tools/snip.js.map +1 -0
  453. package/dist/tools/system-info.d.ts +17 -0
  454. package/dist/tools/system-info.js +33 -0
  455. package/dist/tools/system-info.js.map +1 -0
  456. package/dist/tools/tasks.d.ts +191 -0
  457. package/dist/tools/tasks.js +348 -0
  458. package/dist/tools/tasks.js.map +1 -0
  459. package/dist/tools/text-stats.d.ts +25 -0
  460. package/dist/tools/text-stats.js +25 -0
  461. package/dist/tools/text-stats.js.map +1 -0
  462. package/dist/tools/todo.d.ts +69 -0
  463. package/dist/tools/todo.js +197 -0
  464. package/dist/tools/todo.js.map +1 -0
  465. package/dist/tools/tool-search.d.ts +29 -0
  466. package/dist/tools/tool-search.js +114 -0
  467. package/dist/tools/tool-search.js.map +1 -0
  468. package/dist/tools/tree-view.d.ts +34 -0
  469. package/dist/tools/tree-view.js +39 -0
  470. package/dist/tools/tree-view.js.map +1 -0
  471. package/dist/tools/user-message.d.ts +39 -0
  472. package/dist/tools/user-message.js +70 -0
  473. package/dist/tools/user-message.js.map +1 -0
  474. package/dist/tools/user-question.d.ts +88 -0
  475. package/dist/tools/user-question.js +236 -0
  476. package/dist/tools/user-question.js.map +1 -0
  477. package/dist/tools/web-browser.d.ts +53 -0
  478. package/dist/tools/web-browser.js +373 -0
  479. package/dist/tools/web-browser.js.map +1 -0
  480. package/dist/tools/web-fetch.d.ts +22 -0
  481. package/dist/tools/web-fetch.js +146 -0
  482. package/dist/tools/web-fetch.js.map +1 -0
  483. package/dist/tools/web-search.d.ts +51 -0
  484. package/dist/tools/web-search.js +198 -0
  485. package/dist/tools/web-search.js.map +1 -0
  486. package/dist/tools/which.d.ts +22 -0
  487. package/dist/tools/which.js +21 -0
  488. package/dist/tools/which.js.map +1 -0
  489. package/dist/tools/whoami.d.ts +17 -0
  490. package/dist/tools/whoami.js +24 -0
  491. package/dist/tools/whoami.js.map +1 -0
  492. package/dist/tools/workspace-diagnostics.d.ts +62 -0
  493. package/dist/tools/workspace-diagnostics.js +421 -0
  494. package/dist/tools/workspace-diagnostics.js.map +1 -0
  495. package/dist/tools/workspace.d.ts +14 -0
  496. package/dist/tools/workspace.js +65 -0
  497. package/dist/tools/workspace.js.map +1 -0
  498. package/dist/tools/worktree.d.ts +70 -0
  499. package/dist/tools/worktree.js +150 -0
  500. package/dist/tools/worktree.js.map +1 -0
  501. package/dist/tui/interactions.d.ts +25 -0
  502. package/dist/tui/interactions.js +232 -0
  503. package/dist/tui/interactions.js.map +1 -0
  504. package/dist/tui/multiline-input.d.ts +14 -0
  505. package/dist/tui/multiline-input.js +550 -0
  506. package/dist/tui/multiline-input.js.map +1 -0
  507. package/dist/tui/paste.d.ts +40 -0
  508. package/dist/tui/paste.js +113 -0
  509. package/dist/tui/paste.js.map +1 -0
  510. package/dist/tui/prompt-reader.d.ts +26 -0
  511. package/dist/tui/prompt-reader.js +553 -0
  512. package/dist/tui/prompt-reader.js.map +1 -0
  513. package/dist/tui/transcript.d.ts +34 -0
  514. package/dist/tui/transcript.js +275 -0
  515. package/dist/tui/transcript.js.map +1 -0
  516. package/dist/tui.d.ts +53 -0
  517. package/dist/tui.js +669 -0
  518. package/dist/tui.js.map +1 -0
  519. package/dist/version.d.ts +1 -0
  520. package/dist/version.js +2 -0
  521. package/dist/version.js.map +1 -0
  522. package/dist/vim/lineEditor.d.ts +18 -0
  523. package/dist/vim/lineEditor.js +420 -0
  524. package/dist/vim/lineEditor.js.map +1 -0
  525. package/dist/web/panel-html.d.ts +1 -0
  526. package/dist/web/panel-html.js +532 -0
  527. package/dist/web/panel-html.js.map +1 -0
  528. package/dist/web/panel.d.ts +4 -0
  529. package/dist/web/panel.js +121 -0
  530. package/dist/web/panel.js.map +1 -0
  531. package/package.json +42 -0
@@ -0,0 +1,887 @@
1
+ import http from "node:http";
2
+ import { randomUUID } from "node:crypto";
3
+ import { runHeadlessPrompt } from "../headless.js";
4
+ import { createPairingToken, validateDeviceToken } from "./auth.js";
5
+ import { advertiseMdns, getLocalHostname } from "./mdns.js";
6
+ import { ActiveInteractionCancelledError, ActiveInteractionNotFoundError, ActiveInteractionRegistry, ActiveInteractionStateError } from "../interactions.js";
7
+ import { cancelAgentTask, completeAgentTask, spawnAgentTask, startAgentTask, waitAgentTask } from "../agents/task-queue.js";
8
+ import { listLocalPlugins } from "../plugins/manifest.js";
9
+ import { discoverLocalMarketplaceSources, loadMarketplace } from "../plugins/marketplace.js";
10
+ import { listSkills } from "../skills/loader.js";
11
+ import { openApiDocument, renderPanelClient, renderWebPanel } from "../web/panel.js";
12
+ import { triggerHooks } from "../hooks/events.js";
13
+ import { cronStorePathFromRoot, takeDueCronJobs } from "../tools/cron.js";
14
+ import { toEventView } from "../events.js";
15
+ import { normalizeAskUserQuestionAnswer } from "../tools/user-question.js";
16
+ export async function startControlServer(input) {
17
+ const interactions = new ActiveInteractionRegistry({
18
+ timeoutMs: parseInteractionTimeoutMs(input.env?.MAGI_INTERACTION_TIMEOUT_MS)
19
+ });
20
+ const runningJobs = new Map();
21
+ const server = http.createServer((request, response) => {
22
+ void handleRequest({ ...input, interactions, runningJobs, request, response });
23
+ });
24
+ await new Promise((resolve, reject) => {
25
+ server.once("error", (err) => {
26
+ if (err.code === "EADDRINUSE") {
27
+ reject(new Error([
28
+ `Cannot start control server: port ${input.runtime.controlPort} is already in use on ${input.runtime.controlBind}.`,
29
+ ``,
30
+ `Common fixes:`,
31
+ ` - 'magi daemon status' — see if Magi is already running`,
32
+ ` - 'lsof -i :${input.runtime.controlPort}' — find what's using the port`,
33
+ ` - 'MAGI_CONTROL_PORT=8780 magi serve' — pick a different port`
34
+ ].join("\n")));
35
+ return;
36
+ }
37
+ if (err.code === "EACCES") {
38
+ reject(new Error(`Cannot bind to ${input.runtime.controlBind}:${input.runtime.controlPort} — permission denied. Pick a port above 1024 or run with elevated privileges.`));
39
+ return;
40
+ }
41
+ reject(err);
42
+ });
43
+ server.listen(input.runtime.controlPort, input.runtime.controlBind, () => resolve());
44
+ });
45
+ const address = server.address();
46
+ const cronRunner = startCronRunner(input);
47
+ // Advertise this daemon via mDNS so phones and other Magi instances can discover it.
48
+ let mdnsHandle;
49
+ if (input.env?.MAGI_DISABLE_MDNS !== "1") {
50
+ try {
51
+ const hostname = getLocalHostname();
52
+ const instanceName = `magi-${address.port}-${process.pid}`;
53
+ mdnsHandle = advertiseMdns({
54
+ hostname,
55
+ instanceName,
56
+ port: address.port,
57
+ txt: {
58
+ version: "0.1.0-alpha.0",
59
+ cwd: input.cwd,
60
+ bind: input.runtime.controlBind
61
+ }
62
+ });
63
+ if (input.env?.MAGI_DEBUG_MDNS === "1") {
64
+ process.stdout.write(`[mdns] Advertising ${instanceName} on _magi._tcp.local. (port ${address.port})\n`);
65
+ }
66
+ }
67
+ catch (error) {
68
+ if (input.env?.MAGI_DEBUG_MDNS === "1") {
69
+ process.stdout.write(`[mdns] Failed to advertise: ${error instanceof Error ? error.message : String(error)}\n`);
70
+ }
71
+ }
72
+ }
73
+ return {
74
+ server,
75
+ url: `http://${input.runtime.controlBind}:${address.port}`,
76
+ interactions,
77
+ close: async () => {
78
+ mdnsHandle?.stop();
79
+ cronRunner.close();
80
+ for (const running of runningJobs.values()) {
81
+ running.controller.abort("control server closing");
82
+ }
83
+ await Promise.allSettled([...runningJobs.values()].map((running) => running.promise));
84
+ interactions.close();
85
+ await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
86
+ }
87
+ };
88
+ }
89
+ export function runDueCronJobs(input) {
90
+ const due = takeDueCronJobs(cronStorePathFromRoot(input.paths.stateRoot), input.now ?? new Date());
91
+ return Promise.all(due.map(async ({ job, prompt }) => {
92
+ const result = await runHeadlessPrompt({
93
+ prompt,
94
+ cwd: input.cwd,
95
+ store: input.store,
96
+ config: input.config,
97
+ env: input.env,
98
+ paths: input.paths,
99
+ stateRoot: input.paths.stateRoot,
100
+ modelAlias: "main",
101
+ sessionName: `cron ${job.id}`
102
+ });
103
+ input.store.recordAudit({
104
+ sessionId: result.sessionId,
105
+ jobId: result.jobId,
106
+ action: "cron.job.executed",
107
+ target: job.id,
108
+ metadata: {
109
+ cron: job.cron,
110
+ prompt,
111
+ recurring: job.recurring,
112
+ nextRunAt: job.nextRunAt
113
+ }
114
+ });
115
+ return { cronJob: job, result };
116
+ }));
117
+ }
118
+ function startCronRunner(input) {
119
+ const intervalMs = parseCronIntervalMs(input.env?.MAGI_CRON_POLL_MS);
120
+ let running = false;
121
+ const tick = async () => {
122
+ if (running) {
123
+ return;
124
+ }
125
+ running = true;
126
+ try {
127
+ await runDueCronJobs(input);
128
+ }
129
+ catch (error) {
130
+ const sessionId = input.store.createSession({
131
+ title: "cron runner error",
132
+ cwd: input.cwd,
133
+ metadata: { source: "cron-runner" }
134
+ });
135
+ input.store.recordAudit({
136
+ sessionId,
137
+ action: "cron.runner.failed",
138
+ metadata: { error: error instanceof Error ? error.message : String(error) }
139
+ });
140
+ }
141
+ finally {
142
+ running = false;
143
+ }
144
+ };
145
+ const timer = setInterval(() => {
146
+ void tick();
147
+ }, intervalMs);
148
+ timer.unref?.();
149
+ void tick();
150
+ return {
151
+ close: () => clearInterval(timer)
152
+ };
153
+ }
154
+ function parseCronIntervalMs(raw) {
155
+ if (!raw) {
156
+ return 60_000;
157
+ }
158
+ if (!/^\d+$/.test(raw)) {
159
+ throw new Error(`MAGI_CRON_POLL_MS must be an integer >= 1000, got ${JSON.stringify(raw)}`);
160
+ }
161
+ const interval = Number(raw);
162
+ if (!Number.isInteger(interval) || interval < 1000) {
163
+ throw new Error(`MAGI_CRON_POLL_MS must be an integer >= 1000, got ${JSON.stringify(raw)}`);
164
+ }
165
+ return interval;
166
+ }
167
+ function parseInteractionTimeoutMs(raw) {
168
+ if (!raw) {
169
+ return undefined;
170
+ }
171
+ if (!/^\d+$/.test(raw)) {
172
+ throw new Error(`MAGI_INTERACTION_TIMEOUT_MS must be an integer >= 1, got ${JSON.stringify(raw)}`);
173
+ }
174
+ const timeout = Number(raw);
175
+ if (!Number.isInteger(timeout) || timeout < 1) {
176
+ throw new Error(`MAGI_INTERACTION_TIMEOUT_MS must be an integer >= 1, got ${JSON.stringify(raw)}`);
177
+ }
178
+ return timeout;
179
+ }
180
+ async function handleRequest(input) {
181
+ try {
182
+ const url = new URL(input.request.url ?? "/", `http://${input.request.headers.host ?? "127.0.0.1"}`);
183
+ if (input.request.method === "GET" && url.pathname === "/health") {
184
+ return sendJson(input.response, 200, {
185
+ ok: true,
186
+ root: input.paths.root,
187
+ control: { bind: input.runtime.controlBind, port: input.runtime.controlPort }
188
+ });
189
+ }
190
+ if (input.request.method === "GET" && url.pathname === "/panel") {
191
+ return sendText(input.response, 200, "text/html; charset=utf-8", renderWebPanel());
192
+ }
193
+ if (input.request.method === "GET" && url.pathname === "/panel-client.js") {
194
+ return sendText(input.response, 200, "text/javascript; charset=utf-8", renderPanelClient());
195
+ }
196
+ if (input.request.method === "GET" && url.pathname === "/openapi.json") {
197
+ return sendJson(input.response, 200, openApiDocument());
198
+ }
199
+ if (input.request.method === "POST" && url.pathname === "/pairing") {
200
+ // Pairing endpoint: only allow from loopback OR with an existing valid device token.
201
+ // This prevents an attacker on the network from minting tokens for themselves.
202
+ if (!isLoopbackRequest(input.request) && !isAuthorized(input.request, input.store)) {
203
+ return sendJson(input.response, 403, {
204
+ error: "forbidden",
205
+ message: "Pairing must be initiated from the local machine. Run 'magi pair' on the daemon host."
206
+ });
207
+ }
208
+ const body = await readJson(input.request);
209
+ const token = createPairingToken({
210
+ store: input.store,
211
+ deviceName: typeof body.name === "string" ? body.name : "unnamed device"
212
+ });
213
+ input.store.recordAudit({
214
+ sessionId: input.store.createSession({ title: "control pairing", cwd: input.cwd, metadata: { command: "pairing" } }),
215
+ action: "control.pairing.created",
216
+ target: token.deviceId,
217
+ metadata: { expiresAt: token.expiresAt }
218
+ });
219
+ return sendJson(input.response, 200, token);
220
+ }
221
+ if (!isAuthorized(input.request, input.store)) {
222
+ return sendJson(input.response, 401, { error: "unauthorized" });
223
+ }
224
+ if (input.request.method === "GET" && url.pathname === "/sessions") {
225
+ return sendJson(input.response, 200, { sessions: input.store.listSessions(50) });
226
+ }
227
+ if (input.request.method === "POST" && url.pathname === "/sessions") {
228
+ const body = await readJson(input.request);
229
+ const sessionId = input.store.createSession({
230
+ id: readOptionalString(body.id),
231
+ title: readOptionalString(body.title),
232
+ cwd: readOptionalString(body.cwd) ?? input.cwd,
233
+ metadata: readOptionalRecord(body.metadata)
234
+ });
235
+ return sendJson(input.response, 200, { session: input.store.getSession(sessionId) });
236
+ }
237
+ const sessionEventsRoute = /^\/sessions\/([^/]+)\/events$/.exec(url.pathname);
238
+ if (sessionEventsRoute && input.request.method === "GET") {
239
+ const sessionId = decodeURIComponent(sessionEventsRoute[1]);
240
+ const session = input.store.getSession(sessionId);
241
+ if (!session) {
242
+ return sendJson(input.response, 404, { error: "session not found" });
243
+ }
244
+ const limit = readLimit(url.searchParams.get("limit"), 100);
245
+ return sendJson(input.response, 200, {
246
+ events: input.store.listSessionAuditEvents(sessionId, limit).map(toEventView)
247
+ });
248
+ }
249
+ const sessionRoute = /^\/sessions\/([^/]+)(?:\/messages)?$/.exec(url.pathname);
250
+ if (sessionRoute) {
251
+ const sessionId = decodeURIComponent(sessionRoute[1]);
252
+ const isMessagesRoute = url.pathname.endsWith("/messages");
253
+ const session = input.store.getSession(sessionId);
254
+ if (!session) {
255
+ return sendJson(input.response, 404, { error: "session not found" });
256
+ }
257
+ if (input.request.method === "GET" && !isMessagesRoute) {
258
+ return sendJson(input.response, 200, { session });
259
+ }
260
+ if (input.request.method === "POST" && isMessagesRoute) {
261
+ const body = await readJson(input.request);
262
+ if (typeof body.prompt !== "string" || !body.prompt.trim()) {
263
+ return sendJson(input.response, 400, { error: "prompt is required" });
264
+ }
265
+ const result = await runControlJob(input, body, { sessionId, cwd: session.cwd });
266
+ return sendJson(input.response, 200, result);
267
+ }
268
+ }
269
+ if (input.request.method === "GET" && url.pathname === "/jobs") {
270
+ return sendJson(input.response, 200, { jobs: input.store.listJobs(50) });
271
+ }
272
+ const jobEventsRoute = /^\/jobs\/([^/]+)\/events$/.exec(url.pathname);
273
+ if (jobEventsRoute && input.request.method === "GET") {
274
+ const jobId = decodeURIComponent(jobEventsRoute[1]);
275
+ const job = input.store.getJob(jobId);
276
+ if (!job) {
277
+ return sendJson(input.response, 404, { error: "job not found" });
278
+ }
279
+ const limit = readLimit(url.searchParams.get("limit"), 100);
280
+ return sendJson(input.response, 200, {
281
+ events: input.store.listJobAuditEvents(jobId, limit).map(toEventView)
282
+ });
283
+ }
284
+ const jobApprovalRoute = /^\/jobs\/([^/]+)\/approvals\/([^/]+)$/.exec(url.pathname);
285
+ if (jobApprovalRoute && input.request.method === "POST") {
286
+ const jobId = decodeURIComponent(jobApprovalRoute[1]);
287
+ const toolUseId = decodeURIComponent(jobApprovalRoute[2]);
288
+ const body = await readJson(input.request);
289
+ const job = input.store.getJob(jobId);
290
+ if (!job) {
291
+ return sendJson(input.response, 404, { error: "job not found" });
292
+ }
293
+ const decision = readApprovalDecision(body);
294
+ if (decision === undefined) {
295
+ return sendJson(input.response, 400, { error: "decision must be approve, deny, approved, denied, true, or false" });
296
+ }
297
+ try {
298
+ const interaction = input.interactions.resolveApproval({ jobId, toolUseId, approved: decision });
299
+ input.store.recordAudit({
300
+ sessionId: job.sessionId,
301
+ jobId,
302
+ action: "control.approval.resolved",
303
+ target: toolUseId,
304
+ metadata: {
305
+ status: "resolved",
306
+ interactionKind: "approval",
307
+ toolUseId,
308
+ approved: decision,
309
+ responder: readOptionalString(body.responder),
310
+ interaction
311
+ }
312
+ });
313
+ return sendJson(input.response, 200, { ok: true, interaction });
314
+ }
315
+ catch (error) {
316
+ return sendInteractionError(input.response, error);
317
+ }
318
+ }
319
+ const jobQuestionRoute = /^\/jobs\/([^/]+)\/questions\/([^/]+)$/.exec(url.pathname);
320
+ if (jobQuestionRoute && input.request.method === "POST") {
321
+ const jobId = decodeURIComponent(jobQuestionRoute[1]);
322
+ const toolUseId = decodeURIComponent(jobQuestionRoute[2]);
323
+ const body = await readJson(input.request);
324
+ const job = input.store.getJob(jobId);
325
+ if (!job) {
326
+ return sendJson(input.response, 404, { error: "job not found" });
327
+ }
328
+ try {
329
+ const pending = input.interactions.getPendingQuestion({ jobId, toolUseId });
330
+ const answer = normalizeControlQuestionAnswer(body, pending.question);
331
+ const interaction = input.interactions.resolveQuestion({ jobId, toolUseId, answer });
332
+ input.store.recordAudit({
333
+ sessionId: job.sessionId,
334
+ jobId,
335
+ action: "control.user_question.resolved",
336
+ target: toolUseId,
337
+ metadata: {
338
+ status: "resolved",
339
+ interactionKind: "question",
340
+ toolUseId,
341
+ answer,
342
+ responder: readOptionalString(body.responder),
343
+ interaction
344
+ }
345
+ });
346
+ return sendJson(input.response, 200, { ok: true, interaction });
347
+ }
348
+ catch (error) {
349
+ return sendInteractionError(input.response, error);
350
+ }
351
+ }
352
+ const jobInteractionCancelRoute = /^\/jobs\/([^/]+)\/(approvals|questions)\/([^/]+)\/cancel$/.exec(url.pathname);
353
+ if (jobInteractionCancelRoute && input.request.method === "POST") {
354
+ const jobId = decodeURIComponent(jobInteractionCancelRoute[1]);
355
+ const interactionType = jobInteractionCancelRoute[2] === "approvals" ? "approval" : "question";
356
+ const toolUseId = decodeURIComponent(jobInteractionCancelRoute[3]);
357
+ const body = await readJson(input.request);
358
+ const job = input.store.getJob(jobId);
359
+ if (!job) {
360
+ return sendJson(input.response, 404, { error: "job not found" });
361
+ }
362
+ try {
363
+ const interaction = input.interactions.cancelInteraction({
364
+ jobId,
365
+ toolUseId,
366
+ reason: readOptionalString(body.reason) ?? "cancelled by control API"
367
+ });
368
+ input.store.recordAudit({
369
+ sessionId: job.sessionId,
370
+ jobId,
371
+ action: interactionType === "approval" ? "control.approval.cancelled" : "control.user_question.cancelled",
372
+ target: toolUseId,
373
+ metadata: {
374
+ status: "cancelled",
375
+ interactionKind: interactionType,
376
+ toolUseId,
377
+ reason: interaction.cancelReason,
378
+ interaction
379
+ }
380
+ });
381
+ return sendJson(input.response, 200, { ok: true, interaction });
382
+ }
383
+ catch (error) {
384
+ return sendInteractionError(input.response, error);
385
+ }
386
+ }
387
+ const jobRoute = /^\/jobs\/([^/]+)$/.exec(url.pathname);
388
+ if (jobRoute && input.request.method === "GET") {
389
+ const job = input.store.getJob(decodeURIComponent(jobRoute[1]));
390
+ return job ? sendJson(input.response, 200, { job }) : sendJson(input.response, 404, { error: "job not found" });
391
+ }
392
+ const jobCancelRoute = /^\/jobs\/([^/]+)\/cancel$/.exec(url.pathname);
393
+ if (jobCancelRoute && input.request.method === "POST") {
394
+ const jobId = decodeURIComponent(jobCancelRoute[1]);
395
+ const body = await readJson(input.request);
396
+ const running = input.runningJobs.get(jobId);
397
+ const reason = readOptionalString(body.reason) ?? "cancelled by control API";
398
+ if (running) {
399
+ running.controller.abort(reason);
400
+ input.store.recordAudit({
401
+ sessionId: running.sessionId,
402
+ jobId,
403
+ action: "control.job.cancel_requested",
404
+ target: jobId,
405
+ metadata: { reason }
406
+ });
407
+ return sendJson(input.response, 200, { ok: true, status: "cancelling", jobId, reason });
408
+ }
409
+ const job = input.store.getJob(jobId);
410
+ if (!job) {
411
+ return sendJson(input.response, 404, { error: "job not found" });
412
+ }
413
+ if (job.status === "running") {
414
+ input.store.updateJobStatus({
415
+ id: jobId,
416
+ status: "cancelled",
417
+ metadata: { reason, cancelledWithoutActiveRunner: true }
418
+ });
419
+ input.store.recordAudit({
420
+ sessionId: job.sessionId,
421
+ jobId,
422
+ action: "control.job.cancelled",
423
+ target: jobId,
424
+ metadata: { reason, cancelledWithoutActiveRunner: true }
425
+ });
426
+ return sendJson(input.response, 200, { ok: true, status: "cancelled", jobId, reason });
427
+ }
428
+ return sendJson(input.response, 409, { error: `job is ${job.status}` });
429
+ }
430
+ if (input.request.method === "GET" && url.pathname === "/agents") {
431
+ return sendJson(input.response, 200, { tasks: input.store.listAgentTasks(50) });
432
+ }
433
+ if (input.request.method === "GET" && url.pathname === "/providers") {
434
+ return sendJson(input.response, 200, {
435
+ providers: Object.entries(input.config.providers).map(([name, provider]) => ({
436
+ name,
437
+ type: provider.type,
438
+ defaultModel: provider.defaultModel,
439
+ configured: Boolean(provider.apiKeyEnv ? input.env?.[provider.apiKeyEnv] : true)
440
+ })),
441
+ aliases: input.config.models.aliases
442
+ });
443
+ }
444
+ if (input.request.method === "GET" && url.pathname === "/plugins") {
445
+ return sendJson(input.response, 200, {
446
+ plugins: listLocalPlugins(input.paths),
447
+ marketplaces: discoverLocalMarketplaceSources(input.paths).map(loadMarketplace)
448
+ });
449
+ }
450
+ if (input.request.method === "GET" && url.pathname === "/skills") {
451
+ return sendJson(input.response, 200, { skills: listSkills(input.paths) });
452
+ }
453
+ if (input.request.method === "POST" && url.pathname === "/agents") {
454
+ const body = await readJson(input.request);
455
+ if (body.role !== "explorer" && body.role !== "worker") {
456
+ return sendJson(input.response, 400, { error: "role must be explorer or worker" });
457
+ }
458
+ if (typeof body.prompt !== "string" || !body.prompt.trim()) {
459
+ return sendJson(input.response, 400, { error: "prompt is required" });
460
+ }
461
+ const writeFiles = Array.isArray(body.writeFiles)
462
+ ? body.writeFiles.filter((item) => typeof item === "string")
463
+ : [];
464
+ const cwd = typeof body.cwd === "string" ? body.cwd : input.cwd;
465
+ const sessionId = input.store.createSession({ title: `control agent task ${body.role}`, cwd });
466
+ const task = spawnAgentTask(input.store, {
467
+ role: body.role,
468
+ prompt: body.prompt,
469
+ cwd,
470
+ sessionId,
471
+ writeFiles
472
+ });
473
+ const hooks = await triggerHooks({
474
+ event: "task_created",
475
+ hooks: input.config.hooks,
476
+ store: input.store,
477
+ sessionId,
478
+ cwd,
479
+ env: input.env,
480
+ context: {
481
+ taskId: task.id,
482
+ taskSubject: task.prompt,
483
+ taskDescription: task.prompt,
484
+ agentId: task.id,
485
+ agentType: task.role
486
+ }
487
+ });
488
+ return sendJson(input.response, 200, {
489
+ task,
490
+ hooks
491
+ });
492
+ }
493
+ const agentAction = /^\/agents\/([^/]+)\/(start|wait|cancel|complete)$/.exec(url.pathname);
494
+ if (agentAction && input.request.method === "POST") {
495
+ const [, taskId, action] = agentAction;
496
+ const body = await readJson(input.request);
497
+ if (action === "start") {
498
+ const task = waitAgentTask(input.store, startAgentTask(input.store, taskId).id);
499
+ const sessionId = task.sessionId ?? input.store.createSession({ title: "control agent start", cwd: task.cwd });
500
+ const hooks = await triggerHooks({
501
+ event: "subagent_start",
502
+ hooks: input.config.hooks,
503
+ store: input.store,
504
+ sessionId,
505
+ cwd: task.cwd,
506
+ env: input.env,
507
+ context: {
508
+ agentId: task.id,
509
+ agentType: task.role,
510
+ taskId: task.id,
511
+ taskSubject: task.prompt
512
+ }
513
+ });
514
+ return sendJson(input.response, 200, { task, hooks });
515
+ }
516
+ if (action === "wait") {
517
+ return sendJson(input.response, 200, { task: waitAgentTask(input.store, taskId) });
518
+ }
519
+ if (action === "cancel") {
520
+ const task = cancelAgentTask(input.store, taskId);
521
+ const sessionId = task.sessionId ?? input.store.createSession({ title: "control agent stop", cwd: task.cwd });
522
+ const hooks = await triggerHooks({
523
+ event: "stop",
524
+ hooks: input.config.hooks,
525
+ store: input.store,
526
+ sessionId,
527
+ cwd: task.cwd,
528
+ env: input.env,
529
+ context: {
530
+ message: `Agent task ${task.id} cancelled`,
531
+ notificationType: "agent_task_cancelled",
532
+ lastAssistantMessage: task.result ?? undefined
533
+ }
534
+ });
535
+ const subagentHooks = await triggerHooks({
536
+ event: "subagent_stop",
537
+ hooks: input.config.hooks,
538
+ store: input.store,
539
+ sessionId,
540
+ cwd: task.cwd,
541
+ env: input.env,
542
+ context: {
543
+ agentId: task.id,
544
+ agentType: task.role,
545
+ taskId: task.id,
546
+ taskSubject: task.prompt,
547
+ message: `Agent task ${task.id} cancelled`,
548
+ notificationType: "agent_task_cancelled",
549
+ lastAssistantMessage: task.result ?? undefined
550
+ }
551
+ });
552
+ return sendJson(input.response, 200, { task, hooks: [...hooks, ...subagentHooks] });
553
+ }
554
+ const task = completeAgentTask(input.store, taskId, typeof body.result === "string" ? body.result : "");
555
+ const sessionId = task.sessionId ?? input.store.createSession({ title: "control agent notification", cwd: task.cwd });
556
+ const hooks = await triggerHooks({
557
+ event: "notification",
558
+ hooks: input.config.hooks,
559
+ store: input.store,
560
+ sessionId,
561
+ cwd: task.cwd,
562
+ env: input.env,
563
+ context: {
564
+ message: `Agent task ${task.id} completed`,
565
+ title: "Agent task completed",
566
+ notificationType: "agent_task_completed",
567
+ lastAssistantMessage: task.result ?? undefined
568
+ }
569
+ });
570
+ const taskHooks = await triggerHooks({
571
+ event: "task_completed",
572
+ hooks: input.config.hooks,
573
+ store: input.store,
574
+ sessionId,
575
+ cwd: task.cwd,
576
+ env: input.env,
577
+ context: {
578
+ taskId: task.id,
579
+ taskSubject: task.prompt,
580
+ taskDescription: task.prompt,
581
+ agentId: task.id,
582
+ agentType: task.role,
583
+ lastAssistantMessage: task.result ?? undefined
584
+ }
585
+ });
586
+ const subagentHooks = await triggerHooks({
587
+ event: "subagent_stop",
588
+ hooks: input.config.hooks,
589
+ store: input.store,
590
+ sessionId,
591
+ cwd: task.cwd,
592
+ env: input.env,
593
+ context: {
594
+ agentId: task.id,
595
+ agentType: task.role,
596
+ taskId: task.id,
597
+ taskSubject: task.prompt,
598
+ lastAssistantMessage: task.result ?? undefined
599
+ }
600
+ });
601
+ return sendJson(input.response, 200, { task, hooks: [...hooks, ...taskHooks, ...subagentHooks] });
602
+ }
603
+ if (input.request.method === "GET" && url.pathname === "/audit") {
604
+ return sendJson(input.response, 200, { audit: input.store.listAuditEvents(100) });
605
+ }
606
+ if (input.request.method === "GET" && url.pathname === "/events.json") {
607
+ const limit = readLimit(url.searchParams.get("limit"), 100);
608
+ const sessionId = readOptionalString(url.searchParams.get("sessionId") ?? undefined);
609
+ const jobId = readOptionalString(url.searchParams.get("jobId") ?? undefined);
610
+ return sendJson(input.response, 200, {
611
+ events: input.store.listRecentAuditEvents({ sessionId, jobId, limit }).map(toEventView)
612
+ });
613
+ }
614
+ if (input.request.method === "POST" && url.pathname === "/jobs") {
615
+ const body = await readJson(input.request);
616
+ if (typeof body.prompt !== "string" || !body.prompt.trim()) {
617
+ return sendJson(input.response, 400, { error: "prompt is required" });
618
+ }
619
+ const sessionId = readOptionalString(body.sessionId);
620
+ const existingSession = sessionId ? input.store.getSession(sessionId) : undefined;
621
+ if (sessionId && !existingSession) {
622
+ return sendJson(input.response, 404, { error: "session not found" });
623
+ }
624
+ if (body.background === true || body.async === true) {
625
+ const result = startBackgroundControlJob(input, body, {
626
+ sessionId,
627
+ cwd: existingSession?.cwd
628
+ });
629
+ return sendJson(input.response, 202, result);
630
+ }
631
+ const result = await runControlJob(input, body, {
632
+ sessionId,
633
+ cwd: existingSession?.cwd
634
+ });
635
+ return sendJson(input.response, 200, result);
636
+ }
637
+ const activeInteractionsRoute = /^\/jobs\/([^/]+)\/interactions$/.exec(url.pathname);
638
+ if (activeInteractionsRoute && input.request.method === "GET") {
639
+ const jobId = decodeURIComponent(activeInteractionsRoute[1]);
640
+ const job = input.store.getJob(jobId);
641
+ if (!job) {
642
+ return sendJson(input.response, 404, { error: "job not found" });
643
+ }
644
+ return sendJson(input.response, 200, {
645
+ interactions: input.interactions.listInteractions({ jobId })
646
+ });
647
+ }
648
+ if (input.request.method === "POST" && url.pathname === "/approvals") {
649
+ const body = await readJson(input.request);
650
+ const sessionId = typeof body.sessionId === "string"
651
+ ? body.sessionId
652
+ : input.store.createSession({ title: "control approval", cwd: input.cwd });
653
+ input.store.recordAudit({
654
+ sessionId,
655
+ jobId: typeof body.jobId === "string" ? body.jobId : undefined,
656
+ action: "control.approval.recorded",
657
+ target: typeof body.decision === "string" ? body.decision : "unknown",
658
+ metadata: body
659
+ });
660
+ return sendJson(input.response, 200, { ok: true, approvalId: randomUUID() });
661
+ }
662
+ if (input.request.method === "GET" && url.pathname === "/events") {
663
+ streamEvents({
664
+ request: input.request,
665
+ response: input.response,
666
+ store: input.store,
667
+ limit: readLimit(url.searchParams.get("limit"), 50),
668
+ afterId: readOptionalId(url.searchParams.get("after")),
669
+ sessionId: readOptionalString(url.searchParams.get("sessionId") ?? undefined),
670
+ jobId: readOptionalString(url.searchParams.get("jobId") ?? undefined)
671
+ });
672
+ return;
673
+ }
674
+ return sendJson(input.response, 404, { error: "not found" });
675
+ }
676
+ catch (error) {
677
+ const message = error instanceof Error ? error.message : String(error);
678
+ if (!input.response.headersSent) {
679
+ return sendJson(input.response, 500, { error: message });
680
+ }
681
+ input.response.end();
682
+ return;
683
+ }
684
+ }
685
+ function streamEvents(input) {
686
+ input.response.writeHead(200, {
687
+ "content-type": "text/event-stream",
688
+ "cache-control": "no-cache, no-transform",
689
+ connection: "keep-alive",
690
+ "x-accel-buffering": "no"
691
+ });
692
+ input.response.write(`event: ready\ndata: ${JSON.stringify({
693
+ ok: true,
694
+ sessionId: input.sessionId,
695
+ jobId: input.jobId
696
+ })}\n\n`);
697
+ const historical = input.store.listRecentAuditEvents({
698
+ sessionId: input.sessionId,
699
+ jobId: input.jobId,
700
+ afterId: input.afterId,
701
+ limit: input.limit,
702
+ order: "asc"
703
+ }).map(toEventView);
704
+ for (const event of historical) {
705
+ writeSseEvent(input.response, "audit", event);
706
+ }
707
+ const unsubscribe = input.store.subscribeAuditEvents((event) => {
708
+ if (!matchesEventStreamFilter(event, input)) {
709
+ return;
710
+ }
711
+ writeSseEvent(input.response, "audit", toEventView(event));
712
+ });
713
+ const heartbeat = setInterval(() => {
714
+ input.response.write(`: heartbeat ${new Date().toISOString()}\n\n`);
715
+ }, 15_000);
716
+ heartbeat.unref?.();
717
+ const close = () => {
718
+ clearInterval(heartbeat);
719
+ unsubscribe();
720
+ input.response.end();
721
+ };
722
+ input.request.once("close", close);
723
+ }
724
+ function writeSseEvent(response, eventName, event) {
725
+ response.write(`id: ${event.id}\n`);
726
+ response.write(`event: ${eventName}\n`);
727
+ response.write(`data: ${JSON.stringify(event)}\n\n`);
728
+ }
729
+ function matchesEventStreamFilter(event, filter) {
730
+ if (filter.afterId !== undefined && event.id <= filter.afterId) {
731
+ return false;
732
+ }
733
+ if (filter.sessionId && event.sessionId !== filter.sessionId) {
734
+ return false;
735
+ }
736
+ if (filter.jobId && event.jobId !== filter.jobId) {
737
+ return false;
738
+ }
739
+ return true;
740
+ }
741
+ function runControlJob(input, body, options = {}) {
742
+ return runHeadlessPrompt({
743
+ prompt: String(body.prompt),
744
+ cwd: readOptionalString(body.cwd) ?? options.cwd ?? input.cwd,
745
+ store: input.store,
746
+ config: input.config,
747
+ env: input.env,
748
+ paths: input.paths,
749
+ stateRoot: input.paths.stateRoot,
750
+ modelAlias: readOptionalString(body.model),
751
+ sessionId: options.sessionId,
752
+ sessionName: readOptionalString(body.sessionName),
753
+ persistSession: typeof body.persistSession === "boolean" ? body.persistSession : undefined,
754
+ collectEvents: body.collectEvents === true,
755
+ permissionMode: readPermissionMode(body.permissionMode) ?? "default",
756
+ activeInteractions: input.interactions
757
+ });
758
+ }
759
+ function startBackgroundControlJob(input, body, options = {}) {
760
+ const cwd = readOptionalString(body.cwd) ?? options.cwd ?? input.cwd;
761
+ const prompt = String(body.prompt);
762
+ const sessionId = options.sessionId ?? input.store.createSession({
763
+ title: readOptionalString(body.sessionName) ?? prompt.slice(0, 80),
764
+ cwd,
765
+ metadata: { mode: "control-background" }
766
+ });
767
+ const jobId = randomUUID();
768
+ const controller = new AbortController();
769
+ const promise = runHeadlessPrompt({
770
+ prompt,
771
+ cwd,
772
+ store: input.store,
773
+ config: input.config,
774
+ env: input.env,
775
+ paths: input.paths,
776
+ stateRoot: input.paths.stateRoot,
777
+ modelAlias: readOptionalString(body.model),
778
+ jobId,
779
+ sessionId,
780
+ sessionName: readOptionalString(body.sessionName),
781
+ persistSession: true,
782
+ collectEvents: body.collectEvents === true,
783
+ permissionMode: readPermissionMode(body.permissionMode) ?? "default",
784
+ activeInteractions: input.interactions,
785
+ signal: controller.signal
786
+ }).finally(() => {
787
+ input.runningJobs.delete(jobId);
788
+ });
789
+ input.runningJobs.set(jobId, { jobId, sessionId, controller, promise });
790
+ promise.catch(() => undefined);
791
+ return { sessionId, jobId, status: "running" };
792
+ }
793
+ function isAuthorized(request, store) {
794
+ const deviceId = headerValue(request.headers["x-magi-device-id"]);
795
+ const token = headerValue(request.headers.authorization)?.replace(/^Bearer\s+/i, "");
796
+ return validateDeviceToken({ store, deviceId, token });
797
+ }
798
+ function isLoopbackRequest(request) {
799
+ const addr = request.socket.remoteAddress ?? "";
800
+ return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
801
+ }
802
+ async function readJson(request) {
803
+ const chunks = [];
804
+ for await (const chunk of request) {
805
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
806
+ }
807
+ const text = Buffer.concat(chunks).toString("utf8").trim();
808
+ if (!text) {
809
+ return {};
810
+ }
811
+ const parsed = JSON.parse(text);
812
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
813
+ }
814
+ function sendJson(response, status, body) {
815
+ response.writeHead(status, { "content-type": "application/json" });
816
+ response.end(`${JSON.stringify(body)}\n`);
817
+ }
818
+ function sendText(response, status, contentType, body) {
819
+ response.writeHead(status, { "content-type": contentType });
820
+ response.end(body);
821
+ }
822
+ function sendInteractionError(response, error) {
823
+ if (error instanceof ActiveInteractionNotFoundError) {
824
+ return sendJson(response, 404, { error: error.message });
825
+ }
826
+ if (error instanceof ActiveInteractionStateError || error instanceof ActiveInteractionCancelledError) {
827
+ return sendJson(response, 409, { error: error.message });
828
+ }
829
+ return sendJson(response, 400, { error: error instanceof Error ? error.message : String(error) });
830
+ }
831
+ function headerValue(value) {
832
+ return Array.isArray(value) ? value[0] : value;
833
+ }
834
+ function readOptionalString(value) {
835
+ return typeof value === "string" && value.trim() ? value : undefined;
836
+ }
837
+ function readOptionalRecord(value) {
838
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
839
+ }
840
+ function readApprovalDecision(body) {
841
+ const raw = body.approved ?? body.decision;
842
+ if (raw === true || raw === "approve" || raw === "approved" || raw === "allow" || raw === "allowed") {
843
+ return true;
844
+ }
845
+ if (raw === false || raw === "deny" || raw === "denied" || raw === "reject" || raw === "rejected") {
846
+ return false;
847
+ }
848
+ return undefined;
849
+ }
850
+ function readPermissionMode(value) {
851
+ return value === "default" || value === "acceptEdits" || value === "bypassPermissions" || value === "plan"
852
+ ? value
853
+ : undefined;
854
+ }
855
+ function normalizeControlQuestionAnswer(body, question) {
856
+ const rawAnswer = readOptionalRecord(body.answer) ?? body;
857
+ const candidate = rawAnswer.answers === undefined && Array.isArray(body.selectedLabels)
858
+ ? {
859
+ answers: [{
860
+ question: question.questions[0]?.question ?? "",
861
+ selectedLabels: body.selectedLabels
862
+ }]
863
+ }
864
+ : rawAnswer;
865
+ return normalizeAskUserQuestionAnswer(question, candidate);
866
+ }
867
+ function readLimit(raw, fallback) {
868
+ if (raw === null || raw === "") {
869
+ return fallback;
870
+ }
871
+ if (!/^\d+$/.test(raw)) {
872
+ return fallback;
873
+ }
874
+ const value = Number(raw);
875
+ return Number.isInteger(value) && value >= 1 && value <= 500 ? value : fallback;
876
+ }
877
+ function readOptionalId(raw) {
878
+ if (raw === null || raw === "") {
879
+ return undefined;
880
+ }
881
+ if (!/^\d+$/.test(raw)) {
882
+ return undefined;
883
+ }
884
+ const value = Number(raw);
885
+ return Number.isSafeInteger(value) && value >= 0 ? value : undefined;
886
+ }
887
+ //# sourceMappingURL=server.js.map