@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
@@ -1,15 +1,26 @@
1
1
  // Express REST API for APX. See APC docs reference/apx-daemon.
2
2
  //
3
3
  // Routes are split by domain under ./api/*.js — each module exports
4
- // `register(app, ctx)`. This file is purely orchestration: middlewares,
4
+ // `register(router, ctx)`. This file is purely orchestration: middlewares,
5
5
  // context construction, mount order, 404 catch-all.
6
+ //
7
+ // EVERY data route lives under /api. Domain modules still declare their paths
8
+ // root-relative (`/projects/:pid/tasks`); they register on an express.Router()
9
+ // that is mounted at API_PREFIX, so the effective URL is /api/projects/:pid/
10
+ // tasks. The root namespace belongs exclusively to the admin panel (static
11
+ // bundle + client-side routes), which is why the "is this an API path?" test
12
+ // is now a single startsWith instead of a hand-maintained prefix list.
6
13
  import express from "express";
7
14
 
15
+ import { API_PREFIX } from "./api/prefix.js";
16
+ import { logError } from "#core/logging.js";
17
+
8
18
  import {
9
19
  traceIdMiddleware,
10
20
  buildAuthMiddleware,
11
21
  makeProjectResolver,
12
22
  makeTopProjectResolver,
23
+ errorMiddleware,
13
24
  } from "./api/shared.js";
14
25
 
15
26
  import { register as registerTools } from "./api/tools.js";
@@ -35,6 +46,7 @@ import { register as registerRoutines } from "./api/routines.js";
35
46
  import { register as registerArtifacts } from "./api/artifacts.js";
36
47
  import { register as registerArtifactPreview } from "./api/artifact-preview.js";
37
48
  import { register as registerTasks } from "./api/tasks.js";
49
+ import { register as registerCommitments } from "./api/commitments.js";
38
50
  import { register as registerOrganization } from "./api/organization.js";
39
51
  import { register as registerProjectFiles } from "./api/files-project.js";
40
52
  import { register as registerConfig } from "./api/config.js";
@@ -53,7 +65,9 @@ import { register as registerAdminConfig } from "./api/admin-config.js";
53
65
  import { register as registerIdentity } from "./api/identity.js";
54
66
  import { register as registerProfiles } from "./api/profiles.js";
55
67
  import { register as registerInbox } from "./api/inbox.js";
56
- import { register as registerWeb } from "./api/web.js";
68
+ import { register as registerSelfMemory } from "./api/self-memory.js";
69
+ import { register as registerNudges } from "./api/nudges.js";
70
+ import { register as registerWeb, registerWebToken } from "./api/web.js";
57
71
  import { register as registerConfirm } from "./api/confirm.js";
58
72
 
