@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
@@ -17,6 +17,35 @@
17
17
 
18
18
  const THINK_RE = /<(?:think|thinking)>([\s\S]*?)<\/(?:think|thinking)>/gi;
19
19
 
20
+ // Untagged chain-of-thought.
21
+ //
22
+ // Some models — routed free tiers especially — emit their planning as the
23
+ // answer itself, with no tags at all: "We need to produce a response to user
24
+ // request: ...". That reached a real user's Telegram as a 668-token English
25
+ // wall in place of two sentences of Spanish.
26
+ //
27
+ // Detection is deliberately narrow. It only fires when the text OPENS with
28
+ // first-person-plural planning about producing a response, which is a register
29
+ // no genuine reply uses. Anything looser would eat real answers that happen to
30
+ // begin with "We need to".
31
+ const UNTAGGED_COT_OPENERS = [
32
+ /^we need to (produce|craft|write|generate|compose) (a |an )?(response|reply|answer|message)/i,
33
+ /^(the )?user (is )?(asking|says|wants|requests)\b[\s\S]{0,200}?\bwe (should|need to|must)\b/i,
34
+ /^okay,? (so )?(the )?user\b[\s\S]{0,120}?\bwe (should|need to)\b/i,
35
+ /^interpretation:\s/i,
36
+ /^let'?s (think|analyze|break this down)\b/i,
37
+ ];
38
+
39
+ /**
40
+ * True when `text` reads as raw planning rather than a reply.
41
+ * Only ever consulted for the ANSWER half, after tagged blocks are removed.
42
+ */
43
+ export function looksLikeUntaggedReasoning(text) {
44
+ const t = String(text || "").trimStart();
45
+ if (t.length < 120) return false; // too short to be a planning dump
46
+ return UNTAGGED_COT_OPENERS.some((re) => re.test(t));
47
+ }
48
+
20
49
  export function splitThinking(text) {
21
50
  if (!text || typeof text !== "string") return { thinking: "", answer: text || "" };
22
51
  const blocks = [];
@@ -34,6 +63,28 @@ export function stripThinking(text) {
34
63
  return splitThinking(text).answer;
35
64
  }
36
65
 
66
+ /**
67
+ * The answer, with untagged planning treated as thinking too.
68
+ *
69
+ * `stripThinking` only removes TAGGED blocks. A model that dumps its planning
70
+ * with no tags slips straight through, which is how a 668-token English
71
+ * chain-of-thought reached a user's phone instead of two sentences of Spanish.
72
+ *
73
+ * When the remaining answer still reads as raw planning, this returns "" — the
74
+ * caller decides what to do with an empty reply, which is a far better failure
75
+ * than shipping the model's notes to the user. Never guesses at salvaging a
76
+ * partial answer out of it.
77
+ *
78
+ * @returns {{ answer: string, leaked: boolean, thinking: string }}
79
+ */
80
+ export function stripReasoning(text) {
81
+ const { thinking, answer } = splitThinking(text);
82
+ if (looksLikeUntaggedReasoning(answer)) {
83
+ return { answer: "", leaked: true, thinking: [thinking, answer].filter(Boolean).join("\n\n") };
84
+ }
85
+ return { answer, leaked: false, thinking };
86
+ }
87
+
37
88
  export function formatForChannel(text, channel) {
38
89
  const { thinking, answer } = splitThinking(text);
39
90
  // Channels where reasoning would be noise to a human operator
@@ -13,16 +13,16 @@
13
13
  //
14
14
  // project wins over global when the same name exists in both.
15
15
 
16
- import fs from "node:fs";
17
- import os from "node:os";
18
16
  import path from "node:path";
17
+ import { apxHome } from "#core/config/paths.js";
18
+ import { readJson, writeJson, SECRET_MODE } from "#core/util/json-file.js";
19
19
 
20
- const APX_HOME = path.join(os.homedir(), ".apx");
21
- const GLOBAL_VARS_FILE = path.join(APX_HOME, "vars.json");
20
+
21
+ const globalVarsFile = () => path.join(apxHome(), "vars.json");
22
22
  const PROJECT_VARS_FILENAME = "vars.json";
23
23
 
24
24
  export function globalVarsPath() {
25
- return GLOBAL_VARS_FILE;
25
+ return globalVarsFile();
26
26
  }
27
27
 
28
28
  export function projectVarsPath(storagePath) {
@@ -30,32 +30,25 @@ export function projectVarsPath(storagePath) {
30
30
  return path.join(storagePath, PROJECT_VARS_FILENAME);
31
31
  }
32
32
 
33
+ // A vars file must be a plain object; anything else is treated as empty.
33
34
  function readJsonSafe(absPath) {
34
- if (!absPath || !fs.existsSync(absPath)) return {};
35
- try {
36
- const json = JSON.parse(fs.readFileSync(absPath, "utf8"));
37
- return json && typeof json === "object" && !Array.isArray(json) ? json : {};
38
- } catch {
39
- return {};
40
- }
35
+ if (!absPath) return {};
36
+ const json = readJson(absPath, {});
37
+ return json && typeof json === "object" && !Array.isArray(json) ? json : {};
41
38
  }
42
39
 
40
+ // SECRET_MODE: these files hold tokens. The write is atomic, so a crash can no
41
+ // longer truncate the store.
43
42
  function writeJsonSecure(absPath, obj) {
44
- fs.mkdirSync(path.dirname(absPath), { recursive: true });
45
- fs.writeFileSync(absPath, JSON.stringify(obj, null, 2) + "\n");
46
- try {
47
- fs.chmodSync(absPath, 0o600);
48
- } catch {
49
- // Best effort on non-POSIX filesystems.
50
- }
43
+ writeJson(absPath, obj, { mode: SECRET_MODE });
51
44
  }
52
45
 
53
46
  export function readGlobalVars() {
54
- return readJsonSafe(GLOBAL_VARS_FILE);
47
+ return readJsonSafe(globalVarsFile());
55
48
  }
56
49
 
57
50
  export function writeGlobalVars(obj) {
58
- writeJsonSecure(GLOBAL_VARS_FILE, obj);
51
+ writeJsonSecure(globalVarsFile(), obj);
59
52
  }
60
53
 
61
54
  export function readProjectVars(storagePath) {
@@ -23,7 +23,7 @@ function repoCacheName(repoId) {
23
23
 
24
24
  function dirSizeBytes(dir) {
25
25
  let total = 0;
26
- let stack = [dir];
26
+ const stack = [dir];
27
27
  while (stack.length) {
28
28
  const d = stack.pop();
29
29
  let entries;
@@ -5,11 +5,12 @@
5
5
  //
6
6
  // All audio files land under ~/.apx/tmp/tts/<uuid>.<ext>. Callers are
7
7
  // responsible for cleaning up (Telegram plugin already uses similar temp
8
- // files via /telegram/send_voice).
8
+ // files via /api/telegram/send_voice).
9
9
 
10
10
  import fs from "node:fs";
11
- import path from "node:path";
12
- import os from "node:os";
11
+ import { TTS_TMP_DIR } from "#core/config/paths.js";
12
+
13
+ export { TTS_TMP_DIR };
13
14
  import { readConfig } from "../config/index.js";
14
15
  import {
15
16
  selectTtsEngine,
@@ -19,7 +20,7 @@ import {
19
20
  } from "./engines/index.js";
20
21
  import { emotionConfigFor, stripEmotionTags } from "./emotions.js";
21
22
 
22
- export const TTS_TMP_DIR = path.join(os.homedir(), ".apx", "tmp", "tts");
23
+
23
24
 
24
25
  export function ensureTtsTmpDir() {
25
26
  fs.mkdirSync(TTS_TMP_DIR, { recursive: true });
@@ -8,7 +8,7 @@
8
8
  import { readConfig, writeConfig } from "#core/config/index.js";
9
9
  import { resolveAgentName } from "#core/identity/index.js";
10
10
  import { setDottedKey, unsetDottedKey } from "../project-config.js";
11
- import { PERMISSION_MODES, DEFAULT_PERMISSION_MODE } from "#core/constants/permissions.js";
11
+ import { PERMISSION_MODES } from "#core/constants/permissions.js";
12
12
  import {
13
13
  redactConfig as redact,
14
14
  isSecretMarker,
@@ -16,8 +16,8 @@ import {
16
16
  } from "#core/config/redact.js";
17
17
  import { collectSecretValues, registerSecretValues } from "#core/config/secret-values.js";
18
18
 
19
- export function register(app, { config, scheduler, plugins }) {
20
- app.get("/admin/config", (_req, res) => {
19
+ export function register(api, { config, scheduler, plugins }) {
20
+ api.get("/admin/config", (_req, res) => {
21
21
  try {
22
22
  const fresh = readConfig();
23
23
  res.json({ config: redact(fresh) });
@@ -26,7 +26,7 @@ export function register(app, { config, scheduler, plugins }) {
26
26
  }
27
27
  });
28
28
 
29
- app.patch("/admin/config", (req, res) => {
29
+ api.patch("/admin/config", (req, res) => {
30
30
  const { set, unset } = req.body || {};
31
31
  let cfg;
32
32
  try {
@@ -68,7 +68,7 @@ export function register(app, { config, scheduler, plugins }) {
68
68
  });
69
69
 
70
70
  // Convenience: GET the resolved super_agent prompt (used by SettingsScreen)
71
- app.get("/admin/super-agent", (_req, res) => {
71
+ api.get("/admin/super-agent", (_req, res) => {
72
72
  try {
73
73
  const fresh = readConfig();
74
74
  const sa = fresh.super_agent || {};
@@ -8,11 +8,12 @@ import { execFile } from "node:child_process";
8
8
  import fs from "node:fs";
9
9
  import os from "node:os";
10
10
  import path from "node:path";
11
+ import { LOG_DIR } from "#core/config/paths.js";
11
12
 
12
- export function register(app, { scheduler, plugins, config, registries }) {
13
+ export function register(api, { scheduler, plugins, config, registries }) {
13
14
  // Daemon logs: errors.jsonl (structured) or apx.log (plain), newest first.
14
- app.get("/admin/logs", (req, res) => {
15
- const dir = path.join(os.homedir(), ".apx", "logs");
15
+ api.get("/admin/logs", (req, res) => {
16
+ const dir = LOG_DIR;
16
17
  const which = req.query.file === "apx" ? "apx.log" : "errors.jsonl";
17
18
  const file = path.join(dir, which);
18
19
  const limit = Math.min(parseInt(req.query.limit, 10) || 200, 2000);
@@ -26,7 +27,7 @@ export function register(app, { scheduler, plugins, config, registries }) {
26
27
  res.json({ file: which, lines: tail });
27
28
  });
28
29
 
29
- app.post("/admin/reload", (_req, res) => {
30
+ api.post("/admin/reload", (_req, res) => {
30
31
  try {
31
32
  const fresh = readConfig();
32
33
  // Mutate in place so every closure that captured `config` sees the new
@@ -46,7 +47,7 @@ export function register(app, { scheduler, plugins, config, registries }) {
46
47
  }
47
48
  });
48
49
 
49
- app.post("/admin/shutdown", (_req, res) => {
50
+ api.post("/admin/shutdown", (_req, res) => {
50
51
  res.json({ ok: true });
51
52
  setTimeout(() => process.exit(0), 50);
52
53
  });
@@ -56,7 +57,7 @@ export function register(app, { scheduler, plugins, config, registries }) {
56
57
  // zenity (if present); Windows uses PowerShell's Shell.Application. If the
57
58
  // platform lacks a usable picker — or none is installed — the endpoint
58
59
  // returns 501 so the frontend can fall back to the inline directory list.
59
- app.get("/admin/fs/pick-dir", (req, res) => {
60
+ api.get("/admin/fs/pick-dir", (req, res) => {
60
61
  // The prompt is attacker-controllable (query string). NEVER interpolate it
61
62
  // into a shell string — use execFile (no shell) with an argv array, and
62
63
  // pass the prompt out-of-band via an env var so it can't break out of any
@@ -109,7 +110,7 @@ export function register(app, { scheduler, plugins, config, registries }) {
109
110
  });
110
111
  });
111
112
 
112
- app.get("/admin/fs/dirs", (req, res) => {
113
+ api.get("/admin/fs/dirs", (req, res) => {
113
114
  const requested = String(req.query.path || os.homedir());
114
115
  const base = path.resolve(requested.replace(/^~(?=$|\/)/, os.homedir()));
115
116
  try {
@@ -70,12 +70,12 @@ function normalizeAutonomy(v) {
70
70
  return AUTONOMY_VALUES.has(v) ? v : undefined;
71
71
  }
72
72
 
73
- export function register(app, { projects, project }) {
73
+ export function register(api, { projects, project }) {
74
74
  // Vault = global agent templates. Two-layer: bundled defaults shipped with
75
75
  // APX (assets/agent-vault-defaults/) + user overrides/new ones in
76
76
  // ~/.apx/agents/. The user layer wins per slug; tombstones in .removed.json
77
77
  // hide bundled entries. GET merges both with `source` set per item.
78
- app.get("/agents/vault", (req, res) => {
78
+ api.get("/agents/vault", (req, res) => {
79
79
  const includeRemoved = req.query?.include_removed === "1";
80
80
  res.json(readVaultAgents({ includeRemoved }).map((a) => ({
81
81
  ...agentToResponse(a),
@@ -84,7 +84,7 @@ export function register(app, { projects, project }) {
84
84
  });
85
85
 
86
86
  // Create or replace a vault template (user layer / copy-on-write).
87
- app.post("/agents/vault", (req, res) => {
87
+ api.post("/agents/vault", (req, res) => {
88
88
  const { slug, fields, body = "" } = req.body || {};
89
89
  if (!slug || !/^[a-z][a-z0-9_-]*$/.test(slug)) {
90
90
  return res.status(400).json({ error: "valid slug required" });
@@ -100,7 +100,7 @@ export function register(app, { projects, project }) {
100
100
 
101
101
  // Patch a vault template. If the slug is bundled-only, copy it to the user
102
102
  // layer first (the writer already does this), then apply the merged fields.
103
- app.patch("/agents/vault/:slug", (req, res) => {
103
+ api.patch("/agents/vault/:slug", (req, res) => {
104
104
  const { slug } = req.params;
105
105
  const current = readVaultAgent(slug);
106
106
  if (!current) return res.status(404).json({ error: `vault agent ${slug} not found` });
@@ -118,14 +118,14 @@ export function register(app, { projects, project }) {
118
118
 
119
119
  // Delete a vault template. Tombstones bundled slugs so they stay hidden;
120
120
  // deletes the user-layer file otherwise. POST .../restore lifts a tombstone.
121
- app.delete("/agents/vault/:slug", (req, res) => {
121
+ api.delete("/agents/vault/:slug", (req, res) => {
122
122
  const { slug } = req.params;
123
123
  const out = removeVaultAgent(slug);
124
124
  if (!out.removed) return res.status(404).json({ error: `vault agent ${slug} not found` });
125
125
  res.json({ ok: true, ...out });
126
126
  });
127
127
 
128
- app.post("/agents/vault/:slug/restore", (req, res) => {
128
+ api.post("/agents/vault/:slug/restore", (req, res) => {
129
129
  const { slug } = req.params;
130
130
  const out = restoreVaultAgent(slug);
131
131
  if (!out.restored) return res.status(404).json({ error: `slug ${slug} was not tombstoned` });
@@ -134,7 +134,7 @@ export function register(app, { projects, project }) {
134
134
  });
135
135
 
136
136
  // Import a vault template into a project (copies it to .apc/agents/<slug>.md).
137
- app.post("/projects/:pid/agents/import", (req, res) => {
137
+ api.post("/projects/:pid/agents/import", (req, res) => {
138
138
  const p = project(req, res);
139
139
  if (!p) return;
140
140
  const { slug } = req.body || {};
@@ -154,7 +154,7 @@ export function register(app, { projects, project }) {
154
154
  }
155
155
  });
156
156
 
157
- app.get("/projects/:pid/agents", (req, res) => {
157
+ api.get("/projects/:pid/agents", (req, res) => {
158
158
  const p = project(req, res);
159
159
  if (!p) return;
160
160
  const agents = readAgents(p.path).map(agentToResponse);
@@ -162,7 +162,7 @@ export function register(app, { projects, project }) {
162
162
  res.json(agents);
163
163
  });
164
164
 
165
- app.get("/projects/:pid/agents/:slug", (req, res) => {
165
+ api.get("/projects/:pid/agents/:slug", (req, res) => {
166
166
  const p = project(req, res);
167
167
  if (!p) return;
168
168
  const agents = readAgents(p.path);
@@ -172,7 +172,7 @@ export function register(app, { projects, project }) {
172
172
  res.json({ ...agentToResponse(a), memory, system: a.body || "" });
173
173
  });
174
174
 
175
- app.post("/projects/:pid/agents", (req, res) => {
175
+ api.post("/projects/:pid/agents", (req, res) => {
176
176
  const p = project(req, res);
177
177
  if (!p) return;
178
178
  const {
@@ -213,7 +213,7 @@ export function register(app, { projects, project }) {
213
213
 
214
214
  // Edit an existing agent. Merges provided fields into the AGENT.md
215
215
  // frontmatter; `system` rewrites the body (the agent's system prompt).
216
- app.patch("/projects/:pid/agents/:slug", (req, res) => {
216
+ api.patch("/projects/:pid/agents/:slug", (req, res) => {
217
217
  const p = project(req, res);
218
218
  if (!p) return;
219
219
  const slug = req.params.slug;
@@ -255,7 +255,7 @@ export function register(app, { projects, project }) {
255
255
  });
256
256
 
257
257
  // Delete an agent: removes .apc/agents/<slug>.md and runtime data dir.
258
- app.delete("/projects/:pid/agents/:slug", (req, res) => {
258
+ api.delete("/projects/:pid/agents/:slug", (req, res) => {
259
259
  const p = project(req, res);
260
260
  if (!p) return;
261
261
  const slug = req.params.slug;
@@ -276,7 +276,7 @@ export function register(app, { projects, project }) {
276
276
  });
277
277
 
278
278
  // ---- Project-level memory (.apc/memory.md) ----
279
- app.get("/projects/:pid/memory", (req, res) => {
279
+ api.get("/projects/:pid/memory", (req, res) => {
280
280
  const p = project(req, res);
281
281
  if (!p) return;
282
282
  const memPath = apcMemoryFile(p.path);
@@ -284,7 +284,7 @@ export function register(app, { projects, project }) {
284
284
  res.json({ body, path: memPath });
285
285
  });
286
286
 
287
- app.put("/projects/:pid/memory", (req, res) => {
287
+ api.put("/projects/:pid/memory", (req, res) => {
288
288
  const p = project(req, res);
289
289
  if (!p) return;
290
290
  const { body } = req.body || {};
@@ -298,7 +298,7 @@ export function register(app, { projects, project }) {
298
298
  });
299
299
 
300
300
  // ---- Per-agent memory ----
301
- app.get("/projects/:pid/agents/:slug/memory", (req, res) => {
301
+ api.get("/projects/:pid/agents/:slug/memory", (req, res) => {
302
302
  const p = project(req, res);
303
303
  if (!p) return;
304
304
  // Validate the agent exists — otherwise an unknown slug returned 200 with an
@@ -308,7 +308,7 @@ export function register(app, { projects, project }) {
308
308
  res.json({ body: readAgentMemory(p, req.params.slug) });
309
309
  });
310
310
 
311
- app.put("/projects/:pid/agents/:slug/memory", (req, res) => {
311
+ api.put("/projects/:pid/agents/:slug/memory", (req, res) => {
312
312
  const p = project(req, res);
313
313
  if (!p) return;
314
314
  if (!readAgents(p.path).some((a) => a.slug === req.params.slug))
@@ -12,9 +12,9 @@
12
12
  import { previews } from "#core/artifacts/preview.js";
13
13
  import { tunnels, detectProviders } from "#core/artifacts/tunnel.js";
14
14
 
15
- export function register(app, { project }) {
15
+ export function register(api, { project }) {
16
16
  // Start (or reuse) an ephemeral preview server for an artifact.
17
- app.post("/projects/:pid/artifacts/:name/preview", async (req, res) => {
17
+ api.post("/projects/:pid/artifacts/:name/preview", async (req, res) => {
18
18
  const p = project(req, res);
19
19
  if (!p) return;
20
20
  const name = decodeURIComponent(req.params.name);
@@ -33,24 +33,24 @@ export function register(app, { project }) {
33
33
  });
34
34
 
35
35
  // List preview servers scoped to a project.
36
- app.get("/projects/:pid/previews", (req, res) => {
36
+ api.get("/projects/:pid/previews", (req, res) => {
37
37
  const p = project(req, res);
38
38
  if (!p) return;
39
39
  res.json(previews.list(p.id));
40
40
  });
41
41
 
42
42
  // List every live preview (all projects).
43
- app.get("/previews", (_req, res) => {
43
+ api.get("/previews", (_req, res) => {
44
44
  res.json(previews.list());
45
45
  });
46
46
 
47
47
  // Which tunnel providers this host can use, best first.
48
- app.get("/previews/tunnel-providers", (_req, res) => {
48
+ api.get("/previews/tunnel-providers", (_req, res) => {
49
49
  res.json({ providers: detectProviders() });
50
50
  });
51
51
 
52
52
  // Stop a preview server (also closes its tunnel).
53
- app.delete("/previews/:id", async (req, res) => {
53
+ api.delete("/previews/:id", async (req, res) => {
54
54
  const rec = previews.get(req.params.id);
55
55
  if (rec?.tunnel) tunnels.close(rec.tunnel.id);
56
56
  const ok = await previews.stop(req.params.id);
@@ -58,7 +58,7 @@ export function register(app, { project }) {
58
58
  });
59
59
 
60
60
  // Open a public tunnel to a preview's local port.
61
- app.post("/previews/:id/tunnel", async (req, res) => {
61
+ api.post("/previews/:id/tunnel", async (req, res) => {
62
62
  const rec = previews.get(req.params.id);
63
63
  if (!rec) return res.status(404).json({ error: "preview not found" });
64
64
  if (rec.tunnel) return res.json(rec.tunnel); // already shared
@@ -72,7 +72,7 @@ export function register(app, { project }) {
72
72
  });
73
73
 
74
74
  // Close a preview's tunnel but keep the local server running.
75
- app.delete("/previews/:id/tunnel", (req, res) => {
75
+ api.delete("/previews/:id/tunnel", (req, res) => {
76
76
  const rec = previews.get(req.params.id);
77
77
  if (!rec || !rec.tunnel) return res.status(404).end();
78
78
  tunnels.close(rec.tunnel.id);
@@ -89,14 +89,14 @@ function runArtifact({ absPath, cwd, args, timeoutMs = RUN_TIMEOUT_MS }) {
89
89
  });
90
90
  }
91
91
 
92
- export function register(app, { project }) {
93
- app.get("/projects/:pid/artifacts", (req, res) => {
92
+ export function register(api, { project }) {
93
+ api.get("/projects/:pid/artifacts", (req, res) => {
94
94
  const p = project(req, res);
95
95
  if (!p) return;
96
96
  res.json(listArtifacts(p.storagePath));
97
97
  });
98
98
 
99
- app.post("/projects/:pid/artifacts", (req, res) => {
99
+ api.post("/projects/:pid/artifacts", (req, res) => {
100
100
  const p = project(req, res);
101
101
  if (!p) return;
102
102
  const { name, content = "" } = req.body || {};
@@ -109,7 +109,7 @@ export function register(app, { project }) {
109
109
  }
110
110
  });
111
111
 
112
- app.get("/projects/:pid/artifacts/:name", (req, res) => {
112
+ api.get("/projects/:pid/artifacts/:name", (req, res) => {
113
113
  const p = project(req, res);
114
114
  if (!p) return;
115
115
  try {
@@ -119,7 +119,7 @@ export function register(app, { project }) {
119
119
  }
120
120
  });
121
121
 
122
- app.patch("/projects/:pid/artifacts/:name", (req, res) => {
122
+ api.patch("/projects/:pid/artifacts/:name", (req, res) => {
123
123
  const p = project(req, res);
124
124
  if (!p) return;
125
125
  const name = decodeURIComponent(req.params.name);
@@ -144,7 +144,7 @@ export function register(app, { project }) {
144
144
  }
145
145
  });
146
146
 
147
- app.delete("/projects/:pid/artifacts/:name", (req, res) => {
147
+ api.delete("/projects/:pid/artifacts/:name", (req, res) => {
148
148
  const p = project(req, res);
149
149
  if (!p) return;
150
150
  const ok = removeArtifact(
@@ -158,7 +158,7 @@ export function register(app, { project }) {
158
158
  // its own local spawn (stdio inherited) so it can run interactively. Output
159
159
  // is captured up to MAX_CAPTURE_BYTES and the call is bounded by
160
160
  // RUN_TIMEOUT_MS — anything longer should go through the terminal.
161
- app.post("/projects/:pid/artifacts/:name/run", async (req, res) => {
161
+ api.post("/projects/:pid/artifacts/:name/run", async (req, res) => {
162
162
  const p = project(req, res);
163
163
  if (!p) return;
164
164
  const name = decodeURIComponent(req.params.name);
@@ -47,18 +47,18 @@ function modeGuidanceFor(mode) {
47
47
  // codeSessionHistory() (transcript → engine messages) and makeTurnAccumulator()
48
48
  // (stream events → persistable ChatParts) imported above.
49
49
 
50
- export function register(app, { projects, project, config, registries, plugins }) {
50
+ export function register(api, { projects, project, config, registries, plugins }) {
51
51
  const findProject = (req, res) => project(req, res);
52
52
 
53
53
  // ---- List ----------------------------------------------------------------
54
- app.get("/projects/:pid/code/sessions", (req, res) => {
54
+ api.get("/projects/:pid/code/sessions", (req, res) => {
55
55
  const p = findProject(req, res);
56
56
  if (!p) return;
57
57
  res.json({ sessions: listCodeSessions(p.storagePath) });
58
58
  });
59
59
 
60
60
  // ---- Create (captures git baseline) --------------------------------------
61
- app.post("/projects/:pid/code/sessions", (req, res) => {
61
+ api.post("/projects/:pid/code/sessions", (req, res) => {
62
62
  const p = findProject(req, res);
63
63
  if (!p) return;
64
64
  const { title, model, mode, agentSlug } = req.body || {};
@@ -86,7 +86,7 @@ export function register(app, { projects, project, config, registries, plugins }
86
86
  });
87
87
 
88
88
  // ---- Get full transcript -------------------------------------------------
89
- app.get("/projects/:pid/code/sessions/:sid", (req, res) => {
89
+ api.get("/projects/:pid/code/sessions/:sid", (req, res) => {
90
90
  const p = findProject(req, res);
91
91
  if (!p) return;
92
92
  const session = getCodeSession(p.storagePath, req.params.sid);
@@ -95,7 +95,7 @@ export function register(app, { projects, project, config, registries, plugins }
95
95
  });
96
96
 
97
97
  // ---- Patch (rename / model / mode) ---------------------------------------
98
- app.patch("/projects/:pid/code/sessions/:sid", (req, res) => {
98
+ api.patch("/projects/:pid/code/sessions/:sid", (req, res) => {
99
99
  const p = findProject(req, res);
100
100
  if (!p) return;
101
101
  const session = updateCodeSession(p.storagePath, req.params.sid, req.body || {});
@@ -104,7 +104,7 @@ export function register(app, { projects, project, config, registries, plugins }
104
104
  });
105
105
 
106
106
  // ---- Delete --------------------------------------------------------------
107
- app.delete("/projects/:pid/code/sessions/:sid", (req, res) => {
107
+ api.delete("/projects/:pid/code/sessions/:sid", (req, res) => {
108
108
  const p = findProject(req, res);
109
109
  if (!p) return;
110
110
  const ok = removeCodeSession(p.storagePath, req.params.sid);
@@ -113,7 +113,7 @@ export function register(app, { projects, project, config, registries, plugins }
113
113
  });
114
114
 
115
115
  // ---- Changes (diff vs baseline) ------------------------------------------
116
- app.get("/projects/:pid/code/sessions/:sid/changes", (req, res) => {
116
+ api.get("/projects/:pid/code/sessions/:sid/changes", (req, res) => {
117
117
  const p = findProject(req, res);
118
118
  if (!p) return;
119
119
  const session = getCodeSession(p.storagePath, req.params.sid);
@@ -130,7 +130,7 @@ export function register(app, { projects, project, config, registries, plugins }
130
130
  });
131
131
 
132
132
  // ---- Streaming turn ------------------------------------------------------
133
- app.post("/projects/:pid/code/sessions/:sid/chat/stream", async (req, res) => {
133
+ api.post("/projects/:pid/code/sessions/:sid/chat/stream", async (req, res) => {
134
134
  const p = findProject(req, res);
135
135
  if (!p) return;
136
136
  const session = getCodeSession(p.storagePath, req.params.sid);