@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,8 +1,9 @@
1
1
  // Shared helpers for the Routines screen + its components.
2
2
  // Kept framework-free (pure functions + i18n) so list/detail/editor reuse them.
3
- import { Bot, Crown, Heart, Send, Terminal } from "lucide-react";
3
+ import { Bot, Crown, Eye, Heart, Send, Terminal } from "lucide-react";
4
4
  import type { RoutineEntry } from "../../lib/api";
5
5
  import { t } from "../../i18n";
6
+ import { describeCron } from "../../lib/cron";
6
7
 
7
8
  export type Kind = RoutineEntry["kind"];
8
9
 
@@ -17,6 +18,7 @@ export function kindMeta(): Record<Kind, { label: string; desc: string; icon: ty
17
18
  super_agent: { label: t("agents_ui.kind_super_agent"), desc: t("agents_ui.kind_super_agent_desc"), icon: Crown },
18
19
  telegram: { label: t("agents_ui.kind_telegram"), desc: t("agents_ui.kind_telegram_desc"), icon: Send },
19
20
  shell: { label: t("agents_ui.kind_shell"), desc: t("agents_ui.kind_shell_desc"), icon: Terminal },
21
+ watch: { label: t("agents_ui.kind_watch"), desc: t("agents_ui.kind_watch_desc"), icon: Eye },
20
22
  heartbeat: { label: t("agents_ui.kind_heartbeat"), desc: t("agents_ui.kind_heartbeat_desc"), icon: Heart },
21
23
  };
22
24
  }
@@ -49,16 +51,23 @@ export function scheduleHuman(s?: string): string {
49
51
  return t("agents_ui.every_v", { v });
50
52
  }
51
53
  if (s.startsWith("once:")) return `once · ${new Date(s.slice(5)).toLocaleString()}`;
52
- if (s.startsWith("cron ")) return `cron · ${s.slice(5)}`;
53
- return s;
54
+ if (s.trim().toLowerCase() === "manual") return t("agents_ui.sched_manual");
55
+ // Cron, read as a sentence. `cron · 0 9 * * *` was not a translation of
56
+ // anything — it just moved the expression behind a label. The leading "cron"
57
+ // word is tolerated because this editor used to write it.
58
+ const expr = s.replace(/^cron\s+/i, "");
59
+ return describeCron(expr, t as never) || expr;
54
60
  }
55
61
 
56
62
  export function schedPresets() {
57
63
  return [
58
64
  { label: t("agents_ui.preset_every_10m"), value: "every:10m" },
59
65
  { label: t("agents_ui.preset_hourly"), value: "every:1h" },
60
- { label: t("agents_ui.preset_daily_9am"), value: "cron 0 9 * * *" },
61
- { label: t("agents_ui.preset_weekdays_9am"), value: "cron 0 9 * * 1-5" },
66
+ // BARE cron, not `cron <expr>`: core parses the expression itself, and the
67
+ // labelled form used to come back invalid so these two presets built
68
+ // routines that never ran.
69
+ { label: t("agents_ui.preset_daily_9am"), value: "0 9 * * *" },
70
+ { label: t("agents_ui.preset_weekdays_9am"), value: "0 9 * * 1-5" },
62
71
  ];
63
72
  }
64
73
 