59
73
  export function buildApi({
@@ -70,6 +84,9 @@ export function buildApi({
70
84
  }) {
71
85
  const telegram = plugins?.get("telegram");
72
86
  const app = express();
87
+ // Every domain module registers on this router; it is mounted at /api at the
88
+ // bottom of this function. Modules keep declaring root-relative paths.
89
+ const api = express.Router();
73
90
 
74
91
  // ---- Global middleware -------------------------------------------
75
92
  app.use(express.json({ limit: "2mb" }));
@@ -103,62 +120,74 @@ export function buildApi({
103
120
  };
104
121
 
105
122
  // ---- Tool routers — must mount BEFORE wildcard registry below ----
106
- registerTools(app, ctx);
123
+ registerTools(api, ctx);
107
124
 
108
125
  // ---- Health (unauthenticated) ------------------------------------
109
- registerHealth(app, ctx);
126
+ registerHealth(api, ctx);
110
127
 
111
128
  // ---- Projects + per-project surfaces -----------------------------
112
- registerProjects(app, ctx);
113
- registerAgents(app, ctx);
114
- registerSessions(app, ctx);
115
- registerMcps(app, ctx);
116
- registerIntegrations(app, ctx);
117
- registerVars(app, ctx);
118
- registerMessages(app, ctx);
119
- registerEngines(app, ctx);
120
- registerSkills(app, ctx);
121
- registerExec(app, ctx);
122
- registerSuperAgent(app, ctx);
123
- registerConfirm(app, ctx);
124
- registerCode(app, ctx);
125
- registerConversations(app, ctx);
126
- registerConnections(app, ctx);
127
- registerRuntimes(app, ctx);
128
- registerRoutines(app, ctx);
129
- registerArtifacts(app, ctx);
130
- registerArtifactPreview(app, ctx);
131
- registerTasks(app, ctx);
132
- registerOrganization(app, ctx);
133
- registerProjectFiles(app, ctx);
134
- registerConfig(app, ctx);
129
+ registerProjects(api, ctx);
130
+ registerAgents(api, ctx);
131
+ registerSessions(api, ctx);
132
+ registerMcps(api, ctx);
133
+ registerIntegrations(api, ctx);
134
+ registerVars(api, ctx);
135
+ registerMessages(api, ctx);
136
+ registerEngines(api, ctx);
137
+ registerSkills(api, ctx);
138
+ registerExec(api, ctx);
139
+ registerSuperAgent(api, ctx);
140
+ registerConfirm(api, ctx);
141
+ registerCode(api, ctx);
142
+ registerConversations(api, ctx);
143
+ registerConnections(api, ctx);
144
+ registerRuntimes(api, ctx);
145
+ registerRoutines(api, ctx);
146
+ registerArtifacts(api, ctx);
147
+ registerArtifactPreview(api, ctx);
148
+ registerTasks(api, ctx);
149
+ registerCommitments(api, ctx);
150
+ registerOrganization(api, ctx);
151
+ registerProjectFiles(api, ctx);
152
+ registerConfig(api, ctx);
135
153
 
136
154
  // ---- Top-level shortcuts (MCP server clients) --------------------
137
- registerRun(app, ctx);
138
- registerTopLevel(app, ctx);
139
- registerSessionsSearch(app, ctx);
155
+ registerRun(api, ctx);
156
+ registerTopLevel(api, ctx);
157
+ registerSessionsSearch(api, ctx);
140
158
 
141
159
  // ---- Channels & plugin surfaces ----------------------------------
142
- registerTelegram(app, ctx);
143
- registerPlugins(app, ctx);
144
- registerTranscribe(app, ctx);
145
- registerTts(app, ctx);
146
- registerEmbeddings(app, ctx);
147
- registerVoice(app, ctx);
148
- registerDesktop(app, ctx);
149
- registerDeck(app, ctx);
150
- registerPairing(app, ctx);
160
+ registerTelegram(api, ctx);
161
+ registerPlugins(api, ctx);
162
+ registerTranscribe(api, ctx);
163
+ registerTts(api, ctx);
164
+ registerEmbeddings(api, ctx);
165
+ registerVoice(api, ctx);
166
+ registerDesktop(api, ctx);
167
+ registerDeck(api, ctx);
168
+ registerPairing(api, ctx);
151
169
 
152
170
  // ---- Admin -------------------------------------------------------
153
- registerAdmin(app, ctx);
154
- registerAdminConfig(app, ctx);
155
- registerIdentity(app, ctx);
156
- registerProfiles(app, ctx);
157
- registerInbox(app, ctx);
158
-
159
- // ---- Web admin panel (static SPA, must mount before 404) ---------
160
- // Serves src/interfaces/web/dist when present + the /admin/web-token
161
- // localhost-only token endpoint. No-op until the panel is built.
171
+ registerAdmin(api, ctx);
172
+ registerAdminConfig(api, ctx);
173
+ registerIdentity(api, ctx);
174
+ registerProfiles(api, ctx);
175
+ registerInbox(api, ctx);
176
+ registerSelfMemory(api, ctx);
177
+ registerNudges(api, ctx);
178
+ registerWebToken(api, ctx);
179
+
180
+ // ---- API 404 (MUST be last on the router) ------------------------
181
+ // Terminal for the /api namespace: an unknown /api/* path answers JSON here
182
+ // instead of falling through to the SPA shell below.
183
+ api.use((req, res) =>
184
+ res.status(404).json({ error: `no route ${req.method} ${req.baseUrl}${req.path}` })
185
+ );
186
+
187
+ app.use(API_PREFIX, api);
188
+
189
+ // ---- Web admin panel (static SPA, must mount after /api) ---------
190
+ // Serves src/interfaces/web/dist when present. No-op until the panel is built.
162
191
  registerWeb(app, ctx);
163
192
 
164
193
  // ---- 404 catch-all (MUST be last) --------------------------------
@@ -166,5 +195,11 @@ export function buildApi({
166
195
  res.status(404).json({ error: `no route ${req.method} ${req.path}` })
167
196
  );
168
197
 
198
+ // ---- Error handler (MUST be after everything) --------------------
199
+ // Express only recognises the 4-argument shape, and only when it is mounted
200
+ // last. Anything an asyncRoute() rejects lands here as JSON instead of an
201
+ // unhandled rejection that kills the daemon.
202
+ app.use(errorMiddleware(logError));
203
+
169
204
  return app;
170
205
  }
@@ -15,6 +15,8 @@
15
15
  // session close captured remains). The rich A2A relay (Roby re-voicing the
16
16
  // result) stays the job of the live in-process path.
17
17
  import { listPendingCallbacks, deletePendingCallback, readSessionState } from "#core/stores/runtime-callbacks.js";
18
+ import { readConfig } from "#core/config/index.js";
19
+ import { canNudge, recordNudge } from "#core/nudge/index.js";
18
20
 
19
21
  const GRACE_MS = 30_000; // let the live in-process path win a fresh completion
20
22
  const STALE_MS = 24 * 60 * 60 * 1000; // drop IOUs for runs that never finished in a day
@@ -62,11 +64,25 @@ export async function reconcilePendingCallbacks({ plugins, log }) {
62
64
  if (Number.isFinite(compAge) && compAge < GRACE_MS) continue;
63
65
 
64
66
  if (!telegram) continue; // telegram plugin not up this boot — retry next tick
67
+
68
+ // PUSH PATH 4 OF 4. Declared SOLICITED, and that is a judgement worth
69
+ // stating: the user launched this runtime and is waiting for its result.
70
+ // Arriving late does not make it an interruption, and holding it back
71
+ // for quiet hours would mean losing an answer they asked for. It still
72
+ // passes through the gate so the audit is real and so a future policy
73
+ // can reach it without hunting for a fifth path nobody remembered.
74
+ const gate = canNudge(
75
+ { kind: "session_result", severity: "normal", unsolicited: false, channel: "telegram" },
76
+ readConfig(),
77
+ );
78
+ if (!gate.allowed) continue; // never today; retry next tick if it ever is
79
+
65
80
  await telegram.send({
66
81
  channel: entry.tg_channel || undefined,
67
82
  chat_id: entry.chat_id,
68
83
  text: deliverText(entry, state),
69
84
  });
85
+ recordNudge(gate, { chat_id: entry.chat_id });
70
86
  deletePendingCallback(entry.session_id);
71
87
  log?.(`callback-reconciler: delivered late callback for ${entry.session_id} → chat ${entry.chat_id}`);
72
88
  } catch (e) {
@@ -5,7 +5,7 @@ import path from "node:path";
5
5
  import { appendMessageToFs } from "#core/stores/messages.js";
6
6
  import { effectiveConfig } from "./project-config.js";
7
7
  import { readAgents } from "#core/apc/parser.js";
8
- import { apcDir, apcProjectFile, apcAgentsDir, apcCommandsDir } from "#core/apc/paths.js";
8
+ import { apcProjectFile, apcAgentsDir, apcCommandsDir } from "#core/apc/paths.js";
9
9
  import { getOrCreateApxId } from "#core/apc/scaffold.js";
10
10
  import {
11
11
  ensureProjectStorage,
@@ -1,5 +1,6 @@
1
1
  // Singleton WebSocket hub for the desktop channel.
2
2
  // Imported by api.js (to register connections) and by plugins/desktop/index.js (to broadcast).
3
+ import { apiPath } from "./api/prefix.js";
3
4
 
4
5
  const _clients = new Set(); // Set<WebSocket>
5
6
  let _messageHandler = null; // (ws, data) => void — set by desktop plugin
@@ -12,7 +13,7 @@ export function setDesktopMessageHandler(fn) {
12
13
 
13
14
  // --- WS upgrade auth helpers (shared by the daemon upgrade handler + tests) ---
14
15
  //
15
- // The desktop WS channel must authenticate the same way the HTTP /desktop/*
16
+ // The desktop WS channel must authenticate the same way the HTTP /api/desktop/*
16
17
  // routes do: a bearer token (master or paired client) carried on the upgrade
17
18
  // request. The legitimate desktop window sends `Authorization: Bearer <token>`
18
19
  // (src/interfaces/desktop/main.js); browser clients can pass `?token=`. Without
@@ -20,11 +21,14 @@ export function setDesktopMessageHandler(fn) {
20
21
  // when host is set to 0.0.0.0) could open the channel and drive the
21
22
  // super-agent. See QA BUG-WS-AUTH.
22
23
 
23
- /** Path-gate: is this upgrade for the desktop (or legacy overlay) WS channel? */
24
+ /** The desktop channel's upgrade path. Lives under /api like every other route. */
25
+ export const DESKTOP_WS_PATH = apiPath("/desktop/ws");
26
+
27
+ /** Path-gate: is this upgrade for the desktop WS channel? */
24
28
  export function isDesktopUpgradePath(url) {
25
29
  let pathname = url || "";
26
30
  try { pathname = new URL(url, "http://localhost").pathname; } catch { /* keep raw */ }
27
- return pathname === "/desktop/ws" || pathname === "/overlay/ws";
31
+ return pathname === DESKTOP_WS_PATH;
28
32
  }
29
33
 
30
34
  /** Extract the bearer token from the upgrade request (header first, ?token= fallback). */
@@ -12,12 +12,10 @@ import { randomBytes } from "node:crypto";
12
12
  const LOOPBACK_HOST = "127.0.0.1";
13
13
  import {
14
14
  readConfig,
15
- writeConfig,
16
15
  effectiveHost,
17
16
  effectivePort,
18
17
  addProject as addProjectInConfig,
19
18
  PID_PATH,
20
- LOG_PATH,
21
19
  APX_HOME,
22
20
  TOKEN_PATH,
23
21
  } from "#core/config/index.js";
@@ -297,12 +295,11 @@ async function main() {
297
295
  }).catch(() => {});
298
296
  });
299
297
 
300
- // Attach WebSocket upgrade for the desktop channel on /desktop/ws
301
- // (legacy /overlay/ws still accepted for one release).
298
+ // Attach WebSocket upgrade for the desktop channel on /api/desktop/ws.
302
299
  server.on("upgrade", async (req, socket, head) => {
303
300
  if (!isDesktopUpgradePath(req.url)) { socket.destroy(); return; }
304
301
  // Auth: the WS upgrade must carry a valid token (master or paired client),
305
- // matching the HTTP /desktop/* routes. Without this, any client that can
302
+ // matching the HTTP /api/desktop/* routes. Without this, any client that can
306
303
  // reach the daemon (host binds 0.0.0.0 → the LAN) could open the desktop
307
304
  // channel and drive the super-agent (permission_mode "total"). The
308
305
  // legitimate desktop window already sends the bearer token. See QA BUG-WS-AUTH.
@@ -358,6 +355,15 @@ async function main() {
358
355
  process.on("uncaughtException", (e) => {
359
356
  log(`uncaughtException: ${e.stack || e.message}`);
360
357
  });
358
+ // Node >= 15 terminates the process on an unhandled rejection. The CLI has
359
+ // always registered this; the daemon had not, so one un-awaited failure in a
360
+ // route or a plugin took down the process serving the SPA, Telegram polling,
361
+ // voice and the deck at once. Log and keep running — the request-level
362
+ // errorMiddleware already answers the caller.
363
+ process.on("unhandledRejection", (reason) => {
364
+ const e = reason instanceof Error ? reason : new Error(String(reason));
365
+ log(`unhandledRejection: ${e.stack || e.message}`);
366
+ });
361
367
  }
362
368
 
363
369
  main().catch((e) => {
@@ -25,6 +25,7 @@ import {
25
25
  import { runSuperAgent, isSuperAgentEnabled } from "#core/agent/super-agent.js";
26
26
  import { appendGlobalMessage } from "#core/stores/messages.js";
27
27
  import { stripEmotionTags } from "#core/voice/emotions.js";
28
+ import { stripReasoning } from "#core/util/thinking.js";
28
29
  import { CHANNELS } from "#core/constants/channels.js";
29
30
  import { tryResolveSkillCommand } from "#core/agent/skills/trigger.js";
30
31
 
@@ -103,7 +104,7 @@ async function _handleMessage({ ws, text, previousMessages }, { projects, config
103
104
  await appendGlobalMessage({ channel: CHANNEL, direction: "in", type: "user", author: "user", body: text });
104
105
  } catch {}
105
106
 
106
- let toolsExecuted = [];
107
+ const toolsExecuted = [];
107
108
 
108
109
  // Per-segment streaming: instead of merging the whole turn into one blob, we
109
110
  // emit each assistant text piece as its own `segment` (an intro before a tool,
@@ -123,7 +124,12 @@ async function _handleMessage({ ws, text, previousMessages }, { projects, config
123
124
  emittedSegments.push(seg);
124
125
  // `text` is what the bubble shows (no [tags]); `speak` keeps the inline
125
126
  // emotion tags so the renderer's per-segment TTS can use them.
126
- _send(ws, { type: "segment", seq: ++segSeq, text: stripEmotionTags(seg), speak: seg });
127
+ // Desktop is voice-first: an unstripped reasoning dump would be spoken
128
+ // aloud, at length, in the wrong language. Drop the segment entirely
129
+ // rather than narrate the model's notes.
130
+ const spoken = stripReasoning(seg).answer;
131
+ if (!spoken.trim()) return;
132
+ _send(ws, { type: "segment", seq: ++segSeq, text: stripEmotionTags(spoken), speak: spoken });
127
133
  };
128
134
 
129
135
  try {
@@ -311,9 +311,13 @@ export default {
311
311
  // that bot; otherwise first available bot-tokened channel. Always logs
312
312
  // the outbound on `messages` of the channel's target project so audit
313
313
  // trails are complete.
314
- async send({ channel: channelName, chat_id, text, author = resolveAgentName(config), project }) {
314
+ // `reply_markup` carries the interruption-budget feedback keyboard on
315
+ // proactive pushes (core/nudge). It is NOT a gate: the gate belongs at
316
+ // the call sites that decide to speak unprompted, because this method
317
+ // also carries traffic the user asked for.
318
+ async send({ channel: channelName, chat_id, text, reply_markup, author = resolveAgentName(config), meta: extraMeta }) {
315
319
  const p = pickPoller(pollers, channelName);
316
- const result = await p._send({ chat_id, text });
320
+ const result = await p._send({ chat_id, text, reply_markup });
317
321
  appendGlobalMessage({
318
322
  channel: CHANNELS.TELEGRAM,
319
323
  direction: "out",
@@ -327,6 +331,7 @@ export default {
327
331
  chat_id: chat_id || resolveChatId(p.channel),
328
332
  tg_channel: p.channel.name,
329
333
  via: channelName ? "explicit" : "auto",
334
+ ...(extraMeta || {}),
330
335
  },
331
336
  });
332
337
  return result;
@@ -1,27 +1,33 @@
1
- // Dedicated Python virtualenv for the STT engines.
1
+ // Which Python runs whisper-server.py.
2
2
  //
3
- // We never install into the system or user-site Python — mlx-whisper drags in
3
+ // APX owns a dedicated virtualenv at ~/.apx/runtime/whisper-venv rather than
4
+ // installing into the system or user-site Python: mlx-whisper drags in
4
5
  // torch/scipy/numba and would pollute (or clash with) the user's other
5
- // projects. Instead APX owns ~/.apx/runtime/whisper-venv: create it, install
6
- // faster-whisper / mlx-whisper into it, and spawn whisper-server.py with its
7
- // interpreter. "Reset the engine" = delete the folder and recreate it.
8
- import os from "node:os";
6
+ // projects.
7
+ //
8
+ // This module used to also create that venv and pip-install into it
9
+ // (ensureVenv, pipInstall, venvHasModule, removeVenv, ENGINE_PACKAGES). None of
10
+ // it was ever called — the installer path was built and then superseded — so it
11
+ // was ~120 lines of dead code sitting next to a live one-liner. Removed. If
12
+ // venv provisioning comes back, it belongs in its own module with a caller.
9
13
  import path from "node:path";
10
14
  import fs from "node:fs";
11
- import { spawn } from "node:child_process";
12
-
13
- export const VENV_DIR = path.join(os.homedir(), ".apx", "runtime", "whisper-venv");
15
+ import { WHISPER_VENV_DIR } from "#core/config/paths.js";
14
16
 
15
17
  /** Path to the venv's python (…/bin/python on POSIX, …/Scripts on Windows). */
16
- export function venvPython() {
18
+ function venvPython() {
17
19
  return process.platform === "win32"
18
- ? path.join(VENV_DIR, "Scripts", "python.exe")
19
- : path.join(VENV_DIR, "bin", "python");
20
+ ? path.join(WHISPER_VENV_DIR, "Scripts", "python.exe")
21
+ : path.join(WHISPER_VENV_DIR, "bin", "python");
20
22
  }
21
23
 
22
24
  /** True once the venv has a usable interpreter. */
23
- export function venvExists() {
24
- try { return fs.existsSync(venvPython()); } catch { return false; }
25
+ function venvExists() {
26
+ try {
27
+ return fs.existsSync(venvPython());
28
+ } catch {
29
+ return false;
30
+ }
25
31
  }
26
32
 
27
33
  /**
@@ -31,70 +37,3 @@ export function venvExists() {
31
37
  export function pythonForWhisper() {
32
38
  return venvExists() ? venvPython() : "python3";
33
39
  }
34
-
35
- function run(cmd, args, onLine) {
36
- return new Promise((resolve) => {
37
- let proc;
38
- try {
39
- proc = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
40
- } catch (e) {
41
- onLine?.(`spawn failed: ${e.message}`);
42
- return resolve({ ok: false, code: -1 });
43
- }
44
- const pump = (chunk) => {
45
- for (const line of chunk.toString().split(/\r?\n/)) {
46
- if (line.trim()) onLine?.(line);
47
- }
48
- };
49
- proc.stdout.on("data", pump);
50
- proc.stderr.on("data", pump);
51
- proc.on("exit", (code) => resolve({ ok: code === 0, code }));
52
- proc.on("error", (e) => { onLine?.(e.message); resolve({ ok: false, code: -1 }); });
53
- });
54
- }
55
-
56
- /** Create the venv (idempotent). Streams progress lines to onLine. */
57
- export async function ensureVenv(onLine) {
58
- if (venvExists()) return { ok: true, created: false };
59
- fs.mkdirSync(path.dirname(VENV_DIR), { recursive: true });
60
- onLine?.(`creating venv at ${VENV_DIR}…`);
61
- const r = await run("python3", ["-m", "venv", VENV_DIR], onLine);
62
- if (!r.ok || !venvExists()) return { ok: false, error: "venv creation failed" };
63
- // Upgrade pip once so wheel resolution is fast/modern.
64
- await run(venvPython(), ["-m", "pip", "install", "--upgrade", "pip", "wheel"], onLine);
65
- return { ok: true, created: true };
66
- }
67
-
68
- /** Is a module importable inside the venv? */
69
- export function venvHasModule(mod) {
70
- return new Promise((resolve) => {
71
- if (!venvExists()) return resolve(false);
72
- const p = spawn(venvPython(), ["-c", `import ${mod}`], { stdio: "ignore" });
73
- p.on("exit", (code) => resolve(code === 0));
74
- p.on("error", () => resolve(false));
75
- });
76
- }
77
-
78
- /** pip install <pkgs> into the venv, streaming progress. Creates the venv first. */
79
- export async function pipInstall(pkgs, onLine) {
80
- const ensured = await ensureVenv(onLine);
81
- if (!ensured.ok) return ensured;
82
- const list = Array.isArray(pkgs) ? pkgs : [pkgs];
83
- onLine?.(`installing ${list.join(", ")}…`);
84
- const r = await run(venvPython(), ["-m", "pip", "install", "--upgrade", ...list], onLine);
85
- return { ok: r.ok, code: r.code };
86
- }
87
-
88
- /** Delete the whole venv (so the engine can be reinstalled clean). */
89
- export async function removeVenv(onLine) {
90
- if (!fs.existsSync(VENV_DIR)) return { ok: true, removed: false };
91
- onLine?.(`removing ${VENV_DIR}…`);
92
- await fs.promises.rm(VENV_DIR, { recursive: true, force: true });
93
- return { ok: !fs.existsSync(VENV_DIR), removed: true };
94
- }
95
-
96
- /** Engine → the pip package(s) it needs in the venv. */
97
- export const ENGINE_PACKAGES = {
98
- faster: ["faster-whisper"],
99
- mlx: ["mlx-whisper"],
100
- };
@@ -2,6 +2,7 @@
2
2
  import fetch from "node-fetch";
3
3
  import { readIdentity, writeIdentity } from "#core/identity/index.js";
4
4
  import { resolveProvider, getAdapter } from "#core/engines/index.js";
5
+ import { canNudge, recordNudge, nudgeFeedbackKeyboard } from "#core/nudge/index.js";
5
6
 
6
7
  const WAKEUP_COOLDOWN_MS = 30 * 60 * 1000; // 30 min
7
8
 
@@ -18,7 +19,7 @@ export function detectLanguage(identity, config) {
18
19
  if (cfgLang) return ISO_TO_LANGUAGE[cfgLang.toLowerCase()] || cfgLang;
19
20
  if (identity?.language) return identity.language;
20
21
  const lang = process.env.LANG || process.env.LC_MESSAGES || process.env.LC_ALL || "";
21
- const code = lang.split(/[_\.]/)[0].toLowerCase();
22
+ const code = lang.split(/[_.]/)[0].toLowerCase();
22
23
  return ISO_TO_LANGUAGE[code] || "English";
23
24
  }
24
25
 
@@ -50,12 +51,12 @@ async function generateMessage(identity, engineConfig) {
50
51
  }
51
52
  }
52
53
 
53
- async function sendTelegram(token, chatId, text) {
54
+ async function sendTelegram(token, chatId, text, reply_markup) {
54
55
  const url = `https://api.telegram.org/bot${token}/sendMessage`;
55
56
  const res = await fetch(url, {
56
57
  method: "POST",
57
58
  headers: { "Content-Type": "application/json" },
58
- body: JSON.stringify({ chat_id: chatId, text }),
59
+ body: JSON.stringify({ chat_id: chatId, text, ...(reply_markup ? { reply_markup } : {}) }),
59
60
  });
60
61
  const json = await res.json();
61
62
  if (!json.ok) throw new Error(json.description || "telegram send failed");
@@ -75,10 +76,23 @@ export async function triggerWakeup(config, log) {
75
76
  if (elapsed < WAKEUP_COOLDOWN_MS) return;
76
77
  }
77
78
 
79
+ // PUSH PATH 1 OF 4 — nobody asked for this one. "I restarted" is the least
80
+ // interesting thing APX can say, so it is also the first thing a budget
81
+ // should be allowed to swallow.
82
+ const gate = canNudge(
83
+ { kind: "wakeup", severity: "low", unsolicited: true, channel: "telegram" },
84
+ config,
85
+ );
86
+ if (!gate.allowed) {
87
+ log?.(`wakeup: suppressed by the interruption budget — ${gate.reason}`);
88
+ return;
89
+ }
90
+
78
91
  try {
79
92
  const message = await generateMessage(identity, config);
80
93
  const text = message || `${identity.agent_name} online. Ready.`;
81
- await sendTelegram(tg.bot_token, tg.chat_id, text);
94
+ await sendTelegram(tg.bot_token, tg.chat_id, text, nudgeFeedbackKeyboard(gate.nudge_id));
95
+ recordNudge(gate, { chat_id: tg.chat_id, preview: text });
82
96
  writeIdentity({ last_wakeup: new Date().toISOString() });
83
97
  log?.(`wakeup: sent to Telegram chat ${tg.chat_id}`);
84
98
  } catch (e) {
@@ -6,7 +6,7 @@
6
6
  //
7
7
  // Like the mcp-server surface, this is a thin adapter over the daemon HTTP
8
8
  // API: each `session/prompt` becomes a POST to the NDJSON stream endpoint
9
- // `/projects/:pid/super-agent/chat/stream` and the stream events are mapped
9
+ // `/api/projects/:pid/super-agent/chat/stream` and the stream events are mapped
10
10
  // onto ACP `session/update` notifications:
11
11
  //
12
12
  // assistant_text → agent_message_chunk
@@ -179,7 +179,7 @@ export class AcpAgentServer {
179
179
  session.activeTurn = turn;
180
180
  try {
181
181
  const final = await this.client.streamPost(
182
- `/projects/${session.project.id}/super-agent/chat/stream`,
182
+ `/api/projects/${session.project.id}/super-agent/chat/stream`,
183
183
  {
184
184
  prompt: promptText,
185
185
  // ACP clients are coding surfaces (IDEs) — the `code` channel gives
@@ -314,7 +314,7 @@ export class AcpAgentServer {
314
314
  }
315
315
  try {
316
316
  await this.client.post(
317
- `/super-agent/confirm/${encodeURIComponent(event.correlationId)}`,
317
+ `/api/super-agent/confirm/${encodeURIComponent(event.correlationId)}`,
318
318
  { confirmed }
319
319
  );
320
320
  } catch (e) {
@@ -129,12 +129,12 @@ export async function resolveProjectForCwd(client, cwd) {
129
129
  `No APC project found at or above: ${cwd}. Run \`apx init\` in the workspace first.`
130
130
  );
131
131
  }
132
- const projects = await client.get("/projects");
132
+ const projects = await client.get("/api/projects");
133
133
  const match = (projects || []).find(
134
134
  (p) => path.resolve(p.path) === path.resolve(root)
135
135
  );
136
136
  if (match) return match;
137
- return client.post("/projects", { path: root });
137
+ return client.post("/api/projects", { path: root });
138
138
  }
139
139
 
140
140
  /** Flatten an ACP prompt (ContentBlock[]) into the text the daemon expects. */
@@ -26,7 +26,7 @@ export async function cmdSend(args) {
26
26
  if (!body) throw new Error("apx send: body is empty");
27
27
 
28
28
  const pid = await resolveProjectId(args?.flags?.project);
29
- const result = await http.post(`/projects/${pid}/send`, {
29
+ const result = await http.post(`/api/projects/${pid}/send`, {
30
30
  from,
31
31
  to,
32
32
  body,
@@ -48,7 +48,7 @@ export async function cmdConnections(args) {
48
48
  const slug = args._[0];
49
49
  if (!slug) throw new Error("apx connections: missing <agent-slug>");
50
50
  const pid = await resolveProjectId(args?.flags?.project);
51
- const peers = await http.get(`/projects/${pid}/agents/${slug}/connections`);
51
+ const peers = await http.get(`/api/projects/${pid}/agents/${slug}/connections`);
52
52
  if (peers.length === 0) {
53
53
  console.log(`(no connections logged for ${slug} yet)`);
54
54
  return;
@@ -24,9 +24,9 @@ function requireRoot() {
24
24
  async function nudgeDaemon(root) {
25
25
  try {
26
26
  if (!(await http.ping())) return;
27
- const projects = await http.get("/projects", { autoStart: false });
27
+ const projects = await http.get("/api/projects", { autoStart: false });
28
28
  const me = projects.find((p) => p.path === root);
29
- if (me) await http.post(`/projects/${me.id}/rebuild`, undefined, { autoStart: false });
29
+ if (me) await http.post(`/api/projects/${me.id}/rebuild`, undefined, { autoStart: false });
30
30
  } catch { /* daemon hiccup */ }
31
31
  }
32
32
 
@@ -116,7 +116,7 @@ export async function cmdAgentRemove(args) {
116
116
  }
117
117
  }
118
118
  const pid = await resolveProjectId(args?.flags?.project);
119
- await http.delete(`/projects/${pid}/agents/${slug}`);
119
+ await http.delete(`/api/projects/${pid}/agents/${slug}`);
120
120
  console.log(`${tag("removed")} ${bold(slug)} ${gray("(agent file + runtime memory deleted)")}`);
121
121
  }
122
122