@agentprojectcontext/apx 1.77.3 → 1.79.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 (356) hide show
  1. package/README.md +23 -9
  2. package/package.json +12 -12
  3. package/src/core/agent/build-agent-system.js +3 -2
  4. package/src/core/agent/constants.js +3 -2
  5. package/src/core/agent/loop/greeting-guard.js +55 -0
  6. package/src/core/agent/loop/side-effects.js +52 -0
  7. package/src/core/agent/prompt-builder.js +49 -8
  8. package/src/core/agent/run-agent.js +43 -53
  9. package/src/core/agent/security.js +1 -1
  10. package/src/core/agent/self-memory.js +4 -2
  11. package/src/core/agent/skills/index-store.js +2 -2
  12. package/src/core/agent/skills/loader.js +3 -22
  13. package/src/core/agent/super-agent.js +0 -2
  14. package/src/core/agent/tool-summary.js +65 -0
  15. package/src/core/agent/tools/handlers/call-runtime.js +4 -4
  16. package/src/core/agent/tools/handlers/list-commitments.js +80 -0
  17. package/src/core/agent/tools/handlers/list-tasks.js +66 -27
  18. package/src/core/agent/tools/handlers/record-commitment.js +68 -0
  19. package/src/core/agent/tools/handlers/search-sessions.js +1 -1
  20. package/src/core/agent/tools/handlers/send-telegram.js +68 -2
  21. package/src/core/agent/tools/handlers/set-permission-mode.js +11 -4
  22. package/src/core/agent/tools/names.js +64 -0
  23. package/src/core/agent/tools/registry.js +9 -0
  24. package/src/core/agent/tools/tool-call-parser.js +70 -1
  25. package/src/core/apc/context-copy.js +1 -1
  26. package/src/core/apc/frontmatter.js +85 -0
  27. package/src/core/apc/parser.js +9 -21
  28. package/src/core/apc/paths.js +8 -8
  29. package/src/core/apc/scaffold.js +5 -4
  30. package/src/core/channels/telegram/ask-callbacks.js +35 -0
  31. package/src/core/channels/telegram/config.js +201 -0
  32. package/src/core/channels/telegram/dispatch.js +3 -0
  33. package/src/core/channels/telegram/reply.js +30 -5
  34. package/src/core/config/index.js +25 -183
  35. package/src/core/config/paths.js +57 -1
  36. package/src/core/config/redact.js +22 -0
  37. package/src/core/confirmation/adapters/web.js +1 -1
  38. package/src/core/daemon/service.js +238 -0
  39. package/src/core/deck/manifest.js +12 -12
  40. package/src/core/desktop/autostart.js +3 -2
  41. package/src/core/desktop/process.js +4 -4
  42. package/src/core/engines/gemini.js +322 -60
  43. package/src/core/engines/openai-compatible.js +21 -2
  44. package/src/core/engines/presets.js +2 -2
  45. package/src/core/http-tools/browser.js +1 -1
  46. package/src/core/http-tools/catalog.js +551 -0
  47. package/src/core/http-tools/fetch.js +1 -1
  48. package/src/core/http-tools/glob.js +3 -3
  49. package/src/core/http-tools/grep.js +2 -9
  50. package/src/core/http-tools/inline-handlers.js +59 -0
  51. package/src/core/http-tools/registry.js +31 -615
  52. package/src/core/http-tools/search.js +2 -2
  53. package/src/core/identity/self.js +6 -7
  54. package/src/core/identity/telegram.js +3 -3
  55. package/src/core/logging.js +6 -5
  56. package/src/core/mcp/sources.js +12 -16
  57. package/src/core/memory/consolidate.js +225 -0
  58. package/src/core/memory/indexer.js +2 -5
  59. package/src/core/nudge/index.js +192 -0
  60. package/src/core/nudge/policy.js +143 -0
  61. package/src/core/nudge/store.js +141 -0
  62. package/src/core/profiles/bundled/secretary/PROFILE.md +8 -9
  63. package/src/core/profiles/bundled/secretary/config.schema.json +33 -3
  64. package/src/core/profiles/bundled/secretary/routines/day-close.json +7 -3
  65. package/src/core/profiles/bundled/secretary/routines/day-open.json +7 -3
  66. package/src/core/profiles/bundled/secretary/routines/watch.json +13 -0
  67. package/src/core/profiles/lifecycle.js +0 -2
  68. package/src/core/profiles/store.js +2 -7
  69. package/src/core/routines/runner.js +103 -4
  70. package/src/core/routines/signals.js +270 -0
  71. package/src/core/runtime-skills/apx-agency-agents/SKILL.md +2 -2
  72. package/src/core/runtime-skills/apx-profile/SKILL.md +3 -3
  73. package/src/core/runtime-skills/apx-project/SKILL.md +1 -1
  74. package/src/core/runtime-skills/apx-task/SKILL.md +8 -8
  75. package/src/core/runtime-skills/apx-telegram/SKILL.md +4 -4
  76. package/src/core/runtime-skills/apx-voice/SKILL.md +4 -4
  77. package/src/core/runtimes/detect.js +2 -2
  78. package/src/core/sessions/index.js +800 -0
  79. package/src/core/stores/code-sessions.js +3 -14
  80. package/src/core/stores/commitments.js +331 -0
  81. package/src/core/stores/engine-sessions.js +0 -4
  82. package/src/core/stores/messages.js +4 -5
  83. package/src/core/stores/organization.js +9 -11
  84. package/src/core/stores/routines.js +23 -14
  85. package/src/core/stores/runtime-callbacks.js +2 -1
  86. package/src/core/util/json-file.js +128 -0
  87. package/src/core/util/thinking.js +51 -0
  88. package/src/core/vars/sources.js +14 -21
  89. package/src/core/voice/stt-models.js +1 -1
  90. package/src/core/voice/tts.js +5 -4
  91. package/src/host/daemon/api/admin-config.js +5 -5
  92. package/src/host/daemon/api/admin.js +8 -7
  93. package/src/host/daemon/api/agents.js +16 -16
  94. package/src/host/daemon/api/artifact-preview.js +8 -8
  95. package/src/host/daemon/api/artifacts.js +7 -7
  96. package/src/host/daemon/api/code.js +8 -8
  97. package/src/host/daemon/api/commitments.js +135 -0
  98. package/src/host/daemon/api/config.js +6 -6
  99. package/src/host/daemon/api/confirm.js +2 -2
  100. package/src/host/daemon/api/connections.js +2 -2
  101. package/src/host/daemon/api/conversations.js +12 -12
  102. package/src/host/daemon/api/deck.js +6 -6
  103. package/src/host/daemon/api/desktop.js +8 -8
  104. package/src/host/daemon/api/embeddings.js +4 -4
  105. package/src/host/daemon/api/engines.js +8 -7
  106. package/src/host/daemon/api/exec.js +5 -4
  107. package/src/host/daemon/api/files-project.js +6 -6
  108. package/src/host/daemon/api/health.js +2 -2
  109. package/src/host/daemon/api/identity.js +3 -3
  110. package/src/host/daemon/api/inbox.js +2 -2
  111. package/src/host/daemon/api/integrations.js +23 -19
  112. package/src/host/daemon/api/mcps.js +16 -12
  113. package/src/host/daemon/api/messages.js +5 -5
  114. package/src/host/daemon/api/nudges.js +112 -0
  115. package/src/host/daemon/api/organization.js +8 -8
  116. package/src/host/daemon/api/pairing.js +6 -6
  117. package/src/host/daemon/api/plugins.js +3 -3
  118. package/src/host/daemon/api/prefix.js +20 -0
  119. package/src/host/daemon/api/profiles.js +9 -9
  120. package/src/host/daemon/api/projects.js +5 -5
  121. package/src/host/daemon/api/routines.js +32 -8
  122. package/src/host/daemon/api/run.js +2 -2
  123. package/src/host/daemon/api/runtimes.js +6 -6
  124. package/src/host/daemon/api/self-memory.js +50 -0
  125. package/src/host/daemon/api/sessions-search.js +18 -11
  126. package/src/host/daemon/api/sessions.js +6 -6
  127. package/src/host/daemon/api/shared.js +59 -13
  128. package/src/host/daemon/api/skills.js +12 -12
  129. package/src/host/daemon/api/super-agent.js +4 -4
  130. package/src/host/daemon/api/tasks.js +11 -11
  131. package/src/host/daemon/api/telegram.js +61 -24
  132. package/src/host/daemon/api/tools.js +7 -7
  133. package/src/host/daemon/api/top-level.js +7 -7
  134. package/src/host/daemon/api/transcribe.js +6 -6
  135. package/src/host/daemon/api/tts.js +3 -3
  136. package/src/host/daemon/api/vars.js +11 -8
  137. package/src/host/daemon/api/voice.js +9 -7
  138. package/src/host/daemon/api/web.js +28 -32
  139. package/src/host/daemon/api.js +83 -48
  140. package/src/host/daemon/callback-reconciler.js +16 -0
  141. package/src/host/daemon/db.js +1 -1
  142. package/src/host/daemon/desktop-ws.js +7 -3
  143. package/src/host/daemon/index.js +11 -5
  144. package/src/host/daemon/plugins/desktop/index.js +8 -2
  145. package/src/host/daemon/plugins/telegram/index.js +7 -2
  146. package/src/host/daemon/stt-venv.js +20 -81
  147. package/src/host/daemon/wakeup.js +18 -4
  148. package/src/interfaces/acp/index.js +3 -3
  149. package/src/interfaces/acp/session.js +2 -2
  150. package/src/interfaces/cli/commands/a2a.js +2 -2
  151. package/src/interfaces/cli/commands/agent.js +3 -3
  152. package/src/interfaces/cli/commands/artifact.js +13 -13
  153. package/src/interfaces/cli/commands/chat.js +3 -3
  154. package/src/interfaces/cli/commands/command.js +2 -2
  155. package/src/interfaces/cli/commands/commitment.js +154 -0
  156. package/src/interfaces/cli/commands/config.js +4 -4
  157. package/src/interfaces/cli/commands/daemon.js +74 -18
  158. package/src/interfaces/cli/commands/desktop.js +7 -14
  159. package/src/interfaces/cli/commands/exec.js +2 -2
  160. package/src/interfaces/cli/commands/log.js +3 -4
  161. package/src/interfaces/cli/commands/mcp.js +9 -9
  162. package/src/interfaces/cli/commands/memory.js +75 -2
  163. package/src/interfaces/cli/commands/messages.js +5 -5
  164. package/src/interfaces/cli/commands/model.js +0 -18
  165. package/src/interfaces/cli/commands/nudge.js +130 -0
  166. package/src/interfaces/cli/commands/obsidian.js +5 -5
  167. package/src/interfaces/cli/commands/org.js +5 -5
  168. package/src/interfaces/cli/commands/pair.js +6 -6
  169. package/src/interfaces/cli/commands/plugins.js +2 -2
  170. package/src/interfaces/cli/commands/profile.js +10 -10
  171. package/src/interfaces/cli/commands/project-config.js +6 -6
  172. package/src/interfaces/cli/commands/project.js +10 -10
  173. package/src/interfaces/cli/commands/routine.js +8 -9
  174. package/src/interfaces/cli/commands/runtime.js +2 -2
  175. package/src/interfaces/cli/commands/search.js +0 -1
  176. package/src/interfaces/cli/commands/session.js +12 -40
  177. package/src/interfaces/cli/commands/sessions.js +21 -798
  178. package/src/interfaces/cli/commands/setup.js +16 -6
  179. package/src/interfaces/cli/commands/skills.js +1 -2
  180. package/src/interfaces/cli/commands/status.js +3 -3
  181. package/src/interfaces/cli/commands/task.js +8 -8
  182. package/src/interfaces/cli/commands/telegram.js +21 -21
  183. package/src/interfaces/cli/commands/voice.js +6 -6
  184. package/src/interfaces/cli/help/index.js +2245 -0
  185. package/src/interfaces/cli/http.js +9 -4
  186. package/src/interfaces/cli/index.js +14 -2857
  187. package/src/interfaces/cli/routes/acp.js +13 -0
  188. package/src/interfaces/cli/routes/agent.js +27 -0
  189. package/src/interfaces/cli/routes/artifact.js +28 -0
  190. package/src/interfaces/cli/routes/chat.js +11 -0
  191. package/src/interfaces/cli/routes/code.js +11 -0
  192. package/src/interfaces/cli/routes/command.js +21 -0
  193. package/src/interfaces/cli/routes/commitment.js +19 -0
  194. package/src/interfaces/cli/routes/config.js +16 -0
  195. package/src/interfaces/cli/routes/connections.js +11 -0
  196. package/src/interfaces/cli/routes/conversations.js +21 -0
  197. package/src/interfaces/cli/routes/daemon.js +25 -0
  198. package/src/interfaces/cli/routes/desktop.js +20 -0
  199. package/src/interfaces/cli/routes/env.js +13 -0
  200. package/src/interfaces/cli/routes/exec.js +11 -0
  201. package/src/interfaces/cli/routes/identity.js +11 -0
  202. package/src/interfaces/cli/routes/index.js +75 -0
  203. package/src/interfaces/cli/routes/init.js +11 -0
  204. package/src/interfaces/cli/routes/log.js +20 -0
  205. package/src/interfaces/cli/routes/mcp.js +22 -0
  206. package/src/interfaces/cli/routes/memory.js +19 -0
  207. package/src/interfaces/cli/routes/messages.js +16 -0
  208. package/src/interfaces/cli/routes/model.js +11 -0
  209. package/src/interfaces/cli/routes/nudge.js +17 -0
  210. package/src/interfaces/cli/routes/obsidian.js +17 -0
  211. package/src/interfaces/cli/routes/org.js +34 -0
  212. package/src/interfaces/cli/routes/overlay.js +10 -0
  213. package/src/interfaces/cli/routes/pair.js +17 -0
  214. package/src/interfaces/cli/routes/panel.js +16 -0
  215. package/src/interfaces/cli/routes/permission.js +11 -0
  216. package/src/interfaces/cli/routes/plugins.js +21 -0
  217. package/src/interfaces/cli/routes/profile.js +27 -0
  218. package/src/interfaces/cli/routes/project.js +41 -0
  219. package/src/interfaces/cli/routes/restart.js +15 -0
  220. package/src/interfaces/cli/routes/routine.js +28 -0
  221. package/src/interfaces/cli/routes/run.js +11 -0
  222. package/src/interfaces/cli/routes/search.js +11 -0
  223. package/src/interfaces/cli/routes/send.js +11 -0
  224. package/src/interfaces/cli/routes/session.js +26 -0
  225. package/src/interfaces/cli/routes/sessions.js +15 -0
  226. package/src/interfaces/cli/routes/setup.js +18 -0
  227. package/src/interfaces/cli/routes/skills.js +20 -0
  228. package/src/interfaces/cli/routes/status.js +12 -0
  229. package/src/interfaces/cli/routes/task.js +26 -0
  230. package/src/interfaces/cli/routes/telegram.js +39 -0
  231. package/src/interfaces/cli/routes/update.js +24 -0
  232. package/src/interfaces/cli/routes/voice.js +17 -0
  233. package/src/interfaces/desktop/main.js +6 -7
  234. package/src/interfaces/desktop/renderer.js +0 -5
  235. package/src/interfaces/mcp-server/index.js +15 -14
  236. package/src/interfaces/tui/_shims/globals.d.ts +8 -0
  237. package/src/interfaces/tui/tsconfig.json +4 -1
  238. package/src/interfaces/web/README.md +6 -6
  239. package/src/interfaces/web/dist/assets/index-CvEoGtTf.js +849 -0
  240. package/src/interfaces/web/dist/assets/index-CvEoGtTf.js.map +1 -0
  241. package/src/interfaces/web/dist/assets/index-DzBBXFaO.css +1 -0
  242. package/src/interfaces/web/dist/index.html +2 -2
  243. package/src/interfaces/web/package-lock.json +11 -10
  244. package/src/interfaces/web/src/components/AddProjectDialog.tsx +1 -1
  245. package/src/interfaces/web/src/components/Section.tsx +18 -3
  246. package/src/interfaces/web/src/components/agents/AgentFormFields.tsx +1 -1
  247. package/src/interfaces/web/src/components/chat/ChatList.tsx +2 -2
  248. package/src/interfaces/web/src/components/chat/MessageBubble.tsx +13 -0
  249. package/src/interfaces/web/src/components/chat/SkillPicker.tsx +1 -1
  250. package/src/interfaces/web/src/components/code/CodeFileTree.tsx +1 -1
  251. package/src/interfaces/web/src/components/code/CodeTerminal.tsx +1 -1
  252. package/src/interfaces/web/src/components/cron/CronPicker.tsx +196 -0
  253. package/src/interfaces/web/src/components/desktop/DesktopStatusCard.tsx +1 -1
  254. package/src/interfaces/web/src/components/files/FileBrowser.tsx +2 -2
  255. package/src/interfaces/web/src/components/inbox/InboxList.tsx +145 -0
  256. package/src/interfaces/web/src/components/memory/MemoryBrowser.tsx +36 -6
  257. package/src/interfaces/web/src/components/routines/ExecutionsList.tsx +1 -1
  258. package/src/interfaces/web/src/components/routines/RoutineDetail.tsx +16 -4
  259. package/src/interfaces/web/src/components/routines/RoutineEditor.tsx +14 -4
  260. package/src/interfaces/web/src/components/routines/shared.ts +14 -5
  261. package/src/interfaces/web/src/components/settings/DesktopSettingsPanel.tsx +1 -1
  262. package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +1 -1
  263. package/src/interfaces/web/src/components/settings/NudgePanel.tsx +183 -0
  264. package/src/interfaces/web/src/components/settings/ProfilePanel.tsx +36 -12
  265. package/src/interfaces/web/src/components/settings/SkillsInspectorPanel.tsx +1 -1
  266. package/src/interfaces/web/src/components/settings/SkillsManager.tsx +2 -2
  267. package/src/interfaces/web/src/components/tasks/TaskDetailPanel.tsx +1 -1
  268. package/src/interfaces/web/src/components/ui/filter-chips.tsx +47 -0
  269. package/src/interfaces/web/src/components/ui.tsx +1 -0
  270. package/src/interfaces/web/src/constants/index.ts +1 -0
  271. package/src/interfaces/web/src/hooks/useChat.ts +5 -1
  272. package/src/interfaces/web/src/hooks/useDaemonStatus.ts +1 -1
  273. package/src/interfaces/web/src/hooks/useDevices.ts +1 -1
  274. package/src/interfaces/web/src/hooks/useEngines.ts +1 -1
  275. package/src/interfaces/web/src/hooks/useGlobalConfig.ts +2 -2
  276. package/src/interfaces/web/src/hooks/useIdentity.ts +1 -1
  277. package/src/interfaces/web/src/hooks/useInbox.ts +1 -1
  278. package/src/interfaces/web/src/hooks/useNudges.ts +38 -0
  279. package/src/interfaces/web/src/hooks/useProfiles.ts +3 -3
  280. package/src/interfaces/web/src/hooks/useProjects.ts +1 -1
  281. package/src/interfaces/web/src/hooks/useTelegram.ts +3 -3
  282. package/src/interfaces/web/src/hooks/useTokenBootstrap.ts +3 -3
  283. package/src/interfaces/web/src/i18n/en.ts +127 -0
  284. package/src/interfaces/web/src/i18n/es.ts +127 -0
  285. package/src/interfaces/web/src/lib/api/admin.ts +11 -11
  286. package/src/interfaces/web/src/lib/api/agents.ts +14 -14
  287. package/src/interfaces/web/src/lib/api/artifacts.ts +11 -11
  288. package/src/interfaces/web/src/lib/api/code.ts +1 -1
  289. package/src/interfaces/web/src/lib/api/commitments.ts +57 -0
  290. package/src/interfaces/web/src/lib/api/conversations.ts +8 -8
  291. package/src/interfaces/web/src/lib/api/deck.ts +3 -3
  292. package/src/interfaces/web/src/lib/api/desktop.ts +8 -8
  293. package/src/interfaces/web/src/lib/api/embeddings.ts +3 -3
  294. package/src/interfaces/web/src/lib/api/engines.ts +3 -3
  295. package/src/interfaces/web/src/lib/api/filesystem.ts +2 -2
  296. package/src/interfaces/web/src/lib/api/health.ts +1 -1
  297. package/src/interfaces/web/src/lib/api/identity.ts +2 -2
  298. package/src/interfaces/web/src/lib/api/inbox.ts +1 -1
  299. package/src/interfaces/web/src/lib/api/integrations.ts +8 -8
  300. package/src/interfaces/web/src/lib/api/mcps.ts +6 -6
  301. package/src/interfaces/web/src/lib/api/messages.ts +3 -3
  302. package/src/interfaces/web/src/lib/api/notebook.ts +23 -0
  303. package/src/interfaces/web/src/lib/api/nudges.ts +53 -0
  304. package/src/interfaces/web/src/lib/api/organization.ts +7 -7
  305. package/src/interfaces/web/src/lib/api/profiles.ts +8 -8
  306. package/src/interfaces/web/src/lib/api/projectFiles.ts +5 -5
  307. package/src/interfaces/web/src/lib/api/projects.ts +12 -12
  308. package/src/interfaces/web/src/lib/api/routines.ts +7 -7
  309. package/src/interfaces/web/src/lib/api/sessions.ts +2 -2
  310. package/src/interfaces/web/src/lib/api/skills.ts +11 -11
  311. package/src/interfaces/web/src/lib/api/super_agent.ts +3 -3
  312. package/src/interfaces/web/src/lib/api/tasks.ts +12 -12
  313. package/src/interfaces/web/src/lib/api/telegram.ts +14 -14
  314. package/src/interfaces/web/src/lib/api/tools.ts +1 -1
  315. package/src/interfaces/web/src/lib/api/vars.ts +4 -4
  316. package/src/interfaces/web/src/lib/api/voice.ts +6 -6
  317. package/src/interfaces/web/src/lib/cron.ts +196 -0
  318. package/src/interfaces/web/src/lib/when.ts +32 -0
  319. package/src/interfaces/web/src/screens/InboxScreen.tsx +107 -77
  320. package/src/interfaces/web/src/screens/ProjectScreen.tsx +5 -2
  321. package/src/interfaces/web/src/screens/SettingsScreen.tsx +17 -3
  322. package/src/interfaces/web/src/screens/base/AgentDefaultsTab.tsx +1 -1
  323. package/src/interfaces/web/src/screens/base/CommitmentsTab.tsx +239 -0
  324. package/src/interfaces/web/src/screens/base/GlobalTasksTab.tsx +103 -20
  325. package/src/interfaces/web/src/screens/base/LogsTab.tsx +18 -3
  326. package/src/interfaces/web/src/screens/base/SessionsTab.tsx +1 -1
  327. package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +1 -1
  328. package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +1 -1
  329. package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +1 -1
  330. package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +1 -1
  331. package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +8 -8
  332. package/src/interfaces/web/src/screens/project/AgentsTab.tsx +2 -2
  333. package/src/interfaces/web/src/screens/project/ChatTab.tsx +24 -6
  334. package/src/interfaces/web/src/screens/project/ConfigTab.tsx +1 -1
  335. package/src/interfaces/web/src/screens/project/McpsTab.tsx +3 -3
  336. package/src/interfaces/web/src/screens/project/Overview.tsx +6 -6
  337. package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +15 -13
  338. package/src/interfaces/web/src/screens/project/StructureTab.tsx +1 -1
  339. package/src/interfaces/web/src/screens/project/TasksTab.tsx +1 -1
  340. package/src/interfaces/web/src/screens/project/VarsTab.tsx +1 -1
  341. package/src/interfaces/web/src/types/daemon.ts +10 -1
  342. package/src/interfaces/web/vite.config.ts +13 -18
  343. package/src/interfaces/web/dist/assets/index-BptlbKjU.js +0 -824
  344. package/src/interfaces/web/dist/assets/index-BptlbKjU.js.map +0 -1
  345. package/src/interfaces/web/dist/assets/index-D_EJEA1n.css +0 -1
  346. package/src/skills/apc-context/SKILL.md +0 -159
  347. /package/src/{host/daemon → core}/runtimes/_spawn.js +0 -0
  348. /package/src/{host/daemon → core}/runtimes/aider.js +0 -0
  349. /package/src/{host/daemon → core}/runtimes/antigravity.js +0 -0
  350. /package/src/{host/daemon → core}/runtimes/claude-code.js +0 -0
  351. /package/src/{host/daemon → core}/runtimes/codex.js +0 -0
  352. /package/src/{host/daemon → core}/runtimes/cursor-agent.js +0 -0
  353. /package/src/{host/daemon → core}/runtimes/gemini-cli.js +0 -0
  354. /package/src/{host/daemon → core}/runtimes/index.js +0 -0
  355. /package/src/{host/daemon → core}/runtimes/opencode.js +0 -0
  356. /package/src/{host/daemon → core}/runtimes/qwen-code.js +0 -0