@@ -49,7 +49,7 @@ export function DesktopSettingsPanel() {
49
49
  const position = cfgView.desktop?.position || "right";
50
50
 
51
51
  const { data: autostart, mutate: mutateAutostart } = useSWR(
52
- "/desktop/autostart",
52
+ "/api/desktop/autostart",
53
53
  () => Desktop.autostartGet(),
54
54
  );
55
55
 
@@ -48,7 +48,7 @@ export function MemoryPanel() {
48
48
  const toast = useToast();
49
49
  const { config, isLoading, patch } = useGlobalConfig();
50
50
  const { data: providers, mutate: mutateProviders } = useSWR(
51
- "/embeddings/providers",
51
+ "/api/embeddings/providers",
52
52
  () => Embeddings.providers()
53
53
  );
54
54
  const [busy, setBusy] = useState(false);
@@ -0,0 +1,183 @@
1
+ import { useEffect, useState } from "react";
2
+ import { ThumbsDown, ThumbsUp, Info } from "lucide-react";
3
+ import { Section } from "../Section";
4
+ import { Badge, Button, Empty, Field, Input, Loading, Switch } from "../ui";
5
+ import { useToast } from "../Toast";
6
+ import { useNudges, useNudgePolicy } from "../../hooks/useNudges";
7
+ import { NudgesApi, type NudgePolicy } from "../../lib/api/nudges";
8
+ import { t } from "../../i18n";
9
+
10
+ /**
11
+ * The interruption budget — how often APX may speak unprompted.
12
+ *
13
+ * Two halves that belong together: the rule, and the record of what the rule
14
+ * let through. Showing the ledger next to the dial is the point — someone
15
+ * deciding "is three a day too many?" needs to see the three.
16
+ */
17
+ export function NudgePanel() {
18
+ const toast = useToast();
19
+ const { policy, source, isLoading, mutate } = useNudgePolicy();
20
+ const { entries, stats, mutate: mutateLedger } = useNudges(30);
21
+
22
+ const [draft, setDraft] = useState<NudgePolicy | null>(null);
23
+ const [busy, setBusy] = useState(false);
24
+
25
+ useEffect(() => {
26
+ if (policy) setDraft({ ...policy });
27
+ }, [policy]);
28
+
29
+ if (isLoading || !draft) return <Loading />;
30
+
31
+ const save = async () => {
32
+ setBusy(true);
33
+ try {
34
+ await NudgesApi.setPolicy(draft);
35
+ await mutate();
36
+ toast.success(t("settings.nudge.saved"));
37
+ } catch (e) {
38
+ toast.error((e as Error).message);
39
+ } finally {
40
+ setBusy(false);
41
+ }
42
+ };
43
+
44
+ const rate = async (id: string, useful: boolean) => {
45
+ try {
46
+ await NudgesApi.feedback(id, useful);
47
+ await mutateLedger();
48
+ } catch (e) {
49
+ toast.error((e as Error).message);
50
+ }
51
+ };
52
+
53
+ const num = (key: keyof NudgePolicy, label: string, hint?: string) => (
54
+ <Field label={label} hint={hint}>
55
+ <Input
56
+ type="number"
57
+ min={0}
58
+ value={String(draft[key] ?? 0)}
59
+ disabled={!draft.enabled}
60
+ onChange={(e) => setDraft({ ...draft, [key]: Number(e.target.value) || 0 })}
61
+ />
62
+ </Field>
63
+ );
64
+
65
+ const left = draft.daily_max > 0 ? Math.max(0, draft.daily_max - (stats?.today ?? 0)) : "∞";
66
+
67
+ return (
68
+ <div className="flex flex-col gap-4" data-testid="nudge-panel">
69
+ <Section title={t("settings.nudge.title")} description={t("settings.nudge.subtitle")}>
70
+ <div className="mb-3 flex items-start gap-2 rounded-md border border-border bg-muted/40 p-3 text-sm">
71
+ <Info size={16} className="mt-0.5 shrink-0 opacity-70" />
72
+ <span>
73
+ {draft.enabled
74
+ ? t("settings.nudge.on_hint")
75
+ .replace("{{sent}}", String(stats?.today ?? 0))
76
+ .replace("{{left}}", String(left))
77
+ : t("settings.nudge.off_hint")}
78
+ {source.length ? (
79
+ <span className="ml-1 opacity-60">
80
+ ({t("settings.nudge.source")}: {source.join(" → ")})
81
+ </span>
82
+ ) : null}
83
+ </span>
84
+ </div>
85
+
86
+ <div className="mb-4">
87
+ <Switch
88
+ checked={draft.enabled}
89
+ onChange={(v) => setDraft({ ...draft, enabled: v })}
90
+ label={t("settings.nudge.enabled")}
91
+ />
92
+ </div>
93
+
94
+ <div className="grid gap-3 sm:grid-cols-2">
95
+ {num("daily_max", t("settings.nudge.daily_max"), t("settings.nudge.daily_max_hint"))}
96
+ <Field label={t("settings.nudge.quiet_hours")} hint={t("settings.nudge.quiet_hours_hint")}>
97
+ <Input
98
+ value={draft.quiet_hours ?? ""}
99
+ placeholder="22:00-07:30"
100
+ disabled={!draft.enabled}
101
+ onChange={(e) => setDraft({ ...draft, quiet_hours: e.target.value })}
102
+ />
103
+ </Field>
104
+ {num("cooldown_minutes", t("settings.nudge.cooldown"))}
105
+ {num("project_cooldown_minutes", t("settings.nudge.project_cooldown"))}
106
+ {num("kind_cooldown_minutes", t("settings.nudge.kind_cooldown"))}
107
+ </div>
108
+
109
+ <div className="mt-3">
110
+ <Switch
111
+ checked={draft.critical_bypasses_budget}
112
+ onChange={(v) => setDraft({ ...draft, critical_bypasses_budget: v })}
113
+ label={t("settings.nudge.critical_bypass")}
114
+ disabled={!draft.enabled}
115
+ />
116
+ <p className="mt-1 text-[11px] text-muted-foreground/70">
117
+ {t("settings.nudge.critical_bypass_hint")}
118
+ </p>
119
+ </div>
120
+
121
+ <div className="mt-4">
122
+ <Button variant="primary" loading={busy} onClick={save}>
123
+ {t("common.save")}
124
+ </Button>
125
+ </div>
126
+ </Section>
127
+
128
+ <Section title={t("settings.nudge.log_title")} description={t("settings.nudge.log_subtitle")}>
129
+ {!entries.length ? (
130
+ <Empty>{t("settings.nudge.log_empty")}</Empty>
131
+ ) : (
132
+ <ul className="flex flex-col gap-2" data-testid="nudge-log">
133
+ {entries.map((e) => (
134
+ <li
135
+ key={e.id}
136
+ className="flex items-start gap-3 rounded-md border border-border bg-muted/20 p-2.5 text-sm"
137
+ >
138
+ <span className="min-w-0 flex-1">
139
+ <span className="flex flex-wrap items-center gap-2">
140
+ <Badge>{e.kind}</Badge>
141
+ <span className="text-xs opacity-55">
142
+ {String(e.at).replace("T", " ").slice(0, 16)}
143
+ </span>
144
+ {e.bypassed_budget ? (
145
+ <Badge tone="warning">{t("settings.nudge.bypass")}</Badge>
146
+ ) : null}
147
+ </span>
148
+ <span className="mt-1 block line-clamp-2 opacity-80">{e.preview}</span>
149
+ </span>
150
+
151
+ {/* Rating is available for as long as the entry is on file, not
152
+ only in the chat where the buttons were. Retrospect is when
153
+ people actually notice a pattern was noise. */}
154
+ <span className="flex shrink-0 items-center gap-1">
155
+ <button
156
+ type="button"
157
+ aria-label={t("settings.nudge.useful")}
158
+ onClick={() => rate(e.id, true)}
159
+ className={`rounded p-1.5 transition hover:bg-muted ${
160
+ e.feedback?.useful === true ? "text-emerald-500" : "opacity-40"
161
+ }`}
162
+ >
163
+ <ThumbsUp size={14} />
164
+ </button>
165
+ <button
166
+ type="button"
167
+ aria-label={t("settings.nudge.noise")}
168
+ onClick={() => rate(e.id, false)}
169
+ className={`rounded p-1.5 transition hover:bg-muted ${
170
+ e.feedback?.useful === false ? "text-red-500" : "opacity-40"
171
+ }`}
172
+ >
173
+ <ThumbsDown size={14} />
174
+ </button>
175
+ </span>
176
+ </li>
177
+ ))}
178
+ </ul>
179
+ )}
180
+ </Section>
181
+ </div>
182
+ );
183
+ }
@@ -1,11 +1,12 @@
1
1
  import { useEffect, useState } from "react";
2
2
  import { AlertTriangle, CheckCircle2, Info } from "lucide-react";
3
3
  import { Section } from "../Section";
4
- import { Badge, Button, Dialog, Empty, Field, Input, Loading } from "../ui";
4
+ import { Badge, Button, Dialog, Empty, Field, Input, Loading, Switch } from "../ui";
5
5
  import { UiSelect } from "../UiSelect";
6
6
  import { useToast } from "../Toast";
7
7
  import { useProfiles, useProfile, useProfileDoctor } from "../../hooks/useProfiles";
8
8
  import { ProfilesApi, type ProfileSchemaProp } from "../../lib/api/profiles";
9
+ import { CronPicker } from "../cron/CronPicker";
9
10
  import { t } from "../../i18n";
10
11
 
11
12
  /**
@@ -15,6 +16,11 @@ import { t } from "../../i18n";
15
16
  * Identity), nor with configuration profiles. With none active, APX behaves
16
17
  * exactly as it always has, and the panel says so rather than looking broken.
17
18
  */
19
+ /** Schema keys whose value is a five-field cron expression. */
20
+ function isCronKey(key: string): boolean {
21
+ return /(^|_)schedule$/.test(key) || /_cron$/.test(key);
22
+ }
23
+
18
24
  export function ProfilePanel() {
19
25
  const toast = useToast();
20
26
  const { active, profiles, isLoading, mutate } = useProfiles();
@@ -143,16 +149,25 @@ export function ProfilePanel() {
143
149
  )}
144
150
 
145
151
  {profile ? (
146
- <div className="mt-4 flex flex-wrap items-center gap-2">
147
- {profile.active ? (
148
- <Button variant="destructive" loading={busy} onClick={() => setConfirmOff(true)}>
149
- {t("settings.profile.deactivate")}
150
- </Button>
151
- ) : (
152
- <Button variant="primary" loading={busy} onClick={() => activate(profile.id, !!active)}>
153
- {active ? t("settings.profile.replace_active") : t("settings.profile.activate")}
154
- </Button>
155
- )}
152
+ <div className="mt-4 flex flex-wrap items-center gap-3">
153
+ {/* A switch, like every other on/off in Settings (Engines, Skills,
154
+ Desktop autostart). The red "Deactivate" button read as a
155
+ destructive action — deactivating a profile destroys nothing:
156
+ its settings survive and turning it back on restores them. */}
157
+ <Switch
158
+ checked={!!profile.active}
159
+ disabled={busy}
160
+ label={profile.active ? t("settings.profile.on") : t("settings.profile.off")}
161
+ onChange={(on) => {
162
+ if (on) activate(profile.id, !!active);
163
+ // Switching OFF still confirms: the profile's routines stop
164
+ // with it, and that is worth one deliberate tap.
165
+ else setConfirmOff(true);
166
+ }}
167
+ />
168
+ {!profile.active && active ? (
169
+ <span className="text-xs opacity-60">{t("settings.profile.replaces_active")}</span>
170
+ ) : null}
156
171
  <span className="text-xs opacity-60">
157
172
  {t("settings.profile.token_cost")}: ~{profile.tokens ?? 0}
158
173
  {profile.budget ? ` / ${profile.budget}` : ""}
@@ -183,7 +198,16 @@ export function ProfilePanel() {
183
198
  <div className="grid gap-3 sm:grid-cols-2">
184
199
  {Object.entries(props).map(([key, def]) => (
185
200
  <Field key={key} label={def.title || key} hint={def.description}>
186
- {def.enum ? (
201
+ {/* A schedule is a time and some days, not five
202
+ space-separated fields. Detected by key name because the
203
+ JSON-Schema subset has no "format" for cron. */}
204
+ {isCronKey(key) ? (
205
+ <CronPicker
206
+ value={draft[key] ?? String(def.default ?? "")}
207
+ onChange={(expr) => setDraft({ ...draft, [key]: expr })}
208
+ disabled={!canEdit}
209
+ />
210
+ ) : def.enum ? (
187
211
  <UiSelect
188
212
  value={draft[key] ?? String(def.default ?? "")}
189
213
  onChange={(v) => setDraft({ ...draft, [key]: v })}
@@ -39,7 +39,7 @@ type NumericKnobs = {
39
39
 
40
40
  export function SkillsInspectorPanel() {
41
41
  const toast = useToast();
42
- const { data, mutate, isLoading } = useSWR("/skills/inspector", () => Skills.inspector());
42
+ const { data, mutate, isLoading } = useSWR("/api/skills/inspector", () => Skills.inspector());
43
43
  const [busy, setBusy] = useState(false);
44
44
  const [probe, setProbe] = useState("");
45
45
  const [probeResult, setProbeResult] = useState<InspectTrace | null>(null);
@@ -128,13 +128,13 @@ export function SkillsManager({ scope: fixedScope, selectable = false }: { scope
128
128
  const [repoOpen, setRepoOpen] = useState(false);
129
129
  const fileRef = useRef<HTMLInputElement>(null);
130
130
 
131
- const { data: projects } = useSWR(selectable ? "/projects" : null, () => Projects.list());
131
+ const { data: projects } = useSWR(selectable ? "/api/projects" : null, () => Projects.list());
132
132
  const scopeOptions = useMemo(() => [
133
133
  { value: SUPER, label: t("skills_page.scope_super_agent") },
134
134
  ...(projects ?? []).map((p) => ({ value: p.path, label: p.name || basename(p.path) })),
135
135
  ], [projects]);
136
136
 
137
- const { data, mutate, isLoading } = useSWR(["/skills", scope], () => Skills.list(projectPath));
137
+ const { data, mutate, isLoading } = useSWR(["/api/skills", scope], () => Skills.list(projectPath));
138
138
  const skills = useMemo(() => data?.skills ?? [], [data]);
139
139
  const onCount = skills.filter((s) => s.enabled !== false).length;
140
140
 
@@ -32,7 +32,7 @@ export function TaskDetailPanel({
32
32
  }) {
33
33
  const toast = useToast();
34
34
  const navigate = useNavigate();
35
- const { data: task, isLoading, mutate } = useSWR(`/projects/${pid}/tasks/${taskId}`, () => Tasks.get(pid, taskId));
35
+ const { data: task, isLoading, mutate } = useSWR(`/api/projects/${pid}/tasks/${taskId}`, () => Tasks.get(pid, taskId));
36
36
  const [body, setBody] = useState("");
37
37
  const [busy, setBusy] = useState(false);
38
38
 
@@ -0,0 +1,47 @@
1
+ import { cn } from "../../lib/cn";
2
+
3
+ /**
4
+ * One row of mutually-exclusive filters.
5
+ *
6
+ * Exists so every list page filters the same way and reads the same way.
7
+ * Labels are Capitalised: the chips used to render whatever the state value
8
+ * happened to be ("open", "in review"), which put raw storage vocabulary in
9
+ * the interface.
10
+ */
11
+ export function FilterChips<T extends string>({
12
+ value,
13
+ options,
14
+ onChange,
15
+ label,
16
+ }: {
17
+ value: T;
18
+ options: { value: T; label: string }[];
19
+ onChange: (v: T) => void;
20
+ label?: string;
21
+ }) {
22
+ return (
23
+ <div className="flex flex-wrap items-center gap-1" role="group" aria-label={label}>
24
+ {options.map((o) => (
25
+ <button
26
+ key={o.value}
27
+ type="button"
28
+ aria-pressed={value === o.value}
29
+ onClick={() => onChange(o.value)}
30
+ className={cn(
31
+ "rounded-md px-2.5 py-1 text-xs font-medium transition-colors",
32
+ value === o.value
33
+ ? "bg-accent text-accent-fg"
34
+ : "text-muted-fg hover:bg-muted/50 hover:text-foreground",
35
+ )}
36
+ >
37
+ {capitalise(o.label)}
38
+ </button>
39
+ ))}
40
+ </div>
41
+ );
42
+ }
43
+
44
+ /** First letter up, rest untouched — so "in review" keeps its lowercase w. */
45
+ function capitalise(s: string): string {
46
+ return s ? s.charAt(0).toUpperCase() + s.slice(1) : s;
47
+ }
@@ -15,6 +15,7 @@ import { Dialog as DialogRoot, DialogContent, DialogHeader, DialogTitle, DialogD
15
15
  // Re-export the tooltip convenience wrapper so call sites can grab it from the
16
16
  // same barrel as Button/Field/etc.: `import { Button, Tip } from "../ui"`.
17
17
  export { Tip } from "./ui/tip";
18
+ export { FilterChips } from "./ui/filter-chips";
18
19
 
19
20
  // ── Button ──────────────────────────────────────────────────────────────────
20
21
  type Variant = "primary" | "secondary" | "ghost" | "destructive";
@@ -65,6 +65,7 @@ export const ROUTINE_KINDS = [
65
65
  "super_agent",
66
66
  "telegram",
67
67
  "shell",
68
+ "watch",
68
69
  ] as const;
69
70
  export type RoutineKind = (typeof ROUTINE_KINDS)[number];
70
71
 
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useRef, useState } from "react";
2
2
  import { SuperAgent, Agents, Conversations } from "../lib/api";
3
- import type { ChatStreamEvent, ChatUsage, ConversationMessage } from "../types/daemon";
3
+ import type { ChatStreamEvent, ChatUsage, ConversationMessage, ToolSummary } from "../types/daemon";
4
4
  import { t } from "../i18n";
5
5
 
6
6
  export type ToolStatus = "running" | "done" | "error" | "deduped";
@@ -37,6 +37,9 @@ export interface ChatMsg {
37
37
  usage?: ChatUsage;
38
38
  /** Operational notes (engine fallbacks, retries, suppressions). */
39
39
  notes?: string[];
40
+ /** What a HISTORICAL turn did. Live turns render real ToolCall parts from
41
+ * the stream; replayed ones only have this, recorded at write time. */
42
+ toolSummary?: ToolSummary;
40
43
  /** Skill Inspector decision for this turn (when the feature is on): which
41
44
  * skills the per-turn RAG loaded inline vs merely hinted. */
42
45
  inspector?: {
@@ -179,6 +182,7 @@ function threadToChatMsgs(messages: ConversationMessage[]): ChatMsg[] {
179
182
  if (m.agent) turn.agentId = m.agent;
180
183
  if (m.agent_name) turn.agent = m.agent_name;
181
184
  if (m.model) turn.model = m.model;
185
+ if (m.tool_summary) turn.toolSummary = m.tool_summary;
182
186
  if (m.usage) {
183
187
  turn.usage = {
184
188
  input_tokens: (turn.usage?.input_tokens || 0) + (m.usage.input_tokens || 0),
@@ -4,7 +4,7 @@ import { REFRESH } from "../constants";
4
4
 
5
5
  export function useDaemonStatus() {
6
6
  const { data, error, isLoading } = useSWR(
7
- "/health",
7
+ "/api/health",
8
8
  () => Health.get(),
9
9
  { refreshInterval: REFRESH.health },
10
10
  );
@@ -4,7 +4,7 @@ import { REFRESH } from "../constants";
4
4
 
5
5
  export function useDevices() {
6
6
  const { data, error, isLoading, mutate } = useSWR(
7
- "/pair/list",
7
+ "/api/pair/list",
8
8
  () => Pair.list(),
9
9
  { refreshInterval: REFRESH.pairList },
10
10
  );
@@ -3,7 +3,7 @@ import { Engines } from "../lib/api";
3
3
 
4
4
  export function useEngines() {
5
5
  const { data, error, isLoading, mutate } = useSWR(
6
- "/engines",
6
+ "/api/engines",
7
7
  () => Engines.list(),
8
8
  );
9
9
  return { engines: data?.engines || [], error, isLoading, mutate };
@@ -4,7 +4,7 @@ import type { GlobalConfig, SuperAgentConfig } from "../types/daemon";
4
4
 
5
5
  export function useGlobalConfig() {
6
6
  const { data, error, isLoading, mutate } = useSWR(
7
- "/admin/config",
7
+ "/api/admin/config",
8
8
  () => Admin.config.get(),
9
9
  );
10
10
  const patch = async (set?: Record<string, unknown>, unset?: string[]) => {
@@ -17,7 +17,7 @@ export function useGlobalConfig() {
17
17
 
18
18
  export function useSuperAgentConfig() {
19
19
  const { data, error, isLoading, mutate } = useSWR(
20
- "/admin/super-agent",
20
+ "/api/admin/super-agent",
21
21
  () => Admin.superAgent(),
22
22
  );
23
23
  return { superAgent: data as SuperAgentConfig | undefined, error, isLoading, mutate };
@@ -4,7 +4,7 @@ import type { Identity } from "../types/daemon";
4
4
 
5
5
  export function useIdentity() {
6
6
  const { data, error, isLoading, mutate } = useSWR<Identity>(
7
- "/identity",
7
+ "/api/identity",
8
8
  () => IdentityApi.get(),
9
9
  );
10
10
  const save = async (patch: Partial<Identity>) => {
@@ -4,7 +4,7 @@ import { Inbox, type InboxRow } from "../lib/api/inbox";
4
4
  /** Every agent as a conversation, most recent first, super-agent pinned. */
5
5
  export function useInbox(includeEmpty = false) {
6
6
  const { data, error, isLoading, mutate } = useSWR<InboxRow[]>(
7
- `/inbox?include_empty=${includeEmpty ? 1 : 0}`,
7
+ `/api/inbox?include_empty=${includeEmpty ? 1 : 0}`,
8
8
  () => Inbox.list(includeEmpty),
9
9
  { refreshInterval: 15_000 },
10
10
  );
@@ -0,0 +1,38 @@
1
+ import useSWR from "swr";
2
+ import { NudgesApi } from "../lib/api/nudges";
3
+ import type { NudgeEntry, NudgePolicy, NudgeStats } from "../lib/api/nudges";
4
+
5
+ /** The ledger of unrequested messages, plus what they add up to. */
6
+ export function useNudges(limit = 50) {
7
+ const { data, error, isLoading, mutate } = useSWR<{
8
+ data: NudgeEntry[];
9
+ meta: { total: number; stats: NudgeStats };
10
+ }>(`/api/nudges?limit=${limit}`, () => NudgesApi.list(limit));
11
+
12
+ return {
13
+ entries: data?.data ?? [],
14
+ stats: data?.meta?.stats,
15
+ error,
16
+ isLoading,
17
+ mutate,
18
+ };
19
+ }
20
+
21
+ /** The effective budget, and which layer set each part of it. */
22
+ export function useNudgePolicy() {
23
+ const { data, error, isLoading, mutate } = useSWR<{
24
+ policy: NudgePolicy;
25
+ source: string[];
26
+ defaults: NudgePolicy;
27
+ user_overrides: Partial<NudgePolicy>;
28
+ }>("/api/nudges/policy", () => NudgesApi.policy());
29
+
30
+ return {
31
+ policy: data?.policy,
32
+ source: data?.source ?? [],
33
+ userOverrides: data?.user_overrides ?? {},
34
+ error,
35
+ isLoading,
36
+ mutate,
37
+ };
38
+ }
@@ -7,7 +7,7 @@ export function useProfiles() {
7
7
  const { data, error, isLoading, mutate } = useSWR<{
8
8
  active: string | null;
9
9
  profiles: ProfileSummary[];
10
- }>("/profiles", () => ProfilesApi.list());
10
+ }>("/api/profiles", () => ProfilesApi.list());
11
11
 
12
12
  return {
13
13
  active: data?.active ?? null,
@@ -21,7 +21,7 @@ export function useProfiles() {
21
21
  /** One profile, including its schema, settings and rendered prompt preview. */
22
22
  export function useProfile(id: string | null) {
23
23
  const { data, error, isLoading, mutate } = useSWR<ProfileDetail>(
24
- id ? `/profiles/${id}` : null,
24
+ id ? `/api/profiles/${id}` : null,
25
25
  () => ProfilesApi.get(id as string),
26
26
  );
27
27
  return { profile: data, error, isLoading, mutate };
@@ -30,7 +30,7 @@ export function useProfile(id: string | null) {
30
30
  /** Health of the active profile (or a named one). */
31
31
  export function useProfileDoctor(id: string | null) {
32
32
  const { data, error, isLoading, mutate } = useSWR<ProfileDoctor>(
33
- id ? `/profiles/doctor?id=${id}` : "/profiles/doctor",
33
+ id ? `/api/profiles/doctor?id=${id}` : "/api/profiles/doctor",
34
34
  () => ProfilesApi.doctor(id || undefined),
35
35
  );
36
36
  return { doctor: data, error, isLoading, mutate };
@@ -6,7 +6,7 @@ import type { ProjectEntry } from "../types/daemon";
6
6
  /** Project list, sorted so the default workspace (id=0) is first. */
7
7
  export function useProjects() {
8
8
  const { data, error, isLoading, mutate } = useSWR<ProjectEntry[]>(
9
- "/projects",
9
+ "/api/projects",
10
10
  () => Projects.list(),
11
11
  { refreshInterval: REFRESH.projects },
12
12
  );
@@ -4,7 +4,7 @@ import { REFRESH } from "../constants";
4
4
 
5
5
  export function useTelegramStatus() {
6
6
  const { data, error, isLoading, mutate } = useSWR(
7
- "/telegram/status",
7
+ "/api/telegram/status",
8
8
  () => Telegram.status(),
9
9
  { refreshInterval: REFRESH.telegramStatus },
10
10
  );
@@ -13,7 +13,7 @@ export function useTelegramStatus() {
13
13
 
14
14
  export function useTelegramChannels() {
15
15
  const { data, error, isLoading, mutate } = useSWR(
16
- "/telegram/channels",
16
+ "/api/telegram/channels",
17
17
  () => Telegram.channels.list(),
18
18
  );
19
19
  return { channels: data?.channels || [], error, isLoading, mutate };
@@ -21,7 +21,7 @@ export function useTelegramChannels() {
21
21
 
22
22
  export function useTelegramContacts() {
23
23
  const { data, error, isLoading, mutate } = useSWR(
24
- "/telegram/contacts",
24
+ "/api/telegram/contacts",
25
25
  () => Telegram.contacts.list(),
26
26
  );
27
27
  return {