@@ -16,15 +16,16 @@ import {
16
16
  setEnabled as setRoutineEnabled,
17
17
  runRoutineNow,
18
18
  } from "#core/routines/index.js";
19
+ import { CHANNELS } from "#core/constants/channels.js";
19
20
 
20
- export function register(app, { projects, registries, plugins, project, config }) {
21
- app.get("/projects/:pid/routines", (req, res) => {
21
+ export function register(api, { projects, registries, plugins, project, config }) {
22
+ api.get("/projects/:pid/routines", (req, res) => {
22
23
  const p = project(req, res);
23
24
  if (!p) return;
24
25
  res.json(listRoutines(p.storagePath));
25
26
  });
26
27
 
27
- app.get("/projects/:pid/routines/:name", (req, res) => {
28
+ api.get("/projects/:pid/routines/:name", (req, res) => {
28
29
  const p = project(req, res);
29
30
  if (!p) return;
30
31
  const r = getRoutine(p.storagePath, req.params.name);
@@ -32,41 +33,64 @@ export function register(app, { projects, registries, plugins, project, config }
32
33
  res.json(r);
33
34
  });
34
35
 
35
- app.post("/projects/:pid/routines", (req, res) => {
36
+ api.post("/projects/:pid/routines", (req, res) => {
36
37
  const p = project(req, res);
37
38
  if (!p) return;
38
39
  try {
39
40
  // Accepts every field including the pipeline extensions
40
41
  // (pre_commands, post_commands, skip_prompt_on).
42
+ const existed = !!getRoutine(p.storagePath, (req.body || {}).name);
41
43
  const r = upsertRoutine(p.storagePath, req.body || {});
44
+ // Say it happened, where it happened. A routine appearing in a list on a
45
+ // screen nobody has open is not the same as being told one now exists —
46
+ // and "what did that just create?" is the question people actually ask
47
+ // after asking for something to be set up.
48
+ p.logMessage?.({
49
+ channel: CHANNELS.ROUTINE,
50
+ direction: "out",
51
+ type: "system",
52
+ actor_id: "apx:routine",
53
+ author: "apx",
54
+ body: existed
55
+ ? `routine ${r.name} updated (${r.kind}, ${r.schedule})`
56
+ : `routine ${r.name} created (${r.kind}, ${r.schedule})`,
57
+ meta: {
58
+ event: existed ? "routine_updated" : "routine_created",
59
+ routine: r.name,
60
+ routine_id: r.id,
61
+ kind: r.kind,
62
+ schedule: r.schedule,
63
+ enabled: r.enabled !== false,
64
+ },
65
+ });
42
66
  res.status(201).json(r);
43
67
  } catch (e) {
44
68
  res.status(400).json({ error: e.message });
45
69
  }
46
70
  });
47
71
 
48
- app.delete("/projects/:pid/routines/:name", (req, res) => {
72
+ api.delete("/projects/:pid/routines/:name", (req, res) => {
49
73
  const p = project(req, res);
50
74
  if (!p) return;
51
75
  const ok = deleteRoutine(p.storagePath, req.params.name);
52
76
  res.status(ok ? 204 : 404).end();
53
77
  });
54
78
 
55
- app.post("/projects/:pid/routines/:name/enable", (req, res) => {
79
+ api.post("/projects/:pid/routines/:name/enable", (req, res) => {
56
80
  const p = project(req, res);
57
81
  if (!p) return;
58
82
  setRoutineEnabled(p.storagePath, req.params.name, true);
59
83
  res.json({ ok: true });
60
84
  });
61
85
 
62
- app.post("/projects/:pid/routines/:name/disable", (req, res) => {
86
+ api.post("/projects/:pid/routines/:name/disable", (req, res) => {
63
87
  const p = project(req, res);
64
88
  if (!p) return;
65
89
  setRoutineEnabled(p.storagePath, req.params.name, false);
66
90
  res.json({ ok: true });
67
91
  });
68
92
 
69
- app.post("/projects/:pid/routines/:name/run", async (req, res) => {
93
+ api.post("/projects/:pid/routines/:name/run", async (req, res) => {
70
94
  const p = project(req, res);
71
95
  if (!p) return;
72
96
  const r = getRoutine(p.storagePath, req.params.name);
@@ -4,8 +4,8 @@
4
4
  import path from "node:path";
5
5
  import { execFile } from "node:child_process";
6
6
 
7
- export function register(app, { projects }) {
8
- app.post("/run", (req, res) => {
7
+ export function register(api, { projects }) {
8
+ api.post("/run", (req, res) => {
9
9
  const {
10
10
  cmd,
11
11
  cwd: cwdOverride,
@@ -12,7 +12,7 @@ import { apcProjectFile, apcAgentsDir } from "#core/apc/paths.js";
12
12
  import { readSessionFrontmatter } from "#core/stores/sessions.js";
13
13
  import { buildAgentSystem } from "#core/agent/build-agent-system.js";
14
14
  import { CHANNELS } from "#core/constants/channels.js";
15
- import { getRuntime, RUNTIME_IDS } from "../runtimes/index.js";
15
+ import { getRuntime, RUNTIME_IDS } from "#core/runtimes/index.js";
16
16
  import { detectAll } from "#core/runtimes/detect.js";
17
17
  import { buildRuntimeBridgeHint as buildApfHint } from "#core/agent/runtime-bridge.js";
18
18
  import {
@@ -22,17 +22,17 @@ import {
22
22
  } from "#core/stores/runtime-sessions.js";
23
23
  import { runSuperAgent, isSuperAgentEnabled } from "#core/agent/super-agent.js";
24
24
 
25
- export function register(app, { projects, registries, plugins, project, config }) {
26
- app.get("/runtimes", (_req, res) =>
25
+ export function register(api, { projects, registries, plugins, project, config }) {
26
+ api.get("/runtimes", (_req, res) =>
27
27
  res.json({ runtimes: RUNTIME_IDS })
28
28
  );
29
29
 
30
- app.get("/env/detect", async (_req, res) => {
30
+ api.get("/env/detect", async (_req, res) => {
31
31
  const detected = await detectAll();
32
32
  res.json(detected);
33
33
  });
34
34
 
35
- app.post("/projects/:pid/agents/:slug/runtime", async (req, res) => {
35
+ api.post("/projects/:pid/agents/:slug/runtime", async (req, res) => {
36
36
  const p = project(req, res);
37
37
  if (!p) return;
38
38
  const { runtime, prompt, timeoutMs } = req.body || {};
@@ -146,7 +146,7 @@ export function register(app, { projects, registries, plugins, project, config }
146
146
  });
147
147
 
148
148
  // ---- Session resume — reads APC session file + (optionally) external transcript ----
149
- app.get("/projects/:pid/sessions/:id/resume", async (req, res) => {
149
+ api.get("/projects/:pid/sessions/:id/resume", async (req, res) => {
150
150
  const p = project(req, res);
151
151
  if (!p) return;
152
152
  const { id } = req.params;
@@ -0,0 +1,50 @@
1
+ // The super-agent's own notebook — ~/.apx/memory.md.
2
+ //
3
+ // GET /notebook → { body, path, size, approx_tokens, entries, consolidated }
4
+ // PUT /notebook { body }
5
+ //
6
+ // WHY THIS EXISTS. This file is the one memory that ships in EVERY prompt on
7
+ // every channel, and until now it was the only one with no screen. The Memories
8
+ // tab listed project memory and each agent's memory; the super-agent's own
9
+ // notebook was reachable from the model (read_self_memory / remember) and from
10
+ // the CLI, and nowhere a person could look. So "where is Roby's memory?" had no
11
+ // answer in the product, which is a fair thing to be confused by.
12
+ //
13
+ // It reports its own size because that size is a tax paid on every turn — see
14
+ // core/memory/consolidate.js.
15
+ import { readSelfMemory, SELF_MEMORY_PATH } from "#core/agent/self-memory.js";
16
+ import { notebookSize } from "#core/memory/consolidate.js";
17
+ import fs from "node:fs";
18
+ import path from "node:path";
19
+
20
+ const MAX_BODY = 256 * 1024; // a notebook past this is a symptom, not a note
21
+
22
+ export function register(api) {
23
+ api.get("/notebook", (_req, res) => {
24
+ try {
25
+ const body = readSelfMemory();
26
+ res.json({ body, path: SELF_MEMORY_PATH, ...notebookSize() });
27
+ } catch (e) {
28
+ res.status(500).json({ error: e.message });
29
+ }
30
+ });
31
+
32
+ api.put("/notebook", (req, res) => {
33
+ const { body } = req.body || {};
34
+ if (typeof body !== "string") {
35
+ return res.status(400).json({ error: "body (string) required" });
36
+ }
37
+ if (body.length > MAX_BODY) {
38
+ return res.status(413).json({
39
+ error: `notebook too large (${body.length} > ${MAX_BODY} bytes) — it ships in every prompt`,
40
+ });
41
+ }
42
+ try {
43
+ fs.mkdirSync(path.dirname(SELF_MEMORY_PATH), { recursive: true });
44
+ fs.writeFileSync(SELF_MEMORY_PATH, body);
45
+ res.json({ ok: true, path: SELF_MEMORY_PATH, ...notebookSize() });
46
+ } catch (e) {
47
+ res.status(500).json({ error: e.message });
48
+ }
49
+ });
50
+ }
@@ -2,23 +2,30 @@
2
2
  // GET /sessions/search?q=…&project=…&limit=20
3
3
  // POST /sessions/:id/compact resolves which project/agent owns the file
4
4
  // then delegates to compactConversation.
5
- import path from "node:path";
6
5
  import { readAgents } from "#core/apc/parser.js";
7
6
  import { compactConversation } from "#core/stores/conversations-compactor.js";
8
7
  import { searchSessions, findSessionFile } from "#core/stores/sessions-search.js";
8
+ import { asyncRoute } from "./shared.js";
9
+ import { resolveProject } from "#core/apc/projects-helpers.js";
9
10
 
11
+ // Plural on purpose: no ref means "search every project". This is a different
12
+ // operation from core's resolveProject (which resolves exactly one and throws),
13
+ // but the *matching* must agree with it — this used to accept only an id or an
14
+ // exact path, so `?project=savia` matched nothing here while working on every
15
+ // other route.
10
16
  function resolveProjects(projects, projectRef) {
11
- const all = projects.list();
12
- if (projectRef != null) {
13
- const ref = String(projectRef);
14
- const found = all.find((p) => String(p.id) === ref || p.path === path.resolve(ref));
15
- return found ? [projects.get(found.id)] : [];
17
+ if (projectRef == null || projectRef === "") {
18
+ return projects.list().map((p) => projects.get(p.id)).filter(Boolean);
19
+ }
20
+ try {
21
+ return [resolveProject(projects, projectRef)];
22
+ } catch {
23
+ return []; // unknown or ambiguous ref → no results, not a 500
16
24
  }
17
- return all.map((p) => projects.get(p.id)).filter(Boolean);
18
25
  }
19
26
 
20
- export function register(app, { projects, config }) {
21
- app.get("/sessions/search", (req, res) => {
27
+ export function register(api, { projects, config }) {
28
+ api.get("/sessions/search", (req, res) => {
22
29
  const { q, project: projectRef, limit = "20" } = req.query;
23
30
  if (!q) return res.status(400).json({ error: "q required" });
24
31
  const lim = Math.min(parseInt(limit, 10) || 20, 200);
@@ -27,7 +34,7 @@ export function register(app, { projects, config }) {
27
34
  res.json({ q, count: results.length, results });
28
35
  });
29
36
 
30
- app.post("/sessions/:id/compact", async (req, res) => {
37
+ api.post("/sessions/:id/compact", asyncRoute(async (req, res) => {
31
38
  const { id } = req.params;
32
39
  const { model: modelOverride, project: projectRef } = req.body || {};
33
40
  const candidates = resolveProjects(projects, projectRef);
@@ -57,5 +64,5 @@ export function register(app, { projects, config }) {
57
64
  } catch (e) {
58
65
  res.status(500).json({ error: e.message });
59
66
  }
60
- });
67
+ }));
61
68
  }
@@ -12,16 +12,16 @@ import {
12
12
  agentSessionsDir,
13
13
  createAgentSessionFile,
14
14
  } from "#core/stores/sessions.js";
15
- import { collectAllSessions, filterSessionsByQuery } from "#interfaces/cli/commands/sessions.js";
15
+ import { collectAllSessions, filterSessionsByQuery } from "#core/sessions/index.js";
16
16
  import { pageEnvelope } from "./shared.js";
17
17
 
18
- export function register(app, { projects, project }) {
18
+ export function register(api, { projects, project }) {
19
19
  // Cross-engine sessions (apx · claude · codex), newest first. Returns a
20
20
  // { meta, data } envelope (meta = pagination info, data = rows). Paginated
21
21
  // via ?limit & ?offset; with no limit, data is the full set as one page.
22
22
  // Optional ?q= filters via the same core as `apx session find` (title match,
23
23
  // + transcript content when ?deep=1) so terminal and web search are identical.
24
- app.get("/sessions", (req, res) => {
24
+ api.get("/sessions", (req, res) => {
25
25
  const engineId = req.query.engine ? String(req.query.engine) : null;
26
26
  const q = req.query.q ? String(req.query.q) : "";
27
27
  const deep = req.query.deep === "1" || req.query.deep === "true";
@@ -49,7 +49,7 @@ export function register(app, { projects, project }) {
49
49
  res.json(pageEnvelope(rows, req.query));
50
50
  });
51
51
 
52
- app.get("/projects/:pid/agents/:slug/sessions", (req, res) => {
52
+ api.get("/projects/:pid/agents/:slug/sessions", (req, res) => {
53
53
  const p = project(req, res);
54
54
  if (!p) return;
55
55
  const agents = readAgents(p.path);
@@ -78,7 +78,7 @@ export function register(app, { projects, project }) {
78
78
  res.json(sessions);
79
79
  });
80
80
 
81
- app.post("/projects/:pid/agents/:slug/sessions", (req, res) => {
81
+ api.post("/projects/:pid/agents/:slug/sessions", (req, res) => {
82
82
  const p = project(req, res);
83
83
  if (!p) return;
84
84
  const { title, body = "" } = req.body || {};
@@ -93,7 +93,7 @@ export function register(app, { projects, project }) {
93
93
  });
94
94
 
95
95
  // GET session by filename (sid may include or omit the .md extension)
96
- app.get("/projects/:pid/sessions/:sid", (req, res) => {
96
+ api.get("/projects/:pid/sessions/:sid", (req, res) => {
97
97
  const p = project(req, res);
98
98
  if (!p) return;
99
99
  const sid = req.params.sid;
@@ -2,7 +2,6 @@
2
2
  //
3
3
  // These were inlined in api.js (monolith). They are kept dependency-free so
4
4
  // any route module can `import` them without pulling in the world.
5
- import fs from "node:fs";
6
5
  import path from "node:path";
7
6
  import { randomUUID } from "node:crypto";
8
7
  import { appendErrorTrace, previewText } from "#core/logging.js";
@@ -11,6 +10,7 @@ import { agentMemoryPath } from "#core/agent/memory.js";
11
10
  import { apcMemoryFile } from "#core/apc/paths.js";
12
11
  import { CHANNELS } from "#core/constants/channels.js";
13
12
  import { isKnownSpaRoute } from "./web.js";
13
+ import { apiPath, isApiPath } from "./prefix.js";
14
14
 
15
15
  export const nowIso = () =>
16
16
  new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
@@ -20,6 +20,44 @@ export const nowIso = () =>
20
20
  // full set as a single page (data = 100% of rows) so non-paginated callers get
21
21
  // the same shape — meta just reports one page covering everything.
22
22
  // meta: { total, offset, limit, pageSize, page, pageCount }
23
+ /**
24
+ * Wrap an async route handler so a rejected promise becomes a 500 instead of
25
+ * an unhandled rejection.
26
+ *
27
+ * Express 4 does not await handlers: if one throws asynchronously, the error
28
+ * never reaches the error middleware, the request hangs, and on Node >= 15 the
29
+ * unhandled rejection takes the whole daemon down. Several routes awaited work
30
+ * with no try/catch, so a single failing preview or engine probe could kill a
31
+ * process serving the SPA, Telegram polling and voice at the same time.
32
+ *
33
+ * api.get("/things", asyncRoute(async (req, res) => { ... }))
34
+ */
35
+ export function asyncRoute(handler) {
36
+ return (req, res, next) => Promise.resolve(handler(req, res, next)).catch(next);
37
+ }
38
+
39
+ /**
40
+ * Terminal error middleware. Mounted after every route; turns anything that
41
+ * reached `next(err)` into a JSON `{ error }` with a real status code.
42
+ */
43
+ export function errorMiddleware(log = () => {}) {
44
+ // eslint-disable-next-line no-unused-vars -- Express detects the 4-arg shape.
45
+ return (err, req, res, _next) => {
46
+ const status = Number(err?.status || err?.statusCode) || 500;
47
+ const message = err?.message || "internal error";
48
+ log(`error: ${req.method} ${req.originalUrl} → ${status}: ${err?.stack || message}`);
49
+ try {
50
+ appendErrorTrace({
51
+ where: `${req.method} ${req.originalUrl}`,
52
+ error: message,
53
+ trace_id: req.apxTraceId,
54
+ });
55
+ } catch { /* tracing must never mask the original error */ }
56
+ if (res.headersSent) return;
57
+ res.status(status).json({ error: message });
58
+ };
59
+ }
60
+
23
61
  export function pageEnvelope(rows, query = {}) {
24
62
  const total = rows.length;
25
63
  const hasLimit = query.limit != null && query.limit !== "";
@@ -46,12 +84,19 @@ export function traceIdMiddleware(req, res, next) {
46
84
  next();
47
85
  }
48
86
 
49
- // Paths that bypass auth: /health for liveness probes, /pair/* so a fresh
50
- // client can bootstrap a token without already having one, and
51
- // /admin/web-token so the local same-origin admin panel can self-bootstrap.
52
- // /pair/init and /admin/web-token both enforce localhost-only checks of
87
+ // Paths that bypass auth: /api/health for liveness probes, /api/pair/* so a
88
+ // fresh client can bootstrap a token without already having one, and
89
+ // /api/admin/web-token so the local same-origin admin panel can self-bootstrap.
90
+ // /api/pair/init and /api/admin/web-token both enforce localhost-only checks of
53
91
  // their own — the auth middleware just gets out of their way.
54
- const UNAUTHENTICATED_PREFIXES = ["/health", "/pair/", "/admin/web-token"];
92
+ //
93
+ // These are full paths: the middleware runs at app level (before the /api
94
+ // router), so req.path is the complete URL path.
95
+ const UNAUTHENTICATED_PREFIXES = [
96
+ apiPath("/health"),
97
+ apiPath("/pair/"),
98
+ apiPath("/admin/web-token"),
99
+ ];
55
100
 
56
101
  // Does this path look like a static asset (has a file extension)? Vite emits
57
102
  // hashed, extension-bearing filenames (index-abc123.js, logo.svg, font.woff2),
@@ -62,18 +107,19 @@ function isStaticAssetPath(p) {
62
107
  }
63
108
 
64
109
  function isUnauthenticatedPath(p, method = "GET") {
65
- if (p === "/health") return true;
66
- if (p === "/admin/web-token") return true;
67
110
  for (const prefix of UNAUTHENTICATED_PREFIXES) {
68
111
  if (p === prefix.replace(/\/$/, "") || p.startsWith(prefix)) return true;
69
112
  }
113
+ // Everything else under /api needs a token, full stop. Checked BEFORE the
114
+ // SPA-bootstrap exemption below, which would otherwise let a data route pass
115
+ // simply because its last segment happens to carry a file extension —
116
+ // GET /api/projects/0/artifacts/report.html is a data route, not an asset.
117
+ if (isApiPath(p)) return false;
70
118
  // SPA bootstrap: the admin bundle loads before it holds a bearer, so a GET
71
119
  // for a static asset or a known client-router route is served without auth —
72
- // the bundle then fetches /admin/web-token. Everything else, including every
73
- // data GET (/skills, /plugins, /embeddings, …), REQUIRES a token. This is an
74
- // allowlist by construction: a new data route can never silently become
75
- // public just because someone forgot to register its prefix (the old
76
- // denylist failure mode).
120
+ // the bundle then fetches /api/admin/web-token. Since every data route now
121
+ // lives under /api and returned false above, this exemption can only ever
122
+ // reach panel assets and client-router paths.
77
123
  if (method === "GET" && (isStaticAssetPath(p) || isKnownSpaRoute(p))) return true;
78
124
  return false;
79
125
  }
@@ -113,8 +113,8 @@ function indexStatus() {
113
113
  };
114
114
  }
115
115
 
116
- export function register(app /*, ctx */) {
117
- app.get("/skills", (req, res) => {
116
+ export function register(api /*, ctx */) {
117
+ api.get("/skills", (req, res) => {
118
118
  const projectPath = typeof req.query?.project_path === "string" && req.query.project_path
119
119
  ? req.query.project_path
120
120
  : undefined;
@@ -149,7 +149,7 @@ export function register(app /*, ctx */) {
149
149
 
150
150
  // ---- Full detail (viewer) ----------------------------------------------
151
151
 
152
- app.get("/skills/:slug/detail", (req, res) => {
152
+ api.get("/skills/:slug/detail", (req, res) => {
153
153
  try {
154
154
  const projectPath = typeof req.query?.project_path === "string" && req.query.project_path
155
155
  ? req.query.project_path
@@ -175,7 +175,7 @@ export function register(app /*, ctx */) {
175
175
 
176
176
  // ---- Enable / disable per scope ----------------------------------------
177
177
 
178
- app.put("/skills/enabled", (req, res) => {
178
+ api.put("/skills/enabled", (req, res) => {
179
179
  try {
180
180
  const { slug, enabled, scope, project_path } = req.body || {};
181
181
  if (!slug || typeof slug !== "string") {
@@ -202,7 +202,7 @@ export function register(app /*, ctx */) {
202
202
  // ---- Create / import user skills ---------------------------------------
203
203
 
204
204
  // Online editor: write a SKILL.md from slug + description + body.
205
- app.post("/skills", (req, res) => {
205
+ api.post("/skills", (req, res) => {
206
206
  try {
207
207
  const { slug, description, body, project_path } = req.body || {};
208
208
  if (!slug || typeof slug !== "string" || !SLUG_RE.test(slug)) {
@@ -220,7 +220,7 @@ export function register(app /*, ctx */) {
220
220
  });
221
221
 
222
222
  // Import from an uploaded .zip (sent as base64 in JSON — skills are tiny).
223
- app.post("/skills/import/zip", (req, res) => {
223
+ api.post("/skills/import/zip", (req, res) => {
224
224
  let tmp;
225
225
  try {
226
226
  const { data, project_path } = req.body || {};
@@ -257,7 +257,7 @@ export function register(app /*, ctx */) {
257
257
 
258
258
  // Import by cloning a git repo. The repo root (or its single skill subdir)
259
259
  // must contain a SKILL.md.
260
- app.post("/skills/import/repo", (req, res) => {
260
+ api.post("/skills/import/repo", (req, res) => {
261
261
  let tmp;
262
262
  try {
263
263
  const { url, project_path } = req.body || {};
@@ -293,7 +293,7 @@ export function register(app /*, ctx */) {
293
293
  }
294
294
  });
295
295
 
296
- app.delete("/skills/:slug", (req, res) => {
296
+ api.delete("/skills/:slug", (req, res) => {
297
297
  try {
298
298
  const slug = req.params.slug;
299
299
  if (!slug || !SLUG_RE.test(slug)) {
@@ -344,7 +344,7 @@ export function register(app /*, ctx */) {
344
344
 
345
345
  // ---- Inspector config + status -----------------------------------------
346
346
 
347
- app.get("/skills/inspector", (_req, res) => {
347
+ api.get("/skills/inspector", (_req, res) => {
348
348
  try {
349
349
  const cfg = readConfig();
350
350
  res.json({
@@ -358,7 +358,7 @@ export function register(app /*, ctx */) {
358
358
  }
359
359
  });
360
360
 
361
- app.put("/skills/inspector", (req, res) => {
361
+ api.put("/skills/inspector", (req, res) => {
362
362
  try {
363
363
  const patch = req.body || {};
364
364
  const cfg = readConfig();
@@ -388,7 +388,7 @@ export function register(app /*, ctx */) {
388
388
 
389
389
  // ---- Index build --------------------------------------------------------
390
390
 
391
- app.post("/skills/index", async (req, res) => {
391
+ api.post("/skills/index", async (req, res) => {
392
392
  try {
393
393
  const { project_path, force } = req.body || {};
394
394
  const cfg = readConfig();
@@ -423,7 +423,7 @@ export function register(app /*, ctx */) {
423
423
 
424
424
  // ---- Dry-run ------------------------------------------------------------
425
425
 
426
- app.post("/skills/inspect", async (req, res) => {
426
+ api.post("/skills/inspect", async (req, res) => {
427
427
  try {
428
428
  const { prompt, project_path } = req.body || {};
429
429
  if (!prompt || typeof prompt !== "string") {
@@ -91,8 +91,8 @@ function wrapOnEventForLog(send, { trace_id, channel }) {
91
91
  };
92
92
  }
93
93
 
94
- export function register(app, { projects, registries, plugins, project, config }) {
95
- app.post("/projects/:pid/super-agent/chat/stream", async (req, res) => {
94
+ export function register(api, { projects, registries, plugins, project, config }) {
95
+ api.post("/projects/:pid/super-agent/chat/stream", async (req, res) => {
96
96
  const p = project(req, res);
97
97
  if (!p) return;
98
98
  // Optional coding-surface knobs: the terminal Code TUI (apx code, Build
@@ -223,7 +223,7 @@ export function register(app, { projects, registries, plugins, project, config }
223
223
  // <id>` when the session lives outside any registered APX project (e.g. a
224
224
  // raw Claude/Codex session). Returns { text } so callers can format the
225
225
  // summary however they want.
226
- app.post("/super-agent/summarize", async (req, res) => {
226
+ api.post("/super-agent/summarize", async (req, res) => {
227
227
  const { prompt, context_note: contextNote = "", model, max_tokens } = req.body || {};
228
228
  if (!prompt) return res.status(400).json({ error: "prompt required" });
229
229
  try {
@@ -254,7 +254,7 @@ export function register(app, { projects, registries, plugins, project, config }
254
254
  }
255
255
  });
256
256
 
257
- app.post("/projects/:pid/super-agent/chat", async (req, res) => {
257
+ api.post("/projects/:pid/super-agent/chat", async (req, res) => {
258
258
  const p = project(req, res);
259
259
  if (!p) return;
260
260
  const { prompt, previousMessages, model, maxIters, maxTokens, completionContract } =
@@ -24,11 +24,11 @@ import {
24
24
  } from "#core/stores/tasks.js";
25
25
  import { pageEnvelope } from "./shared.js";
26
26
 
27
- export function register(app, { project, projects }) {
27
+ export function register(api, { project, projects }) {
28
28
  // Global tasks across every project, newest first. Returns a { meta, data }
29
29
  // envelope. Paginated via ?limit & ?offset; with no limit, data is the full
30
30
  // set as one page.
31
- app.get("/tasks", (req, res) => {
31
+ api.get("/tasks", (req, res) => {
32
32
  const { state, tag, agent, due_before, due_after, status, updated_since } = req.query;
33
33
 
34
34
  // Resolve the registered projects to what core needs, dropping any the
@@ -63,7 +63,7 @@ export function register(app, { project, projects }) {
63
63
 
64
64
  // Per-project tasks. Returns a { meta, data } envelope; with no ?limit the
65
65
  // data array is the full filtered set (one page).
66
- app.get("/projects/:pid/tasks", (req, res) => {
66
+ api.get("/projects/:pid/tasks", (req, res) => {
67
67
  const p = project(req, res);
68
68
  if (!p) return;
69
69
  const { state, tag, agent, due_before, due_after, status, updated_since } = req.query;
@@ -79,7 +79,7 @@ export function register(app, { project, projects }) {
79
79
  res.json(pageEnvelope(all, req.query));
80
80
  });
81
81
 
82
- app.post("/projects/:pid/tasks", (req, res) => {
82
+ api.post("/projects/:pid/tasks", (req, res) => {
83
83
  const p = project(req, res);
84
84
  if (!p) return;
85
85
  try {
@@ -90,7 +90,7 @@ export function register(app, { project, projects }) {
90
90
  }
91
91
  });
92
92
 
93
- app.get("/projects/:pid/tasks/:id", (req, res) => {
93
+ api.get("/projects/:pid/tasks/:id", (req, res) => {
94
94
  const p = project(req, res);
95
95
  if (!p) return;
96
96
  const task = getTask(p.storagePath, req.params.id);
@@ -98,7 +98,7 @@ export function register(app, { project, projects }) {
98
98
  res.json(task);
99
99
  });
100
100
 
101
- app.patch("/projects/:pid/tasks/:id", (req, res) => {
101
+ api.patch("/projects/:pid/tasks/:id", (req, res) => {
102
102
  const p = project(req, res);
103
103
  if (!p) return;
104
104
  const { patch } = req.body || {};
@@ -110,7 +110,7 @@ export function register(app, { project, projects }) {
110
110
  res.json(updated);
111
111
  });
112
112
 
113
- app.post("/projects/:pid/tasks/:id/done", (req, res) => {
113
+ api.post("/projects/:pid/tasks/:id/done", (req, res) => {
114
114
  const p = project(req, res);
115
115
  if (!p) return;
116
116
  const { by = null } = req.body || {};
@@ -119,7 +119,7 @@ export function register(app, { project, projects }) {
119
119
  res.json(updated);
120
120
  });
121
121
 
122
- app.post("/projects/:pid/tasks/:id/drop", (req, res) => {
122
+ api.post("/projects/:pid/tasks/:id/drop", (req, res) => {
123
123
  const p = project(req, res);
124
124
  if (!p) return;
125
125
  const { by = null } = req.body || {};
@@ -128,7 +128,7 @@ export function register(app, { project, projects }) {
128
128
  res.json(updated);
129
129
  });
130
130
 
131
- app.post("/projects/:pid/tasks/:id/reopen", (req, res) => {
131
+ api.post("/projects/:pid/tasks/:id/reopen", (req, res) => {
132
132
  const p = project(req, res);
133
133
  if (!p) return;
134
134
  const updated = reopenTask(p.storagePath, req.params.id);
@@ -137,7 +137,7 @@ export function register(app, { project, projects }) {
137
137
  });
138
138
 
139
139
  // Move an open task through its workflow (pending → running → in_review …).
140
- app.post("/projects/:pid/tasks/:id/status", (req, res) => {
140
+ api.post("/projects/:pid/tasks/:id/status", (req, res) => {
141
141
  const p = project(req, res);
142
142
  if (!p) return;
143
143
  const { status } = req.body || {};
@@ -149,7 +149,7 @@ export function register(app, { project, projects }) {
149
149
  });
150
150
 
151
151
  // Lightweight summary endpoint for status displays.
152
- app.get("/projects/:pid/tasks-summary", (req, res) => {
152
+ api.get("/projects/:pid/tasks-summary", (req, res) => {
153
153
  const p = project(req, res);
154
154
  if (!p) return;
155
155
  res.json(countTasks(p.storagePath));